1326938Sdim//===-- OpDescriptor.h ------------------------------------------*- C++ -*-===//
2326938Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6326938Sdim//
7326938Sdim//===----------------------------------------------------------------------===//
8326938Sdim//
9326938Sdim// Provides the fuzzerop::Descriptor class and related tools for describing
10326938Sdim// operations an IR fuzzer can work with.
11326938Sdim//
12326938Sdim//===----------------------------------------------------------------------===//
13326938Sdim
14326938Sdim#ifndef LLVM_FUZZMUTATE_OPDESCRIPTOR_H
15326938Sdim#define LLVM_FUZZMUTATE_OPDESCRIPTOR_H
16326938Sdim
17326938Sdim#include "llvm/ADT/ArrayRef.h"
18326938Sdim#include "llvm/ADT/STLExtras.h"
19326938Sdim#include "llvm/ADT/SmallVector.h"
20326938Sdim#include "llvm/IR/Constants.h"
21326938Sdim#include "llvm/IR/DerivedTypes.h"
22341825Sdim#include "llvm/IR/Instructions.h"
23326938Sdim#include "llvm/IR/Type.h"
24326938Sdim#include "llvm/IR/Value.h"
25326938Sdim#include <functional>
26326938Sdim
27326938Sdimnamespace llvm {
28326938Sdimnamespace fuzzerop {
29326938Sdim
30326938Sdim/// @{
31326938Sdim/// Populate a small list of potentially interesting constants of a given type.
32326938Sdimvoid makeConstantsWithType(Type *T, std::vector<Constant *> &Cs);
33326938Sdimstd::vector<Constant *> makeConstantsWithType(Type *T);
34326938Sdim/// @}
35326938Sdim
36326938Sdim/// A matcher/generator for finding suitable values for the next source in an
37326938Sdim/// operation's partially completed argument list.
38326938Sdim///
39326938Sdim/// Given that we're building some operation X and may have already filled some
40326938Sdim/// subset of its operands, this predicate determines if some value New is
41326938Sdim/// suitable for the next operand or generates a set of values that are
42326938Sdim/// suitable.
43326938Sdimclass SourcePred {
44326938Sdimpublic:
45326938Sdim  /// Given a list of already selected operands, returns whether a given new
46326938Sdim  /// operand is suitable for the next operand.
47326938Sdim  using PredT = std::function<bool(ArrayRef<Value *> Cur, const Value *New)>;
48326938Sdim  /// Given a list of already selected operands and a set of valid base types
49326938Sdim  /// for a fuzzer, generates a list of constants that could be used for the
50326938Sdim  /// next operand.
51326938Sdim  using MakeT = std::function<std::vector<Constant *>(
52326938Sdim      ArrayRef<Value *> Cur, ArrayRef<Type *> BaseTypes)>;
53326938Sdim
54326938Sdimprivate:
55326938Sdim  PredT Pred;
56326938Sdim  MakeT Make;
57326938Sdim
58326938Sdimpublic:
59326938Sdim  /// Create a fully general source predicate.
60326938Sdim  SourcePred(PredT Pred, MakeT Make) : Pred(Pred), Make(Make) {}
61326938Sdim  SourcePred(PredT Pred, NoneType) : Pred(Pred) {
62326938Sdim    Make = [Pred](ArrayRef<Value *> Cur, ArrayRef<Type *> BaseTypes) {
63326938Sdim      // Default filter just calls Pred on each of the base types.
64326938Sdim      std::vector<Constant *> Result;
65326938Sdim      for (Type *T : BaseTypes) {
66326938Sdim        Constant *V = UndefValue::get(T);
67326938Sdim        if (Pred(Cur, V))
68326938Sdim          makeConstantsWithType(T, Result);
69326938Sdim      }
70326938Sdim      if (Result.empty())
71326938Sdim        report_fatal_error("Predicate does not match for base types");
72326938Sdim      return Result;
73326938Sdim    };
74326938Sdim  }
75326938Sdim
76326938Sdim  /// Returns true if \c New is compatible for the argument after \c Cur
77326938Sdim  bool matches(ArrayRef<Value *> Cur, const Value *New) {
78326938Sdim    return Pred(Cur, New);
79326938Sdim  }
80326938Sdim
81326938Sdim  /// Generates a list of potential values for the argument after \c Cur.
82326938Sdim  std::vector<Constant *> generate(ArrayRef<Value *> Cur,
83326938Sdim                                   ArrayRef<Type *> BaseTypes) {
84326938Sdim    return Make(Cur, BaseTypes);
85326938Sdim  }
86326938Sdim};
87326938Sdim
88326938Sdim/// A description of some operation we can build while fuzzing IR.
89326938Sdimstruct OpDescriptor {
90326938Sdim  unsigned Weight;
91326938Sdim  SmallVector<SourcePred, 2> SourcePreds;
92326938Sdim  std::function<Value *(ArrayRef<Value *>, Instruction *)> BuilderFunc;
93326938Sdim};
94326938Sdim
95326938Sdimstatic inline SourcePred onlyType(Type *Only) {
96326938Sdim  auto Pred = [Only](ArrayRef<Value *>, const Value *V) {
97326938Sdim    return V->getType() == Only;
98326938Sdim  };
99326938Sdim  auto Make = [Only](ArrayRef<Value *>, ArrayRef<Type *>) {
100326938Sdim    return makeConstantsWithType(Only);
101326938Sdim  };
102326938Sdim  return {Pred, Make};
103326938Sdim}
104326938Sdim
105326938Sdimstatic inline SourcePred anyType() {
106326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
107326938Sdim    return !V->getType()->isVoidTy();
108326938Sdim  };
109326938Sdim  auto Make = None;
110326938Sdim  return {Pred, Make};
111326938Sdim}
112326938Sdim
113326938Sdimstatic inline SourcePred anyIntType() {
114326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
115326938Sdim    return V->getType()->isIntegerTy();
116326938Sdim  };
117326938Sdim  auto Make = None;
118326938Sdim  return {Pred, Make};
119326938Sdim}
120326938Sdim
121326938Sdimstatic inline SourcePred anyFloatType() {
122326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
123326938Sdim    return V->getType()->isFloatingPointTy();
124326938Sdim  };
125326938Sdim  auto Make = None;
126326938Sdim  return {Pred, Make};
127326938Sdim}
128326938Sdim
129326938Sdimstatic inline SourcePred anyPtrType() {
130326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
131341825Sdim    return V->getType()->isPointerTy() && !V->isSwiftError();
132326938Sdim  };
133326938Sdim  auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
134326938Sdim    std::vector<Constant *> Result;
135326938Sdim    // TODO: Should these point at something?
136326938Sdim    for (Type *T : Ts)
137326938Sdim      Result.push_back(UndefValue::get(PointerType::getUnqual(T)));
138326938Sdim    return Result;
139326938Sdim  };
140326938Sdim  return {Pred, Make};
141326938Sdim}
142326938Sdim
143326938Sdimstatic inline SourcePred sizedPtrType() {
144326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
145341825Sdim    if (V->isSwiftError())
146341825Sdim      return false;
147341825Sdim
148326938Sdim    if (const auto *PtrT = dyn_cast<PointerType>(V->getType()))
149326938Sdim      return PtrT->getElementType()->isSized();
150326938Sdim    return false;
151326938Sdim  };
152326938Sdim  auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
153326938Sdim    std::vector<Constant *> Result;
154326938Sdim
155326938Sdim    for (Type *T : Ts)
156326938Sdim      if (T->isSized())
157326938Sdim        Result.push_back(UndefValue::get(PointerType::getUnqual(T)));
158326938Sdim
159326938Sdim    return Result;
160326938Sdim  };
161326938Sdim  return {Pred, Make};
162326938Sdim}
163326938Sdim
164326938Sdimstatic inline SourcePred anyAggregateType() {
165326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
166326938Sdim    // We can't index zero sized arrays.
167326938Sdim    if (isa<ArrayType>(V->getType()))
168326938Sdim      return V->getType()->getArrayNumElements() > 0;
169326938Sdim
170326938Sdim    // Structs can also be zero sized. I.e opaque types.
171326938Sdim    if (isa<StructType>(V->getType()))
172326938Sdim      return V->getType()->getStructNumElements() > 0;
173326938Sdim
174326938Sdim    return V->getType()->isAggregateType();
175326938Sdim  };
176326938Sdim  // TODO: For now we only find aggregates in BaseTypes. It might be better to
177326938Sdim  // manufacture them out of the base types in some cases.
178326938Sdim  auto Find = None;
179326938Sdim  return {Pred, Find};
180326938Sdim}
181326938Sdim
182326938Sdimstatic inline SourcePred anyVectorType() {
183326938Sdim  auto Pred = [](ArrayRef<Value *>, const Value *V) {
184326938Sdim    return V->getType()->isVectorTy();
185326938Sdim  };
186326938Sdim  // TODO: For now we only find vectors in BaseTypes. It might be better to
187326938Sdim  // manufacture vectors out of the base types, but it's tricky to be sure
188326938Sdim  // that's actually a reasonable type.
189326938Sdim  auto Make = None;
190326938Sdim  return {Pred, Make};
191326938Sdim}
192326938Sdim
193326938Sdim/// Match values that have the same type as the first source.
194326938Sdimstatic inline SourcePred matchFirstType() {
195326938Sdim  auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
196326938Sdim    assert(!Cur.empty() && "No first source yet");
197326938Sdim    return V->getType() == Cur[0]->getType();
198326938Sdim  };
199326938Sdim  auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
200326938Sdim    assert(!Cur.empty() && "No first source yet");
201326938Sdim    return makeConstantsWithType(Cur[0]->getType());
202326938Sdim  };
203326938Sdim  return {Pred, Make};
204326938Sdim}
205326938Sdim
206326938Sdim/// Match values that have the first source's scalar type.
207326938Sdimstatic inline SourcePred matchScalarOfFirstType() {
208326938Sdim  auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
209326938Sdim    assert(!Cur.empty() && "No first source yet");
210326938Sdim    return V->getType() == Cur[0]->getType()->getScalarType();
211326938Sdim  };
212326938Sdim  auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
213326938Sdim    assert(!Cur.empty() && "No first source yet");
214326938Sdim    return makeConstantsWithType(Cur[0]->getType()->getScalarType());
215326938Sdim  };
216326938Sdim  return {Pred, Make};
217326938Sdim}
218326938Sdim
219326938Sdim} // end fuzzerop namespace
220326938Sdim} // end llvm namespace
221326938Sdim
222326938Sdim#endif // LLVM_FUZZMUTATE_OPDESCRIPTOR_H
223