diff --git a/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp b/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp index 24f023a3d70e7b..4ae2e48af337fd 100644 --- a/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp +++ b/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp @@ -13,6 +13,7 @@ #include "AArch64InstrInfo.h" #include "AArch64Subtarget.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/CodeGen/IndirectThunks.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" @@ -23,6 +24,7 @@ #include "llvm/IR/DebugLoc.h" #include "llvm/Pass.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Target/TargetMachine.h" #include @@ -32,17 +34,103 @@ using namespace llvm; #define AARCH64_SLS_HARDENING_NAME "AArch64 sls hardening pass" -static const char SLSBLRNamePrefix[] = "__llvm_slsblr_thunk_"; +// Common name prefix of all thunks generated by this pass. +// +// The generic form is +// __llvm_slsblr_thunk_xN for BLR thunks +// __llvm_slsblr_thunk_(aaz|abz)_xN for BLRAAZ and BLRABZ thunks +// __llvm_slsblr_thunk_(aa|ab)_xN_xM for BLRAA and BLRAB thunks +static constexpr StringRef CommonNamePrefix = "__llvm_slsblr_thunk_"; namespace { -// Set of inserted thunks: bitmask with bits corresponding to -// indexes in SLSBLRThunks array. -typedef uint32_t ThunksSet; +struct ThunkKind { + enum ThunkKindId { + ThunkBR, + ThunkBRAA, + ThunkBRAB, + ThunkBRAAZ, + ThunkBRABZ, + }; + + ThunkKindId Id; + StringRef NameInfix; + bool HasXmOperand; + bool NeedsPAuth; + + // Opcode to perform indirect jump from inside the thunk. + unsigned BROpcode; + + static const ThunkKind BR; + static const ThunkKind BRAA; + static const ThunkKind BRAB; + static const ThunkKind BRAAZ; + static const ThunkKind BRABZ; +}; + +// Set of inserted thunks. +class ThunksSet { +public: + static constexpr unsigned NumXRegisters = 32; + + // Given Xn register, returns n. + static unsigned indexOfXReg(Register Xn); + // Given n, returns Xn register. + static Register xRegByIndex(unsigned N); + + ThunksSet &operator|=(const ThunksSet &Other) { + BLRThunks |= Other.BLRThunks; + BLRAAZThunks |= Other.BLRAAZThunks; + BLRABZThunks |= Other.BLRABZThunks; + for (unsigned I = 0; I < NumXRegisters; ++I) + BLRAAThunks[I] |= Other.BLRAAThunks[I]; + for (unsigned I = 0; I < NumXRegisters; ++I) + BLRABThunks[I] |= Other.BLRABThunks[I]; + + return *this; + } + + bool get(ThunkKind::ThunkKindId Kind, Register Xn, Register Xm) { + uint32_t XnBit = 1u << indexOfXReg(Xn); + return getBitmask(Kind, Xm) & XnBit; + } + + void set(ThunkKind::ThunkKindId Kind, Register Xn, Register Xm) { + uint32_t XnBit = 1u << indexOfXReg(Xn); + getBitmask(Kind, Xm) |= XnBit; + } + +private: + // Bitmasks representing operands used, with n-th bit corresponding to Xn + // register operand. If the instruction has a second operand (Xm), an array + // of bitmasks is used, indexed by m. + // Indexes corresponding to the forbidden x16, x17 and x30 registers are + // always unset, for simplicity there are no holes. + uint32_t BLRThunks = 0; + uint32_t BLRAAZThunks = 0; + uint32_t BLRABZThunks = 0; + uint32_t BLRAAThunks[NumXRegisters] = {}; + uint32_t BLRABThunks[NumXRegisters] = {}; + + uint32_t &getBitmask(ThunkKind::ThunkKindId Kind, Register Xm) { + switch (Kind) { + case ThunkKind::ThunkBR: + return BLRThunks; + case ThunkKind::ThunkBRAAZ: + return BLRAAZThunks; + case ThunkKind::ThunkBRABZ: + return BLRABZThunks; + case ThunkKind::ThunkBRAA: + return BLRAAThunks[indexOfXReg(Xm)]; + case ThunkKind::ThunkBRAB: + return BLRABThunks[indexOfXReg(Xm)]; + } + } +}; struct SLSHardeningInserter : ThunkInserter { public: - const char *getThunkPrefix() { return SLSBLRNamePrefix; } + const char *getThunkPrefix() { return CommonNamePrefix.data(); } bool mayUseThunk(const MachineFunction &MF) { ComdatThunks &= !MF.getSubtarget().hardenSlsNoComdat(); // We are inserting barriers aside from thunk calls, so @@ -68,6 +156,57 @@ struct SLSHardeningInserter : ThunkInserter { } // end anonymous namespace +const ThunkKind ThunkKind::BR = {ThunkBR, "", false, false, AArch64::BR}; +const ThunkKind ThunkKind::BRAA = {ThunkBRAA, "aa_", true, true, AArch64::BRAA}; +const ThunkKind ThunkKind::BRAB = {ThunkBRAB, "ab_", true, true, AArch64::BRAB}; +const ThunkKind ThunkKind::BRAAZ = {ThunkBRAAZ, "aaz_", false, true, + AArch64::BRAAZ}; +const ThunkKind ThunkKind::BRABZ = {ThunkBRABZ, "abz_", false, true, + AArch64::BRABZ}; + +static const ThunkKind *getThunkKind(unsigned OriginalOpcode) { + switch (OriginalOpcode) { + case AArch64::BLR: + case AArch64::BLRNoIP: + return &ThunkKind::BR; + case AArch64::BLRAA: + return &ThunkKind::BRAA; + case AArch64::BLRAB: + return &ThunkKind::BRAB; + case AArch64::BLRAAZ: + return &ThunkKind::BRAAZ; + case AArch64::BLRABZ: + return &ThunkKind::BRABZ; + } + return nullptr; +} + +static bool isBLR(const MachineInstr &MI) { + return getThunkKind(MI.getOpcode()) != nullptr; +} + +unsigned ThunksSet::indexOfXReg(Register Reg) { + assert(AArch64::GPR64RegClass.contains(Reg)); + assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR); + + // Most Xn registers have consequent ids, except for FP and XZR. + unsigned Result = (unsigned)Reg - (unsigned)AArch64::X0; + if (Reg == AArch64::FP) + Result = 29; + else if (Reg == AArch64::XZR) + Result = 31; + + assert(Result < NumXRegisters && "Internal register numbering changed"); + assert(AArch64::GPR64RegClass.getRegister(Result).id() == Reg && + "Internal register numbering changed"); + + return Result; +} + +Register ThunksSet::xRegByIndex(unsigned N) { + return AArch64::GPR64RegClass.getRegister(N); +} + static void insertSpeculationBarrier(const AArch64Subtarget *ST, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, @@ -104,22 +243,6 @@ ThunksSet SLSHardeningInserter::insertThunks(MachineModuleInfo &MMI, return ExistingThunks; } -static bool isBLR(const MachineInstr &MI) { - switch (MI.getOpcode()) { - case AArch64::BLR: - case AArch64::BLRNoIP: - return true; - case AArch64::BLRAA: - case AArch64::BLRAB: - case AArch64::BLRAAZ: - case AArch64::BLRABZ: - llvm_unreachable("Currently, LLVM's code generator does not support " - "producing BLRA* instructions. Therefore, there's no " - "support in this pass for those instructions."); - } - return false; -} - bool SLSHardeningInserter::hardenReturnsAndBRs(MachineModuleInfo &MMI, MachineBasicBlock &MBB) { const AArch64Subtarget *ST = @@ -139,52 +262,49 @@ bool SLSHardeningInserter::hardenReturnsAndBRs(MachineModuleInfo &MMI, return Modified; } -static const unsigned NumPermittedRegs = 29; -static const struct ThunkNameAndReg { - const char* Name; - Register Reg; -} SLSBLRThunks[NumPermittedRegs] = { - { "__llvm_slsblr_thunk_x0", AArch64::X0}, - { "__llvm_slsblr_thunk_x1", AArch64::X1}, - { "__llvm_slsblr_thunk_x2", AArch64::X2}, - { "__llvm_slsblr_thunk_x3", AArch64::X3}, - { "__llvm_slsblr_thunk_x4", AArch64::X4}, - { "__llvm_slsblr_thunk_x5", AArch64::X5}, - { "__llvm_slsblr_thunk_x6", AArch64::X6}, - { "__llvm_slsblr_thunk_x7", AArch64::X7}, - { "__llvm_slsblr_thunk_x8", AArch64::X8}, - { "__llvm_slsblr_thunk_x9", AArch64::X9}, - { "__llvm_slsblr_thunk_x10", AArch64::X10}, - { "__llvm_slsblr_thunk_x11", AArch64::X11}, - { "__llvm_slsblr_thunk_x12", AArch64::X12}, - { "__llvm_slsblr_thunk_x13", AArch64::X13}, - { "__llvm_slsblr_thunk_x14", AArch64::X14}, - { "__llvm_slsblr_thunk_x15", AArch64::X15}, - // X16 and X17 are deliberately missing, as the mitigation requires those - // register to not be used in BLR. See comment in ConvertBLRToBL for more - // details. - { "__llvm_slsblr_thunk_x18", AArch64::X18}, - { "__llvm_slsblr_thunk_x19", AArch64::X19}, - { "__llvm_slsblr_thunk_x20", AArch64::X20}, - { "__llvm_slsblr_thunk_x21", AArch64::X21}, - { "__llvm_slsblr_thunk_x22", AArch64::X22}, - { "__llvm_slsblr_thunk_x23", AArch64::X23}, - { "__llvm_slsblr_thunk_x24", AArch64::X24}, - { "__llvm_slsblr_thunk_x25", AArch64::X25}, - { "__llvm_slsblr_thunk_x26", AArch64::X26}, - { "__llvm_slsblr_thunk_x27", AArch64::X27}, - { "__llvm_slsblr_thunk_x28", AArch64::X28}, - { "__llvm_slsblr_thunk_x29", AArch64::FP}, - // X30 is deliberately missing, for similar reasons as X16 and X17 are - // missing. - { "__llvm_slsblr_thunk_x31", AArch64::XZR}, -}; +static SmallString<32> createThunkName(const ThunkKind &Kind, Register Xn, + Register Xm) { + unsigned N = ThunksSet::indexOfXReg(Xn); + if (!Kind.HasXmOperand) + return formatv("{0}{1}x{2}", CommonNamePrefix, Kind.NameInfix, N); + + unsigned M = ThunksSet::indexOfXReg(Xm); + return formatv("{0}{1}x{2}_x{3}", CommonNamePrefix, Kind.NameInfix, N, M); +} -unsigned getThunkIndex(Register Reg) { - for (unsigned I = 0; I < NumPermittedRegs; ++I) - if (SLSBLRThunks[I].Reg == Reg) - return I; - llvm_unreachable("Unexpected register"); +static const ThunkKind &parseThunkName(StringRef ThunkName, Register &Xn, + Register &Xm) { + assert(ThunkName.starts_with(CommonNamePrefix) && + "Should be filtered out by ThunkInserter"); + // Thunk name suffix, such as "x1" or "aa_x2_x3". + StringRef NameSuffix = ThunkName.drop_front(CommonNamePrefix.size()); + + // Parse thunk kind based on thunk name infix. + const ThunkKind &Kind = *StringSwitch(NameSuffix) + .StartsWith("aa_", &ThunkKind::BRAA) + .StartsWith("ab_", &ThunkKind::BRAB) + .StartsWith("aaz_", &ThunkKind::BRAAZ) + .StartsWith("abz_", &ThunkKind::BRABZ) + .Default(&ThunkKind::BR); + + auto ParseRegName = [](StringRef Name) { + assert(Name.starts_with("x") && "xN register name expected"); + unsigned N; + bool Fail = Name.drop_front(1).getAsInteger(/*Radix=*/10, N); + assert(!Fail && N < 32 && "Unexpected register"); + return ThunksSet::xRegByIndex(N); + }; + + // For example, "x1" or "x2_x3". + StringRef RegsStr = NameSuffix.drop_front(Kind.NameInfix.size()); + StringRef XnStr, XmStr; + std::tie(XnStr, XmStr) = RegsStr.split('_'); + + // Parse register operands. + Xn = ParseRegName(XnStr); + Xm = Kind.HasXmOperand ? ParseRegName(XmStr) : AArch64::NoRegister; + + return Kind; } void SLSHardeningInserter::populateThunk(MachineFunction &MF) { @@ -192,11 +312,8 @@ void SLSHardeningInserter::populateThunk(MachineFunction &MF) { "ComdatThunks value changed since MF creation"); // FIXME: How to better communicate Register number, rather than through // name and lookup table? - assert(MF.getName().starts_with(getThunkPrefix())); - auto ThunkIt = llvm::find_if( - SLSBLRThunks, [&MF](auto T) { return T.Name == MF.getName(); }); - assert(ThunkIt != std::end(SLSBLRThunks)); - Register ThunkReg = ThunkIt->Reg; + Register Xn, Xm; + const ThunkKind &Kind = parseThunkName(MF.getName(), Xn, Xm); const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); @@ -221,13 +338,19 @@ void SLSHardeningInserter::populateThunk(MachineFunction &MF) { // __llvm_slsblr_thunk_xN: // BR xN // barrierInsts - Entry->addLiveIn(ThunkReg); - // MOV X16, ThunkReg == ORR X16, XZR, ThunkReg, LSL #0 + Entry->addLiveIn(Xn); + // MOV X16, Reg == ORR X16, XZR, Reg, LSL #0 BuildMI(Entry, DebugLoc(), TII->get(AArch64::ORRXrs), AArch64::X16) .addReg(AArch64::XZR) - .addReg(ThunkReg) + .addReg(Xn) .addImm(0); - BuildMI(Entry, DebugLoc(), TII->get(AArch64::BR)).addReg(AArch64::X16); + auto &Builder = + BuildMI(Entry, DebugLoc(), TII->get(Kind.BROpcode)).addReg(AArch64::X16); + if (Xm != AArch64::NoRegister) { + Entry->addLiveIn(Xm); + Builder.addReg(Xm); + } + // Make sure the thunks do not make use of the SB extension in case there is // a function somewhere that will call to it that for some reason disabled // the SB extension locally on that function, even though it's enabled for @@ -274,40 +397,31 @@ void SLSHardeningInserter::convertBLRToBL( MachineInstr &BLR = *MBBI; assert(isBLR(BLR)); - unsigned BLOpcode; - Register Reg; - bool RegIsKilled; - switch (BLR.getOpcode()) { - case AArch64::BLR: - case AArch64::BLRNoIP: - BLOpcode = AArch64::BL; - Reg = BLR.getOperand(0).getReg(); - assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR); - RegIsKilled = BLR.getOperand(0).isKill(); - break; - case AArch64::BLRAA: - case AArch64::BLRAB: - case AArch64::BLRAAZ: - case AArch64::BLRABZ: - llvm_unreachable("BLRA instructions cannot yet be produced by LLVM, " - "therefore there is no need to support them for now."); - default: - llvm_unreachable("unhandled BLR"); - } + const ThunkKind &Kind = *getThunkKind(BLR.getOpcode()); + + unsigned NumRegOperands = Kind.HasXmOperand ? 2 : 1; + assert(BLR.getNumExplicitOperands() == NumRegOperands && + "Expected one or two register inputs"); + Register Xn = BLR.getOperand(0).getReg(); + Register Xm = + Kind.HasXmOperand ? BLR.getOperand(1).getReg() : AArch64::NoRegister; + DebugLoc DL = BLR.getDebugLoc(); MachineFunction &MF = *MBBI->getMF(); MCContext &Context = MBB.getParent()->getContext(); const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); - unsigned ThunkIndex = getThunkIndex(Reg); - StringRef ThunkName = SLSBLRThunks[ThunkIndex].Name; + + auto ThunkName = createThunkName(Kind, Xn, Xm); MCSymbol *Sym = Context.getOrCreateSymbol(ThunkName); - if (!(Thunks & (1u << ThunkIndex))) { - Thunks |= 1u << ThunkIndex; - createThunkFunction(MMI, ThunkName, ComdatThunks); + + if (!Thunks.get(Kind.Id, Xn, Xm)) { + StringRef TargetAttrs = Kind.NeedsPAuth ? "+pauth" : ""; + Thunks.set(Kind.Id, Xn, Xm); + createThunkFunction(MMI, ThunkName, ComdatThunks, TargetAttrs); } - MachineInstr *BL = BuildMI(MBB, MBBI, DL, TII->get(BLOpcode)).addSym(Sym); + MachineInstr *BL = BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL)).addSym(Sym); // Now copy the implicit operands from BLR to BL and copy other necessary // info. @@ -338,9 +452,13 @@ void SLSHardeningInserter::convertBLRToBL( // Now copy over the implicit operands from the original BLR BL->copyImplicitOps(MF, BLR); MF.moveCallSiteInfo(&BLR, BL); - // Also add the register called in the BLR as being used in the called thunk. - BL->addOperand(MachineOperand::CreateReg(Reg, false /*isDef*/, true /*isImp*/, - RegIsKilled /*isKill*/)); + // Also add the register operands of the original BLR* instruction + // as being used in the called thunk. + for (unsigned OpIdx = 0; OpIdx < NumRegOperands; ++OpIdx) { + MachineOperand &Op = BLR.getOperand(OpIdx); + BL->addOperand(MachineOperand::CreateReg(Op.getReg(), /*isDef=*/false, + /*isImp=*/true, Op.isKill())); + } // Remove BLR instruction MBB.erase(MBBI); } diff --git a/llvm/test/CodeGen/AArch64/speculation-hardening-sls-blra.mir b/llvm/test/CodeGen/AArch64/speculation-hardening-sls-blra.mir new file mode 100644 index 00000000000000..bdeba53e250713 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/speculation-hardening-sls-blra.mir @@ -0,0 +1,210 @@ +# RUN: llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu \ +# RUN: -start-before aarch64-sls-hardening -o - %s \ +# RUN: -asm-verbose=0 \ +# RUN: | FileCheck %s \ +# RUN: --implicit-check-not=__llvm_slsblr_thunk_aa_x5_x8 \ +# RUN: --implicit-check-not=__llvm_slsblr_thunk_ab_x5_x8 \ +# RUN: --implicit-check-not=__llvm_slsblr_thunk_aaz_x5 \ +# RUN: --implicit-check-not=__llvm_slsblr_thunk_abz_x5 + +# Pointer Authentication extension introduces more branch-with-link instructions +# for the BLR SLS hardening to handle, namely BLRAA, BLRAB, BLRAAZ and BLRABZ. +# Unlike the non-authenticating BLR instruction, BLRAA and BLRAB accept two +# register operands (almost 900 combinations for each instruction). +# For that reason, it is not practical to create all possible thunks. + +# Check that the BLR SLS hardening transforms BLRA* instructions into +# unconditional BL calls to the correct thunk functions. +# Check that only relevant thunk functions are generated. +--- | + define void @test_instructions() #0 { + entry: + ret void + } + + define void @test_no_redef() #0 { + entry: + ret void + } + + define void @test_regs() #0 { + entry: + ret void + } + + attributes #0 = { "target-features"="+pauth,+harden-sls-blr" } +... + +# Test that all BLRA* instructions are handled. +--- +name: test_instructions +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $lr, $x0, $x1, $x2, $x3 + + BLRAA $x0, $x1, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAB $x1, $x2, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAAZ $x2, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRABZ $x3, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + RET undef $lr +... + +# Test that the same thunk function is not created twice. +--- +name: test_no_redef +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $lr, $x0, $x1, $x2, $x3, $x4 + + ; thunk used by @test_instructions + BLRAB $x1, $x2, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + + ; thunk used by this function twice + BLRAB $x3, $x4, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAB $x3, $x4, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + + RET undef $lr +... + +# Test that all xN registers (except x16, x17, x30 and xzr) are handled. +--- +name: test_regs +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $lr, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $fp + + BLRAA $x0, $x1, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x2, $x3, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x4, $x5, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x6, $x7, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x8, $x9, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x10, $x11, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x12, $x13, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x14, $x15, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + ; skipping x16 and x17 + BLRAA $x18, $x19, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x20, $x21, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x22, $x23, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x24, $x25, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x26, $x27, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + BLRAA $x28, $fp, implicit-def $lr, implicit $sp, implicit-def $sp, implicit-def $w0 + RET undef $lr +... + +# CHECK-LABEL: test_instructions: +# CHECK-NEXT: .cfi_startproc +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x0_x1 +# CHECK-NEXT: bl __llvm_slsblr_thunk_ab_x1_x2 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aaz_x2 +# CHECK-NEXT: bl __llvm_slsblr_thunk_abz_x3 +# CHECK-NEXT: ret + +# CHECK-LABEL: test_no_redef: +# CHECK-NEXT: .cfi_startproc +# CHECK-NEXT: bl __llvm_slsblr_thunk_ab_x1_x2 +# CHECK-NEXT: bl __llvm_slsblr_thunk_ab_x3_x4 +# CHECK-NEXT: bl __llvm_slsblr_thunk_ab_x3_x4 +# CHECK-NEXT: ret + +# CHECK-LABEL: test_regs: +# CHECK-NEXT: .cfi_startproc +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x0_x1 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x2_x3 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x4_x5 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x6_x7 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x8_x9 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x10_x11 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x12_x13 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x14_x15 +# skipping x16 and x17 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x18_x19 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x20_x21 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x22_x23 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x24_x25 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x26_x27 +# CHECK-NEXT: bl __llvm_slsblr_thunk_aa_x28_x29 +# CHECK-NEXT: ret + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x0_x1: +# CHECK-NEXT: mov x16, x0 +# CHECK-NEXT: braa x16, x1 +# CHECK-NEXT: dsb sy +# CHECK-NEXT: isb + +# CHECK-LABEL: __llvm_slsblr_thunk_ab_x1_x2: +# CHECK-NEXT: mov x16, x1 +# CHECK-NEXT: brab x16, x2 +# CHECK-NEXT: dsb sy +# CHECK-NEXT: isb + +# CHECK-LABEL: __llvm_slsblr_thunk_aaz_x2: +# CHECK-NEXT: mov x16, x2 +# CHECK-NEXT: braaz x16 +# CHECK-NEXT: dsb sy +# CHECK-NEXT: isb + +# CHECK-LABEL: __llvm_slsblr_thunk_abz_x3: +# CHECK-NEXT: mov x16, x3 +# CHECK-NEXT: brabz x16 +# CHECK-NEXT: dsb sy +# CHECK-NEXT: isb + +# The instruction *operands* should correspond to the thunk function *name* +# (check that the name is parsed correctly when populating the thunk). + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x2_x3: +# CHECK-NEXT: mov x16, x2 +# CHECK: braa x16, x3 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x4_x5: +# CHECK-NEXT: mov x16, x4 +# CHECK: braa x16, x5 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x6_x7: +# CHECK-NEXT: mov x16, x6 +# CHECK: braa x16, x7 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x8_x9: +# CHECK-NEXT: mov x16, x8 +# CHECK: braa x16, x9 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x10_x11: +# CHECK-NEXT: mov x16, x10 +# CHECK: braa x16, x11 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x12_x13: +# CHECK-NEXT: mov x16, x12 +# CHECK: braa x16, x13 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x14_x15: +# CHECK-NEXT: mov x16, x14 +# CHECK: braa x16, x15 + +# skipping x16 and x17 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x18_x19: +# CHECK-NEXT: mov x16, x18 +# CHECK: braa x16, x19 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x20_x21: +# CHECK-NEXT: mov x16, x20 +# CHECK: braa x16, x21 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x22_x23: +# CHECK-NEXT: mov x16, x22 +# CHECK: braa x16, x23 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x24_x25: +# CHECK-NEXT: mov x16, x24 +# CHECK: braa x16, x25 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x26_x27: +# CHECK-NEXT: mov x16, x26 +# CHECK: braa x16, x27 + +# CHECK-LABEL: __llvm_slsblr_thunk_aa_x28_x29: +# CHECK-NEXT: mov x16, x28 +# CHECK: braa x16, x29