diff --git a/llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp b/llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp --- a/llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp +++ b/llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp @@ -308,4 +308,228 @@ } } +TEST(RandomIRBuilderTest, createStackMemory) { + LLVMContext Ctx; + const char *SourceCode = "\n\ + define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\ + Entry: \n\ + ret void \n\ + }"; + Type *Int32Ty = Type::getInt32Ty(Ctx); + Constant *Int32_1 = ConstantInt::get(Int32Ty, APInt(32, 1)); + Type *Int64Ty = Type::getInt64Ty(Ctx); + Constant *Int64_42 = ConstantInt::get(Int64Ty, APInt(64, 42)); + Type *DoubleTy = Type::getDoubleTy(Ctx); + Constant *Double_0 = + ConstantFP::get(Ctx, APFloat::getZero(DoubleTy->getFltSemantics())); + std::array Types = { + Int32Ty, + Int64Ty, + DoubleTy, + PointerType::get(Ctx, 0), + PointerType::get(Int32Ty, 0), + VectorType::get(Int32Ty, 4, false), + StructType::create({Int32Ty, DoubleTy, Int64Ty}), + ArrayType::get(Int64Ty, 4), + }; + std::array Inits = { + Int32_1, + Int64_42, + Double_0, + UndefValue::get(Types[3]), + UndefValue::get(Types[4]), + ConstantVector::get({Int32_1, Int32_1, Int32_1, Int32_1}), + ConstantStruct::get(cast(Types[6]), + {Int32_1, Double_0, Int64_42}), + ConstantArray::get(cast(Types[7]), + {Int64_42, Int64_42, Int64_42, Int64_42}), + }; + ASSERT_EQ(Types.size(), Inits.size()); + unsigned NumTests = Types.size(); + RandomIRBuilder IB(Seed, Types); + auto CreateStackMemoryAndVerify = [&Ctx, &SourceCode, &IB](Type *Ty, + Value *Init) { + std::unique_ptr M = parseAssembly(SourceCode, Ctx); + Function &F = *M->getFunction("test"); + // Create stack memory without initializer. + IB.createStackMemory(&F, Ty, nullptr); + // Create stack memory with initializer. + IB.createStackMemory(&F, Ty, Init); + EXPECT_FALSE(verifyModule(*M, &errs())); + }; + for (unsigned i = 0; i < NumTests; i++) { + CreateStackMemoryAndVerify(Types[i], Inits[i]); + } +} + +TEST(RandomIRBuilderTest, findOrCreateGlobalVariable) { + LLVMContext Ctx; + const char *SourceCode = "\n\ + @G0 = external global i16 \n\ + @G1 = global i32 1 \n\ + "; + std::array Types = {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx), + Type::getInt64Ty(Ctx)}; + RandomIRBuilder IB(Seed, Types); + + // Find external global + std::unique_ptr M0 = parseAssembly(SourceCode, Ctx); + Type *ExternalTy = M0->globals().begin()->getValueType(); + ASSERT_TRUE(ExternalTy->isIntegerTy(16)); + IB.findOrCreateGlobalVariable(&*M0, {}, fuzzerop::onlyType(Types[0])); + ASSERT_FALSE(verifyModule(*M0, &errs())); + unsigned NumGV0 = M0->getNumNamedValues(); + auto [GV0, DidCreate0] = + IB.findOrCreateGlobalVariable(&*M0, {}, fuzzerop::onlyType(Types[0])); + ASSERT_FALSE(verifyModule(*M0, &errs())); + ASSERT_EQ(M0->getNumNamedValues(), NumGV0 + DidCreate0); + + // Find existing global + std::unique_ptr M1 = parseAssembly(SourceCode, Ctx); + IB.findOrCreateGlobalVariable(&*M1, {}, fuzzerop::onlyType(Types[1])); + ASSERT_FALSE(verifyModule(*M1, &errs())); + unsigned NumGV1 = M1->getNumNamedValues(); + auto [GV1, DidCreate1] = + IB.findOrCreateGlobalVariable(&*M1, {}, fuzzerop::onlyType(Types[1])); + ASSERT_FALSE(verifyModule(*M1, &errs())); + ASSERT_EQ(M1->getNumNamedValues(), NumGV1 + DidCreate1); + + // Create new global + std::unique_ptr M2 = parseAssembly(SourceCode, Ctx); + auto [GV2, DidCreate2] = + IB.findOrCreateGlobalVariable(&*M2, {}, fuzzerop::onlyType(Types[2])); + ASSERT_FALSE(verifyModule(*M2, &errs())); + ASSERT_TRUE(DidCreate2); +} + +/// Checks if the source and sink we find for an instruction has correct +/// domination relation. +TEST(RandomIRBuilderTest, findSourceAndSink) { + const char *Source = "\n\ + define i64 @test(i1 %0, i1 %1, i1 %2, i32 %3, i32 %4) { \n\ + Entry: \n\ + %A = alloca i32, i32 8, align 4 \n\ + %E.1 = and i32 %3, %4 \n\ + %E.2 = add i32 %4 , 1 \n\ + %A.GEP.1 = getelementptr i32, ptr %A, i32 0 \n\ + %A.GEP.2 = getelementptr i32, ptr %A.GEP.1, i32 1 \n\ + %L.2 = load i32, ptr %A.GEP.2 \n\ + %L.1 = load i32, ptr %A.GEP.1 \n\ + %E.3 = sub i32 %E.2, %L.1 \n\ + %Cond.1 = icmp eq i32 %E.3, %E.2 \n\ + %Cond.2 = and i1 %0, %1 \n\ + %Cond = or i1 %Cond.1, %Cond.2 \n\ + br i1 %Cond, label %BB0, label %BB1 \n\ + BB0: \n\ + %Add = add i32 %L.1, %L.2 \n\ + %Sub = sub i32 %L.1, %L.2 \n\ + %Sub.1 = sub i32 %Sub, 12 \n\ + %Cast.1 = bitcast i32 %4 to float \n\ + %Add.2 = add i32 %3, 1 \n\ + %Cast.2 = bitcast i32 %Add.2 to float \n\ + %FAdd = fadd float %Cast.1, %Cast.2 \n\ + %Add.3 = add i32 %L.2, %L.1 \n\ + %Cast.3 = bitcast float %FAdd to i32 \n\ + %Sub.2 = sub i32 %Cast.3, %Sub.1 \n\ + %SExt = sext i32 %Cast.3 to i64 \n\ + %A.GEP.3 = getelementptr i64, ptr %A, i32 1 \n\ + store i64 %SExt, ptr %A.GEP.3 \n\ + br label %Exit \n\ + BB1: \n\ + %PHI.1 = phi i32 [0, %Entry] \n\ + %SExt.1 = sext i1 %Cond.2 to i32 \n\ + %SExt.2 = sext i1 %Cond.1 to i32 \n\ + %E.164 = zext i32 %E.1 to i64 \n\ + %E.264 = zext i32 %E.2 to i64 \n\ + %E.1264 = mul i64 %E.164, %E.264 \n\ + %E.12 = trunc i64 %E.1264 to i32 \n\ + %A.GEP.4 = getelementptr i32, ptr %A, i32 2 \n\ + %A.GEP.5 = getelementptr i32, ptr %A.GEP.4, i32 2 \n\ + store i32 %E.12, ptr %A.GEP.5 \n\ + br label %Exit \n\ + Exit: \n\ + %PHI.2 = phi i32 [%Add, %BB0], [%E.3, %BB1] \n\ + %PHI.3 = phi i64 [%SExt, %BB0], [%E.1264, %BB1] \n\ + %ZExt = zext i32 %PHI.2 to i64 \n\ + %Add.5 = add i64 %PHI.3, 3 \n\ + ret i64 %Add.5 \n\ + }"; + LLVMContext Ctx; + std::array Types = {Type::getInt1Ty(Ctx), Type::getInt32Ty(Ctx), + Type::getInt64Ty(Ctx)}; + std::mt19937 mt(Seed); + std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); + + // Get a random instruction, try to find source and sink, make sure it is + // dominated. + for (int i = 0; i < 100; i++) { + RandomIRBuilder IB(RandInt(mt), Types); + std::unique_ptr M = parseAssembly(Source, Ctx); + Function &F = *M->getFunction("test"); + DominatorTree DT(F); + BasicBlock *BB = makeSampler(IB.Rand, make_pointer_range(F)).getSelection(); + SmallVector Insts; + for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I) + Insts.push_back(&*I); + // Choose an insertion point for our new instruction. + size_t IP = uniform(IB.Rand, 1, Insts.size() - 2); + + auto InstsBefore = ArrayRef(Insts).slice(0, IP); + auto InstsAfter = ArrayRef(Insts).slice(IP); + Value *Src = IB.findOrCreateSource( + *BB, InstsBefore, {}, fuzzerop::onlyType(Types[i % Types.size()])); + ASSERT_TRUE(DT.dominates(Src, Insts[IP + 1])); + Instruction *Sink = IB.connectToSink(*BB, InstsAfter, Insts[IP - 1]); + if (!DT.dominates(Insts[IP - 1], Sink)) { + errs() << *Insts[IP - 1] << "\n" << *Sink << "\n "; + } + ASSERT_TRUE(DT.dominates(Insts[IP - 1], Sink)); + } +} +TEST(RandomIRBuilderTest, sinkToInstrinsic) { + const char *Source = "\n\ + declare double @llvm.sqrt.f64(double %Val) \n\ + declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind \n\ + \n\ + define double @test(double %0, double %1, i64 %2, i64 %3, i64 %4, i8 %5) { \n\ + Entry: \n\ + %sqrt = call double @llvm.sqrt.f64(double %0) \n\ + call void @llvm.ubsantrap(i8 1) \n\ + ret double %sqrt \n\ + }"; + LLVMContext Ctx; + std::array Types = {Type::getInt8Ty(Ctx), Type::getInt64Ty(Ctx), + Type::getDoubleTy(Ctx)}; + std::mt19937 mt(Seed); + std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); + + RandomIRBuilder IB(RandInt(mt), Types); + std::unique_ptr M = parseAssembly(Source, Ctx); + Function &F = *M->getFunction("test"); + BasicBlock &BB = F.getEntryBlock(); + bool Modified = false; + + Instruction *I = &*BB.begin(); + for (int i = 0; i < 20; i++) { + Value *OldOperand = I->getOperand(0); + Value *Src = F.getArg(1); + IB.connectToSink(BB, {I}, Src); + Value *NewOperand = I->getOperand(0); + Modified |= (OldOperand != NewOperand); + ASSERT_FALSE(verifyModule(*M, &errs())); + } + ASSERT_TRUE(Modified); + + Modified = false; + I = I->getNextNonDebugInstruction(); + for (int i = 0; i < 20; i++) { + Value *OldOperand = I->getOperand(0); + Value *Src = F.getArg(5); + IB.connectToSink(BB, {I}, Src); + Value *NewOperand = I->getOperand(0); + Modified |= (OldOperand != NewOperand); + ASSERT_FALSE(verifyModule(*M, &errs())); + } + ASSERT_FALSE(Modified); +} } // namespace