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