35#define DEBUG_TYPE "aarch64-sls-hardening"
37#define AARCH64_SLS_HARDENING_NAME "AArch64 sls hardening pass"
66 static const ThunkKind BR;
67 static const ThunkKind BRAA;
68 static const ThunkKind BRAB;
69 static const ThunkKind BRAAZ;
70 static const ThunkKind BRABZ;
76 static constexpr unsigned NumXRegisters = 32;
79 static unsigned indexOfXReg(
Register Xn);
84 BLRThunks |=
Other.BLRThunks;
85 BLRAAZThunks |=
Other.BLRAAZThunks;
86 BLRABZThunks |=
Other.BLRABZThunks;
87 for (
unsigned I = 0;
I < NumXRegisters; ++
I)
88 BLRAAThunks[
I] |=
Other.BLRAAThunks[
I];
89 for (
unsigned I = 0;
I < NumXRegisters; ++
I)
90 BLRABThunks[
I] |=
Other.BLRABThunks[
I];
96 reg_bitmask_t XnBit = reg_bitmask_t(1) << indexOfXReg(Xn);
97 return getBitmask(Kind, Xm) & XnBit;
101 reg_bitmask_t XnBit = reg_bitmask_t(1) << indexOfXReg(Xn);
102 getBitmask(Kind, Xm) |= XnBit;
107 static_assert(NumXRegisters <=
sizeof(reg_bitmask_t) * CHAR_BIT,
108 "Bitmask is not wide enough to hold all Xn registers");
115 reg_bitmask_t BLRThunks = 0;
116 reg_bitmask_t BLRAAZThunks = 0;
117 reg_bitmask_t BLRABZThunks = 0;
118 reg_bitmask_t BLRAAThunks[NumXRegisters] = {};
119 reg_bitmask_t BLRABThunks[NumXRegisters] = {};
121 reg_bitmask_t &getBitmask(ThunkKind::ThunkKindId Kind,
Register Xm) {
123 case ThunkKind::ThunkBR:
125 case ThunkKind::ThunkBRAAZ:
127 case ThunkKind::ThunkBRABZ:
129 case ThunkKind::ThunkBRAA:
130 return BLRAAThunks[indexOfXReg(Xm)];
131 case ThunkKind::ThunkBRAB:
132 return BLRABThunks[indexOfXReg(Xm)];
138struct SLSHardeningInserter :
ThunkInserter<SLSHardeningInserter, ThunksSet> {
149 ThunksSet ExistingThunks);
153 bool ComdatThunks =
true;
166const ThunkKind ThunkKind::BR = {ThunkBR,
"",
false,
168const ThunkKind ThunkKind::BRAA = {ThunkBRAA,
"aa_",
true,
169 true, AArch64::BRAA};
170const ThunkKind ThunkKind::BRAB = {ThunkBRAB,
"ab_",
true,
171 true, AArch64::BRAB};
172const ThunkKind ThunkKind::BRAAZ = {ThunkBRAAZ,
"aaz_",
false,
173 true, AArch64::BRAAZ};
174const ThunkKind ThunkKind::BRABZ = {ThunkBRABZ,
"abz_",
false,
175 true, AArch64::BRABZ};
179 switch (OriginalOpcode) {
181 case AArch64::BLRNoIP:
182 return &ThunkKind::BR;
184 return &ThunkKind::BRAA;
186 return &ThunkKind::BRAB;
187 case AArch64::BLRAAZ:
188 return &ThunkKind::BRAAZ;
189 case AArch64::BLRABZ:
190 return &ThunkKind::BRABZ;
199unsigned ThunksSet::indexOfXReg(
Register Reg) {
201 assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR);
205 if (Reg == AArch64::FP)
207 else if (Reg == AArch64::XZR)
210 assert(Result < NumXRegisters &&
"Internal register numbering changed");
211 assert(AArch64::GPR64RegClass.getRegister(Result).
id() == Reg &&
212 "Internal register numbering changed");
217Register ThunksSet::xRegByIndex(
unsigned N) {
218 return AArch64::GPR64RegClass.getRegister(
N);
227 "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
229 "SpeculationBarrierEndBB must only follow unconditional control flow "
232 "SpeculationBarrierEndBB must only follow terminators.");
235 ? AArch64::SpeculationBarrierSBEndBB
236 : AArch64::SpeculationBarrierISBDSBEndBB;
238 (
MBBI->getOpcode() != AArch64::SpeculationBarrierSBEndBB &&
239 MBBI->getOpcode() != AArch64::SpeculationBarrierISBDSBEndBB))
245 ThunksSet ExistingThunks) {
248 for (
auto &
MBB : MF) {
249 if (
ST->hardenSlsRetBr())
250 hardenReturnsAndBRs(MMI,
MBB);
251 if (
ST->hardenSlsBlr())
252 hardenBLRs(MMI,
MBB, ExistingThunks);
254 return ExistingThunks;
264 for (;
MBBI != E;
MBBI = NextMBBI) {
266 NextMBBI = std::next(
MBBI);
281 unsigned N = ThunksSet::indexOfXReg(Xn);
282 if (!Kind.HasXmOperand)
285 unsigned M = ThunksSet::indexOfXReg(Xm);
289static std::tuple<const ThunkKind &, Register, Register>
292 "Should be filtered out by ThunkInserter");
307 assert(
Name.starts_with(
"x") &&
"xN register name expected");
308 bool Fail =
Name.drop_front(1).getAsInteger(10,
N);
309 assert(!
Fail &&
N < ThunksSet::NumXRegisters &&
"Unexpected register");
312 return ThunksSet::xRegByIndex(
N);
318 std::tie(XnStr, XmStr) = RegsStr.
split(
'_');
322 Register Xm = Kind.HasXmOperand ? ParseRegName(XmStr) : AArch64::NoRegister;
324 return std::make_tuple(std::ref(Kind), Xn, Xm);
329 "ComdatThunks value changed since MF creation");
332 const ThunkKind &
Kind = std::get<0>(KindAndRegs);
333 std::tie(std::ignore, Xn, Xm) = KindAndRegs;
342 if (MF.
size() == 1) {
362 Entry->addLiveIn(Xn);
370 if (Xm != AArch64::NoRegister) {
371 Entry->addLiveIn(Xm);
383void SLSHardeningInserter::convertBLRToBL(
426 unsigned NumRegOperands =
Kind.HasXmOperand ? 2 : 1;
428 "Expected one or two register inputs");
442 if (!Thunks.get(
Kind.Id, Xn, Xm)) {
444 Thunks.set(
Kind.Id, Xn, Xm);
445 createThunkFunction(MMI, ThunkName, ComdatThunks, TargetAttrs);
460 for (
unsigned OpIdx =
BL->getNumExplicitOperands();
461 OpIdx < BL->getNumOperands(); OpIdx++) {
465 if (
Op.getReg() == AArch64::LR &&
Op.isDef())
467 if (
Op.getReg() == AArch64::SP && !
Op.isDef())
472 int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx);
473 int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx);
474 BL->removeOperand(FirstOpIdxToRemove);
475 BL->removeOperand(SecondOpIdxToRemove);
477 BL->copyImplicitOps(MF, BLR);
481 for (
unsigned OpIdx = 0; OpIdx < NumRegOperands; ++OpIdx) {
497 for (;
MBBI != E;
MBBI = NextMBBI) {
499 NextMBBI = std::next(
MBBI);
501 convertBLRToBL(MMI,
MBB,
MBBI, Thunks);
520char AArch64SLSHardening::ID = 0;
526 return new AArch64SLSHardening();
for(const MachineOperand &MO :llvm::drop_begin(OldMI.operands(), Desc.getNumOperands()))
#define AARCH64_SLS_HARDENING_NAME
static void insertSpeculationBarrier(const AArch64Subtarget *ST, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL, bool AlwaysUseISBDSB=false)
static constexpr StringRef CommonNamePrefix
static SmallString< 32 > createThunkName(const ThunkKind &Kind, Register Xn, Register Xm)
static std::tuple< const ThunkKind &, Register, Register > parseThunkName(StringRef ThunkName)
static const ThunkKind * getThunkKind(unsigned OriginalOpcode)
static bool isBLR(const MachineInstr &MI)
MachineBasicBlock MachineBasicBlock::iterator DebugLoc bool AlwaysUseISBDSB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
MachineBasicBlock MachineBasicBlock::iterator MBBI
std::optional< std::vector< StOtherPiece > > Other
const HexagonInstrInfo * TII
Contains a base ThunkInserter class that simplifies injection of MI thunks as well as a default imple...
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
This file declares the machine register scavenger class.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
This class represents an Operation in the Expression.
FunctionPass class - This class is used to implement most global optimizations.
Context object for machine code objects.
MCSymbol * getOrCreateSymbol(const Twine &Name)
Lookup the symbol inside with the specified Name.
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
instr_iterator instr_begin()
iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
Instructions::iterator instr_iterator
instr_iterator instr_end()
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
instr_iterator erase(instr_iterator I)
Remove an instruction from the instruction list and delete it.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
void push_back(MachineBasicBlock *MBB)
MCContext & getContext() const
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineBasicBlock & front() const
void moveCallSiteInfo(const MachineInstr *Old, const MachineInstr *New)
Move the call site info from Old to \New call site info.
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
unsigned getNumExplicitOperands() const
Returns the number of non-implicit operands.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
This class contains meta information specific to a module.
MachineOperand class - Representation of each machine instruction operand.
Register getReg() const
getReg - Returns the register number.
static MachineOperand CreateReg(Register Reg, bool isDef, bool isImp=false, bool isKill=false, bool isDead=false, bool isUndef=false, bool isEarlyClobber=false, unsigned SubReg=0, bool isDebug=false, bool isInternalRead=false, bool isRenamable=false)
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Wrapper class representing virtual and physical registers.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
StringRef - Represent a constant reference to a string, i.e.
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
constexpr size_t size() const
size - Get the string size.
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
A switch()-like statement whose cases are string literals.
StringSwitch & StartsWith(StringLiteral S, T Value)
TargetInstrInfo - Interface to description of machine instruction set.
virtual const TargetInstrInfo * getInstrInfo() const
Basic implementation of MachineFunctionPass wrapping one or more ThunkInserters passed as type parame...
This class assists in inserting MI thunk functions into the module and rewriting the existing machine...
bool mayUseThunk(const MachineFunction &MF)
Checks if MF may use thunks (true - maybe, false - definitely not).
InsertedThunksTy insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, InsertedThunksTy ExistingThunks)
Rewrites the function if necessary, returns the set of thunks added.
const char * getThunkPrefix()
Returns common prefix for thunk function's names.
void populateThunk(MachineFunction &MF)
Populate the thunk function with instructions.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
This is an optimization pass for GlobalISel generic memory operations.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
static bool isIndirectBranchOpcode(int Opc)
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
FunctionPass * createAArch64SLSHardeningPass()
bool operator|=(SparseBitVector< ElementSize > &LHS, const SparseBitVector< ElementSize > *RHS)