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*)\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 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 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 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 Tests.push_back(std::make_pair( 387 "call void @llvm.assume(i1 true) [\"align\"(i8* undef, i32 undef)]", 388 [](Instruction *I) { 389 // Don't crash but don't learn from undef. 390 RetainedKnowledgeMap Map; 391 fillMapFromAssume(*cast<IntrinsicInst>(I), Map); 392 393 ASSERT_TRUE(Map.empty()); 394 })); 395 RunTest(Head, Tail, Tests); 396 } 397 398 static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount, 399 unsigned MaxValue) { 400 LLVMContext C; 401 SMDiagnostic Err; 402 403 std::random_device dev; 404 std::mt19937 Rng(Seed); 405 std::uniform_int_distribution<int> DistCount(MinCount, MaxCount); 406 std::uniform_int_distribution<unsigned> DistValue(0, MaxValue); 407 std::uniform_int_distribution<unsigned> DistAttr(0, 408 Attribute::EndAttrKinds - 1); 409 410 std::unique_ptr<Module> Mod = std::make_unique<Module>("AssumeQueryAPI", C); 411 if (!Mod) 412 Err.print("AssumeQueryAPI", errs()); 413 414 std::vector<Type *> TypeArgs; 415 for (int i = 0; i < (Size * 2); i++) 416 TypeArgs.push_back(Type::getInt32PtrTy(C)); 417 FunctionType *FuncType = 418 FunctionType::get(Type::getVoidTy(C), TypeArgs, false); 419 420 Function *F = 421 Function::Create(FuncType, GlobalValue::ExternalLinkage, "test", &*Mod); 422 BasicBlock *BB = BasicBlock::Create(C); 423 BB->insertInto(F); 424 Instruction *Ret = ReturnInst::Create(C); 425 BB->getInstList().insert(BB->begin(), Ret); 426 Function *FnAssume = Intrinsic::getDeclaration(Mod.get(), Intrinsic::assume); 427 428 std::vector<Argument *> ShuffledArgs; 429 std::vector<bool> HasArg; 430 for (auto &Arg : F->args()) { 431 ShuffledArgs.push_back(&Arg); 432 HasArg.push_back(false); 433 } 434 435 std::shuffle(ShuffledArgs.begin(), ShuffledArgs.end(), Rng); 436 437 std::vector<OperandBundleDef> OpBundle; 438 OpBundle.reserve(Size); 439 std::vector<Value *> Args; 440 Args.reserve(2); 441 for (int i = 0; i < Size; i++) { 442 int count = DistCount(Rng); 443 int value = DistValue(Rng); 444 int attr = DistAttr(Rng); 445 std::string str; 446 raw_string_ostream ss(str); 447 ss << Attribute::getNameFromAttrKind( 448 static_cast<Attribute::AttrKind>(attr)); 449 Args.clear(); 450 451 if (count > 0) { 452 Args.push_back(ShuffledArgs[i]); 453 HasArg[i] = true; 454 } 455 if (count > 1) 456 Args.push_back(ConstantInt::get(Type::getInt32Ty(C), value)); 457 458 OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)}); 459 } 460 461 auto *Assume = cast<IntrinsicInst>(IntrinsicInst::Create( 462 FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle)); 463 Assume->insertBefore(&F->begin()->front()); 464 RetainedKnowledgeMap Map; 465 fillMapFromAssume(*Assume, Map); 466 for (int i = 0; i < (Size * 2); i++) { 467 if (!HasArg[i]) 468 continue; 469 RetainedKnowledge K = 470 getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin()); 471 auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind}); 472 ASSERT_TRUE(LookupIt != Map.end()); 473 MinMax MM = LookupIt->second[Assume]; 474 ASSERT_TRUE(MM.Min == MM.Max); 475 ASSERT_TRUE(MM.Min == K.ArgValue); 476 } 477 } 478 479 TEST(AssumeQueryAPI, getKnowledgeFromUseInAssume) { 480 // // For Fuzzing 481 // std::random_device dev; 482 // std::mt19937 Rng(dev()); 483 // while (true) { 484 // unsigned Seed = Rng(); 485 // dbgs() << Seed << "\n"; 486 // RunRandTest(Seed, 100000, 0, 2, 100); 487 // } 488 RunRandTest(23456, 4, 0, 2, 100); 489 RunRandTest(560987, 25, -3, 2, 100); 490 491 // Large bundles can lead to special cases. this is why this test is soo 492 // large. 493 RunRandTest(9876789, 100000, -0, 7, 100); 494 } 495 496 TEST(AssumeQueryAPI, AssumptionCache) { 497 LLVMContext C; 498 SMDiagnostic Err; 499 std::unique_ptr<Module> Mod = parseAssemblyString( 500 "declare void @llvm.assume(i1)\n" 501 "define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3, i1 %B) {\n" 502 "call void @llvm.assume(i1 true) [\"nonnull\"(i32* %P), \"align\"(i32* " 503 "%P2, i32 4), \"align\"(i32* %P, i32 8)]\n" 504 "call void @llvm.assume(i1 %B) [\"test\"(i32* %P1), " 505 "\"dereferenceable\"(i32* %P, i32 4)]\n" 506 "ret void\n}\n", 507 Err, C); 508 if (!Mod) 509 Err.print("AssumeQueryAPI", errs()); 510 Function *F = Mod->getFunction("test"); 511 BasicBlock::iterator First = F->begin()->begin(); 512 BasicBlock::iterator Second = F->begin()->begin(); 513 Second++; 514 AssumptionCacheTracker ACT; 515 AssumptionCache &AC = ACT.getAssumptionCache(*F); 516 auto AR = AC.assumptionsFor(F->getArg(3)); 517 ASSERT_EQ(AR.size(), 0u); 518 AR = AC.assumptionsFor(F->getArg(1)); 519 ASSERT_EQ(AR.size(), 1u); 520 ASSERT_EQ(AR[0].Index, 0u); 521 ASSERT_EQ(AR[0].Assume, &*Second); 522 AR = AC.assumptionsFor(F->getArg(2)); 523 ASSERT_EQ(AR.size(), 1u); 524 ASSERT_EQ(AR[0].Index, 1u); 525 ASSERT_EQ(AR[0].Assume, &*First); 526 AR = AC.assumptionsFor(F->getArg(0)); 527 ASSERT_EQ(AR.size(), 3u); 528 llvm::sort(AR, 529 [](const auto &L, const auto &R) { return L.Index < R.Index; }); 530 ASSERT_EQ(AR[0].Assume, &*First); 531 ASSERT_EQ(AR[0].Index, 0u); 532 ASSERT_EQ(AR[1].Assume, &*Second); 533 ASSERT_EQ(AR[1].Index, 1u); 534 ASSERT_EQ(AR[2].Assume, &*First); 535 ASSERT_EQ(AR[2].Index, 2u); 536 AR = AC.assumptionsFor(F->getArg(4)); 537 ASSERT_EQ(AR.size(), 1u); 538 ASSERT_EQ(AR[0].Assume, &*Second); 539 ASSERT_EQ(AR[0].Index, AssumptionCache::ExprResultIdx); 540 AC.unregisterAssumption(cast<CallInst>(&*Second)); 541 AR = AC.assumptionsFor(F->getArg(1)); 542 ASSERT_EQ(AR.size(), 0u); 543 AR = AC.assumptionsFor(F->getArg(0)); 544 ASSERT_EQ(AR.size(), 3u); 545 llvm::sort(AR, 546 [](const auto &L, const auto &R) { return L.Index < R.Index; }); 547 ASSERT_EQ(AR[0].Assume, &*First); 548 ASSERT_EQ(AR[0].Index, 0u); 549 ASSERT_EQ(AR[1].Assume, nullptr); 550 ASSERT_EQ(AR[1].Index, 1u); 551 ASSERT_EQ(AR[2].Assume, &*First); 552 ASSERT_EQ(AR[2].Index, 2u); 553 AR = AC.assumptionsFor(F->getArg(2)); 554 ASSERT_EQ(AR.size(), 1u); 555 ASSERT_EQ(AR[0].Index, 1u); 556 ASSERT_EQ(AR[0].Assume, &*First); 557 } 558 559 TEST(AssumeQueryAPI, Alignment) { 560 LLVMContext C; 561 SMDiagnostic Err; 562 std::unique_ptr<Module> Mod = parseAssemblyString( 563 "declare void @llvm.assume(i1)\n" 564 "define void @test(i32* %P, i32* %P1, i32* %P2, i32 %I3, i1 %B) {\n" 565 "call void @llvm.assume(i1 true) [\"align\"(i32* %P, i32 8, i32 %I3)]\n" 566 "call void @llvm.assume(i1 true) [\"align\"(i32* %P1, i32 %I3, i32 " 567 "%I3)]\n" 568 "call void @llvm.assume(i1 true) [\"align\"(i32* %P2, i32 16, i32 8)]\n" 569 "ret void\n}\n", 570 Err, C); 571 if (!Mod) 572 Err.print("AssumeQueryAPI", errs()); 573 574 Function *F = Mod->getFunction("test"); 575 BasicBlock::iterator Start = F->begin()->begin(); 576 IntrinsicInst *II; 577 RetainedKnowledge RK; 578 II = cast<IntrinsicInst>(&*Start); 579 RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]); 580 ASSERT_EQ(RK.AttrKind, Attribute::Alignment); 581 ASSERT_EQ(RK.WasOn, F->getArg(0)); 582 ASSERT_EQ(RK.ArgValue, 1u); 583 Start++; 584 II = cast<IntrinsicInst>(&*Start); 585 RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]); 586 ASSERT_EQ(RK.AttrKind, Attribute::Alignment); 587 ASSERT_EQ(RK.WasOn, F->getArg(1)); 588 ASSERT_EQ(RK.ArgValue, 1u); 589 Start++; 590 II = cast<IntrinsicInst>(&*Start); 591 RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]); 592 ASSERT_EQ(RK.AttrKind, Attribute::Alignment); 593 ASSERT_EQ(RK.WasOn, F->getArg(2)); 594 ASSERT_EQ(RK.ArgValue, 8u); 595 } 596