1fd4e5da5Sopenharmony_ci// Copyright (c) 2017 Google Inc.
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#include <string>
16fd4e5da5Sopenharmony_ci#include <unordered_set>
17fd4e5da5Sopenharmony_ci#include <vector>
18fd4e5da5Sopenharmony_ci
19fd4e5da5Sopenharmony_ci#include "gmock/gmock.h"
20fd4e5da5Sopenharmony_ci#include "test/opt/pass_fixture.h"
21fd4e5da5Sopenharmony_ci#include "test/opt/pass_utils.h"
22fd4e5da5Sopenharmony_ci
23fd4e5da5Sopenharmony_cinamespace spvtools {
24fd4e5da5Sopenharmony_cinamespace opt {
25fd4e5da5Sopenharmony_cinamespace {
26fd4e5da5Sopenharmony_ci
27fd4e5da5Sopenharmony_ciusing ::testing::HasSubstr;
28fd4e5da5Sopenharmony_ciusing ::testing::MatchesRegex;
29fd4e5da5Sopenharmony_ciusing StrengthReductionBasicTest = PassTest<::testing::Test>;
30fd4e5da5Sopenharmony_ci
31fd4e5da5Sopenharmony_ci// Test to make sure we replace 5*8.
32fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicReplaceMulBy8) {
33fd4e5da5Sopenharmony_ci  const std::vector<const char*> text = {
34fd4e5da5Sopenharmony_ci      // clang-format off
35fd4e5da5Sopenharmony_ci               "OpCapability Shader",
36fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
37fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
38fd4e5da5Sopenharmony_ci               "OpEntryPoint Vertex %main \"main\"",
39fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
40fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
41fd4e5da5Sopenharmony_ci          "%4 = OpTypeFunction %void",
42fd4e5da5Sopenharmony_ci       "%uint = OpTypeInt 32 0",
43fd4e5da5Sopenharmony_ci     "%uint_5 = OpConstant %uint 5",
44fd4e5da5Sopenharmony_ci     "%uint_8 = OpConstant %uint 8",
45fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %4",
46fd4e5da5Sopenharmony_ci          "%8 = OpLabel",
47fd4e5da5Sopenharmony_ci          "%9 = OpIMul %uint %uint_5 %uint_8",
48fd4e5da5Sopenharmony_ci               "OpReturn",
49fd4e5da5Sopenharmony_ci               "OpFunctionEnd"
50fd4e5da5Sopenharmony_ci      // clang-format on
51fd4e5da5Sopenharmony_ci  };
52fd4e5da5Sopenharmony_ci
53fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
54fd4e5da5Sopenharmony_ci      JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
55fd4e5da5Sopenharmony_ci
56fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
57fd4e5da5Sopenharmony_ci  const std::string& output = std::get<0>(result);
58fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
59fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_3"));
60fd4e5da5Sopenharmony_ci}
61fd4e5da5Sopenharmony_ci
62fd4e5da5Sopenharmony_ci// TODO(dneto): Add Effcee as required dependency, and make this unconditional.
63fd4e5da5Sopenharmony_ci// Test to make sure we replace 16*5
64fd4e5da5Sopenharmony_ci// Also demonstrate use of Effcee matching.
65fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicReplaceMulBy16) {
66fd4e5da5Sopenharmony_ci  const std::string text = R"(
67fd4e5da5Sopenharmony_ci               OpCapability Shader
68fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
69fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
70fd4e5da5Sopenharmony_ci               OpEntryPoint Vertex %main "main"
71fd4e5da5Sopenharmony_ci               OpName %main "main"
72fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
73fd4e5da5Sopenharmony_ci          %4 = OpTypeFunction %void
74fd4e5da5Sopenharmony_ci; We know disassembly will produce %uint here, but
75fd4e5da5Sopenharmony_ci;  CHECK: %uint = OpTypeInt 32 0
76fd4e5da5Sopenharmony_ci;  CHECK-DAG: [[five:%[a-zA-Z_\d]+]] = OpConstant %uint 5
77fd4e5da5Sopenharmony_ci
78fd4e5da5Sopenharmony_ci; We have RE2 regular expressions, so \w matches [_a-zA-Z0-9].
79fd4e5da5Sopenharmony_ci; This shows the preferred pattern for matching SPIR-V identifiers.
80fd4e5da5Sopenharmony_ci; (We could have cheated in this case since we know the disassembler will
81fd4e5da5Sopenharmony_ci; generate the 'nice' name of "%uint_4".
82fd4e5da5Sopenharmony_ci;  CHECK-DAG: [[four:%\w+]] = OpConstant %uint 4
83fd4e5da5Sopenharmony_ci       %uint = OpTypeInt 32 0
84fd4e5da5Sopenharmony_ci     %uint_5 = OpConstant %uint 5
85fd4e5da5Sopenharmony_ci    %uint_16 = OpConstant %uint 16
86fd4e5da5Sopenharmony_ci       %main = OpFunction %void None %4
87fd4e5da5Sopenharmony_ci; CHECK: OpLabel
88fd4e5da5Sopenharmony_ci          %8 = OpLabel
89fd4e5da5Sopenharmony_ci; CHECK-NEXT: OpShiftLeftLogical %uint [[five]] [[four]]
90fd4e5da5Sopenharmony_ci; The multiplication disappears.
91fd4e5da5Sopenharmony_ci; CHECK-NOT: OpIMul
92fd4e5da5Sopenharmony_ci          %9 = OpIMul %uint %uint_16 %uint_5
93fd4e5da5Sopenharmony_ci               OpReturn
94fd4e5da5Sopenharmony_ci; CHECK: OpFunctionEnd
95fd4e5da5Sopenharmony_ci               OpFunctionEnd)";
96fd4e5da5Sopenharmony_ci
97fd4e5da5Sopenharmony_ci  SinglePassRunAndMatch<StrengthReductionPass>(text, false);
98fd4e5da5Sopenharmony_ci}
99fd4e5da5Sopenharmony_ci
100fd4e5da5Sopenharmony_ci// Test to make sure we replace a multiple of 32 and 4.
101fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicTwoPowersOf2) {
102fd4e5da5Sopenharmony_ci  // In this case, we have two powers of 2.  Need to make sure we replace only
103fd4e5da5Sopenharmony_ci  // one of them for the bit shift.
104fd4e5da5Sopenharmony_ci  // clang-format off
105fd4e5da5Sopenharmony_ci  const std::string text = R"(
106fd4e5da5Sopenharmony_ci          OpCapability Shader
107fd4e5da5Sopenharmony_ci     %1 = OpExtInstImport "GLSL.std.450"
108fd4e5da5Sopenharmony_ci          OpMemoryModel Logical GLSL450
109fd4e5da5Sopenharmony_ci          OpEntryPoint Vertex %main "main"
110fd4e5da5Sopenharmony_ci          OpName %main "main"
111fd4e5da5Sopenharmony_ci  %void = OpTypeVoid
112fd4e5da5Sopenharmony_ci     %4 = OpTypeFunction %void
113fd4e5da5Sopenharmony_ci   %int = OpTypeInt 32 1
114fd4e5da5Sopenharmony_ci%int_32 = OpConstant %int 32
115fd4e5da5Sopenharmony_ci %int_4 = OpConstant %int 4
116fd4e5da5Sopenharmony_ci  %main = OpFunction %void None %4
117fd4e5da5Sopenharmony_ci     %8 = OpLabel
118fd4e5da5Sopenharmony_ci     %9 = OpIMul %int %int_32 %int_4
119fd4e5da5Sopenharmony_ci          OpReturn
120fd4e5da5Sopenharmony_ci          OpFunctionEnd
121fd4e5da5Sopenharmony_ci)";
122fd4e5da5Sopenharmony_ci  // clang-format on
123fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
124fd4e5da5Sopenharmony_ci      text, /* skip_nop = */ true, /* do_validation = */ false);
125fd4e5da5Sopenharmony_ci
126fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
127fd4e5da5Sopenharmony_ci  const std::string& output = std::get<0>(result);
128fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
129fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %int %int_4 %uint_5"));
130fd4e5da5Sopenharmony_ci}
131fd4e5da5Sopenharmony_ci
132fd4e5da5Sopenharmony_ci// Test to make sure we don't replace 0*5.
133fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicDontReplace0) {
134fd4e5da5Sopenharmony_ci  const std::vector<const char*> text = {
135fd4e5da5Sopenharmony_ci      // clang-format off
136fd4e5da5Sopenharmony_ci               "OpCapability Shader",
137fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
138fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
139fd4e5da5Sopenharmony_ci               "OpEntryPoint Vertex %main \"main\"",
140fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
141fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
142fd4e5da5Sopenharmony_ci          "%4 = OpTypeFunction %void",
143fd4e5da5Sopenharmony_ci        "%int = OpTypeInt 32 1",
144fd4e5da5Sopenharmony_ci      "%int_0 = OpConstant %int 0",
145fd4e5da5Sopenharmony_ci      "%int_5 = OpConstant %int 5",
146fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %4",
147fd4e5da5Sopenharmony_ci          "%8 = OpLabel",
148fd4e5da5Sopenharmony_ci          "%9 = OpIMul %int %int_0 %int_5",
149fd4e5da5Sopenharmony_ci               "OpReturn",
150fd4e5da5Sopenharmony_ci               "OpFunctionEnd"
151fd4e5da5Sopenharmony_ci      // clang-format on
152fd4e5da5Sopenharmony_ci  };
153fd4e5da5Sopenharmony_ci
154fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
155fd4e5da5Sopenharmony_ci      JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
156fd4e5da5Sopenharmony_ci
157fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
158fd4e5da5Sopenharmony_ci}
159fd4e5da5Sopenharmony_ci
160fd4e5da5Sopenharmony_ci// Test to make sure we do not replace a multiple of 5 and 7.
161fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicNoChange) {
162fd4e5da5Sopenharmony_ci  const std::vector<const char*> text = {
163fd4e5da5Sopenharmony_ci      // clang-format off
164fd4e5da5Sopenharmony_ci             "OpCapability Shader",
165fd4e5da5Sopenharmony_ci        "%1 = OpExtInstImport \"GLSL.std.450\"",
166fd4e5da5Sopenharmony_ci             "OpMemoryModel Logical GLSL450",
167fd4e5da5Sopenharmony_ci             "OpEntryPoint Vertex %2 \"main\"",
168fd4e5da5Sopenharmony_ci             "OpName %2 \"main\"",
169fd4e5da5Sopenharmony_ci        "%3 = OpTypeVoid",
170fd4e5da5Sopenharmony_ci        "%4 = OpTypeFunction %3",
171fd4e5da5Sopenharmony_ci        "%5 = OpTypeInt 32 1",
172fd4e5da5Sopenharmony_ci        "%6 = OpTypeInt 32 0",
173fd4e5da5Sopenharmony_ci        "%7 = OpConstant %5 5",
174fd4e5da5Sopenharmony_ci        "%8 = OpConstant %5 7",
175fd4e5da5Sopenharmony_ci        "%2 = OpFunction %3 None %4",
176fd4e5da5Sopenharmony_ci        "%9 = OpLabel",
177fd4e5da5Sopenharmony_ci        "%10 = OpIMul %5 %7 %8",
178fd4e5da5Sopenharmony_ci             "OpReturn",
179fd4e5da5Sopenharmony_ci             "OpFunctionEnd",
180fd4e5da5Sopenharmony_ci      // clang-format on
181fd4e5da5Sopenharmony_ci  };
182fd4e5da5Sopenharmony_ci
183fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
184fd4e5da5Sopenharmony_ci      JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
185fd4e5da5Sopenharmony_ci
186fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
187fd4e5da5Sopenharmony_ci}
188fd4e5da5Sopenharmony_ci
189fd4e5da5Sopenharmony_ci// Test to make sure constants and types are reused and not duplicated.
190fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, NoDuplicateConstantsAndTypes) {
191fd4e5da5Sopenharmony_ci  const std::vector<const char*> text = {
192fd4e5da5Sopenharmony_ci      // clang-format off
193fd4e5da5Sopenharmony_ci               "OpCapability Shader",
194fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
195fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
196fd4e5da5Sopenharmony_ci               "OpEntryPoint Vertex %main \"main\"",
197fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
198fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
199fd4e5da5Sopenharmony_ci          "%4 = OpTypeFunction %void",
200fd4e5da5Sopenharmony_ci       "%uint = OpTypeInt 32 0",
201fd4e5da5Sopenharmony_ci     "%uint_8 = OpConstant %uint 8",
202fd4e5da5Sopenharmony_ci     "%uint_3 = OpConstant %uint 3",
203fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %4",
204fd4e5da5Sopenharmony_ci          "%8 = OpLabel",
205fd4e5da5Sopenharmony_ci          "%9 = OpIMul %uint %uint_8 %uint_3",
206fd4e5da5Sopenharmony_ci               "OpReturn",
207fd4e5da5Sopenharmony_ci               "OpFunctionEnd",
208fd4e5da5Sopenharmony_ci      // clang-format on
209fd4e5da5Sopenharmony_ci  };
210fd4e5da5Sopenharmony_ci
211fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
212fd4e5da5Sopenharmony_ci      JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
213fd4e5da5Sopenharmony_ci
214fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
215fd4e5da5Sopenharmony_ci  const std::string& output = std::get<0>(result);
216fd4e5da5Sopenharmony_ci  EXPECT_THAT(output,
217fd4e5da5Sopenharmony_ci              Not(MatchesRegex(".*OpConstant %uint 3.*OpConstant %uint 3.*")));
218fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, Not(MatchesRegex(".*OpTypeInt 32 0.*OpTypeInt 32 0.*")));
219fd4e5da5Sopenharmony_ci}
220fd4e5da5Sopenharmony_ci
221fd4e5da5Sopenharmony_ci// Test to make sure we generate the constants only once
222fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicCreateOneConst) {
223fd4e5da5Sopenharmony_ci  const std::vector<const char*> text = {
224fd4e5da5Sopenharmony_ci      // clang-format off
225fd4e5da5Sopenharmony_ci               "OpCapability Shader",
226fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
227fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
228fd4e5da5Sopenharmony_ci               "OpEntryPoint Vertex %main \"main\"",
229fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
230fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
231fd4e5da5Sopenharmony_ci          "%4 = OpTypeFunction %void",
232fd4e5da5Sopenharmony_ci       "%uint = OpTypeInt 32 0",
233fd4e5da5Sopenharmony_ci     "%uint_5 = OpConstant %uint 5",
234fd4e5da5Sopenharmony_ci     "%uint_9 = OpConstant %uint 9",
235fd4e5da5Sopenharmony_ci   "%uint_128 = OpConstant %uint 128",
236fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %4",
237fd4e5da5Sopenharmony_ci          "%8 = OpLabel",
238fd4e5da5Sopenharmony_ci          "%9 = OpIMul %uint %uint_5 %uint_128",
239fd4e5da5Sopenharmony_ci         "%10 = OpIMul %uint %uint_9 %uint_128",
240fd4e5da5Sopenharmony_ci               "OpReturn",
241fd4e5da5Sopenharmony_ci               "OpFunctionEnd"
242fd4e5da5Sopenharmony_ci      // clang-format on
243fd4e5da5Sopenharmony_ci  };
244fd4e5da5Sopenharmony_ci
245fd4e5da5Sopenharmony_ci  auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
246fd4e5da5Sopenharmony_ci      JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
247fd4e5da5Sopenharmony_ci
248fd4e5da5Sopenharmony_ci  EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
249fd4e5da5Sopenharmony_ci  const std::string& output = std::get<0>(result);
250fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
251fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_7"));
252fd4e5da5Sopenharmony_ci  EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_9 %uint_7"));
253fd4e5da5Sopenharmony_ci}
254fd4e5da5Sopenharmony_ci
255fd4e5da5Sopenharmony_ci// Test to make sure we generate the instructions in the correct position and
256fd4e5da5Sopenharmony_ci// that the uses get replaced as well.  Here we check that the use in the return
257fd4e5da5Sopenharmony_ci// is replaced, we also check that we can replace two OpIMuls when one feeds the
258fd4e5da5Sopenharmony_ci// other.
259fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicCheckPositionAndReplacement) {
260fd4e5da5Sopenharmony_ci  // This is just the preamble to set up the test.
261fd4e5da5Sopenharmony_ci  const std::vector<const char*> common_text = {
262fd4e5da5Sopenharmony_ci      // clang-format off
263fd4e5da5Sopenharmony_ci               "OpCapability Shader",
264fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
265fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
266fd4e5da5Sopenharmony_ci               "OpEntryPoint Fragment %main \"main\" %gl_FragColor",
267fd4e5da5Sopenharmony_ci               "OpExecutionMode %main OriginUpperLeft",
268fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
269fd4e5da5Sopenharmony_ci               "OpName %foo_i1_ \"foo(i1;\"",
270fd4e5da5Sopenharmony_ci               "OpName %n \"n\"",
271fd4e5da5Sopenharmony_ci               "OpName %gl_FragColor \"gl_FragColor\"",
272fd4e5da5Sopenharmony_ci               "OpName %param \"param\"",
273fd4e5da5Sopenharmony_ci               "OpDecorate %gl_FragColor Location 0",
274fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
275fd4e5da5Sopenharmony_ci          "%3 = OpTypeFunction %void",
276fd4e5da5Sopenharmony_ci        "%int = OpTypeInt 32 1",
277fd4e5da5Sopenharmony_ci"%_ptr_Function_int = OpTypePointer Function %int",
278fd4e5da5Sopenharmony_ci          "%8 = OpTypeFunction %int %_ptr_Function_int",
279fd4e5da5Sopenharmony_ci    "%int_256 = OpConstant %int 256",
280fd4e5da5Sopenharmony_ci      "%int_2 = OpConstant %int 2",
281fd4e5da5Sopenharmony_ci      "%float = OpTypeFloat 32",
282fd4e5da5Sopenharmony_ci    "%v4float = OpTypeVector %float 4",
283fd4e5da5Sopenharmony_ci"%_ptr_Output_v4float = OpTypePointer Output %v4float",
284fd4e5da5Sopenharmony_ci"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
285fd4e5da5Sopenharmony_ci    "%float_1 = OpConstant %float 1",
286fd4e5da5Sopenharmony_ci     "%int_10 = OpConstant %int 10",
287fd4e5da5Sopenharmony_ci  "%float_0_375 = OpConstant %float 0.375",
288fd4e5da5Sopenharmony_ci  "%float_0_75 = OpConstant %float 0.75",
289fd4e5da5Sopenharmony_ci       "%uint = OpTypeInt 32 0",
290fd4e5da5Sopenharmony_ci     "%uint_8 = OpConstant %uint 8",
291fd4e5da5Sopenharmony_ci     "%uint_1 = OpConstant %uint 1",
292fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %3",
293fd4e5da5Sopenharmony_ci          "%5 = OpLabel",
294fd4e5da5Sopenharmony_ci      "%param = OpVariable %_ptr_Function_int Function",
295fd4e5da5Sopenharmony_ci               "OpStore %param %int_10",
296fd4e5da5Sopenharmony_ci         "%26 = OpFunctionCall %int %foo_i1_ %param",
297fd4e5da5Sopenharmony_ci         "%27 = OpConvertSToF %float %26",
298fd4e5da5Sopenharmony_ci         "%28 = OpFDiv %float %float_1 %27",
299fd4e5da5Sopenharmony_ci         "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1",
300fd4e5da5Sopenharmony_ci               "OpStore %gl_FragColor %31",
301fd4e5da5Sopenharmony_ci               "OpReturn",
302fd4e5da5Sopenharmony_ci               "OpFunctionEnd"
303fd4e5da5Sopenharmony_ci      // clang-format on
304fd4e5da5Sopenharmony_ci  };
305fd4e5da5Sopenharmony_ci
306fd4e5da5Sopenharmony_ci  // This is the real test.  The two OpIMul should be replaced.  The expected
307fd4e5da5Sopenharmony_ci  // output is in |foo_after|.
308fd4e5da5Sopenharmony_ci  const std::vector<const char*> foo_before = {
309fd4e5da5Sopenharmony_ci      // clang-format off
310fd4e5da5Sopenharmony_ci    "%foo_i1_ = OpFunction %int None %8",
311fd4e5da5Sopenharmony_ci          "%n = OpFunctionParameter %_ptr_Function_int",
312fd4e5da5Sopenharmony_ci         "%11 = OpLabel",
313fd4e5da5Sopenharmony_ci         "%12 = OpLoad %int %n",
314fd4e5da5Sopenharmony_ci         "%14 = OpIMul %int %12 %int_256",
315fd4e5da5Sopenharmony_ci         "%16 = OpIMul %int %14 %int_2",
316fd4e5da5Sopenharmony_ci               "OpReturnValue %16",
317fd4e5da5Sopenharmony_ci               "OpFunctionEnd",
318fd4e5da5Sopenharmony_ci
319fd4e5da5Sopenharmony_ci      // clang-format on
320fd4e5da5Sopenharmony_ci  };
321fd4e5da5Sopenharmony_ci
322fd4e5da5Sopenharmony_ci  const std::vector<const char*> foo_after = {
323fd4e5da5Sopenharmony_ci      // clang-format off
324fd4e5da5Sopenharmony_ci    "%foo_i1_ = OpFunction %int None %8",
325fd4e5da5Sopenharmony_ci          "%n = OpFunctionParameter %_ptr_Function_int",
326fd4e5da5Sopenharmony_ci         "%11 = OpLabel",
327fd4e5da5Sopenharmony_ci         "%12 = OpLoad %int %n",
328fd4e5da5Sopenharmony_ci         "%33 = OpShiftLeftLogical %int %12 %uint_8",
329fd4e5da5Sopenharmony_ci         "%34 = OpShiftLeftLogical %int %33 %uint_1",
330fd4e5da5Sopenharmony_ci               "OpReturnValue %34",
331fd4e5da5Sopenharmony_ci               "OpFunctionEnd",
332fd4e5da5Sopenharmony_ci      // clang-format on
333fd4e5da5Sopenharmony_ci  };
334fd4e5da5Sopenharmony_ci
335fd4e5da5Sopenharmony_ci  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
336fd4e5da5Sopenharmony_ci  SinglePassRunAndCheck<StrengthReductionPass>(
337fd4e5da5Sopenharmony_ci      JoinAllInsts(Concat(common_text, foo_before)),
338fd4e5da5Sopenharmony_ci      JoinAllInsts(Concat(common_text, foo_after)),
339fd4e5da5Sopenharmony_ci      /* skip_nop = */ true, /* do_validate = */ true);
340fd4e5da5Sopenharmony_ci}
341fd4e5da5Sopenharmony_ci
342fd4e5da5Sopenharmony_ci// Test that, when the result of an OpIMul instruction has more than 1 use, and
343fd4e5da5Sopenharmony_ci// the instruction is replaced, all of the uses of the results are replace with
344fd4e5da5Sopenharmony_ci// the new result.
345fd4e5da5Sopenharmony_ciTEST_F(StrengthReductionBasicTest, BasicTestMultipleReplacements) {
346fd4e5da5Sopenharmony_ci  // This is just the preamble to set up the test.
347fd4e5da5Sopenharmony_ci  const std::vector<const char*> common_text = {
348fd4e5da5Sopenharmony_ci      // clang-format off
349fd4e5da5Sopenharmony_ci               "OpCapability Shader",
350fd4e5da5Sopenharmony_ci          "%1 = OpExtInstImport \"GLSL.std.450\"",
351fd4e5da5Sopenharmony_ci               "OpMemoryModel Logical GLSL450",
352fd4e5da5Sopenharmony_ci               "OpEntryPoint Fragment %main \"main\" %gl_FragColor",
353fd4e5da5Sopenharmony_ci               "OpExecutionMode %main OriginUpperLeft",
354fd4e5da5Sopenharmony_ci               "OpName %main \"main\"",
355fd4e5da5Sopenharmony_ci               "OpName %foo_i1_ \"foo(i1;\"",
356fd4e5da5Sopenharmony_ci               "OpName %n \"n\"",
357fd4e5da5Sopenharmony_ci               "OpName %gl_FragColor \"gl_FragColor\"",
358fd4e5da5Sopenharmony_ci               "OpName %param \"param\"",
359fd4e5da5Sopenharmony_ci               "OpDecorate %gl_FragColor Location 0",
360fd4e5da5Sopenharmony_ci       "%void = OpTypeVoid",
361fd4e5da5Sopenharmony_ci          "%3 = OpTypeFunction %void",
362fd4e5da5Sopenharmony_ci        "%int = OpTypeInt 32 1",
363fd4e5da5Sopenharmony_ci"%_ptr_Function_int = OpTypePointer Function %int",
364fd4e5da5Sopenharmony_ci          "%8 = OpTypeFunction %int %_ptr_Function_int",
365fd4e5da5Sopenharmony_ci    "%int_256 = OpConstant %int 256",
366fd4e5da5Sopenharmony_ci      "%int_2 = OpConstant %int 2",
367fd4e5da5Sopenharmony_ci      "%float = OpTypeFloat 32",
368fd4e5da5Sopenharmony_ci    "%v4float = OpTypeVector %float 4",
369fd4e5da5Sopenharmony_ci"%_ptr_Output_v4float = OpTypePointer Output %v4float",
370fd4e5da5Sopenharmony_ci"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
371fd4e5da5Sopenharmony_ci    "%float_1 = OpConstant %float 1",
372fd4e5da5Sopenharmony_ci     "%int_10 = OpConstant %int 10",
373fd4e5da5Sopenharmony_ci  "%float_0_375 = OpConstant %float 0.375",
374fd4e5da5Sopenharmony_ci  "%float_0_75 = OpConstant %float 0.75",
375fd4e5da5Sopenharmony_ci       "%uint = OpTypeInt 32 0",
376fd4e5da5Sopenharmony_ci     "%uint_8 = OpConstant %uint 8",
377fd4e5da5Sopenharmony_ci     "%uint_1 = OpConstant %uint 1",
378fd4e5da5Sopenharmony_ci       "%main = OpFunction %void None %3",
379fd4e5da5Sopenharmony_ci          "%5 = OpLabel",
380fd4e5da5Sopenharmony_ci      "%param = OpVariable %_ptr_Function_int Function",
381fd4e5da5Sopenharmony_ci               "OpStore %param %int_10",
382fd4e5da5Sopenharmony_ci         "%26 = OpFunctionCall %int %foo_i1_ %param",
383fd4e5da5Sopenharmony_ci         "%27 = OpConvertSToF %float %26",
384fd4e5da5Sopenharmony_ci         "%28 = OpFDiv %float %float_1 %27",
385fd4e5da5Sopenharmony_ci         "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1",
386fd4e5da5Sopenharmony_ci               "OpStore %gl_FragColor %31",
387fd4e5da5Sopenharmony_ci               "OpReturn",
388fd4e5da5Sopenharmony_ci               "OpFunctionEnd"
389fd4e5da5Sopenharmony_ci      // clang-format on
390fd4e5da5Sopenharmony_ci  };
391fd4e5da5Sopenharmony_ci
392fd4e5da5Sopenharmony_ci  // This is the real test.  The two OpIMul instructions should be replaced.  In
393fd4e5da5Sopenharmony_ci  // particular, we want to be sure that both uses of %16 are changed to use the
394fd4e5da5Sopenharmony_ci  // new result.
395fd4e5da5Sopenharmony_ci  const std::vector<const char*> foo_before = {
396fd4e5da5Sopenharmony_ci      // clang-format off
397fd4e5da5Sopenharmony_ci    "%foo_i1_ = OpFunction %int None %8",
398fd4e5da5Sopenharmony_ci          "%n = OpFunctionParameter %_ptr_Function_int",
399fd4e5da5Sopenharmony_ci         "%11 = OpLabel",
400fd4e5da5Sopenharmony_ci         "%12 = OpLoad %int %n",
401fd4e5da5Sopenharmony_ci         "%14 = OpIMul %int %12 %int_256",
402fd4e5da5Sopenharmony_ci         "%16 = OpIMul %int %14 %int_2",
403fd4e5da5Sopenharmony_ci         "%17 = OpIAdd %int %14 %16",
404fd4e5da5Sopenharmony_ci               "OpReturnValue %17",
405fd4e5da5Sopenharmony_ci               "OpFunctionEnd",
406fd4e5da5Sopenharmony_ci
407fd4e5da5Sopenharmony_ci      // clang-format on
408fd4e5da5Sopenharmony_ci  };
409fd4e5da5Sopenharmony_ci
410fd4e5da5Sopenharmony_ci  const std::vector<const char*> foo_after = {
411fd4e5da5Sopenharmony_ci      // clang-format off
412fd4e5da5Sopenharmony_ci    "%foo_i1_ = OpFunction %int None %8",
413fd4e5da5Sopenharmony_ci          "%n = OpFunctionParameter %_ptr_Function_int",
414fd4e5da5Sopenharmony_ci         "%11 = OpLabel",
415fd4e5da5Sopenharmony_ci         "%12 = OpLoad %int %n",
416fd4e5da5Sopenharmony_ci         "%34 = OpShiftLeftLogical %int %12 %uint_8",
417fd4e5da5Sopenharmony_ci         "%35 = OpShiftLeftLogical %int %34 %uint_1",
418fd4e5da5Sopenharmony_ci         "%17 = OpIAdd %int %34 %35",
419fd4e5da5Sopenharmony_ci               "OpReturnValue %17",
420fd4e5da5Sopenharmony_ci               "OpFunctionEnd",
421fd4e5da5Sopenharmony_ci      // clang-format on
422fd4e5da5Sopenharmony_ci  };
423fd4e5da5Sopenharmony_ci
424fd4e5da5Sopenharmony_ci  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
425fd4e5da5Sopenharmony_ci  SinglePassRunAndCheck<StrengthReductionPass>(
426fd4e5da5Sopenharmony_ci      JoinAllInsts(Concat(common_text, foo_before)),
427fd4e5da5Sopenharmony_ci      JoinAllInsts(Concat(common_text, foo_after)),
428fd4e5da5Sopenharmony_ci      /* skip_nop = */ true, /* do_validate = */ true);
429fd4e5da5Sopenharmony_ci}
430fd4e5da5Sopenharmony_ci
431fd4e5da5Sopenharmony_ci}  // namespace
432fd4e5da5Sopenharmony_ci}  // namespace opt
433fd4e5da5Sopenharmony_ci}  // namespace spvtools
434