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