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