1 | //===- InjectorIRStrategyTest.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/ADT/DenseMap.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/Operations.h" |
15 | #include "llvm/FuzzMutate/RandomIRBuilder.h" |
16 | #include "llvm/IR/FMF.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 | #include <random> |
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<IRMutator> createInjectorMutator() { |
33 | std::vector<TypeGetter> Types{ |
34 | Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, |
35 | Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; |
36 | |
37 | // Add vector 1, 2, 3, 4, and 8. |
38 | int VectorLength[] = {1, 2, 3, 4, 8}; |
39 | std::vector<TypeGetter> BasicTypeGetters(Types); |
40 | for (auto typeGetter : BasicTypeGetters) { |
41 | for (int length : VectorLength) { |
42 | Types.push_back(x: [typeGetter, length](LLVMContext &C) { |
43 | return VectorType::get(ElementType: typeGetter(C), NumElements: length, Scalable: false); |
44 | }); |
45 | } |
46 | } |
47 | |
48 | std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; |
49 | Strategies.push_back(x: std::make_unique<InjectorIRStrategy>( |
50 | args: InjectorIRStrategy::getDefaultOps())); |
51 | |
52 | return std::make_unique<IRMutator>(args: std::move(Types), args: std::move(Strategies)); |
53 | } |
54 | |
55 | template <class Strategy> std::unique_ptr<IRMutator> createMutator() { |
56 | std::vector<TypeGetter> Types{ |
57 | Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, |
58 | Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; |
59 | |
60 | std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; |
61 | Strategies.push_back(std::make_unique<Strategy>()); |
62 | |
63 | return std::make_unique<IRMutator>(args: std::move(Types), args: std::move(Strategies)); |
64 | } |
65 | |
66 | std::unique_ptr<Module> parseAssembly(const char *Assembly, |
67 | LLVMContext &Context) { |
68 | |
69 | SMDiagnostic Error; |
70 | std::unique_ptr<Module> M = parseAssemblyString(AsmString: Assembly, Err&: Error, Context); |
71 | |
72 | std::string ErrMsg; |
73 | raw_string_ostream OS(ErrMsg); |
74 | Error.print(ProgName: "" , S&: OS); |
75 | |
76 | assert(M && !verifyModule(*M, &errs())); |
77 | return M; |
78 | } |
79 | |
80 | void IterateOnSource(StringRef Source, IRMutator &Mutator) { |
81 | LLVMContext Ctx; |
82 | |
83 | for (int i = 0; i < 10; ++i) { |
84 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
85 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
86 | |
87 | Mutator.mutateModule(M&: *M, Seed, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
88 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
89 | } |
90 | } |
91 | |
92 | static void mutateAndVerifyModule(StringRef Source, |
93 | std::unique_ptr<IRMutator> &Mutator, |
94 | int repeat = 100) { |
95 | LLVMContext Ctx; |
96 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
97 | std::mt19937 mt(Seed); |
98 | std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); |
99 | for (int i = 0; i < repeat; i++) { |
100 | Mutator->mutateModule(M&: *M, Seed: RandInt(mt), MaxSize: IRMutator::getModuleSize(M: *M) + 1024); |
101 | ASSERT_FALSE(verifyModule(*M, &errs())); |
102 | } |
103 | } |
104 | template <class Strategy> |
105 | static void mutateAndVerifyModule(StringRef Source, int repeat = 100) { |
106 | auto Mutator = createMutator<Strategy>(); |
107 | ASSERT_TRUE(Mutator); |
108 | mutateAndVerifyModule(Source, Mutator, repeat); |
109 | } |
110 | |
111 | TEST(InjectorIRStrategyTest, EmptyModule) { |
112 | // Test that we can inject into empty module |
113 | |
114 | LLVMContext Ctx; |
115 | auto M = std::make_unique<Module>(args: "M" , args&: Ctx); |
116 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
117 | |
118 | auto Mutator = createInjectorMutator(); |
119 | ASSERT_TRUE(Mutator); |
120 | |
121 | Mutator->mutateModule(M&: *M, Seed, MaxSize: IRMutator::getModuleSize(M: *M) + 1); |
122 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
123 | } |
124 | |
125 | TEST(InjectorIRStrategyTest, LargeInsertion) { |
126 | StringRef Source = "" ; |
127 | auto Mutator = createInjectorMutator(); |
128 | ASSERT_TRUE(Mutator); |
129 | mutateAndVerifyModule(Source, Mutator, repeat: 100); |
130 | } |
131 | |
132 | TEST(InjectorIRStrategyTest, InsertWMustTailCall) { |
133 | StringRef Source = "\n\ |
134 | define i1 @recursive() { \n\ |
135 | Entry: \n\ |
136 | %Ret = musttail call i1 @recursive() \n\ |
137 | ret i1 %Ret \n\ |
138 | }" ; |
139 | auto Mutator = createInjectorMutator(); |
140 | ASSERT_TRUE(Mutator); |
141 | mutateAndVerifyModule(Source, Mutator, repeat: 100); |
142 | } |
143 | |
144 | TEST(InjectorIRStrategyTest, InsertWTailCall) { |
145 | StringRef Source = "\n\ |
146 | define i1 @recursive() { \n\ |
147 | Entry: \n\ |
148 | %Ret = tail call i1 @recursive() \n\ |
149 | ret i1 %Ret \n\ |
150 | }" ; |
151 | auto Mutator = createInjectorMutator(); |
152 | ASSERT_TRUE(Mutator); |
153 | mutateAndVerifyModule(Source, Mutator, repeat: 100); |
154 | } |
155 | |
156 | TEST(InstDeleterIRStrategyTest, EmptyFunction) { |
157 | // Test that we don't crash even if we can't remove from one of the functions. |
158 | |
159 | StringRef Source = "" |
160 | "define <8 x i32> @func1() {\n" |
161 | "ret <8 x i32> undef\n" |
162 | "}\n" |
163 | "\n" |
164 | "define i32 @func2() {\n" |
165 | "%A9 = alloca i32\n" |
166 | "%L6 = load i32, i32* %A9\n" |
167 | "ret i32 %L6\n" |
168 | "}\n" ; |
169 | |
170 | auto Mutator = createMutator<InstDeleterIRStrategy>(); |
171 | ASSERT_TRUE(Mutator); |
172 | |
173 | IterateOnSource(Source, Mutator&: *Mutator); |
174 | } |
175 | |
176 | TEST(InstDeleterIRStrategyTest, PhiNodes) { |
177 | // Test that inst deleter works correctly with the phi nodes. |
178 | |
179 | LLVMContext Ctx; |
180 | StringRef Source = "\n\ |
181 | define i32 @earlyreturncrash(i32 %x) {\n\ |
182 | entry:\n\ |
183 | switch i32 %x, label %sw.epilog [\n\ |
184 | i32 1, label %sw.bb1\n\ |
185 | ]\n\ |
186 | \n\ |
187 | sw.bb1:\n\ |
188 | br label %sw.epilog\n\ |
189 | \n\ |
190 | sw.epilog:\n\ |
191 | %a.0 = phi i32 [ 7, %entry ], [ 9, %sw.bb1 ]\n\ |
192 | %b.0 = phi i32 [ 10, %entry ], [ 4, %sw.bb1 ]\n\ |
193 | ret i32 %a.0\n\ |
194 | }" ; |
195 | |
196 | auto Mutator = createMutator<InstDeleterIRStrategy>(); |
197 | ASSERT_TRUE(Mutator); |
198 | |
199 | IterateOnSource(Source, Mutator&: *Mutator); |
200 | } |
201 | |
202 | static void checkModifyNoUnsignedAndNoSignedWrap(StringRef Opc) { |
203 | LLVMContext Ctx; |
204 | std::string Source = std::string("\n\ |
205 | define i32 @test(i32 %x) {\n\ |
206 | %a = " ) + Opc.str() + |
207 | std::string(" i32 %x, 10\n\ |
208 | ret i32 %a\n\ |
209 | }" ); |
210 | |
211 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
212 | ASSERT_TRUE(Mutator); |
213 | |
214 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
215 | auto &F = *M->begin(); |
216 | auto *AddI = &*F.begin()->begin(); |
217 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
218 | bool FoundNUW = false; |
219 | bool FoundNSW = false; |
220 | for (int i = 0; i < 100; ++i) { |
221 | Mutator->mutateModule(M&: *M, Seed: Seed + i, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
222 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
223 | FoundNUW |= AddI->hasNoUnsignedWrap(); |
224 | FoundNSW |= AddI->hasNoSignedWrap(); |
225 | } |
226 | |
227 | // The mutator should have added nuw and nsw during some mutations. |
228 | EXPECT_TRUE(FoundNUW); |
229 | EXPECT_TRUE(FoundNSW); |
230 | } |
231 | TEST(InstModificationIRStrategyTest, Add) { |
232 | checkModifyNoUnsignedAndNoSignedWrap(Opc: "add" ); |
233 | } |
234 | |
235 | TEST(InstModificationIRStrategyTest, Sub) { |
236 | checkModifyNoUnsignedAndNoSignedWrap(Opc: "sub" ); |
237 | } |
238 | |
239 | TEST(InstModificationIRStrategyTest, Mul) { |
240 | checkModifyNoUnsignedAndNoSignedWrap(Opc: "mul" ); |
241 | } |
242 | |
243 | TEST(InstModificationIRStrategyTest, Shl) { |
244 | checkModifyNoUnsignedAndNoSignedWrap(Opc: "shl" ); |
245 | } |
246 | |
247 | TEST(InstModificationIRStrategyTest, ICmp) { |
248 | LLVMContext Ctx; |
249 | StringRef Source = "\n\ |
250 | define i1 @test(i32 %x) {\n\ |
251 | %a = icmp eq i32 %x, 10\n\ |
252 | ret i1 %a\n\ |
253 | }" ; |
254 | |
255 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
256 | ASSERT_TRUE(Mutator); |
257 | |
258 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
259 | auto &F = *M->begin(); |
260 | CmpInst *CI = cast<CmpInst>(Val: &*F.begin()->begin()); |
261 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
262 | bool FoundNE = false; |
263 | for (int i = 0; i < 100; ++i) { |
264 | Mutator->mutateModule(M&: *M, Seed: Seed + i, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
265 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
266 | FoundNE |= CI->getPredicate() == CmpInst::ICMP_NE; |
267 | } |
268 | |
269 | EXPECT_TRUE(FoundNE); |
270 | } |
271 | |
272 | TEST(InstModificationIRStrategyTest, FCmp) { |
273 | LLVMContext Ctx; |
274 | StringRef Source = "\n\ |
275 | define i1 @test(float %x) {\n\ |
276 | %a = fcmp oeq float %x, 10.0\n\ |
277 | ret i1 %a\n\ |
278 | }" ; |
279 | |
280 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
281 | ASSERT_TRUE(Mutator); |
282 | |
283 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
284 | auto &F = *M->begin(); |
285 | CmpInst *CI = cast<CmpInst>(Val: &*F.begin()->begin()); |
286 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
287 | bool FoundONE = false; |
288 | for (int i = 0; i < 100; ++i) { |
289 | Mutator->mutateModule(M&: *M, Seed: Seed + i, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
290 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
291 | FoundONE |= CI->getPredicate() == CmpInst::FCMP_ONE; |
292 | } |
293 | |
294 | EXPECT_TRUE(FoundONE); |
295 | } |
296 | |
297 | TEST(InstModificationIRStrategyTest, GEP) { |
298 | LLVMContext Ctx; |
299 | StringRef Source = "\n\ |
300 | define i32* @test(i32* %ptr) {\n\ |
301 | %gep = getelementptr i32, i32* %ptr, i32 10\n\ |
302 | ret i32* %gep\n\ |
303 | }" ; |
304 | |
305 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
306 | ASSERT_TRUE(Mutator); |
307 | |
308 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
309 | auto &F = *M->begin(); |
310 | GetElementPtrInst *GEP = cast<GetElementPtrInst>(Val: &*F.begin()->begin()); |
311 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
312 | bool FoundInbounds = false; |
313 | for (int i = 0; i < 100; ++i) { |
314 | Mutator->mutateModule(M&: *M, Seed: Seed + i, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
315 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
316 | FoundInbounds |= GEP->isInBounds(); |
317 | } |
318 | |
319 | EXPECT_TRUE(FoundInbounds); |
320 | } |
321 | |
322 | /// The caller has to guarantee that function argument are used in the SAME |
323 | /// place as the operand. |
324 | void VerfyOperandShuffled(StringRef Source, std::pair<int, int> ShuffleItems) { |
325 | LLVMContext Ctx; |
326 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
327 | ASSERT_TRUE(Mutator); |
328 | |
329 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
330 | auto &F = *M->begin(); |
331 | Instruction *Inst = &*F.begin()->begin(); |
332 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
333 | ASSERT_TRUE(Inst->getOperand(ShuffleItems.first) == |
334 | dyn_cast<Value>(F.getArg(ShuffleItems.first))); |
335 | ASSERT_TRUE(Inst->getOperand(ShuffleItems.second) == |
336 | dyn_cast<Value>(F.getArg(ShuffleItems.second))); |
337 | |
338 | Mutator->mutateModule(M&: *M, Seed: 0, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
339 | ASSERT_TRUE(!verifyModule(*M, &errs())); |
340 | |
341 | ASSERT_TRUE(Inst->getOperand(ShuffleItems.first) == |
342 | dyn_cast<Value>(F.getArg(ShuffleItems.second))); |
343 | ASSERT_TRUE(Inst->getOperand(ShuffleItems.second) == |
344 | dyn_cast<Value>(F.getArg(ShuffleItems.first))); |
345 | } |
346 | |
347 | TEST(InstModificationIRStrategyTest, ShuffleAnd) { |
348 | StringRef Source = "\n\ |
349 | define i32 @test(i32 %0, i32 %1) {\n\ |
350 | %add = and i32 %0, %1\n\ |
351 | ret i32 %add\n\ |
352 | }" ; |
353 | VerfyOperandShuffled(Source, ShuffleItems: {0, 1}); |
354 | } |
355 | TEST(InstModificationIRStrategyTest, ShuffleSelect) { |
356 | StringRef Source = "\n\ |
357 | define i32 @test(i1 %0, i32 %1, i32 %2) {\n\ |
358 | %select = select i1 %0, i32 %1, i32 %2\n\ |
359 | ret i32 %select\n\ |
360 | }" ; |
361 | VerfyOperandShuffled(Source, ShuffleItems: {1, 2}); |
362 | } |
363 | |
364 | void VerfyDivDidntShuffle(StringRef Source) { |
365 | LLVMContext Ctx; |
366 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
367 | ASSERT_TRUE(Mutator); |
368 | |
369 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
370 | auto &F = *M->begin(); |
371 | Instruction *Inst = &*F.begin()->begin(); |
372 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
373 | |
374 | EXPECT_TRUE(isa<Constant>(Inst->getOperand(0))); |
375 | EXPECT_TRUE(Inst->getOperand(1) == dyn_cast<Value>(F.getArg(0))); |
376 | |
377 | Mutator->mutateModule(M&: *M, Seed, MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
378 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
379 | |
380 | // Didn't shuffle. |
381 | EXPECT_TRUE(isa<Constant>(Inst->getOperand(0))); |
382 | EXPECT_TRUE(Inst->getOperand(1) == dyn_cast<Value>(F.getArg(0))); |
383 | } |
384 | TEST(InstModificationIRStrategyTest, DidntShuffleSDiv) { |
385 | StringRef Source = "\n\ |
386 | define i32 @test(i32 %0) {\n\ |
387 | %div = sdiv i32 0, %0\n\ |
388 | ret i32 %div\n\ |
389 | }" ; |
390 | VerfyDivDidntShuffle(Source); |
391 | } |
392 | TEST(InstModificationIRStrategyTest, DidntShuffleFRem) { |
393 | StringRef Source = "\n\ |
394 | define <2 x double> @test(<2 x double> %0) {\n\ |
395 | %div = frem <2 x double> <double 0.0, double 0.0>, %0\n\ |
396 | ret <2 x double> %div\n\ |
397 | }" ; |
398 | VerfyDivDidntShuffle(Source); |
399 | } |
400 | |
401 | TEST(InsertFunctionStrategy, Func) { |
402 | LLVMContext Ctx; |
403 | const char *Source = "" ; |
404 | auto Mutator = createMutator<InsertFunctionStrategy>(); |
405 | ASSERT_TRUE(Mutator); |
406 | |
407 | auto M = parseAssembly(Assembly: Source, Context&: Ctx); |
408 | srand(seed: Seed); |
409 | for (int i = 0; i < 100; i++) { |
410 | Mutator->mutateModule(M&: *M, Seed: rand(), MaxSize: 1024); |
411 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
412 | } |
413 | } |
414 | |
415 | TEST(InsertFunctionStrategy, AvoidCallingFunctionWithSpecialParam) { |
416 | LLVMContext Ctx; |
417 | StringRef Source = "\n\ |
418 | declare void @llvm.dbg.value(metadata %0, metadata %1, metadata %2)\n\ |
419 | declare i1 @llvm.experimental.gc.result.i1(token %0)\n\ |
420 | define i32 @test(i32 %0) gc \"statepoint-example\" {\n\ |
421 | ret i32 %0 \n\ |
422 | }" ; |
423 | auto Mutator = createMutator<InsertFunctionStrategy>(); |
424 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
425 | srand(seed: Seed); |
426 | for (int i = 0; i < 100; i++) { |
427 | Mutator->mutateModule(M&: *M, Seed: rand(), MaxSize: 1024); |
428 | EXPECT_TRUE(!verifyModule(*M, &errs())); |
429 | } |
430 | } |
431 | |
432 | TEST(InstModificationIRStrategy, Exact) { |
433 | LLVMContext Ctx; |
434 | StringRef Source = "\n\ |
435 | define i32 @test(i32 %a, i32 %b) {\n\ |
436 | %c = ashr i32 %a, %b \n\ |
437 | ret i32 %c\n\ |
438 | }" ; |
439 | |
440 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
441 | ASSERT_TRUE(Mutator); |
442 | |
443 | std::unique_ptr<Module> M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
444 | std::mt19937 mt(Seed); |
445 | std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); |
446 | auto &F = *M->begin(); |
447 | BinaryOperator *AShr = cast<BinaryOperator>(Val: &*F.begin()->begin()); |
448 | bool FoundExact = false; |
449 | for (int i = 0; i < 100; ++i) { |
450 | Mutator->mutateModule(M&: *M, Seed: RandInt(mt), MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
451 | ASSERT_FALSE(verifyModule(*M, &errs())); |
452 | FoundExact |= AShr->isExact(); |
453 | } |
454 | |
455 | EXPECT_TRUE(FoundExact); |
456 | } |
457 | TEST(InstModificationIRStrategy, FastMath) { |
458 | LLVMContext Ctx; |
459 | StringRef Source = "\n\ |
460 | declare [4 x <4 x double>] @vecdouble(double) \n\ |
461 | define double @test(i1 %C, double %a, double %b) { \n\ |
462 | Entry: \n\ |
463 | br i1 %C, label %True, label %False \n\ |
464 | True: \n\ |
465 | br label %Exit \n\ |
466 | False: \n\ |
467 | br label %Exit \n\ |
468 | Exit: \n\ |
469 | %PHIi32 = phi i32 [1, %True], [2, %False] \n\ |
470 | %PHIdouble = phi double [%a, %True], [%b, %False] \n\ |
471 | %Call = call [4 x <4 x double>] @vecdouble(double %PHIdouble) \n\ |
472 | %c = fneg double %PHIdouble \n\ |
473 | %s = select i1 %C, double %a, double %b \n\ |
474 | %d = fadd double %s, %c \n\ |
475 | ret double %d \n\ |
476 | }" ; |
477 | |
478 | auto Mutator = createMutator<InstModificationIRStrategy>(); |
479 | ASSERT_TRUE(Mutator); |
480 | |
481 | std::unique_ptr<Module> M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
482 | std::mt19937 mt(Seed); |
483 | std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); |
484 | DenseMap<Instruction *, bool> FPOpsHasFastMath; |
485 | for (auto &F : *M) { |
486 | for (auto &BB : F) { |
487 | for (auto &I : BB) { |
488 | Type *Ty = I.getType(); |
489 | if (Ty->isFPOrFPVectorTy() || Ty->isArrayTy()) { |
490 | FPOpsHasFastMath[&I] = false; |
491 | } |
492 | } |
493 | } |
494 | } |
495 | ASSERT_TRUE(M && !verifyModule(*M, &errs())); |
496 | for (int i = 0; i < 300; ++i) { |
497 | Mutator->mutateModule(M&: *M, Seed: RandInt(mt), MaxSize: IRMutator::getModuleSize(M: *M) + 100); |
498 | for (auto p : FPOpsHasFastMath) |
499 | FPOpsHasFastMath[p.first] |= p.first->getFastMathFlags().any(); |
500 | ASSERT_FALSE(verifyModule(*M, &errs())); |
501 | } |
502 | for (auto p : FPOpsHasFastMath) |
503 | ASSERT_TRUE(p.second); |
504 | } |
505 | |
506 | TEST(InsertCFGStrategy, CFG) { |
507 | StringRef Source = "\n\ |
508 | define i32 @test(i1 %C1, i1 %C2, i1 %C3, i16 %S1, i16 %S2, i32 %I1) { \n\ |
509 | Entry: \n\ |
510 | %I2 = add i32 %I1, 1 \n\ |
511 | %C = and i1 %C1, %C2 \n\ |
512 | br label %Body \n\ |
513 | Body: \n\ |
514 | %IB = add i32 %I1, %I2 \n\ |
515 | %CB = and i1 %C1, %C \n\ |
516 | br label %Exit \n\ |
517 | Exit: \n\ |
518 | %IE = add i32 %IB, %I2 \n\ |
519 | %CE = and i1 %CB, %C \n\ |
520 | ret i32 %IE \n\ |
521 | }" ; |
522 | mutateAndVerifyModule<InsertCFGStrategy>(Source); |
523 | } |
524 | |
525 | TEST(InsertPHIStrategy, PHI) { |
526 | StringRef Source = "\n\ |
527 | define void @test(i1 %C1, i1 %C2, i32 %I, double %FP) { \n\ |
528 | Entry: \n\ |
529 | %C = and i1 %C1, %C2 \n\ |
530 | br i1 %C, label %LoopHead, label %Exit \n\ |
531 | LoopHead: ; pred Entry, LoopBody \n\ |
532 | switch i32 %I, label %Default [ \n\ |
533 | i32 1, label %OnOne \n\ |
534 | i32 2, label %OnTwo \n\ |
535 | i32 3, label %OnThree \n\ |
536 | ] \n\ |
537 | Default: \n\ |
538 | br label %LoopBody \n\ |
539 | OnOne: ; pred LoopHead \n\ |
540 | %DFP = fmul double %FP, 2.0 \n\ |
541 | %OnOneCond = fcmp ogt double %DFP, %FP \n\ |
542 | br i1 %OnOneCond, label %LoopBody, label %Exit \n\ |
543 | OnTwo: ; pred Entry \n\ |
544 | br i1 %C1, label %OnThree, label %LoopBody \n\ |
545 | OnThree: ; pred Entry, OnTwo, OnThree \n\ |
546 | br i1 %C2, label %OnThree, label %LoopBody \n\ |
547 | LoopBody: ; pred Default, OnOne, OnTwo, OnThree \n\ |
548 | br label %LoopHead \n\ |
549 | Exit: ; pred Entry, OnOne \n\ |
550 | ret void \n\ |
551 | }" ; |
552 | mutateAndVerifyModule<InsertPHIStrategy>(Source); |
553 | } |
554 | |
555 | TEST(InsertPHIStrategy, PHIWithSameIncomingBlock) { |
556 | LLVMContext Ctx; |
557 | StringRef Source = "\n\ |
558 | define void @test(i32 %I) { \n\ |
559 | Entry: \n\ |
560 | switch i32 %I, label %Exit [ \n\ |
561 | i32 1, label %IdentCase \n\ |
562 | i32 2, label %IdentCase \n\ |
563 | i32 3, label %IdentCase \n\ |
564 | i32 4, label %IdentCase \n\ |
565 | ] \n\ |
566 | IdentCase: \n\ |
567 | br label %Exit \n\ |
568 | Exit: \n\ |
569 | ret void \n\ |
570 | }" ; |
571 | auto IPS = std::make_unique<InsertPHIStrategy>(); |
572 | RandomIRBuilder IB(Seed, {IntegerType::getInt32Ty(C&: Ctx)}); |
573 | auto M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
574 | Function &F = *M->begin(); |
575 | for (auto &BB : F) { |
576 | IPS->mutate(BB, IB); |
577 | ASSERT_FALSE(verifyModule(*M, &errs())); |
578 | } |
579 | } |
580 | |
581 | TEST(SinkInstructionStrategy, Operand) { |
582 | StringRef Source = "\n\ |
583 | define i32 @test(i1 %C1, i1 %C2, i1 %C3, i32 %I, i32 %J) { \n\ |
584 | Entry: \n\ |
585 | %I100 = add i32 %I, 100 \n\ |
586 | switch i32 %I100, label %BB0 [ \n\ |
587 | i32 42, label %BB1 \n\ |
588 | ] \n\ |
589 | BB0: \n\ |
590 | %IAJ = add i32 %I, %J \n\ |
591 | %ISJ = sub i32 %I, %J \n\ |
592 | br label %Exit \n\ |
593 | BB1: \n\ |
594 | %IJ = mul i32 %I, %J \n\ |
595 | %C = and i1 %C2, %C3 \n\ |
596 | br i1 %C, label %BB0, label %Exit \n\ |
597 | Exit: \n\ |
598 | ret i32 %I \n\ |
599 | }" ; |
600 | mutateAndVerifyModule<SinkInstructionStrategy>(Source); |
601 | } |
602 | |
603 | TEST(SinkInstructionStrategy, DoNotSinkTokenType) { |
604 | StringRef Source = "\n\ |
605 | declare ptr @fake_personality_function() \n\ |
606 | declare token @llvm.experimental.gc.statepoint.p0(i64 immarg %0, i32 immarg %1, ptr %2, i32 immarg %3, i32 immarg %4, ...) \n\ |
607 | define void @test() gc \"statepoint-example\" personality ptr @fake_personality_function { \n\ |
608 | Entry: \n\ |
609 | %token1 = call token (i64, i32, ptr, i32, i32, ...) \ |
610 | @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr addrspace(1) ()) undef, i32 0, i32 0, i32 0, i32 0) \n\ |
611 | ret void \n\ |
612 | }" ; |
613 | mutateAndVerifyModule<SinkInstructionStrategy>(Source); |
614 | } |
615 | |
616 | static void VerifyBlockShuffle(StringRef Source) { |
617 | LLVMContext Ctx; |
618 | auto Mutator = createMutator<ShuffleBlockStrategy>(); |
619 | ASSERT_TRUE(Mutator); |
620 | |
621 | std::unique_ptr<Module> M = parseAssembly(Assembly: Source.data(), Context&: Ctx); |
622 | Function *F = &*M->begin(); |
623 | DenseMap<BasicBlock *, int> PreShuffleInstCnt; |
624 | for (BasicBlock &BB : *F) { |
625 | PreShuffleInstCnt.insert(KV: {&BB, BB.size()}); |
626 | } |
627 | std::mt19937 mt(Seed); |
628 | std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX); |
629 | for (int i = 0; i < 100; i++) { |
630 | Mutator->mutateModule(M&: *M, Seed: RandInt(mt), MaxSize: IRMutator::getModuleSize(M: *M) + 1024); |
631 | for (BasicBlock &BB : *F) { |
632 | int PostShuffleIntCnt = BB.size(); |
633 | EXPECT_EQ(PostShuffleIntCnt, PreShuffleInstCnt[&BB]); |
634 | } |
635 | EXPECT_FALSE(verifyModule(*M, &errs())); |
636 | } |
637 | } |
638 | |
639 | TEST(ShuffleBlockStrategy, ShuffleBlocks) { |
640 | StringRef Source = "\n\ |
641 | define i64 @test(i1 %0, i1 %1, i1 %2, i32 %3, i32 %4) { \n\ |
642 | Entry: \n\ |
643 | %A = alloca i32, i32 8, align 4 \n\ |
644 | %E.1 = and i32 %3, %4 \n\ |
645 | %E.2 = add i32 %4 , 1 \n\ |
646 | %A.GEP.1 = getelementptr i32, ptr %A, i32 0 \n\ |
647 | %A.GEP.2 = getelementptr i32, ptr %A.GEP.1, i32 1 \n\ |
648 | %L.2 = load i32, ptr %A.GEP.2 \n\ |
649 | %L.1 = load i32, ptr %A.GEP.1 \n\ |
650 | %E.3 = sub i32 %E.2, %L.1 \n\ |
651 | %Cond.1 = icmp eq i32 %E.3, %E.2 \n\ |
652 | %Cond.2 = and i1 %0, %1 \n\ |
653 | %Cond = or i1 %Cond.1, %Cond.2 \n\ |
654 | br i1 %Cond, label %BB0, label %BB1 \n\ |
655 | BB0: \n\ |
656 | %Add = add i32 %L.1, %L.2 \n\ |
657 | %Sub = sub i32 %L.1, %L.2 \n\ |
658 | %Sub.1 = sub i32 %Sub, 12 \n\ |
659 | %Cast.1 = bitcast i32 %4 to float \n\ |
660 | %Add.2 = add i32 %3, 1 \n\ |
661 | %Cast.2 = bitcast i32 %Add.2 to float \n\ |
662 | %FAdd = fadd float %Cast.1, %Cast.2 \n\ |
663 | %Add.3 = add i32 %L.2, %L.1 \n\ |
664 | %Cast.3 = bitcast float %FAdd to i32 \n\ |
665 | %Sub.2 = sub i32 %Cast.3, %Sub.1 \n\ |
666 | %SExt = sext i32 %Cast.3 to i64 \n\ |
667 | %A.GEP.3 = getelementptr i64, ptr %A, i32 1 \n\ |
668 | store i64 %SExt, ptr %A.GEP.3 \n\ |
669 | br label %Exit \n\ |
670 | BB1: \n\ |
671 | %PHI.1 = phi i32 [0, %Entry] \n\ |
672 | %SExt.1 = sext i1 %Cond.2 to i32 \n\ |
673 | %SExt.2 = sext i1 %Cond.1 to i32 \n\ |
674 | %E.164 = zext i32 %E.1 to i64 \n\ |
675 | %E.264 = zext i32 %E.2 to i64 \n\ |
676 | %E.1264 = mul i64 %E.164, %E.264 \n\ |
677 | %E.12 = trunc i64 %E.1264 to i32 \n\ |
678 | %A.GEP.4 = getelementptr i32, ptr %A, i32 2 \n\ |
679 | %A.GEP.5 = getelementptr i32, ptr %A.GEP.4, i32 2 \n\ |
680 | store i32 %E.12, ptr %A.GEP.5 \n\ |
681 | br label %Exit \n\ |
682 | Exit: \n\ |
683 | %PHI.2 = phi i32 [%Add, %BB0], [%E.3, %BB1] \n\ |
684 | %PHI.3 = phi i64 [%SExt, %BB0], [%E.1264, %BB1] \n\ |
685 | %ZExt = zext i32 %PHI.2 to i64 \n\ |
686 | %Add.5 = add i64 %PHI.3, 3 \n\ |
687 | ret i64 %Add.5 \n\ |
688 | }" ; |
689 | VerifyBlockShuffle(Source); |
690 | } |
691 | |
692 | TEST(ShuffleBlockStrategy, ShuffleLoop) { |
693 | StringRef Source = "\n\ |
694 | define i32 @foo(i32 %Left, i32 %Right) { \n\ |
695 | Entry: \n\ |
696 | %LPtr = alloca i32, align 4 \n\ |
697 | %RPtr = alloca i32, align 4 \n\ |
698 | %RetValPtr = alloca i32, align 4 \n\ |
699 | store i32 %Left, ptr %LPtr, align 4 \n\ |
700 | store i32 %Right, ptr %RPtr, align 4 \n\ |
701 | store i32 0, ptr %RetValPtr, align 4 \n\ |
702 | br label %LoopHead \n\ |
703 | LoopHead: \n\ |
704 | %L = load i32, ptr %LPtr, align 4 \n\ |
705 | %R = load i32, ptr %RPtr, align 4 \n\ |
706 | %C = icmp slt i32 %L, %R \n\ |
707 | br i1 %C, label %LoopBody, label %Exit \n\ |
708 | LoopBody: \n\ |
709 | %OldL = load i32, ptr %LPtr, align 4 \n\ |
710 | %NewL = add nsw i32 %OldL, 1 \n\ |
711 | store i32 %NewL, ptr %LPtr, align 4 \n\ |
712 | %OldRetVal = load i32, ptr %RetValPtr, align 4 \n\ |
713 | %NewRetVal = add nsw i32 %OldRetVal, 1 \n\ |
714 | store i32 %NewRetVal, ptr %RetValPtr, align 4 \n\ |
715 | br label %LoopHead \n\ |
716 | Exit: \n\ |
717 | %RetVal = load i32, ptr %RetValPtr, align 4 \n\ |
718 | ret i32 %RetVal \n\ |
719 | }" ; |
720 | VerifyBlockShuffle(Source); |
721 | } |
722 | |
723 | TEST(AllStrategies, SkipEHPad) { |
724 | StringRef Source = "\n\ |
725 | define void @f(i32 %x) personality ptr @__CxxFrameHandler3 { \n\ |
726 | entry: \n\ |
727 | invoke void @g() to label %try.cont unwind label %catch.dispatch \n\ |
728 | catch.dispatch: \n\ |
729 | %0 = catchswitch within none [label %catch] unwind to caller \n\ |
730 | catch: \n\ |
731 | %1 = catchpad within %0 [ptr null, i32 64, ptr null] \n\ |
732 | catchret from %1 to label %try.cont \n\ |
733 | try.cont: \n\ |
734 | ret void \n\ |
735 | } \n\ |
736 | declare void @g() \n\ |
737 | declare i32 @__CxxFrameHandler3(...) \n\ |
738 | " ; |
739 | |
740 | mutateAndVerifyModule<ShuffleBlockStrategy>(Source); |
741 | mutateAndVerifyModule<InsertPHIStrategy>(Source); |
742 | mutateAndVerifyModule<InsertFunctionStrategy>(Source); |
743 | mutateAndVerifyModule<InsertCFGStrategy>(Source); |
744 | mutateAndVerifyModule<SinkInstructionStrategy>(Source); |
745 | mutateAndVerifyModule<InjectorIRStrategy>(Source); |
746 | mutateAndVerifyModule<InstModificationIRStrategy>(Source); |
747 | } |
748 | } // namespace |
749 | |