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 RunTest(Head, Tail, Tests); 387 } 388 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 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 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 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