LLVM 24.0.0git
AArch64StackTaggingPreRA.cpp
Go to the documentation of this file.
1//===-- AArch64StackTaggingPreRA.cpp --- Stack Tagging for AArch64 -----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "AArch64.h"
10#include "AArch64InstrInfo.h"
12#include "llvm/ADT/SetVector.h"
13#include "llvm/ADT/Statistic.h"
20#include "llvm/CodeGen/Passes.h"
26#include "llvm/Support/Debug.h"
28
29using namespace llvm;
30
31#define DEBUG_TYPE "aarch64-stack-tagging-pre-ra"
32
34
36 "stack-tagging-unchecked-ld-st", cl::Hidden, cl::init(UncheckedSafe),
38 "Unconditionally apply unchecked-ld-st optimization (even for large "
39 "stack frames, or in the presence of variable sized allocas)."),
41 clEnumValN(UncheckedNever, "never", "never apply unchecked-ld-st"),
43 UncheckedSafe, "safe",
44 "apply unchecked-ld-st when the target is definitely within range"),
45 clEnumValN(UncheckedAlways, "always", "always apply unchecked-ld-st")));
46
47static cl::opt<bool>
48 ClFirstSlot("stack-tagging-first-slot-opt", cl::Hidden, cl::init(true),
49 cl::desc("Apply first slot optimization for stack tagging "
50 "(eliminate ADDG Rt, Rn, 0, 0)."));
51
52namespace {
53
54class AArch64StackTaggingPreRAImpl {
60 const AArch64InstrInfo *TII;
61
63
64public:
65 bool run(MachineFunction &Func);
66
67private:
68 bool mayUseUncheckedLoadStore();
69 void uncheckUsesOf(unsigned TaggedReg, int FI);
70 void uncheckLoadsAndStores();
71 std::optional<int> findFirstSlotCandidate();
72};
73
74class AArch64StackTaggingPreRALegacy : public MachineFunctionPass {
75public:
76 static char ID;
77 AArch64StackTaggingPreRALegacy() : MachineFunctionPass(ID) {}
78
79 bool runOnMachineFunction(MachineFunction &MF) override {
80 if (skipFunction(MF.getFunction()))
81 return false;
82 return AArch64StackTaggingPreRAImpl().run(MF);
83 }
84
85 StringRef getPassName() const override {
86 return "AArch64 Stack Tagging PreRA";
87 }
88
89 void getAnalysisUsage(AnalysisUsage &AU) const override {
90 AU.setPreservesCFG();
92 }
93};
94} // end anonymous namespace
95
96char AArch64StackTaggingPreRALegacy::ID = 0;
97
98INITIALIZE_PASS_BEGIN(AArch64StackTaggingPreRALegacy,
99 "aarch64-stack-tagging-pre-ra",
100 "AArch64 Stack Tagging PreRA Pass", false, false)
101INITIALIZE_PASS_END(AArch64StackTaggingPreRALegacy,
102 "aarch64-stack-tagging-pre-ra",
103 "AArch64 Stack Tagging PreRA Pass", false, false)
104
106 return new AArch64StackTaggingPreRALegacy();
107}
108
112 if (AArch64StackTaggingPreRAImpl().run(MF)) {
115 return PA;
116 }
117 return PreservedAnalyses::all();
118}
119
120static bool isUncheckedLoadOrStoreOpcode(unsigned Opcode) {
121 switch (Opcode) {
122 case AArch64::LDRBBui:
123 case AArch64::LDRHHui:
124 case AArch64::LDRWui:
125 case AArch64::LDRXui:
126
127 case AArch64::LDRBui:
128 case AArch64::LDRHui:
129 case AArch64::LDRSui:
130 case AArch64::LDRDui:
131 case AArch64::LDRQui:
132
133 case AArch64::LDRSHWui:
134 case AArch64::LDRSHXui:
135
136 case AArch64::LDRSBWui:
137 case AArch64::LDRSBXui:
138
139 case AArch64::LDRSWui:
140
141 case AArch64::STRBBui:
142 case AArch64::STRHHui:
143 case AArch64::STRWui:
144 case AArch64::STRXui:
145
146 case AArch64::STRBui:
147 case AArch64::STRHui:
148 case AArch64::STRSui:
149 case AArch64::STRDui:
150 case AArch64::STRQui:
151
152 case AArch64::LDPWi:
153 case AArch64::LDPXi:
154 case AArch64::LDPSi:
155 case AArch64::LDPDi:
156 case AArch64::LDPQi:
157
158 case AArch64::LDPSWi:
159
160 case AArch64::STPWi:
161 case AArch64::STPXi:
162 case AArch64::STPSi:
163 case AArch64::STPDi:
164 case AArch64::STPQi:
165 return true;
166 default:
167 return false;
168 }
169}
170
171bool AArch64StackTaggingPreRAImpl::mayUseUncheckedLoadStore() {
173 return false;
175 return true;
176
177 // This estimate can be improved if we had harder guarantees about stack frame
178 // layout. With LocalStackAllocation we can estimate SP offset to any
179 // preallocated slot. AArch64FrameLowering::orderFrameObjects could put tagged
180 // objects ahead of non-tagged ones, but that's not always desirable.
181 //
182 // Underestimating SP offset here may require the use of LDG to materialize
183 // the tagged address of the stack slot, along with a scratch register
184 // allocation (post-regalloc!).
185 //
186 // For now we do the safe thing here and require that the entire stack frame
187 // is within range of the shortest of the unchecked instructions.
188 unsigned FrameSize = 0;
189 for (unsigned i = 0, e = MFI->getObjectIndexEnd(); i != e; ++i)
190 FrameSize += MFI->getObjectSize(i);
191 bool EntireFrameReachableFromSP = FrameSize < 0xf00;
192 return !MFI->hasVarSizedObjects() && EntireFrameReachableFromSP;
193}
194
195void AArch64StackTaggingPreRAImpl::uncheckUsesOf(unsigned TaggedReg, int FI) {
196 for (MachineInstr &UseI :
197 llvm::make_early_inc_range(MRI->use_instructions(TaggedReg))) {
198 if (isUncheckedLoadOrStoreOpcode(UseI.getOpcode())) {
199 // FI operand is always the one before the immediate offset.
200 unsigned OpIdx = TII->getLoadStoreImmIdx(UseI.getOpcode()) - 1;
201 if (UseI.getOperand(OpIdx).isReg() &&
202 UseI.getOperand(OpIdx).getReg() == TaggedReg) {
203 UseI.getOperand(OpIdx).ChangeToFrameIndex(FI);
204 UseI.getOperand(OpIdx).setTargetFlags(AArch64II::MO_TAGGED);
205 }
206 } else if (UseI.isCopy() && UseI.getOperand(0).getReg().isVirtual()) {
207 uncheckUsesOf(UseI.getOperand(0).getReg(), FI);
208 }
209 }
210}
211
212void AArch64StackTaggingPreRAImpl::uncheckLoadsAndStores() {
213 for (auto *I : ReTags) {
214 Register TaggedReg = I->getOperand(0).getReg();
215 int FI = I->getOperand(1).getIndex();
216 uncheckUsesOf(TaggedReg, FI);
217 }
218}
219
220namespace {
221struct SlotWithTag {
222 int FI;
223 int Tag;
224 SlotWithTag(int FI, int Tag) : FI(FI), Tag(Tag) {}
225 explicit SlotWithTag(const MachineInstr &MI)
226 : FI(MI.getOperand(1).getIndex()), Tag(MI.getOperand(4).getImm()) {}
227 bool operator==(const SlotWithTag &Other) const {
228 return FI == Other.FI && Tag == Other.Tag;
229 }
230};
231} // namespace
232
233namespace llvm {
234template <> struct DenseMapInfo<SlotWithTag> {
235 static unsigned getHashValue(const SlotWithTag &V) {
238 }
239 static bool isEqual(const SlotWithTag &A, const SlotWithTag &B) {
240 return A == B;
241 }
242};
243} // namespace llvm
244
245static bool isSlotPreAllocated(MachineFrameInfo *MFI, int FI) {
246 return MFI->getUseLocalStackAllocationBlock() &&
247 MFI->isObjectPreAllocated(FI);
248}
249
250// Pin one of the tagged slots to offset 0 from the tagged base pointer.
251// This would make its address available in a virtual register (IRG's def), as
252// opposed to requiring an ADDG instruction to materialize. This effectively
253// eliminates a vreg (by replacing it with direct uses of IRG, which is usually
254// live almost everywhere anyway), and therefore needs to happen before
255// regalloc.
256std::optional<int> AArch64StackTaggingPreRAImpl::findFirstSlotCandidate() {
257 // Find the best (FI, Tag) pair to pin to offset 0.
258 // Looking at the possible uses of a tagged address, the advantage of pinning
259 // is:
260 // - COPY to physical register.
261 // Does not matter, this would trade a MOV instruction for an ADDG.
262 // - ST*G matter, but those mostly appear near the function prologue where all
263 // the tagged addresses need to be materialized anyway; also, counting ST*G
264 // uses would overweight large allocas that require more than one ST*G
265 // instruction.
266 // - Load/Store instructions in the address operand do not require a tagged
267 // pointer, so they also do not benefit. These operands have already been
268 // eliminated (see uncheckLoadsAndStores) so all remaining load/store
269 // instructions count.
270 // - Any other instruction may benefit from being pinned to offset 0.
272 dbgs() << "AArch64StackTaggingPreRAImpl::findFirstSlotCandidate\n");
273 if (!ClFirstSlot)
274 return std::nullopt;
275
277 SlotWithTag MaxScoreST{-1, -1};
278 int MaxScore = -1;
279 for (auto *I : ReTags) {
280 SlotWithTag ST{*I};
281 if (isSlotPreAllocated(MFI, ST.FI))
282 continue;
283
284 Register RetagReg = I->getOperand(0).getReg();
285 if (!RetagReg.isVirtual())
286 continue;
287
288 int Score = 0;
290 WorkList.push_back(RetagReg);
291
292 while (!WorkList.empty()) {
293 Register UseReg = WorkList.pop_back_val();
294 for (auto &UseI : MRI->use_instructions(UseReg)) {
295 unsigned Opcode = UseI.getOpcode();
296 if (Opcode == AArch64::STGi || Opcode == AArch64::ST2Gi ||
297 Opcode == AArch64::STZGi || Opcode == AArch64::STZ2Gi ||
298 Opcode == AArch64::STGPi || Opcode == AArch64::STGloop ||
299 Opcode == AArch64::STZGloop || Opcode == AArch64::STGloop_wback ||
300 Opcode == AArch64::STZGloop_wback)
301 continue;
302 if (UseI.isCopy()) {
303 Register DstReg = UseI.getOperand(0).getReg();
304 if (DstReg.isVirtual())
305 WorkList.push_back(DstReg);
306 continue;
307 }
308 LLVM_DEBUG(dbgs() << "[" << ST.FI << ":" << ST.Tag << "] use of "
309 << printReg(UseReg) << " in " << UseI << "\n");
310 Score++;
311 }
312 }
313
314 int TotalScore = RetagScore[ST] += Score;
315 if (TotalScore > MaxScore ||
316 (TotalScore == MaxScore && ST.FI > MaxScoreST.FI)) {
317 MaxScore = TotalScore;
318 MaxScoreST = ST;
319 }
320 }
321
322 if (MaxScoreST.FI < 0)
323 return std::nullopt;
324
325 // If FI's tag is already 0, we are done.
326 if (MaxScoreST.Tag == 0)
327 return MaxScoreST.FI;
328
329 // Otherwise, find a random victim pair (FI, Tag) where Tag == 0.
330 SlotWithTag SwapST{-1, -1};
331 for (auto *I : ReTags) {
332 SlotWithTag ST{*I};
333 if (ST.Tag == 0) {
334 SwapST = ST;
335 break;
336 }
337 }
338
339 // Swap tags between the victim and the highest scoring pair.
340 // If SwapWith is still (-1, -1), that's fine, too - we'll simply take tag for
341 // the highest score slot without changing anything else.
342 for (auto *&I : ReTags) {
343 SlotWithTag ST{*I};
344 MachineOperand &TagOp = I->getOperand(4);
345 if (ST == MaxScoreST) {
346 TagOp.setImm(0);
347 } else if (ST == SwapST) {
348 TagOp.setImm(MaxScoreST.Tag);
349 }
350 }
351 return MaxScoreST.FI;
352}
353
354bool AArch64StackTaggingPreRAImpl::run(MachineFunction &Func) {
355 MF = &Func;
356 MRI = &MF->getRegInfo();
357 AFI = MF->getInfo<AArch64FunctionInfo>();
358 TII = static_cast<const AArch64InstrInfo *>(MF->getSubtarget().getInstrInfo());
359 TRI = static_cast<const AArch64RegisterInfo *>(
360 MF->getSubtarget().getRegisterInfo());
361 MFI = &MF->getFrameInfo();
362 ReTags.clear();
363
364 assert(MRI->isSSA());
365
366 LLVM_DEBUG(dbgs() << "********** AArch64 Stack Tagging PreRA **********\n"
367 << "********** Function: " << MF->getName() << '\n');
368
369 SmallSetVector<int, 8> TaggedSlots;
370 for (auto &BB : *MF) {
371 for (auto &I : BB) {
372 if (I.getOpcode() == AArch64::TAGPstack) {
373 ReTags.push_back(&I);
374 int FI = I.getOperand(1).getIndex();
375 TaggedSlots.insert(FI);
376 // There should be no offsets in TAGP yet.
377 assert(I.getOperand(2).getImm() == 0);
378 }
379 }
380 }
381
382 // Take over from SSP. It does nothing for tagged slots, and should not really
383 // have been enabled in the first place.
384 for (int FI : TaggedSlots)
385 MFI->setObjectSSPLayout(FI, MachineFrameInfo::SSPLK_None);
386
387 if (ReTags.empty())
388 return false;
389
390 if (mayUseUncheckedLoadStore())
391 uncheckLoadsAndStores();
392
393 // Find a slot that is used with zero tag offset, like ADDG #fi, 0.
394 // If the base tagged pointer is set up to the address of this slot,
395 // the ADDG instruction can be eliminated.
396 std::optional<int> BaseSlot = findFirstSlotCandidate();
397 if (BaseSlot)
398 AFI->setTaggedBasePointerIndex(*BaseSlot);
399
400 for (auto *I : ReTags) {
401 int FI = I->getOperand(1).getIndex();
402 int Tag = I->getOperand(4).getImm();
403 Register Base = I->getOperand(3).getReg();
404 if (Tag == 0 && FI == BaseSlot) {
405 BuildMI(*I->getParent(), I, {}, TII->get(AArch64::COPY),
406 I->getOperand(0).getReg())
407 .addReg(Base);
409 }
410 }
411
412 return true;
413}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static bool isSlotPreAllocated(MachineFrameInfo *MFI, int FI)
static cl::opt< UncheckedLdStMode > ClUncheckedLdSt("stack-tagging-unchecked-ld-st", cl::Hidden, cl::init(UncheckedSafe), cl::desc("Unconditionally apply unchecked-ld-st optimization (even for large " "stack frames, or in the presence of variable sized allocas)."), cl::values(clEnumValN(UncheckedNever, "never", "never apply unchecked-ld-st"), clEnumValN(UncheckedSafe, "safe", "apply unchecked-ld-st when the target is definitely within range"), clEnumValN(UncheckedAlways, "always", "always apply unchecked-ld-st")))
static cl::opt< bool > ClFirstSlot("stack-tagging-first-slot-opt", cl::Hidden, cl::init(true), cl::desc("Apply first slot optimization for stack tagging " "(eliminate ADDG Rt, Rn, 0, 0)."))
static bool isUncheckedLoadOrStoreOpcode(unsigned Opcode)
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
static Register UseReg(const MachineOperand &MO)
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
MachineInstr unsigned OpIdx
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file implements a set that has insertion order iteration characteristics.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define LLVM_DEBUG(...)
Definition Debug.h:119
AArch64FunctionInfo - This class is derived from MachineFunctionInfo and contains private AArch64-spe...
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:275
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
The MachineFrameInfo class represents an abstract stack frame until prolog/epilog code is inserted.
bool isObjectPreAllocated(int ObjectIdx) const
Return true if the object was pre-allocated into the local block.
@ SSPLK_None
Did not trigger a stack protector.
bool getUseLocalStackAllocationBlock() const
Get whether the local allocation blob should be allocated together or let PEI allocate the locals in ...
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
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.
MachineFrameInfo & getFrameInfo()
getFrameInfo - Return the frame info object for the current function.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
Representation of each machine instruction.
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
MachineOperand class - Representation of each machine instruction operand.
void setImm(int64_t immVal)
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
Definition Register.h:79
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:157
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
@ MO_TAGGED
MO_TAGGED - With MO_PAGE, indicates that the page includes a memory tag in bits 56-63.
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
initializer< Ty > init(const Ty &Val)
NodeAddr< FuncNode * > Func
Definition RDFGraph.h:393
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.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
bool operator==(const AddressRangeValuePair &LHS, const AddressRangeValuePair &RHS)
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
FunctionPass * createAArch64StackTaggingPreRALegacyPass()
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
@ Other
Any other memory.
Definition ModRef.h:68
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition Hashing.h:305
LLVM_ABI Printable printReg(Register Reg, const TargetRegisterInfo *TRI=nullptr, unsigned SubIdx=0, const MachineRegisterInfo *MRI=nullptr)
Prints virtual and physical registers with or without a TRI instance.
static bool isEqual(const SlotWithTag &A, const SlotWithTag &B)
static unsigned getHashValue(const SlotWithTag &V)
An information struct used to provide DenseMap with the various necessary components for a given valu...