123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- // Copyright (c) 2017 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <algorithm>
- #include <cstdarg>
- #include <iostream>
- #include <sstream>
- #include <string>
- #include <unordered_set>
- #include <vector>
- #include "gmock/gmock.h"
- #include "test/opt/assembly_builder.h"
- #include "test/opt/pass_fixture.h"
- #include "test/opt/pass_utils.h"
- namespace spvtools {
- namespace opt {
- namespace {
- using ::testing::HasSubstr;
- using ::testing::MatchesRegex;
- using StrengthReductionBasicTest = PassTest<::testing::Test>;
- // Test to make sure we replace 5*8.
- TEST_F(StrengthReductionBasicTest, BasicReplaceMulBy8) {
- const std::vector<const char*> text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %main \"main\"",
- "OpName %main \"main\"",
- "%void = OpTypeVoid",
- "%4 = OpTypeFunction %void",
- "%uint = OpTypeInt 32 0",
- "%uint_5 = OpConstant %uint 5",
- "%uint_8 = OpConstant %uint 8",
- "%main = OpFunction %void None %4",
- "%8 = OpLabel",
- "%9 = OpIMul %uint %uint_5 %uint_8",
- "OpReturn",
- "OpFunctionEnd"
- // clang-format on
- };
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
- const std::string& output = std::get<0>(result);
- EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
- EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_3"));
- }
- // TODO(dneto): Add Effcee as required dependency, and make this unconditional.
- // Test to make sure we replace 16*5
- // Also demonstrate use of Effcee matching.
- TEST_F(StrengthReductionBasicTest, BasicReplaceMulBy16) {
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpName %main "main"
- %void = OpTypeVoid
- %4 = OpTypeFunction %void
- ; We know disassembly will produce %uint here, but
- ; CHECK: %uint = OpTypeInt 32 0
- ; CHECK-DAG: [[five:%[a-zA-Z_\d]+]] = OpConstant %uint 5
- ; We have RE2 regular expressions, so \w matches [_a-zA-Z0-9].
- ; This shows the preferred pattern for matching SPIR-V identifiers.
- ; (We could have cheated in this case since we know the disassembler will
- ; generate the 'nice' name of "%uint_4".
- ; CHECK-DAG: [[four:%\w+]] = OpConstant %uint 4
- %uint = OpTypeInt 32 0
- %uint_5 = OpConstant %uint 5
- %uint_16 = OpConstant %uint 16
- %main = OpFunction %void None %4
- ; CHECK: OpLabel
- %8 = OpLabel
- ; CHECK-NEXT: OpShiftLeftLogical %uint [[five]] [[four]]
- ; The multiplication disappears.
- ; CHECK-NOT: OpIMul
- %9 = OpIMul %uint %uint_16 %uint_5
- OpReturn
- ; CHECK: OpFunctionEnd
- OpFunctionEnd)";
- SinglePassRunAndMatch<StrengthReductionPass>(text, false);
- }
- // Test to make sure we replace a multiple of 32 and 4.
- TEST_F(StrengthReductionBasicTest, BasicTwoPowersOf2) {
- // In this case, we have two powers of 2. Need to make sure we replace only
- // one of them for the bit shift.
- // clang-format off
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main"
- OpName %main "main"
- %void = OpTypeVoid
- %4 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %int_32 = OpConstant %int 32
- %int_4 = OpConstant %int 4
- %main = OpFunction %void None %4
- %8 = OpLabel
- %9 = OpIMul %int %int_32 %int_4
- OpReturn
- OpFunctionEnd
- )";
- // clang-format on
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- text, /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
- const std::string& output = std::get<0>(result);
- EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
- EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %int %int_4 %uint_5"));
- }
- // Test to make sure we don't replace 0*5.
- TEST_F(StrengthReductionBasicTest, BasicDontReplace0) {
- const std::vector<const char*> text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %main \"main\"",
- "OpName %main \"main\"",
- "%void = OpTypeVoid",
- "%4 = OpTypeFunction %void",
- "%int = OpTypeInt 32 1",
- "%int_0 = OpConstant %int 0",
- "%int_5 = OpConstant %int 5",
- "%main = OpFunction %void None %4",
- "%8 = OpLabel",
- "%9 = OpIMul %int %int_0 %int_5",
- "OpReturn",
- "OpFunctionEnd"
- // clang-format on
- };
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
- }
- // Test to make sure we do not replace a multiple of 5 and 7.
- TEST_F(StrengthReductionBasicTest, BasicNoChange) {
- const std::vector<const char*> text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %2 \"main\"",
- "OpName %2 \"main\"",
- "%3 = OpTypeVoid",
- "%4 = OpTypeFunction %3",
- "%5 = OpTypeInt 32 1",
- "%6 = OpTypeInt 32 0",
- "%7 = OpConstant %5 5",
- "%8 = OpConstant %5 7",
- "%2 = OpFunction %3 None %4",
- "%9 = OpLabel",
- "%10 = OpIMul %5 %7 %8",
- "OpReturn",
- "OpFunctionEnd",
- // clang-format on
- };
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
- }
- // Test to make sure constants and types are reused and not duplicated.
- TEST_F(StrengthReductionBasicTest, NoDuplicateConstantsAndTypes) {
- const std::vector<const char*> text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %main \"main\"",
- "OpName %main \"main\"",
- "%void = OpTypeVoid",
- "%4 = OpTypeFunction %void",
- "%uint = OpTypeInt 32 0",
- "%uint_8 = OpConstant %uint 8",
- "%uint_3 = OpConstant %uint 3",
- "%main = OpFunction %void None %4",
- "%8 = OpLabel",
- "%9 = OpIMul %uint %uint_8 %uint_3",
- "OpReturn",
- "OpFunctionEnd",
- // clang-format on
- };
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
- const std::string& output = std::get<0>(result);
- EXPECT_THAT(output,
- Not(MatchesRegex(".*OpConstant %uint 3.*OpConstant %uint 3.*")));
- EXPECT_THAT(output, Not(MatchesRegex(".*OpTypeInt 32 0.*OpTypeInt 32 0.*")));
- }
- // Test to make sure we generate the constants only once
- TEST_F(StrengthReductionBasicTest, BasicCreateOneConst) {
- const std::vector<const char*> text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %main \"main\"",
- "OpName %main \"main\"",
- "%void = OpTypeVoid",
- "%4 = OpTypeFunction %void",
- "%uint = OpTypeInt 32 0",
- "%uint_5 = OpConstant %uint 5",
- "%uint_9 = OpConstant %uint 9",
- "%uint_128 = OpConstant %uint 128",
- "%main = OpFunction %void None %4",
- "%8 = OpLabel",
- "%9 = OpIMul %uint %uint_5 %uint_128",
- "%10 = OpIMul %uint %uint_9 %uint_128",
- "OpReturn",
- "OpFunctionEnd"
- // clang-format on
- };
- auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
- JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false);
- EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
- const std::string& output = std::get<0>(result);
- EXPECT_THAT(output, Not(HasSubstr("OpIMul")));
- EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_7"));
- EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_9 %uint_7"));
- }
- // Test to make sure we generate the instructions in the correct position and
- // that the uses get replaced as well. Here we check that the use in the return
- // is replaced, we also check that we can replace two OpIMuls when one feeds the
- // other.
- TEST_F(StrengthReductionBasicTest, BasicCheckPositionAndReplacement) {
- // This is just the preamble to set up the test.
- const std::vector<const char*> common_text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Fragment %main \"main\" %gl_FragColor",
- "OpExecutionMode %main OriginUpperLeft",
- "OpName %main \"main\"",
- "OpName %foo_i1_ \"foo(i1;\"",
- "OpName %n \"n\"",
- "OpName %gl_FragColor \"gl_FragColor\"",
- "OpName %param \"param\"",
- "OpDecorate %gl_FragColor Location 0",
- "%void = OpTypeVoid",
- "%3 = OpTypeFunction %void",
- "%int = OpTypeInt 32 1",
- "%_ptr_Function_int = OpTypePointer Function %int",
- "%8 = OpTypeFunction %int %_ptr_Function_int",
- "%int_256 = OpConstant %int 256",
- "%int_2 = OpConstant %int 2",
- "%float = OpTypeFloat 32",
- "%v4float = OpTypeVector %float 4",
- "%_ptr_Output_v4float = OpTypePointer Output %v4float",
- "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
- "%float_1 = OpConstant %float 1",
- "%int_10 = OpConstant %int 10",
- "%float_0_375 = OpConstant %float 0.375",
- "%float_0_75 = OpConstant %float 0.75",
- "%uint = OpTypeInt 32 0",
- "%uint_8 = OpConstant %uint 8",
- "%uint_1 = OpConstant %uint 1",
- "%main = OpFunction %void None %3",
- "%5 = OpLabel",
- "%param = OpVariable %_ptr_Function_int Function",
- "OpStore %param %int_10",
- "%26 = OpFunctionCall %int %foo_i1_ %param",
- "%27 = OpConvertSToF %float %26",
- "%28 = OpFDiv %float %float_1 %27",
- "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1",
- "OpStore %gl_FragColor %31",
- "OpReturn",
- "OpFunctionEnd"
- // clang-format on
- };
- // This is the real test. The two OpIMul should be replaced. The expected
- // output is in |foo_after|.
- const std::vector<const char*> foo_before = {
- // clang-format off
- "%foo_i1_ = OpFunction %int None %8",
- "%n = OpFunctionParameter %_ptr_Function_int",
- "%11 = OpLabel",
- "%12 = OpLoad %int %n",
- "%14 = OpIMul %int %12 %int_256",
- "%16 = OpIMul %int %14 %int_2",
- "OpReturnValue %16",
- "OpFunctionEnd",
- // clang-format on
- };
- const std::vector<const char*> foo_after = {
- // clang-format off
- "%foo_i1_ = OpFunction %int None %8",
- "%n = OpFunctionParameter %_ptr_Function_int",
- "%11 = OpLabel",
- "%12 = OpLoad %int %n",
- "%33 = OpShiftLeftLogical %int %12 %uint_8",
- "%34 = OpShiftLeftLogical %int %33 %uint_1",
- "OpReturnValue %34",
- "OpFunctionEnd",
- // clang-format on
- };
- SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<StrengthReductionPass>(
- JoinAllInsts(Concat(common_text, foo_before)),
- JoinAllInsts(Concat(common_text, foo_after)),
- /* skip_nop = */ true, /* do_validate = */ true);
- }
- // Test that, when the result of an OpIMul instruction has more than 1 use, and
- // the instruction is replaced, all of the uses of the results are replace with
- // the new result.
- TEST_F(StrengthReductionBasicTest, BasicTestMultipleReplacements) {
- // This is just the preamble to set up the test.
- const std::vector<const char*> common_text = {
- // clang-format off
- "OpCapability Shader",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Fragment %main \"main\" %gl_FragColor",
- "OpExecutionMode %main OriginUpperLeft",
- "OpName %main \"main\"",
- "OpName %foo_i1_ \"foo(i1;\"",
- "OpName %n \"n\"",
- "OpName %gl_FragColor \"gl_FragColor\"",
- "OpName %param \"param\"",
- "OpDecorate %gl_FragColor Location 0",
- "%void = OpTypeVoid",
- "%3 = OpTypeFunction %void",
- "%int = OpTypeInt 32 1",
- "%_ptr_Function_int = OpTypePointer Function %int",
- "%8 = OpTypeFunction %int %_ptr_Function_int",
- "%int_256 = OpConstant %int 256",
- "%int_2 = OpConstant %int 2",
- "%float = OpTypeFloat 32",
- "%v4float = OpTypeVector %float 4",
- "%_ptr_Output_v4float = OpTypePointer Output %v4float",
- "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
- "%float_1 = OpConstant %float 1",
- "%int_10 = OpConstant %int 10",
- "%float_0_375 = OpConstant %float 0.375",
- "%float_0_75 = OpConstant %float 0.75",
- "%uint = OpTypeInt 32 0",
- "%uint_8 = OpConstant %uint 8",
- "%uint_1 = OpConstant %uint 1",
- "%main = OpFunction %void None %3",
- "%5 = OpLabel",
- "%param = OpVariable %_ptr_Function_int Function",
- "OpStore %param %int_10",
- "%26 = OpFunctionCall %int %foo_i1_ %param",
- "%27 = OpConvertSToF %float %26",
- "%28 = OpFDiv %float %float_1 %27",
- "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1",
- "OpStore %gl_FragColor %31",
- "OpReturn",
- "OpFunctionEnd"
- // clang-format on
- };
- // This is the real test. The two OpIMul instructions should be replaced. In
- // particular, we want to be sure that both uses of %16 are changed to use the
- // new result.
- const std::vector<const char*> foo_before = {
- // clang-format off
- "%foo_i1_ = OpFunction %int None %8",
- "%n = OpFunctionParameter %_ptr_Function_int",
- "%11 = OpLabel",
- "%12 = OpLoad %int %n",
- "%14 = OpIMul %int %12 %int_256",
- "%16 = OpIMul %int %14 %int_2",
- "%17 = OpIAdd %int %14 %16",
- "OpReturnValue %17",
- "OpFunctionEnd",
- // clang-format on
- };
- const std::vector<const char*> foo_after = {
- // clang-format off
- "%foo_i1_ = OpFunction %int None %8",
- "%n = OpFunctionParameter %_ptr_Function_int",
- "%11 = OpLabel",
- "%12 = OpLoad %int %n",
- "%34 = OpShiftLeftLogical %int %12 %uint_8",
- "%35 = OpShiftLeftLogical %int %34 %uint_1",
- "%17 = OpIAdd %int %34 %35",
- "OpReturnValue %17",
- "OpFunctionEnd",
- // clang-format on
- };
- SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<StrengthReductionPass>(
- JoinAllInsts(Concat(common_text, foo_before)),
- JoinAllInsts(Concat(common_text, foo_after)),
- /* skip_nop = */ true, /* do_validate = */ true);
- }
- } // namespace
- } // namespace opt
- } // namespace spvtools
|