123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===-- 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/SmallVector.h"
- #include "llvm/IR/Constants.h"
- #include "llvm/IR/DerivedTypes.h"
- #include "llvm/IR/Type.h"
- #include "llvm/IR/Value.h"
- #include <functional>
- namespace llvm {
- class Instruction;
- namespace fuzzerop {
- /// @{
- /// Populate a small list of potentially interesting constants of a given type.
- void makeConstantsWithType(Type *T, std::vector<Constant *> &Cs);
- std::vector<Constant *> 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<bool(ArrayRef<Value *> 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<std::vector<Constant *>(
- ArrayRef<Value *> Cur, ArrayRef<Type *> 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, std::nullopt_t) : Pred(Pred) {
- Make = [Pred](ArrayRef<Value *> Cur, ArrayRef<Type *> BaseTypes) {
- // Default filter just calls Pred on each of the base types.
- std::vector<Constant *> 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<Value *> Cur, const Value *New) {
- return Pred(Cur, New);
- }
- /// Generates a list of potential values for the argument after \c Cur.
- std::vector<Constant *> generate(ArrayRef<Value *> Cur,
- ArrayRef<Type *> BaseTypes) {
- return Make(Cur, BaseTypes);
- }
- };
- /// A description of some operation we can build while fuzzing IR.
- struct OpDescriptor {
- unsigned Weight;
- SmallVector<SourcePred, 2> SourcePreds;
- std::function<Value *(ArrayRef<Value *>, Instruction *)> BuilderFunc;
- };
- static inline SourcePred onlyType(Type *Only) {
- auto Pred = [Only](ArrayRef<Value *>, const Value *V) {
- return V->getType() == Only;
- };
- auto Make = [Only](ArrayRef<Value *>, ArrayRef<Type *>) {
- return makeConstantsWithType(Only);
- };
- return {Pred, Make};
- }
- static inline SourcePred anyType() {
- auto Pred = [](ArrayRef<Value *>, const Value *V) {
- return !V->getType()->isVoidTy();
- };
- auto Make = std::nullopt;
- return {Pred, Make};
- }
- static inline SourcePred anyIntType() {
- auto Pred = [](ArrayRef<Value *>, const Value *V) {
- return V->getType()->isIntegerTy();
- };
- auto Make = std::nullopt;
- return {Pred, Make};
- }
- static inline SourcePred anyFloatType() {
- auto Pred = [](ArrayRef<Value *>, const Value *V) {
- return V->getType()->isFloatingPointTy();
- };
- auto Make = std::nullopt;
- return {Pred, Make};
- }
- static inline SourcePred anyPtrType() {
- auto Pred = [](ArrayRef<Value *>, const Value *V) {
- return V->getType()->isPointerTy() && !V->isSwiftError();
- };
- auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
- std::vector<Constant *> 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<Value *>, const Value *V) {
- if (V->isSwiftError())
- return false;
- if (const auto *PtrT = dyn_cast<PointerType>(V->getType()))
- return PtrT->isOpaque() ||
- PtrT->getNonOpaquePointerElementType()->isSized();
- return false;
- };
- auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
- std::vector<Constant *> 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<Value *>, const Value *V) {
- // We can't index zero sized arrays.
- if (isa<ArrayType>(V->getType()))
- return V->getType()->getArrayNumElements() > 0;
- // Structs can also be zero sized. I.e opaque types.
- if (isa<StructType>(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 = std::nullopt;
- return {Pred, Find};
- }
- static inline SourcePred anyVectorType() {
- auto Pred = [](ArrayRef<Value *>, 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 = std::nullopt;
- return {Pred, Make};
- }
- /// Match values that have the same type as the first source.
- static inline SourcePred matchFirstType() {
- auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
- assert(!Cur.empty() && "No first source yet");
- return V->getType() == Cur[0]->getType();
- };
- auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
- 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<Value *> Cur, const Value *V) {
- assert(!Cur.empty() && "No first source yet");
- return V->getType() == Cur[0]->getType()->getScalarType();
- };
- auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
- assert(!Cur.empty() && "No first source yet");
- return makeConstantsWithType(Cur[0]->getType()->getScalarType());
- };
- return {Pred, Make};
- }
- } // namespace fuzzerop
- } // namespace llvm
- #endif // LLVM_FUZZMUTATE_OPDESCRIPTOR_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|