1 //===- RandomIRBuilderTest.cpp - Tests for injector strategy --------------===// 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/FuzzMutate/RandomIRBuilder.h" 10 #include "llvm/ADT/StringRef.h" 11 #include "llvm/AsmParser/Parser.h" 12 #include "llvm/AsmParser/SlotMapping.h" 13 #include "llvm/FuzzMutate/IRMutator.h" 14 #include "llvm/FuzzMutate/OpDescriptor.h" 15 #include "llvm/FuzzMutate/Operations.h" 16 #include "llvm/FuzzMutate/Random.h" 17 #include "llvm/IR/Constants.h" 18 #include "llvm/IR/Dominators.h" 19 #include "llvm/IR/Instructions.h" 20 #include "llvm/IR/LLVMContext.h" 21 #include "llvm/IR/Module.h" 22 #include "llvm/IR/Verifier.h" 23 #include "llvm/Support/SourceMgr.h" 24 25 #include "gtest/gtest.h" 26 27 using namespace llvm; 28 29 static constexpr int Seed = 5; 30 31 namespace { 32 33 std::unique_ptr<Module> parseAssembly(const char *Assembly, 34 LLVMContext &Context) { 35 36 SMDiagnostic Error; 37 std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context); 38 39 std::string ErrMsg; 40 raw_string_ostream OS(ErrMsg); 41 Error.print("", OS); 42 43 assert(M && !verifyModule(*M, &errs())); 44 return M; 45 } 46 47 TEST(RandomIRBuilderTest, ShuffleVectorIncorrectOperands) { 48 // Test that we don't create load instruction as a source for the shuffle 49 // vector operation. 50 51 LLVMContext Ctx; 52 const char *Source = 53 "define <2 x i32> @test(<2 x i1> %cond, <2 x i32> %a) {\n" 54 " %A = alloca <2 x i32>\n" 55 " %I = insertelement <2 x i32> %a, i32 1, i32 1\n" 56 " ret <2 x i32> undef\n" 57 "}"; 58 auto M = parseAssembly(Source, Ctx); 59 60 fuzzerop::OpDescriptor Descr = fuzzerop::shuffleVectorDescriptor(1); 61 62 // Empty known types since we ShuffleVector descriptor doesn't care about them 63 RandomIRBuilder IB(Seed, {}); 64 65 // Get first basic block of the first function 66 Function &F = *M->begin(); 67 BasicBlock &BB = *F.begin(); 68 69 SmallVector<Instruction *, 32> Insts; 70 for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I) 71 Insts.push_back(&*I); 72 73 // Pick first and second sources 74 SmallVector<Value *, 2> Srcs; 75 ASSERT_TRUE(Descr.SourcePreds[0].matches(Srcs, Insts[1])); 76 Srcs.push_back(Insts[1]); 77 ASSERT_TRUE(Descr.SourcePreds[1].matches(Srcs, Insts[1])); 78 Srcs.push_back(Insts[1]); 79 80 // Create new source. Check that it always matches with the descriptor. 81 // Run some iterations to account for random decisions. 82 for (int i = 0; i < 10; ++i) { 83 Value *LastSrc = IB.newSource(BB, Insts, Srcs, Descr.SourcePreds[2]); 84 ASSERT_TRUE(Descr.SourcePreds[2].matches(Srcs, LastSrc)); 85 } 86 } 87 88 TEST(RandomIRBuilderTest, InsertValueIndexes) { 89 // Check that we will generate correct indexes for the insertvalue operation 90 91 LLVMContext Ctx; 92 const char *Source = "%T = type {i8, i32, i64}\n" 93 "define void @test() {\n" 94 " %A = alloca %T\n" 95 " %L = load %T, ptr %A" 96 " ret void\n" 97 "}"; 98 auto M = parseAssembly(Source, Ctx); 99 100 fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1); 101 102 std::array<Type *, 3> Types = {Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx), 103 Type::getInt64Ty(Ctx)}; 104 RandomIRBuilder IB(Seed, Types); 105 106 // Get first basic block of the first function 107 Function &F = *M->begin(); 108 BasicBlock &BB = *F.begin(); 109 110 // Pick first source 111 Instruction *Src = &*std::next(BB.begin()); 112 113 SmallVector<Value *, 2> Srcs(2); 114 ASSERT_TRUE(IVDescr.SourcePreds[0].matches({}, Src)); 115 Srcs[0] = Src; 116 117 // Generate constants for each of the types and check that we pick correct 118 // index for the given type 119 for (auto *T : Types) { 120 // Loop to account for possible random decisions 121 for (int i = 0; i < 10; ++i) { 122 // Create value we want to insert. Only it's type matters. 123 Srcs[1] = ConstantInt::get(T, 5); 124 125 // Try to pick correct index 126 Value *Src = 127 IB.findOrCreateSource(BB, &*BB.begin(), Srcs, IVDescr.SourcePreds[2]); 128 ASSERT_TRUE(IVDescr.SourcePreds[2].matches(Srcs, Src)); 129 } 130 } 131 } 132 133 TEST(RandomIRBuilderTest, ShuffleVectorSink) { 134 // Check that we will never use shuffle vector mask as a sink from the 135 // unrelated operation. 136 137 LLVMContext Ctx; 138 const char *SourceCode = 139 "define void @test(<4 x i32> %a) {\n" 140 " %S1 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n" 141 " %S2 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n" 142 " ret void\n" 143 "}"; 144 auto M = parseAssembly(SourceCode, Ctx); 145 146 fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1); 147 148 RandomIRBuilder IB(Seed, {}); 149 150 // Get first basic block of the first function 151 Function &F = *M->begin(); 152 BasicBlock &BB = *F.begin(); 153 154 // Source is %S1 155 Instruction *Source = &*BB.begin(); 156 // Sink is %S2 157 SmallVector<Instruction *, 1> Sinks = {&*std::next(BB.begin())}; 158 159 // Loop to account for random decisions 160 for (int i = 0; i < 10; ++i) { 161 // Try to connect S1 to S2. We should always create new sink. 162 IB.connectToSink(BB, Sinks, Source); 163 ASSERT_TRUE(!verifyModule(*M, &errs())); 164 } 165 } 166 167 TEST(RandomIRBuilderTest, InsertValueArray) { 168 // Check that we can generate insertvalue for the vector operations 169 170 LLVMContext Ctx; 171 const char *SourceCode = "define void @test() {\n" 172 " %A = alloca [8 x i32]\n" 173 " %L = load [8 x i32], ptr %A" 174 " ret void\n" 175 "}"; 176 auto M = parseAssembly(SourceCode, Ctx); 177 178 fuzzerop::OpDescriptor Descr = fuzzerop::insertValueDescriptor(1); 179 180 std::array<Type *, 3> Types = {Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx), 181 Type::getInt64Ty(Ctx)}; 182 RandomIRBuilder IB(Seed, Types); 183 184 // Get first basic block of the first function 185 Function &F = *M->begin(); 186 BasicBlock &BB = *F.begin(); 187 188 // Pick first source 189 Instruction *Source = &*std::next(BB.begin()); 190 ASSERT_TRUE(Descr.SourcePreds[0].matches({}, Source)); 191 192 SmallVector<Value *, 2> Srcs(2); 193 194 // Check that we can always pick the last two operands. 195 for (int i = 0; i < 10; ++i) { 196 Srcs[0] = Source; 197 Srcs[1] = IB.findOrCreateSource(BB, {Source}, Srcs, Descr.SourcePreds[1]); 198 IB.findOrCreateSource(BB, {}, Srcs, Descr.SourcePreds[2]); 199 } 200 } 201 202 TEST(RandomIRBuilderTest, Invokes) { 203 // Check that we never generate load or store after invoke instruction 204 205 LLVMContext Ctx; 206 const char *SourceCode = 207 "declare ptr @f()" 208 "declare i32 @personality_function()" 209 "define ptr @test() personality ptr @personality_function {\n" 210 "entry:\n" 211 " %val = invoke ptr @f()\n" 212 " to label %normal unwind label %exceptional\n" 213 "normal:\n" 214 " ret ptr %val\n" 215 "exceptional:\n" 216 " %landing_pad4 = landingpad token cleanup\n" 217 " ret ptr undef\n" 218 "}"; 219 auto M = parseAssembly(SourceCode, Ctx); 220 221 std::array<Type *, 1> Types = {Type::getInt8Ty(Ctx)}; 222 RandomIRBuilder IB(Seed, Types); 223 224 // Get first basic block of the test function 225 Function &F = *M->getFunction("test"); 226 BasicBlock &BB = *F.begin(); 227 228 Instruction *Invoke = &*BB.begin(); 229 230 // Find source but never insert new load after invoke 231 for (int i = 0; i < 10; ++i) { 232 (void)IB.findOrCreateSource(BB, {Invoke}, {}, fuzzerop::anyIntType()); 233 ASSERT_TRUE(!verifyModule(*M, &errs())); 234 } 235 } 236 237 TEST(RandomIRBuilderTest, FirstClassTypes) { 238 // Check that we never insert new source as a load from non first class 239 // or unsized type. 240 241 LLVMContext Ctx; 242 const char *SourceCode = "%Opaque = type opaque\n" 243 "define void @test(i8* %ptr) {\n" 244 "entry:\n" 245 " %tmp = bitcast i8* %ptr to i32* (i32*)*\n" 246 " %tmp1 = bitcast i8* %ptr to %Opaque*\n" 247 " ret void\n" 248 "}"; 249 auto M = parseAssembly(SourceCode, Ctx); 250 251 std::array<Type *, 1> Types = {Type::getInt8Ty(Ctx)}; 252 RandomIRBuilder IB(Seed, Types); 253 254 Function &F = *M->getFunction("test"); 255 BasicBlock &BB = *F.begin(); 256 // Non first class type 257 Instruction *FuncPtr = &*BB.begin(); 258 // Unsized type 259 Instruction *OpaquePtr = &*std::next(BB.begin()); 260 261 for (int i = 0; i < 10; ++i) { 262 Value *V = IB.findOrCreateSource(BB, {FuncPtr, OpaquePtr}); 263 // To make sure we are allowed to load from a global variable 264 if (LoadInst *LI = dyn_cast<LoadInst>(V)) { 265 EXPECT_NE(LI->getOperand(0), FuncPtr); 266 } 267 } 268 } 269 270 TEST(RandomIRBuilderTest, SwiftError) { 271 // Check that we never pick swifterror value as a source for operation 272 // other than load, store and call. 273 274 LLVMContext Ctx; 275 const char *SourceCode = "declare void @use(ptr swifterror %err)" 276 "define void @test() {\n" 277 "entry:\n" 278 " %err = alloca swifterror ptr, align 8\n" 279 " call void @use(ptr swifterror %err)\n" 280 " ret void\n" 281 "}"; 282 auto M = parseAssembly(SourceCode, Ctx); 283 284 std::array<Type *, 1> Types = {Type::getInt8Ty(Ctx)}; 285 RandomIRBuilder IB(Seed, Types); 286 287 // Get first basic block of the test function 288 Function &F = *M->getFunction("test"); 289 BasicBlock &BB = *F.begin(); 290 Instruction *Alloca = &*BB.begin(); 291 292 fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1); 293 294 for (int i = 0; i < 10; ++i) { 295 Value *V = IB.findOrCreateSource(BB, {Alloca}, {}, Descr.SourcePreds[0]); 296 ASSERT_FALSE(isa<AllocaInst>(V)); 297 } 298 } 299 300 TEST(RandomIRBuilderTest, dontConnectToSwitch) { 301 // Check that we never put anything into switch's case branch 302 // If we accidently put a variable, the module is invalid. 303 LLVMContext Ctx; 304 const char *SourceCode = "\n\ 305 define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\ 306 Entry: \n\ 307 %I.1 = add i32 %I, 42 \n\ 308 %J.1 = add i32 %J, 42 \n\ 309 %IJ = add i32 %I, %J \n\ 310 switch i32 %I, label %Default [ \n\ 311 i32 1, label %OnOne \n\ 312 ] \n\ 313 Default: \n\ 314 %CIEqJ = icmp eq i32 %I.1, %J.1 \n\ 315 %CISltJ = icmp slt i32 %I.1, %J.1 \n\ 316 %CAnd = and i1 %C1, %C2 \n\ 317 br i1 %CIEqJ, label %Default, label %Exit \n\ 318 OnOne: \n\ 319 br i1 %C1, label %OnOne, label %Exit \n\ 320 Exit: \n\ 321 ret void \n\ 322 }"; 323 324 std::array<Type *, 2> Types = {Type::getInt32Ty(Ctx), Type::getInt1Ty(Ctx)}; 325 RandomIRBuilder IB(Seed, Types); 326 for (int i = 0; i < 20; i++) { 327 std::unique_ptr<Module> M = parseAssembly(SourceCode, Ctx); 328 Function &F = *M->getFunction("test"); 329 auto RS = makeSampler(IB.Rand, make_pointer_range(F)); 330 BasicBlock *BB = RS.getSelection(); 331 SmallVector<Instruction *, 32> Insts; 332 for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I) 333 Insts.push_back(&*I); 334 if (Insts.size() < 2) 335 continue; 336 // Choose an instruction and connect to later operations. 337 size_t IP = uniform<size_t>(IB.Rand, 1, Insts.size() - 1); 338 Instruction *Inst = Insts[IP - 1]; 339 auto ConnectAfter = ArrayRef(Insts).slice(IP); 340 IB.connectToSink(*BB, ConnectAfter, Inst); 341 ASSERT_FALSE(verifyModule(*M, &errs())); 342 } 343 } 344 345 TEST(RandomIRBuilderTest, createStackMemory) { 346 LLVMContext Ctx; 347 const char *SourceCode = "\n\ 348 define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\ 349 Entry: \n\ 350 ret void \n\ 351 }"; 352 Type *Int32Ty = Type::getInt32Ty(Ctx); 353 Constant *Int32_1 = ConstantInt::get(Int32Ty, APInt(32, 1)); 354 Type *Int64Ty = Type::getInt64Ty(Ctx); 355 Constant *Int64_42 = ConstantInt::get(Int64Ty, APInt(64, 42)); 356 Type *DoubleTy = Type::getDoubleTy(Ctx); 357 Constant *Double_0 = 358 ConstantFP::get(Ctx, APFloat::getZero(DoubleTy->getFltSemantics())); 359 std::array<Type *, 8> Types = { 360 Int32Ty, 361 Int64Ty, 362 DoubleTy, 363 PointerType::get(Ctx, 0), 364 PointerType::get(Int32Ty, 0), 365 VectorType::get(Int32Ty, 4, false), 366 StructType::create({Int32Ty, DoubleTy, Int64Ty}), 367 ArrayType::get(Int64Ty, 4), 368 }; 369 std::array<Value *, 8> Inits = { 370 Int32_1, 371 Int64_42, 372 Double_0, 373 UndefValue::get(Types[3]), 374 UndefValue::get(Types[4]), 375 ConstantVector::get({Int32_1, Int32_1, Int32_1, Int32_1}), 376 ConstantStruct::get(cast<StructType>(Types[6]), 377 {Int32_1, Double_0, Int64_42}), 378 ConstantArray::get(cast<ArrayType>(Types[7]), 379 {Int64_42, Int64_42, Int64_42, Int64_42}), 380 }; 381 ASSERT_EQ(Types.size(), Inits.size()); 382 unsigned NumTests = Types.size(); 383 RandomIRBuilder IB(Seed, Types); 384 auto CreateStackMemoryAndVerify = [&Ctx, &SourceCode, &IB](Type *Ty, 385 Value *Init) { 386 std::unique_ptr<Module> M = parseAssembly(SourceCode, Ctx); 387 Function &F = *M->getFunction("test"); 388 // Create stack memory without initializer. 389 IB.createStackMemory(&F, Ty, nullptr); 390 // Create stack memory with initializer. 391 IB.createStackMemory(&F, Ty, Init); 392 EXPECT_FALSE(verifyModule(*M, &errs())); 393 }; 394 for (unsigned i = 0; i < NumTests; i++) { 395 CreateStackMemoryAndVerify(Types[i], Inits[i]); 396 } 397 } 398 399 TEST(RandomIRBuilderTest, findOrCreateGlobalVariable) { 400 LLVMContext Ctx; 401 const char *SourceCode = "\n\ 402 @G0 = external global i16 \n\ 403 @G1 = global i32 1 \n\ 404 "; 405 std::array<Type *, 3> Types = {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx), 406 Type::getInt64Ty(Ctx)}; 407 RandomIRBuilder IB(Seed, Types); 408 409 // Find external global 410 std::unique_ptr<Module> M0 = parseAssembly(SourceCode, Ctx); 411 Type *ExternalTy = M0->globals().begin()->getValueType(); 412 ASSERT_TRUE(ExternalTy->isIntegerTy(16)); 413 IB.findOrCreateGlobalVariable(&*M0, {}, fuzzerop::onlyType(Types[0])); 414 ASSERT_FALSE(verifyModule(*M0, &errs())); 415 unsigned NumGV0 = M0->getNumNamedValues(); 416 auto [GV0, DidCreate0] = 417 IB.findOrCreateGlobalVariable(&*M0, {}, fuzzerop::onlyType(Types[0])); 418 ASSERT_FALSE(verifyModule(*M0, &errs())); 419 ASSERT_EQ(M0->getNumNamedValues(), NumGV0 + DidCreate0); 420 421 // Find existing global 422 std::unique_ptr<Module> M1 = parseAssembly(SourceCode, Ctx); 423 IB.findOrCreateGlobalVariable(&*M1, {}, fuzzerop::onlyType(Types[1])); 424 ASSERT_FALSE(verifyModule(*M1, &errs())); 425 unsigned NumGV1 = M1->getNumNamedValues(); 426 auto [GV1, DidCreate1] = 427 IB.findOrCreateGlobalVariable(&*M1, {}, fuzzerop::onlyType(Types[1])); 428 ASSERT_FALSE(verifyModule(*M1, &errs())); 429 ASSERT_EQ(M1->getNumNamedValues(), NumGV1 + DidCreate1); 430 431 // Create new global 432 std::unique_ptr<Module> M2 = parseAssembly(SourceCode, Ctx); 433 auto [GV2, DidCreate2] = 434 IB.findOrCreateGlobalVariable(&*M2, {}, fuzzerop::onlyType(Types[2])); 435 ASSERT_FALSE(verifyModule(*M2, &errs())); 436 ASSERT_TRUE(DidCreate2); 437 } 438 439 /// Checks if the source and sink we find for an instruction has correct 440 /// domination relation. 441 TEST(RandomIRBuilderTest, findSourceAndSink) { 442 const char *Source = "\n\ 443 define i64 @test(i1 %0, i1 %1, i1 %2, i32 %3, i32 %4) { \n\ 444 Entry: \n\ 445 %A = alloca i32, i32 8, align 4 \n\ 446 %E.1 = and i32 %3, %4 \n\ 447 %E.2 = add i32 %4 , 1 \n\ 448 %A.GEP.1 = getelementptr i32, ptr %A, i32 0 \n\ 449 %A.GEP.2 = getelementptr i32, ptr %A.GEP.1, i32 1 \n\ 450 %L.2 = load i32, ptr %A.GEP.2 \n\ 451 %L.1 = load i32, ptr %A.GEP.1 \n\ 452 %E.3 = sub i32 %E.2, %L.1 \n\ 453 %Cond.1 = icmp eq i32 %E.3, %E.2 \n\ 454 %Cond.2 = and i1 %0, %1 \n\ 455 %Cond = or i1 %Cond.1, %Cond.2 \n\ 456 br i1 %Cond, label %BB0, label %BB1 \n\ 457 BB0: \n\ 458 %Add = add i32 %L.1, %L.2 \n\ 459 %Sub = sub i32 %L.1, %L.2 \n\ 460 %Sub.1 = sub i32 %Sub, 12 \n\ 461 %Cast.1 = bitcast i32 %4 to float \n\ 462 %Add.2 = add i32 %3, 1 \n\ 463 %Cast.2 = bitcast i32 %Add.2 to float \n\ 464 %FAdd = fadd float %Cast.1, %Cast.2 \n\ 465 %Add.3 = add i32 %L.2, %L.1 \n\ 466 %Cast.3 = bitcast float %FAdd to i32 \n\ 467 %Sub.2 = sub i32 %Cast.3, %Sub.1 \n\ 468 %SExt = sext i32 %Cast.3 to i64 \n\ 469 %A.GEP.3 = getelementptr i64, ptr %A, i32 1 \n\ 470 store i64 %SExt, ptr %A.GEP.3 \n\ 471 br label %Exit \n\ 472 BB1: \n\ 473 %PHI.1 = phi i32 [0, %Entry] \n\ 474 %SExt.1 = sext i1 %Cond.2 to i32 \n\ 475 %SExt.2 = sext i1 %Cond.1 to i32 \n\ 476 %E.164 = zext i32 %E.1 to i64 \n\ 477 %E.264 = zext i32 %E.2 to i64 \n\ 478 %E.1264 = mul i64 %E.164, %E.264 \n\ 479 %E.12 = trunc i64 %E.1264 to i32 \n\ 480 %A.GEP.4 = getelementptr i32, ptr %A, i32 2 \n\ 481 %A.GEP.5 = getelementptr i32, ptr %A.GEP.4, i32 2 \n\ 482 store i32 %E.12, ptr %A.GEP.5 \n\ 483 br label %Exit \n\ 484 Exit: \n\ 485 %PHI.2 = phi i32 [%Add, %BB0], [%E.3, %BB1] \n\ 486 %PHI.3 = phi i64 [%SExt, %BB0], [%E.1264, %BB1] \n\ 487 %ZExt = zext i32 %PHI.2 to i64 \n\ 488 %Add.5 = add i64 %PHI.3, 3 \n\ 489 ret i64 %Add.5 \n\ 490 }"; 491 LLVMContext Ctx; 492 std::array<Type *, 3> Types = {Type::getInt1Ty(Ctx), Type::getInt32Ty(Ctx), 493 Type::getInt64Ty(Ctx)}; 494 std::mt19937 mt(Seed); 495 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); 496 497 // Get a random instruction, try to find source and sink, make sure it is 498 // dominated. 499 for (int i = 0; i < 100; i++) { 500 RandomIRBuilder IB(RandInt(mt), Types); 501 std::unique_ptr<Module> M = parseAssembly(Source, Ctx); 502 Function &F = *M->getFunction("test"); 503 DominatorTree DT(F); 504 BasicBlock *BB = makeSampler(IB.Rand, make_pointer_range(F)).getSelection(); 505 SmallVector<Instruction *, 32> Insts; 506 for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I) 507 Insts.push_back(&*I); 508 // Choose an insertion point for our new instruction. 509 size_t IP = uniform<size_t>(IB.Rand, 1, Insts.size() - 2); 510 511 auto InstsBefore = ArrayRef(Insts).slice(0, IP); 512 auto InstsAfter = ArrayRef(Insts).slice(IP); 513 Value *Src = IB.findOrCreateSource( 514 *BB, InstsBefore, {}, fuzzerop::onlyType(Types[i % Types.size()])); 515 ASSERT_TRUE(DT.dominates(Src, Insts[IP + 1])); 516 Instruction *Sink = IB.connectToSink(*BB, InstsAfter, Insts[IP - 1]); 517 if (!DT.dominates(Insts[IP - 1], Sink)) { 518 errs() << *Insts[IP - 1] << "\n" << *Sink << "\n "; 519 } 520 ASSERT_TRUE(DT.dominates(Insts[IP - 1], Sink)); 521 } 522 } 523 TEST(RandomIRBuilderTest, sinkToInstrinsic) { 524 const char *Source = "\n\ 525 declare double @llvm.sqrt.f64(double %Val) \n\ 526 declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind \n\ 527 \n\ 528 define double @test(double %0, double %1, i64 %2, i64 %3, i64 %4, i8 %5) { \n\ 529 Entry: \n\ 530 %sqrt = call double @llvm.sqrt.f64(double %0) \n\ 531 call void @llvm.ubsantrap(i8 1) \n\ 532 ret double %sqrt \n\ 533 }"; 534 LLVMContext Ctx; 535 std::array<Type *, 3> Types = {Type::getInt8Ty(Ctx), Type::getInt64Ty(Ctx), 536 Type::getDoubleTy(Ctx)}; 537 std::mt19937 mt(Seed); 538 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); 539 540 RandomIRBuilder IB(RandInt(mt), Types); 541 std::unique_ptr<Module> M = parseAssembly(Source, Ctx); 542 Function &F = *M->getFunction("test"); 543 BasicBlock &BB = F.getEntryBlock(); 544 bool Modified = false; 545 546 Instruction *I = &*BB.begin(); 547 for (int i = 0; i < 20; i++) { 548 Value *OldOperand = I->getOperand(0); 549 Value *Src = F.getArg(1); 550 IB.connectToSink(BB, {I}, Src); 551 Value *NewOperand = I->getOperand(0); 552 Modified |= (OldOperand != NewOperand); 553 ASSERT_FALSE(verifyModule(*M, &errs())); 554 } 555 ASSERT_TRUE(Modified); 556 557 Modified = false; 558 I = I->getNextNonDebugInstruction(); 559 for (int i = 0; i < 20; i++) { 560 Value *OldOperand = I->getOperand(0); 561 Value *Src = F.getArg(5); 562 IB.connectToSink(BB, {I}, Src); 563 Value *NewOperand = I->getOperand(0); 564 Modified |= (OldOperand != NewOperand); 565 ASSERT_FALSE(verifyModule(*M, &errs())); 566 } 567 ASSERT_FALSE(Modified); 568 } 569 } // namespace 570