1 //===-- Operations.cpp ----------------------------------------------------===//
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 #include "llvm/FuzzMutate/Operations.h"
10 #include "llvm/IR/BasicBlock.h"
11 #include "llvm/IR/Constants.h"
12 #include "llvm/IR/Function.h"
13 #include "llvm/IR/Instructions.h"
14
15 using namespace llvm;
16 using namespace fuzzerop;
17
describeFuzzerIntOps(std::vector<fuzzerop::OpDescriptor> & Ops)18 void llvm::describeFuzzerIntOps(std::vector<fuzzerop::OpDescriptor> &Ops) {
19 Ops.push_back(binOpDescriptor(1, Instruction::Add));
20 Ops.push_back(binOpDescriptor(1, Instruction::Sub));
21 Ops.push_back(binOpDescriptor(1, Instruction::Mul));
22 Ops.push_back(binOpDescriptor(1, Instruction::SDiv));
23 Ops.push_back(binOpDescriptor(1, Instruction::UDiv));
24 Ops.push_back(binOpDescriptor(1, Instruction::SRem));
25 Ops.push_back(binOpDescriptor(1, Instruction::URem));
26 Ops.push_back(binOpDescriptor(1, Instruction::Shl));
27 Ops.push_back(binOpDescriptor(1, Instruction::LShr));
28 Ops.push_back(binOpDescriptor(1, Instruction::AShr));
29 Ops.push_back(binOpDescriptor(1, Instruction::And));
30 Ops.push_back(binOpDescriptor(1, Instruction::Or));
31 Ops.push_back(binOpDescriptor(1, Instruction::Xor));
32
33 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_EQ));
34 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_NE));
35 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_UGT));
36 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_UGE));
37 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_ULT));
38 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_ULE));
39 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SGT));
40 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SGE));
41 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SLT));
42 Ops.push_back(cmpOpDescriptor(1, Instruction::ICmp, CmpInst::ICMP_SLE));
43 }
44
describeFuzzerFloatOps(std::vector<fuzzerop::OpDescriptor> & Ops)45 void llvm::describeFuzzerFloatOps(std::vector<fuzzerop::OpDescriptor> &Ops) {
46 Ops.push_back(binOpDescriptor(1, Instruction::FAdd));
47 Ops.push_back(binOpDescriptor(1, Instruction::FSub));
48 Ops.push_back(binOpDescriptor(1, Instruction::FMul));
49 Ops.push_back(binOpDescriptor(1, Instruction::FDiv));
50 Ops.push_back(binOpDescriptor(1, Instruction::FRem));
51
52 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_FALSE));
53 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OEQ));
54 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OGT));
55 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OGE));
56 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OLT));
57 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_OLE));
58 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ONE));
59 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ORD));
60 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UNO));
61 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UEQ));
62 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UGT));
63 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UGE));
64 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ULT));
65 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_ULE));
66 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_UNE));
67 Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_TRUE));
68 }
69
describeFuzzerControlFlowOps(std::vector<fuzzerop::OpDescriptor> & Ops)70 void llvm::describeFuzzerControlFlowOps(
71 std::vector<fuzzerop::OpDescriptor> &Ops) {
72 Ops.push_back(splitBlockDescriptor(1));
73 }
74
describeFuzzerPointerOps(std::vector<fuzzerop::OpDescriptor> & Ops)75 void llvm::describeFuzzerPointerOps(std::vector<fuzzerop::OpDescriptor> &Ops) {
76 Ops.push_back(gepDescriptor(1));
77 }
78
describeFuzzerAggregateOps(std::vector<fuzzerop::OpDescriptor> & Ops)79 void llvm::describeFuzzerAggregateOps(
80 std::vector<fuzzerop::OpDescriptor> &Ops) {
81 Ops.push_back(extractValueDescriptor(1));
82 Ops.push_back(insertValueDescriptor(1));
83 }
84
describeFuzzerVectorOps(std::vector<fuzzerop::OpDescriptor> & Ops)85 void llvm::describeFuzzerVectorOps(std::vector<fuzzerop::OpDescriptor> &Ops) {
86 Ops.push_back(extractElementDescriptor(1));
87 Ops.push_back(insertElementDescriptor(1));
88 Ops.push_back(shuffleVectorDescriptor(1));
89 }
90
binOpDescriptor(unsigned Weight,Instruction::BinaryOps Op)91 OpDescriptor llvm::fuzzerop::binOpDescriptor(unsigned Weight,
92 Instruction::BinaryOps Op) {
93 auto buildOp = [Op](ArrayRef<Value *> Srcs, Instruction *Inst) {
94 return BinaryOperator::Create(Op, Srcs[0], Srcs[1], "B", Inst);
95 };
96 switch (Op) {
97 case Instruction::Add:
98 case Instruction::Sub:
99 case Instruction::Mul:
100 case Instruction::SDiv:
101 case Instruction::UDiv:
102 case Instruction::SRem:
103 case Instruction::URem:
104 case Instruction::Shl:
105 case Instruction::LShr:
106 case Instruction::AShr:
107 case Instruction::And:
108 case Instruction::Or:
109 case Instruction::Xor:
110 return {Weight, {anyIntType(), matchFirstType()}, buildOp};
111 case Instruction::FAdd:
112 case Instruction::FSub:
113 case Instruction::FMul:
114 case Instruction::FDiv:
115 case Instruction::FRem:
116 return {Weight, {anyFloatType(), matchFirstType()}, buildOp};
117 case Instruction::BinaryOpsEnd:
118 llvm_unreachable("Value out of range of enum");
119 }
120 llvm_unreachable("Covered switch");
121 }
122
cmpOpDescriptor(unsigned Weight,Instruction::OtherOps CmpOp,CmpInst::Predicate Pred)123 OpDescriptor llvm::fuzzerop::cmpOpDescriptor(unsigned Weight,
124 Instruction::OtherOps CmpOp,
125 CmpInst::Predicate Pred) {
126 auto buildOp = [CmpOp, Pred](ArrayRef<Value *> Srcs, Instruction *Inst) {
127 return CmpInst::Create(CmpOp, Pred, Srcs[0], Srcs[1], "C", Inst);
128 };
129
130 switch (CmpOp) {
131 case Instruction::ICmp:
132 return {Weight, {anyIntType(), matchFirstType()}, buildOp};
133 case Instruction::FCmp:
134 return {Weight, {anyFloatType(), matchFirstType()}, buildOp};
135 default:
136 llvm_unreachable("CmpOp must be ICmp or FCmp");
137 }
138 }
139
splitBlockDescriptor(unsigned Weight)140 OpDescriptor llvm::fuzzerop::splitBlockDescriptor(unsigned Weight) {
141 auto buildSplitBlock = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
142 BasicBlock *Block = Inst->getParent();
143 BasicBlock *Next = Block->splitBasicBlock(Inst, "BB");
144
145 // If it was an exception handling block, we are done.
146 if (Block->isEHPad())
147 return nullptr;
148
149 // Loop back on this block by replacing the unconditional forward branch
150 // with a conditional with a backedge.
151 if (Block != &Block->getParent()->getEntryBlock()) {
152 BranchInst::Create(Block, Next, Srcs[0], Block->getTerminator());
153 Block->getTerminator()->eraseFromParent();
154
155 // We need values for each phi in the block. Since there isn't a good way
156 // to do a variable number of input values currently, we just fill them
157 // with undef.
158 for (PHINode &PHI : Block->phis())
159 PHI.addIncoming(UndefValue::get(PHI.getType()), Block);
160 }
161 return nullptr;
162 };
163 SourcePred isInt1Ty{[](ArrayRef<Value *>, const Value *V) {
164 return V->getType()->isIntegerTy(1);
165 },
166 std::nullopt};
167 return {Weight, {isInt1Ty}, buildSplitBlock};
168 }
169
gepDescriptor(unsigned Weight)170 OpDescriptor llvm::fuzzerop::gepDescriptor(unsigned Weight) {
171 auto buildGEP = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
172 // TODO: It would be better to generate a random type here, rather than
173 // generating a random value and picking its type.
174 Type *Ty = Srcs[0]->getType()->isOpaquePointerTy()
175 ? Srcs[1]->getType()
176 : Srcs[0]->getType()->getNonOpaquePointerElementType();
177 auto Indices = ArrayRef(Srcs).drop_front(2);
178 return GetElementPtrInst::Create(Ty, Srcs[0], Indices, "G", Inst);
179 };
180 // TODO: Handle aggregates and vectors
181 // TODO: Support multiple indices.
182 // TODO: Try to avoid meaningless accesses.
183 SourcePred sizedType(
184 [](ArrayRef<Value *>, const Value *V) { return V->getType()->isSized(); },
185 std::nullopt);
186 return {Weight, {sizedPtrType(), sizedType, anyIntType()}, buildGEP};
187 }
188
getAggregateNumElements(Type * T)189 static uint64_t getAggregateNumElements(Type *T) {
190 assert(T->isAggregateType() && "Not a struct or array");
191 if (isa<StructType>(T))
192 return T->getStructNumElements();
193 return T->getArrayNumElements();
194 }
195
validExtractValueIndex()196 static SourcePred validExtractValueIndex() {
197 auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
198 if (auto *CI = dyn_cast<ConstantInt>(V))
199 if (!CI->uge(getAggregateNumElements(Cur[0]->getType())))
200 return true;
201 return false;
202 };
203 auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {
204 std::vector<Constant *> Result;
205 auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());
206 uint64_t N = getAggregateNumElements(Cur[0]->getType());
207 // Create indices at the start, end, and middle, but avoid dups.
208 Result.push_back(ConstantInt::get(Int32Ty, 0));
209 if (N > 1)
210 Result.push_back(ConstantInt::get(Int32Ty, N - 1));
211 if (N > 2)
212 Result.push_back(ConstantInt::get(Int32Ty, N / 2));
213 return Result;
214 };
215 return {Pred, Make};
216 }
217
extractValueDescriptor(unsigned Weight)218 OpDescriptor llvm::fuzzerop::extractValueDescriptor(unsigned Weight) {
219 auto buildExtract = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
220 // TODO: It's pretty inefficient to shuffle this all through constants.
221 unsigned Idx = cast<ConstantInt>(Srcs[1])->getZExtValue();
222 return ExtractValueInst::Create(Srcs[0], {Idx}, "E", Inst);
223 };
224 // TODO: Should we handle multiple indices?
225 return {Weight, {anyAggregateType(), validExtractValueIndex()}, buildExtract};
226 }
227
matchScalarInAggregate()228 static SourcePred matchScalarInAggregate() {
229 auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
230 if (auto *ArrayT = dyn_cast<ArrayType>(Cur[0]->getType()))
231 return V->getType() == ArrayT->getElementType();
232
233 auto *STy = cast<StructType>(Cur[0]->getType());
234 for (int I = 0, E = STy->getNumElements(); I < E; ++I)
235 if (STy->getTypeAtIndex(I) == V->getType())
236 return true;
237 return false;
238 };
239 auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *>) {
240 if (auto *ArrayT = dyn_cast<ArrayType>(Cur[0]->getType()))
241 return makeConstantsWithType(ArrayT->getElementType());
242
243 std::vector<Constant *> Result;
244 auto *STy = cast<StructType>(Cur[0]->getType());
245 for (int I = 0, E = STy->getNumElements(); I < E; ++I)
246 makeConstantsWithType(STy->getTypeAtIndex(I), Result);
247 return Result;
248 };
249 return {Pred, Make};
250 }
251
validInsertValueIndex()252 static SourcePred validInsertValueIndex() {
253 auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
254 if (auto *CI = dyn_cast<ConstantInt>(V))
255 if (CI->getBitWidth() == 32) {
256 Type *Indexed = ExtractValueInst::getIndexedType(Cur[0]->getType(),
257 CI->getZExtValue());
258 return Indexed == Cur[1]->getType();
259 }
260 return false;
261 };
262 auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {
263 std::vector<Constant *> Result;
264 auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());
265 auto *BaseTy = Cur[0]->getType();
266 int I = 0;
267 while (Type *Indexed = ExtractValueInst::getIndexedType(BaseTy, I)) {
268 if (Indexed == Cur[1]->getType())
269 Result.push_back(ConstantInt::get(Int32Ty, I));
270 ++I;
271 }
272 return Result;
273 };
274 return {Pred, Make};
275 }
276
insertValueDescriptor(unsigned Weight)277 OpDescriptor llvm::fuzzerop::insertValueDescriptor(unsigned Weight) {
278 auto buildInsert = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
279 // TODO: It's pretty inefficient to shuffle this all through constants.
280 unsigned Idx = cast<ConstantInt>(Srcs[2])->getZExtValue();
281 return InsertValueInst::Create(Srcs[0], Srcs[1], {Idx}, "I", Inst);
282 };
283 return {
284 Weight,
285 {anyAggregateType(), matchScalarInAggregate(), validInsertValueIndex()},
286 buildInsert};
287 }
288
extractElementDescriptor(unsigned Weight)289 OpDescriptor llvm::fuzzerop::extractElementDescriptor(unsigned Weight) {
290 auto buildExtract = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
291 return ExtractElementInst::Create(Srcs[0], Srcs[1], "E", Inst);
292 };
293 // TODO: Try to avoid undefined accesses.
294 return {Weight, {anyVectorType(), anyIntType()}, buildExtract};
295 }
296
insertElementDescriptor(unsigned Weight)297 OpDescriptor llvm::fuzzerop::insertElementDescriptor(unsigned Weight) {
298 auto buildInsert = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
299 return InsertElementInst::Create(Srcs[0], Srcs[1], Srcs[2], "I", Inst);
300 };
301 // TODO: Try to avoid undefined accesses.
302 return {Weight,
303 {anyVectorType(), matchScalarOfFirstType(), anyIntType()},
304 buildInsert};
305 }
306
validShuffleVectorIndex()307 static SourcePred validShuffleVectorIndex() {
308 auto Pred = [](ArrayRef<Value *> Cur, const Value *V) {
309 return ShuffleVectorInst::isValidOperands(Cur[0], Cur[1], V);
310 };
311 auto Make = [](ArrayRef<Value *> Cur, ArrayRef<Type *> Ts) {
312 auto *FirstTy = cast<VectorType>(Cur[0]->getType());
313 auto *Int32Ty = Type::getInt32Ty(Cur[0]->getContext());
314 // TODO: It's straighforward to make up reasonable values, but listing them
315 // exhaustively would be insane. Come up with a couple of sensible ones.
316 return std::vector<Constant *>{
317 UndefValue::get(VectorType::get(Int32Ty, FirstTy->getElementCount()))};
318 };
319 return {Pred, Make};
320 }
321
shuffleVectorDescriptor(unsigned Weight)322 OpDescriptor llvm::fuzzerop::shuffleVectorDescriptor(unsigned Weight) {
323 auto buildShuffle = [](ArrayRef<Value *> Srcs, Instruction *Inst) {
324 return new ShuffleVectorInst(Srcs[0], Srcs[1], Srcs[2], "S", Inst);
325 };
326 return {Weight,
327 {anyVectorType(), matchFirstType(), validShuffleVectorIndex()},
328 buildShuffle};
329 }
330