xref: /llvm-project/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp (revision a6d2a8d6f59a974752666305c4baaabebee41b95)
1 //===- AssumeBundleQueriesTest.cpp ------------------------------*- 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/AssumptionCache.h"
10 #include "llvm/Analysis/AssumeBundleQueries.h"
11 #include "llvm/AsmParser/Parser.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/IntrinsicInst.h"
14 #include "llvm/Support/Regex.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
18 #include "gtest/gtest.h"
19 #include <random>
20 
21 using namespace llvm;
22 
23 extern cl::opt<bool> ShouldPreserveAllAttributes;
24 extern cl::opt<bool> EnableKnowledgeRetention;
25 
26 static void RunTest(
27     StringRef Head, StringRef Tail,
28     std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
29         &Tests) {
30   for (auto &Elem : Tests) {
31     std::string IR;
32     IR.append(Head.begin(), Head.end());
33     IR.append(Elem.first.begin(), Elem.first.end());
34     IR.append(Tail.begin(), Tail.end());
35     LLVMContext C;
36     SMDiagnostic Err;
37     std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
38     if (!Mod)
39       Err.print("AssumeQueryAPI", errs());
40     Elem.second(&*(Mod->getFunction("test")->begin()->begin()));
41   }
42 }
43 
44 bool hasMatchesExactlyAttributes(IntrinsicInst *Assume, Value *WasOn,
45                                     StringRef AttrToMatch) {
46   Regex Reg(AttrToMatch);
47   SmallVector<StringRef, 1> Matches;
48   for (StringRef Attr : {
49 #define GET_ATTR_NAMES
50 #define ATTRIBUTE_ALL(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
51 #include "llvm/IR/Attributes.inc"
52        }) {
53     bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
54     if (ShouldHaveAttr != hasAttributeInAssume(*Assume, WasOn, Attr))
55       return false;
56   }
57   return true;
58 }
59 
60 bool hasTheRightValue(IntrinsicInst *Assume, Value *WasOn,
61                       Attribute::AttrKind Kind, unsigned Value) {
62   uint64_t ArgVal = 0;
63   if (!hasAttributeInAssume(*Assume, WasOn, Kind, &ArgVal))
64     return false;
65   if (ArgVal != Value)
66     return false;
67   return true;
68 }
69 
70 TEST(AssumeQueryAPI, hasAttributeInAssume) {
71   EnableKnowledgeRetention.setValue(true);
72   StringRef Head =
73       "declare void @llvm.assume(i1)\n"
74       "declare void @func(i32*, i32*, i32*)\n"
75       "declare void @func1(i32*, i32*, i32*, i32*)\n"
76       "declare void @func_many(i32*) \"no-jump-tables\" nounwind "
77       "\"less-precise-fpmad\" willreturn norecurse\n"
78       "define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
79   StringRef Tail = "ret void\n"
80                    "}";
81   std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
82       Tests;
83   Tests.push_back(std::make_pair(
84       "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
85       "8 noalias %P1, i32* align 8 noundef %P2)\n",
86       [](Instruction *I) {
87         IntrinsicInst *Assume = buildAssumeFromInst(I);
88         Assume->insertBefore(I);
89         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
90                                        "(nonnull|align|dereferenceable)"));
91         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
92                                        "()"));
93         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
94                                        "(align|noundef)"));
95         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
96                                      Attribute::AttrKind::Dereferenceable, 16));
97         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
98                                      Attribute::AttrKind::Alignment, 4));
99         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
100                                      Attribute::AttrKind::Alignment, 4));
101       }));
102   Tests.push_back(std::make_pair(
103       "call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
104       "nonnull "
105       "align 8 dereferenceable(28) %P, i32* nonnull align 64 "
106       "dereferenceable(4) "
107       "%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
108       [](Instruction *I) {
109         IntrinsicInst *Assume = buildAssumeFromInst(I);
110         Assume->insertBefore(I);
111         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
112                                        "(nonnull|align|dereferenceable)"));
113         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
114                                        "(nonnull|align|dereferenceable)"));
115         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
116                                        "(nonnull|align|dereferenceable)"));
117         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
118                                        "(nonnull|align|dereferenceable)"));
119         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
120                                      Attribute::AttrKind::Dereferenceable, 48));
121         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
122                                      Attribute::AttrKind::Alignment, 64));
123         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
124                                      Attribute::AttrKind::Alignment, 64));
125       }));
126   Tests.push_back(std::make_pair(
127       "call void @func_many(i32* align 8 noundef %P1) cold\n", [](Instruction *I) {
128         ShouldPreserveAllAttributes.setValue(true);
129         IntrinsicInst *Assume = buildAssumeFromInst(I);
130         Assume->insertBefore(I);
131         ASSERT_TRUE(hasMatchesExactlyAttributes(
132             Assume, nullptr,
133             "(align|nounwind|norecurse|noundef|willreturn|cold)"));
134         ShouldPreserveAllAttributes.setValue(false);
135       }));
136   Tests.push_back(
137       std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
138         IntrinsicInst *Assume = cast<IntrinsicInst>(I);
139         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr, ""));
140       }));
141   Tests.push_back(std::make_pair(
142       "call void @func1(i32* readnone align 32 "
143       "dereferenceable(48) noalias %P, i32* "
144       "align 8 dereferenceable(28) %P1, i32* align 64 "
145       "dereferenceable(4) "
146       "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
147       [](Instruction *I) {
148         IntrinsicInst *Assume = buildAssumeFromInst(I);
149         Assume->insertBefore(I);
150         ASSERT_TRUE(hasMatchesExactlyAttributes(
151             Assume, I->getOperand(0),
152             "(align|dereferenceable)"));
153         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
154                                        "(align|dereferenceable)"));
155         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
156                                        "(align|dereferenceable)"));
157         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
158                                        "(nonnull|align|dereferenceable)"));
159         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
160                                      Attribute::AttrKind::Alignment, 32));
161         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
162                                      Attribute::AttrKind::Dereferenceable, 48));
163         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
164                                      Attribute::AttrKind::Dereferenceable, 28));
165         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
166                                      Attribute::AttrKind::Alignment, 8));
167         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),
168                                      Attribute::AttrKind::Alignment, 64));
169         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),
170                                      Attribute::AttrKind::Dereferenceable, 4));
171         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),
172                                      Attribute::AttrKind::Alignment, 16));
173         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),
174                                      Attribute::AttrKind::Dereferenceable, 12));
175       }));
176 
177   Tests.push_back(std::make_pair(
178       "call void @func1(i32* readnone align 32 "
179       "dereferenceable(48) noalias %P, i32* "
180       "align 8 dereferenceable(28) %P1, i32* align 64 "
181       "dereferenceable(4) "
182       "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
183       [](Instruction *I) {
184         IntrinsicInst *Assume = buildAssumeFromInst(I);
185         Assume->insertBefore(I);
186         I->getOperand(1)->dropDroppableUses();
187         I->getOperand(2)->dropDroppableUses();
188         I->getOperand(3)->dropDroppableUses();
189         ASSERT_TRUE(hasMatchesExactlyAttributes(
190             Assume, I->getOperand(0),
191             "(align|dereferenceable)"));
192         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
193                                        ""));
194         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
195                                        ""));
196         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
197                                        ""));
198         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
199                                      Attribute::AttrKind::Alignment, 32));
200         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
201                                      Attribute::AttrKind::Dereferenceable, 48));
202       }));
203   Tests.push_back(std::make_pair(
204       "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
205       "8 noalias %P1, i32* %P1)\n",
206       [](Instruction *I) {
207         IntrinsicInst *Assume = buildAssumeFromInst(I);
208         Assume->insertBefore(I);
209         Value *New = I->getFunction()->getArg(3);
210         Value *Old = I->getOperand(0);
211         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New, ""));
212         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old,
213                                        "(nonnull|align|dereferenceable)"));
214         Old->replaceAllUsesWith(New);
215         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New,
216                                        "(nonnull|align|dereferenceable)"));
217         ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old, ""));
218       }));
219   RunTest(Head, Tail, Tests);
220 }
221 
222 static bool FindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,
223                                  StringRef AttrToMatch) {
224   Regex Reg(AttrToMatch);
225   SmallVector<StringRef, 1> Matches;
226   for (StringRef Attr : {
227 #define GET_ATTR_NAMES
228 #define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
229 #include "llvm/IR/Attributes.inc"
230        }) {
231     bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
232 
233     if (ShouldHaveAttr != (Map.find(RetainedKnowledgeKey{WasOn, Attribute::getAttrKindFromName(Attr)}) != Map.end()))
234       return false;
235   }
236   return true;
237 }
238 
239 static bool MapHasRightValue(RetainedKnowledgeMap &Map, IntrinsicInst *II,
240                              RetainedKnowledgeKey Key, MinMax MM) {
241   auto LookupIt = Map.find(Key);
242   return (LookupIt != Map.end()) && (LookupIt->second[II].Min == MM.Min) &&
243          (LookupIt->second[II].Max == MM.Max);
244 }
245 
246 TEST(AssumeQueryAPI, fillMapFromAssume) {
247   EnableKnowledgeRetention.setValue(true);
248   StringRef Head =
249       "declare void @llvm.assume(i1)\n"
250       "declare void @func(i32*, i32*, i32*)\n"
251       "declare void @func1(i32*, i32*, i32*, i32*)\n"
252       "declare void @func_many(i32*) \"no-jump-tables\" nounwind "
253       "\"less-precise-fpmad\" willreturn norecurse\n"
254       "define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
255   StringRef Tail = "ret void\n"
256                    "}";
257   std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
258       Tests;
259   Tests.push_back(std::make_pair(
260       "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
261       "8 noalias %P1, i32* align 8 dereferenceable(8) %P2)\n",
262       [](Instruction *I) {
263         IntrinsicInst *Assume = buildAssumeFromInst(I);
264         Assume->insertBefore(I);
265 
266         RetainedKnowledgeMap Map;
267         fillMapFromAssume(*Assume, Map);
268         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
269                                        "(nonnull|align|dereferenceable)"));
270         ASSERT_FALSE(FindExactlyAttributes(Map, I->getOperand(1),
271                                        "(align)"));
272         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
273                                        "(align|dereferenceable)"));
274         ASSERT_TRUE(MapHasRightValue(
275             Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16}));
276         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
277                                {4, 4}));
278         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
279                                {4, 4}));
280       }));
281   Tests.push_back(std::make_pair(
282       "call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
283       "nonnull "
284       "align 8 dereferenceable(28) %P, i32* nonnull align 64 "
285       "dereferenceable(4) "
286       "%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
287       [](Instruction *I) {
288         IntrinsicInst *Assume = buildAssumeFromInst(I);
289         Assume->insertBefore(I);
290 
291         RetainedKnowledgeMap Map;
292         fillMapFromAssume(*Assume, Map);
293 
294         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
295                                        "(nonnull|align|dereferenceable)"));
296         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
297                                        "(nonnull|align|dereferenceable)"));
298         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
299                                        "(nonnull|align|dereferenceable)"));
300         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
301                                        "(nonnull|align|dereferenceable)"));
302         ASSERT_TRUE(MapHasRightValue(
303             Map, Assume, {I->getOperand(0), Attribute::Dereferenceable},
304             {48, 48}));
305         ASSERT_TRUE(MapHasRightValue(
306             Map, Assume, {I->getOperand(0), Attribute::Alignment}, {64, 64}));
307       }));
308   Tests.push_back(std::make_pair(
309       "call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
310         ShouldPreserveAllAttributes.setValue(true);
311         IntrinsicInst *Assume = buildAssumeFromInst(I);
312         Assume->insertBefore(I);
313 
314         RetainedKnowledgeMap Map;
315         fillMapFromAssume(*Assume, Map);
316 
317         ASSERT_TRUE(FindExactlyAttributes(
318             Map, nullptr, "(nounwind|norecurse|willreturn|cold)"));
319         ShouldPreserveAllAttributes.setValue(false);
320       }));
321   Tests.push_back(
322       std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
323         RetainedKnowledgeMap Map;
324         fillMapFromAssume(*cast<IntrinsicInst>(I), Map);
325 
326         ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, ""));
327         ASSERT_TRUE(Map.empty());
328       }));
329   Tests.push_back(std::make_pair(
330       "call void @func1(i32* readnone align 32 "
331       "dereferenceable(48) noalias %P, i32* "
332       "align 8 dereferenceable(28) %P1, i32* align 64 "
333       "dereferenceable(4) "
334       "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
335       [](Instruction *I) {
336         IntrinsicInst *Assume = buildAssumeFromInst(I);
337         Assume->insertBefore(I);
338 
339         RetainedKnowledgeMap Map;
340         fillMapFromAssume(*Assume, Map);
341 
342         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
343                                     "(align|dereferenceable)"));
344         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
345                                     "(align|dereferenceable)"));
346         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
347                                        "(align|dereferenceable)"));
348         ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
349                                        "(nonnull|align|dereferenceable)"));
350         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
351                                {32, 32}));
352         ASSERT_TRUE(MapHasRightValue(
353             Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48}));
354         ASSERT_TRUE(MapHasRightValue(
355             Map, Assume, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28}));
356         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Alignment},
357                                {8, 8}));
358         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Alignment},
359                                {64, 64}));
360         ASSERT_TRUE(MapHasRightValue(
361             Map, Assume, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4}));
362         ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Alignment},
363                                {16, 16}));
364         ASSERT_TRUE(MapHasRightValue(
365             Map, Assume, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12}));
366       }));
367 
368   /// Keep this test last as it modifies the function.
369   Tests.push_back(std::make_pair(
370       "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
371       "8 noalias %P1, i32* %P2)\n",
372       [](Instruction *I) {
373         IntrinsicInst *Assume = buildAssumeFromInst(I);
374         Assume->insertBefore(I);
375 
376         RetainedKnowledgeMap Map;
377         fillMapFromAssume(*Assume, Map);
378 
379         Value *New = I->getFunction()->getArg(3);
380         Value *Old = I->getOperand(0);
381         ASSERT_TRUE(FindExactlyAttributes(Map, New, ""));
382         ASSERT_TRUE(FindExactlyAttributes(Map, Old,
383                                        "(nonnull|align|dereferenceable)"));
384         Old->replaceAllUsesWith(New);
385         Map.clear();
386         fillMapFromAssume(*Assume, Map);
387         ASSERT_TRUE(FindExactlyAttributes(Map, New,
388                                        "(nonnull|align|dereferenceable)"));
389         ASSERT_TRUE(FindExactlyAttributes(Map, Old, ""));
390       }));
391   Tests.push_back(std::make_pair(
392       "call void @llvm.assume(i1 true) [\"align\"(i8* undef, i32 undef)]",
393       [](Instruction *I) {
394         // Don't crash but don't learn from undef.
395         RetainedKnowledgeMap Map;
396         fillMapFromAssume(*cast<IntrinsicInst>(I), Map);
397 
398         ASSERT_TRUE(Map.empty());
399       }));
400   RunTest(Head, Tail, Tests);
401 }
402 
403 static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,
404                         unsigned MaxValue) {
405   LLVMContext C;
406   SMDiagnostic Err;
407 
408   std::random_device dev;
409   std::mt19937 Rng(Seed);
410   std::uniform_int_distribution<int> DistCount(MinCount, MaxCount);
411   std::uniform_int_distribution<unsigned> DistValue(0, MaxValue);
412   std::uniform_int_distribution<unsigned> DistAttr(0,
413                                                    Attribute::EndAttrKinds - 1);
414 
415   std::unique_ptr<Module> Mod = std::make_unique<Module>("AssumeQueryAPI", C);
416   if (!Mod)
417     Err.print("AssumeQueryAPI", errs());
418 
419   std::vector<Type *> TypeArgs;
420   for (int i = 0; i < (Size * 2); i++)
421     TypeArgs.push_back(Type::getInt32PtrTy(C));
422   FunctionType *FuncType =
423       FunctionType::get(Type::getVoidTy(C), TypeArgs, false);
424 
425   Function *F =
426       Function::Create(FuncType, GlobalValue::ExternalLinkage, "test", &*Mod);
427   BasicBlock *BB = BasicBlock::Create(C);
428   BB->insertInto(F);
429   Instruction *Ret = ReturnInst::Create(C);
430   BB->getInstList().insert(BB->begin(), Ret);
431   Function *FnAssume = Intrinsic::getDeclaration(Mod.get(), Intrinsic::assume);
432 
433   std::vector<Argument *> ShuffledArgs;
434   std::vector<bool> HasArg;
435   for (auto &Arg : F->args()) {
436     ShuffledArgs.push_back(&Arg);
437     HasArg.push_back(false);
438   }
439 
440   std::shuffle(ShuffledArgs.begin(), ShuffledArgs.end(), Rng);
441 
442   std::vector<OperandBundleDef> OpBundle;
443   OpBundle.reserve(Size);
444   std::vector<Value *> Args;
445   Args.reserve(2);
446   for (int i = 0; i < Size; i++) {
447     int count = DistCount(Rng);
448     int value = DistValue(Rng);
449     int attr = DistAttr(Rng);
450     std::string str;
451     raw_string_ostream ss(str);
452     ss << Attribute::getNameFromAttrKind(
453         static_cast<Attribute::AttrKind>(attr));
454     Args.clear();
455 
456     if (count > 0) {
457       Args.push_back(ShuffledArgs[i]);
458       HasArg[i] = true;
459     }
460     if (count > 1)
461       Args.push_back(ConstantInt::get(Type::getInt32Ty(C), value));
462 
463     OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)});
464   }
465 
466   auto *Assume = cast<IntrinsicInst>(IntrinsicInst::Create(
467       FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle));
468   Assume->insertBefore(&F->begin()->front());
469   RetainedKnowledgeMap Map;
470   fillMapFromAssume(*Assume, Map);
471   for (int i = 0; i < (Size * 2); i++) {
472     if (!HasArg[i])
473       continue;
474     RetainedKnowledge K =
475         getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin());
476     auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind});
477     ASSERT_TRUE(LookupIt != Map.end());
478     MinMax MM = LookupIt->second[Assume];
479     ASSERT_TRUE(MM.Min == MM.Max);
480     ASSERT_TRUE(MM.Min == K.ArgValue);
481   }
482 }
483 
484 TEST(AssumeQueryAPI, getKnowledgeFromUseInAssume) {
485   // // For Fuzzing
486   // std::random_device dev;
487   // std::mt19937 Rng(dev());
488   // while (true) {
489   //   unsigned Seed = Rng();
490   //   dbgs() << Seed << "\n";
491   //   RunRandTest(Seed, 100000, 0, 2, 100);
492   // }
493   RunRandTest(23456, 4, 0, 2, 100);
494   RunRandTest(560987, 25, -3, 2, 100);
495 
496   // Large bundles can lead to special cases. this is why this test is soo
497   // large.
498   RunRandTest(9876789, 100000, -0, 7, 100);
499 }
500 
501 TEST(AssumeQueryAPI, AssumptionCache) {
502   LLVMContext C;
503   SMDiagnostic Err;
504   std::unique_ptr<Module> Mod = parseAssemblyString(
505       "declare void @llvm.assume(i1)\n"
506       "define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3, i1 %B) {\n"
507       "call void @llvm.assume(i1 true) [\"nonnull\"(i32* %P), \"align\"(i32* "
508       "%P2, i32 4), \"align\"(i32* %P, i32 8)]\n"
509       "call void @llvm.assume(i1 %B) [\"test\"(i32* %P1), "
510       "\"dereferenceable\"(i32* %P, i32 4)]\n"
511       "ret void\n}\n",
512       Err, C);
513   if (!Mod)
514     Err.print("AssumeQueryAPI", errs());
515   Function *F = Mod->getFunction("test");
516   BasicBlock::iterator First = F->begin()->begin();
517   BasicBlock::iterator Second = F->begin()->begin();
518   Second++;
519   AssumptionCacheTracker ACT;
520   AssumptionCache &AC = ACT.getAssumptionCache(*F);
521   auto AR = AC.assumptionsFor(F->getArg(3));
522   ASSERT_EQ(AR.size(), 0u);
523   AR = AC.assumptionsFor(F->getArg(1));
524   ASSERT_EQ(AR.size(), 1u);
525   ASSERT_EQ(AR[0].Index, 0u);
526   ASSERT_EQ(AR[0].Assume, &*Second);
527   AR = AC.assumptionsFor(F->getArg(2));
528   ASSERT_EQ(AR.size(), 1u);
529   ASSERT_EQ(AR[0].Index, 1u);
530   ASSERT_EQ(AR[0].Assume, &*First);
531   AR = AC.assumptionsFor(F->getArg(0));
532   ASSERT_EQ(AR.size(), 3u);
533   llvm::sort(AR,
534              [](const auto &L, const auto &R) { return L.Index < R.Index; });
535   ASSERT_EQ(AR[0].Assume, &*First);
536   ASSERT_EQ(AR[0].Index, 0u);
537   ASSERT_EQ(AR[1].Assume, &*Second);
538   ASSERT_EQ(AR[1].Index, 1u);
539   ASSERT_EQ(AR[2].Assume, &*First);
540   ASSERT_EQ(AR[2].Index, 2u);
541   AR = AC.assumptionsFor(F->getArg(4));
542   ASSERT_EQ(AR.size(), 1u);
543   ASSERT_EQ(AR[0].Assume, &*Second);
544   ASSERT_EQ(AR[0].Index, AssumptionCache::ExprResultIdx);
545   AC.unregisterAssumption(cast<AssumeInst>(&*Second));
546   AR = AC.assumptionsFor(F->getArg(1));
547   ASSERT_EQ(AR.size(), 0u);
548   AR = AC.assumptionsFor(F->getArg(0));
549   ASSERT_EQ(AR.size(), 3u);
550   llvm::sort(AR,
551              [](const auto &L, const auto &R) { return L.Index < R.Index; });
552   ASSERT_EQ(AR[0].Assume, &*First);
553   ASSERT_EQ(AR[0].Index, 0u);
554   ASSERT_EQ(AR[1].Assume, nullptr);
555   ASSERT_EQ(AR[1].Index, 1u);
556   ASSERT_EQ(AR[2].Assume, &*First);
557   ASSERT_EQ(AR[2].Index, 2u);
558   AR = AC.assumptionsFor(F->getArg(2));
559   ASSERT_EQ(AR.size(), 1u);
560   ASSERT_EQ(AR[0].Index, 1u);
561   ASSERT_EQ(AR[0].Assume, &*First);
562 }
563 
564 TEST(AssumeQueryAPI, Alignment) {
565   LLVMContext C;
566   SMDiagnostic Err;
567   std::unique_ptr<Module> Mod = parseAssemblyString(
568       "declare void @llvm.assume(i1)\n"
569       "define void @test(i32* %P, i32* %P1, i32* %P2, i32 %I3, i1 %B) {\n"
570       "call void @llvm.assume(i1 true) [\"align\"(i32* %P, i32 8, i32 %I3)]\n"
571       "call void @llvm.assume(i1 true) [\"align\"(i32* %P1, i32 %I3, i32 "
572       "%I3)]\n"
573       "call void @llvm.assume(i1 true) [\"align\"(i32* %P2, i32 16, i32 8)]\n"
574       "ret void\n}\n",
575       Err, C);
576   if (!Mod)
577     Err.print("AssumeQueryAPI", errs());
578 
579   Function *F = Mod->getFunction("test");
580   BasicBlock::iterator Start = F->begin()->begin();
581   IntrinsicInst *II;
582   RetainedKnowledge RK;
583   II = cast<IntrinsicInst>(&*Start);
584   RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
585   ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
586   ASSERT_EQ(RK.WasOn, F->getArg(0));
587   ASSERT_EQ(RK.ArgValue, 1u);
588   Start++;
589   II = cast<IntrinsicInst>(&*Start);
590   RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
591   ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
592   ASSERT_EQ(RK.WasOn, F->getArg(1));
593   ASSERT_EQ(RK.ArgValue, 1u);
594   Start++;
595   II = cast<IntrinsicInst>(&*Start);
596   RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
597   ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
598   ASSERT_EQ(RK.WasOn, F->getArg(2));
599   ASSERT_EQ(RK.ArgValue, 8u);
600 }
601