diff --git a/llvm/include/llvm/FuzzMutate/IRMutator.h b/llvm/include/llvm/FuzzMutate/IRMutator.h --- a/llvm/include/llvm/FuzzMutate/IRMutator.h +++ b/llvm/include/llvm/FuzzMutate/IRMutator.h @@ -95,6 +95,7 @@ void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; }; +/// Strategy that deletes instructions when the Module is too large. class InstDeleterIRStrategy : public IRMutationStrategy { public: uint64_t getWeight(size_t CurrentSize, size_t MaxSize, @@ -105,6 +106,7 @@ void mutate(Instruction &Inst, RandomIRBuilder &IB) override; }; +/// Strategy that modifies instruction attributes and operands. class InstModificationIRStrategy : public IRMutationStrategy { public: uint64_t getWeight(size_t CurrentSize, size_t MaxSize, diff --git a/llvm/lib/FuzzMutate/IRMutator.cpp b/llvm/lib/FuzzMutate/IRMutator.cpp --- a/llvm/lib/FuzzMutate/IRMutator.cpp +++ b/llvm/lib/FuzzMutate/IRMutator.cpp @@ -162,8 +162,8 @@ auto RS = makeSampler(IB.Rand); for (Instruction &Inst : instructions(F)) { // TODO: We can't handle these instructions. - if (Inst.isTerminator() || Inst.isEHPad() || - Inst.isSwiftError() || isa(Inst)) + if (Inst.isTerminator() || Inst.isEHPad() || Inst.isSwiftError() || + isa(Inst)) continue; RS.sample(&Inst, /*Weight=*/1); @@ -218,8 +218,8 @@ case Instruction::Mul: case Instruction::Sub: case Instruction::Shl: - Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(true); }), - Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(false); }); + Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(true); }); + Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(false); }); Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(true); }); Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(false); }); @@ -244,6 +244,54 @@ break; } + // Randomly switch operands of instructions + std::pair NoneItem({-1, -1}), ShuffleItems(NoneItem); + switch (Inst.getOpcode()) { + case Instruction::SDiv: + case Instruction::UDiv: + case Instruction::SRem: + case Instruction::URem: + case Instruction::FDiv: + case Instruction::FRem: { + // Verify that the after shuffle the second operand is not + // constant 0. + Value *Operand = Inst.getOperand(0); + if (Constant *C = dyn_cast(Operand)) { + if (!C->isZeroValue()) { + ShuffleItems = {0, 1}; + } + } + break; + } + case Instruction::Select: + ShuffleItems = {1, 2}; + break; + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::ICmp: + case Instruction::FCmp: + case Instruction::ShuffleVector: + ShuffleItems = {0, 1}; + break; + } + if (ShuffleItems != NoneItem) { + Modifications.push_back([&Inst, &ShuffleItems]() { + Value *Op0 = Inst.getOperand(ShuffleItems.first); + Inst.setOperand(ShuffleItems.first, Inst.getOperand(ShuffleItems.second)); + Inst.setOperand(ShuffleItems.second, Op0); + }); + } + auto RS = makeSampler(IB.Rand, Modifications); if (RS) RS.getSelection()(); diff --git a/llvm/unittests/FuzzMutate/StrategiesTest.cpp b/llvm/unittests/FuzzMutate/StrategiesTest.cpp --- a/llvm/unittests/FuzzMutate/StrategiesTest.cpp +++ b/llvm/unittests/FuzzMutate/StrategiesTest.cpp @@ -31,37 +31,25 @@ Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; std::vector> Strategies; - Strategies.push_back( - std::make_unique( - InjectorIRStrategy::getDefaultOps())); + Strategies.push_back(std::make_unique( + InjectorIRStrategy::getDefaultOps())); return std::make_unique(std::move(Types), std::move(Strategies)); } -std::unique_ptr createDeleterMutator() { +template std::unique_ptr createMutator() { std::vector Types{ Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; std::vector> Strategies; - Strategies.push_back(std::make_unique()); + Strategies.push_back(std::make_unique()); return std::make_unique(std::move(Types), std::move(Strategies)); } -std::unique_ptr createInstModifierMutator() { - std::vector Types{ - Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, - Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; - - std::vector> Strategies; - Strategies.push_back(std::make_unique()); - - return std::make_unique(std::move(Types), std::move(Strategies)); -} - -std::unique_ptr parseAssembly( - const char *Assembly, LLVMContext &Context) { +std::unique_ptr parseAssembly(const char *Assembly, + LLVMContext &Context) { SMDiagnostic Error; std::unique_ptr M = parseAssemblyString(Assembly, Error, Context); @@ -104,17 +92,17 @@ // Test that we don't crash even if we can't remove from one of the functions. StringRef Source = "" - "define <8 x i32> @func1() {\n" - "ret <8 x i32> undef\n" - "}\n" - "\n" - "define i32 @func2() {\n" - "%A9 = alloca i32\n" - "%L6 = load i32, i32* %A9\n" - "ret i32 %L6\n" - "}\n"; - - auto Mutator = createDeleterMutator(); + "define <8 x i32> @func1() {\n" + "ret <8 x i32> undef\n" + "}\n" + "\n" + "define i32 @func2() {\n" + "%A9 = alloca i32\n" + "%L6 = load i32, i32* %A9\n" + "ret i32 %L6\n" + "}\n"; + + auto Mutator = createMutator(); ASSERT_TRUE(Mutator); IterateOnSource(Source, *Mutator); @@ -140,7 +128,7 @@ ret i32 %a.0\n\ }"; - auto Mutator = createDeleterMutator(); + auto Mutator = createMutator(); ASSERT_TRUE(Mutator); IterateOnSource(Source, *Mutator); @@ -155,7 +143,7 @@ ret i32 %a\n\ }"); - auto Mutator = createInstModifierMutator(); + auto Mutator = createMutator(); ASSERT_TRUE(Mutator); auto M = parseAssembly(Source.data(), Ctx); @@ -199,7 +187,7 @@ ret i1 %a\n\ }"; - auto Mutator = createInstModifierMutator(); + auto Mutator = createMutator(); ASSERT_TRUE(Mutator); auto M = parseAssembly(Source.data(), Ctx); @@ -224,7 +212,7 @@ ret i32* %gep\n\ }"; - auto Mutator = createInstModifierMutator(); + auto Mutator = createMutator(); ASSERT_TRUE(Mutator); auto M = parseAssembly(Source.data(), Ctx); @@ -240,4 +228,93 @@ EXPECT_TRUE(FoundInbounds); } + +/// The caller has to guarantee that function argument are used in the SAME +/// place as the operand. +void VerfyOperandShuffled(StringRef Source, std::pair ShuffleItems) { + LLVMContext Ctx; + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + + auto M = parseAssembly(Source.data(), Ctx); + auto &F = *M->begin(); + Instruction *Inst = &*F.begin()->begin(); + ASSERT_TRUE(M && !verifyModule(*M, &errs())); + ASSERT_TRUE(Inst->getOperand(ShuffleItems.first) == + dyn_cast(F.getArg(ShuffleItems.first))); + ASSERT_TRUE(Inst->getOperand(ShuffleItems.second) == + dyn_cast(F.getArg(ShuffleItems.second))); + + // Use Seed = 0 so the first instruction is selected. + Mutator->mutateModule(*M, 0, Source.size(), Source.size() + 100); + EXPECT_TRUE(!verifyModule(*M, &errs())); + + EXPECT_TRUE(Inst->getOperand(ShuffleItems.first) == + dyn_cast(F.getArg(ShuffleItems.second))); + EXPECT_TRUE(Inst->getOperand(ShuffleItems.second) == + dyn_cast(F.getArg(ShuffleItems.first))); +} + +TEST(InstModificationIRStrategyTest, ShuffleFAdd) { + StringRef Source = "\n\ + define float @test(float %0, float %1) {\n\ + %add = fadd float %0, %1\n\ + ret float %add\n\ + }"; + VerfyOperandShuffled(Source, {0, 1}); +} +TEST(InstModificationIRStrategyTest, ShuffleICMP) { + StringRef Source = "\n\ + define <8 x i1> @test(<8 x i32> %0, <8 x i32> %1) {\n\ + %cmp = icmp eq <8 x i32> %0, %1\n\ + ret <8 x i1> %cmp\n\ + }"; + VerfyOperandShuffled(Source, {0, 1}); +} + +TEST(InstModificationIRStrategyTest, ShuffleSelect) { + StringRef Source = "\n\ + define float @test(i1 %0, float %1, float %2) {\n\ + %select = select i1 %0, float %1, float %2\n\ + ret float %select\n\ + }"; + VerfyOperandShuffled(Source, {1, 2}); +} + +void VerfyDivDidntShuffle(StringRef Source) { + LLVMContext Ctx; + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + + auto M = parseAssembly(Source.data(), Ctx); + auto &F = *M->begin(); + Instruction *Inst = &*F.begin()->begin(); + ASSERT_TRUE(M && !verifyModule(*M, &errs())); + + EXPECT_TRUE(isa(Inst->getOperand(0))); + EXPECT_TRUE(Inst->getOperand(1) == dyn_cast(F.getArg(0))); + + Mutator->mutateModule(*M, Seed, Source.size(), Source.size() + 100); + EXPECT_TRUE(!verifyModule(*M, &errs())); + + // Didn't shuffle. + EXPECT_TRUE(isa(Inst->getOperand(0))); + EXPECT_TRUE(Inst->getOperand(1) == dyn_cast(F.getArg(0))); +} +TEST(InstModificationIRStrategyTest, DidntShuffleSDiv) { + StringRef Source = "\n\ + define i32 @test(i32 %0) {\n\ + %div = sdiv i32 0, %0\n\ + ret i32 %div\n\ + }"; + VerfyDivDidntShuffle(Source); +} +TEST(InstModificationIRStrategyTest, DidntShuffleFRem) { + StringRef Source = "\n\ + define <2 x double> @test(<2 x double> %0) {\n\ + %div = frem <2 x double> , %0\n\ + ret <2 x double> %div\n\ + }"; + VerfyDivDidntShuffle(Source); } +} // namespace