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 @@ -17,10 +17,12 @@ #include "llvm/FuzzMutate/Random.h" #include "llvm/FuzzMutate/RandomIRBuilder.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/FMF.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -217,6 +219,7 @@ switch (Inst.getOpcode()) { default: break; + // Add nsw, nuw flag case Instruction::Add: case Instruction::Mul: case Instruction::Sub: @@ -225,26 +228,60 @@ Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(false); }); Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(true); }); Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(false); }); - break; case Instruction::ICmp: CI = cast(&Inst); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_EQ); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_NE); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_UGT); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_UGE); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_ULT); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_ULE); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SGT); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SGE); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SLT); }); - Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SLE); }); + for (unsigned p = CmpInst::FIRST_ICMP_PREDICATE; + p <= CmpInst::LAST_ICMP_PREDICATE; p++) { + Modifications.push_back( + [CI, p]() { CI->setPredicate(static_cast(p)); }); + } break; + // Add inbound flag. case Instruction::GetElementPtr: GEP = cast(&Inst); Modifications.push_back([GEP]() { GEP->setIsInBounds(true); }); Modifications.push_back([GEP]() { GEP->setIsInBounds(false); }); break; + // Add exact flag. + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::LShr: + case Instruction::AShr: + Modifications.push_back([&Inst] { Inst.setIsExact(!Inst.isExact()); }); + break; + + case Instruction::FCmp: + CI = cast(&Inst); + for (unsigned p = CmpInst::FIRST_FCMP_PREDICATE; + p <= CmpInst::LAST_FCMP_PREDICATE; p++) { + Modifications.push_back( + [CI, p]() { CI->setPredicate(static_cast(p)); }); + } + break; + } + + // Add fast math flag if possible. + if (isa(&Inst)) { + // Try setting everything unless they are already on. + Modifications.push_back( + [&Inst] { Inst.setFast(!Inst.getFastMathFlags().all()); }); + // Try unsetting everything unless they are already off. + Modifications.push_back( + [&Inst] { Inst.setFast(!Inst.getFastMathFlags().none()); }); + // Individual setting by flipping the bit + Modifications.push_back( + [&Inst] { Inst.setHasAllowReassoc(!Inst.hasAllowReassoc()); }); + Modifications.push_back([&Inst] { Inst.setHasNoNaNs(!Inst.hasNoNaNs()); }); + Modifications.push_back([&Inst] { Inst.setHasNoInfs(!Inst.hasNoInfs()); }); + Modifications.push_back( + [&Inst] { Inst.setHasNoSignedZeros(!Inst.hasNoSignedZeros()); }); + Modifications.push_back( + [&Inst] { Inst.setHasAllowReciprocal(!Inst.hasAllowReciprocal()); }); + Modifications.push_back( + [&Inst] { Inst.setHasAllowContract(!Inst.hasAllowContract()); }); + Modifications.push_back( + [&Inst] { Inst.setHasApproxFunc(!Inst.hasApproxFunc()); }); } // Randomly switch operands of instructions 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 @@ -13,6 +13,7 @@ #include "llvm/FuzzMutate/IRMutator.h" #include "llvm/FuzzMutate/Operations.h" #include "llvm/FuzzMutate/RandomIRBuilder.h" +#include "llvm/IR/FMF.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -249,27 +250,27 @@ dyn_cast(F.getArg(ShuffleItems.second))); Mutator->mutateModule(*M, 0, Source.size(), Source.size() + 100); - EXPECT_TRUE(!verifyModule(*M, &errs())); + ASSERT_TRUE(!verifyModule(*M, &errs())); - EXPECT_TRUE(Inst->getOperand(ShuffleItems.first) == + ASSERT_TRUE(Inst->getOperand(ShuffleItems.first) == dyn_cast(F.getArg(ShuffleItems.second))); - EXPECT_TRUE(Inst->getOperand(ShuffleItems.second) == + ASSERT_TRUE(Inst->getOperand(ShuffleItems.second) == dyn_cast(F.getArg(ShuffleItems.first))); } -TEST(InstModificationIRStrategyTest, ShuffleFAdd) { +TEST(InstModificationIRStrategyTest, ShuffleAnd) { StringRef Source = "\n\ - define float @test(float %0, float %1) {\n\ - %add = fadd float %0, %1\n\ - ret float %add\n\ + define i32 @test(i32 %0, i32 %1) {\n\ + %add = and i32 %0, %1\n\ + ret i32 %add\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\ + define i32 @test(i1 %0, i32 %1, i32 %2) {\n\ + %select = select i1 %0, i32 %1, i32 %2\n\ + ret i32 %select\n\ }"; VerfyOperandShuffled(Source, {1, 2}); } @@ -310,6 +311,79 @@ }"; VerfyDivDidntShuffle(Source); } +TEST(InstModificationIRStrategy, Exact) { + LLVMContext Ctx; + StringRef Source = "\n\ + define i32 @test(i32 %a, i32 %b) {\n\ + %c = ashr i32 %a, %b \n\ + ret i32 %c\n\ + }"; + + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + + std::unique_ptr M = parseAssembly(Source.data(), Ctx); + std::mt19937 mt(Seed); + std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); + auto &F = *M->begin(); + BinaryOperator *AShr = cast(&*F.begin()->begin()); + bool FoundExact = false; + for (int i = 0; i < 100; ++i) { + Mutator->mutateModule(*M, RandInt(mt), Source.size(), Source.size() + 100); + ASSERT_FALSE(verifyModule(*M, &errs())); + FoundExact |= AShr->isExact(); + } + + EXPECT_TRUE(FoundExact); +} +TEST(InstModificationIRStrategy, FastMath) { + LLVMContext Ctx; + StringRef Source = "\n\ + declare [4 x <4 x double>] @vecdouble(double) \n\ + define double @test(i1 %C, double %a, double %b) { \n\ + Entry: \n\ + br i1 %C, label %True, label %False \n\ + True: \n\ + br label %Exit \n\ + False: \n\ + br label %Exit \n\ + Exit: \n\ + %PHIi32 = phi i32 [1, %True], [2, %False] \n\ + %PHIdouble = phi double [%a, %True], [%b, %False] \n\ + %Call = call [4 x <4 x double>] @vecdouble(double %PHIdouble) \n\ + %c = fneg double %PHIdouble \n\ + %s = select i1 %C, double %a, double %b \n\ + %d = fadd double %s, %c \n\ + ret double %d \n\ + }"; + + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + + std::unique_ptr M = parseAssembly(Source.data(), Ctx); + std::mt19937 mt(Seed); + std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); + DenseMap FPOpsHasFastMath; + for (auto &F : *M) { + for (auto &BB : F) { + for (auto &I : BB) { + Type *Ty = I.getType(); + if (Ty->isFPOrFPVectorTy() || Ty->isArrayTy()) { + FPOpsHasFastMath[&I] = false; + } + } + } + } + ASSERT_TRUE(M && !verifyModule(*M, &errs())); + for (int i = 0; i < 300; ++i) { + Mutator->mutateModule(*M, RandInt(mt), Source.size(), Source.size() + 100); + for (auto p : FPOpsHasFastMath) + FPOpsHasFastMath[p.first] |= p.first->getFastMathFlags().any(); + ASSERT_FALSE(verifyModule(*M, &errs())); + } + for (auto p : FPOpsHasFastMath) + ASSERT_TRUE(p.second); +} template static void mutateAndVerifyModule(StringRef Source, int repeat = 100) {