1 //===- AssumeBundleBuilder.cpp - tools to preserve informations -*- C++ -*-===// 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 "llvm/Transforms/Utils/AssumeBundleBuilder.h" 10 #include "llvm/Analysis/AssumeBundleQueries.h" 11 #include "llvm/Analysis/AssumptionCache.h" 12 #include "llvm/Analysis/ValueTracking.h" 13 #include "llvm/IR/Dominators.h" 14 #include "llvm/IR/Function.h" 15 #include "llvm/IR/InstIterator.h" 16 #include "llvm/IR/IntrinsicInst.h" 17 #include "llvm/IR/Module.h" 18 #include "llvm/Support/CommandLine.h" 19 20 using namespace llvm; 21 22 cl::opt<bool> ShouldPreserveAllAttributes( 23 "assume-preserve-all", cl::init(false), cl::Hidden, 24 cl::desc("enable preservation of all attrbitues. even those that are " 25 "unlikely to be usefull")); 26 27 cl::opt<bool> EnableKnowledgeRetention( 28 "enable-knowledge-retention", cl::init(false), cl::Hidden, 29 cl::desc( 30 "enable preservation of attributes throughout code transformation")); 31 32 namespace { 33 34 /// Deterministically compare OperandBundleDef. 35 /// The ordering is: 36 /// - by the attribute's name aka operand bundle tag, (doesn't change) 37 /// - then by the numeric Value of the argument, (doesn't change) 38 /// - lastly by the Name of the current Value it WasOn. (may change) 39 /// This order is deterministic and allows looking for the right kind of 40 /// attribute with binary search. However finding the right WasOn needs to be 41 /// done via linear search because values can get replaced. 42 bool isLowerOpBundle(const OperandBundleDef &LHS, const OperandBundleDef &RHS) { 43 auto getTuple = [](const OperandBundleDef &Op) { 44 return std::make_tuple( 45 Op.getTag(), 46 Op.input_size() <= ABA_Argument 47 ? 0 48 : cast<ConstantInt>(*(Op.input_begin() + ABA_Argument)) 49 ->getZExtValue(), 50 Op.input_size() <= ABA_WasOn 51 ? StringRef("") 52 : (*(Op.input_begin() + ABA_WasOn))->getName()); 53 }; 54 return getTuple(LHS) < getTuple(RHS); 55 } 56 57 bool isUsefullToPreserve(Attribute::AttrKind Kind) { 58 switch (Kind) { 59 case Attribute::NonNull: 60 case Attribute::Alignment: 61 case Attribute::Dereferenceable: 62 case Attribute::DereferenceableOrNull: 63 case Attribute::Cold: 64 return true; 65 default: 66 return false; 67 } 68 } 69 70 /// This class contain all knowledge that have been gather while building an 71 /// llvm.assume and the function to manipulate it. 72 struct AssumeBuilderState { 73 Module *M; 74 75 using MapKey = std::pair<Value *, Attribute::AttrKind>; 76 SmallDenseMap<MapKey, unsigned, 8> AssumedKnowledgeMap; 77 Instruction *InsertBeforeInstruction = nullptr; 78 AssumptionCache* AC = nullptr; 79 DominatorTree* DT = nullptr; 80 81 AssumeBuilderState(Module *M, Instruction *I = nullptr, 82 AssumptionCache *AC = nullptr, DominatorTree *DT = nullptr) 83 : M(M), InsertBeforeInstruction(I), AC(AC), DT(DT) {} 84 85 bool tryToPreserveWithoutAddingAssume(RetainedKnowledge RK) { 86 if (!InsertBeforeInstruction || !AC || !RK.WasOn) 87 return false; 88 bool HasBeenPreserved = false; 89 Use* ToUpdate = nullptr; 90 getKnowledgeForValue( 91 RK.WasOn, {RK.AttrKind}, AC, 92 [&](RetainedKnowledge RKOther, Instruction *Assume, 93 const CallInst::BundleOpInfo *Bundle) { 94 if (!isValidAssumeForContext(Assume, InsertBeforeInstruction, DT)) 95 return false; 96 if (RKOther.ArgValue >= RK.ArgValue) { 97 HasBeenPreserved = true; 98 return true; 99 } else if (isValidAssumeForContext(InsertBeforeInstruction, Assume, 100 DT)) { 101 HasBeenPreserved = true; 102 IntrinsicInst *Intr = cast<IntrinsicInst>(Assume); 103 ToUpdate = &Intr->op_begin()[Bundle->Begin + ABA_Argument]; 104 return true; 105 } 106 return false; 107 }); 108 if (ToUpdate) 109 ToUpdate->set( 110 ConstantInt::get(Type::getInt64Ty(M->getContext()), RK.ArgValue)); 111 return HasBeenPreserved; 112 } 113 114 void addKnowledge(RetainedKnowledge RK) { 115 if (tryToPreserveWithoutAddingAssume(RK)) 116 return; 117 MapKey Key{RK.WasOn, RK.AttrKind}; 118 auto Lookup = AssumedKnowledgeMap.find(Key); 119 if (Lookup == AssumedKnowledgeMap.end()) { 120 AssumedKnowledgeMap[Key] = RK.ArgValue; 121 return; 122 } 123 assert(((Lookup->second == 0 && RK.ArgValue == 0) || 124 (Lookup->second != 0 && RK.ArgValue != 0)) && 125 "inconsistent argument value"); 126 127 /// This is only desirable because for all attributes taking an argument 128 /// higher is better. 129 Lookup->second = std::max(Lookup->second, RK.ArgValue); 130 } 131 132 void addAttribute(Attribute Attr, Value *WasOn) { 133 if (Attr.isTypeAttribute() || Attr.isStringAttribute() || 134 (!ShouldPreserveAllAttributes && 135 !isUsefullToPreserve(Attr.getKindAsEnum()))) 136 return; 137 unsigned AttrArg = 0; 138 if (Attr.isIntAttribute()) 139 AttrArg = Attr.getValueAsInt(); 140 addKnowledge({Attr.getKindAsEnum(), AttrArg, WasOn}); 141 } 142 143 void addCall(const CallBase *Call) { 144 auto addAttrList = [&](AttributeList AttrList) { 145 for (unsigned Idx = AttributeList::FirstArgIndex; 146 Idx < AttrList.getNumAttrSets(); Idx++) 147 for (Attribute Attr : AttrList.getAttributes(Idx)) 148 addAttribute(Attr, Call->getArgOperand(Idx - 1)); 149 for (Attribute Attr : AttrList.getFnAttributes()) 150 addAttribute(Attr, nullptr); 151 }; 152 addAttrList(Call->getAttributes()); 153 if (Function *Fn = Call->getCalledFunction()) 154 addAttrList(Fn->getAttributes()); 155 } 156 157 IntrinsicInst *build() { 158 if (AssumedKnowledgeMap.empty()) 159 return nullptr; 160 Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume); 161 LLVMContext &C = M->getContext(); 162 SmallVector<OperandBundleDef, 8> OpBundle; 163 for (auto &MapElem : AssumedKnowledgeMap) { 164 SmallVector<Value *, 2> Args; 165 if (MapElem.first.first) 166 Args.push_back(MapElem.first.first); 167 168 /// This is only valid because for all attribute that currently exist a 169 /// value of 0 is useless. and should not be preserved. 170 if (MapElem.second) 171 Args.push_back(ConstantInt::get(Type::getInt64Ty(M->getContext()), 172 MapElem.second)); 173 OpBundle.push_back(OperandBundleDefT<Value *>( 174 std::string(Attribute::getNameFromAttrKind(MapElem.first.second)), 175 Args)); 176 } 177 llvm::sort(OpBundle, isLowerOpBundle); 178 return cast<IntrinsicInst>(CallInst::Create( 179 FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle)); 180 } 181 182 void addAccessedPtr(Instruction *MemInst, Value *Pointer, Type *AccType, 183 MaybeAlign MA) { 184 unsigned DerefSize = MemInst->getModule() 185 ->getDataLayout() 186 .getTypeStoreSize(AccType) 187 .getKnownMinSize(); 188 if (DerefSize != 0) { 189 addKnowledge({Attribute::Dereferenceable, DerefSize, Pointer}); 190 if (!NullPointerIsDefined(MemInst->getFunction(), 191 Pointer->getType()->getPointerAddressSpace())) 192 addKnowledge({Attribute::NonNull, 0u, Pointer}); 193 } 194 if (MA.valueOrOne() > 1) 195 addKnowledge( 196 {Attribute::Alignment, unsigned(MA.valueOrOne().value()), Pointer}); 197 } 198 199 void addInstruction(Instruction *I) { 200 if (auto *Call = dyn_cast<CallBase>(I)) 201 return addCall(Call); 202 if (auto *Load = dyn_cast<LoadInst>(I)) 203 return addAccessedPtr(I, Load->getPointerOperand(), Load->getType(), 204 Load->getAlign()); 205 if (auto *Store = dyn_cast<StoreInst>(I)) 206 return addAccessedPtr(I, Store->getPointerOperand(), 207 Store->getValueOperand()->getType(), 208 Store->getAlign()); 209 // TODO: Add support for the other Instructions. 210 // TODO: Maybe we should look around and merge with other llvm.assume. 211 } 212 }; 213 214 } // namespace 215 216 IntrinsicInst *llvm::buildAssumeFromInst(Instruction *I) { 217 if (!EnableKnowledgeRetention) 218 return nullptr; 219 AssumeBuilderState Builder(I->getModule()); 220 Builder.addInstruction(I); 221 return Builder.build(); 222 } 223 224 void llvm::salvageKnowledge(Instruction *I, AssumptionCache *AC, DominatorTree* DT) { 225 if (!EnableKnowledgeRetention) 226 return; 227 AssumeBuilderState Builder(I->getModule(), I, AC, DT); 228 Builder.addInstruction(I); 229 if (IntrinsicInst *Intr = Builder.build()) { 230 Intr->insertBefore(I); 231 if (AC) 232 AC->registerAssumption(Intr); 233 } 234 } 235 236 PreservedAnalyses AssumeBuilderPass::run(Function &F, 237 FunctionAnalysisManager &AM) { 238 AssumptionCache* AC = AM.getCachedResult<AssumptionAnalysis>(F); 239 DominatorTree* DT = AM.getCachedResult<DominatorTreeAnalysis>(F); 240 for (Instruction &I : instructions(F)) 241 salvageKnowledge(&I, AC, DT); 242 return PreservedAnalyses::all(); 243 } 244