Index: llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
===================================================================
--- llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -1158,11 +1158,23 @@
   // ORR is sufficient, it is assumed a Swift kernel would initialize the TBI
   // bits so that is still true.
   if (HasFP && AFI->hasSwiftAsyncContext()) {
-    // ORR x29, x29, #0x1000_0000_0000_0000
-    BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXri), AArch64::FP)
-        .addUse(AArch64::FP)
-        .addImm(0x1100)
-        .setMIFlag(MachineInstr::FrameSetup);
+    if  (Subtarget.swiftAsyncContextIsDynamicallySet()) {
+      // The special symbol below is absolute and has a *value* that can be
+      // combined with the frame pointer to signal an extended frame.
+      BuildMI(MBB, MBBI, DL, TII->get(AArch64::LOADgot), AArch64::X16)
+          .addExternalSymbol("swift_async_extendedFramePointerFlags",
+                             AArch64II::MO_GOT);
+      BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), AArch64::FP)
+          .addUse(AArch64::FP)
+          .addUse(AArch64::X16)
+          .addImm(Subtarget.isTargetILP32() ? 32 : 0);
+    } else {
+      // ORR x29, x29, #0x1000_0000_0000_0000
+      BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXri), AArch64::FP)
+          .addUse(AArch64::FP)
+          .addImm(0x1100)
+          .setMIFlag(MachineInstr::FrameSetup);
+    }
   }
 
   // All calls are tail calls in GHC calling conv, and functions have no
Index: llvm/lib/Target/AArch64/AArch64Subtarget.h
===================================================================
--- llvm/lib/Target/AArch64/AArch64Subtarget.h
+++ llvm/lib/Target/AArch64/AArch64Subtarget.h
@@ -600,6 +600,31 @@
     }
   }
 
+  /// Return whether FrameLowering should always set the "extended frame
+  /// present" bit in FP, or set it based on a symbol in the runtime.
+  bool swiftAsyncContextIsDynamicallySet() const {
+    // Older OS versions (particularly system unwinders) are confused by the
+    // Swift extended frame, so when building code that might be run on them we
+    // must dynamically query the concurrency library to determine whether
+    // extended frames should be flagged as present.
+    const Triple &TT = getTargetTriple();
+
+    unsigned Major, Minor, Micro;
+    TT.getOSVersion(Major, Minor, Micro);
+    switch(TT.getOS()) {
+    default:
+      return false;
+    case Triple::IOS:
+    case Triple::TvOS:
+      return Major < 15;
+    case Triple::WatchOS:
+      return Major < 8;
+    case Triple::MacOSX:
+    case Triple::Darwin:
+      return Major < 12;
+    }
+  }
+
   void mirFileLoaded(MachineFunction &MF) const override;
 
   // Return the known range for the bit length of SVE data registers. A value
Index: llvm/lib/Target/X86/X86FrameLowering.cpp
===================================================================
--- llvm/lib/Target/X86/X86FrameLowering.cpp
+++ llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -1360,11 +1360,23 @@
   unsigned StackProbeSize = STI.getTargetLowering()->getStackProbeSize(MF);
 
   if (HasFP && X86FI->hasSwiftAsyncContext()) {
-    BuildMI(MBB, MBBI, DL, TII.get(X86::BTS64ri8),
-            MachineFramePtr)
-        .addUse(MachineFramePtr)
-        .addImm(60)
-        .setMIFlag(MachineInstr::FrameSetup);
+    if (STI.swiftAsyncContextIsDynamicallySet()) {
+      // The special symbol below is absolute and has a *value* suitable to be
+      // combined with the frame pointer directly.
+      BuildMI(MBB, MBBI, DL, TII.get(X86::OR64rm), MachineFramePtr)
+          .addUse(MachineFramePtr)
+          .addUse(X86::RIP)
+          .addImm(1)
+          .addUse(X86::NoRegister)
+          .addExternalSymbol("swift_async_extendedFramePointerFlags",
+                             X86II::MO_GOTPCREL)
+          .addUse(X86::NoRegister);
+    } else {
+      BuildMI(MBB, MBBI, DL, TII.get(X86::BTS64ri8), MachineFramePtr)
+          .addUse(MachineFramePtr)
+          .addImm(60)
+          .setMIFlag(MachineInstr::FrameSetup);
+    }
   }
 
   // Re-align the stack on 64-bit if the x86-interrupt calling convention is
Index: llvm/lib/Target/X86/X86Subtarget.h
===================================================================
--- llvm/lib/Target/X86/X86Subtarget.h
+++ llvm/lib/Target/X86/X86Subtarget.h
@@ -937,6 +937,31 @@
   /// Return true if the subtarget allows calls to immediate address.
   bool isLegalToCallImmediateAddr() const;
 
+  /// Return whether FrameLowering should always set the "extended frame
+  /// present" bit in FP, or set it based on a symbol in the runtime.
+  bool swiftAsyncContextIsDynamicallySet() const {
+    // Older OS versions (particularly system unwinders) are confused by the
+    // Swift extended frame, so when building code that might be run on them we
+    // must dynamically query the concurrency library to determine whether
+    // extended frames should be flagged as present.
+    const Triple &TT = getTargetTriple();
+
+    unsigned Major, Minor, Micro;
+    TT.getOSVersion(Major, Minor, Micro);
+    switch(TT.getOS()) {
+    default:
+      return false;
+    case Triple::IOS:
+    case Triple::TvOS:
+      return Major < 15;
+    case Triple::WatchOS:
+      return Major < 8;
+    case Triple::MacOSX:
+    case Triple::Darwin:
+      return Major < 12;
+    }
+  }
+
   /// If we are using indirect thunks, we need to expand indirectbr to avoid it
   /// lowering to an actual indirect jump.
   bool enableIndirectBrExpand() const override {
Index: llvm/test/CodeGen/AArch64/swift-async.ll
===================================================================
--- llvm/test/CodeGen/AArch64/swift-async.ll
+++ llvm/test/CodeGen/AArch64/swift-async.ll
@@ -1,6 +1,6 @@
-; RUN: llc -mtriple=arm64-apple-ios %s -o - | FileCheck %s --check-prefixes=CHECK-NOAUTH,CHECK
-; RUN: llc -mtriple=arm64-apple-ios -mcpu=apple-a13 %s -o - | FileCheck %s --check-prefixes=CHECK-NOAUTH,CHECK
-; RUN: llc -mtriple=arm64e-apple-ios %s -o - | FileCheck %s --check-prefixes=CHECK-AUTH,CHECK
+; RUN: llc -mtriple=arm64-apple-ios15 %s -o - | FileCheck %s --check-prefixes=CHECK-NOAUTH,CHECK
+; RUN: llc -mtriple=arm64-apple-ios15 -mcpu=apple-a13 %s -o - | FileCheck %s --check-prefixes=CHECK-NOAUTH,CHECK
+; RUN: llc -mtriple=arm64e-apple-ios15 %s -o - | FileCheck %s --check-prefixes=CHECK-AUTH,CHECK
 
 ; Important details in prologue:
 ;   * x22 is stored just below x29
Index: llvm/test/CodeGen/AArch64/swift-dynamic-async-frame.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/AArch64/swift-dynamic-async-frame.ll
@@ -0,0 +1,25 @@
+; RUN: llc -mtriple arm64-apple-ios15.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
+; RUN: llc -mtriple arm64-apple-ios14.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC
+; RUN: llc -mtriple arm64-apple-tvos15.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
+; RUN: llc -mtriple arm64-apple-tvos14.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC
+; RUN: llc -mtriple arm64-apple-macosx12.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
+; RUN: llc -mtriple arm64-apple-macosx11.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC
+; RUN: llc -mtriple arm64_32-apple-watchos8.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
+; RUN: llc -mtriple arm64_32-apple-watchos7.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC-32
+
+; CHECK-STATIC-LABEL: foo:
+; CHECK-STATIC: orr x29, x29, #0x1000000000000000
+
+; CHECK-DYNAMIC-LABEL: foo:
+; CHECK-DYNAMIC: adrp x16, _swift_async_extendedFramePointerFlags@GOTPAGE
+; CHECK-DYNAMIC: ldr x16, [x16, _swift_async_extendedFramePointerFlags@GOTPAGEOFF]
+; CHECK-DYNAMIC: orr x29, x29, x16
+
+; CHECK-DYNAMIC-32-LABEL: foo:
+; CHECK-DYNAMIC-32: adrp x16, _swift_async_extendedFramePointerFlags@GOTPAGE
+; CHECK-DYNAMIC-32: ldr w16, [x16, _swift_async_extendedFramePointerFlags@GOTPAGEOFF]
+; CHECK-DYNAMIC-32: orr x29, x29, x16, lsl #32
+
+define void @foo(i8* swiftasync) "frame-pointer"="all" {
+  ret void
+}
Index: llvm/test/CodeGen/X86/swift-async.ll
===================================================================
--- llvm/test/CodeGen/X86/swift-async.ll
+++ llvm/test/CodeGen/X86/swift-async.ll
@@ -1,4 +1,4 @@
-; RUN: llc -mtriple=x86_64-apple-darwin %s -o - | FileCheck %s
+; RUN: llc -mtriple=x86_64-apple-macosx12.0 %s -o - | FileCheck %s
 ; RUN: llc -mtriple=i686-apple-darwin %s -o - | FileCheck %s --check-prefix=CHECK-32
 
 
Index: llvm/test/CodeGen/X86/swift-dynamic-async-frame.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/X86/swift-dynamic-async-frame.ll
@@ -0,0 +1,12 @@
+; RUN: llc -mtriple x86_64-apple-macosx12.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
+; RUN: llc -mtriple x86_64-apple-macosx11.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC
+
+; CHECK-STATIC-LABEL: foo:
+; CHECK-STATIC: btsq $60, %rbp
+
+; CHECK-DYNAMIC-LABEL: foo:
+; CHECK-DYNAMIC: orq _swift_async_extendedFramePointerFlags@GOTPCREL(%rip), %rbp
+
+define void @foo(i8* swiftasync) "frame-pointer"="all" {
+  ret void
+}