diff options
author | Justin Bogner <mail@justinbogner.com> | 2017-08-21 17:44:36 +0000 |
---|---|---|
committer | Justin Bogner <mail@justinbogner.com> | 2017-08-21 17:44:36 +0000 |
commit | 02336370858e69cee50019f6bcea89de041c7df3 (patch) | |
tree | f78de55d532e82bf4fe7c8d351e4029d0ee91904 /llvm/lib/FuzzMutate/RandomIRBuilder.cpp | |
parent | d5ac1812b7fa2f748b900de09b9160cf8a5c5a12 (diff) | |
download | bcm5719-llvm-02336370858e69cee50019f6bcea89de041c7df3.tar.gz bcm5719-llvm-02336370858e69cee50019f6bcea89de041c7df3.zip |
Introduce FuzzMutate library
This introduces the FuzzMutate library, which provides structured
fuzzing for LLVM IR, as described in my [EuroLLVM 2017 talk][1]. Most
of the basic mutators to inject and delete IR are provided, with
support for most basic operations.
I will follow up with the instruction selection fuzzer, which is
implemented in terms of this library.
[1]: http://llvm.org/devmtg/2017-03//2017/02/20/accepted-sessions.html#2
llvm-svn: 311356
Diffstat (limited to 'llvm/lib/FuzzMutate/RandomIRBuilder.cpp')
-rw-r--r-- | llvm/lib/FuzzMutate/RandomIRBuilder.cpp | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp new file mode 100644 index 00000000000..fd93c4005d3 --- /dev/null +++ b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp @@ -0,0 +1,140 @@ +//===-- RandomIRBuilder.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/RandomIRBuilder.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/FuzzMutate/Random.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" + +using namespace llvm; +using namespace fuzzerop; + +Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB, + ArrayRef<Instruction *> Insts) { + return findOrCreateSource(BB, Insts, {}, anyType()); +} + +Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB, + ArrayRef<Instruction *> Insts, + ArrayRef<Value *> Srcs, + SourcePred Pred) { + auto MatchesPred = [&Srcs, &Pred](Instruction *Inst) { + return Pred.matches(Srcs, Inst); + }; + auto RS = makeSampler(Rand, make_filter_range(Insts, MatchesPred)); + // Also consider choosing no source, meaning we want a new one. + RS.sample(nullptr, /*Weight=*/1); + if (Instruction *Src = RS.getSelection()) + return Src; + return newSource(BB, Insts, Srcs, Pred); +} + +Value *RandomIRBuilder::newSource(BasicBlock &BB, ArrayRef<Instruction *> Insts, + ArrayRef<Value *> Srcs, SourcePred Pred) { + // Generate some constants to choose from. + auto RS = makeSampler<Value *>(Rand); + RS.sample(Pred.generate(Srcs, KnownTypes)); + assert(!RS.isEmpty() && "Failed to generate sources"); + + // If we can find a pointer to load from, use it half the time. + Value *Ptr = findPointer(BB, Insts, Srcs, Pred); + if (Ptr) + RS.sample(Ptr, RS.totalWeight()); + + Value *Result = RS.getSelection(); + if (Result != Ptr) + return Result; + + // If we choose the pointer, we need to create a load. + auto IP = BB.getFirstInsertionPt(); + if (auto *I = dyn_cast<Instruction>(Ptr)) + IP = ++I->getIterator(); + return new LoadInst(Ptr, "L", &*IP); +} + +static bool isCompatibleReplacement(const Instruction *I, const Use &Operand, + const Value *Replacement) { + if (Operand->getType() != Replacement->getType()) + return false; + switch (I->getOpcode()) { + case Instruction::GetElementPtr: + case Instruction::ExtractElement: + case Instruction::ExtractValue: + // TODO: We could potentially validate these, but for now just leave indices + // alone. + if (Operand.getOperandNo() > 1) + return false; + break; + case Instruction::InsertValue: + case Instruction::InsertElement: + if (Operand.getOperandNo() > 2) + return false; + break; + default: + break; + } + return true; +} + +void RandomIRBuilder::connectToSink(BasicBlock &BB, + ArrayRef<Instruction *> Insts, Value *V) { + auto RS = makeSampler<Use *>(Rand); + for (auto &I : Insts) { + if (isa<IntrinsicInst>(I)) + // TODO: Replacing operands of intrinsics would be interesting, but + // there's no easy way to verify that a given replacement is valid given + // that intrinsics can impose arbitrary constraints. + continue; + for (Use &U : I->operands()) + if (isCompatibleReplacement(I, U, V)) + RS.sample(&U, 1); + } + // Also consider choosing no sink, meaning we want a new one. + RS.sample(nullptr, /*Weight=*/1); + + if (Use *Sink = RS.getSelection()) { + User *U = Sink->getUser(); + unsigned OpNo = Sink->getOperandNo(); + U->setOperand(OpNo, V); + return; + } + newSink(BB, Insts, V); +} + +void RandomIRBuilder::newSink(BasicBlock &BB, ArrayRef<Instruction *> Insts, + Value *V) { + Value *Ptr = findPointer(BB, Insts, {V}, matchFirstType()); + if (!Ptr) { + if (uniform<bool>(Rand)) + Ptr = new AllocaInst(V->getType(), 0, "A", &*BB.getFirstInsertionPt()); + else + Ptr = UndefValue::get(PointerType::get(V->getType(), 0)); + } + + new StoreInst(V, Ptr, Insts.back()); +} + +Value *RandomIRBuilder::findPointer(BasicBlock &BB, + ArrayRef<Instruction *> Insts, + ArrayRef<Value *> Srcs, SourcePred Pred) { + auto IsMatchingPtr = [&Srcs, &Pred](Instruction *Inst) { + if (auto PtrTy = dyn_cast<PointerType>(Inst->getType())) + // TODO: Check if this is horribly expensive. + return Pred.matches(Srcs, UndefValue::get(PtrTy->getElementType())); + return false; + }; + if (auto RS = makeSampler(Rand, make_filter_range(Insts, IsMatchingPtr))) + return RS.getSelection(); + return nullptr; +} |