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