diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -441,6 +441,14 @@
   /// `MCAsmInfo::ExceptionsType == ExceptionHandling::None`.
   bool needsCFIForDebug() const;
 
+  /// Emitting CFI unwind information is also entangled with supporting
+  /// unwinding used by sanitizers. This returns true for platforms which use
+  /// CFI for the unwind tables or exception handling.
+  bool needsCFIForUnwindTables(const Function &F) const;
+
+  /// MachineFunction version of @ref needsCFIForUnwindTables(const Function &F)
+  bool needsCFIForUnwindTables(const MachineFunction &F) const;
+
   /// Print to the current output stream assembly representations of the
   /// constants in the constant pool MCP. This is used to print out constants
   /// which have been "spilled to memory" by the code generator.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -172,6 +172,13 @@
   void deleted() override;
   void allUsesReplacedWith(Value *V2) override;
 };
+
+static bool anyFunctionNeedsCFIForUnwindTables(const AsmPrinter &Printer,
+                                               const Module &M) {
+  return any_of(M, [&Printer](const Function &F) {
+    return Printer.needsCFIForUnwindTables(F);
+  });
+}
 } // namespace
 
 class llvm::AddrLabelMap {
@@ -528,7 +535,8 @@
                           PPTimerDescription, PPGroupName, PPGroupDescription);
   }
 
-  switch (MAI->getExceptionHandlingType()) {
+  auto const ExceptionHandlingType = MAI->getExceptionHandlingType();
+  switch (ExceptionHandlingType) {
   case ExceptionHandling::None:
     // We may want to emit CFI for debug.
     [[fallthrough]];
@@ -536,24 +544,23 @@
   case ExceptionHandling::DwarfCFI:
   case ExceptionHandling::ARM:
     for (auto &F : M.getFunctionList()) {
-      if (getFunctionCFISectionType(F) != CFISection::None)
-        ModuleCFISection = getFunctionCFISectionType(F);
+      auto FunctionCFISection = getFunctionCFISectionType(F);
+      if (FunctionCFISection != CFISection::None)
+        ModuleCFISection = FunctionCFISection;
       // If any function needsUnwindTableEntry(), it needs .eh_frame and hence
       // the module needs .eh_frame. If we have found that case, we are done.
       if (ModuleCFISection == CFISection::EH)
         break;
     }
-    assert(MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI ||
-           ModuleCFISection != CFISection::EH);
     break;
   default:
     break;
   }
 
   EHStreamer *ES = nullptr;
-  switch (MAI->getExceptionHandlingType()) {
+  switch (ExceptionHandlingType) {
   case ExceptionHandling::None:
-    if (!needsCFIForDebug())
+    if (!needsCFIForDebug() && !anyFunctionNeedsCFIForUnwindTables(*this, M))
       break;
     [[fallthrough]];
   case ExceptionHandling::SjLj:
@@ -1250,8 +1257,7 @@
   if (F.isDeclarationForLinker())
     return CFISection::None;
 
-  if (MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI &&
-      F.needsUnwindTableEntry())
+  if (needsCFIForUnwindTables(F))
     return CFISection::EH;
 
   if (MMI->hasDebugInfo() || TM.Options.ForceDwarfFrameSection)
@@ -1269,6 +1275,30 @@
   return MAI->usesWindowsCFI() && MF->getFunction().needsUnwindTableEntry();
 }
 
+bool AsmPrinter::needsCFIForUnwindTables(const MachineFunction &F) const {
+  return needsCFIForUnwindTables(F.getFunction());
+}
+
+bool AsmPrinter::needsCFIForUnwindTables(const Function &F) const {
+  // If the unwind table entries are used for eh, emit them.
+  // If the unwind table entires are explicitely needed via the corresponding
+  // attribute, and cfi is supported by the target, emit unwind table entries
+  // This last case is used by sanitizers (e.g. asan) that force the use of
+  // unwind tables
+  if (F.isDeclarationForLinker())
+    return false;
+
+  const auto EHType = MAI->getExceptionHandlingType();
+  if (EHType == ExceptionHandling::DwarfCFI && F.needsUnwindTableEntry())
+    return true;
+
+  if (EHType == ExceptionHandling::None && MAI->doesUseCFIForDebug() &&
+      F.hasUWTable())
+    return true;
+
+  return false;
+}
+
 bool AsmPrinter::needsCFIForDebug() const {
   return MAI->getExceptionHandlingType() == ExceptionHandling::None &&
          MAI->doesUseCFIForDebug() && ModuleCFISection == CFISection::Debug;
@@ -1276,8 +1306,8 @@
 
 void AsmPrinter::emitCFIInstruction(const MachineInstr &MI) {
   ExceptionHandling ExceptionHandlingType = MAI->getExceptionHandlingType();
-  if (!needsCFIForDebug() &&
-      ExceptionHandlingType != ExceptionHandling::DwarfCFI &&
+  bool NeedsCFI = needsCFIForDebug() || needsCFIForUnwindTables(*MI.getMF());
+  if (!NeedsCFI && ExceptionHandlingType != ExceptionHandling::DwarfCFI &&
       ExceptionHandlingType != ExceptionHandling::ARM)
     return;
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCFIException.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCFIException.cpp
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCFIException.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCFIException.cpp
@@ -101,7 +101,9 @@
     shouldEmitCFI =
         MAI.usesCFIForEH() && (shouldEmitPersonality || shouldEmitMoves);
   else
-    shouldEmitCFI = Asm->needsCFIForDebug() && shouldEmitMoves;
+    shouldEmitCFI =
+        (Asm->needsCFIForDebug() || Asm->needsCFIForUnwindTables(*MF)) &&
+        shouldEmitMoves;
 }
 
 void DwarfCFIException::beginBasicBlockSection(const MachineBasicBlock &MBB) {
diff --git a/llvm/test/CodeGen/AMDGPU/eh_frame.ll b/llvm/test/CodeGen/AMDGPU/eh_frame.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/eh_frame.ll
@@ -0,0 +1,29 @@
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx908 -o - < %s | FileCheck %s --check-prefix=EH
+; RUN: llc -mtriple=amdgcn-amd-amdhsa --force-dwarf-frame-section -o - < %s | FileCheck %s --check-prefix=BOTH
+; RUN: llc -mtriple=amdgcn-amd-amdhsa --exception-model=dwarf -o - < %s | FileCheck %s --check-prefix=EH
+; RUN: llc -mtriple=amdgcn-amd-amdhsa --force-dwarf-frame-section --exception-model=dwarf -o - < %s | FileCheck %s --check-prefix=BOTH
+
+; EH: f:
+; EH-NOT: .cfi_sections
+; EH: .cfi_startproc
+
+; BOTH: f:
+; BOTH: .cfi_sections .eh_frame, .debug_frame
+; BOTH: .cfi_startproc
+
+define void @f() nounwind uwtable !dbg !0 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7}
+!5 = !{!0}
+
+!0 = distinct !DISubprogram(name: "f", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !2, scopeLine: 1, file: !6, scope: !1, type: !3)
+!1 = !DIFile(filename: "/home/llvm/test.c", directory: "/home/llvm/build")
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", isOptimized: true, emissionKind: FullDebug, file: !6, enums: !{}, retainedTypes: !{})
+!3 = !DISubroutineType(types: !4)
+!4 = !{null}
+!6 = !DIFile(filename: "/home/llvm/test.c", directory: "/home/llvm/build")
+!7 = !{i32 1, !"Debug Info Version", i32 3}
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.generated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.generated.expected
--- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.generated.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.generated.expected
@@ -67,6 +67,7 @@
 ; CHECK-LABEL: check_boundaries:
 ; CHECK:       check_boundaries$local:
 ; CHECK-NEXT:    .type check_boundaries$local,@function
+; CHECK-NEXT:    .cfi_startproc
 ; CHECK-NEXT:  ; %bb.0:
 ; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; CHECK-NEXT:    s_mov_b32 s4, s33
@@ -78,6 +79,7 @@
 ; CHECK-LABEL: main:
 ; CHECK:       main$local:
 ; CHECK-NEXT:    .type main$local,@function
+; CHECK-NEXT:    .cfi_startproc
 ; CHECK-NEXT:  ; %bb.0:
 ; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; CHECK-NEXT:    s_mov_b32 s6, s33
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.nogenerated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.nogenerated.expected
--- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.nogenerated.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_generated_funcs.ll.nogenerated.expected
@@ -8,6 +8,7 @@
 ; CHECK-LABEL: check_boundaries:
 ; CHECK:       check_boundaries$local:
 ; CHECK-NEXT:    .type check_boundaries$local,@function
+; CHECK-NEXT:    .cfi_startproc
 ; CHECK-NEXT:  ; %bb.0:
 ; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; CHECK-NEXT:    s_mov_b32 s4, s33
@@ -55,6 +56,7 @@
 ; CHECK-LABEL: main:
 ; CHECK:       main$local:
 ; CHECK-NEXT:    .type main$local,@function
+; CHECK-NEXT:    .cfi_startproc
 ; CHECK-NEXT:  ; %bb.0:
 ; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; CHECK-NEXT:    s_mov_b32 s6, s33