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