1 //===- AArch64StackTagging.cpp - Stack tagging in IR --===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
12 #include "AArch64InstrInfo.h"
13 #include "AArch64Subtarget.h"
14 #include "AArch64TargetMachine.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/DepthFirstIterator.h"
17 #include "llvm/ADT/MapVector.h"
18 #include "llvm/ADT/None.h"
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/Statistic.h"
22 #include "llvm/Analysis/LoopInfo.h"
23 #include "llvm/Analysis/ScalarEvolution.h"
24 #include "llvm/Analysis/ScalarEvolutionExpressions.h"
25 #include "llvm/Analysis/ValueTracking.h"
26 #include "llvm/CodeGen/LiveRegUnits.h"
27 #include "llvm/CodeGen/MachineBasicBlock.h"
28 #include "llvm/CodeGen/MachineFunction.h"
29 #include "llvm/CodeGen/MachineFunctionPass.h"
30 #include "llvm/CodeGen/MachineInstr.h"
31 #include "llvm/CodeGen/MachineInstrBuilder.h"
32 #include "llvm/CodeGen/MachineLoopInfo.h"
33 #include "llvm/CodeGen/MachineOperand.h"
34 #include "llvm/CodeGen/MachineRegisterInfo.h"
35 #include "llvm/CodeGen/TargetPassConfig.h"
36 #include "llvm/CodeGen/TargetRegisterInfo.h"
37 #include "llvm/IR/DebugLoc.h"
38 #include "llvm/IR/Dominators.h"
39 #include "llvm/IR/Function.h"
40 #include "llvm/IR/GetElementPtrTypeIterator.h"
41 #include "llvm/IR/Instruction.h"
42 #include "llvm/IR/Instructions.h"
43 #include "llvm/IR/IntrinsicInst.h"
44 #include "llvm/IR/Metadata.h"
45 #include "llvm/Pass.h"
46 #include "llvm/Support/Casting.h"
47 #include "llvm/Support/Debug.h"
48 #include "llvm/Support/raw_ostream.h"
49 #include "llvm/Transforms/Utils/Local.h"
56 #define DEBUG_TYPE "stack-tagging"
58 static constexpr unsigned kTagGranuleSize = 16;
61 class AArch64StackTagging : public FunctionPass {
64 SmallVector<IntrinsicInst *, 2> LifetimeStart;
65 SmallVector<IntrinsicInst *, 2> LifetimeEnd;
66 SmallVector<DbgVariableIntrinsic *, 2> DbgVariableIntrinsics;
67 int Tag; // -1 for non-tagged allocations
71 static char ID; // Pass ID, replacement for typeid
73 AArch64StackTagging() : FunctionPass(ID) {
74 initializeAArch64StackTaggingPass(*PassRegistry::getPassRegistry());
77 bool isInterestingAlloca(const AllocaInst &AI);
78 void alignAndPadAlloca(AllocaInfo &Info);
80 void tagAlloca(AllocaInst *AI, Instruction *InsertBefore, Value *Ptr,
82 void untagAlloca(AllocaInst *AI, Instruction *InsertBefore, uint64_t Size);
85 insertBaseTaggedPointer(const MapVector<AllocaInst *, AllocaInfo> &Allocas,
86 const DominatorTree *DT);
87 bool runOnFunction(Function &F) override;
89 StringRef getPassName() const override { return "AArch64 Stack Tagging"; }
96 void getAnalysisUsage(AnalysisUsage &AU) const override {
101 } // end anonymous namespace
103 char AArch64StackTagging::ID = 0;
105 INITIALIZE_PASS_BEGIN(AArch64StackTagging, DEBUG_TYPE, "AArch64 Stack Tagging",
107 INITIALIZE_PASS_END(AArch64StackTagging, DEBUG_TYPE, "AArch64 Stack Tagging",
110 FunctionPass *llvm::createAArch64StackTaggingPass() {
111 return new AArch64StackTagging();
114 bool AArch64StackTagging::isInterestingAlloca(const AllocaInst &AI) {
115 // FIXME: support dynamic allocas
117 AI.getAllocatedType()->isSized() && AI.isStaticAlloca() &&
118 // alloca() may be called with 0 size, ignore it.
119 AI.getAllocationSizeInBits(*DL).getValue() > 0 &&
120 // inalloca allocas are not treated as static, and we don't want
121 // dynamic alloca instrumentation for them as well.
122 !AI.isUsedWithInAlloca() &&
123 // swifterror allocas are register promoted by ISel
125 return IsInteresting;
128 void AArch64StackTagging::tagAlloca(AllocaInst *AI, Instruction *InsertBefore,
129 Value *Ptr, uint64_t Size) {
130 IRBuilder<> IRB(InsertBefore);
131 IRB.CreateCall(SetTagFunc, {Ptr, ConstantInt::get(IRB.getInt64Ty(), Size)});
134 void AArch64StackTagging::untagAlloca(AllocaInst *AI, Instruction *InsertBefore,
136 IRBuilder<> IRB(InsertBefore);
137 IRB.CreateCall(SetTagFunc, {IRB.CreatePointerCast(AI, IRB.getInt8PtrTy()),
138 ConstantInt::get(IRB.getInt64Ty(), Size)});
141 Instruction *AArch64StackTagging::insertBaseTaggedPointer(
142 const MapVector<AllocaInst *, AllocaInfo> &Allocas,
143 const DominatorTree *DT) {
144 BasicBlock *PrologueBB = nullptr;
145 // Try sinking IRG as deep as possible to avoid hurting shrink wrap.
146 for (auto &I : Allocas) {
147 const AllocaInfo &Info = I.second;
148 AllocaInst *AI = Info.AI;
152 PrologueBB = AI->getParent();
155 PrologueBB = DT->findNearestCommonDominator(PrologueBB, AI->getParent());
159 IRBuilder<> IRB(&PrologueBB->front());
161 Intrinsic::getDeclaration(F->getParent(), Intrinsic::aarch64_irg_sp);
163 IRB.CreateCall(IRG_SP, {Constant::getNullValue(IRB.getInt64Ty())});
164 Base->setName("basetag");
168 void AArch64StackTagging::alignAndPadAlloca(AllocaInfo &Info) {
169 unsigned NewAlignment = std::max(Info.AI->getAlignment(), kTagGranuleSize);
170 Info.AI->setAlignment(NewAlignment);
172 uint64_t Size = Info.AI->getAllocationSizeInBits(*DL).getValue() / 8;
173 uint64_t AlignedSize = alignTo(Size, kTagGranuleSize);
174 if (Size == AlignedSize)
177 // Add padding to the alloca.
178 Type *AllocatedType =
179 Info.AI->isArrayAllocation()
181 Info.AI->getAllocatedType(),
182 dyn_cast<ConstantInt>(Info.AI->getArraySize())->getZExtValue())
183 : Info.AI->getAllocatedType();
185 ArrayType::get(Type::getInt8Ty(F->getContext()), AlignedSize - Size);
186 Type *TypeWithPadding = StructType::get(AllocatedType, PaddingType);
187 auto *NewAI = new AllocaInst(
188 TypeWithPadding, Info.AI->getType()->getAddressSpace(), nullptr, "", Info.AI);
189 NewAI->takeName(Info.AI);
190 NewAI->setAlignment(Info.AI->getAlignment());
191 NewAI->setUsedWithInAlloca(Info.AI->isUsedWithInAlloca());
192 NewAI->setSwiftError(Info.AI->isSwiftError());
193 NewAI->copyMetadata(*Info.AI);
195 auto *NewPtr = new BitCastInst(NewAI, Info.AI->getType(), "", Info.AI);
196 Info.AI->replaceAllUsesWith(NewPtr);
197 Info.AI->eraseFromParent();
201 // FIXME: check for MTE extension
202 bool AArch64StackTagging::runOnFunction(Function &Fn) {
203 if (!Fn.hasFnAttribute(Attribute::SanitizeMemTag))
207 DL = &Fn.getParent()->getDataLayout();
209 MapVector<AllocaInst *, AllocaInfo> Allocas; // need stable iteration order
210 SmallVector<Instruction *, 8> RetVec;
211 DenseMap<Value *, AllocaInst *> AllocaForValue;
212 SmallVector<Instruction *, 4> UnrecognizedLifetimes;
214 for (auto &BB : *F) {
215 for (BasicBlock::iterator IT = BB.begin(); IT != BB.end(); ++IT) {
216 Instruction *I = &*IT;
217 if (auto *AI = dyn_cast<AllocaInst>(I)) {
222 if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(I)) {
224 dyn_cast_or_null<AllocaInst>(DVI->getVariableLocation())) {
225 Allocas[AI].DbgVariableIntrinsics.push_back(DVI);
230 auto *II = dyn_cast<IntrinsicInst>(I);
231 if (II && (II->getIntrinsicID() == Intrinsic::lifetime_start ||
232 II->getIntrinsicID() == Intrinsic::lifetime_end)) {
234 llvm::findAllocaForValue(II->getArgOperand(1), AllocaForValue);
236 UnrecognizedLifetimes.push_back(I);
239 if (II->getIntrinsicID() == Intrinsic::lifetime_start)
240 Allocas[AI].LifetimeStart.push_back(II);
242 Allocas[AI].LifetimeEnd.push_back(II);
245 if (isa<ReturnInst>(I) || isa<ResumeInst>(I) || isa<CleanupReturnInst>(I))
254 int NumInterestingAllocas = 0;
255 for (auto &I : Allocas) {
256 AllocaInfo &Info = I.second;
259 if (!isInterestingAlloca(*Info.AI)) {
264 alignAndPadAlloca(Info);
265 NumInterestingAllocas++;
267 NextTag = (NextTag + 1) % 16;
270 if (NumInterestingAllocas == 0)
274 Intrinsic::getDeclaration(F->getParent(), Intrinsic::aarch64_settag);
276 // Compute DT only if the function has the attribute, there are more than 1
277 // interesting allocas, and it is not available for free.
279 if (NumInterestingAllocas > 1) {
280 auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>();
282 Base = insertBaseTaggedPointer(Allocas, &DTWP->getDomTree());
284 DominatorTree DT(*F);
285 Base = insertBaseTaggedPointer(Allocas, &DT);
288 Base = insertBaseTaggedPointer(Allocas, nullptr);
291 for (auto &I : Allocas) {
292 const AllocaInfo &Info = I.second;
293 AllocaInst *AI = Info.AI;
297 // Replace alloca with tagp(alloca).
298 IRBuilder<> IRB(Info.AI->getNextNode());
299 Function *TagP = Intrinsic::getDeclaration(
300 F->getParent(), Intrinsic::aarch64_tagp, {Info.AI->getType()});
301 Instruction *TagPCall =
302 IRB.CreateCall(TagP, {Constant::getNullValue(Info.AI->getType()), Base,
303 ConstantInt::get(IRB.getInt64Ty(), Info.Tag)});
304 if (Info.AI->hasName())
305 TagPCall->setName(Info.AI->getName() + ".tag");
306 Info.AI->replaceAllUsesWith(TagPCall);
307 TagPCall->setOperand(0, Info.AI);
309 if (UnrecognizedLifetimes.empty() && Info.LifetimeStart.size() == 1 &&
310 Info.LifetimeEnd.size() == 1) {
311 IntrinsicInst *Start = Info.LifetimeStart[0];
313 dyn_cast<ConstantInt>(Start->getArgOperand(0))->getZExtValue();
314 Size = alignTo(Size, kTagGranuleSize);
315 tagAlloca(AI, Start->getNextNode(), Start->getArgOperand(1), Size);
316 untagAlloca(AI, Info.LifetimeEnd[0], Size);
318 uint64_t Size = Info.AI->getAllocationSizeInBits(*DL).getValue() / 8;
319 Value *Ptr = IRB.CreatePointerCast(TagPCall, IRB.getInt8PtrTy());
320 tagAlloca(AI, &*IRB.GetInsertPoint(), Ptr, Size);
321 for (auto &RI : RetVec) {
322 untagAlloca(AI, RI, Size);
324 // We may have inserted tag/untag outside of any lifetime interval.
325 // Remove all lifetime intrinsics for this alloca.
326 for (auto &II : Info.LifetimeStart)
327 II->eraseFromParent();
328 for (auto &II : Info.LifetimeEnd)
329 II->eraseFromParent();
332 // Fixup debug intrinsics to point to the new alloca.
333 for (auto DVI : Info.DbgVariableIntrinsics)
336 MetadataAsValue::get(F->getContext(), LocalAsMetadata::get(Info.AI)));
339 // If we have instrumented at least one alloca, all unrecognized lifetime
340 // instrinsics have to go.
341 for (auto &I : UnrecognizedLifetimes)
342 I->eraseFromParent();