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" @@ -213,9 +215,36 @@ SmallVector, 8> Modifications; CmpInst *CI = nullptr; GetElementPtrInst *GEP = nullptr; + auto addFastMathFlagSetterIf = + [&Modifications, &Inst](std::function Condition) { + if (Condition(Inst)) { + // Set everything + Modifications.push_back([&Inst] { Inst.setFast(true); }); + Modifications.push_back([&Inst] { Inst.setFast(false); }); + // Individual setting + Modifications.push_back([&Inst] { Inst.setHasAllowReassoc(true); }); + Modifications.push_back([&Inst] { Inst.setHasAllowReassoc(false); }); + Modifications.push_back([&Inst] { Inst.setHasNoNaNs(true); }); + Modifications.push_back([&Inst] { Inst.setHasNoNaNs(false); }); + Modifications.push_back([&Inst] { Inst.setHasNoInfs(true); }); + Modifications.push_back([&Inst] { Inst.setHasNoInfs(false); }); + Modifications.push_back([&Inst] { Inst.setHasNoSignedZeros(true); }); + Modifications.push_back([&Inst] { Inst.setHasNoSignedZeros(false); }); + Modifications.push_back( + [&Inst] { Inst.setHasAllowReciprocal(true); }); + Modifications.push_back( + [&Inst] { Inst.setHasAllowReciprocal(false); }); + Modifications.push_back([&Inst] { Inst.setHasAllowContract(true); }); + Modifications.push_back([&Inst] { Inst.setHasAllowContract(false); }); + Modifications.push_back([&Inst] { Inst.setHasApproxFunc(true); }); + Modifications.push_back([&Inst] { Inst.setHasApproxFunc(false); }); + } + }; + switch (Inst.getOpcode()) { default: break; + // Add nsw, nuw flag case Instruction::Add: case Instruction::Mul: case Instruction::Sub: @@ -224,26 +253,63 @@ 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(true); }); + Modifications.push_back([&Inst] { Inst.setIsExact(false); }); + break; + + // Add fast math flag. + 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)); }); + } + // Explict don't break yet, fcmp can have fast math flags too. + LLVM_FALLTHROUGH; + case Instruction::FNeg: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + case Instruction::FRem: + addFastMathFlagSetterIf([](Instruction &) { return true; }); + break; + case Instruction::Select: + case Instruction::PHI: + case Instruction::Call: + addFastMathFlagSetterIf([](Instruction &I) { + Type *Ty = I.getType(); + // Use while to unwrap nested vector/array type until we get the scalar + while (Ty->isArrayTy() || Ty->isVectorTy()) { + if (ArrayType *ATy = dyn_cast(Ty)) + Ty = ATy->getElementType(); + if (VectorType *TTy = dyn_cast(Ty)) + Ty = TTy->getElementType(); + } + return Ty->isFloatingPointTy(); + }); + break; } // 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,77 @@ }"; 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()); + ASSERT_TRUE(M && !verifyModule(*M, &errs())); + 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); +} TEST(InsertPHIStrategy, PHI) { LLVMContext Ctx;