Skip to content

[stable/20210726] Swift async extended frame info (LLVM only) #3253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions llvm/include/llvm/CodeGen/CommandFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ llvm::FloatABI::ABIType getFloatABIForCalls();

llvm::FPOpFusion::FPOpFusionMode getFuseFPOps();

SwiftAsyncFramePointerMode getSwiftAsyncFramePointer();

bool getDontPlaceZerosInBSS();

bool getEnableGuaranteedTailCallOpt();
Expand Down
16 changes: 16 additions & 0 deletions llvm/include/llvm/Target/TargetOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ namespace llvm {
DisableWithDiag // Disable the abort but emit a diagnostic on failure.
};

/// Indicates when and how the Swift async frame pointer bit should be set.
enum class SwiftAsyncFramePointerMode {
/// Determine whether to set the bit statically or dynamically based
/// on the deployment target.
DeploymentBased,
/// Always set the bit.
Always,
/// Never set the bit.
Never,
};

class TargetOptions {
public:
TargetOptions()
Expand Down Expand Up @@ -219,6 +230,11 @@ namespace llvm {
/// selection fails to lower/select an instruction.
GlobalISelAbortMode GlobalISelAbort = GlobalISelAbortMode::Enable;

/// Control when and how the Swift async frame pointer bit should
/// be set.
SwiftAsyncFramePointerMode SwiftAsyncFramePointer =
SwiftAsyncFramePointerMode::Always;

/// UseInitArray - Use .init_array instead of .ctors for static
/// constructors.
unsigned UseInitArray : 1;
Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/CodeGen/CommandFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ CGOPT(DenormalMode::DenormalModeKind, DenormalFP32Math)
CGOPT(bool, EnableHonorSignDependentRoundingFPMath)
CGOPT(FloatABI::ABIType, FloatABIForCalls)
CGOPT(FPOpFusion::FPOpFusionMode, FuseFPOps)
CGOPT(SwiftAsyncFramePointerMode, SwiftAsyncFramePointer)
CGOPT(bool, DontPlaceZerosInBSS)
CGOPT(bool, EnableGuaranteedTailCallOpt)
CGOPT(bool, DisableTailCalls)
Expand Down Expand Up @@ -277,6 +278,18 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
"Only fuse FP ops when the result won't be affected.")));
CGBINDOPT(FuseFPOps);

static cl::opt<SwiftAsyncFramePointerMode> SwiftAsyncFramePointer(
"swift-async-fp",
cl::desc("Determine when the Swift async frame pointer should be set"),
cl::init(SwiftAsyncFramePointerMode::Always),
cl::values(clEnumValN(SwiftAsyncFramePointerMode::DeploymentBased, "auto",
"Determine based on deployment target"),
clEnumValN(SwiftAsyncFramePointerMode::Always, "always",
"Always set the bit"),
clEnumValN(SwiftAsyncFramePointerMode::Never, "never",
"Never set the bit")));
CGBINDOPT(SwiftAsyncFramePointer);

static cl::opt<bool> DontPlaceZerosInBSS(
"nozero-initialized-in-bss",
cl::desc("Don't place zero-initialized symbols into bss section"),
Expand Down Expand Up @@ -533,7 +546,7 @@ codegen::InitTargetOptionsFromCodeGenFlags(const Triple &TheTriple) {
Options.ThreadModel = getThreadModel();
Options.EABIVersion = getEABIVersion();
Options.DebuggerTuning = getDebuggerTuningOpt();

Options.SwiftAsyncFramePointer = getSwiftAsyncFramePointer();
return Options;
}

Expand Down
32 changes: 27 additions & 5 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1180,11 +1180,33 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
// 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);
switch (MF.getTarget().Options.SwiftAsyncFramePointer) {
case SwiftAsyncFramePointerMode::DeploymentBased:
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);
break;
}
LLVM_FALLTHROUGH;

case SwiftAsyncFramePointerMode::Always:
// 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);
break;

case SwiftAsyncFramePointerMode::Never:
break;
}
}

// All calls are tail calls in GHC calling conv, and functions have no
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/Target/AArch64/AArch64Subtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,31 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
}
}

/// 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
Expand Down
32 changes: 27 additions & 5 deletions llvm/lib/Target/X86/X86FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1363,11 +1363,33 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
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);
switch (MF.getTarget().Options.SwiftAsyncFramePointer) {
case SwiftAsyncFramePointerMode::DeploymentBased:
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);
break;
}
LLVM_FALLTHROUGH;

case SwiftAsyncFramePointerMode::Always:
BuildMI(MBB, MBBI, DL, TII.get(X86::BTS64ri8), MachineFramePtr)
.addUse(MachineFramePtr)
.addImm(60)
.setMIFlag(MachineInstr::FrameSetup);
break;

case SwiftAsyncFramePointerMode::Never:
break;
}
}

// Re-align the stack on 64-bit if the x86-interrupt calling convention is
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/Target/X86/X86Subtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,31 @@ class X86Subtarget final : public X86GenSubtargetInfo {
/// 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 {
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/CodeGen/AArch64/swift-async.ll
Original file line number Diff line number Diff line change
@@ -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
Expand Down
34 changes: 34 additions & 0 deletions llvm/test/CodeGen/AArch64/swift-dynamic-async-frame.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
; RUN: llc -mtriple arm64-apple-ios15.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple arm64-apple-ios15.0.0 -swift-async-fp=auto %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple arm64-apple-ios15.0.0 -swift-async-fp=never %s -o - | FileCheck %s --check-prefix=CHECK-NEVER
; RUN: llc -mtriple arm64-apple-ios14.9.0 -swift-async-fp=always %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple arm64-apple-ios14.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple arm64-apple-ios14.9.0 -swift-async-fp=auto %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-STATIC
; 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-STATIC
; 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-STATIC
; RUN: llc -mtriple arm64_32-apple-watchos7.9.0 -swift-async-fp=auto %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC-32

; CHECK-STATIC-LABEL: foo:
; CHECK-STATIC: orr x29, x29, #0x1000000000000000

; CHECK-NEVER-LABEL: foo:
; CHECK-NEVER-NOT: orr x29, x29, #0x1000000000000000
; CHECK-NEVER-NOT: _swift_async_extendedFramePointerFlags

; 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
}
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/X86/swift-async.ll
Original file line number Diff line number Diff line change
@@ -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


Expand Down
22 changes: 22 additions & 0 deletions llvm/test/CodeGen/X86/swift-dynamic-async-frame.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
; RUN: llc -mtriple x86_64-apple-macosx12.0.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx12.0.0 -swift-async-fp=always %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx12.0.0 -swift-async-fp=auto %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx11.9.0 -swift-async-fp=always %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx11.9.0 %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx11.9.0 -swift-async-fp=auto %s -o - | FileCheck %s --check-prefix=CHECK-DYNAMIC
; RUN: llc -mtriple x86_64-apple-macosx11.9.0 -swift-async-fp=always %s -o - | FileCheck %s --check-prefix=CHECK-STATIC
; RUN: llc -mtriple x86_64-apple-macosx11.9.0 -swift-async-fp=never %s -o - | FileCheck %s --check-prefix=CHECK-NEVER

; CHECK-STATIC-LABEL: foo:
; CHECK-STATIC: btsq $60, %rbp

; CHECK-DYNAMIC-LABEL: foo:
; CHECK-DYNAMIC: orq _swift_async_extendedFramePointerFlags@GOTPCREL(%rip), %rbp

; CHECK-NEVER-LABEL: foo:
; CHECK-NEVER-NOT: btsq $60, %rbp
; CHECK-NEVER-NOT: _swift_async_extendedFramePointerFlags

define void @foo(i8* swiftasync) "frame-pointer"="all" {
ret void
}