xref: /llvm-project/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp (revision 821a0f23d83921bf32a0a290b5f267f3514d09df)
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