// 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 #include "gmock/gmock.h" #include "source/opt/build_module.h" #include "source/opt/value_number_table.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 LocalRedundancyEliminationTest = PassTest<::testing::Test>; // Remove an instruction when it was already computed. TEST_F(LocalRedundancyEliminationTest, RemoveRedundantAdd) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource GLSL 430 %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeFloat 32 %6 = OpTypePointer Function %5 %2 = OpFunction %3 None %4 %7 = OpLabel %8 = OpVariable %6 Function %9 = OpLoad %5 %8 %10 = OpFAdd %5 %9 %9 ; CHECK: OpFAdd ; CHECK-NOT: OpFAdd %11 = OpFAdd %5 %9 %9 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(text, false); } // Make sure we keep instruction that are different, but look similar. TEST_F(LocalRedundancyEliminationTest, KeepDifferentAdd) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource GLSL 430 %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeFloat 32 %6 = OpTypePointer Function %5 %2 = OpFunction %3 None %4 %7 = OpLabel %8 = OpVariable %6 Function %9 = OpLoad %5 %8 %10 = OpFAdd %5 %9 %9 ; CHECK: OpFAdd OpStore %8 %10 %11 = OpLoad %5 %8 ; CHECK: %11 = OpLoad %12 = OpFAdd %5 %11 %11 ; CHECK: OpFAdd [[:%\w+]] %11 %11 OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, false); } // This test is check that the values are being propagated properly, and that // we are able to identify sequences of instruction that are not needed. TEST_F(LocalRedundancyEliminationTest, RemoveMultipleInstructions) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource GLSL 430 %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeFloat 32 %6 = OpTypePointer Uniform %5 %8 = OpVariable %6 Uniform %2 = OpFunction %3 None %4 %7 = OpLabel ; CHECK: [[r1:%\w+]] = OpLoad %9 = OpLoad %5 %8 ; CHECK-NEXT: [[r2:%\w+]] = OpFAdd [[:%\w+]] [[r1]] [[r1]] %10 = OpFAdd %5 %9 %9 ; CHECK-NEXT: [[r3:%\w+]] = OpFMul [[:%\w+]] [[r2]] [[r1]] %11 = OpFMul %5 %10 %9 ; CHECK-NOT: OpLoad %12 = OpLoad %5 %8 ; CHECK-NOT: OpFAdd [[:\w+]] %12 %12 %13 = OpFAdd %5 %12 %12 ; CHECK-NOT: OpFMul %14 = OpFMul %5 %13 %12 ; CHECK-NEXT: [[:%\w+]] = OpFAdd [[:%\w+]] [[r3]] [[r3]] %15 = OpFAdd %5 %14 %11 OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, false); } // Redundant instructions in different blocks should be kept. TEST_F(LocalRedundancyEliminationTest, KeepInstructionsInDifferentBlocks) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource GLSL 430 %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeFloat 32 %6 = OpTypePointer Function %5 %2 = OpFunction %3 None %4 %bb1 = OpLabel %8 = OpVariable %6 Function %9 = OpLoad %5 %8 %10 = OpFAdd %5 %9 %9 ; CHECK: OpFAdd OpBranch %bb2 %bb2 = OpLabel ; CHECK: OpFAdd %11 = OpFAdd %5 %9 %9 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(text, false); } TEST_F(LocalRedundancyEliminationTest, StorageBufferIdentification) { const std::string text = R"( ; CHECK: [[gep:%\w+]] = OpAccessChain ; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] ; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] ; CHECK: OpStore [[gep]] [[add]] ; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] ; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] ; CHECK: OpStore [[gep]] [[add]] OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %block BufferBlock OpMemberDecorate %block 0 Offset 0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %block = OpTypeStruct %int %array = OpTypeArray %block %int_1 %ptr_ssbo_array = OpTypePointer Uniform %array %ptr_ssbo_int = OpTypePointer Uniform %int %var = OpVariable %ptr_ssbo_array Uniform %void_fn = OpTypeFunction %void %fn = OpFunction %void None %void_fn %entry = OpLabel %gep1 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 %ld1 = OpLoad %int %gep1 %add1 = OpIAdd %int %ld1 %int_1 %gep2 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 OpStore %gep2 %add1 %gep3 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 %ld3 = OpLoad %int %gep3 %add3 = OpIAdd %int %ld3 %int_1 %gep4 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 OpStore %gep4 %add3 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(text, true); } } // namespace } // namespace opt } // namespace spvtools