1 // Copyright (c) 2016 Google Inc.
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 <string>
16 #include <tuple>
17 #include <unordered_set>
18 #include <utility>
19 #include <vector>
20 
21 #include "test/opt/assembly_builder.h"
22 #include "test/opt/pass_fixture.h"
23 #include "test/opt/pass_utils.h"
24 
25 namespace spvtools {
26 namespace opt {
27 namespace {
28 
29 // Returns the types defining instructions commonly used in many tests.
CommonTypes()30 std::vector<std::string> CommonTypes() {
31   return std::vector<std::string>{
32       // clang-format off
33     // scalar types
34     "%bool = OpTypeBool",
35     "%uint = OpTypeInt 32 0",
36     "%int = OpTypeInt 32 1",
37     "%uint64 = OpTypeInt 64 0",
38     "%int64 = OpTypeInt 64 1",
39     "%float = OpTypeFloat 32",
40     "%double = OpTypeFloat 64",
41     // vector types
42     "%v2bool = OpTypeVector %bool 2",
43     "%v2uint = OpTypeVector %uint 2",
44     "%v2int = OpTypeVector %int 2",
45     "%v3int = OpTypeVector %int 3",
46     "%v4int = OpTypeVector %int 4",
47     "%v2float = OpTypeVector %float 2",
48     "%v3float = OpTypeVector %float 3",
49     "%v2double = OpTypeVector %double 2",
50     // struct types
51     "%inner_struct = OpTypeStruct %bool %float",
52     "%outer_struct = OpTypeStruct %inner_struct %int %double",
53     "%flat_struct = OpTypeStruct %bool %int %float %double",
54     // variable pointer types
55     "%_pf_bool = OpTypePointer Function %bool",
56     "%_pf_uint = OpTypePointer Function %uint",
57     "%_pf_int = OpTypePointer Function %int",
58     "%_pf_uint64 = OpTypePointer Function %uint64",
59     "%_pf_int64 = OpTypePointer Function %int64",
60     "%_pf_float = OpTypePointer Function %float",
61     "%_pf_double = OpTypePointer Function %double",
62     "%_pf_v2int = OpTypePointer Function %v2int",
63     "%_pf_v3int = OpTypePointer Function %v3int",
64     "%_pf_v4int = OpTypePointer Function %v4int",
65     "%_pf_v2float = OpTypePointer Function %v2float",
66     "%_pf_v3float = OpTypePointer Function %v3float",
67     "%_pf_v2double = OpTypePointer Function %v2double",
68     "%_pf_inner_struct = OpTypePointer Function %inner_struct",
69     "%_pf_outer_struct = OpTypePointer Function %outer_struct",
70     "%_pf_flat_struct = OpTypePointer Function %flat_struct",
71       // clang-format on
72   };
73 }
74 
75 // A helper function to strip OpName instructions from the given string of
76 // disassembly code and put those debug instructions to a set. Returns the
77 // string with all OpName instruction stripped and a set of OpName
78 // instructions.
79 std::tuple<std::string, std::unordered_set<std::string>>
StripOpNameInstructionsToSet(const std::string & str)80 StripOpNameInstructionsToSet(const std::string& str) {
81   std::stringstream ss(str);
82   std::ostringstream oss;
83   std::string inst_str;
84   std::unordered_set<std::string> opname_instructions;
85   while (std::getline(ss, inst_str, '\n')) {
86     if (inst_str.find("OpName %") == std::string::npos) {
87       oss << inst_str << '\n';
88     } else {
89       opname_instructions.insert(inst_str);
90     }
91   }
92   return std::make_tuple(oss.str(), std::move(opname_instructions));
93 }
94 
95 // The test fixture for all tests of UnifyConstantPass. This fixture defines
96 // the rule of checking: all the optimized code should be exactly the same as
97 // the expected code, except the OpName instructions, which can be different in
98 // order.
99 template <typename T>
100 class UnifyConstantTest : public PassTest<T> {
101  protected:
102   // Runs UnifyConstantPass on the code built from the given |test_builder|,
103   // and checks whether the optimization result matches with the code built
104   // from |expected_builder|.
Check(const AssemblyBuilder & expected_builder,const AssemblyBuilder & test_builder)105   void Check(const AssemblyBuilder& expected_builder,
106              const AssemblyBuilder& test_builder) {
107     // unoptimized code
108     const std::string original_before_strip = test_builder.GetCode();
109     std::string original_without_opnames;
110     std::unordered_set<std::string> original_opnames;
111     std::tie(original_without_opnames, original_opnames) =
112         StripOpNameInstructionsToSet(original_before_strip);
113 
114     // expected code
115     std::string expected_without_opnames;
116     std::unordered_set<std::string> expected_opnames;
117     std::tie(expected_without_opnames, expected_opnames) =
118         StripOpNameInstructionsToSet(expected_builder.GetCode());
119 
120     // optimized code
121     std::string optimized_before_strip;
122     auto status = Pass::Status::SuccessWithoutChange;
123     std::tie(optimized_before_strip, status) =
124         this->template SinglePassRunAndDisassemble<UnifyConstantPass>(
125             test_builder.GetCode(),
126             /* skip_nop = */ true, /* do_validation = */ false);
127     std::string optimized_without_opnames;
128     std::unordered_set<std::string> optimized_opnames;
129     std::tie(optimized_without_opnames, optimized_opnames) =
130         StripOpNameInstructionsToSet(optimized_before_strip);
131 
132     // Flag "status" should be returned correctly.
133     EXPECT_NE(Pass::Status::Failure, status);
134     EXPECT_EQ(expected_without_opnames == original_without_opnames,
135               status == Pass::Status::SuccessWithoutChange);
136     // Code except OpName instructions should be exactly the same.
137     EXPECT_EQ(expected_without_opnames, optimized_without_opnames);
138     // OpName instructions can be in different order, but the content must be
139     // the same.
140     EXPECT_EQ(expected_opnames, optimized_opnames);
141   }
142 };
143 
144 using UnifyFrontEndConstantSingleTest =
145     UnifyConstantTest<PassTest<::testing::Test>>;
146 
TEST_F(UnifyFrontEndConstantSingleTest,Basic)147 TEST_F(UnifyFrontEndConstantSingleTest, Basic) {
148   AssemblyBuilder test_builder;
149   AssemblyBuilder expected_builder;
150 
151   test_builder
152       .AppendTypesConstantsGlobals({
153           "%uint = OpTypeInt 32 0", "%_pf_uint = OpTypePointer Function %uint",
154           "%unsigned_1 = OpConstant %uint 1",
155           "%unsigned_1_duplicate = OpConstant %uint 1",  // duplicated constant
156       })
157       .AppendInMain({
158           "%uint_var = OpVariable %_pf_uint Function",
159           "OpStore %uint_var %unsigned_1_duplicate",
160       });
161 
162   expected_builder
163       .AppendTypesConstantsGlobals({
164           "%uint = OpTypeInt 32 0",
165           "%_pf_uint = OpTypePointer Function %uint",
166           "%unsigned_1 = OpConstant %uint 1",
167       })
168       .AppendInMain({
169           "%uint_var = OpVariable %_pf_uint Function",
170           "OpStore %uint_var %unsigned_1",
171       })
172       .AppendNames({
173           "OpName %unsigned_1 \"unsigned_1_duplicate\"",  // the OpName
174                                                           // instruction of the
175                                                           // removed duplicated
176                                                           // constant won't be
177                                                           // erased.
178       });
179   Check(expected_builder, test_builder);
180 }
181 
TEST_F(UnifyFrontEndConstantSingleTest,SkipWhenResultIdHasDecorations)182 TEST_F(UnifyFrontEndConstantSingleTest, SkipWhenResultIdHasDecorations) {
183   AssemblyBuilder test_builder;
184   AssemblyBuilder expected_builder;
185 
186   test_builder
187       .AppendAnnotations({
188           // So far we don't have valid decorations for constants. This is
189           // preparing for the future updates of SPIR-V.
190           // TODO(qining): change to a valid decoration once they are available.
191           "OpDecorate %f_1 RelaxedPrecision",
192           "OpDecorate %f_2_dup RelaxedPrecision",
193       })
194       .AppendTypesConstantsGlobals({
195           // clang-format off
196           "%float = OpTypeFloat 32",
197           "%_pf_float = OpTypePointer Function %float",
198           "%f_1 = OpConstant %float 1",
199           // %f_1 has decoration, so %f_1 will not be used to replace %f_1_dup.
200           "%f_1_dup = OpConstant %float 1",
201           "%f_2 = OpConstant %float 2",
202           // %_2_dup has decoration, so %f_2 will not replace %f_2_dup.
203           "%f_2_dup = OpConstant %float 2",
204           // no decoration for %f_3 or %f_3_dup, %f_3_dup should be replaced.
205           "%f_3 = OpConstant %float 3",
206           "%f_3_dup = OpConstant %float 3",
207           // clang-format on
208       })
209       .AppendInMain({
210           // clang-format off
211           "%f_var = OpVariable %_pf_float Function",
212           "OpStore %f_var %f_1_dup",
213           "OpStore %f_var %f_2_dup",
214           "OpStore %f_var %f_3_dup",
215           // clang-format on
216       });
217 
218   expected_builder
219       .AppendAnnotations({
220           "OpDecorate %f_1 RelaxedPrecision",
221           "OpDecorate %f_2_dup RelaxedPrecision",
222       })
223       .AppendTypesConstantsGlobals({
224           // clang-format off
225           "%float = OpTypeFloat 32",
226           "%_pf_float = OpTypePointer Function %float",
227           "%f_1 = OpConstant %float 1",
228           "%f_1_dup = OpConstant %float 1",
229           "%f_2 = OpConstant %float 2",
230           "%f_2_dup = OpConstant %float 2",
231           "%f_3 = OpConstant %float 3",
232           // clang-format on
233       })
234       .AppendInMain({
235           // clang-format off
236           "%f_var = OpVariable %_pf_float Function",
237           "OpStore %f_var %f_1_dup",
238           "OpStore %f_var %f_2_dup",
239           "OpStore %f_var %f_3",
240           // clang-format on
241       })
242       .AppendNames({
243           "OpName %f_3 \"f_3_dup\"",
244       });
245 
246   Check(expected_builder, test_builder);
247 }
248 
TEST_F(UnifyFrontEndConstantSingleTest,UnifyWithDecorationOnTypes)249 TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) {
250   AssemblyBuilder test_builder;
251   AssemblyBuilder expected_builder;
252 
253   test_builder
254       .AppendAnnotations({
255           "OpMemberDecorate %flat_d 1 RelaxedPrecision",
256       })
257       .AppendTypesConstantsGlobals({
258           // clang-format off
259           "%int = OpTypeInt 32 1",
260           "%float = OpTypeFloat 32",
261           "%flat = OpTypeStruct %int %float",
262           "%_pf_flat = OpTypePointer Function %flat",
263           // decorated flat struct
264           "%flat_d = OpTypeStruct %int %float",
265           "%_pf_flat_d = OpTypePointer Function %flat_d",
266           // perserved contants. %flat_1 and %flat_d has same members, but
267           // their type are different in decorations, so they should not be
268           // used to replace each other.
269           "%int_1 = OpConstant %int 1",
270           "%float_1 = OpConstant %float 1",
271           "%flat_1 = OpConstantComposite %flat %int_1 %float_1",
272           "%flat_d_1 = OpConstantComposite %flat_d %int_1 %float_1",
273           // duplicated constants.
274           "%flat_1_dup = OpConstantComposite %flat %int_1 %float_1",
275           "%flat_d_1_dup = OpConstantComposite %flat_d %int_1 %float_1",
276           // clang-format on
277       })
278       .AppendInMain({
279           "%flat_var = OpVariable %_pf_flat Function",
280           "OpStore %flat_var %flat_1_dup",
281           "%flat_d_var = OpVariable %_pf_flat_d Function",
282           "OpStore %flat_d_var %flat_d_1_dup",
283       });
284 
285   expected_builder
286       .AppendAnnotations({
287           "OpMemberDecorate %flat_d 1 RelaxedPrecision",
288       })
289       .AppendTypesConstantsGlobals({
290           // clang-format off
291           "%int = OpTypeInt 32 1",
292           "%float = OpTypeFloat 32",
293           "%flat = OpTypeStruct %int %float",
294           "%_pf_flat = OpTypePointer Function %flat",
295           // decorated flat struct
296           "%flat_d = OpTypeStruct %int %float",
297           "%_pf_flat_d = OpTypePointer Function %flat_d",
298           "%int_1 = OpConstant %int 1",
299           "%float_1 = OpConstant %float 1",
300           "%flat_1 = OpConstantComposite %flat %int_1 %float_1",
301           "%flat_d_1 = OpConstantComposite %flat_d %int_1 %float_1",
302           // clang-format on
303       })
304       .AppendInMain({
305           "%flat_var = OpVariable %_pf_flat Function",
306           "OpStore %flat_var %flat_1",
307           "%flat_d_var = OpVariable %_pf_flat_d Function",
308           "OpStore %flat_d_var %flat_d_1",
309       })
310       .AppendNames({
311           "OpName %flat_1 \"flat_1_dup\"",
312           "OpName %flat_d_1 \"flat_d_1_dup\"",
313       });
314 
315   Check(expected_builder, test_builder);
316 }
317 
318 struct UnifyConstantTestCase {
319   // preserved constants.
320   std::vector<std::string> preserved_consts;
321   // expected uses of the preserved constants.
322   std::vector<std::string> use_preserved_consts;
323   // duplicated constants of the preserved constants.
324   std::vector<std::string> duplicate_consts;
325   // uses of the duplicated constants, expected to be updated to use the
326   // preserved constants.
327   std::vector<std::string> use_duplicate_consts;
328   // The updated OpName instructions that originally refer to duplicated
329   // constants.
330   std::vector<std::string> remapped_names;
331 };
332 
333 using UnifyFrontEndConstantParamTest = UnifyConstantTest<
334     PassTest<::testing::TestWithParam<UnifyConstantTestCase>>>;
335 
TEST_P(UnifyFrontEndConstantParamTest,TestCase)336 TEST_P(UnifyFrontEndConstantParamTest, TestCase) {
337   auto& tc = GetParam();
338   AssemblyBuilder test_builder;
339   AssemblyBuilder expected_builder;
340   test_builder.AppendTypesConstantsGlobals(CommonTypes());
341   expected_builder.AppendTypesConstantsGlobals(CommonTypes());
342 
343   test_builder.AppendTypesConstantsGlobals(tc.preserved_consts)
344       .AppendTypesConstantsGlobals(tc.duplicate_consts)
345       .AppendInMain(tc.use_duplicate_consts);
346 
347   // Duplicated constants are killed in the expected output, and the debug
348   // instructions attached to those duplicated instructions will be migrated to
349   // the corresponding preserved constants.
350   expected_builder.AppendTypesConstantsGlobals(tc.preserved_consts)
351       .AppendInMain(tc.use_preserved_consts)
352       .AppendNames(tc.remapped_names);
353 
354   Check(expected_builder, test_builder);
355 }
356 
357 INSTANTIATE_TEST_CASE_P(Case, UnifyFrontEndConstantParamTest,
358                         ::testing::ValuesIn(std::vector<UnifyConstantTestCase>({
359                             // clang-format off
360         // basic tests for scalar constants
361         {
362           // preserved constants
363           {
364             "%bool_true = OpConstantTrue %bool",
365             "%signed_1 = OpConstant %int 1",
366             "%signed_minus_1 = OpConstant %int64 -1",
367             "%unsigned_max = OpConstant %uint64 18446744073709551615",
368             "%float_1 = OpConstant %float 1",
369             "%double_1 = OpConstant %double 1",
370           },
371           // use preserved constants in main
372           {
373             "%bool_var = OpVariable %_pf_bool Function",
374             "OpStore %bool_var %bool_true",
375             "%int_var = OpVariable %_pf_int Function",
376             "OpStore %int_var %signed_1",
377             "%int64_var = OpVariable %_pf_int64 Function",
378             "OpStore %int64_var %signed_minus_1",
379             "%uint64_var = OpVariable %_pf_uint64 Function",
380             "OpStore %uint64_var %unsigned_max",
381             "%float_var = OpVariable %_pf_float Function",
382             "OpStore %float_var %float_1",
383             "%double_var = OpVariable %_pf_double Function",
384             "OpStore %double_var %double_1",
385           },
386           // duplicated constants
387           {
388             "%bool_true_duplicate = OpConstantTrue %bool",
389             "%signed_1_duplicate = OpConstant %int 1",
390             "%signed_minus_1_duplicate = OpConstant %int64 -1",
391             "%unsigned_max_duplicate = OpConstant %uint64 18446744073709551615",
392             "%float_1_duplicate = OpConstant %float 1",
393             "%double_1_duplicate = OpConstant %double 1",
394           },
395           // use duplicated constants in main
396           {
397             "%bool_var = OpVariable %_pf_bool Function",
398             "OpStore %bool_var %bool_true_duplicate",
399             "%int_var = OpVariable %_pf_int Function",
400             "OpStore %int_var %signed_1_duplicate",
401             "%int64_var = OpVariable %_pf_int64 Function",
402             "OpStore %int64_var %signed_minus_1_duplicate",
403             "%uint64_var = OpVariable %_pf_uint64 Function",
404             "OpStore %uint64_var %unsigned_max_duplicate",
405             "%float_var = OpVariable %_pf_float Function",
406             "OpStore %float_var %float_1_duplicate",
407             "%double_var = OpVariable %_pf_double Function",
408             "OpStore %double_var %double_1_duplicate",
409           },
410           // remapped names
411           {
412             "OpName %bool_true \"bool_true_duplicate\"",
413             "OpName %signed_1 \"signed_1_duplicate\"",
414             "OpName %signed_minus_1 \"signed_minus_1_duplicate\"",
415             "OpName %unsigned_max \"unsigned_max_duplicate\"",
416             "OpName %float_1 \"float_1_duplicate\"",
417             "OpName %double_1 \"double_1_duplicate\"",
418           },
419         },
420         // NaN in different bit patterns should not be unified, but the ones
421         // using same bit pattern should be unified.
422         {
423           // preserved constants
424           {
425             "%float_nan_1 = OpConstant %float 0x1.8p+128", // !2143289344, 7FC00000
426             "%float_nan_2 = OpConstant %float 0x1.800002p+128",// !2143289345 7FC00001
427           },
428           // use preserved constants in main
429           {
430             "%float_var = OpVariable %_pf_float Function",
431             "OpStore %float_var %float_nan_1",
432             "OpStore %float_var %float_nan_2",
433           },
434           // duplicated constants
435           {
436             "%float_nan_1_duplicate = OpConstant %float 0x1.8p+128", // !2143289344, 7FC00000
437             "%float_nan_2_duplicate = OpConstant %float 0x1.800002p+128",// !2143289345, 7FC00001
438           },
439           // use duplicated constants in main
440           {
441             "%float_var = OpVariable %_pf_float Function",
442             "OpStore %float_var %float_nan_1_duplicate",
443             "OpStore %float_var %float_nan_2_duplicate",
444           },
445           // remapped names
446           {
447             "OpName %float_nan_1 \"float_nan_1_duplicate\"",
448             "OpName %float_nan_2 \"float_nan_2_duplicate\"",
449           },
450         },
451         // null values
452         {
453           // preserved constants
454           {
455             "%bool_null = OpConstantNull %bool",
456             "%signed_null = OpConstantNull %int",
457             "%signed_64_null = OpConstantNull %int64",
458             "%float_null = OpConstantNull %float",
459             "%double_null = OpConstantNull %double",
460             // zero-valued constants will not be unified with the equivalent
461             // null constants.
462             "%signed_zero = OpConstant %int 0",
463           },
464           // use preserved constants in main
465           {
466             "%bool_var = OpVariable %_pf_bool Function",
467             "OpStore %bool_var %bool_null",
468             "%int_var = OpVariable %_pf_int Function",
469             "OpStore %int_var %signed_null",
470             "%int64_var = OpVariable %_pf_int64 Function",
471             "OpStore %int64_var %signed_64_null",
472             "%float_var = OpVariable %_pf_float Function",
473             "OpStore %float_var %float_null",
474             "%double_var = OpVariable %_pf_double Function",
475             "OpStore %double_var %double_null",
476           },
477           // duplicated constants
478           {
479             "%bool_null_duplicate = OpConstantNull %bool",
480             "%signed_null_duplicate = OpConstantNull %int",
481             "%signed_64_null_duplicate = OpConstantNull %int64",
482             "%float_null_duplicate = OpConstantNull %float",
483             "%double_null_duplicate = OpConstantNull %double",
484           },
485           // use duplicated constants in main
486           {
487             "%bool_var = OpVariable %_pf_bool Function",
488             "OpStore %bool_var %bool_null_duplicate",
489             "%int_var = OpVariable %_pf_int Function",
490             "OpStore %int_var %signed_null_duplicate",
491             "%int64_var = OpVariable %_pf_int64 Function",
492             "OpStore %int64_var %signed_64_null_duplicate",
493             "%float_var = OpVariable %_pf_float Function",
494             "OpStore %float_var %float_null_duplicate",
495             "%double_var = OpVariable %_pf_double Function",
496             "OpStore %double_var %double_null_duplicate",
497           },
498           // remapped names
499           {
500             "OpName %bool_null \"bool_null_duplicate\"",
501             "OpName %signed_null \"signed_null_duplicate\"",
502             "OpName %signed_64_null \"signed_64_null_duplicate\"",
503             "OpName %float_null \"float_null_duplicate\"",
504             "OpName %double_null \"double_null_duplicate\"",
505           },
506         },
507         // constant sampler
508         {
509           // preserved constants
510           {
511             "%sampler = OpTypeSampler",
512             "%_pf_sampler = OpTypePointer Function %sampler",
513             "%sampler_1 = OpConstantSampler %sampler Repeat 0 Linear",
514           },
515           // use preserved constants in main
516           {
517             "%sampler_var = OpVariable %_pf_sampler Function",
518             "OpStore %sampler_var %sampler_1",
519           },
520           // duplicated constants
521           {
522             "%sampler_1_duplicate = OpConstantSampler %sampler Repeat 0 Linear",
523           },
524           // use duplicated constants in main
525           {
526             "%sampler_var = OpVariable %_pf_sampler Function",
527             "OpStore %sampler_var %sampler_1_duplicate",
528           },
529           // remapped names
530           {
531             "OpName %sampler_1 \"sampler_1_duplicate\"",
532           },
533         },
534         // duplicate vector built from same ids.
535         {
536           // preserved constants
537           {
538             "%signed_1 = OpConstant %int 1",
539             "%signed_2 = OpConstant %int 2",
540             "%signed_3 = OpConstant %int 3",
541             "%signed_4 = OpConstant %int 4",
542             "%vec = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4",
543           },
544           // use preserved constants in main
545           {
546             "%vec_var = OpVariable %_pf_v4int Function",
547             "OpStore %vec_var %vec",
548           },
549           // duplicated constants
550           {
551             "%vec_duplicate = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4",
552           },
553           // use duplicated constants in main
554           {
555             "%vec_var = OpVariable %_pf_v4int Function",
556             "OpStore %vec_var %vec_duplicate",
557           },
558           // remapped names
559           {
560             "OpName %vec \"vec_duplicate\"",
561           }
562         },
563         // duplicate vector built from duplicated ids.
564         {
565           // preserved constants
566           {
567             "%signed_1 = OpConstant %int 1",
568             "%signed_2 = OpConstant %int 2",
569             "%signed_3 = OpConstant %int 3",
570             "%signed_4 = OpConstant %int 4",
571             "%vec = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4",
572           },
573           // use preserved constants in main
574           {
575             "%vec_var = OpVariable %_pf_v4int Function",
576             "OpStore %vec_var %vec",
577           },
578           // duplicated constants
579           {
580             "%signed_3_duplicate = OpConstant %int 3",
581             "%signed_4_duplicate = OpConstant %int 4",
582             "%vec_duplicate = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3_duplicate %signed_4_duplicate",
583           },
584           // use duplicated constants in main
585           {
586             "%vec_var = OpVariable %_pf_v4int Function",
587             "OpStore %vec_var %vec_duplicate",
588           },
589           // remapped names
590           {
591             "OpName %signed_3 \"signed_3_duplicate\"",
592             "OpName %signed_4 \"signed_4_duplicate\"",
593             "OpName %vec \"vec_duplicate\"",
594           },
595         },
596         // flat struct
597         {
598           // preserved constants
599           {
600             "%bool_true = OpConstantTrue %bool",
601             "%signed_1 = OpConstant %int 1",
602             "%float_1 = OpConstant %float 1",
603             "%double_1 = OpConstant %double 1",
604             "%s = OpConstantComposite %flat_struct %bool_true %signed_1 %float_1 %double_1",
605           },
606           // use preserved constants in main
607           {
608             "%s_var = OpVariable %_pf_flat_struct Function",
609             "OpStore %s_var %s",
610           },
611           // duplicated constants
612           {
613             "%float_1_duplicate = OpConstant %float 1",
614             "%double_1_duplicate = OpConstant %double 1",
615             "%s_duplicate = OpConstantComposite %flat_struct %bool_true %signed_1 %float_1_duplicate %double_1_duplicate",
616           },
617           // use duplicated constants in main
618           {
619             "%s_var = OpVariable %_pf_flat_struct Function",
620             "OpStore %s_var %s_duplicate",
621           },
622           // remapped names
623           {
624             "OpName %float_1 \"float_1_duplicate\"",
625             "OpName %double_1 \"double_1_duplicate\"",
626             "OpName %s \"s_duplicate\"",
627           },
628         },
629         // nested struct
630         {
631           // preserved constants
632           {
633             "%bool_true = OpConstantTrue %bool",
634             "%signed_1 = OpConstant %int 1",
635             "%float_1 = OpConstant %float 1",
636             "%double_1 = OpConstant %double 1",
637             "%inner = OpConstantComposite %inner_struct %bool_true %float_1",
638             "%outer = OpConstantComposite %outer_struct %inner %signed_1 %double_1",
639           },
640           // use preserved constants in main
641           {
642             "%outer_var = OpVariable %_pf_outer_struct Function",
643             "OpStore %outer_var %outer",
644           },
645           // duplicated constants
646           {
647             "%float_1_duplicate = OpConstant %float 1",
648             "%double_1_duplicate = OpConstant %double 1",
649             "%inner_duplicate = OpConstantComposite %inner_struct %bool_true %float_1_duplicate",
650             "%outer_duplicate = OpConstantComposite %outer_struct %inner_duplicate %signed_1 %double_1_duplicate",
651           },
652           // use duplicated constants in main
653           {
654             "%outer_var = OpVariable %_pf_outer_struct Function",
655             "OpStore %outer_var %outer_duplicate",
656           },
657           // remapped names
658           {
659             "OpName %float_1 \"float_1_duplicate\"",
660             "OpName %double_1 \"double_1_duplicate\"",
661             "OpName %inner \"inner_duplicate\"",
662             "OpName %outer \"outer_duplicate\"",
663           },
664         },
665         // composite type null constants. Null constants and zero-valued
666         // constants should not be used to replace each other.
667         {
668           // preserved constants
669           {
670             "%bool_zero = OpConstantFalse %bool",
671             "%float_zero = OpConstant %float 0",
672             "%int_null = OpConstantNull %int",
673             "%double_null = OpConstantNull %double",
674             // inner_struct type null constant.
675             "%null_inner = OpConstantNull %inner_struct",
676             // zero-valued composite constant built from zero-valued constant
677             // component. inner_zero should not be replace by null_inner.
678             "%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero",
679             // zero-valued composite contant built from zero-valued constants
680             // and null constants.
681             "%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null",
682             // outer_struct type null constant, it should not be replaced by
683             // outer_zero.
684             "%null_outer = OpConstantNull %outer_struct",
685           },
686           // use preserved constants in main
687           {
688             "%inner_var = OpVariable %_pf_inner_struct Function",
689             "OpStore %inner_var %inner_zero",
690             "OpStore %inner_var %null_inner",
691             "%outer_var = OpVariable %_pf_outer_struct Function",
692             "OpStore %outer_var %outer_zero",
693             "OpStore %outer_var %null_outer",
694           },
695           // duplicated constants
696           {
697             "%null_inner_dup = OpConstantNull %inner_struct",
698             "%null_outer_dup = OpConstantNull %outer_struct",
699             "%inner_zero_dup = OpConstantComposite %inner_struct %bool_zero %float_zero",
700             "%outer_zero_dup = OpConstantComposite %outer_struct %inner_zero_dup %int_null %double_null",
701           },
702           // use duplicated constants in main
703           {
704             "%inner_var = OpVariable %_pf_inner_struct Function",
705             "OpStore %inner_var %inner_zero_dup",
706             "OpStore %inner_var %null_inner_dup",
707             "%outer_var = OpVariable %_pf_outer_struct Function",
708             "OpStore %outer_var %outer_zero_dup",
709             "OpStore %outer_var %null_outer_dup",
710           },
711           // remapped names
712           {
713             "OpName %null_inner \"null_inner_dup\"",
714             "OpName %null_outer \"null_outer_dup\"",
715             "OpName %inner_zero \"inner_zero_dup\"",
716             "OpName %outer_zero \"outer_zero_dup\"",
717           },
718         },
719         // Spec Constants with SpecId decoration should be skipped.
720         {
721           // preserved constants
722           {
723             // Assembly builder will add OpDecorate SpecId instruction for the
724             // following spec constant instructions automatically.
725             "%spec_bool_1 = OpSpecConstantTrue %bool",
726             "%spec_bool_2 = OpSpecConstantTrue %bool",
727             "%spec_int_1 = OpSpecConstant %int 1",
728             "%spec_int_2 = OpSpecConstant %int 1",
729           },
730           // use preserved constants in main
731           {
732             "%bool_var = OpVariable %_pf_bool Function",
733             "OpStore %bool_var %spec_bool_1",
734             "OpStore %bool_var %spec_bool_2",
735             "%int_var = OpVariable %_pf_int Function",
736             "OpStore %int_var %spec_int_1",
737             "OpStore %int_var %spec_int_2",
738           },
739           // duplicated constants. No duplicated instruction to remove in this
740           // case.
741           {},
742           // use duplicated constants in main. Same as the above 'use preserved
743           // constants in main' defined above, as no instruction should be
744           // removed in this case.
745           {
746             "%bool_var = OpVariable %_pf_bool Function",
747             "OpStore %bool_var %spec_bool_1",
748             "OpStore %bool_var %spec_bool_2",
749             "%int_var = OpVariable %_pf_int Function",
750             "OpStore %int_var %spec_int_1",
751             "OpStore %int_var %spec_int_2",
752           },
753           // remapped names. No duplicated instruction removed, so this is
754           // empty.
755           {}
756         },
757         // spec constant composite
758         {
759           // preserved constants
760           {
761             "%spec_bool_true = OpSpecConstantTrue %bool",
762             "%spec_signed_1 = OpSpecConstant %int 1",
763             "%float_1 = OpConstant %float 1",
764             "%double_1 = OpConstant %double 1",
765             "%spec_inner = OpSpecConstantComposite %inner_struct %spec_bool_true %float_1",
766             "%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %spec_signed_1 %double_1",
767             "%spec_vec2 = OpSpecConstantComposite %v2float %float_1 %float_1",
768           },
769           // use preserved constants in main
770           {
771             "%outer_var = OpVariable %_pf_outer_struct Function",
772             "OpStore %outer_var %spec_outer",
773             "%v2float_var = OpVariable %_pf_v2float Function",
774             "OpStore %v2float_var %spec_vec2",
775           },
776           // duplicated constants
777           {
778             "%float_1_duplicate = OpConstant %float 1",
779             "%double_1_duplicate = OpConstant %double 1",
780             "%spec_inner_duplicate = OpSpecConstantComposite %inner_struct %spec_bool_true %float_1_duplicate",
781             "%spec_outer_duplicate = OpSpecConstantComposite %outer_struct %spec_inner_duplicate %spec_signed_1 %double_1_duplicate",
782             "%spec_vec2_duplicate = OpSpecConstantComposite %v2float %float_1 %float_1_duplicate",
783           },
784           // use duplicated constants in main
785           {
786             "%outer_var = OpVariable %_pf_outer_struct Function",
787             "OpStore %outer_var %spec_outer_duplicate",
788             "%v2float_var = OpVariable %_pf_v2float Function",
789             "OpStore %v2float_var %spec_vec2_duplicate",
790           },
791           // remapped names
792           {
793             "OpName %float_1 \"float_1_duplicate\"",
794             "OpName %double_1 \"double_1_duplicate\"",
795             "OpName %spec_inner \"spec_inner_duplicate\"",
796             "OpName %spec_outer \"spec_outer_duplicate\"",
797             "OpName %spec_vec2 \"spec_vec2_duplicate\"",
798           },
799         },
800         // spec constant op with int scalar
801         {
802           // preserved constants
803           {
804             "%spec_signed_1 = OpSpecConstant %int 1",
805             "%spec_signed_2 = OpSpecConstant %int 2",
806             "%spec_signed_add = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2",
807           },
808           // use preserved constants in main
809           {
810             "%int_var = OpVariable %_pf_int Function",
811             "OpStore %int_var %spec_signed_add",
812           },
813           // duplicated constants
814           {
815             "%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2",
816           },
817           // use duplicated contants in main
818           {
819             "%int_var = OpVariable %_pf_int Function",
820             "OpStore %int_var %spec_signed_add_duplicate",
821           },
822           // remapped names
823           {
824             "OpName %spec_signed_add \"spec_signed_add_duplicate\"",
825           },
826         },
827         // spec constant op composite extract
828         {
829           // preserved constants
830           {
831             "%float_1 = OpConstant %float 1",
832             "%spec_vec2 = OpSpecConstantComposite %v2float %float_1 %float_1",
833             "%spec_extract = OpSpecConstantOp %float CompositeExtract %spec_vec2 1",
834           },
835           // use preserved constants in main
836           {
837             "%float_var = OpVariable %_pf_float Function",
838             "OpStore %float_var %spec_extract",
839           },
840           // duplicated constants
841           {
842             "%spec_extract_duplicate = OpSpecConstantOp %float CompositeExtract %spec_vec2 1",
843           },
844           // use duplicated constants in main
845           {
846             "%float_var = OpVariable %_pf_float Function",
847             "OpStore %float_var %spec_extract_duplicate",
848           },
849           // remapped names
850           {
851             "OpName %spec_extract \"spec_extract_duplicate\"",
852           },
853         },
854         // spec constant op vector shuffle
855         {
856           // preserved constants
857           {
858             "%float_1 = OpConstant %float 1",
859             "%float_2 = OpConstant %float 2",
860             "%spec_vec2_1 = OpSpecConstantComposite %v2float %float_1 %float_1",
861             "%spec_vec2_2 = OpSpecConstantComposite %v2float %float_2 %float_2",
862             "%spec_vector_shuffle = OpSpecConstantOp %v2float VectorShuffle %spec_vec2_1 %spec_vec2_2 1 2",
863           },
864           // use preserved constants in main
865           {
866             "%v2float_var = OpVariable %_pf_v2float Function",
867             "OpStore %v2float_var %spec_vector_shuffle",
868           },
869           // duplicated constants
870           {
871             "%spec_vector_shuffle_duplicate = OpSpecConstantOp %v2float VectorShuffle %spec_vec2_1 %spec_vec2_2 1 2",
872           },
873           // use duplicated constants in main
874           {
875             "%v2float_var = OpVariable %_pf_v2float Function",
876             "OpStore %v2float_var %spec_vector_shuffle_duplicate",
877           },
878           // remapped names
879           {
880             "OpName %spec_vector_shuffle \"spec_vector_shuffle_duplicate\"",
881           },
882         },
883         // long dependency chain
884         {
885           // preserved constants
886           {
887             "%array_size = OpConstant %int 4",
888             "%type_arr_int_4 = OpTypeArray %int %array_size",
889             "%signed_0 = OpConstant %int 100",
890             "%signed_1 = OpConstant %int 1",
891             "%signed_2 = OpSpecConstantOp %int IAdd %signed_0 %signed_1",
892             "%signed_3 = OpSpecConstantOp %int ISub %signed_0 %signed_2",
893             "%signed_4 = OpSpecConstantOp %int IAdd %signed_0 %signed_3",
894             "%signed_5 = OpSpecConstantOp %int ISub %signed_0 %signed_4",
895             "%signed_6 = OpSpecConstantOp %int IAdd %signed_0 %signed_5",
896             "%signed_7 = OpSpecConstantOp %int ISub %signed_0 %signed_6",
897             "%signed_8 = OpSpecConstantOp %int IAdd %signed_0 %signed_7",
898             "%signed_9 = OpSpecConstantOp %int ISub %signed_0 %signed_8",
899             "%signed_10 = OpSpecConstantOp %int IAdd %signed_0 %signed_9",
900             "%signed_11 = OpSpecConstantOp %int ISub %signed_0 %signed_10",
901             "%signed_12 = OpSpecConstantOp %int IAdd %signed_0 %signed_11",
902             "%signed_13 = OpSpecConstantOp %int ISub %signed_0 %signed_12",
903             "%signed_14 = OpSpecConstantOp %int IAdd %signed_0 %signed_13",
904             "%signed_15 = OpSpecConstantOp %int ISub %signed_0 %signed_14",
905             "%signed_16 = OpSpecConstantOp %int ISub %signed_0 %signed_15",
906             "%signed_17 = OpSpecConstantOp %int IAdd %signed_0 %signed_16",
907             "%signed_18 = OpSpecConstantOp %int ISub %signed_0 %signed_17",
908             "%signed_19 = OpSpecConstantOp %int IAdd %signed_0 %signed_18",
909             "%signed_20 = OpSpecConstantOp %int ISub %signed_0 %signed_19",
910             "%signed_vec_a = OpSpecConstantComposite %v2int %signed_18 %signed_19",
911             "%signed_vec_b = OpSpecConstantOp %v2int IMul %signed_vec_a %signed_vec_a",
912             "%signed_21 = OpSpecConstantOp %int CompositeExtract %signed_vec_b 0",
913             "%signed_array = OpConstantComposite %type_arr_int_4 %signed_20 %signed_20 %signed_21 %signed_21",
914             "%signed_22 = OpSpecConstantOp %int CompositeExtract %signed_array 0",
915           },
916           // use preserved constants in main
917           {
918             "%int_var = OpVariable %_pf_int Function",
919             "OpStore %int_var %signed_22",
920           },
921           // duplicated constants
922           {
923             "%signed_0_dup = OpConstant %int 100",
924             "%signed_1_dup = OpConstant %int 1",
925             "%signed_2_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_1_dup",
926             "%signed_3_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_2_dup",
927             "%signed_4_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_3_dup",
928             "%signed_5_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_4_dup",
929             "%signed_6_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_5_dup",
930             "%signed_7_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_6_dup",
931             "%signed_8_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_7_dup",
932             "%signed_9_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_8_dup",
933             "%signed_10_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_9_dup",
934             "%signed_11_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_10_dup",
935             "%signed_12_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_11_dup",
936             "%signed_13_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_12_dup",
937             "%signed_14_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_13_dup",
938             "%signed_15_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_14_dup",
939             "%signed_16_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_15_dup",
940             "%signed_17_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_16_dup",
941             "%signed_18_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_17_dup",
942             "%signed_19_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_18_dup",
943             "%signed_20_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_19_dup",
944             "%signed_vec_a_dup = OpSpecConstantComposite %v2int %signed_18_dup %signed_19_dup",
945             "%signed_vec_b_dup = OpSpecConstantOp %v2int IMul %signed_vec_a_dup %signed_vec_a_dup",
946             "%signed_21_dup = OpSpecConstantOp %int CompositeExtract %signed_vec_b_dup 0",
947             "%signed_array_dup = OpConstantComposite %type_arr_int_4 %signed_20_dup %signed_20_dup %signed_21_dup %signed_21_dup",
948             "%signed_22_dup = OpSpecConstantOp %int CompositeExtract %signed_array_dup 0",
949           },
950           // use duplicated constants in main
951           {
952             "%int_var = OpVariable %_pf_int Function",
953             "OpStore %int_var %signed_22_dup",
954           },
955           // remapped names
956           {
957             "OpName %signed_0 \"signed_0_dup\"",
958             "OpName %signed_1 \"signed_1_dup\"",
959             "OpName %signed_2 \"signed_2_dup\"",
960             "OpName %signed_3 \"signed_3_dup\"",
961             "OpName %signed_4 \"signed_4_dup\"",
962             "OpName %signed_5 \"signed_5_dup\"",
963             "OpName %signed_6 \"signed_6_dup\"",
964             "OpName %signed_7 \"signed_7_dup\"",
965             "OpName %signed_8 \"signed_8_dup\"",
966             "OpName %signed_9 \"signed_9_dup\"",
967             "OpName %signed_10 \"signed_10_dup\"",
968             "OpName %signed_11 \"signed_11_dup\"",
969             "OpName %signed_12 \"signed_12_dup\"",
970             "OpName %signed_13 \"signed_13_dup\"",
971             "OpName %signed_14 \"signed_14_dup\"",
972             "OpName %signed_15 \"signed_15_dup\"",
973             "OpName %signed_16 \"signed_16_dup\"",
974             "OpName %signed_17 \"signed_17_dup\"",
975             "OpName %signed_18 \"signed_18_dup\"",
976             "OpName %signed_19 \"signed_19_dup\"",
977             "OpName %signed_20 \"signed_20_dup\"",
978             "OpName %signed_vec_a \"signed_vec_a_dup\"",
979             "OpName %signed_vec_b \"signed_vec_b_dup\"",
980             "OpName %signed_21 \"signed_21_dup\"",
981             "OpName %signed_array \"signed_array_dup\"",
982             "OpName %signed_22 \"signed_22_dup\"",
983           },
984         },
985                             // clang-format on
986                         })));
987 
988 }  // namespace
989 }  // namespace opt
990 }  // namespace spvtools
991