Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1278,6 +1278,12 @@ a valid attribute for return values and can only be applied to one parameter. +``swiftasync`` + This indicates that the parameter is the asynchronous context parameter and + triggers the creation of a target-specific extended frame record to store + this pointer. This is not a valid attribute for return values and can only + be applied to one parameter. + ``swifterror`` This attribute is motivated to model and optimize Swift error handling. It can be applied to a parameter with pointer to pointer type or a @@ -12350,6 +12356,29 @@ other aggressive transformations, so the value returned may not be that of the obvious source-language caller. +'``llvm.swift.async.context.addr``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i8** @llvm.swift.async.context.addr() + +Overview: +""""""""" + +The '``llvm.swift.async.context.addr``' intrinsic returns a pointer to +the part of the extended frame record containing the asynchronous +context of a Swift execution. + +Semantics: +"""""""""" + +If the function has a ``swiftasync`` parameter, that argument will initially +be stored at the returned address. If not, it will be initialized to null. + '``llvm.localescape``' and '``llvm.localrecover``' Intrinsics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: llvm/include/llvm/AsmParser/LLToken.h =================================================================== --- llvm/include/llvm/AsmParser/LLToken.h +++ llvm/include/llvm/AsmParser/LLToken.h @@ -239,6 +239,7 @@ kw_strictfp, kw_swifterror, kw_swiftself, + kw_swiftasync, kw_uwtable, kw_vscale_range, kw_willreturn, Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -660,6 +660,7 @@ ATTR_KIND_HOT = 72, ATTR_KIND_NO_PROFILE = 73, ATTR_KIND_VSCALE_RANGE = 74, + ATTR_KIND_SWIFT_ASYNC = 75, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/CodeGen/TargetCallingConv.h =================================================================== --- llvm/include/llvm/CodeGen/TargetCallingConv.h +++ llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -39,6 +39,7 @@ unsigned IsPreallocated : 1; ///< ByVal without the copy unsigned IsSplitEnd : 1; ///< Last part of a split unsigned IsSwiftSelf : 1; ///< Swift self parameter + unsigned IsSwiftAsync : 1; ///< Swift async context parameter unsigned IsSwiftError : 1; ///< Swift error parameter unsigned IsCFGuardTarget : 1; ///< Control Flow Guard target unsigned IsHva : 1; ///< HVA field for @@ -58,11 +59,12 @@ public: ArgFlagsTy() - : IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsByRef(0), - IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0), IsPreallocated(0), - IsSplitEnd(0), IsSwiftSelf(0), IsSwiftError(0), IsCFGuardTarget(0), - IsHva(0), IsHvaStart(0), IsSecArgPass(0), MemAlign(0), - OrigAlign(0), IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0), + : IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsByRef(0), + IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0), + IsPreallocated(0), IsSplitEnd(0), IsSwiftSelf(0), IsSwiftAsync(0), + IsSwiftError(0), IsCFGuardTarget(0), IsHva(0), IsHvaStart(0), + IsSecArgPass(0), MemAlign(0), OrigAlign(0), + IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0), IsCopyElisionCandidate(0), IsPointer(0), ByValOrByRefSize(0), PointerAddrSpace(0) { static_assert(sizeof(*this) == 3 * sizeof(unsigned), "flags are too big"); @@ -95,6 +97,9 @@ bool isSwiftSelf() const { return IsSwiftSelf; } void setSwiftSelf() { IsSwiftSelf = 1; } + bool isSwiftAsync() const { return IsSwiftAsync; } + void setSwiftAsync() { IsSwiftAsync = 1; } + bool isSwiftError() const { return IsSwiftError; } void setSwiftError() { IsSwiftError = 1; } Index: llvm/include/llvm/CodeGen/TargetFrameLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetFrameLowering.h +++ llvm/include/llvm/CodeGen/TargetFrameLowering.h @@ -149,6 +149,14 @@ /// returns false, spill slots will be assigned using generic implementation. /// assignCalleeSavedSpillSlots() may add, delete or rearrange elements of /// CSI. + virtual bool assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector &CSI, + unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const { + return assignCalleeSavedSpillSlots(MF, TRI, CSI); + } + virtual bool assignCalleeSavedSpillSlots(MachineFunction &MF, const TargetRegisterInfo *TRI, Index: llvm/include/llvm/CodeGen/TargetLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetLowering.h +++ llvm/include/llvm/CodeGen/TargetLowering.h @@ -284,6 +284,7 @@ bool IsPreallocated : 1; bool IsReturned : 1; bool IsSwiftSelf : 1; + bool IsSwiftAsync : 1; bool IsSwiftError : 1; bool IsCFGuardTarget : 1; MaybeAlign Alignment = None; @@ -294,7 +295,7 @@ : IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false), IsNest(false), IsByVal(false), IsByRef(false), IsInAlloca(false), IsPreallocated(false), IsReturned(false), IsSwiftSelf(false), - IsSwiftError(false), IsCFGuardTarget(false) {} + IsSwiftAsync(false), IsSwiftError(false), IsCFGuardTarget(false) {} void setAttributes(const CallBase *Call, unsigned ArgIdx); }; Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -244,6 +244,9 @@ /// Argument is swift self/context. def SwiftSelf : EnumAttr<"swiftself">; +/// Argument is swift async context. +def SwiftAsync : EnumAttr<"swiftasync">; + /// Function must be in a unwind table. def UWTable : EnumAttr<"uwtable">; Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -479,7 +479,12 @@ def int_objc_arc_annotation_bottomup_bbend : Intrinsic<[], [llvm_ptrptr_ty, llvm_ptrptr_ty]>; +//===--------------- Swift asynchronous context intrinsics ----------------===// +// Returns the location of the Swift asynchronous context (usually stored just +// before the frame pointer), and triggers the creation of a null context if it +// would otherwise be unneeded. +def int_swift_async_context_addr : Intrinsic<[llvm_ptrptr_ty], [], [IntrNoMem]>; //===--------------------- Code Generator Intrinsics ----------------------===// // Index: llvm/include/llvm/Target/TargetCallingConv.td =================================================================== --- llvm/include/llvm/Target/TargetCallingConv.td +++ llvm/include/llvm/Target/TargetCallingConv.td @@ -51,6 +51,11 @@ class CCIfSwiftSelf : CCIf<"ArgFlags.isSwiftSelf()", A> { } +/// CCIfSwiftAsync - If the current argument has swiftasync parameter attribute, +/// apply Action A. +class CCIfSwiftAsync : CCIf<"ArgFlags.isSwiftAsync()", A> { +} + /// CCIfSwiftError - If the current argument has swifterror parameter attribute, /// apply Action A. class CCIfSwiftError : CCIf<"ArgFlags.isSwiftError()", A> { Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -696,6 +696,7 @@ KEYWORD(speculative_load_hardening); KEYWORD(swifterror); KEYWORD(swiftself); + KEYWORD(swiftasync); KEYWORD(uwtable); KEYWORD(vscale_range); KEYWORD(willreturn); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1460,6 +1460,7 @@ case lltok::kw_sret: case lltok::kw_swifterror: case lltok::kw_swiftself: + case lltok::kw_swiftasync: case lltok::kw_immarg: case lltok::kw_byref: HaveError |= @@ -1799,6 +1800,7 @@ case lltok::kw_signext: B.addAttribute(Attribute::SExt); break; case lltok::kw_swifterror: B.addAttribute(Attribute::SwiftError); break; case lltok::kw_swiftself: B.addAttribute(Attribute::SwiftSelf); break; + case lltok::kw_swiftasync: B.addAttribute(Attribute::SwiftAsync); break; case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break; case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break; case lltok::kw_immarg: B.addAttribute(Attribute::ImmArg); break; @@ -1905,6 +1907,7 @@ case lltok::kw_sret: case lltok::kw_swifterror: case lltok::kw_swiftself: + case lltok::kw_swiftasync: case lltok::kw_immarg: case lltok::kw_byref: HaveError |= Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1524,6 +1524,8 @@ return Attribute::SwiftError; case bitc::ATTR_KIND_SWIFT_SELF: return Attribute::SwiftSelf; + case bitc::ATTR_KIND_SWIFT_ASYNC: + return Attribute::SwiftAsync; case bitc::ATTR_KIND_UW_TABLE: return Attribute::UWTable; case bitc::ATTR_KIND_VSCALE_RANGE: Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -736,6 +736,8 @@ return bitc::ATTR_KIND_SWIFT_ERROR; case Attribute::SwiftSelf: return bitc::ATTR_KIND_SWIFT_SELF; + case Attribute::SwiftAsync: + return bitc::ATTR_KIND_SWIFT_ASYNC; case Attribute::UWTable: return bitc::ATTR_KIND_UW_TABLE; case Attribute::VScaleRange: Index: llvm/lib/CodeGen/GlobalISel/CallLowering.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -54,6 +54,8 @@ Flags.setReturned(); if (AttrFn(Attribute::SwiftSelf)) Flags.setSwiftSelf(); + if (AttrFn(Attribute::SwiftAsync)) + Flags.setSwiftAsync(); if (AttrFn(Attribute::SwiftError)) Flags.setSwiftError(); } Index: llvm/lib/CodeGen/PrologEpilogInserter.cpp =================================================================== --- llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -399,7 +399,8 @@ const TargetFrameLowering *TFI = F.getSubtarget().getFrameLowering(); MachineFrameInfo &MFI = F.getFrameInfo(); - if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI)) { + if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI, MinCSFrameIndex, + MaxCSFrameIndex)) { // If target doesn't implement this, use generic code. if (CSI.empty()) @@ -677,10 +678,12 @@ // StackSlot scavenging is only implemented for the default stack. if (MFI.getStackID(i) == TargetStackID::Default) AllocatedFrameSlots.push_back(i); - // Add callee-save objects. - for (int i = MinCSFrameIndex; i <= (int)MaxCSFrameIndex; ++i) - if (MFI.getStackID(i) == TargetStackID::Default) - AllocatedFrameSlots.push_back(i); + // Add callee-save objects if there are any. + if (MinCSFrameIndex <= MaxCSFrameIndex) { + for (int i = MinCSFrameIndex; i <= (int)MaxCSFrameIndex; ++i) + if (MFI.getStackID(i) == TargetStackID::Default) + AllocatedFrameSlots.push_back(i); + } for (int i : AllocatedFrameSlots) { // These are converted from int64_t, but they should always fit in int @@ -833,7 +836,7 @@ // First assign frame offsets to stack objects that are used to spill // callee saved registers. - if (StackGrowsDown) { + if (StackGrowsDown && MaxCSFrameIndex >= MinCSFrameIndex) { for (unsigned i = MinCSFrameIndex; i <= MaxCSFrameIndex; ++i) { if (MFI.getStackID(i) != TargetStackID::Default) // Only allocate objects on the default stack. Index: llvm/lib/CodeGen/SelectionDAG/FastISel.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1048,6 +1048,8 @@ Flags.setSRet(); if (Arg.IsSwiftSelf) Flags.setSwiftSelf(); + if (Arg.IsSwiftAsync) + Flags.setSwiftAsync(); if (Arg.IsSwiftError) Flags.setSwiftError(); if (Arg.IsCFGuardTarget) Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -9339,6 +9339,7 @@ Entry.IsByRef = false; Entry.IsReturned = false; Entry.IsSwiftSelf = false; + Entry.IsSwiftAsync = false; Entry.IsSwiftError = false; Entry.IsCFGuardTarget = false; Entry.Alignment = Alignment; @@ -9452,6 +9453,8 @@ Flags.setSRet(); if (Args[i].IsSwiftSelf) Flags.setSwiftSelf(); + if (Args[i].IsSwiftAsync) + Flags.setSwiftAsync(); if (Args[i].IsSwiftError) Flags.setSwiftError(); if (Args[i].IsCFGuardTarget) @@ -9989,6 +9992,8 @@ Flags.setSRet(); if (Arg.hasAttribute(Attribute::SwiftSelf)) Flags.setSwiftSelf(); + if (Arg.hasAttribute(Attribute::SwiftAsync)) + Flags.setSwiftAsync(); if (Arg.hasAttribute(Attribute::SwiftError)) Flags.setSwiftError(); if (Arg.hasAttribute(Attribute::ByVal)) Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -115,6 +115,7 @@ IsNest = Attrs.hasParamAttribute(ArgIdx, Attribute::Nest); IsReturned = Attrs.hasParamAttribute(ArgIdx, Attribute::Returned); IsSwiftSelf = Attrs.hasParamAttribute(ArgIdx, Attribute::SwiftSelf); + IsSwiftAsync = Attrs.hasParamAttr(ArgIdx, Attribute::SwiftAsync); IsSwiftError = Attrs.hasParamAttribute(ArgIdx, Attribute::SwiftError); Alignment = Attrs.getParamStackAlignment(ArgIdx); Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -384,6 +384,8 @@ return "swifterror"; if (hasAttribute(Attribute::SwiftSelf)) return "swiftself"; + if (hasAttribute(Attribute::SwiftAsync)) + return "swiftasync"; if (hasAttribute(Attribute::InaccessibleMemOnly)) return "inaccessiblememonly"; if (hasAttribute(Attribute::InaccessibleMemOrArgMemOnly)) Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1910,6 +1910,7 @@ bool SawReturned = false; bool SawSRet = false; bool SawSwiftSelf = false; + bool SawSwiftAsync = false; bool SawSwiftError = false; // Verify return value attributes. @@ -1924,11 +1925,12 @@ !RetAttrs.hasAttribute(Attribute::Preallocated) && !RetAttrs.hasAttribute(Attribute::ByRef) && !RetAttrs.hasAttribute(Attribute::SwiftSelf) && + !RetAttrs.hasAttribute(Attribute::SwiftAsync) && !RetAttrs.hasAttribute(Attribute::SwiftError)), "Attributes 'byval', 'inalloca', 'preallocated', 'byref', " "'nest', 'sret', 'nocapture', 'nofree', " - "'returned', 'swiftself', and 'swifterror' do not apply to return " - "values!", + "'returned', 'swiftself', 'swiftasync', and 'swifterror'" + " do not apply to return values!", V); Assert((!RetAttrs.hasAttribute(Attribute::ReadOnly) && !RetAttrs.hasAttribute(Attribute::WriteOnly) && @@ -1976,6 +1978,11 @@ SawSwiftSelf = true; } + if (ArgAttrs.hasAttribute(Attribute::SwiftAsync)) { + Assert(!SawSwiftAsync, "Cannot have multiple 'swiftasync' parameters!", V); + SawSwiftAsync = true; + } + if (ArgAttrs.hasAttribute(Attribute::SwiftError)) { Assert(!SawSwiftError, "Cannot have multiple 'swifterror' parameters!", V); @@ -3368,9 +3375,10 @@ static AttrBuilder getParameterABIAttributes(int I, AttributeList Attrs) { static const Attribute::AttrKind ABIAttrs[] = { - Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, - Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError, - Attribute::Preallocated, Attribute::ByRef, Attribute::StackAlignment}; + Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, + Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftAsync, + Attribute::SwiftError, Attribute::SwiftAsync, Attribute::Preallocated, + Attribute::ByRef}; AttrBuilder Copy; for (auto AK : ABIAttrs) { if (Attrs.hasParamAttribute(I, AK)) Index: llvm/lib/Target/AArch64/AArch64CallingConvention.td =================================================================== --- llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -69,6 +69,10 @@ // A SwiftError is passed in X21. CCIfSwiftError>>, + // Pass SwiftAsync in an otherwise callee saved register so that it will be + // preserved for normal function calls. + CCIfSwiftAsync>>, + CCIfConsecutiveRegs>, CCIfType<[nxv16i8, nxv8i16, nxv4i32, nxv2i64, nxv2f16, nxv4f16, nxv8f16, @@ -203,6 +207,10 @@ // A SwiftError is passed in X21. CCIfSwiftError>>, + // Pass SwiftAsync in an otherwise callee saved register so that it will be + // preserved for normal function calls. + CCIfSwiftAsync>>, + CCIfConsecutiveRegs>, // Handle i1, i8, i16, i32, i64, f32, f64 and v2f64 by passing in registers, Index: llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -86,6 +86,8 @@ unsigned N); bool expandCALL_RVMARKER(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI); + bool expandStoreSwiftAsyncContext(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI); }; } // end anonymous namespace @@ -696,6 +698,63 @@ return true; } +bool AArch64ExpandPseudo::expandStoreSwiftAsyncContext( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) { + Register CtxReg = MBBI->getOperand(0).getReg(); + Register BaseReg = MBBI->getOperand(1).getReg(); + int Offset = MBBI->getOperand(2).getImm(); + DebugLoc DL(MBBI->getDebugLoc()); + auto &STI = MBB.getParent()->getSubtarget(); + + if (STI.getTargetTriple().getArchName() != "arm64e") { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::STRXui)) + .addUse(CtxReg) + .addUse(BaseReg) + .addImm(Offset / 8) + .setMIFlag(MachineInstr::FrameSetup); + MBBI->eraseFromParent(); + return true; + } + + // We need to sign the context in an address-discriminated way. 0xc31a is a + // fixed random value, chosen as part of the ABI. + // add x16, xBase, #Offset + // movk x16, #0xc31a, lsl #48 + // mov x17, x22/xzr + // pacdb x17, x16 + // str x17, [xBase, #Offset] + unsigned Opc = Offset >= 0 ? AArch64::ADDXri : AArch64::SUBXri; + BuildMI(MBB, MBBI, DL, TII->get(Opc), AArch64::X16) + .addUse(BaseReg) + .addImm(abs(Offset)) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), AArch64::X16) + .addUse(AArch64::X16) + .addImm(0xc31a) + .addImm(48) + .setMIFlag(MachineInstr::FrameSetup); + // We're not allowed to clobber X22 (and couldn't clobber XZR if we tried), so + // move it somewhere before signing. + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), AArch64::X17) + .addUse(AArch64::XZR) + .addUse(CtxReg) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACDB), AArch64::X17) + .addUse(AArch64::X17) + .addUse(AArch64::X16) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::STRXui)) + .addUse(AArch64::X17) + .addUse(BaseReg) + .addImm(Offset / 8) + .setMIFlag(MachineInstr::FrameSetup); + + MBBI->eraseFromParent(); + return true; +} + /// If MBBI references a pseudo instruction that should be expanded here, /// do the expansion and return true. Otherwise return false. bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB, @@ -1110,6 +1169,8 @@ return expandSVESpillFill(MBB, MBBI, AArch64::LDR_ZXI, 2); case AArch64::BLR_RVMARKER: return expandCALL_RVMARKER(MBB, MBBI); + case AArch64::StoreSwiftAsyncContext: + return expandStoreSwiftAsyncContext(MBB, MBBI); } return false; } Index: llvm/lib/Target/AArch64/AArch64FastISel.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -2899,6 +2899,7 @@ Arg.hasAttribute(Attribute::InReg) || Arg.hasAttribute(Attribute::StructRet) || Arg.hasAttribute(Attribute::SwiftSelf) || + Arg.hasAttribute(Attribute::SwiftAsync) || Arg.hasAttribute(Attribute::SwiftError) || Arg.hasAttribute(Attribute::Nest)) return false; @@ -3157,7 +3158,7 @@ for (auto Flag : CLI.OutFlags) if (Flag.isInReg() || Flag.isSRet() || Flag.isNest() || Flag.isByVal() || - Flag.isSwiftSelf() || Flag.isSwiftError()) + Flag.isSwiftSelf() || Flag.isSwiftAsync() || Flag.isSwiftError()) return false; // Set up the argument vectors. Index: llvm/lib/Target/AArch64/AArch64FrameLowering.h =================================================================== --- llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -67,10 +67,13 @@ bool hasFP(const MachineFunction &MF) const override; bool hasReservedCallFrame(const MachineFunction &MF) const override; - bool - assignCalleeSavedSpillSlots(MachineFunction &MF, - const TargetRegisterInfo *TRI, - std::vector &CSI) const override; + bool hasSwiftExtendedFrame(const MachineFunction &MF) const; + + bool assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector &CSI, + unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const override; void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const override; Index: llvm/lib/Target/AArch64/AArch64FrameLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -47,8 +47,9 @@ // | callee-saved gpr registers | <--. // | | | On Darwin platforms these // |- - - - - - - - - - - - - - - - - -| | callee saves are swapped, -// | | | (frame record first) -// | prev_fp, prev_lr | <--' +// | prev_lr | | (frame record first) +// | prev_fp | <--' +// | async context if needed | // | (a.k.a. "frame record") | // |-----------------------------------| <- fp(=x29) // | | @@ -940,6 +941,16 @@ SEH->eraseFromParent(); } + // If the first store isn't right where we want SP then we can't fold the + // update in so create a normal arithmetic instruction instead. + if (MBBI->getOperand(MBBI->getNumOperands() - 1).getImm() != 0) { + emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, + StackOffset::getFixed(CSStackSizeInc), TII, + InProlog ? MachineInstr::FrameSetup + : MachineInstr::FrameDestroy); + return std::prev(MBBI); + } + MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(NewOpc)); MIB.addReg(AArch64::SP, RegState::Define); @@ -1128,6 +1139,18 @@ .setMIFlags(MachineInstr::FrameSetup); } + // We signal the presence of a Swift extended frame to external tools by + // storing FP with 0b0001 in bits 63:60. In normal userland operation a simple + // 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); + } + // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. if (MF.getFunction().getCallingConv() == CallingConv::GHC) @@ -1234,6 +1257,20 @@ if (CombineSPBump) FPOffset += AFI->getLocalStackSize(); + if (AFI->hasSwiftAsyncContext()) { + // Before we update the live FP we have to ensure there's a valid (or + // null) asynchronous context in its slot just before FP in the frame + // record, so store it now. + const auto &Attrs = MF.getFunction().getAttributes(); + bool HaveInitialContext = Attrs.hasAttrSomewhere(Attribute::SwiftAsync); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::StoreSwiftAsyncContext)) + .addUse(HaveInitialContext ? AArch64::X22 : AArch64::XZR) + .addUse(AArch64::SP) + .addImm(FPOffset - 8) + .setMIFlags(MachineInstr::FrameSetup); + } + if (HomPrologEpilog) { auto Prolog = MBBI; --Prolog; @@ -1745,6 +1782,18 @@ .setMIFlag(MachineInstr::FrameDestroy); } + if (hasFP(MF) && AFI->hasSwiftAsyncContext()) { + // We need to reset FP to its untagged state on return. Bit 60 is currently + // used to show the presence of an extended frame. + + // BIC x29, x29, #0x1000_0000_0000_0000 + BuildMI(MBB, MBB.getFirstTerminator(), DL, TII->get(AArch64::ANDXri), + AArch64::FP) + .addUse(AArch64::FP) + .addImm(0x10fe) + .setMIFlag(MachineInstr::FrameDestroy); + } + const StackOffset &SVEStackSize = getSVEStackSize(MF); // If there is a single SP update, insert it before the ret and we're done. @@ -2309,6 +2358,12 @@ else ByteOffset += StackFillDir * (RPI.isPaired() ? 2 * Scale : Scale); + // Swift's async context is directly before FP, so allocate an extra + // 8 bytes for it. + if (NeedsFrameRecord && AFI->hasSwiftAsyncContext() && + RPI.Reg2 == AArch64::FP) + ByteOffset += StackFillDir * 8; + assert(!(RPI.isScalable() && RPI.isPaired()) && "Paired spill/fill instructions don't exist for SVE vectors"); @@ -2332,6 +2387,12 @@ // If filling top down (default), we want the offset after incrementing it. // If fillibg bootom up (WinCFI) we need the original offset. int Offset = NeedsWinCFI ? OffsetPre : OffsetPost; + + // The FP, LR pair goes 8 bytes into our expanded 24-byte slot so that the + // Swift context can directly precede FP. + if (NeedsFrameRecord && AFI->hasSwiftAsyncContext() && + RPI.Reg2 == AArch64::FP) + Offset += 8; RPI.Offset = Offset / Scale; assert(((!RPI.isScalable() && RPI.Offset >= -64 && RPI.Offset <= 63) || @@ -2800,6 +2861,12 @@ // Adding the size of additional 64bit GPR saves. CSStackSize += 8 * (SavedRegs.count() - NumSavedRegs); + + // A Swift asynchronous context extends the frame record with a pointer + // directly before FP. + if (hasFP(MF) && AFI->hasSwiftAsyncContext()) + CSStackSize += 8; + uint64_t AlignedCSStackSize = alignTo(CSStackSize, 16); LLVM_DEBUG(dbgs() << "Estimated stack frame size: " << EstimatedStackSize + AlignedCSStackSize @@ -2817,8 +2884,9 @@ } bool AArch64FrameLowering::assignCalleeSavedSpillSlots( - MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector &CSI) const { + MachineFunction &MF, const TargetRegisterInfo *RegInfo, + std::vector &CSI, unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const { bool NeedsWinCFI = needsWinCFI(MF); // To match the canonical windows frame layout, reverse the list of // callee saved registers to get them laid out by PrologEpilogInserter @@ -2827,8 +2895,37 @@ // the top, thus have the CSI array start from the highest registers.) if (NeedsWinCFI) std::reverse(CSI.begin(), CSI.end()); - // Let the generic code do the rest of the setup. - return false; + + if (CSI.empty()) + return true; // Early exit if no callee saved registers are modified! + + // Now that we know which registers need to be saved and restored, allocate + // stack slots for them. + MachineFrameInfo &MFI = MF.getFrameInfo(); + auto *AFI = MF.getInfo(); + for (auto &CS : CSI) { + Register Reg = CS.getReg(); + const TargetRegisterClass *RC = RegInfo->getMinimalPhysRegClass(Reg); + + int FrameIdx; + unsigned Size = RegInfo->getSpillSize(*RC); + + Align Alignment(RegInfo->getSpillAlign(*RC)); + FrameIdx = MFI.CreateStackObject(Size, Alignment, true); + CS.setFrameIdx(FrameIdx); + + if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; + if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + + // Grab 8 bytes below FP for the extended asynchronous frame info. + if (hasFP(MF) && AFI->hasSwiftAsyncContext() && Reg == AArch64::FP) { + FrameIdx = MFI.CreateStackObject(8, Alignment, true); + AFI->setSwiftAsyncContextFrameIdx(FrameIdx); + if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; + if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + } + } + return true; } bool AArch64FrameLowering::enableStackSlotScavenging( Index: llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -3889,6 +3889,18 @@ if (tryMULLV64LaneV128(IntNo, Node)) return; break; + case Intrinsic::swift_async_context_addr: { + SDLoc DL(Node); + CurDAG->SelectNodeTo(Node, AArch64::SUBXri, MVT::i64, + CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, + AArch64::FP, MVT::i64), + CurDAG->getTargetConstant(8, DL, MVT::i32), + CurDAG->getTargetConstant(0, DL, MVT::i32)); + auto &MF = CurDAG->getMachineFunction(); + MF.getFrameInfo().setFrameAddressIsTaken(true); + MF.getInfo()->setHasSwiftAsyncContext(true); + return; + } } break; } Index: llvm/lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -4793,6 +4793,9 @@ continue; } + if (Ins[i].Flags.isSwiftAsync()) + MF.getInfo()->setHasSwiftAsyncContext(true); + SDValue ArgValue; if (VA.isRegLoc()) { // Arguments stored in registers. Index: llvm/lib/Target/AArch64/AArch64InstrFormats.td =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -1957,7 +1957,8 @@ } class SignAuthOneData opcode_prefix, bits<2> opcode, string asm> - : I<(outs GPR64:$Rd), (ins GPR64sp:$Rn), asm, "\t$Rd, $Rn", "", + : I<(outs GPR64:$Rd), (ins GPR64:$src, GPR64sp:$Rn), asm, "\t$Rd, $Rn", + "$Rd = $src", []>, Sched<[WriteI, ReadI]> { bits<5> Rd; @@ -1970,7 +1971,8 @@ } class SignAuthZero opcode_prefix, bits<2> opcode, string asm> - : I<(outs GPR64:$Rd), (ins), asm, "\t$Rd", "", []>, Sched<[]> { + : I<(outs GPR64:$Rd), (ins GPR64:$src), asm, "\t$Rd", "$Rd = $src", + []>, Sched<[]> { bits<5> Rd; let Inst{31-15} = 0b11011010110000010; let Inst{14-12} = opcode_prefix; Index: llvm/lib/Target/AArch64/AArch64InstrInfo.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -135,6 +135,9 @@ case AArch64::SPACE: NumBytes = MI.getOperand(1).getImm(); break; + case AArch64::StoreSwiftAsyncContext: + NumBytes = 20; + break; case TargetOpcode::BUNDLE: NumBytes = getInstBundleLength(MI); break; @@ -2640,6 +2643,13 @@ MinOffset = 0; MaxOffset = 4095; break; + case AArch64::StoreSwiftAsyncContext: + // Store is an STRXui, but there might be an ADDXri in the expansion too. + Scale = TypeSize::Fixed(1); + Width = 8; + MinOffset = 0; + MaxOffset = 4095; + break; case AArch64::LDPWi: case AArch64::LDPSi: case AArch64::LDNPWi: Index: llvm/lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -7979,6 +7979,11 @@ def : ST64BPattern; } +let Defs = [X16, X17], mayStore = 1, isCodeGenOnly = 1 in +def StoreSwiftAsyncContext + : Pseudo<(outs), (ins GPR64:$ctx, GPR64sp:$base, simm9:$offset), + []>, Sched<[]>; + include "AArch64InstrAtomics.td" include "AArch64SVEInstrInfo.td" Index: llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h =================================================================== --- llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -159,6 +159,14 @@ /// indirect branch destinations. bool BranchTargetEnforcement = false; + /// Whether this function has an extended frame record [Ctx, FP, LR]. If so, + /// bit 60 of the in-memory FP will be 1 to enable other tools to detect the + /// extended record. + bool HasSwiftAsyncContext = false; + + /// The stack slot where the Swift asynchronous context is stored. + int SwiftAsyncContextFrameIdx = std::numeric_limits::max(); + public: explicit AArch64FunctionInfo(MachineFunction &MF); @@ -239,6 +247,13 @@ MaxOffset = std::max(Offset + ObjSize, MaxOffset); } + if (SwiftAsyncContextFrameIdx != std::numeric_limits::max()) { + int64_t Offset = MFI.getObjectOffset(getSwiftAsyncContextFrameIdx()); + int64_t ObjSize = MFI.getObjectSize(getSwiftAsyncContextFrameIdx()); + MinOffset = std::min(Offset, MinOffset); + MaxOffset = std::max(Offset + ObjSize, MaxOffset); + } + unsigned Size = alignTo(MaxOffset - MinOffset, 16); assert((!HasCalleeSavedStackSize || getCalleeSavedStackSize() == Size) && "Invalid size calculated for callee saves"); @@ -372,6 +387,16 @@ bool branchTargetEnforcement() const { return BranchTargetEnforcement; } + void setHasSwiftAsyncContext(bool HasContext) { + HasSwiftAsyncContext = HasContext; + } + bool hasSwiftAsyncContext() const { return HasSwiftAsyncContext; } + + void setSwiftAsyncContextFrameIdx(int FI) { + SwiftAsyncContextFrameIdx = FI; + } + int getSwiftAsyncContextFrameIdx() const { return SwiftAsyncContextFrameIdx; } + private: // Hold the lists of LOHs. MILOHContainer LOHContainerSet; Index: llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp +++ llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp @@ -520,6 +520,9 @@ ArgInfo OrigArg{VRegs[i], Arg}; setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F); + if (Arg.hasAttribute(Attribute::SwiftAsync)) + MF.getInfo()->setHasSwiftAsyncContext(true); + splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv()); ++i; } Index: llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp =================================================================== --- llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -5017,6 +5017,17 @@ I.eraseFromParent(); return true; } + case Intrinsic::swift_async_context_addr: + auto Sub = MIB.buildInstr(AArch64::SUBXri, {I.getOperand(0).getReg()}, + {Register(AArch64::FP)}) + .addImm(8) + .addImm(0); + constrainSelectedInstRegOperands(*Sub, TII, TRI, RBI); + + MF->getFrameInfo().setFrameAddressIsTaken(true); + MF->getInfo()->setHasSwiftAsyncContext(true); + I.eraseFromParent(); + return true; } return false; } Index: llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp =================================================================== --- llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -574,6 +574,7 @@ unsigned StackSize = 0; uint32_t CompactUnwindEncoding = 0; + int CurOffset = 0; for (size_t i = 0, e = Instrs.size(); i != e; ++i) { const MCCFIInstruction &Inst = Instrs[i]; @@ -603,6 +604,9 @@ assert(FPPush.getOperation() == MCCFIInstruction::OpOffset && "Frame pointer not pushed!"); + assert(FPPush.getOffset() + 8 == LRPush.getOffset()); + CurOffset = FPPush.getOffset(); + unsigned LRReg = *MRI.getLLVMRegNum(LRPush.getRegister(), true); unsigned FPReg = *MRI.getLLVMRegNum(FPPush.getRegister(), true); @@ -629,11 +633,19 @@ if (i + 1 == e) return CU::UNWIND_ARM64_MODE_DWARF; + if (CurOffset != 0 && Inst.getOffset() != CurOffset - 8) + return CU::UNWIND_ARM64_MODE_DWARF; + CurOffset = Inst.getOffset(); + const MCCFIInstruction &Inst2 = Instrs[++i]; if (Inst2.getOperation() != MCCFIInstruction::OpOffset) return CU::UNWIND_ARM64_MODE_DWARF; unsigned Reg2 = *MRI.getLLVMRegNum(Inst2.getRegister(), true); + if (Inst2.getOffset() != CurOffset - 8) + return CU::UNWIND_ARM64_MODE_DWARF; + CurOffset = Inst2.getOffset(); + // N.B. The encodings must be in register number order, and the X // registers before the D registers. Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -929,6 +929,7 @@ case Attribute::StructRet: case Attribute::SwiftError: case Attribute::SwiftSelf: + case Attribute::SwiftAsync: case Attribute::WillReturn: case Attribute::WriteOnly: case Attribute::ZExt: Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -447,6 +447,12 @@ ret void } +; CHECK: define void @f76(i8* swiftasync %0) +define void @f76(i8* swiftasync %0) +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } Index: llvm/test/Bitcode/compatibility.ll =================================================================== --- llvm/test/Bitcode/compatibility.ll +++ llvm/test/Bitcode/compatibility.ll @@ -552,6 +552,12 @@ ; CHECK: declare void @f.param.dereferenceable_or_null(i8* dereferenceable_or_null(4)) declare void @f.param.stack_align([2 x double] alignstack(16)) ; CHECK: declare void @f.param.stack_align([2 x double] alignstack(16)) +declare void @f.param.swiftself(i8* swiftself) +; CHECK: declare void @f.param.swiftself(i8* swiftself) +declare void @f.param.swiftasync(i8* swiftasync) +; CHECK: declare void @f.param.swiftasync(i8* swiftasync) +declare void @f.param.swifterror(i8** swifterror) +; CHECK: declare void @f.param.swifterror(i8** swifterror) ; Functions -- unnamed_addr and local_unnamed_addr declare void @f.unnamed_addr() unnamed_addr Index: llvm/test/CodeGen/AArch64/swift-async-reg.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/swift-async-reg.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=arm64-apple-ios %s -o - | FileCheck %s +; RUN: llc -mtriple=arm64-apple-ios %s -o - -global-isel | FileCheck %s +; RUN: llc -mtriple=arm64-apple-ios %s -o - -fast-isel | FileCheck %s + +define i8* @argument(i8* swiftasync %in) { +; CHECK-LABEL: argument: +; CHECK: mov x0, x22 + + ret i8* %in +} + +define void @call(i8* %in) { +; CHECK-LABEL: call: +; CHECK: mov x22, x0 + + call i8* @argument(i8* swiftasync %in) + ret void +} Index: llvm/test/CodeGen/AArch64/swift-async-unwind.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/swift-async-unwind.ll @@ -0,0 +1,13 @@ +; RUN: llc -mtriple=arm64-apple-ios %s -filetype=obj -o - | llvm-objdump --unwind-info - | FileCheck %s + +; Swift asynchronous context is incompatible with the compact unwind encoding +; that currently exists and assumes callee-saved registers are right next to FP +; in a particular order. This isn't a problem now because C++ exceptions aren't +; allowed to unwind through Swift code, but at least make sure the compact info +; says to use DWARF correctly. + +; CHECK: compact encoding: 0x03000000 +define void @foo(i8* swiftasync %in) "frame-pointer"="all" { + call void asm sideeffect "", "~{x23}"() + ret void +} Index: llvm/test/CodeGen/AArch64/swift-async.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/swift-async.ll @@ -0,0 +1,157 @@ +; 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 + +; Important details in prologue: +; * x22 is stored just below x29 +; * Enough stack space is allocated for everything +define void @simple(i8* swiftasync %ctx) "frame-pointer"="all" { +; CHECK-LABEL: simple: +; CHECK: orr x29, x29, #0x100000000000000 +; CHECK: sub sp, sp, #32 +; CHECK: stp x29, x30, [sp, #16] + +; CHECK-NOAUTH: str x22, [sp, #8] +; CHECK-AUTH: add x16, sp, #8 +; CHECK-AUTH: movk x16, #49946, lsl #48 +; CHECK-AUTH: mov x17, x22 +; CHECK-AUTH: pacdb x17, x16 +; CHECK-AUTH: str x17, [sp, #8] + +; CHECK: add x29, sp, #16 +; CHECK: .cfi_def_cfa w29, 16 +; CHECK: .cfi_offset w30, -8 +; CHECK: .cfi_offset w29, -16 + +;[...] + +; CHECK: ldp x29, x30, [sp, #16] +; CHECK: and x29, x29, #0xefffffffffffffff +; CHECK: add sp, sp, #32 + + ret void +} + +define void @more_csrs(i8* swiftasync %ctx) "frame-pointer"="all" { +; CHECK-LABEL: more_csrs: +; CHECK: orr x29, x29, #0x100000000000000 +; CHECK: sub sp, sp, #48 +; CHECK: stp x24, x23, [sp, #8] +; CHECK: stp x29, x30, [sp, #32] + +; CHECK-NOAUTH: str x22, [sp, #24] +; CHECK-AUTH: add x16, sp, #24 +; CHECK-AUTH: movk x16, #49946, lsl #48 +; CHECK-AUTH: mov x17, x22 +; CHECK-AUTH: pacdb x17, x16 +; CHECK-AUTH: str x17, [sp, #24] + +; CHECK: add x29, sp, #32 +; CHECK: .cfi_def_cfa w29, 16 +; CHECK: .cfi_offset w30, -8 +; CHECK: .cfi_offset w29, -16 +; CHECK: .cfi_offset w23, -32 + +; [...] + +; CHECK: ldp x29, x30, [sp, #32] +; CHECK: ldp x24, x23, [sp, #8] +; CHECK: and x29, x29, #0xefffffffffffffff +; CHECK: add sp, sp, #48 + call void asm sideeffect "", "~{x23}"() + ret void +} + +define void @locals(i8* swiftasync %ctx) "frame-pointer"="all" { +; CHECK-LABEL: locals: +; CHECK: orr x29, x29, #0x100000000000000 +; CHECK: sub sp, sp, #64 +; CHECK: stp x29, x30, [sp, #48] + +; CHECK-NOAUTH: str x22, [sp, #40] +; CHECK-AUTH: add x16, sp, #40 +; CHECK-AUTH: movk x16, #49946, lsl #48 +; CHECK-AUTH: mov x17, x22 +; CHECK-AUTH: pacdb x17, x16 +; CHECK-AUTH: str x17, [sp, #40] + +; CHECK: add x29, sp, #48 +; CHECK: .cfi_def_cfa w29, 16 +; CHECK: .cfi_offset w30, -8 +; CHECK: .cfi_offset w29, -16 + +; CHECK: mov x0, sp +; CHECK: bl _bar + +; [...] + +; CHECK: ldp x29, x30, [sp, #48] +; CHECK: and x29, x29, #0xefffffffffffffff +; CHECK: add sp, sp, #64 + %var = alloca i32, i32 10 + call void @bar(i32* %var) + ret void +} + +define void @use_input_context(i8* swiftasync %ctx, i8** %ptr) "frame-pointer"="all" { +; CHECK-LABEL: use_input_context: + +; CHECK-NOAUTH: str x22, [sp +; CHECK-AUTH: mov x17, x22 + +; CHECK-NOT: x22 +; CHECK: str x22, [x0] + + store i8* %ctx, i8** %ptr + ret void +} + +define i8** @context_in_func() "frame-pointer"="non-leaf" { +; CHECK-LABEL: context_in_func: + +; CHECK-NOAUTH: str xzr, [sp, #8] +; CHECK-AUTH: add x16, sp, #8 +; CHECK-AUTH: movk x16, #49946, lsl #48 +; CHECK-AUTH: mov x17, xzr +; CHECK-AUTH: pacdb x17, x16 +; CHECK-AUTH: str x17, [sp, #8] + + %ptr = call i8** @llvm.swift.async.context.addr() + ret i8** %ptr +} + +define void @write_frame_context(i8* swiftasync %ctx, i8* %newctx) "frame-pointer"="non-leaf" { +; CHECK-LABEL: write_frame_context: +; CHECK: sub x[[ADDR:[0-9]+]], x29, #8 +; CHECK: str x0, [x[[ADDR]]] + %ptr = call i8** @llvm.swift.async.context.addr() + store i8* %newctx, i8** %ptr + ret void +} + +define void @simple_fp_elim(i8* swiftasync %ctx) "frame-pointer"="non-leaf" { +; CHECK-LABEL: simple_fp_elim: +; CHECK-NOT: orr x29, x29, #0x100000000000000 + + ret void +} + +define void @large_frame(i8* swiftasync %ctx) "frame-pointer"="all" { +; CHECK-LABEL: large_frame: +; CHECK: sub sp, sp, #48 +; CHECK: stp x28, x27, [sp, #8] +; CHECK: stp x29, x30, [sp, #32] +; CHECK-NOAUTH: str x22, [sp, #24] +; CHECK: add x29, sp, #32 +; CHECK: sub sp, sp, #1024 +; [...] +; CHECK: add sp, sp, #1024 +; CHECK: ldp x29, x30, [sp, #32] +; CHECK: ldp x28, x27, [sp, #8] +; CHECK: ret + %var = alloca i8, i32 1024 + ret void +} + +declare void @bar(i32*) +declare i8** @llvm.swift.async.context.addr() Index: llvm/test/Verifier/swiftasync.ll =================================================================== --- /dev/null +++ llvm/test/Verifier/swiftasync.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @a(i32* swiftasync %a, i32* swiftasync %b) +; CHECK: Cannot have multiple 'swiftasync' parameters!