Path: blob/main/contrib/llvm-project/llvm/lib/FuzzMutate/Operations.cpp
35233 views
//===-- Operations.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/FuzzMutate/Operations.h"9#include "llvm/IR/BasicBlock.h"10#include "llvm/IR/Constants.h"11#include "llvm/IR/Function.h"12#include "llvm/IR/Instructions.h"1314using namespace llvm;15using namespace fuzzerop;1617void llvm::describeFuzzerIntOps(std::vector<fuzzerop::OpDescriptor> &Ops) {18Ops.push_back(binOpDescriptor(1, Instruction::Add));19Ops.push_back(binOpDescriptor(1, Instruction::Sub));20Ops.push_back(binOpDescriptor(1, Instruction::Mul));21Ops.push_back(binOpDescriptor(1, Instruction::SDiv));22Ops.push_back(binOpDescriptor(1, Instruction::UDiv));23Ops.push_back(binOpDescriptor(1, Instruction::SRem));24Ops.push_back(binOpDescriptor(1, Instruction::URem));25Ops.push_back(binOpDescriptor(1, Instruction::Shl));26Ops.push_back(binOpDescriptor(1, Instruction::LShr));27Ops.push_back(binOpDescriptor(1, Instruction::AShr));28Ops.push_back(binOpDescriptor(1, Instruction::And));29Ops.push_back(binOpDescriptor(1, Instruction::Or));30Ops.push_back(binOpDescriptor(1, Instruction::Xor));3132Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_EQ));33Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_NE));34Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_UGT));35Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_UGE));36Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_ULT));37Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_ULE));38Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SGT));39Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SGE));40Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SLT));41Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SLE));42}4344void llvm::describeFuzzerFloatOps(std::vector<fuzzerop::OpDescriptor> &Ops) {45Ops.push_back(binOpDescriptor(1, Instruction::FAdd));46Ops.push_back(binOpDescriptor(1, Instruction::FSub));47Ops.push_back(binOpDescriptor(1, Instruction::FMul));48Ops.push_back(binOpDescriptor(1, Instruction::FDiv));49Ops.push_back(binOpDescriptor(1, Instruction::FRem));5051Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_FALSE));52Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OEQ));53Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OGT));54Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OGE));55Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OLT));56Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OLE));57Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ONE));58Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ORD));59Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UNO));60Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UEQ));61Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UGT));62Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UGE));63Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ULT));64Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ULE));65Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UNE));66Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_TRUE));67}6869void llvm::describeFuzzerUnaryOperations(70std::vector<fuzzerop::OpDescriptor> &Ops) {71Ops.push_back(fnegDescriptor(1));72}7374void llvm::describeFuzzerControlFlowOps(75std::vector<fuzzerop::OpDescriptor> &Ops) {76Ops.push_back(splitBlockDescriptor(1));77}7879void llvm::describeFuzzerOtherOps(std::vector<fuzzerop::OpDescriptor> &Ops) {80Ops.push_back(selectDescriptor(1));81}8283void llvm::describeFuzzerPointerOps(std::vector<fuzzerop::OpDescriptor> &Ops) {84Ops.push_back(gepDescriptor(1));85}8687void llvm::describeFuzzerAggregateOps(88std::vector<fuzzerop::OpDescriptor> &Ops) {89Ops.push_back(extractValueDescriptor(1));90Ops.push_back(insertValueDescriptor(1));91}9293void llvm::describeFuzzerVectorOps(std::vector<fuzzerop::OpDescriptor> &Ops) {94Ops.push_back(extractElementDescriptor(1));95Ops.push_back(insertElementDescriptor(1));96Ops.push_back(shuffleVectorDescriptor(1));97}9899OpDescriptor llvm::fuzzerop::selectDescriptor(unsigned Weight) {100auto buildOp = [](ArrayRef<Value *> Srcs, Instruction *Inst) {101return SelectInst::Create(Srcs[0], Srcs[1], Srcs[2], "S", Inst);102};103return {Weight,104{boolOrVecBoolType(), matchFirstLengthWAnyType(), matchSecondType()},105buildOp};106}107108OpDescriptor llvm::fuzzerop::fnegDescriptor(unsigned Weight) {109auto buildOp = [](ArrayRef<Value *> Srcs, Instruction *Inst) {110return UnaryOperator::Create(Instruction::FNeg, Srcs[0], "F", Inst);111};112return {Weight, {anyFloatOrVecFloatType()}, buildOp};113}114115OpDescriptor llvm::fuzzerop::binOpDescriptor(unsigned Weight,116Instruction::BinaryOps Op) {117auto buildOp = [Op](ArrayRef<Value *> Srcs, Instruction *Inst) {118return BinaryOperator::Create(Op, Srcs[0], Srcs[1], "B", Inst);119};120switch (Op) {121case Instruction::Add:122case Instruction::Sub:123case Instruction::Mul:124case Instruction::SDiv:125case Instruction::UDiv:126case Instruction::SRem:127case Instruction::URem:128case Instruction::Shl:129case Instruction::LShr:130case Instruction::AShr:131case Instruction::And:132case Instruction::Or:133case Instruction::Xor:134return {Weight, {anyIntOrVecIntType(), matchFirstType()}, buildOp};135case Instruction::FAdd:136case Instruction::FSub:137case Instruction::FMul:138case Instruction::FDiv:139case Instruction::FRem:140return {Weight, {anyFloatOrVecFloatType(), matchFirstType()}, buildOp};141case Instruction::BinaryOpsEnd:142llvm_unreachable("Value out of range of enum");143}144llvm_unreachable("Covered switch");145}146147OpDescriptor llvm::fuzzerop::cmpOpDescriptor(unsigned Weight,148Instruction::OtherOps CmpOp,149CmpInst::Predicate Pred) {150auto buildOp = [CmpOp, Pred](ArrayRef<Value *> Srcs, Instruction *Inst) {151return CmpInst::Create(CmpOp, Pred, Srcs[0], Srcs[1], "C", Inst);152};153154switch (CmpOp) {155case Instruction::ICmp:156return {Weight, {anyIntOrVecIntType(), matchFirstType()}, buildOp};157case Instruction::FCmp:158return {Weight, {anyFloatOrVecFloatType(), matchFirstType()}, buildOp};159default:160llvm_unreachable("CmpOp must be ICmp or FCmp");161}162}163164OpDescriptor llvm::fuzzerop::splitBlockDescriptor(unsigned Weight) {165auto buildSplitBlock = [](ArrayRef<Value *> Srcs, Instruction *Inst) {166BasicBlock *Block = Inst->getParent();167BasicBlock *Next = Block->splitBasicBlock(Inst, "BB");168169// If it was an exception handling block, we are done.170if (Block->isEHPad())171return nullptr;172173// Loop back on this block by replacing the unconditional forward branch174// with a conditional with a backedge.175if (Block != &Block->getParent()->getEntryBlock()) {176BranchInst::Create(Block, Next, Srcs[0], Block->getTerminator());177Block->getTerminator()->eraseFromParent();178179// We need values for each phi in the block. Since there isn't a good way180// to do a variable number of input values currently, we just fill them181// with undef.182for (PHINode &PHI : Block->phis())183PHI.addIncoming(UndefValue::get(PHI.getType()), Block);184}185return nullptr;186};187SourcePred isInt1Ty{[](ArrayRef<Value *>, const Value *V) {188return V->getType()->isIntegerTy(1);189},190std::nullopt};191return {Weight, {isInt1Ty}, buildSplitBlock};192}193194OpDescriptor llvm::fuzzerop::gepDescriptor(unsigned Weight) {195auto buildGEP = [](ArrayRef<Value *> Srcs, Instruction *Inst) {196// TODO: It would be better to generate a random type here, rather than197// generating a random value and picking its type.198Type *Ty = Srcs[1]->getType();199auto Indices = ArrayRef(Srcs).drop_front(2);200return GetElementPtrInst::Create(Ty, Srcs[0], Indices, "G", Inst);201};202// TODO: Handle aggregates and vectors203// TODO: Support multiple indices.204// TODO: Try to avoid meaningless accesses.205SourcePred sizedType(206[](ArrayRef<Value *>, const Value *V) { return V->getType()->isSized(); },207std::nullopt);208return {Weight, {sizedPtrType(), sizedType, anyIntType()}, buildGEP};209}210211static uint64_t getAggregateNumElements(Type *T) {212assert(T->isAggregateType() && "Not a struct or array");213if (isa<StructType>(T))214return T->getStructNumElements();215return T->getArrayNumElements();216}217218static SourcePred validExtractValueIndex() {219auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {220if (auto *CI = dyn_cast<ConstantInt>(V))221if (!CI->uge(getAggregateNumElements(Cur[0]->getType())))222return true;223return false;224};225auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {226std::vector<Constant *> Result;227auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());228uint64_t N = getAggregateNumElements(Cur[0]->getType());229// Create indices at the start, end, and middle, but avoid dups.230Result.push_back(ConstantInt::get(Int32Ty, 0));231if (N > 1)232Result.push_back(ConstantInt::get(Int32Ty, N - 1));233if (N > 2)234Result.push_back(ConstantInt::get(Int32Ty, N / 2));235return Result;236};237return {Pred, Make};238}239240OpDescriptor llvm::fuzzerop::extractValueDescriptor(unsigned Weight) {241auto buildExtract = [](ArrayRef<Value *> Srcs, Instruction *Inst) {242// TODO: It's pretty inefficient to shuffle this all through constants.243unsigned Idx = cast<ConstantInt>(Srcs[1])->getZExtValue();244return ExtractValueInst::Create(Srcs[0], {Idx}, "E", Inst);245};246// TODO: Should we handle multiple indices?247return {Weight, {anyAggregateType(), validExtractValueIndex()}, buildExtract};248}249250static SourcePred matchScalarInAggregate() {251auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {252if (auto *ArrayT = dyn_cast<ArrayType>(Cur[0]->getType()))253return V->getType() == ArrayT->getElementType();254255auto *STy = cast<StructType>(Cur[0]->getType());256for (int I = 0, E = STy->getNumElements(); I < E; ++I)257if (STy->getTypeAtIndex(I) == V->getType())258return true;259return false;260};261auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {262if (auto *ArrayT = dyn_cast<ArrayType>(Cur[0]->getType()))263return makeConstantsWithType(ArrayT->getElementType());264265std::vector<Constant *> Result;266auto *STy = cast<StructType>(Cur[0]->getType());267for (int I = 0, E = STy->getNumElements(); I < E; ++I)268makeConstantsWithType(STy->getTypeAtIndex(I), Result);269return Result;270};271return {Pred, Make};272}273274static SourcePred validInsertValueIndex() {275auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {276if (auto *CI = dyn_cast<ConstantInt>(V))277if (CI->getBitWidth() == 32) {278Type *Indexed = ExtractValueInst::getIndexedType(Cur[0]->getType(),279CI->getZExtValue());280return Indexed == Cur[1]->getType();281}282return false;283};284auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {285std::vector<Constant *> Result;286auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());287auto *BaseTy = Cur[0]->getType();288int I = 0;289while (Type *Indexed = ExtractValueInst::getIndexedType(BaseTy, I)) {290if (Indexed == Cur[1]->getType())291Result.push_back(ConstantInt::get(Int32Ty, I));292++I;293}294return Result;295};296return {Pred, Make};297}298299OpDescriptor llvm::fuzzerop::insertValueDescriptor(unsigned Weight) {300auto buildInsert = [](ArrayRef<Value *> Srcs, Instruction *Inst) {301// TODO: It's pretty inefficient to shuffle this all through constants.302unsigned Idx = cast<ConstantInt>(Srcs[2])->getZExtValue();303return InsertValueInst::Create(Srcs[0], Srcs[1], {Idx}, "I", Inst);304};305return {306Weight,307{anyAggregateType(), matchScalarInAggregate(), validInsertValueIndex()},308buildInsert};309}310311OpDescriptor llvm::fuzzerop::extractElementDescriptor(unsigned Weight) {312auto buildExtract = [](ArrayRef<Value *> Srcs, Instruction *Inst) {313return ExtractElementInst::Create(Srcs[0], Srcs[1], "E", Inst);314};315// TODO: Try to avoid undefined accesses.316return {Weight, {anyVectorType(), anyIntType()}, buildExtract};317}318319OpDescriptor llvm::fuzzerop::insertElementDescriptor(unsigned Weight) {320auto buildInsert = [](ArrayRef<Value *> Srcs, Instruction *Inst) {321return InsertElementInst::Create(Srcs[0], Srcs[1], Srcs[2], "I", Inst);322};323// TODO: Try to avoid undefined accesses.324return {Weight,325{anyVectorType(), matchScalarOfFirstType(), anyIntType()},326buildInsert};327}328329static SourcePred validShuffleVectorIndex() {330auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {331return ShuffleVectorInst::isValidOperands(Cur[0], Cur[1], V);332};333auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {334auto *FirstTy = cast<VectorType>(Cur[0]->getType());335auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());336// TODO: It's straighforward to make up reasonable values, but listing them337// exhaustively would be insane. Come up with a couple of sensible ones.338return std::vector<Constant *>{339UndefValue::get(VectorType::get(Int32Ty, FirstTy->getElementCount()))};340};341return {Pred, Make};342}343344OpDescriptor llvm::fuzzerop::shuffleVectorDescriptor(unsigned Weight) {345auto buildShuffle = [](ArrayRef<Value *> Srcs, Instruction *Inst) {346return new ShuffleVectorInst(Srcs[0], Srcs[1], Srcs[2], "S", Inst);347};348return {Weight,349{anyVectorType(), matchFirstType(), validShuffleVectorIndex()},350buildShuffle};351}352353354