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 @@ -118,6 +118,20 @@ void mutate(Instruction &Inst, RandomIRBuilder &IB) override; }; +/// Strategy that generates new function calls and inserts function signatures +/// to the modules. If any signatures are present in the module it will be +/// called. +class InsertFunctionStrategy : public IRMutationStrategy { +public: + uint64_t getWeight(size_t CurrentSize, size_t MaxSize, + uint64_t CurrentWeight) override { + return 10; + } + + using IRMutationStrategy::mutate; + void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; +}; + /// Strategy to split a random block and insert a random CFG in between. class InsertCFGStrategy : public IRMutationStrategy { private: diff --git a/llvm/include/llvm/FuzzMutate/RandomIRBuilder.h b/llvm/include/llvm/FuzzMutate/RandomIRBuilder.h --- a/llvm/include/llvm/FuzzMutate/RandomIRBuilder.h +++ b/llvm/include/llvm/FuzzMutate/RandomIRBuilder.h @@ -25,6 +25,8 @@ class Instruction; class LLVMContext; class Type; +class Module; +class Function; class Value; class Module; @@ -38,6 +40,10 @@ RandomEngine Rand; SmallVector KnownTypes; + uint64_t MinArgNum = 0; + uint64_t MaxArgNum = 5; + uint64_t MinFunctionNum = 1; + RandomIRBuilder(int Seed, ArrayRef AllowedTypes) : Rand(Seed), KnownTypes(AllowedTypes.begin(), AllowedTypes.end()) {} @@ -98,6 +104,10 @@ fuzzerop::SourcePred Pred); /// Return a uniformly choosen type from \c AllowedTypes Type *randomType(); + Function *createFunctionDeclaration(Module &M, uint64_t ArgNum); + Function *createFunctionDeclaration(Module &M); + Function *createFunctionDefinition(Module &M, uint64_t ArgNum); + Function *createFunctionDefinition(Module &M); }; } // namespace llvm 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 @@ -31,26 +31,17 @@ using namespace llvm; -static void createEmptyFunction(Module &M) { - // TODO: Some arguments and a return value would probably be more interesting. - LLVMContext &Context = M.getContext(); - Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Context), {}, - /*isVarArg=*/false), - GlobalValue::ExternalLinkage, "f", &M); - BasicBlock *BB = BasicBlock::Create(Context, "BB", F); - ReturnInst::Create(Context, BB); -} - void IRMutationStrategy::mutate(Module &M, RandomIRBuilder &IB) { auto RS = makeSampler(IB.Rand); for (Function &F : M) if (!F.isDeclaration()) RS.sample(&F, /*Weight=*/1); - if (RS.isEmpty()) - createEmptyFunction(M); - else - mutate(*RS.getSelection(), IB); + while (RS.totalWeight() < IB.MinFunctionNum) { + Function *F = IB.createFunctionDefinition(M); + RS.sample(F, /*Weight=*/1); + } + mutate(*RS.getSelection(), IB); } void IRMutationStrategy::mutate(Function &F, RandomIRBuilder &IB) { @@ -349,6 +340,60 @@ return tmp; } +void InsertFunctionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) { + Module *M = BB.getParent()->getParent(); + // If nullptr is selected, we will create a new function declaration. + SmallVector Functions({nullptr}); + for (Function &F : M->functions()) { + Functions.push_back(&F); + } + + auto RS = makeSampler(IB.Rand, Functions); + Function *F = RS.getSelection(); + if (!F) + F = IB.createFunctionDeclaration(*M); + + FunctionType *FTy = F->getFunctionType(); + SmallVector SourcePreds; + if (F->arg_empty()) { + for (Type *ArgTy : FTy->params()) { + SourcePreds.push_back(fuzzerop::onlyType(ArgTy)); + } + } + bool isRetVoid = (F->getReturnType() == Type::getVoidTy(M->getContext())); + auto BuilderFunc = [FTy, F, isRetVoid](ArrayRef Srcs, + Instruction *Inst) { + StringRef Name = isRetVoid ? nullptr : "C"; + CallInst *Call = CallInst::Create(FTy, F, Srcs, Name, Inst); + // Don't return this call inst if it return void as it can't be sinked. + return isRetVoid ? nullptr : Call; + }; + + SmallVector Insts; + for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I) + Insts.push_back(&*I); + if (Insts.size() < 1) + return; + + // Choose an insertion point for our new call instruction. + uint64_t IP = uniform(IB.Rand, 0, Insts.size() - 1); + + auto InstsBefore = ArrayRef(Insts).slice(0, IP); + auto InstsAfter = ArrayRef(Insts).slice(IP); + + // Choose a source, which will be used to constrain the operation selection. + SmallVector Srcs; + + for (const auto &Pred : ArrayRef(SourcePreds)) { + Srcs.push_back(IB.findOrCreateSource(BB, InstsBefore, Srcs, Pred)); + } + + if (Value *Op = BuilderFunc(Srcs, Insts[IP])) { + // Find a sink and wire up the results of the operation. + IB.connectToSink(BB, InstsAfter, Op); + } +} + void InsertCFGStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) { SmallVector Insts; for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I) diff --git a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp --- a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp +++ b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp @@ -393,3 +393,46 @@ uint64_t TyIdx = uniform(Rand, 0, KnownTypes.size() - 1); return KnownTypes[TyIdx]; } + +Function *RandomIRBuilder::createFunctionDeclaration(Module &M, + uint64_t ArgNum) { + Type *RetType = randomType(); + + SmallVector Args; + for (uint64_t i = 0; i < ArgNum; i++) { + Args.push_back(randomType()); + } + + Function *F = Function::Create(FunctionType::get(RetType, Args, + /*isVarArg=*/false), + GlobalValue::ExternalLinkage, "f", &M); + return F; +} +Function *RandomIRBuilder::createFunctionDeclaration(Module &M) { + return createFunctionDeclaration( + M, uniform(Rand, MinArgNum, MaxArgNum)); +} + +Function *RandomIRBuilder::createFunctionDefinition(Module &M, + uint64_t ArgNum) { + Function *F = this->createFunctionDeclaration(M, ArgNum); + + // TODO: Some arguments and a return value would probably be more + // interesting. + LLVMContext &Context = M.getContext(); + BasicBlock *BB = BasicBlock::Create(Context, "BB", F); + Type *RetTy = F->getReturnType(); + if (RetTy != Type::getVoidTy(Context)) { + Instruction *RetAlloca = new AllocaInst(RetTy, 0, "RP", BB); + Instruction *RetLoad = new LoadInst(RetTy, RetAlloca, "", BB); + ReturnInst::Create(Context, RetLoad, BB); + } else { + ReturnInst::Create(Context, BB); + } + + return F; +} +Function *RandomIRBuilder::createFunctionDefinition(Module &M) { + return createFunctionDefinition( + M, uniform(Rand, MinArgNum, MaxArgNum)); +} 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 @@ -348,6 +348,21 @@ }"; VerfyDivDidntShuffle(Source); } + +TEST(FunctionIRStrategy, Func) { + LLVMContext Ctx; + const char *Source = ""; + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + + auto M = parseAssembly(Source, Ctx); + srand(Seed); + for (int i = 0; i < 100; i++) { + Mutator->mutateModule(*M, rand(), 0, 1024); + EXPECT_TRUE(!verifyModule(*M, &errs())); + } +} + TEST(InstModificationIRStrategy, Exact) { LLVMContext Ctx; StringRef Source = "\n\