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_add_loop_preheader.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(TransformationAddLoopPreheaderTest,SimpleTest)25 TEST(TransformationAddLoopPreheaderTest, SimpleTest) {
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 310
33                OpName %4 "main"
34           %2 = OpTypeVoid
35           %3 = OpTypeFunction %2
36           %6 = OpTypeBool
37           %7 = OpConstantFalse %6
38           %4 = OpFunction %2 None %3
39           %5 = OpLabel
40                OpSelectionMerge %10 None
41                OpBranchConditional %7 %8 %9
42           %8 = OpLabel
43                OpBranch %10
44           %9 = OpLabel
45                OpBranch %10
46          %10 = OpLabel
47                OpLoopMerge %12 %11 None
48                OpBranch %11
49          %11 = OpLabel
50                OpBranchConditional %7 %10 %12
51          %12 = OpLabel
52                OpLoopMerge %14 %13 None
53                OpBranch %13
54          %13 = OpLabel
55                OpBranchConditional %7 %14 %12
56          %15 = OpLabel
57                OpLoopMerge %17 %16 None
58                OpBranch %16
59          %16 = OpLabel
60                OpBranchConditional %7 %15 %17
61          %17 = OpLabel
62                OpBranch %14
63          %14 = OpLabel
64                OpReturn
65                OpFunctionEnd
66   )";
67 
68   const auto env = SPV_ENV_UNIVERSAL_1_5;
69   const auto consumer = nullptr;
70   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
71 
72   spvtools::ValidatorOptions validator_options;
73   TransformationContext transformation_context(
74       MakeUnique<FactManager>(context.get()), validator_options);
75   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
76                                                kConsoleMessageConsumer));
77 
78   // %9 is not a loop header
79   ASSERT_FALSE(TransformationAddLoopPreheader(9, 15, {}).IsApplicable(
80       context.get(), transformation_context));
81 
82   // The id %12 is not fresh
83   ASSERT_FALSE(TransformationAddLoopPreheader(10, 12, {})
84                    .IsApplicable(context.get(), transformation_context));
85 
86   // Loop header %15 is not reachable (the only predecessor is the back-edge
87   // block)
88   ASSERT_FALSE(TransformationAddLoopPreheader(15, 100, {})
89                    .IsApplicable(context.get(), transformation_context));
90 
91   auto transformation1 = TransformationAddLoopPreheader(10, 20, {});
92   ASSERT_TRUE(
93       transformation1.IsApplicable(context.get(), transformation_context));
94   ApplyAndCheckFreshIds(transformation1, context.get(),
95                         &transformation_context);
96 
97   auto transformation2 = TransformationAddLoopPreheader(12, 21, {});
98   ASSERT_TRUE(
99       transformation2.IsApplicable(context.get(), transformation_context));
100   ApplyAndCheckFreshIds(transformation2, context.get(),
101                         &transformation_context);
102 
103   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
104                                                kConsoleMessageConsumer));
105 
106   std::string after_transformations = R"(
107                OpCapability Shader
108           %1 = OpExtInstImport "GLSL.std.450"
109                OpMemoryModel Logical GLSL450
110                OpEntryPoint Fragment %4 "main"
111                OpExecutionMode %4 OriginUpperLeft
112                OpSource ESSL 310
113                OpName %4 "main"
114           %2 = OpTypeVoid
115           %3 = OpTypeFunction %2
116           %6 = OpTypeBool
117           %7 = OpConstantFalse %6
118           %4 = OpFunction %2 None %3
119           %5 = OpLabel
120                OpSelectionMerge %20 None
121                OpBranchConditional %7 %8 %9
122           %8 = OpLabel
123                OpBranch %20
124           %9 = OpLabel
125                OpBranch %20
126          %20 = OpLabel
127                OpBranch %10
128          %10 = OpLabel
129                OpLoopMerge %21 %11 None
130                OpBranch %11
131          %11 = OpLabel
132                OpBranchConditional %7 %10 %21
133          %21 = OpLabel
134                OpBranch %12
135          %12 = OpLabel
136                OpLoopMerge %14 %13 None
137                OpBranch %13
138          %13 = OpLabel
139                OpBranchConditional %7 %14 %12
140          %15 = OpLabel
141                OpLoopMerge %17 %16 None
142                OpBranch %16
143          %16 = OpLabel
144                OpBranchConditional %7 %15 %17
145          %17 = OpLabel
146                OpBranch %14
147          %14 = OpLabel
148                OpReturn
149                OpFunctionEnd
150 )";
151 
152   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
153 }
154 
TEST(TransformationAddLoopPreheaderTest,OpPhi)155 TEST(TransformationAddLoopPreheaderTest, OpPhi) {
156   std::string shader = R"(
157                OpCapability Shader
158           %1 = OpExtInstImport "GLSL.std.450"
159                OpMemoryModel Logical GLSL450
160                OpEntryPoint Fragment %4 "main"
161                OpExecutionMode %4 OriginUpperLeft
162                OpSource ESSL 310
163                OpName %4 "main"
164           %2 = OpTypeVoid
165           %3 = OpTypeFunction %2
166           %6 = OpTypeBool
167           %7 = OpConstantFalse %6
168           %4 = OpFunction %2 None %3
169           %5 = OpLabel
170          %20 = OpCopyObject %6 %7
171                OpBranch %8
172           %8 = OpLabel
173          %31 = OpPhi %6 %20 %5 %21 %9
174                OpLoopMerge %10 %9 None
175                OpBranch %9
176           %9 = OpLabel
177          %21 = OpCopyObject %6 %7
178                OpBranchConditional %7 %8 %10
179          %10 = OpLabel
180                OpSelectionMerge %13 None
181                OpBranchConditional %7 %11 %12
182          %11 = OpLabel
183          %22 = OpCopyObject %6 %7
184                OpBranch %13
185          %12 = OpLabel
186          %23 = OpCopyObject %6 %7
187                OpBranch %13
188          %13 = OpLabel
189          %32 = OpPhi %6 %22 %11 %23 %12 %24 %14
190          %33 = OpPhi %6 %7 %11 %7 %12 %24 %14
191                OpLoopMerge %15 %14 None
192                OpBranch %14
193          %14 = OpLabel
194          %24 = OpCopyObject %6 %7
195                OpBranchConditional %7 %13 %15
196          %15 = OpLabel
197                OpReturn
198                OpFunctionEnd
199   )";
200 
201   const auto env = SPV_ENV_UNIVERSAL_1_5;
202   const auto consumer = nullptr;
203   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
204 
205   spvtools::ValidatorOptions validator_options;
206   TransformationContext transformation_context(
207       MakeUnique<FactManager>(context.get()), validator_options);
208   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
209                                                kConsoleMessageConsumer));
210 
211   auto transformation1 = TransformationAddLoopPreheader(8, 40, {});
212   ASSERT_TRUE(
213       transformation1.IsApplicable(context.get(), transformation_context));
214   ApplyAndCheckFreshIds(transformation1, context.get(),
215                         &transformation_context);
216 
217   // Not enough ids for the OpPhi instructions are given
218   ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {})
219                    .IsApplicable(context.get(), transformation_context));
220 
221   // Not enough ids for the OpPhi instructions are given
222   ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {42})
223                    .IsApplicable(context.get(), transformation_context));
224 
225   // One of the ids is not fresh
226   ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {31, 42})
227                    .IsApplicable(context.get(), transformation_context));
228 
229   // One of the ids is repeated
230   ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {41, 42})
231                    .IsApplicable(context.get(), transformation_context));
232 
233   auto transformation2 = TransformationAddLoopPreheader(13, 41, {42, 43});
234   ASSERT_TRUE(
235       transformation2.IsApplicable(context.get(), transformation_context));
236   ApplyAndCheckFreshIds(transformation2, context.get(),
237                         &transformation_context);
238 
239   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
240                                                kConsoleMessageConsumer));
241 
242   std::string after_transformations = R"(
243                OpCapability Shader
244           %1 = OpExtInstImport "GLSL.std.450"
245                OpMemoryModel Logical GLSL450
246                OpEntryPoint Fragment %4 "main"
247                OpExecutionMode %4 OriginUpperLeft
248                OpSource ESSL 310
249                OpName %4 "main"
250           %2 = OpTypeVoid
251           %3 = OpTypeFunction %2
252           %6 = OpTypeBool
253           %7 = OpConstantFalse %6
254           %4 = OpFunction %2 None %3
255           %5 = OpLabel
256          %20 = OpCopyObject %6 %7
257                OpBranch %40
258          %40 = OpLabel
259                OpBranch %8
260           %8 = OpLabel
261          %31 = OpPhi %6 %20 %40 %21 %9
262                OpLoopMerge %10 %9 None
263                OpBranch %9
264           %9 = OpLabel
265          %21 = OpCopyObject %6 %7
266                OpBranchConditional %7 %8 %10
267          %10 = OpLabel
268                OpSelectionMerge %41 None
269                OpBranchConditional %7 %11 %12
270          %11 = OpLabel
271          %22 = OpCopyObject %6 %7
272                OpBranch %41
273          %12 = OpLabel
274          %23 = OpCopyObject %6 %7
275                OpBranch %41
276          %41 = OpLabel
277          %42 = OpPhi %6 %22 %11 %23 %12
278          %43 = OpPhi %6 %7 %11 %7 %12
279                OpBranch %13
280          %13 = OpLabel
281          %32 = OpPhi %6 %42 %41 %24 %14
282          %33 = OpPhi %6 %43 %41 %24 %14
283                OpLoopMerge %15 %14 None
284                OpBranch %14
285          %14 = OpLabel
286          %24 = OpCopyObject %6 %7
287                OpBranchConditional %7 %13 %15
288          %15 = OpLabel
289                OpReturn
290                OpFunctionEnd
291   )";
292 
293   ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
294 }
295 
296 }  // namespace
297 }  // namespace fuzz
298 }  // namespace spvtools
299