Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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 | |||
25 | namespace llvm { |
||
26 | class Instruction; |
||
27 | namespace fuzzerop { |
||
28 | |||
29 | /// @{ |
||
30 | /// Populate a small list of potentially interesting constants of a given type. |
||
31 | void makeConstantsWithType(Type *T, std::vector<Constant *> &Cs); |
||
32 | std::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. |
||
42 | class SourcePred { |
||
43 | public: |
||
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 | |||
53 | private: |
||
54 | PredT Pred; |
||
55 | MakeT Make; |
||
56 | |||
57 | public: |
||
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. |
||
88 | struct OpDescriptor { |
||
89 | unsigned Weight; |
||
90 | SmallVector<SourcePred, 2> SourcePreds; |
||
91 | std::function<Value *(ArrayRef<Value *>, Instruction *)> BuilderFunc; |
||
92 | }; |
||
93 | |||
94 | static 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 | |||
104 | static 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 | |||
112 | static 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 | |||
120 | static 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 | |||
128 | static 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 | |||
142 | static 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 | |||
164 | static 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 | |||
182 | static 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. |
||
194 | static 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. |
||
207 | static 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 |