1//===- RandomIRBuilderTest.cpp - Tests for injector strategy --------------===//
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/RandomIRBuilder.h"
10#include "llvm/ADT/StringRef.h"
11#include "llvm/AsmParser/Parser.h"
12#include "llvm/AsmParser/SlotMapping.h"
13#include "llvm/FuzzMutate/IRMutator.h"
14#include "llvm/FuzzMutate/OpDescriptor.h"
15#include "llvm/FuzzMutate/Operations.h"
16#include "llvm/FuzzMutate/Random.h"
17#include "llvm/IR/Constants.h"
18#include "llvm/IR/Dominators.h"
19#include "llvm/IR/Instructions.h"
20#include "llvm/IR/LLVMContext.h"
21#include "llvm/IR/Module.h"
22#include "llvm/IR/Verifier.h"
23#include "llvm/Support/SourceMgr.h"
24
25#include "gtest/gtest.h"
26
27using namespace llvm;
28
29static constexpr int Seed = 5;
30
31namespace {
32
33std::unique_ptr<Module> parseAssembly(const char *Assembly,
34 LLVMContext &Context) {
35
36 SMDiagnostic Error;
37 std::unique_ptr<Module> M = parseAssemblyString(AsmString: Assembly, Err&: Error, Context);
38
39 std::string ErrMsg;
40 raw_string_ostream OS(ErrMsg);
41 Error.print(ProgName: "", S&: OS);
42
43 assert(M && !verifyModule(*M, &errs()));
44 return M;
45}
46
47TEST(RandomIRBuilderTest, ShuffleVectorIncorrectOperands) {
48 // Test that we don't create load instruction as a source for the shuffle
49 // vector operation.
50
51 LLVMContext Ctx;
52 const char *Source =
53 "define <2 x i32> @test(<2 x i1> %cond, <2 x i32> %a) {\n"
54 " %A = alloca <2 x i32>\n"
55 " %I = insertelement <2 x i32> %a, i32 1, i32 1\n"
56 " ret <2 x i32> undef\n"
57 "}";
58 auto M = parseAssembly(Assembly: Source, Context&: Ctx);
59
60 fuzzerop::OpDescriptor Descr = fuzzerop::shuffleVectorDescriptor(Weight: 1);
61
62 // Empty known types since we ShuffleVector descriptor doesn't care about them
63 RandomIRBuilder IB(Seed, {});
64
65 // Get first basic block of the first function
66 Function &F = *M->begin();
67 BasicBlock &BB = *F.begin();
68
69 SmallVector<Instruction *, 32> Insts;
70 for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
71 Insts.push_back(Elt: &*I);
72
73 // Pick first and second sources
74 SmallVector<Value *, 2> Srcs;
75 ASSERT_TRUE(Descr.SourcePreds[0].matches(Srcs, Insts[1]));
76 Srcs.push_back(Elt: Insts[1]);
77 ASSERT_TRUE(Descr.SourcePreds[1].matches(Srcs, Insts[1]));
78 Srcs.push_back(Elt: Insts[1]);
79
80 // Create new source. Check that it always matches with the descriptor.
81 // Run some iterations to account for random decisions.
82 for (int i = 0; i < 10; ++i) {
83 Value *LastSrc = IB.newSource(BB, Insts, Srcs, Pred: Descr.SourcePreds[2]);
84 ASSERT_TRUE(Descr.SourcePreds[2].matches(Srcs, LastSrc));
85 }
86}
87
88TEST(RandomIRBuilderTest, InsertValueIndexes) {
89 // Check that we will generate correct indexes for the insertvalue operation
90
91 LLVMContext Ctx;
92 const char *Source = "%T = type {i8, i32, i64}\n"
93 "define void @test() {\n"
94 " %A = alloca %T\n"
95 " %L = load %T, ptr %A"
96 " ret void\n"
97 "}";
98 auto M = parseAssembly(Assembly: Source, Context&: Ctx);
99
100 fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(Weight: 1);
101
102 std::array<Type *, 3> Types = {Type::getInt8Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx),
103 Type::getInt64Ty(C&: Ctx)};
104 RandomIRBuilder IB(Seed, Types);
105
106 // Get first basic block of the first function
107 Function &F = *M->begin();
108 BasicBlock &BB = *F.begin();
109
110 // Pick first source
111 Instruction *Src = &*std::next(x: BB.begin());
112
113 SmallVector<Value *, 2> Srcs(2);
114 ASSERT_TRUE(IVDescr.SourcePreds[0].matches({}, Src));
115 Srcs[0] = Src;
116
117 // Generate constants for each of the types and check that we pick correct
118 // index for the given type
119 for (auto *T : Types) {
120 // Loop to account for possible random decisions
121 for (int i = 0; i < 10; ++i) {
122 // Create value we want to insert. Only it's type matters.
123 Srcs[1] = ConstantInt::get(Ty: T, V: 5);
124
125 // Try to pick correct index
126 Value *Src =
127 IB.findOrCreateSource(BB, Insts: &*BB.begin(), Srcs, Pred: IVDescr.SourcePreds[2]);
128 ASSERT_TRUE(IVDescr.SourcePreds[2].matches(Srcs, Src));
129 }
130 }
131}
132
133TEST(RandomIRBuilderTest, ShuffleVectorSink) {
134 // Check that we will never use shuffle vector mask as a sink from the
135 // unrelated operation.
136
137 LLVMContext Ctx;
138 const char *SourceCode =
139 "define void @test(<4 x i32> %a) {\n"
140 " %S1 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"
141 " %S2 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"
142 " ret void\n"
143 "}";
144 auto M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
145
146 fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(Weight: 1);
147
148 RandomIRBuilder IB(Seed, {});
149
150 // Get first basic block of the first function
151 Function &F = *M->begin();
152 BasicBlock &BB = *F.begin();
153
154 // Source is %S1
155 Instruction *Source = &*BB.begin();
156 // Sink is %S2
157 SmallVector<Instruction *, 1> Sinks = {&*std::next(x: BB.begin())};
158
159 // Loop to account for random decisions
160 for (int i = 0; i < 10; ++i) {
161 // Try to connect S1 to S2. We should always create new sink.
162 IB.connectToSink(BB, Insts: Sinks, V: Source);
163 ASSERT_TRUE(!verifyModule(*M, &errs()));
164 }
165}
166
167TEST(RandomIRBuilderTest, InsertValueArray) {
168 // Check that we can generate insertvalue for the vector operations
169
170 LLVMContext Ctx;
171 const char *SourceCode = "define void @test() {\n"
172 " %A = alloca [8 x i32]\n"
173 " %L = load [8 x i32], ptr %A"
174 " ret void\n"
175 "}";
176 auto M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
177
178 fuzzerop::OpDescriptor Descr = fuzzerop::insertValueDescriptor(Weight: 1);
179
180 std::array<Type *, 3> Types = {Type::getInt8Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx),
181 Type::getInt64Ty(C&: Ctx)};
182 RandomIRBuilder IB(Seed, Types);
183
184 // Get first basic block of the first function
185 Function &F = *M->begin();
186 BasicBlock &BB = *F.begin();
187
188 // Pick first source
189 Instruction *Source = &*std::next(x: BB.begin());
190 ASSERT_TRUE(Descr.SourcePreds[0].matches({}, Source));
191
192 SmallVector<Value *, 2> Srcs(2);
193
194 // Check that we can always pick the last two operands.
195 for (int i = 0; i < 10; ++i) {
196 Srcs[0] = Source;
197 Srcs[1] = IB.findOrCreateSource(BB, Insts: {Source}, Srcs, Pred: Descr.SourcePreds[1]);
198 IB.findOrCreateSource(BB, Insts: {}, Srcs, Pred: Descr.SourcePreds[2]);
199 }
200}
201
202TEST(RandomIRBuilderTest, Invokes) {
203 // Check that we never generate load or store after invoke instruction
204
205 LLVMContext Ctx;
206 const char *SourceCode =
207 "declare ptr @f()"
208 "declare i32 @personality_function()"
209 "define ptr @test() personality ptr @personality_function {\n"
210 "entry:\n"
211 " %val = invoke ptr @f()\n"
212 " to label %normal unwind label %exceptional\n"
213 "normal:\n"
214 " ret ptr %val\n"
215 "exceptional:\n"
216 " %landing_pad4 = landingpad token cleanup\n"
217 " ret ptr undef\n"
218 "}";
219 auto M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
220
221 std::array<Type *, 1> Types = {Type::getInt8Ty(C&: Ctx)};
222 RandomIRBuilder IB(Seed, Types);
223
224 // Get first basic block of the test function
225 Function &F = *M->getFunction(Name: "test");
226 BasicBlock &BB = *F.begin();
227
228 Instruction *Invoke = &*BB.begin();
229
230 // Find source but never insert new load after invoke
231 for (int i = 0; i < 10; ++i) {
232 (void)IB.findOrCreateSource(BB, Insts: {Invoke}, Srcs: {}, Pred: fuzzerop::anyIntType());
233 ASSERT_TRUE(!verifyModule(*M, &errs()));
234 }
235}
236
237TEST(RandomIRBuilderTest, SwiftError) {
238 // Check that we never pick swifterror value as a source for operation
239 // other than load, store and call.
240
241 LLVMContext Ctx;
242 const char *SourceCode = "declare void @use(ptr swifterror %err)"
243 "define void @test() {\n"
244 "entry:\n"
245 " %err = alloca swifterror ptr, align 8\n"
246 " call void @use(ptr swifterror %err)\n"
247 " ret void\n"
248 "}";
249 auto M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
250
251 std::array<Type *, 1> Types = {Type::getInt8Ty(C&: Ctx)};
252 RandomIRBuilder IB(Seed, Types);
253
254 // Get first basic block of the test function
255 Function &F = *M->getFunction(Name: "test");
256 BasicBlock &BB = *F.begin();
257 Instruction *Alloca = &*BB.begin();
258
259 fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(Weight: 1);
260
261 for (int i = 0; i < 10; ++i) {
262 Value *V = IB.findOrCreateSource(BB, Insts: {Alloca}, Srcs: {}, Pred: Descr.SourcePreds[0]);
263 ASSERT_FALSE(isa<AllocaInst>(V));
264 }
265}
266
267TEST(RandomIRBuilderTest, dontConnectToSwitch) {
268 // Check that we never put anything into switch's case branch
269 // If we accidently put a variable, the module is invalid.
270 LLVMContext Ctx;
271 const char *SourceCode = "\n\
272 define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\
273 Entry: \n\
274 %I.1 = add i32 %I, 42 \n\
275 %J.1 = add i32 %J, 42 \n\
276 %IJ = add i32 %I, %J \n\
277 switch i32 %I, label %Default [ \n\
278 i32 1, label %OnOne \n\
279 ] \n\
280 Default: \n\
281 %CIEqJ = icmp eq i32 %I.1, %J.1 \n\
282 %CISltJ = icmp slt i32 %I.1, %J.1 \n\
283 %CAnd = and i1 %C1, %C2 \n\
284 br i1 %CIEqJ, label %Default, label %Exit \n\
285 OnOne: \n\
286 br i1 %C1, label %OnOne, label %Exit \n\
287 Exit: \n\
288 ret void \n\
289 }";
290
291 std::array<Type *, 2> Types = {Type::getInt32Ty(C&: Ctx), Type::getInt1Ty(C&: Ctx)};
292 RandomIRBuilder IB(Seed, Types);
293 for (int i = 0; i < 20; i++) {
294 std::unique_ptr<Module> M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
295 Function &F = *M->getFunction(Name: "test");
296 auto RS = makeSampler(RandGen&: IB.Rand, Items: make_pointer_range(Range&: F));
297 BasicBlock *BB = RS.getSelection();
298 SmallVector<Instruction *, 32> Insts;
299 for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I)
300 Insts.push_back(Elt: &*I);
301 if (Insts.size() < 2)
302 continue;
303 // Choose an instruction and connect to later operations.
304 size_t IP = uniform<size_t>(Gen&: IB.Rand, Min: 1, Max: Insts.size() - 1);
305 Instruction *Inst = Insts[IP - 1];
306 auto ConnectAfter = ArrayRef(Insts).slice(N: IP);
307 IB.connectToSink(BB&: *BB, Insts: ConnectAfter, V: Inst);
308 ASSERT_FALSE(verifyModule(*M, &errs()));
309 }
310}
311
312TEST(RandomIRBuilderTest, createStackMemory) {
313 LLVMContext Ctx;
314 const char *SourceCode = "\n\
315 define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\
316 Entry: \n\
317 ret void \n\
318 }";
319 Type *Int32Ty = Type::getInt32Ty(C&: Ctx);
320 Constant *Int32_1 = ConstantInt::get(Ty: Int32Ty, V: APInt(32, 1));
321 Type *Int64Ty = Type::getInt64Ty(C&: Ctx);
322 Constant *Int64_42 = ConstantInt::get(Ty: Int64Ty, V: APInt(64, 42));
323 Type *DoubleTy = Type::getDoubleTy(C&: Ctx);
324 Constant *Double_0 =
325 ConstantFP::get(Context&: Ctx, V: APFloat::getZero(Sem: DoubleTy->getFltSemantics()));
326 std::array<Type *, 8> Types = {
327 Int32Ty,
328 Int64Ty,
329 DoubleTy,
330 PointerType::get(C&: Ctx, AddressSpace: 0),
331 PointerType::get(ElementType: Int32Ty, AddressSpace: 0),
332 VectorType::get(ElementType: Int32Ty, NumElements: 4, Scalable: false),
333 StructType::create(Elements: {Int32Ty, DoubleTy, Int64Ty}),
334 ArrayType::get(ElementType: Int64Ty, NumElements: 4),
335 };
336 std::array<Value *, 8> Inits = {
337 Int32_1,
338 Int64_42,
339 Double_0,
340 UndefValue::get(T: Types[3]),
341 UndefValue::get(T: Types[4]),
342 ConstantVector::get(V: {Int32_1, Int32_1, Int32_1, Int32_1}),
343 ConstantStruct::get(T: cast<StructType>(Val: Types[6]),
344 V: {Int32_1, Double_0, Int64_42}),
345 ConstantArray::get(T: cast<ArrayType>(Val: Types[7]),
346 V: {Int64_42, Int64_42, Int64_42, Int64_42}),
347 };
348 ASSERT_EQ(Types.size(), Inits.size());
349 unsigned NumTests = Types.size();
350 RandomIRBuilder IB(Seed, Types);
351 auto CreateStackMemoryAndVerify = [&Ctx, &SourceCode, &IB](Type *Ty,
352 Value *Init) {
353 std::unique_ptr<Module> M = parseAssembly(Assembly: SourceCode, Context&: Ctx);
354 Function &F = *M->getFunction(Name: "test");
355 // Create stack memory without initializer.
356 IB.createStackMemory(F: &F, Ty, Init: nullptr);
357 // Create stack memory with initializer.
358 IB.createStackMemory(F: &F, Ty, Init);
359 EXPECT_FALSE(verifyModule(*M, &errs()));
360 };
361 for (unsigned i = 0; i < NumTests; i++) {
362 CreateStackMemoryAndVerify(Types[i], Inits[i]);
363 }
364}
365
366TEST(RandomIRBuilderTest, findOrCreateGlobalVariable) {
367 LLVMContext Ctx;
368 const char *SourceCode = "\n\
369 @G0 = external global i16 \n\
370 @G1 = global i32 1 \n\
371 ";
372 std::array<Type *, 3> Types = {Type::getInt16Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx),
373 Type::getInt64Ty(C&: Ctx)};
374 RandomIRBuilder IB(Seed, Types);
375
376 // Find external global
377 std::unique_ptr<Module> M0 = parseAssembly(Assembly: SourceCode, Context&: Ctx);
378 Type *ExternalTy = M0->globals().begin()->getValueType();
379 ASSERT_TRUE(ExternalTy->isIntegerTy(16));
380 IB.findOrCreateGlobalVariable(M: &*M0, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[0]));
381 ASSERT_FALSE(verifyModule(*M0, &errs()));
382 unsigned NumGV0 = M0->getNumNamedValues();
383 auto [GV0, DidCreate0] =
384 IB.findOrCreateGlobalVariable(M: &*M0, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[0]));
385 ASSERT_FALSE(verifyModule(*M0, &errs()));
386 ASSERT_EQ(M0->getNumNamedValues(), NumGV0 + DidCreate0);
387
388 // Find existing global
389 std::unique_ptr<Module> M1 = parseAssembly(Assembly: SourceCode, Context&: Ctx);
390 IB.findOrCreateGlobalVariable(M: &*M1, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[1]));
391 ASSERT_FALSE(verifyModule(*M1, &errs()));
392 unsigned NumGV1 = M1->getNumNamedValues();
393 auto [GV1, DidCreate1] =
394 IB.findOrCreateGlobalVariable(M: &*M1, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[1]));
395 ASSERT_FALSE(verifyModule(*M1, &errs()));
396 ASSERT_EQ(M1->getNumNamedValues(), NumGV1 + DidCreate1);
397
398 // Create new global
399 std::unique_ptr<Module> M2 = parseAssembly(Assembly: SourceCode, Context&: Ctx);
400 auto [GV2, DidCreate2] =
401 IB.findOrCreateGlobalVariable(M: &*M2, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[2]));
402 ASSERT_FALSE(verifyModule(*M2, &errs()));
403 ASSERT_TRUE(DidCreate2);
404}
405
406/// Checks if the source and sink we find for an instruction has correct
407/// domination relation.
408TEST(RandomIRBuilderTest, findSourceAndSink) {
409 const char *Source = "\n\
410 define i64 @test(i1 %0, i1 %1, i1 %2, i32 %3, i32 %4) { \n\
411 Entry: \n\
412 %A = alloca i32, i32 8, align 4 \n\
413 %E.1 = and i32 %3, %4 \n\
414 %E.2 = add i32 %4 , 1 \n\
415 %A.GEP.1 = getelementptr i32, ptr %A, i32 0 \n\
416 %A.GEP.2 = getelementptr i32, ptr %A.GEP.1, i32 1 \n\
417 %L.2 = load i32, ptr %A.GEP.2 \n\
418 %L.1 = load i32, ptr %A.GEP.1 \n\
419 %E.3 = sub i32 %E.2, %L.1 \n\
420 %Cond.1 = icmp eq i32 %E.3, %E.2 \n\
421 %Cond.2 = and i1 %0, %1 \n\
422 %Cond = or i1 %Cond.1, %Cond.2 \n\
423 br i1 %Cond, label %BB0, label %BB1 \n\
424 BB0: \n\
425 %Add = add i32 %L.1, %L.2 \n\
426 %Sub = sub i32 %L.1, %L.2 \n\
427 %Sub.1 = sub i32 %Sub, 12 \n\
428 %Cast.1 = bitcast i32 %4 to float \n\
429 %Add.2 = add i32 %3, 1 \n\
430 %Cast.2 = bitcast i32 %Add.2 to float \n\
431 %FAdd = fadd float %Cast.1, %Cast.2 \n\
432 %Add.3 = add i32 %L.2, %L.1 \n\
433 %Cast.3 = bitcast float %FAdd to i32 \n\
434 %Sub.2 = sub i32 %Cast.3, %Sub.1 \n\
435 %SExt = sext i32 %Cast.3 to i64 \n\
436 %A.GEP.3 = getelementptr i64, ptr %A, i32 1 \n\
437 store i64 %SExt, ptr %A.GEP.3 \n\
438 br label %Exit \n\
439 BB1: \n\
440 %PHI.1 = phi i32 [0, %Entry] \n\
441 %SExt.1 = sext i1 %Cond.2 to i32 \n\
442 %SExt.2 = sext i1 %Cond.1 to i32 \n\
443 %E.164 = zext i32 %E.1 to i64 \n\
444 %E.264 = zext i32 %E.2 to i64 \n\
445 %E.1264 = mul i64 %E.164, %E.264 \n\
446 %E.12 = trunc i64 %E.1264 to i32 \n\
447 %A.GEP.4 = getelementptr i32, ptr %A, i32 2 \n\
448 %A.GEP.5 = getelementptr i32, ptr %A.GEP.4, i32 2 \n\
449 store i32 %E.12, ptr %A.GEP.5 \n\
450 br label %Exit \n\
451 Exit: \n\
452 %PHI.2 = phi i32 [%Add, %BB0], [%E.3, %BB1] \n\
453 %PHI.3 = phi i64 [%SExt, %BB0], [%E.1264, %BB1] \n\
454 %ZExt = zext i32 %PHI.2 to i64 \n\
455 %Add.5 = add i64 %PHI.3, 3 \n\
456 ret i64 %Add.5 \n\
457 }";
458 LLVMContext Ctx;
459 std::array<Type *, 3> Types = {Type::getInt1Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx),
460 Type::getInt64Ty(C&: Ctx)};
461 std::mt19937 mt(Seed);
462 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
463
464 // Get a random instruction, try to find source and sink, make sure it is
465 // dominated.
466 for (int i = 0; i < 100; i++) {
467 RandomIRBuilder IB(RandInt(mt), Types);
468 std::unique_ptr<Module> M = parseAssembly(Assembly: Source, Context&: Ctx);
469 Function &F = *M->getFunction(Name: "test");
470 DominatorTree DT(F);
471 BasicBlock *BB = makeSampler(RandGen&: IB.Rand, Items: make_pointer_range(Range&: F)).getSelection();
472 SmallVector<Instruction *, 32> Insts;
473 for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I)
474 Insts.push_back(Elt: &*I);
475 // Choose an insertion point for our new instruction.
476 size_t IP = uniform<size_t>(Gen&: IB.Rand, Min: 1, Max: Insts.size() - 2);
477
478 auto InstsBefore = ArrayRef(Insts).slice(N: 0, M: IP);
479 auto InstsAfter = ArrayRef(Insts).slice(N: IP);
480 Value *Src = IB.findOrCreateSource(
481 BB&: *BB, Insts: InstsBefore, Srcs: {}, Pred: fuzzerop::onlyType(Only: Types[i % Types.size()]));
482 ASSERT_TRUE(DT.dominates(Src, Insts[IP + 1]));
483 Instruction *Sink = IB.connectToSink(BB&: *BB, Insts: InstsAfter, V: Insts[IP - 1]);
484 if (!DT.dominates(Def: Insts[IP - 1], User: Sink)) {
485 errs() << *Insts[IP - 1] << "\n" << *Sink << "\n ";
486 }
487 ASSERT_TRUE(DT.dominates(Insts[IP - 1], Sink));
488 }
489}
490TEST(RandomIRBuilderTest, sinkToInstrinsic) {
491 const char *Source = "\n\
492 declare double @llvm.sqrt.f64(double %Val) \n\
493 declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind \n\
494 \n\
495 define double @test(double %0, double %1, i64 %2, i64 %3, i64 %4, i8 %5) { \n\
496 Entry: \n\
497 %sqrt = call double @llvm.sqrt.f64(double %0) \n\
498 call void @llvm.ubsantrap(i8 1) \n\
499 ret double %sqrt \n\
500 }";
501 LLVMContext Ctx;
502 std::array<Type *, 3> Types = {Type::getInt8Ty(C&: Ctx), Type::getInt64Ty(C&: Ctx),
503 Type::getDoubleTy(C&: Ctx)};
504 std::mt19937 mt(Seed);
505 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
506
507 RandomIRBuilder IB(RandInt(mt), Types);
508 std::unique_ptr<Module> M = parseAssembly(Assembly: Source, Context&: Ctx);
509 Function &F = *M->getFunction(Name: "test");
510 BasicBlock &BB = F.getEntryBlock();
511 bool Modified = false;
512
513 Instruction *I = &*BB.begin();
514 for (int i = 0; i < 20; i++) {
515 Value *OldOperand = I->getOperand(i: 0);
516 Value *Src = F.getArg(i: 1);
517 IB.connectToSink(BB, Insts: {I}, V: Src);
518 Value *NewOperand = I->getOperand(i: 0);
519 Modified |= (OldOperand != NewOperand);
520 ASSERT_FALSE(verifyModule(*M, &errs()));
521 }
522 ASSERT_TRUE(Modified);
523
524 Modified = false;
525 I = I->getNextNonDebugInstruction();
526 for (int i = 0; i < 20; i++) {
527 Value *OldOperand = I->getOperand(i: 0);
528 Value *Src = F.getArg(i: 5);
529 IB.connectToSink(BB, Insts: {I}, V: Src);
530 Value *NewOperand = I->getOperand(i: 0);
531 Modified |= (OldOperand != NewOperand);
532 ASSERT_FALSE(verifyModule(*M, &errs()));
533 }
534 ASSERT_FALSE(Modified);
535}
536
537TEST(RandomIRBuilderTest, DoNotCallPointerWhenSink) {
538 const char *Source = "\n\
539 declare void @g() \n\
540 define void @f(ptr %ptr) { \n\
541 Entry: \n\
542 call void @g() \n\
543 ret void \n\
544 }";
545 LLVMContext Ctx;
546 std::mt19937 mt(Seed);
547 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
548
549 RandomIRBuilder IB(RandInt(mt), {});
550 std::unique_ptr<Module> M = parseAssembly(Assembly: Source, Context&: Ctx);
551 Function &F = *M->getFunction(Name: "f");
552 BasicBlock &BB = F.getEntryBlock();
553 bool Modified = false;
554
555 Instruction *I = &*BB.begin();
556 for (int i = 0; i < 20; i++) {
557 Value *OldOperand = I->getOperand(i: 0);
558 Value *Src = F.getArg(i: 0);
559 IB.connectToSink(BB, Insts: {I}, V: Src);
560 Value *NewOperand = I->getOperand(i: 0);
561 Modified |= (OldOperand != NewOperand);
562 ASSERT_FALSE(verifyModule(*M, &errs()));
563 }
564 ASSERT_FALSE(Modified);
565}
566
567TEST(RandomIRBuilderTest, SrcAndSinkWOrphanBlock) {
568 const char *Source = "\n\
569 define i1 @test(i1 %Bool, i32 %Int, i64 %Long) { \n\
570 Entry: \n\
571 %Eq0 = icmp eq i64 %Long, 0 \n\
572 br i1 %Eq0, label %True, label %False \n\
573 True: \n\
574 %Or = or i1 %Bool, %Eq0 \n\
575 ret i1 %Or \n\
576 False: \n\
577 %And = and i1 %Bool, %Eq0 \n\
578 ret i1 %And \n\
579 Orphan_1: \n\
580 %NotBool = sub i1 1, %Bool \n\
581 ret i1 %NotBool \n\
582 Orphan_2: \n\
583 %Le42 = icmp sle i32 %Int, 42 \n\
584 ret i1 %Le42 \n\
585 }";
586 LLVMContext Ctx;
587 std::mt19937 mt(Seed);
588 std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
589 std::array<Type *, 3> IntTys(
590 {Type::getInt64Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), Type::getInt1Ty(C&: Ctx)});
591 std::vector<Value *> Constants;
592 for (Type *IntTy : IntTys) {
593 for (size_t v : {1, 42}) {
594 Constants.push_back(x: ConstantInt::get(Ty: IntTy, V: v));
595 }
596 }
597 for (int i = 0; i < 10; i++) {
598 RandomIRBuilder IB(RandInt(mt), IntTys);
599 std::unique_ptr<Module> M = parseAssembly(Assembly: Source, Context&: Ctx);
600 Function &F = *M->getFunction(Name: "test");
601 for (BasicBlock &BB : F) {
602 SmallVector<Instruction *, 4> Insts;
603 for (Instruction &I : BB) {
604 Insts.push_back(Elt: &I);
605 }
606 for (int j = 0; j < 10; j++) {
607 IB.findOrCreateSource(BB, Insts);
608 }
609 for (Value *V : Constants) {
610 IB.connectToSink(BB, Insts, V);
611 }
612 }
613 }
614}
615} // namespace
616

source code of llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp