1//===-- OpDescriptor.h ------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Provides the fuzzerop::Descriptor class and related tools for describing
10// operations an IR fuzzer can work with.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_FUZZMUTATE_OPDESCRIPTOR_H
15#define LLVM_FUZZMUTATE_OPDESCRIPTOR_H
16
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/IR/Constants.h"
20#include "llvm/IR/DerivedTypes.h"
21#include "llvm/IR/Type.h"
22#include "llvm/IR/Value.h"
23#include <functional>
24
25namespace llvm {
26class Instruction;
27namespace fuzzerop {
28
29/// @{
30/// Populate a small list of potentially interesting constants of a given type.
31void makeConstantsWithType(Type *T, std::vector<Constant *> &Cs);
32std::vector<Constant *> makeConstantsWithType(Type *T);
33/// @}
34
35/// A matcher/generator for finding suitable values for the next source in an
36/// operation's partially completed argument list.
37///
38/// Given that we're building some operation X and may have already filled some
39/// subset of its operands, this predicate determines if some value New is
40/// suitable for the next operand or generates a set of values that are
41/// suitable.
42class SourcePred {
43public:
44  /// Given a list of already selected operands, returns whether a given new
45  /// operand is suitable for the next operand.
46  using PredT = std::function<bool(ArrayRef<Value *> Cur, const Value *New)>;
47  /// Given a list of already selected operands and a set of valid base types
48  /// for a fuzzer, generates a list of constants that could be used for the
49  /// next operand.
50  using MakeT = std::function<std::vector<Constant *>(
51      ArrayRef<Value *> Cur, ArrayRef<Type *> BaseTypes)>;
52
53private:
54  PredT Pred;
55  MakeT Make;
56
57public:
58  /// Create a fully general source predicate.
59  SourcePred(PredT Pred, MakeT Make) : Pred(Pred), Make(Make) {}
60  SourcePred(PredT Pred, std::nullopt_t) : Pred(Pred) {
61    Make = [Pred](ArrayRef<Value *> Cur, ArrayRef<Type *> BaseTypes) {
62      // Default filter just calls Pred on each of the base types.
63      std::vector<Constant *> Result;
64      for (Type *T : BaseTypes) {
65        Constant *V = UndefValue::get(T);
66        if (Pred(Cur, V))
67          makeConstantsWithType(T, Result);
68      }
69      if (Result.empty())
70        report_fatal_error("Predicate does not match for base types");
71      return Result;
72    };
73  }
74
75  /// Returns true if \c New is compatible for the argument after \c Cur
76  bool matches(ArrayRef<Value *> Cur, const Value *New) {
77    return Pred(Cur, New);
78  }
79
80  /// Generates a list of potential values for the argument after \c Cur.
81  std::vector<Constant *> generate(ArrayRef<Value *> Cur,
82                                   ArrayRef<Type *> BaseTypes) {
83    return Make(Cur, BaseTypes);
84  }
85};
86
87/// A description of some operation we can build while fuzzing IR.
88struct OpDescriptor {
89  unsigned Weight;
90  SmallVector<SourcePred, 2> SourcePreds;
91  std::function<Value *(ArrayRef<Value *>, Instruction *)> BuilderFunc;
92};
93
94static inline SourcePred onlyType(Type *Only) {
95  auto Pred = [Only](ArrayRef<Value *>, const Value *V) {
96    return V->getType() == Only;
97  };
98  auto Make = [Only](ArrayRef<Value *>, ArrayRef<Type *>) {
99    return makeConstantsWithType(Only);
100  };
101  return {Pred, Make};
102}
103
104static inline SourcePred anyType() {
105  auto Pred = [](ArrayRef<Value *>, const Value *V) {
106    return !V->getType()->isVoidTy();
107  };
108  auto Make = std::nullopt;
109  return {Pred, Make};
110}
111
112static inline SourcePred anyIntType() {
113  auto Pred = [](ArrayRef<Value *>, const Value *V) {
114    return V->getType()->isIntegerTy();
115  };
116  auto Make = std::nullopt;
117  return {Pred, Make};
118}
119
120static inline SourcePred anyFloatType() {
121  auto Pred = [](ArrayRef<Value *>, const Value *V) {
122    return V->getType()->isFloatingPointTy();
123  };
124  auto Make = std::nullopt;
125  return {Pred, Make};
126}
127
128static inline SourcePred anyPtrType() {
129  auto Pred = [](ArrayRef<Value *>, const Value *V) {
130    return V->getType()->isPointerTy() && !V->isSwiftError();
131  };
132  auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
133    std::vector<Constant *> Result;
134    // TODO: Should these point at something?
135    for (Type *T : Ts)
136      Result.push_back(UndefValue::get(PointerType::getUnqual(T)));
137    return Result;
138  };
139  return {Pred, Make};
140}
141
142static inline SourcePred sizedPtrType() {
143  auto Pred = [](ArrayRef<Value *>, const Value *V) {
144    if (V->isSwiftError())
145      return false;
146
147    if (const auto *PtrT = dyn_cast<PointerType>(V->getType()))
148      return PtrT->isOpaque() ||
149             PtrT->getNonOpaquePointerElementType()->isSized();
150    return false;
151  };
152  auto Make = [](ArrayRef<Value *>, ArrayRef<Type *> Ts) {
153    std::vector<Constant *> Result;
154
155    for (Type *T : Ts)
156      if (T->isSized())
157        Result.push_back(UndefValue::get(PointerType::getUnqual(T)));
158
159    return Result;
160  };
161  return {Pred, Make};
162}
163
164static inline SourcePred anyAggregateType() {
165  auto Pred = [](ArrayRef<Value *>, const Value *V) {
166    // We can't index zero sized arrays.
167    if (isa<ArrayType>(V->getType()))
168      return V->getType()->getArrayNumElements() > 0;
169
170    // Structs can also be zero sized. I.e opaque types.
171    if (isa<StructType>(V->getType()))
172      return V->getType()->getStructNumElements() > 0;
173
174    return V->getType()->isAggregateType();
175  };
176  // TODO: For now we only find aggregates in BaseTypes. It might be better to
177  // manufacture them out of the base types in some cases.
178  auto Find = std::nullopt;
179  return {Pred, Find};
180}
181
182static inline SourcePred anyVectorType() {
183  auto Pred = [](ArrayRef<Value *>, const Value *V) {
184    return V->getType()->isVectorTy();
185  };
186  // TODO: For now we only find vectors in BaseTypes. It might be better to
187  // manufacture vectors out of the base types, but it's tricky to be sure
188  // that's actually a reasonable type.
189  auto Make = std::nullopt;
190  return {Pred, Make};
191}
192
193/// Match values that have the same type as the first source.
194static inline SourcePred matchFirstType() {
195  auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
196    assert(!Cur.empty() && "No first source yet");
197    return V->getType() == Cur[0]->getType();
198  };
199  auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
200    assert(!Cur.empty() && "No first source yet");
201    return makeConstantsWithType(Cur[0]->getType());
202  };
203  return {Pred, Make};
204}
205
206/// Match values that have the first source's scalar type.
207static inline SourcePred matchScalarOfFirstType() {
208  auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
209    assert(!Cur.empty() && "No first source yet");
210    return V->getType() == Cur[0]->getType()->getScalarType();
211  };
212  auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
213    assert(!Cur.empty() && "No first source yet");
214    return makeConstantsWithType(Cur[0]->getType()->getScalarType());
215  };
216  return {Pred, Make};
217}
218
219} // namespace fuzzerop
220} // namespace llvm
221
222#endif // LLVM_FUZZMUTATE_OPDESCRIPTOR_H
223