1 // Copyright (c) 2020 Google LLC
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/transformation_wrap_early_terminator_in_function.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationWrapEarlyTerminatorInFunctionTest,IsApplicable)26 TEST(TransformationWrapEarlyTerminatorInFunctionTest, IsApplicable) {
27 std::string shader = R"(
28 OpCapability Shader
29 OpExtension "SPV_KHR_terminate_invocation"
30 %1 = OpExtInstImport "GLSL.std.450"
31 OpMemoryModel Logical GLSL450
32 OpEntryPoint Fragment %4 "main"
33 OpExecutionMode %4 OriginUpperLeft
34 OpSource ESSL 320
35 OpName %4 "main"
36 %2 = OpTypeVoid
37 %3 = OpTypeFunction %2
38 %6 = OpTypeInt 32 1
39 %7 = OpConstant %6 0
40 %90 = OpTypeBool
41 %91 = OpConstantFalse %90
42
43 %20 = OpTypeFunction %2 %6
44 %21 = OpTypeFunction %6
45
46 %4 = OpFunction %2 None %3
47 %5 = OpLabel
48 OpSelectionMerge %11 None
49 OpSwitch %7 %11 0 %8 1 %9 2 %10
50 %8 = OpLabel
51 OpKill
52 %9 = OpLabel
53 OpUnreachable
54 %10 = OpLabel
55 OpTerminateInvocation
56 %11 = OpLabel
57 OpReturn
58 OpFunctionEnd
59
60 %30 = OpFunction %2 None %3
61 %31 = OpLabel
62 OpKill
63 OpFunctionEnd
64
65 %50 = OpFunction %2 None %3
66 %51 = OpLabel
67 OpTerminateInvocation
68 OpFunctionEnd
69
70 %60 = OpFunction %6 None %21
71 %61 = OpLabel
72 OpBranch %62
73 %62 = OpLabel
74 OpKill
75 OpFunctionEnd
76
77 %70 = OpFunction %6 None %21
78 %71 = OpLabel
79 OpUnreachable
80 OpFunctionEnd
81
82 %80 = OpFunction %2 None %20
83 %81 = OpFunctionParameter %6
84 %82 = OpLabel
85 OpTerminateInvocation
86 OpFunctionEnd
87
88 )";
89
90 const auto env = SPV_ENV_UNIVERSAL_1_4;
91 const auto consumer = nullptr;
92 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
93 spvtools::ValidatorOptions validator_options;
94 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
95 kConsoleMessageConsumer));
96 TransformationContext transformation_context(
97 MakeUnique<FactManager>(context.get()), validator_options);
98
99 // Bad: id is not fresh
100 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
101 61, MakeInstructionDescriptor(8, SpvOpKill, 0), 0)
102 .IsApplicable(context.get(), transformation_context));
103
104 // Bad: early terminator instruction descriptor does not exist
105 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
106 100, MakeInstructionDescriptor(82, SpvOpKill, 0), 0)
107 .IsApplicable(context.get(), transformation_context));
108
109 // Bad: early terminator instruction does not identify an early terminator
110 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
111 100, MakeInstructionDescriptor(5, SpvOpSelectionMerge, 0), 0)
112 .IsApplicable(context.get(), transformation_context));
113
114 // Bad: no wrapper function is available
115 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
116 100, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0)
117 .IsApplicable(context.get(), transformation_context));
118
119 // Bad: returned value does not exist
120 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
121 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 1000)
122 .IsApplicable(context.get(), transformation_context));
123
124 // Bad: returned value does not have a type
125 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
126 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 61)
127 .IsApplicable(context.get(), transformation_context));
128
129 // Bad: returned value type does not match
130 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
131 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 91)
132 .IsApplicable(context.get(), transformation_context));
133
134 // Bad: returned value is not available
135 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
136 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 81)
137 .IsApplicable(context.get(), transformation_context));
138
139 // Bad: the OpKill being targeted is in the only available wrapper; we cannot
140 // have the wrapper call itself.
141 ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
142 100, MakeInstructionDescriptor(31, SpvOpKill, 0), 0)
143 .IsApplicable(context.get(), transformation_context));
144 }
145
TEST(TransformationWrapEarlyTerminatorInFunctionTest,Apply)146 TEST(TransformationWrapEarlyTerminatorInFunctionTest, Apply) {
147 std::string shader = R"(
148 OpCapability Shader
149 OpExtension "SPV_KHR_terminate_invocation"
150 %1 = OpExtInstImport "GLSL.std.450"
151 OpMemoryModel Logical GLSL450
152 OpEntryPoint Fragment %4 "main"
153 OpExecutionMode %4 OriginUpperLeft
154 OpSource ESSL 320
155 OpName %4 "main"
156 %2 = OpTypeVoid
157 %3 = OpTypeFunction %2
158 %6 = OpTypeInt 32 1
159 %7 = OpConstant %6 0
160
161 %20 = OpTypeFunction %2 %6
162 %21 = OpTypeFunction %6
163
164 %4 = OpFunction %2 None %3
165 %5 = OpLabel
166 OpSelectionMerge %11 None
167 OpSwitch %7 %11 0 %8 1 %9 2 %10
168 %8 = OpLabel
169 OpKill
170 %9 = OpLabel
171 OpUnreachable
172 %10 = OpLabel
173 OpTerminateInvocation
174 %11 = OpLabel
175 OpReturn
176 OpFunctionEnd
177
178 %30 = OpFunction %2 None %3
179 %31 = OpLabel
180 OpKill
181 OpFunctionEnd
182
183 %40 = OpFunction %2 None %3
184 %41 = OpLabel
185 OpUnreachable
186 OpFunctionEnd
187
188 %50 = OpFunction %2 None %3
189 %51 = OpLabel
190 OpTerminateInvocation
191 OpFunctionEnd
192
193 %60 = OpFunction %2 None %3
194 %61 = OpLabel
195 OpBranch %62
196 %62 = OpLabel
197 OpKill
198 OpFunctionEnd
199
200 %70 = OpFunction %6 None %21
201 %71 = OpLabel
202 OpUnreachable
203 OpFunctionEnd
204
205 %80 = OpFunction %2 None %20
206 %81 = OpFunctionParameter %6
207 %82 = OpLabel
208 OpTerminateInvocation
209 OpFunctionEnd
210 )";
211
212 const auto env = SPV_ENV_UNIVERSAL_1_4;
213 const auto consumer = nullptr;
214 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
215 spvtools::ValidatorOptions validator_options;
216 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
217 kConsoleMessageConsumer));
218 TransformationContext transformation_context(
219 MakeUnique<FactManager>(context.get()), validator_options);
220
221 for (auto& transformation :
222 {TransformationWrapEarlyTerminatorInFunction(
223 100, MakeInstructionDescriptor(8, SpvOpKill, 0), 0),
224 TransformationWrapEarlyTerminatorInFunction(
225 101, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0),
226 TransformationWrapEarlyTerminatorInFunction(
227 102, MakeInstructionDescriptor(10, SpvOpTerminateInvocation, 0), 0),
228 TransformationWrapEarlyTerminatorInFunction(
229 103, MakeInstructionDescriptor(62, SpvOpKill, 0), 0),
230 TransformationWrapEarlyTerminatorInFunction(
231 104, MakeInstructionDescriptor(71, SpvOpUnreachable, 0), 7),
232 TransformationWrapEarlyTerminatorInFunction(
233 105, MakeInstructionDescriptor(82, SpvOpTerminateInvocation, 0),
234 0)}) {
235 ASSERT_TRUE(
236 transformation.IsApplicable(context.get(), transformation_context));
237 ApplyAndCheckFreshIds(transformation, context.get(),
238 &transformation_context);
239 }
240 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
241 kConsoleMessageConsumer));
242
243 std::string after_transformation = R"(
244 OpCapability Shader
245 OpExtension "SPV_KHR_terminate_invocation"
246 %1 = OpExtInstImport "GLSL.std.450"
247 OpMemoryModel Logical GLSL450
248 OpEntryPoint Fragment %4 "main"
249 OpExecutionMode %4 OriginUpperLeft
250 OpSource ESSL 320
251 OpName %4 "main"
252 %2 = OpTypeVoid
253 %3 = OpTypeFunction %2
254 %6 = OpTypeInt 32 1
255 %7 = OpConstant %6 0
256
257 %20 = OpTypeFunction %2 %6
258 %21 = OpTypeFunction %6
259
260 %4 = OpFunction %2 None %3
261 %5 = OpLabel
262 OpSelectionMerge %11 None
263 OpSwitch %7 %11 0 %8 1 %9 2 %10
264 %8 = OpLabel
265 %100 = OpFunctionCall %2 %30
266 OpReturn
267 %9 = OpLabel
268 %101 = OpFunctionCall %2 %40
269 OpReturn
270 %10 = OpLabel
271 %102 = OpFunctionCall %2 %50
272 OpReturn
273 %11 = OpLabel
274 OpReturn
275 OpFunctionEnd
276
277 %30 = OpFunction %2 None %3
278 %31 = OpLabel
279 OpKill
280 OpFunctionEnd
281
282 %40 = OpFunction %2 None %3
283 %41 = OpLabel
284 OpUnreachable
285 OpFunctionEnd
286
287 %50 = OpFunction %2 None %3
288 %51 = OpLabel
289 OpTerminateInvocation
290 OpFunctionEnd
291
292 %60 = OpFunction %2 None %3
293 %61 = OpLabel
294 OpBranch %62
295 %62 = OpLabel
296 %103 = OpFunctionCall %2 %30
297 OpReturn
298 OpFunctionEnd
299
300 %70 = OpFunction %6 None %21
301 %71 = OpLabel
302 %104 = OpFunctionCall %2 %40
303 OpReturnValue %7
304 OpFunctionEnd
305
306 %80 = OpFunction %2 None %20
307 %81 = OpFunctionParameter %6
308 %82 = OpLabel
309 %105 = OpFunctionCall %2 %50
310 OpReturn
311 OpFunctionEnd
312 )";
313 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
314 }
315
316 } // namespace
317 } // namespace fuzz
318 } // namespace spvtools
319