Skip to content

Commit

Permalink
[FuzzMutate] Add mutator to modify instruction flags.
Browse files Browse the repository at this point in the history
This patch adds a new InstModificationIRStrategy to mutate flags/options
for instructions. For example, it may add or remove nuw/nsw flags from
add, mul, sub, shl instructions or change the predicate for icmp
instructions.

Subtle changes such as those mentioned above should lead to a more
interesting range of inputs. The presence or absence of overflow flags
can expose subtle bugs, for example.

Reviewed By: bogner

Differential Revision: https://reviews.llvm.org/D94905
  • Loading branch information
fhahn committed Jan 23, 2021
1 parent 3b9677e commit 166d40f
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 0 deletions.
11 changes: 11 additions & 0 deletions llvm/include/llvm/FuzzMutate/IRMutator.h
Expand Up @@ -102,6 +102,17 @@ class InstDeleterIRStrategy : public IRMutationStrategy {
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};

class InstModificationIRStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 4;
}

using IRMutationStrategy::mutate;
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};

} // end llvm namespace

#endif // LLVM_FUZZMUTATE_IRMUTATOR_H
43 changes: 43 additions & 0 deletions llvm/lib/FuzzMutate/IRMutator.cpp
Expand Up @@ -197,3 +197,46 @@ void InstDeleterIRStrategy::mutate(Instruction &Inst, RandomIRBuilder &IB) {
Inst.replaceAllUsesWith(RS.getSelection());
Inst.eraseFromParent();
}

void InstModificationIRStrategy::mutate(Instruction &Inst,
RandomIRBuilder &IB) {
SmallVector<std::function<void()>, 8> Modifications;
CmpInst *CI = nullptr;
GetElementPtrInst *GEP = nullptr;
switch (Inst.getOpcode()) {
default:
break;
case Instruction::Add:
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.setHasNoUnsignedWrap(true); });
Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(false); });

break;
case Instruction::ICmp:
CI = cast<ICmpInst>(&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); });
break;
case Instruction::GetElementPtr:
GEP = cast<GetElementPtrInst>(&Inst);
Modifications.push_back([GEP]() { GEP->setIsInBounds(true); });
Modifications.push_back([GEP]() { GEP->setIsInBounds(false); });
break;
}

auto RS = makeSampler(IB.Rand, Modifications);
if (RS)
RS.getSelection()();
}
1 change: 1 addition & 0 deletions llvm/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
Expand Up @@ -51,6 +51,7 @@ std::unique_ptr<IRMutator> createOptMutator() {
InjectorIRStrategy::getDefaultOps()));
Strategies.push_back(
std::make_unique<InstDeleterIRStrategy>());
Strategies.push_back(std::make_unique<InstModificationIRStrategy>());

return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));
}
Expand Down
105 changes: 105 additions & 0 deletions llvm/unittests/FuzzMutate/StrategiesTest.cpp
Expand Up @@ -49,6 +49,17 @@ std::unique_ptr<IRMutator> createDeleterMutator() {
return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));
}

std::unique_ptr<IRMutator> createInstModifierMutator() {
std::vector<TypeGetter> Types{
Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,
Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};

std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
Strategies.push_back(std::make_unique<InstModificationIRStrategy>());

return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));
}

std::unique_ptr<Module> parseAssembly(
const char *Assembly, LLVMContext &Context) {

Expand Down Expand Up @@ -135,4 +146,98 @@ TEST(InstDeleterIRStrategyTest, PhiNodes) {
IterateOnSource(Source, *Mutator);
}

static void checkModifyNoUnsignedAndNoSignedWrap(StringRef Opc) {
LLVMContext Ctx;
std::string Source = std::string("\n\
define i32 @test(i32 %x) {\n\
%a = ") + Opc.str() +
std::string(" i32 %x, 10\n\
ret i32 %a\n\
}");

auto Mutator = createInstModifierMutator();
ASSERT_TRUE(Mutator);

auto M = parseAssembly(Source.data(), Ctx);
auto &F = *M->begin();
auto *AddI = &*F.begin()->begin();
ASSERT_TRUE(M && !verifyModule(*M, &errs()));
bool FoundNUW = false;
bool FoundNSW = false;
for (int i = 0; i < 100; ++i) {
Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);
EXPECT_TRUE(!verifyModule(*M, &errs()));
FoundNUW |= AddI->hasNoUnsignedWrap();
FoundNSW |= AddI->hasNoSignedWrap();
}

// The mutator should have added nuw and nsw during some mutations.
EXPECT_TRUE(FoundNUW);
EXPECT_TRUE(FoundNSW);
}
TEST(InstModificationIRStrategyTest, Add) {
checkModifyNoUnsignedAndNoSignedWrap("add");
}

TEST(InstModificationIRStrategyTest, Sub) {
checkModifyNoUnsignedAndNoSignedWrap("sub");
}

TEST(InstModificationIRStrategyTest, Mul) {
checkModifyNoUnsignedAndNoSignedWrap("mul");
}

TEST(InstModificationIRStrategyTest, Shl) {
checkModifyNoUnsignedAndNoSignedWrap("shl");
}

TEST(InstModificationIRStrategyTest, ICmp) {
LLVMContext Ctx;
StringRef Source = "\n\
define i1 @test(i32 %x) {\n\
%a = icmp eq i32 %x, 10\n\
ret i1 %a\n\
}";

auto Mutator = createInstModifierMutator();
ASSERT_TRUE(Mutator);

auto M = parseAssembly(Source.data(), Ctx);
auto &F = *M->begin();
CmpInst *CI = cast<CmpInst>(&*F.begin()->begin());
ASSERT_TRUE(M && !verifyModule(*M, &errs()));
bool FoundNE = false;
for (int i = 0; i < 100; ++i) {
Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);
EXPECT_TRUE(!verifyModule(*M, &errs()));
FoundNE |= CI->getPredicate() == CmpInst::ICMP_NE;
}

EXPECT_TRUE(FoundNE);
}

TEST(InstModificationIRStrategyTest, GEP) {
LLVMContext Ctx;
StringRef Source = "\n\
define i32* @test(i32* %ptr) {\n\
%gep = getelementptr i32, i32* %ptr, i32 10\n\
ret i32* %gep\n\
}";

auto Mutator = createInstModifierMutator();
ASSERT_TRUE(Mutator);

auto M = parseAssembly(Source.data(), Ctx);
auto &F = *M->begin();
GetElementPtrInst *GEP = cast<GetElementPtrInst>(&*F.begin()->begin());
ASSERT_TRUE(M && !verifyModule(*M, &errs()));
bool FoundInbounds = false;
for (int i = 0; i < 100; ++i) {
Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);
EXPECT_TRUE(!verifyModule(*M, &errs()));
FoundInbounds |= GEP->isInBounds();
}

EXPECT_TRUE(FoundInbounds);
}
}

0 comments on commit 166d40f

Please sign in to comment.