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