//===-- RandomIRBuilder.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #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" using namespace llvm; using namespace fuzzerop; Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB, ArrayRef Insts) { return findOrCreateSource(BB, Insts, {}, anyType()); } Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB, ArrayRef Insts, ArrayRef 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 Insts, ArrayRef Srcs, SourcePred Pred) { // Generate some constants to choose from. auto RS = makeSampler(Rand); RS.sample(Pred.generate(Srcs, KnownTypes)); // If we can find a pointer to load from, use it half the time. Value *Ptr = findPointer(BB, Insts, Srcs, Pred); if (Ptr) { // Create load from the chosen pointer auto IP = BB.getFirstInsertionPt(); if (auto *I = dyn_cast(Ptr)) { IP = ++I->getIterator(); assert(IP != BB.end() && "guaranteed by the findPointer"); } auto *NewLoad = new LoadInst( cast(Ptr->getType())->getElementType(), Ptr, "L", &*IP); // Only sample this load if it really matches the descriptor if (Pred.matches(Srcs, NewLoad)) RS.sample(NewLoad, RS.totalWeight()); else NewLoad->eraseFromParent(); } assert(!RS.isEmpty() && "Failed to generate sources"); return RS.getSelection(); } 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: case Instruction::ShuffleVector: if (Operand.getOperandNo() >= 2) return false; break; default: break; } return true; } void RandomIRBuilder::connectToSink(BasicBlock &BB, ArrayRef Insts, Value *V) { auto RS = makeSampler(Rand); for (auto &I : Insts) { if (isa(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 Insts, Value *V) { Value *Ptr = findPointer(BB, Insts, {V}, matchFirstType()); if (!Ptr) { if (uniform(Rand, 0, 1)) 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 Insts, ArrayRef Srcs, SourcePred Pred) { auto IsMatchingPtr = [&Srcs, &Pred](Instruction *Inst) { // Invoke instructions sometimes produce valid pointers but currently // we can't insert loads or stores from them if (Inst->isTerminator()) return false; if (auto PtrTy = dyn_cast(Inst->getType())) { // We can never generate loads from non first class or non sized types if (!PtrTy->getElementType()->isSized() || !PtrTy->getElementType()->isFirstClassType()) return false; // 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; }