1 //===- AssumeBundleQueries.cpp - tool to query assume bundles ---*- 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/Analysis/AssumeBundleQueries.h"
10 #include "llvm/IR/Function.h"
11 #include "llvm/IR/InstIterator.h"
12 #include "llvm/IR/IntrinsicInst.h"
13 
14 using namespace llvm;
15 
16 static bool bundleHasArgument(const CallBase::BundleOpInfo &BOI, unsigned Idx) {
17   return BOI.End - BOI.Begin > Idx;
18 }
19 
20 static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume,
21                                        const CallBase::BundleOpInfo &BOI,
22                                        unsigned Idx) {
23   assert(bundleHasArgument(BOI, Idx) && "index out of range");
24   return (Assume.op_begin() + BOI.Begin + Idx)->get();
25 }
26 
27 bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
28                                 StringRef AttrName, uint64_t *ArgVal,
29                                 AssumeQuery AQR) {
30   assert(isa<IntrinsicInst>(AssumeCI) &&
31          "this function is intended to be used on llvm.assume");
32   IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
33   assert(Assume.getIntrinsicID() == Intrinsic::assume &&
34          "this function is intended to be used on llvm.assume");
35   assert(Attribute::isExistingAttribute(AttrName) &&
36          "this attribute doesn't exist");
37   assert((ArgVal == nullptr || Attribute::doesAttrKindHaveArgument(
38                                    Attribute::getAttrKindFromName(AttrName))) &&
39          "requested value for an attribute that has no argument");
40   if (Assume.bundle_op_infos().empty())
41     return false;
42 
43   auto Loop = [&](auto &&Range) {
44     for (auto &BOI : Range) {
45       if (BOI.Tag->getKey() != AttrName)
46         continue;
47       if (IsOn && (BOI.End - BOI.Begin <= ABA_WasOn ||
48                    IsOn != getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn)))
49         continue;
50       if (ArgVal) {
51         assert(BOI.End - BOI.Begin > ABA_Argument);
52         *ArgVal = cast<ConstantInt>(
53                       getValueFromBundleOpInfo(Assume, BOI, ABA_Argument))
54                       ->getZExtValue();
55       }
56       return true;
57     }
58     return false;
59   };
60 
61   if (AQR == AssumeQuery::Lowest)
62     return Loop(Assume.bundle_op_infos());
63   return Loop(reverse(Assume.bundle_op_infos()));
64 }
65 
66 void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {
67   IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
68   assert(Assume.getIntrinsicID() == Intrinsic::assume &&
69          "this function is intended to be used on llvm.assume");
70   for (auto &Bundles : Assume.bundle_op_infos()) {
71     std::pair<Value *, Attribute::AttrKind> Key{
72         nullptr, Attribute::getAttrKindFromName(Bundles.Tag->getKey())};
73     if (bundleHasArgument(Bundles, ABA_WasOn))
74       Key.first = getValueFromBundleOpInfo(Assume, Bundles, ABA_WasOn);
75 
76     if (Key.first == nullptr && Key.second == Attribute::None)
77       continue;
78     if (!bundleHasArgument(Bundles, ABA_Argument)) {
79       Result[Key][&Assume] = {0, 0};
80       continue;
81     }
82     unsigned Val = cast<ConstantInt>(
83                        getValueFromBundleOpInfo(Assume, Bundles, ABA_Argument))
84                        ->getZExtValue();
85     auto Lookup = Result.find(Key);
86     if (Lookup == Result.end() || !Lookup->second.count(&Assume)) {
87       Result[Key][&Assume] = {Val, Val};
88       continue;
89     }
90     Lookup->second[&Assume].Min = std::min(Val, Lookup->second[&Assume].Min);
91     Lookup->second[&Assume].Max = std::max(Val, Lookup->second[&Assume].Max);
92   }
93 }
94 
95 RetainedKnowledge llvm::getKnowledgeFromOperandInAssume(CallInst &AssumeCI,
96                                                         unsigned Idx) {
97   IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
98   assert(Assume.getIntrinsicID() == Intrinsic::assume &&
99          "this function is intended to be used on llvm.assume");
100   CallBase::BundleOpInfo BOI = Assume.getBundleOpInfoForOperand(Idx);
101   RetainedKnowledge Result;
102   Result.AttrKind = Attribute::getAttrKindFromName(BOI.Tag->getKey());
103   Result.WasOn = getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn);
104   if (BOI.End - BOI.Begin > ABA_Argument)
105     Result.ArgValue =
106         cast<ConstantInt>(getValueFromBundleOpInfo(Assume, BOI, ABA_Argument))
107             ->getZExtValue();
108 
109   return Result;
110 }
111 
112 bool llvm::isAssumeWithEmptyBundle(CallInst &CI) {
113   IntrinsicInst &Assume = cast<IntrinsicInst>(CI);
114   assert(Assume.getIntrinsicID() == Intrinsic::assume &&
115          "this function is intended to be used on llvm.assume");
116   return none_of(Assume.bundle_op_infos(),
117                  [](const CallBase::BundleOpInfo &BOI) {
118                    return BOI.Tag->getKey() != "ignore";
119                  });
120 }
121