1 // Copyright (c) 2021 Alastair F. Donaldson
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/fuzz/available_instructions.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "test/fuzz/fuzz_test_util.h"
20 
21 namespace spvtools {
22 namespace fuzz {
23 namespace {
24 
TEST(AvailableInstructionsTest,BasicTest)25 TEST(AvailableInstructionsTest, BasicTest) {
26   std::string shader = R"(
27                OpCapability Shader
28           %1 = OpExtInstImport "GLSL.std.450"
29                OpMemoryModel Logical GLSL450
30                OpEntryPoint Fragment %4 "main"
31                OpExecutionMode %4 OriginUpperLeft
32                OpSource ESSL 320
33           %2 = OpTypeVoid
34           %3 = OpTypeFunction %2
35           %6 = OpTypeInt 32 1
36           %7 = OpTypePointer Function %6
37           %8 = OpTypeFloat 32
38           %9 = OpTypePointer Function %8
39          %10 = OpTypeFunction %6 %7 %9
40          %15 = OpTypeVector %8 2
41          %16 = OpTypePointer Private %15
42          %17 = OpVariable %16 Private
43          %18 = OpConstant %8 1
44          %19 = OpConstant %8 2
45          %20 = OpConstantComposite %15 %18 %19
46          %21 = OpTypeVector %8 4
47          %22 = OpTypePointer Private %21
48          %23 = OpVariable %22 Private
49          %24 = OpConstant %8 10
50          %25 = OpConstant %8 20
51          %26 = OpConstant %8 30
52          %27 = OpConstant %8 40
53          %28 = OpConstantComposite %21 %24 %25 %26 %27
54          %31 = OpTypeInt 32 0
55          %32 = OpConstant %31 0
56          %33 = OpTypePointer Private %8
57          %41 = OpTypeBool
58          %46 = OpConstant %6 1
59          %54 = OpConstant %6 10
60          %57 = OpConstant %31 3
61          %61 = OpConstant %6 0
62          %66 = OpConstant %6 3
63           %4 = OpFunction %2 None %3
64           %5 = OpLabel
65          %55 = OpVariable %7 Function
66          %56 = OpVariable %9 Function
67          %65 = OpVariable %7 Function
68          %68 = OpVariable %7 Function
69                OpStore %17 %20
70                OpStore %23 %28
71                OpStore %55 %54
72          %58 = OpAccessChain %33 %23 %57
73          %59 = OpLoad %8 %58
74                OpStore %56 %59
75          %60 = OpFunctionCall %6 %13 %55 %56
76         %100 = OpCopyObject %21 %28
77          %62 = OpSGreaterThan %41 %60 %61
78                OpSelectionMerge %64 None
79                OpBranchConditional %62 %63 %67
80          %63 = OpLabel
81                OpStore %65 %66
82         %101 = OpCopyObject %21 %28
83                OpBranch %64
84          %67 = OpLabel
85                OpStore %68 %61
86                OpBranch %69
87          %69 = OpLabel
88                OpLoopMerge %71 %72 None
89                OpBranch %73
90          %73 = OpLabel
91          %74 = OpLoad %6 %68
92          %75 = OpSLessThan %41 %74 %54
93                OpBranchConditional %75 %70 %71
94          %70 = OpLabel
95          %76 = OpLoad %6 %65
96          %77 = OpIAdd %6 %76 %46
97                OpStore %65 %77
98                OpBranch %72
99          %72 = OpLabel
100          %78 = OpLoad %6 %68
101          %79 = OpIAdd %6 %78 %46
102                OpStore %68 %79
103                OpBranch %69
104          %71 = OpLabel
105         %102 = OpCopyObject %21 %28
106                OpBranch %64
107          %64 = OpLabel
108                OpReturn
109                OpFunctionEnd
110          %13 = OpFunction %6 None %10
111          %11 = OpFunctionParameter %7
112          %12 = OpFunctionParameter %9
113          %14 = OpLabel
114          %29 = OpVariable %7 Function
115          %30 = OpLoad %6 %11
116          %34 = OpAccessChain %33 %17 %32
117          %35 = OpLoad %8 %34
118          %36 = OpConvertFToS %6 %35
119          %37 = OpIAdd %6 %30 %36
120                OpStore %29 %37
121          %38 = OpLoad %6 %11
122          %39 = OpLoad %8 %12
123          %40 = OpConvertFToS %6 %39
124          %42 = OpSLessThan %41 %38 %40
125         %103 = OpCopyObject %21 %28
126                OpSelectionMerge %44 None
127                OpBranchConditional %42 %43 %48
128          %43 = OpLabel
129          %45 = OpLoad %6 %29
130          %47 = OpIAdd %6 %45 %46
131                OpStore %29 %47
132                OpBranch %44
133          %48 = OpLabel
134          %49 = OpLoad %6 %29
135          %50 = OpISub %6 %49 %46
136                OpStore %29 %50
137                OpBranch %44
138          %44 = OpLabel
139          %51 = OpLoad %6 %29
140                OpReturnValue %51
141                OpFunctionEnd
142   )";
143 
144   const auto env = SPV_ENV_UNIVERSAL_1_3;
145   const auto consumer = nullptr;
146   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
147   spvtools::ValidatorOptions validator_options;
148   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
149                                                kConsoleMessageConsumer));
150 
151   opt::Instruction* i1 = context->get_def_use_mgr()->GetDef(55);
152   opt::Instruction* i2 = context->get_def_use_mgr()->GetDef(101);
153   opt::Instruction* i3 = &*context->cfg()->block(67)->begin();
154   opt::Instruction* i4 = context->get_def_use_mgr()->GetDef(74);
155   opt::Instruction* i5 = context->get_def_use_mgr()->GetDef(102);
156   opt::Instruction* i6 = context->get_def_use_mgr()->GetDef(30);
157   opt::Instruction* i7 = context->get_def_use_mgr()->GetDef(47);
158   opt::Instruction* i8 = context->get_def_use_mgr()->GetDef(50);
159   opt::Instruction* i9 = context->get_def_use_mgr()->GetDef(51);
160 
161   {
162     AvailableInstructions no_instructions(
163         context.get(),
164         [](opt::IRContext*, opt::Instruction*) -> bool { return false; });
165     for (auto i : {i1, i2, i3, i4, i5, i6, i7, i8, i9}) {
166       auto available = no_instructions.GetAvailableBeforeInstruction(i);
167       ASSERT_EQ(0, available.size());
168       ASSERT_TRUE(available.empty());
169     }
170   }
171   {
172     AvailableInstructions all_instructions(
173         context.get(),
174         [](opt::IRContext*, opt::Instruction*) -> bool { return true; });
175     {
176       auto available = all_instructions.GetAvailableBeforeInstruction(i1);
177       ASSERT_FALSE(available.empty());
178       ASSERT_EQ(30, available.size());
179       ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
180       ASSERT_EQ(SpvOpVariable, available[15]->opcode());
181     }
182     {
183       auto available = all_instructions.GetAvailableBeforeInstruction(i2);
184       ASSERT_FALSE(available.empty());
185       ASSERT_EQ(46, available.size());
186       ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
187       ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
188       ASSERT_EQ(SpvOpVariable, available[15]->opcode());
189       ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
190       ASSERT_EQ(SpvOpStore, available[45]->opcode());
191     }
192     {
193       auto available = all_instructions.GetAvailableBeforeInstruction(i3);
194       ASSERT_FALSE(available.empty());
195       ASSERT_EQ(45, available.size());
196       ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
197       ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
198       ASSERT_EQ(SpvOpVariable, available[15]->opcode());
199       ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
200       ASSERT_EQ(SpvOpBranchConditional, available[44]->opcode());
201     }
202     {
203       auto available = all_instructions.GetAvailableBeforeInstruction(i6);
204       ASSERT_FALSE(available.empty());
205       ASSERT_EQ(33, available.size());
206       ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
207       ASSERT_EQ(SpvOpTypeFloat, available[4]->opcode());
208       ASSERT_EQ(SpvOpTypePointer, available[8]->opcode());
209       ASSERT_EQ(SpvOpConstantComposite, available[12]->opcode());
210       ASSERT_EQ(SpvOpConstant, available[16]->opcode());
211       ASSERT_EQ(SpvOpFunctionParameter, available[30]->opcode());
212       ASSERT_EQ(SpvOpFunctionParameter, available[31]->opcode());
213       ASSERT_EQ(SpvOpVariable, available[32]->opcode());
214     }
215   }
216   {
217     AvailableInstructions vector_instructions(
218         context.get(),
219         [](opt::IRContext* ir_context, opt::Instruction* inst) -> bool {
220           return inst->type_id() != 0 && ir_context->get_type_mgr()
221                                                  ->GetType(inst->type_id())
222                                                  ->AsVector() != nullptr;
223         });
224     {
225       auto available = vector_instructions.GetAvailableBeforeInstruction(i4);
226       ASSERT_FALSE(available.empty());
227       ASSERT_EQ(3, available.size());
228       ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
229       ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
230       ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
231     }
232     {
233       auto available = vector_instructions.GetAvailableBeforeInstruction(i5);
234       ASSERT_FALSE(available.empty());
235       ASSERT_EQ(3, available.size());
236       ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
237       ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
238       ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
239     }
240     {
241       auto available = vector_instructions.GetAvailableBeforeInstruction(i6);
242       ASSERT_FALSE(available.empty());
243       ASSERT_EQ(2, available.size());
244       ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
245       ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
246     }
247   }
248   {
249     AvailableInstructions integer_add_instructions(
250         context.get(), [](opt::IRContext*, opt::Instruction* inst) -> bool {
251           return inst->opcode() == SpvOpIAdd;
252         });
253     {
254       auto available =
255           integer_add_instructions.GetAvailableBeforeInstruction(i7);
256       ASSERT_FALSE(available.empty());
257       ASSERT_EQ(1, available.size());
258       ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
259     }
260     {
261       auto available =
262           integer_add_instructions.GetAvailableBeforeInstruction(i8);
263       ASSERT_FALSE(available.empty());
264       ASSERT_EQ(1, available.size());
265       ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
266     }
267     {
268       auto available =
269           integer_add_instructions.GetAvailableBeforeInstruction(i9);
270       ASSERT_FALSE(available.empty());
271       ASSERT_EQ(1, available.size());
272       ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
273     }
274   }
275 }
276 
TEST(AvailableInstructionsTest,UnreachableBlock)277 TEST(AvailableInstructionsTest, UnreachableBlock) {
278   std::string shader = R"(
279                OpCapability Shader
280           %1 = OpExtInstImport "GLSL.std.450"
281                OpMemoryModel Logical GLSL450
282                OpEntryPoint Fragment %4 "main"
283                OpExecutionMode %4 OriginUpperLeft
284                OpSource ESSL 320
285                OpName %4 "main"
286                OpName %8 "x"
287           %2 = OpTypeVoid
288           %3 = OpTypeFunction %2
289           %6 = OpTypeInt 32 1
290           %7 = OpTypePointer Function %6
291           %9 = OpConstant %6 2
292           %4 = OpFunction %2 None %3
293           %5 = OpLabel
294           %8 = OpVariable %7 Function
295                OpStore %8 %9
296          %12 = OpLoad %6 %8
297                OpReturn
298          %10 = OpLabel
299          %11 = OpLoad %6 %8
300                OpReturn
301                OpFunctionEnd
302   )";
303 
304   const auto env = SPV_ENV_UNIVERSAL_1_3;
305   const auto consumer = nullptr;
306   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
307   spvtools::ValidatorOptions validator_options;
308   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
309                                                kConsoleMessageConsumer));
310 
311   AvailableInstructions all_instructions(
312       context.get(),
313       [](opt::IRContext*, opt::Instruction*) -> bool { return true; });
314   ASSERT_EQ(7, all_instructions
315                    .GetAvailableBeforeInstruction(
316                        context->get_def_use_mgr()->GetDef(12))
317                    .size());
318 
319 #ifndef NDEBUG
320   ASSERT_DEATH(all_instructions.GetAvailableBeforeInstruction(
321                    context->get_def_use_mgr()->GetDef(11)),
322                "Availability can only be queried for reachable instructions.");
323 #endif
324 }
325 
326 }  // namespace
327 }  // namespace fuzz
328 }  // namespace spvtools
329