xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
15ffd83dbSDimitry Andric //===- AArch64SLSHardening.cpp - Harden Straight Line Missspeculation -----===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // This file contains a pass to insert code to mitigate against side channel
105ffd83dbSDimitry Andric // vulnerabilities that may happen under straight line miss-speculation.
115ffd83dbSDimitry Andric //
125ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
135ffd83dbSDimitry Andric 
145ffd83dbSDimitry Andric #include "AArch64InstrInfo.h"
155ffd83dbSDimitry Andric #include "AArch64Subtarget.h"
165ffd83dbSDimitry Andric #include "Utils/AArch64BaseInfo.h"
175ffd83dbSDimitry Andric #include "llvm/ADT/BitVector.h"
185ffd83dbSDimitry Andric #include "llvm/ADT/SmallVector.h"
195ffd83dbSDimitry Andric #include "llvm/CodeGen/IndirectThunks.h"
205ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
215ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
225ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
235ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineInstr.h"
245ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
255ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineOperand.h"
265ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
275ffd83dbSDimitry Andric #include "llvm/CodeGen/RegisterScavenging.h"
285ffd83dbSDimitry Andric #include "llvm/IR/DebugLoc.h"
295ffd83dbSDimitry Andric #include "llvm/Pass.h"
305ffd83dbSDimitry Andric #include "llvm/Support/CodeGen.h"
315ffd83dbSDimitry Andric #include "llvm/Support/Debug.h"
325ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h"
335ffd83dbSDimitry Andric #include <cassert>
345ffd83dbSDimitry Andric 
355ffd83dbSDimitry Andric using namespace llvm;
365ffd83dbSDimitry Andric 
375ffd83dbSDimitry Andric #define DEBUG_TYPE "aarch64-sls-hardening"
385ffd83dbSDimitry Andric 
395ffd83dbSDimitry Andric #define AARCH64_SLS_HARDENING_NAME "AArch64 sls hardening pass"
405ffd83dbSDimitry Andric 
415ffd83dbSDimitry Andric namespace {
425ffd83dbSDimitry Andric 
435ffd83dbSDimitry Andric class AArch64SLSHardening : public MachineFunctionPass {
445ffd83dbSDimitry Andric public:
455ffd83dbSDimitry Andric   const TargetInstrInfo *TII;
465ffd83dbSDimitry Andric   const TargetRegisterInfo *TRI;
475ffd83dbSDimitry Andric   const AArch64Subtarget *ST;
485ffd83dbSDimitry Andric 
495ffd83dbSDimitry Andric   static char ID;
505ffd83dbSDimitry Andric 
515ffd83dbSDimitry Andric   AArch64SLSHardening() : MachineFunctionPass(ID) {
525ffd83dbSDimitry Andric     initializeAArch64SLSHardeningPass(*PassRegistry::getPassRegistry());
535ffd83dbSDimitry Andric   }
545ffd83dbSDimitry Andric 
555ffd83dbSDimitry Andric   bool runOnMachineFunction(MachineFunction &Fn) override;
565ffd83dbSDimitry Andric 
575ffd83dbSDimitry Andric   StringRef getPassName() const override { return AARCH64_SLS_HARDENING_NAME; }
585ffd83dbSDimitry Andric 
595ffd83dbSDimitry Andric private:
605ffd83dbSDimitry Andric   bool hardenReturnsAndBRs(MachineBasicBlock &MBB) const;
615ffd83dbSDimitry Andric   bool hardenBLRs(MachineBasicBlock &MBB) const;
625ffd83dbSDimitry Andric   MachineBasicBlock &ConvertBLRToBL(MachineBasicBlock &MBB,
635ffd83dbSDimitry Andric                                     MachineBasicBlock::iterator) const;
645ffd83dbSDimitry Andric };
655ffd83dbSDimitry Andric 
665ffd83dbSDimitry Andric } // end anonymous namespace
675ffd83dbSDimitry Andric 
685ffd83dbSDimitry Andric char AArch64SLSHardening::ID = 0;
695ffd83dbSDimitry Andric 
705ffd83dbSDimitry Andric INITIALIZE_PASS(AArch64SLSHardening, "aarch64-sls-hardening",
715ffd83dbSDimitry Andric                 AARCH64_SLS_HARDENING_NAME, false, false)
725ffd83dbSDimitry Andric 
735ffd83dbSDimitry Andric static void insertSpeculationBarrier(const AArch64Subtarget *ST,
745ffd83dbSDimitry Andric                                      MachineBasicBlock &MBB,
755ffd83dbSDimitry Andric                                      MachineBasicBlock::iterator MBBI,
765ffd83dbSDimitry Andric                                      DebugLoc DL,
775ffd83dbSDimitry Andric                                      bool AlwaysUseISBDSB = false) {
785ffd83dbSDimitry Andric   assert(MBBI != MBB.begin() &&
795ffd83dbSDimitry Andric          "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
805ffd83dbSDimitry Andric   assert(std::prev(MBBI)->isBarrier() &&
815ffd83dbSDimitry Andric          "SpeculationBarrierEndBB must only follow unconditional control flow "
825ffd83dbSDimitry Andric          "instructions.");
835ffd83dbSDimitry Andric   assert(std::prev(MBBI)->isTerminator() &&
845ffd83dbSDimitry Andric          "SpeculationBarrierEndBB must only follow terminators.");
855ffd83dbSDimitry Andric   const TargetInstrInfo *TII = ST->getInstrInfo();
865ffd83dbSDimitry Andric   unsigned BarrierOpc = ST->hasSB() && !AlwaysUseISBDSB
875ffd83dbSDimitry Andric                             ? AArch64::SpeculationBarrierSBEndBB
885ffd83dbSDimitry Andric                             : AArch64::SpeculationBarrierISBDSBEndBB;
895ffd83dbSDimitry Andric   if (MBBI == MBB.end() ||
905ffd83dbSDimitry Andric       (MBBI->getOpcode() != AArch64::SpeculationBarrierSBEndBB &&
915ffd83dbSDimitry Andric        MBBI->getOpcode() != AArch64::SpeculationBarrierISBDSBEndBB))
925ffd83dbSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc));
935ffd83dbSDimitry Andric }
945ffd83dbSDimitry Andric 
955ffd83dbSDimitry Andric bool AArch64SLSHardening::runOnMachineFunction(MachineFunction &MF) {
965ffd83dbSDimitry Andric   ST = &MF.getSubtarget<AArch64Subtarget>();
975ffd83dbSDimitry Andric   TII = MF.getSubtarget().getInstrInfo();
985ffd83dbSDimitry Andric   TRI = MF.getSubtarget().getRegisterInfo();
995ffd83dbSDimitry Andric 
1005ffd83dbSDimitry Andric   bool Modified = false;
1015ffd83dbSDimitry Andric   for (auto &MBB : MF) {
1025ffd83dbSDimitry Andric     Modified |= hardenReturnsAndBRs(MBB);
1035ffd83dbSDimitry Andric     Modified |= hardenBLRs(MBB);
1045ffd83dbSDimitry Andric   }
1055ffd83dbSDimitry Andric 
1065ffd83dbSDimitry Andric   return Modified;
1075ffd83dbSDimitry Andric }
1085ffd83dbSDimitry Andric 
1095ffd83dbSDimitry Andric static bool isBLR(const MachineInstr &MI) {
1105ffd83dbSDimitry Andric   switch (MI.getOpcode()) {
1115ffd83dbSDimitry Andric   case AArch64::BLR:
1125ffd83dbSDimitry Andric   case AArch64::BLRNoIP:
1135ffd83dbSDimitry Andric     return true;
1145ffd83dbSDimitry Andric   case AArch64::BLRAA:
1155ffd83dbSDimitry Andric   case AArch64::BLRAB:
1165ffd83dbSDimitry Andric   case AArch64::BLRAAZ:
1175ffd83dbSDimitry Andric   case AArch64::BLRABZ:
1185ffd83dbSDimitry Andric     llvm_unreachable("Currently, LLVM's code generator does not support "
1195ffd83dbSDimitry Andric                      "producing BLRA* instructions. Therefore, there's no "
1205ffd83dbSDimitry Andric                      "support in this pass for those instructions.");
1215ffd83dbSDimitry Andric   }
1225ffd83dbSDimitry Andric   return false;
1235ffd83dbSDimitry Andric }
1245ffd83dbSDimitry Andric 
1255ffd83dbSDimitry Andric bool AArch64SLSHardening::hardenReturnsAndBRs(MachineBasicBlock &MBB) const {
1265ffd83dbSDimitry Andric   if (!ST->hardenSlsRetBr())
1275ffd83dbSDimitry Andric     return false;
1285ffd83dbSDimitry Andric   bool Modified = false;
1295ffd83dbSDimitry Andric   MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end();
1305ffd83dbSDimitry Andric   MachineBasicBlock::iterator NextMBBI;
1315ffd83dbSDimitry Andric   for (; MBBI != E; MBBI = NextMBBI) {
1325ffd83dbSDimitry Andric     MachineInstr &MI = *MBBI;
1335ffd83dbSDimitry Andric     NextMBBI = std::next(MBBI);
1345ffd83dbSDimitry Andric     if (MI.isReturn() || isIndirectBranchOpcode(MI.getOpcode())) {
1355ffd83dbSDimitry Andric       assert(MI.isTerminator());
1365ffd83dbSDimitry Andric       insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc());
1375ffd83dbSDimitry Andric       Modified = true;
1385ffd83dbSDimitry Andric     }
1395ffd83dbSDimitry Andric   }
1405ffd83dbSDimitry Andric   return Modified;
1415ffd83dbSDimitry Andric }
1425ffd83dbSDimitry Andric 
1435ffd83dbSDimitry Andric static const char SLSBLRNamePrefix[] = "__llvm_slsblr_thunk_";
1445ffd83dbSDimitry Andric 
1455ffd83dbSDimitry Andric static const struct ThunkNameAndReg {
1465ffd83dbSDimitry Andric   const char* Name;
1475ffd83dbSDimitry Andric   Register Reg;
1485ffd83dbSDimitry Andric } SLSBLRThunks[] = {
1495ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x0",  AArch64::X0},
1505ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x1",  AArch64::X1},
1515ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x2",  AArch64::X2},
1525ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x3",  AArch64::X3},
1535ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x4",  AArch64::X4},
1545ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x5",  AArch64::X5},
1555ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x6",  AArch64::X6},
1565ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x7",  AArch64::X7},
1575ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x8",  AArch64::X8},
1585ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x9",  AArch64::X9},
1595ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x10",  AArch64::X10},
1605ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x11",  AArch64::X11},
1615ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x12",  AArch64::X12},
1625ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x13",  AArch64::X13},
1635ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x14",  AArch64::X14},
1645ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x15",  AArch64::X15},
1655ffd83dbSDimitry Andric   // X16 and X17 are deliberately missing, as the mitigation requires those
1665ffd83dbSDimitry Andric   // register to not be used in BLR. See comment in ConvertBLRToBL for more
1675ffd83dbSDimitry Andric   // details.
1685ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x18",  AArch64::X18},
1695ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x19",  AArch64::X19},
1705ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x20",  AArch64::X20},
1715ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x21",  AArch64::X21},
1725ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x22",  AArch64::X22},
1735ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x23",  AArch64::X23},
1745ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x24",  AArch64::X24},
1755ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x25",  AArch64::X25},
1765ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x26",  AArch64::X26},
1775ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x27",  AArch64::X27},
1785ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x28",  AArch64::X28},
1795ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x29",  AArch64::FP},
1805ffd83dbSDimitry Andric   // X30 is deliberately missing, for similar reasons as X16 and X17 are
1815ffd83dbSDimitry Andric   // missing.
1825ffd83dbSDimitry Andric   { "__llvm_slsblr_thunk_x31",  AArch64::XZR},
1835ffd83dbSDimitry Andric };
1845ffd83dbSDimitry Andric 
1855ffd83dbSDimitry Andric namespace {
1865ffd83dbSDimitry Andric struct SLSBLRThunkInserter : ThunkInserter<SLSBLRThunkInserter> {
1875ffd83dbSDimitry Andric   const char *getThunkPrefix() { return SLSBLRNamePrefix; }
1885ffd83dbSDimitry Andric   bool mayUseThunk(const MachineFunction &MF) {
189fe6060f1SDimitry Andric     ComdatThunks &= !MF.getSubtarget<AArch64Subtarget>().hardenSlsNoComdat();
1905ffd83dbSDimitry Andric     // FIXME: This could also check if there are any BLRs in the function
1915ffd83dbSDimitry Andric     // to more accurately reflect if a thunk will be needed.
1925ffd83dbSDimitry Andric     return MF.getSubtarget<AArch64Subtarget>().hardenSlsBlr();
1935ffd83dbSDimitry Andric   }
1945ffd83dbSDimitry Andric   void insertThunks(MachineModuleInfo &MMI);
1955ffd83dbSDimitry Andric   void populateThunk(MachineFunction &MF);
196fe6060f1SDimitry Andric 
197fe6060f1SDimitry Andric private:
198fe6060f1SDimitry Andric   bool ComdatThunks = true;
1995ffd83dbSDimitry Andric };
2005ffd83dbSDimitry Andric } // namespace
2015ffd83dbSDimitry Andric 
2025ffd83dbSDimitry Andric void SLSBLRThunkInserter::insertThunks(MachineModuleInfo &MMI) {
2035ffd83dbSDimitry Andric   // FIXME: It probably would be possible to filter which thunks to produce
2045ffd83dbSDimitry Andric   // based on which registers are actually used in BLR instructions in this
2055ffd83dbSDimitry Andric   // function. But would that be a worthwhile optimization?
2065ffd83dbSDimitry Andric   for (auto T : SLSBLRThunks)
207fe6060f1SDimitry Andric     createThunkFunction(MMI, T.Name, ComdatThunks);
2085ffd83dbSDimitry Andric }
2095ffd83dbSDimitry Andric 
2105ffd83dbSDimitry Andric void SLSBLRThunkInserter::populateThunk(MachineFunction &MF) {
2115ffd83dbSDimitry Andric   // FIXME: How to better communicate Register number, rather than through
2125ffd83dbSDimitry Andric   // name and lookup table?
2135ffd83dbSDimitry Andric   assert(MF.getName().startswith(getThunkPrefix()));
2145ffd83dbSDimitry Andric   auto ThunkIt = llvm::find_if(
2155ffd83dbSDimitry Andric       SLSBLRThunks, [&MF](auto T) { return T.Name == MF.getName(); });
2165ffd83dbSDimitry Andric   assert(ThunkIt != std::end(SLSBLRThunks));
2175ffd83dbSDimitry Andric   Register ThunkReg = ThunkIt->Reg;
2185ffd83dbSDimitry Andric 
2195ffd83dbSDimitry Andric   const TargetInstrInfo *TII =
2205ffd83dbSDimitry Andric       MF.getSubtarget<AArch64Subtarget>().getInstrInfo();
2215ffd83dbSDimitry Andric   assert (MF.size() == 1);
2225ffd83dbSDimitry Andric   MachineBasicBlock *Entry = &MF.front();
2235ffd83dbSDimitry Andric   Entry->clear();
2245ffd83dbSDimitry Andric 
2255ffd83dbSDimitry Andric   //  These thunks need to consist of the following instructions:
2265ffd83dbSDimitry Andric   //  __llvm_slsblr_thunk_xN:
2275ffd83dbSDimitry Andric   //      BR xN
2285ffd83dbSDimitry Andric   //      barrierInsts
2295ffd83dbSDimitry Andric   Entry->addLiveIn(ThunkReg);
2305ffd83dbSDimitry Andric   // MOV X16, ThunkReg == ORR X16, XZR, ThunkReg, LSL #0
2315ffd83dbSDimitry Andric   BuildMI(Entry, DebugLoc(), TII->get(AArch64::ORRXrs), AArch64::X16)
2325ffd83dbSDimitry Andric       .addReg(AArch64::XZR)
2335ffd83dbSDimitry Andric       .addReg(ThunkReg)
2345ffd83dbSDimitry Andric       .addImm(0);
2355ffd83dbSDimitry Andric   BuildMI(Entry, DebugLoc(), TII->get(AArch64::BR)).addReg(AArch64::X16);
2365ffd83dbSDimitry Andric   // Make sure the thunks do not make use of the SB extension in case there is
2375ffd83dbSDimitry Andric   // a function somewhere that will call to it that for some reason disabled
2385ffd83dbSDimitry Andric   // the SB extension locally on that function, even though it's enabled for
2395ffd83dbSDimitry Andric   // the module otherwise. Therefore set AlwaysUseISBSDB to true.
2405ffd83dbSDimitry Andric   insertSpeculationBarrier(&MF.getSubtarget<AArch64Subtarget>(), *Entry,
2415ffd83dbSDimitry Andric                            Entry->end(), DebugLoc(), true /*AlwaysUseISBDSB*/);
2425ffd83dbSDimitry Andric }
2435ffd83dbSDimitry Andric 
2445ffd83dbSDimitry Andric MachineBasicBlock &
2455ffd83dbSDimitry Andric AArch64SLSHardening::ConvertBLRToBL(MachineBasicBlock &MBB,
2465ffd83dbSDimitry Andric                                     MachineBasicBlock::iterator MBBI) const {
2475ffd83dbSDimitry Andric   // Transform a BLR to a BL as follows:
2485ffd83dbSDimitry Andric   // Before:
2495ffd83dbSDimitry Andric   //   |-----------------------------|
2505ffd83dbSDimitry Andric   //   |      ...                    |
2515ffd83dbSDimitry Andric   //   |  instI                      |
2525ffd83dbSDimitry Andric   //   |  BLR xN                     |
2535ffd83dbSDimitry Andric   //   |  instJ                      |
2545ffd83dbSDimitry Andric   //   |      ...                    |
2555ffd83dbSDimitry Andric   //   |-----------------------------|
2565ffd83dbSDimitry Andric   //
2575ffd83dbSDimitry Andric   // After:
2585ffd83dbSDimitry Andric   //   |-----------------------------|
2595ffd83dbSDimitry Andric   //   |      ...                    |
2605ffd83dbSDimitry Andric   //   |  instI                      |
2615ffd83dbSDimitry Andric   //   |  BL __llvm_slsblr_thunk_xN  |
2625ffd83dbSDimitry Andric   //   |  instJ                      |
2635ffd83dbSDimitry Andric   //   |      ...                    |
2645ffd83dbSDimitry Andric   //   |-----------------------------|
2655ffd83dbSDimitry Andric   //
2665ffd83dbSDimitry Andric   //   __llvm_slsblr_thunk_xN:
2675ffd83dbSDimitry Andric   //   |-----------------------------|
2685ffd83dbSDimitry Andric   //   |  BR xN                      |
2695ffd83dbSDimitry Andric   //   |  barrierInsts               |
2705ffd83dbSDimitry Andric   //   |-----------------------------|
2715ffd83dbSDimitry Andric   //
2725ffd83dbSDimitry Andric   // The __llvm_slsblr_thunk_xN thunks are created by the SLSBLRThunkInserter.
2735ffd83dbSDimitry Andric   // This function merely needs to transform BLR xN into BL
2745ffd83dbSDimitry Andric   // __llvm_slsblr_thunk_xN.
2755ffd83dbSDimitry Andric   //
2765ffd83dbSDimitry Andric   // Since linkers are allowed to clobber X16 and X17 on function calls, the
2775ffd83dbSDimitry Andric   // above mitigation only works if the original BLR instruction was not
2785ffd83dbSDimitry Andric   // BLR X16 nor BLR X17. Code generation before must make sure that no BLR
2795ffd83dbSDimitry Andric   // X16|X17 was produced if the mitigation is enabled.
2805ffd83dbSDimitry Andric 
2815ffd83dbSDimitry Andric   MachineInstr &BLR = *MBBI;
2825ffd83dbSDimitry Andric   assert(isBLR(BLR));
2835ffd83dbSDimitry Andric   unsigned BLOpcode;
2845ffd83dbSDimitry Andric   Register Reg;
2855ffd83dbSDimitry Andric   bool RegIsKilled;
2865ffd83dbSDimitry Andric   switch (BLR.getOpcode()) {
2875ffd83dbSDimitry Andric   case AArch64::BLR:
2885ffd83dbSDimitry Andric   case AArch64::BLRNoIP:
2895ffd83dbSDimitry Andric     BLOpcode = AArch64::BL;
2905ffd83dbSDimitry Andric     Reg = BLR.getOperand(0).getReg();
2915ffd83dbSDimitry Andric     assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR);
2925ffd83dbSDimitry Andric     RegIsKilled = BLR.getOperand(0).isKill();
2935ffd83dbSDimitry Andric     break;
2945ffd83dbSDimitry Andric   case AArch64::BLRAA:
2955ffd83dbSDimitry Andric   case AArch64::BLRAB:
2965ffd83dbSDimitry Andric   case AArch64::BLRAAZ:
2975ffd83dbSDimitry Andric   case AArch64::BLRABZ:
2985ffd83dbSDimitry Andric     llvm_unreachable("BLRA instructions cannot yet be produced by LLVM, "
2995ffd83dbSDimitry Andric                      "therefore there is no need to support them for now.");
3005ffd83dbSDimitry Andric   default:
3015ffd83dbSDimitry Andric     llvm_unreachable("unhandled BLR");
3025ffd83dbSDimitry Andric   }
3035ffd83dbSDimitry Andric   DebugLoc DL = BLR.getDebugLoc();
3045ffd83dbSDimitry Andric 
3055ffd83dbSDimitry Andric   // If we'd like to support also BLRAA and BLRAB instructions, we'd need
3065ffd83dbSDimitry Andric   // a lot more different kind of thunks.
3075ffd83dbSDimitry Andric   // For example, a
3085ffd83dbSDimitry Andric   //
3095ffd83dbSDimitry Andric   // BLRAA xN, xM
3105ffd83dbSDimitry Andric   //
3115ffd83dbSDimitry Andric   // instruction probably would need to be transformed to something like:
3125ffd83dbSDimitry Andric   //
3135ffd83dbSDimitry Andric   // BL __llvm_slsblraa_thunk_x<N>_x<M>
3145ffd83dbSDimitry Andric   //
3155ffd83dbSDimitry Andric   // __llvm_slsblraa_thunk_x<N>_x<M>:
3165ffd83dbSDimitry Andric   //   BRAA x<N>, x<M>
3175ffd83dbSDimitry Andric   //   barrierInsts
3185ffd83dbSDimitry Andric   //
3195ffd83dbSDimitry Andric   // Given that about 30 different values of N are possible and about 30
3205ffd83dbSDimitry Andric   // different values of M are possible in the above, with the current way
3215ffd83dbSDimitry Andric   // of producing indirect thunks, we'd be producing about 30 times 30, i.e.
3225ffd83dbSDimitry Andric   // about 900 thunks (where most might not be actually called). This would
3235ffd83dbSDimitry Andric   // multiply further by two to support both BLRAA and BLRAB variants of those
3245ffd83dbSDimitry Andric   // instructions.
3255ffd83dbSDimitry Andric   // If we'd want to support this, we'd probably need to look into a different
3265ffd83dbSDimitry Andric   // way to produce thunk functions, based on which variants are actually
3275ffd83dbSDimitry Andric   // needed, rather than producing all possible variants.
3285ffd83dbSDimitry Andric   // So far, LLVM does never produce BLRA* instructions, so let's leave this
3295ffd83dbSDimitry Andric   // for the future when LLVM can start producing BLRA* instructions.
3305ffd83dbSDimitry Andric   MachineFunction &MF = *MBBI->getMF();
3315ffd83dbSDimitry Andric   MCContext &Context = MBB.getParent()->getContext();
3325ffd83dbSDimitry Andric   auto ThunkIt =
3335ffd83dbSDimitry Andric       llvm::find_if(SLSBLRThunks, [Reg](auto T) { return T.Reg == Reg; });
3345ffd83dbSDimitry Andric   assert (ThunkIt != std::end(SLSBLRThunks));
3355ffd83dbSDimitry Andric   MCSymbol *Sym = Context.getOrCreateSymbol(ThunkIt->Name);
3365ffd83dbSDimitry Andric 
3375ffd83dbSDimitry Andric   MachineInstr *BL = BuildMI(MBB, MBBI, DL, TII->get(BLOpcode)).addSym(Sym);
3385ffd83dbSDimitry Andric 
3395ffd83dbSDimitry Andric   // Now copy the implicit operands from BLR to BL and copy other necessary
3405ffd83dbSDimitry Andric   // info.
3415ffd83dbSDimitry Andric   // However, both BLR and BL instructions implictly use SP and implicitly
3425ffd83dbSDimitry Andric   // define LR. Blindly copying implicit operands would result in SP and LR
3435ffd83dbSDimitry Andric   // operands to be present multiple times. While this may not be too much of
3445ffd83dbSDimitry Andric   // an issue, let's avoid that for cleanliness, by removing those implicit
3455ffd83dbSDimitry Andric   // operands from the BL created above before we copy over all implicit
3465ffd83dbSDimitry Andric   // operands from the BLR.
3475ffd83dbSDimitry Andric   int ImpLROpIdx = -1;
3485ffd83dbSDimitry Andric   int ImpSPOpIdx = -1;
3495ffd83dbSDimitry Andric   for (unsigned OpIdx = BL->getNumExplicitOperands();
3505ffd83dbSDimitry Andric        OpIdx < BL->getNumOperands(); OpIdx++) {
3515ffd83dbSDimitry Andric     MachineOperand Op = BL->getOperand(OpIdx);
3525ffd83dbSDimitry Andric     if (!Op.isReg())
3535ffd83dbSDimitry Andric       continue;
3545ffd83dbSDimitry Andric     if (Op.getReg() == AArch64::LR && Op.isDef())
3555ffd83dbSDimitry Andric       ImpLROpIdx = OpIdx;
3565ffd83dbSDimitry Andric     if (Op.getReg() == AArch64::SP && !Op.isDef())
3575ffd83dbSDimitry Andric       ImpSPOpIdx = OpIdx;
3585ffd83dbSDimitry Andric   }
3595ffd83dbSDimitry Andric   assert(ImpLROpIdx != -1);
3605ffd83dbSDimitry Andric   assert(ImpSPOpIdx != -1);
3615ffd83dbSDimitry Andric   int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx);
3625ffd83dbSDimitry Andric   int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx);
363*81ad6265SDimitry Andric   BL->removeOperand(FirstOpIdxToRemove);
364*81ad6265SDimitry Andric   BL->removeOperand(SecondOpIdxToRemove);
3655ffd83dbSDimitry Andric   // Now copy over the implicit operands from the original BLR
3665ffd83dbSDimitry Andric   BL->copyImplicitOps(MF, BLR);
3675ffd83dbSDimitry Andric   MF.moveCallSiteInfo(&BLR, BL);
3685ffd83dbSDimitry Andric   // Also add the register called in the BLR as being used in the called thunk.
3695ffd83dbSDimitry Andric   BL->addOperand(MachineOperand::CreateReg(Reg, false /*isDef*/, true /*isImp*/,
3705ffd83dbSDimitry Andric                                            RegIsKilled /*isKill*/));
3715ffd83dbSDimitry Andric   // Remove BLR instruction
3725ffd83dbSDimitry Andric   MBB.erase(MBBI);
3735ffd83dbSDimitry Andric 
3745ffd83dbSDimitry Andric   return MBB;
3755ffd83dbSDimitry Andric }
3765ffd83dbSDimitry Andric 
3775ffd83dbSDimitry Andric bool AArch64SLSHardening::hardenBLRs(MachineBasicBlock &MBB) const {
3785ffd83dbSDimitry Andric   if (!ST->hardenSlsBlr())
3795ffd83dbSDimitry Andric     return false;
3805ffd83dbSDimitry Andric   bool Modified = false;
3815ffd83dbSDimitry Andric   MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
3825ffd83dbSDimitry Andric   MachineBasicBlock::iterator NextMBBI;
3835ffd83dbSDimitry Andric   for (; MBBI != E; MBBI = NextMBBI) {
3845ffd83dbSDimitry Andric     MachineInstr &MI = *MBBI;
3855ffd83dbSDimitry Andric     NextMBBI = std::next(MBBI);
3865ffd83dbSDimitry Andric     if (isBLR(MI)) {
3875ffd83dbSDimitry Andric       ConvertBLRToBL(MBB, MBBI);
3885ffd83dbSDimitry Andric       Modified = true;
3895ffd83dbSDimitry Andric     }
3905ffd83dbSDimitry Andric   }
3915ffd83dbSDimitry Andric   return Modified;
3925ffd83dbSDimitry Andric }
3935ffd83dbSDimitry Andric 
3945ffd83dbSDimitry Andric FunctionPass *llvm::createAArch64SLSHardeningPass() {
3955ffd83dbSDimitry Andric   return new AArch64SLSHardening();
3965ffd83dbSDimitry Andric }
3975ffd83dbSDimitry Andric 
3985ffd83dbSDimitry Andric namespace {
3995ffd83dbSDimitry Andric class AArch64IndirectThunks : public MachineFunctionPass {
4005ffd83dbSDimitry Andric public:
4015ffd83dbSDimitry Andric   static char ID;
4025ffd83dbSDimitry Andric 
4035ffd83dbSDimitry Andric   AArch64IndirectThunks() : MachineFunctionPass(ID) {}
4045ffd83dbSDimitry Andric 
4055ffd83dbSDimitry Andric   StringRef getPassName() const override { return "AArch64 Indirect Thunks"; }
4065ffd83dbSDimitry Andric 
4075ffd83dbSDimitry Andric   bool doInitialization(Module &M) override;
4085ffd83dbSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
4095ffd83dbSDimitry Andric 
4105ffd83dbSDimitry Andric private:
4115ffd83dbSDimitry Andric   std::tuple<SLSBLRThunkInserter> TIs;
4125ffd83dbSDimitry Andric 
4135ffd83dbSDimitry Andric   // FIXME: When LLVM moves to C++17, these can become folds
4145ffd83dbSDimitry Andric   template <typename... ThunkInserterT>
4155ffd83dbSDimitry Andric   static void initTIs(Module &M,
4165ffd83dbSDimitry Andric                       std::tuple<ThunkInserterT...> &ThunkInserters) {
4175ffd83dbSDimitry Andric     (void)std::initializer_list<int>{
4185ffd83dbSDimitry Andric         (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
4195ffd83dbSDimitry Andric   }
4205ffd83dbSDimitry Andric   template <typename... ThunkInserterT>
4215ffd83dbSDimitry Andric   static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
4225ffd83dbSDimitry Andric                      std::tuple<ThunkInserterT...> &ThunkInserters) {
4235ffd83dbSDimitry Andric     bool Modified = false;
4245ffd83dbSDimitry Andric     (void)std::initializer_list<int>{
4255ffd83dbSDimitry Andric         Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
4265ffd83dbSDimitry Andric     return Modified;
4275ffd83dbSDimitry Andric   }
4285ffd83dbSDimitry Andric };
4295ffd83dbSDimitry Andric 
4305ffd83dbSDimitry Andric } // end anonymous namespace
4315ffd83dbSDimitry Andric 
4325ffd83dbSDimitry Andric char AArch64IndirectThunks::ID = 0;
4335ffd83dbSDimitry Andric 
4345ffd83dbSDimitry Andric FunctionPass *llvm::createAArch64IndirectThunks() {
4355ffd83dbSDimitry Andric   return new AArch64IndirectThunks();
4365ffd83dbSDimitry Andric }
4375ffd83dbSDimitry Andric 
4385ffd83dbSDimitry Andric bool AArch64IndirectThunks::doInitialization(Module &M) {
4395ffd83dbSDimitry Andric   initTIs(M, TIs);
4405ffd83dbSDimitry Andric   return false;
4415ffd83dbSDimitry Andric }
4425ffd83dbSDimitry Andric 
4435ffd83dbSDimitry Andric bool AArch64IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
4445ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << getPassName() << '\n');
4455ffd83dbSDimitry Andric   auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
4465ffd83dbSDimitry Andric   return runTIs(MMI, MF, TIs);
4475ffd83dbSDimitry Andric }
448