//===-- OpDescriptor.h ------------------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // Provides the fuzzerop::Descriptor class and related tools for describing // operations an IR fuzzer can work with. // //===----------------------------------------------------------------------===// #ifndef LLVM_FUZZMUTATE_OPDESCRIPTOR_H #define LLVM_FUZZMUTATE_OPDESCRIPTOR_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include namespace llvm { namespace fuzzerop { /// @{ /// Populate a small list of potentially interesting constants of a given type. void makeConstantsWithType(Type *T, std::vector &Cs); std::vector makeConstantsWithType(Type *T); /// @} /// A matcher/generator for finding suitable values for the next source in an /// operation's partially completed argument list. /// /// Given that we're building some operation X and may have already filled some /// subset of its operands, this predicate determines if some value New is /// suitable for the next operand or generates a set of values that are /// suitable. class SourcePred { public: /// Given a list of already selected operands, returns whether a given new /// operand is suitable for the next operand. using PredT = std::function Cur, const Value *New)>; /// Given a list of already selected operands and a set of valid base types /// for a fuzzer, generates a list of constants that could be used for the /// next operand. using MakeT = std::function( ArrayRef Cur, ArrayRef BaseTypes)>; private: PredT Pred; MakeT Make; public: /// Create a fully general source predicate. SourcePred(PredT Pred, MakeT Make) : Pred(Pred), Make(Make) {} SourcePred(PredT Pred, NoneType) : Pred(Pred) { Make = [Pred](ArrayRef Cur, ArrayRef BaseTypes) { // Default filter just calls Pred on each of the base types. std::vector Result; for (Type *T : BaseTypes) { Constant *V = UndefValue::get(T); if (Pred(Cur, V)) makeConstantsWithType(T, Result); } if (Result.empty()) report_fatal_error("Predicate does not match for base types"); return Result; }; } /// Returns true if \c New is compatible for the argument after \c Cur bool matches(ArrayRef Cur, const Value *New) { return Pred(Cur, New); } /// Generates a list of potential values for the argument after \c Cur. std::vector generate(ArrayRef Cur, ArrayRef BaseTypes) { return Make(Cur, BaseTypes); } }; /// A description of some operation we can build while fuzzing IR. struct OpDescriptor { unsigned Weight; SmallVector SourcePreds; std::function, Instruction *)> BuilderFunc; }; static inline SourcePred onlyType(Type *Only) { auto Pred = [Only](ArrayRef, const Value *V) { return V->getType() == Only; }; auto Make = [Only](ArrayRef, ArrayRef) { return makeConstantsWithType(Only); }; return {Pred, Make}; } static inline SourcePred anyType() { auto Pred = [](ArrayRef, const Value *V) { return !V->getType()->isVoidTy(); }; auto Make = None; return {Pred, Make}; } static inline SourcePred anyIntType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isIntegerTy(); }; auto Make = None; return {Pred, Make}; } static inline SourcePred anyFloatType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isFloatingPointTy(); }; auto Make = None; return {Pred, Make}; } static inline SourcePred anyPtrType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isPointerTy() && !V->isSwiftError(); }; auto Make = [](ArrayRef, ArrayRef Ts) { std::vector Result; // TODO: Should these point at something? for (Type *T : Ts) Result.push_back(UndefValue::get(PointerType::getUnqual(T))); return Result; }; return {Pred, Make}; } static inline SourcePred sizedPtrType() { auto Pred = [](ArrayRef, const Value *V) { if (V->isSwiftError()) return false; if (const auto *PtrT = dyn_cast(V->getType())) return PtrT->getElementType()->isSized(); return false; }; auto Make = [](ArrayRef, ArrayRef Ts) { std::vector Result; for (Type *T : Ts) if (T->isSized()) Result.push_back(UndefValue::get(PointerType::getUnqual(T))); return Result; }; return {Pred, Make}; } static inline SourcePred anyAggregateType() { auto Pred = [](ArrayRef, const Value *V) { // We can't index zero sized arrays. if (isa(V->getType())) return V->getType()->getArrayNumElements() > 0; // Structs can also be zero sized. I.e opaque types. if (isa(V->getType())) return V->getType()->getStructNumElements() > 0; return V->getType()->isAggregateType(); }; // TODO: For now we only find aggregates in BaseTypes. It might be better to // manufacture them out of the base types in some cases. auto Find = None; return {Pred, Find}; } static inline SourcePred anyVectorType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isVectorTy(); }; // TODO: For now we only find vectors in BaseTypes. It might be better to // manufacture vectors out of the base types, but it's tricky to be sure // that's actually a reasonable type. auto Make = None; return {Pred, Make}; } /// Match values that have the same type as the first source. static inline SourcePred matchFirstType() { auto Pred = [](ArrayRef Cur, const Value *V) { assert(!Cur.empty() && "No first source yet"); return V->getType() == Cur[0]->getType(); }; auto Make = [](ArrayRef Cur, ArrayRef) { assert(!Cur.empty() && "No first source yet"); return makeConstantsWithType(Cur[0]->getType()); }; return {Pred, Make}; } /// Match values that have the first source's scalar type. static inline SourcePred matchScalarOfFirstType() { auto Pred = [](ArrayRef Cur, const Value *V) { assert(!Cur.empty() && "No first source yet"); return V->getType() == Cur[0]->getType()->getScalarType(); }; auto Make = [](ArrayRef Cur, ArrayRef) { assert(!Cur.empty() && "No first source yet"); return makeConstantsWithType(Cur[0]->getType()->getScalarType()); }; return {Pred, Make}; } } // end fuzzerop namespace } // end llvm namespace #endif // LLVM_FUZZMUTATE_OPDESCRIPTOR_H