xref: /llvm-project/llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp (revision a933f6003ecf79521fcfd28d7e31475e21d3121b)
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/Instructions.h"
19 #include "llvm/IR/LLVMContext.h"
20 #include "llvm/IR/Module.h"
21 #include "llvm/IR/Verifier.h"
22 #include "llvm/Support/SourceMgr.h"
23 
24 #include "gtest/gtest.h"
25 
26 using namespace llvm;
27 
28 static constexpr int Seed = 5;
29 
30 namespace {
31 
32 std::unique_ptr<Module> parseAssembly(const char *Assembly,
33                                       LLVMContext &Context) {
34 
35   SMDiagnostic Error;
36   std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);
37 
38   std::string ErrMsg;
39   raw_string_ostream OS(ErrMsg);
40   Error.print("", OS);
41 
42   assert(M && !verifyModule(*M, &errs()));
43   return M;
44 }
45 
46 TEST(RandomIRBuilderTest, ShuffleVectorIncorrectOperands) {
47   // Test that we don't create load instruction as a source for the shuffle
48   // vector operation.
49 
50   LLVMContext Ctx;
51   const char *Source =
52       "define <2 x i32> @test(<2 x i1> %cond, <2 x i32> %a) {\n"
53       "  %A = alloca <2 x i32>\n"
54       "  %I = insertelement <2 x i32> %a, i32 1, i32 1\n"
55       "  ret <2 x i32> undef\n"
56       "}";
57   auto M = parseAssembly(Source, Ctx);
58 
59   fuzzerop::OpDescriptor Descr = fuzzerop::shuffleVectorDescriptor(1);
60 
61   // Empty known types since we ShuffleVector descriptor doesn't care about them
62   RandomIRBuilder IB(Seed, {});
63 
64   // Get first basic block of the first function
65   Function &F = *M->begin();
66   BasicBlock &BB = *F.begin();
67 
68   SmallVector<Instruction *, 32> Insts;
69   for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
70     Insts.push_back(&*I);
71 
72   // Pick first and second sources
73   SmallVector<Value *, 2> Srcs;
74   ASSERT_TRUE(Descr.SourcePreds[0].matches(Srcs, Insts[1]));
75   Srcs.push_back(Insts[1]);
76   ASSERT_TRUE(Descr.SourcePreds[1].matches(Srcs, Insts[1]));
77   Srcs.push_back(Insts[1]);
78 
79   // Create new source. Check that it always matches with the descriptor.
80   // Run some iterations to account for random decisions.
81   for (int i = 0; i < 10; ++i) {
82     Value *LastSrc = IB.newSource(BB, Insts, Srcs, Descr.SourcePreds[2]);
83     ASSERT_TRUE(Descr.SourcePreds[2].matches(Srcs, LastSrc));
84   }
85 }
86 
87 TEST(RandomIRBuilderTest, InsertValueIndexes) {
88   // Check that we will generate correct indexes for the insertvalue operation
89 
90   LLVMContext Ctx;
91   const char *Source = "%T = type {i8, i32, i64}\n"
92                        "define void @test() {\n"
93                        "  %A = alloca %T\n"
94                        "  %L = load %T, ptr %A"
95                        "  ret void\n"
96                        "}";
97   auto M = parseAssembly(Source, Ctx);
98 
99   fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1);
100 
101   std::vector<Type *> Types = {Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx),
102                                Type::getInt64Ty(Ctx)};
103   RandomIRBuilder IB(Seed, Types);
104 
105   // Get first basic block of the first function
106   Function &F = *M->begin();
107   BasicBlock &BB = *F.begin();
108 
109   // Pick first source
110   Instruction *Src = &*std::next(BB.begin());
111 
112   SmallVector<Value *, 2> Srcs(2);
113   ASSERT_TRUE(IVDescr.SourcePreds[0].matches({}, Src));
114   Srcs[0] = Src;
115 
116   // Generate constants for each of the types and check that we pick correct
117   // index for the given type
118   for (auto *T : Types) {
119     // Loop to account for possible random decisions
120     for (int i = 0; i < 10; ++i) {
121       // Create value we want to insert. Only it's type matters.
122       Srcs[1] = ConstantInt::get(T, 5);
123 
124       // Try to pick correct index
125       Value *Src =
126           IB.findOrCreateSource(BB, &*BB.begin(), Srcs, IVDescr.SourcePreds[2]);
127       ASSERT_TRUE(IVDescr.SourcePreds[2].matches(Srcs, Src));
128     }
129   }
130 }
131 
132 TEST(RandomIRBuilderTest, ShuffleVectorSink) {
133   // Check that we will never use shuffle vector mask as a sink from the
134   // unrelated operation.
135 
136   LLVMContext Ctx;
137   const char *SourceCode =
138       "define void @test(<4 x i32> %a) {\n"
139       "  %S1 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"
140       "  %S2 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"
141       "  ret void\n"
142       "}";
143   auto M = parseAssembly(SourceCode, Ctx);
144 
145   fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1);
146 
147   RandomIRBuilder IB(Seed, {});
148 
149   // Get first basic block of the first function
150   Function &F = *M->begin();
151   BasicBlock &BB = *F.begin();
152 
153   // Source is %S1
154   Instruction *Source = &*BB.begin();
155   // Sink is %S2
156   SmallVector<Instruction *, 1> Sinks = {&*std::next(BB.begin())};
157 
158   // Loop to account for random decisions
159   for (int i = 0; i < 10; ++i) {
160     // Try to connect S1 to S2. We should always create new sink.
161     IB.connectToSink(BB, Sinks, Source);
162     ASSERT_TRUE(!verifyModule(*M, &errs()));
163   }
164 }
165 
166 TEST(RandomIRBuilderTest, InsertValueArray) {
167   // Check that we can generate insertvalue for the vector operations
168 
169   LLVMContext Ctx;
170   const char *SourceCode = "define void @test() {\n"
171                            "  %A = alloca [8 x i32]\n"
172                            "  %L = load [8 x i32], ptr %A"
173                            "  ret void\n"
174                            "}";
175   auto M = parseAssembly(SourceCode, Ctx);
176 
177   fuzzerop::OpDescriptor Descr = fuzzerop::insertValueDescriptor(1);
178 
179   std::vector<Type *> Types = {Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx),
180                                Type::getInt64Ty(Ctx)};
181   RandomIRBuilder IB(Seed, Types);
182 
183   // Get first basic block of the first function
184   Function &F = *M->begin();
185   BasicBlock &BB = *F.begin();
186 
187   // Pick first source
188   Instruction *Source = &*std::next(BB.begin());
189   ASSERT_TRUE(Descr.SourcePreds[0].matches({}, Source));
190 
191   SmallVector<Value *, 2> Srcs(2);
192 
193   // Check that we can always pick the last two operands.
194   for (int i = 0; i < 10; ++i) {
195     Srcs[0] = Source;
196     Srcs[1] = IB.findOrCreateSource(BB, {Source}, Srcs, Descr.SourcePreds[1]);
197     IB.findOrCreateSource(BB, {}, Srcs, Descr.SourcePreds[2]);
198   }
199 }
200 
201 TEST(RandomIRBuilderTest, Invokes) {
202   // Check that we never generate load or store after invoke instruction
203 
204   LLVMContext Ctx;
205   const char *SourceCode =
206       "declare ptr @f()"
207       "declare i32 @personality_function()"
208       "define ptr @test() personality ptr @personality_function {\n"
209       "entry:\n"
210       "  %val = invoke ptr @f()\n"
211       "          to label %normal unwind label %exceptional\n"
212       "normal:\n"
213       "  ret ptr %val\n"
214       "exceptional:\n"
215       "  %landing_pad4 = landingpad token cleanup\n"
216       "  ret ptr undef\n"
217       "}";
218   auto M = parseAssembly(SourceCode, Ctx);
219 
220   std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};
221   RandomIRBuilder IB(Seed, Types);
222 
223   // Get first basic block of the test function
224   Function &F = *M->getFunction("test");
225   BasicBlock &BB = *F.begin();
226 
227   Instruction *Invoke = &*BB.begin();
228 
229   // Find source but never insert new load after invoke
230   for (int i = 0; i < 10; ++i) {
231     (void)IB.findOrCreateSource(BB, {Invoke}, {}, fuzzerop::anyIntType());
232     ASSERT_TRUE(!verifyModule(*M, &errs()));
233   }
234 }
235 
236 TEST(RandomIRBuilderTest, SwiftError) {
237   // Check that we never pick swifterror value as a source for operation
238   // other than load, store and call.
239 
240   LLVMContext Ctx;
241   const char *SourceCode = "declare void @use(ptr swifterror %err)"
242                            "define void @test() {\n"
243                            "entry:\n"
244                            "  %err = alloca swifterror ptr, align 8\n"
245                            "  call void @use(ptr swifterror %err)\n"
246                            "  ret void\n"
247                            "}";
248   auto M = parseAssembly(SourceCode, Ctx);
249 
250   std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};
251   RandomIRBuilder IB(Seed, Types);
252 
253   // Get first basic block of the test function
254   Function &F = *M->getFunction("test");
255   BasicBlock &BB = *F.begin();
256   Instruction *Alloca = &*BB.begin();
257 
258   fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1);
259 
260   for (int i = 0; i < 10; ++i) {
261     Value *V = IB.findOrCreateSource(BB, {Alloca}, {}, Descr.SourcePreds[0]);
262     ASSERT_FALSE(isa<AllocaInst>(V));
263   }
264 }
265 
266 TEST(RandomIRBuilderTest, dontConnectToSwitch) {
267   // Check that we never put anything into switch's case branch
268   // If we accidently put a variable, the module is invalid.
269   LLVMContext Ctx;
270   const char *SourceCode = "\n\
271     define void @test(i1 %C1, i1 %C2, i32 %I, i32 %J) { \n\
272     Entry:  \n\
273       %I.1 = add i32 %I, 42 \n\
274       %J.1 = add i32 %J, 42 \n\
275       %IJ = add i32 %I, %J \n\
276       switch i32 %I, label %Default [ \n\
277         i32 1, label %OnOne  \n\
278       ] \n\
279     Default:  \n\
280       %CIEqJ = icmp eq i32 %I.1, %J.1 \n\
281       %CISltJ = icmp slt i32 %I.1, %J.1 \n\
282       %CAnd = and i1 %C1, %C2 \n\
283       br i1 %CIEqJ, label %Default, label %Exit \n\
284     OnOne:  \n\
285       br i1 %C1, label %OnOne, label %Exit \n\
286     Exit:  \n\
287       ret void \n\
288     }";
289 
290   std::vector<Type *> Types = {Type::getInt32Ty(Ctx), Type::getInt1Ty(Ctx)};
291   RandomIRBuilder IB(Seed, Types);
292   for (int i = 0; i < 20; i++) {
293     std::unique_ptr<Module> M = parseAssembly(SourceCode, Ctx);
294     Function &F = *M->getFunction("test");
295     auto RS = makeSampler(IB.Rand, make_pointer_range(F));
296     BasicBlock *BB = RS.getSelection();
297     SmallVector<Instruction *, 32> Insts;
298     for (auto I = BB->getFirstInsertionPt(), E = BB->end(); I != E; ++I)
299       Insts.push_back(&*I);
300     if (Insts.size() < 2)
301       continue;
302     // Choose an instruction and connect to later operations.
303     size_t IP = uniform<size_t>(IB.Rand, 1, Insts.size() - 1);
304     Instruction *Inst = Insts[IP - 1];
305     auto ConnectAfter = ArrayRef(Insts).slice(IP);
306     IB.connectToSink(*BB, ConnectAfter, Inst);
307     ASSERT_FALSE(verifyModule(*M, &errs()));
308   }
309 }
310 
311 } // namespace
312