Go to the documentation of this file.
49 #include "llvm/IR/IntrinsicsAArch64.h"
65 #define DEBUG_TYPE "aarch64-stack-tagging"
69 cl::desc(
"merge stack variable initializers with tagging when possible"));
74 cl::desc(
"Use Stack Safety analysis results"));
86 cl::desc(
"How many lifetime ends to handle for a single alloca."),
93 class InitializerBuilder {
109 std::map<uint64_t, Value *> Out;
116 SetTagZeroFn(SetTagZeroFn), StgpFn(StgpFn) {}
123 if (
I != Ranges.end() && End >
I->Start) {
127 Ranges.
insert(
I, {Start, End, Inst});
132 int64_t StoreSize =
DL->getTypeStoreSize(
SI->getOperand(0)->getType());
133 if (!
addRange(Offset, Offset + StoreSize,
SI))
136 applyStore(IRB, Offset, Offset + StoreSize,
SI->getOperand(0));
142 if (!
addRange(Offset, Offset + StoreSize, MSI))
145 applyMemSet(IRB, Offset, Offset + StoreSize,
146 cast<ConstantInt>(MSI->
getValue()));
150 void applyMemSet(
IRBuilder<> &IRB, int64_t Start, int64_t End,
157 for (int64_t Offset = Start - Start % 8;
Offset < End;
Offset += 8) {
158 uint64_t Cst = 0x0101010101010101UL;
159 int LowBits =
Offset < Start ? (Start -
Offset) * 8 : 0;
161 Cst = (Cst >> LowBits) << LowBits;
162 int HighBits = End -
Offset < 8 ? (8 - (End -
Offset)) * 8 : 0;
164 Cst = (Cst << HighBits) >> HighBits;
183 }
else if (Offset < 0) {
192 void applyStore(
IRBuilder<> &IRB, int64_t Start, int64_t End,
193 Value *StoredValue) {
194 StoredValue =
flatten(IRB, StoredValue);
195 for (int64_t Offset = Start - Start % 8;
Offset < End;
Offset += 8) {
196 Value *V = sliceValue(IRB, StoredValue, Offset - Start);
201 CurrentV = IRB.
CreateOr(CurrentV, V);
209 if (Ranges.empty()) {
210 emitUndef(IRB, 0, Size);
219 auto I1 = Out.find(Offset);
220 auto I2 = Out.find(Offset + 8);
221 if (
I1 == Out.end() && I2 == Out.end())
224 if (Offset > LastOffset)
225 emitZeroes(IRB, LastOffset, Offset - LastOffset);
231 emitPair(IRB, Offset, Store1, Store2);
237 if (LastOffset < Size)
238 emitZeroes(IRB, LastOffset, Size - LastOffset);
240 for (
const auto &R : Ranges) {
241 R.Inst->eraseFromParent();
265 LLVM_DEBUG(
dbgs() <<
" [" << Offset <<
", " << Offset + 16 <<
"):\n");
279 Type *EltTy = VecTy->getElementType();
281 uint32_t EltSize =
DL->getTypeSizeInBits(EltTy);
284 cast<FixedVectorType>(VecTy)->getNumElements());
294 const bool MergeInit;
295 const bool UseStackSafety;
300 AArch64StackTagging(
bool IsOptNone =
false)
308 bool isInterestingAlloca(
const AllocaInst &AI);
315 uint64_t Size, InitializerBuilder &IB);
322 StringRef getPassName()
const override {
return "AArch64 Stack Tagging"; }
352 return new AArch64StackTagging(IsOptNone);
358 InitializerBuilder &IB) {
364 for (; Count <
ClScanLimit && !BI->isTerminator(); ++BI) {
365 if (!isa<DbgInfoIntrinsic>(*BI))
371 if (!isa<StoreInst>(BI) && !isa<MemSetInst>(BI)) {
375 if (BI->mayWriteToMemory() || BI->mayReadFromMemory())
380 if (
StoreInst *NextStore = dyn_cast<StoreInst>(BI)) {
381 if (!NextStore->isSimple())
390 if (!IB.addStore(*Offset, NextStore,
DL))
392 LastInst = NextStore;
399 if (!isa<ConstantInt>(MSI->
getValue()))
407 if (!IB.addMemSet(*Offset, MSI))
415 bool AArch64StackTagging::isInterestingAlloca(
const AllocaInst &AI) {
427 !(SSI && SSI->
isSafe(AI));
428 return IsInteresting;
433 auto SetTagZeroFunc =
438 InitializerBuilder IB(Size,
DL, Ptr, SetTagFunc, SetTagZeroFunc, StgpFunc);
442 if (MergeInit && !
F->hasOptNone() && LittleEndian &&
445 <<
", size = " << Size <<
"\n");
446 InsertBefore = collectInitializers(InsertBefore, Ptr, Size, IB);
460 Instruction *AArch64StackTagging::insertBaseTaggedPointer(
465 for (
auto &
I : AllocasToInstrument) {
481 Base->setName(
"basetag");
491 SSI = &getAnalysis<StackSafetyGlobalInfoWrapperPass>().getResult();
495 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
498 [
this](
const AllocaInst &AI) {
return isInterestingAlloca(AI); });
506 std::unique_ptr<DominatorTree> DeleteDT;
508 if (
auto *
P = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
509 DT = &
P->getDomTree();
512 DeleteDT = std::make_unique<DominatorTree>(*
F);
516 std::unique_ptr<PostDominatorTree> DeletePDT;
518 if (
auto *
P = getAnalysisIfAvailable<PostDominatorTreeWrapperPass>())
519 PDT = &
P->getPostDomTree();
521 if (PDT ==
nullptr) {
522 DeletePDT = std::make_unique<PostDominatorTree>(*
F);
523 PDT = DeletePDT.get();
539 NextTag = (NextTag + 1) % 16;
543 F->getParent(), Intrinsic::aarch64_tagp, {Info.AI->getType()});
547 if (
Info.AI->hasName())
549 Info.AI->replaceAllUsesWith(TagPCall);
556 bool StandardLifetime =
561 if (StandardLifetime) {
564 cast<ConstantInt>(Start->getArgOperand(0))->getZExtValue();
566 tagAlloca(AI, Start->getNextNode(), Start->getArgOperand(1), Size);
568 auto TagEnd = [&](
Instruction *Node) { untagAlloca(AI, Node, Size); };
572 for (
auto *End :
Info.LifetimeEnd)
573 End->eraseFromParent();
579 for (
auto &RI : SInfo.
RetVec) {
580 untagAlloca(AI, RI, Size);
584 for (
auto &II :
Info.LifetimeStart)
585 II->eraseFromParent();
586 for (
auto &II :
Info.LifetimeEnd)
587 II->eraseFromParent();
591 for (
auto DVI :
Info.DbgVariableIntrinsics)
592 DVI->replaceVariableLocationOp(OldAI,
Info.AI);
598 I->eraseFromParent();
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
bool isSized(SmallPtrSetImpl< Type * > *Visited=nullptr) const
Return true if it makes sense to take the size of this type.
This is an optimization pass for GlobalISel generic memory operations.
NodeT * findNearestCommonDominator(NodeT *A, NodeT *B) const
Find nearest common dominator basic block for basic block A and B.
void initializeAArch64StackTaggingPass(PassRegistry &)
static cl::opt< bool > ClUseStackSafety("stack-tagging-use-stack-safety", cl::Hidden, cl::init(true), cl::ZeroOrMore, cl::desc("Use Stack Safety analysis results"))
const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
IntegerType * getInt64Ty()
Fetch the type representing a 64-bit integer.
bool isSafe(const AllocaInst &AI) const
A parsed version of the target data layout string in and methods for querying it.
Function * getDeclaration(Module *M, ID id, ArrayRef< Type * > Tys=None)
Create or insert an LLVM Function declaration for an intrinsic, and return it.
InstListType::iterator iterator
Instruction iterators...
bool isPointerTy() const
True if this is an instance of PointerType.
auto lower_bound(R &&Range, T &&Value)
Provide wrappers to std::lower_bound which take ranges instead of having to pass begin/end explicitly...
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="")
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Triple - Helper class for working with autoconf configuration names.
The instances of the Type class are immutable: once they are created, they are never changed.
This class implements a map that also provides access to all stored values in a deterministic order.
static cl::opt< unsigned > ClScanLimit("stack-tagging-merge-init-scan-limit", cl::init(40), cl::Hidden)
LLVM Basic Block Representation.
SmallVector< Instruction *, 8 > RetVec
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Optional< int64_t > isPointerOffset(const Value *Ptr1, const Value *Ptr2, const DataLayout &DL)
If Ptr1 is provably equal to Ptr2 plus a constant offset, return that offset.
bool isLittleEndian() const
Tests whether the target triple is little endian.
This is the shared class of boolean and integer constants.
bool isStaticAlloca() const
Return true if this alloca is in the entry block of the function and is a constant size.
static cl::opt< size_t > ClMaxLifetimes("stack-tagging-max-lifetimes-for-alloca", cl::Hidden, cl::init(3), cl::ReallyHidden, cl::desc("How many lifetime ends to handle for a single alloca."), cl::Optional)
static PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
(vector float) vec_cmpeq(*A, *B) C
IntegerType * getIntNTy(unsigned N)
Fetch the type representing an N-bit integer.
Represent the analysis usage information of a pass.
bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT, const Instruction *Start, const SmallVectorImpl< IntrinsicInst * > &Ends, const SmallVectorImpl< Instruction * > &RetVec, llvm::function_ref< void(Instruction *)> Callback)
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static const Align kTagGranuleSize
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
LLVMContext & getContext() const
void setName(const Twine &Name)
Change the name of the value.
static Constant * get(Type *Ty, uint64_t V, bool IsSigned=false)
If Ty is a vector type, return a Constant with a splat of the given value.
Analysis containing CSE Info
static FixedVectorType * get(Type *ElementType, unsigned NumElts)
This struct is a compact representation of a valid (non-zero power of two) alignment.
Optional< TypeSize > getAllocationSizeInBits(const DataLayout &DL) const
Get allocation size in bits.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
This class wraps the llvm.memset intrinsic.
Value * CreatePointerCast(Value *V, Type *DestTy, const Twine &Name="")
INITIALIZE_PASS_BEGIN(AArch64StackTagging, DEBUG_TYPE, "AArch64 Stack Tagging", false, false) INITIALIZE_PASS_END(AArch64StackTagging
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
bool isSwiftError() const
Return true if this alloca is used as a swifterror argument to a call.
bool isIntegerTy() const
True if this is an instance of IntegerType.
Base class of all SIMD vector types.
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
static cl::opt< unsigned > ClMergeInitSizeLimit("stack-tagging-merge-init-size-limit", cl::init(272), cl::Hidden)
inst_range instructions(Function *F)
An instruction for storing to memory.
LLVM_NODISCARD bool isNoModRef(const ModRefInfo MRI)
BasicBlock::iterator GetInsertPoint() const
Module * getParent()
Get the module that this global value is contained inside of...
SmallVector< Instruction *, 4 > UnrecognizedLifetimes
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
This is an important class for using LLVM in a threaded context.
PointerType * getInt8PtrTy(unsigned AddrSpace=0)
Fetch the type representing a pointer to an 8-bit integer value.
initializer< Ty > init(const Ty &Val)
Value handle that tracks a Value across RAUW.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
StandardInstrumentations SI(Debug, VerifyEach)
void setOperand(unsigned i, Value *Val)
Value * getDest() const
This is just like getRawDest, but it strips off any cast instructions (including addrspacecast) that ...
void setPreservesCFG()
This function should be called by the pass, iff they do not:
StringRef - Represent a constant reference to a string, i.e.
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Type * getType() const
All values are typed, get the type of this value.
bool isZero() const
This is just a convenience method to make client code smaller for a common code.
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
void visit(Instruction &Inst)
Value * CreateConstGEP1_32(Type *Ty, Value *Ptr, unsigned Idx0, const Twine &Name="")
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
const Instruction & front() const
static bool runOnFunction(Function &F, bool PostInlining)
Value * CreateBitOrPointerCast(Value *V, Type *DestTy, const Twine &Name="")
static Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Value * getLength() const
void alignAndPadAlloca(memtag::AllocaInfo &Info, llvm::Align Align)
MapVector< AllocaInst *, AllocaInfo > AllocasToInstrument
A wrapper class for inspecting calls to intrinsic functions.
This pass performs the global (interprocedural) stack safety analysis (legacy pass manager).
bool isUsedWithInAlloca() const
Return true if this alloca is used as an inalloca argument to a call.
FunctionPass * createAArch64StackTaggingPass(bool IsOptNone)
Value * CreateLShr(Value *LHS, Value *RHS, const Twine &Name="", bool isExact=false)
const std::string & getTargetTriple() const
Get the target triple which is a string describing the target host.
static cl::opt< bool > ClMergeInit("stack-tagging-merge-init", cl::Hidden, cl::init(true), cl::ZeroOrMore, cl::desc("merge stack variable initializers with tagging when possible"))
Value * CreateShl(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
A wrapper pass to provide the legacy pass manager access to a suitably prepared AAResults object.
const BasicBlock * getParent() const
Value * CreateZExtOrTrunc(Value *V, Type *DestTy, const Twine &Name="")
Create a ZExt or Trunc from the integer value V to DestTy.
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
static IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
FunctionPass class - This class is used to implement most global optimizations.
AnalysisUsage & addRequired()
an instruction to allocate memory on the stack
LLVM Value Representation.
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args=None, const Twine &Name="", MDNode *FPMathTag=nullptr)
bool isStandardLifetime(const SmallVectorImpl< IntrinsicInst * > &LifetimeStart, const SmallVectorImpl< IntrinsicInst * > &LifetimeEnd, const DominatorTree *DT, size_t MaxLifetimes)
Representation for a specific memory location.
iterator insert(iterator I, T &&Elt)