// Copyright (c) 2018 Google LLC. // // 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 #include #include "gmock/gmock.h" #include "source/opt/register_pressure.h" #include "test/opt/assembly_builder.h" #include "test/opt/function_utils.h" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" namespace spvtools { namespace opt { namespace { using ::testing::UnorderedElementsAre; using PassClassTest = PassTest<::testing::Test>; void CompareSets(const std::unordered_set& computed, const std::unordered_set& expected) { for (Instruction* insn : computed) { EXPECT_TRUE(expected.count(insn->result_id())) << "Unexpected instruction in live set: " << *insn; } EXPECT_EQ(computed.size(), expected.size()); } /* Generated from the following GLSL #version 330 in vec4 BaseColor; flat in int Count; void main() { vec4 color = BaseColor; vec4 acc; if (Count == 0) { acc = color; } else { acc = color + vec4(0,1,2,0); } gl_FragColor = acc + color; } */ TEST_F(PassClassTest, LivenessWithIf) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %11 %15 %32 OpExecutionMode %4 OriginLowerLeft OpSource GLSL 330 OpName %4 "main" OpName %11 "BaseColor" OpName %15 "Count" OpName %32 "gl_FragColor" OpDecorate %11 Location 0 OpDecorate %15 Flat OpDecorate %15 Location 0 OpDecorate %32 Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeVector %6 4 %10 = OpTypePointer Input %7 %11 = OpVariable %10 Input %13 = OpTypeInt 32 1 %14 = OpTypePointer Input %13 %15 = OpVariable %14 Input %17 = OpConstant %13 0 %18 = OpTypeBool %26 = OpConstant %6 0 %27 = OpConstant %6 1 %28 = OpConstant %6 2 %29 = OpConstantComposite %7 %26 %27 %28 %26 %31 = OpTypePointer Output %7 %32 = OpVariable %31 Output %4 = OpFunction %2 None %3 %5 = OpLabel %12 = OpLoad %7 %11 %16 = OpLoad %13 %15 %19 = OpIEqual %18 %16 %17 OpSelectionMerge %21 None OpBranchConditional %19 %20 %24 %20 = OpLabel OpBranch %21 %24 = OpLabel %30 = OpFAdd %7 %12 %29 OpBranch %21 %21 = OpLabel %36 = OpPhi %7 %12 %20 %30 %24 %35 = OpFAdd %7 %36 %12 OpStore %32 %35 OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); Module* module = context->module(); EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" << text << std::endl; Function* f = &*module->begin(); LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); const RegisterLiveness* register_liveness = liveness_analysis->Get(f); { SCOPED_TRACE("Block 5"); auto live_sets = register_liveness->Get(5); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 15, // %15 = OpVariable %14 Input 32, // %32 = OpVariable %31 Output }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 12, // %12 = OpLoad %7 %11 32, // %32 = OpVariable %31 Output }; CompareSets(live_sets->live_out_, live_out); } { SCOPED_TRACE("Block 20"); auto live_sets = register_liveness->Get(20); std::unordered_set live_inout{ 12, // %12 = OpLoad %7 %11 32, // %32 = OpVariable %31 Output }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); } { SCOPED_TRACE("Block 24"); auto live_sets = register_liveness->Get(24); std::unordered_set live_in{ 12, // %12 = OpLoad %7 %11 32, // %32 = OpVariable %31 Output }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 12, // %12 = OpLoad %7 %11 30, // %30 = OpFAdd %7 %12 %29 32, // %32 = OpVariable %31 Output }; CompareSets(live_sets->live_out_, live_out); } { SCOPED_TRACE("Block 21"); auto live_sets = register_liveness->Get(21); std::unordered_set live_in{ 12, // %12 = OpLoad %7 %11 32, // %32 = OpVariable %31 Output 36, // %36 = OpPhi %7 %12 %20 %30 %24 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{}; CompareSets(live_sets->live_out_, live_out); } } /* Generated from the following GLSL #version 330 in vec4 bigColor; in vec4 BaseColor; in float f; flat in int Count; flat in uvec4 v4; void main() { vec4 color = BaseColor; for (int i = 0; i < Count; ++i) color += bigColor; float sum = 0.0; for (int i = 0; i < 4; ++i) { float acc = 0.0; if (sum == 0.0) { acc = v4[i]; } else { acc = BaseColor[i]; } sum += acc + v4[i]; } vec4 tv4; for (int i = 0; i < 4; ++i) tv4[i] = v4[i] * 4u; color += vec4(sum) + tv4; vec4 r; r.xyz = BaseColor.xyz; for (int i = 0; i < Count; ++i) r.w = f; color.xyz += r.xyz; for (int i = 0; i < 16; i += 4) for (int j = 0; j < 4; j++) color *= f; gl_FragColor = color + tv4; } */ TEST_F(PassClassTest, RegisterLiveness) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %11 %24 %28 %55 %124 %176 OpExecutionMode %4 OriginLowerLeft OpSource GLSL 330 OpName %4 "main" OpName %11 "BaseColor" OpName %24 "Count" OpName %28 "bigColor" OpName %55 "v4" OpName %84 "tv4" OpName %124 "f" OpName %176 "gl_FragColor" OpDecorate %11 Location 0 OpDecorate %24 Flat OpDecorate %24 Location 0 OpDecorate %28 Location 0 OpDecorate %55 Flat OpDecorate %55 Location 0 OpDecorate %124 Location 0 OpDecorate %176 Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeVector %6 4 %8 = OpTypePointer Function %7 %10 = OpTypePointer Input %7 %11 = OpVariable %10 Input %13 = OpTypeInt 32 1 %16 = OpConstant %13 0 %23 = OpTypePointer Input %13 %24 = OpVariable %23 Input %26 = OpTypeBool %28 = OpVariable %10 Input %33 = OpConstant %13 1 %35 = OpTypePointer Function %6 %37 = OpConstant %6 0 %45 = OpConstant %13 4 %52 = OpTypeInt 32 0 %53 = OpTypeVector %52 4 %54 = OpTypePointer Input %53 %55 = OpVariable %54 Input %57 = OpTypePointer Input %52 %63 = OpTypePointer Input %6 %89 = OpConstant %52 4 %102 = OpTypeVector %6 3 %124 = OpVariable %63 Input %158 = OpConstant %13 16 %175 = OpTypePointer Output %7 %176 = OpVariable %175 Output %195 = OpUndef %7 %4 = OpFunction %2 None %3 %5 = OpLabel %84 = OpVariable %8 Function %12 = OpLoad %7 %11 OpBranch %17 %17 = OpLabel %191 = OpPhi %7 %12 %5 %31 %18 %184 = OpPhi %13 %16 %5 %34 %18 %25 = OpLoad %13 %24 %27 = OpSLessThan %26 %184 %25 OpLoopMerge %19 %18 None OpBranchConditional %27 %18 %19 %18 = OpLabel %29 = OpLoad %7 %28 %31 = OpFAdd %7 %191 %29 %34 = OpIAdd %13 %184 %33 OpBranch %17 %19 = OpLabel OpBranch %39 %39 = OpLabel %188 = OpPhi %6 %37 %19 %73 %51 %185 = OpPhi %13 %16 %19 %75 %51 %46 = OpSLessThan %26 %185 %45 OpLoopMerge %41 %51 None OpBranchConditional %46 %40 %41 %40 = OpLabel %49 = OpFOrdEqual %26 %188 %37 OpSelectionMerge %51 None OpBranchConditional %49 %50 %61 %50 = OpLabel %58 = OpAccessChain %57 %55 %185 %59 = OpLoad %52 %58 %60 = OpConvertUToF %6 %59 OpBranch %51 %61 = OpLabel %64 = OpAccessChain %63 %11 %185 %65 = OpLoad %6 %64 OpBranch %51 %51 = OpLabel %210 = OpPhi %6 %60 %50 %65 %61 %68 = OpAccessChain %57 %55 %185 %69 = OpLoad %52 %68 %70 = OpConvertUToF %6 %69 %71 = OpFAdd %6 %210 %70 %73 = OpFAdd %6 %188 %71 %75 = OpIAdd %13 %185 %33 OpBranch %39 %41 = OpLabel OpBranch %77 %77 = OpLabel %186 = OpPhi %13 %16 %41 %94 %78 %83 = OpSLessThan %26 %186 %45 OpLoopMerge %79 %78 None OpBranchConditional %83 %78 %79 %78 = OpLabel %87 = OpAccessChain %57 %55 %186 %88 = OpLoad %52 %87 %90 = OpIMul %52 %88 %89 %91 = OpConvertUToF %6 %90 %92 = OpAccessChain %35 %84 %186 OpStore %92 %91 %94 = OpIAdd %13 %186 %33 OpBranch %77 %79 = OpLabel %96 = OpCompositeConstruct %7 %188 %188 %188 %188 %97 = OpLoad %7 %84 %98 = OpFAdd %7 %96 %97 %100 = OpFAdd %7 %191 %98 %104 = OpVectorShuffle %102 %12 %12 0 1 2 %106 = OpVectorShuffle %7 %195 %104 4 5 6 3 OpBranch %108 %108 = OpLabel %197 = OpPhi %7 %106 %79 %208 %133 %196 = OpPhi %13 %16 %79 %143 %133 %115 = OpSLessThan %26 %196 %25 OpLoopMerge %110 %133 None OpBranchConditional %115 %109 %110 %109 = OpLabel OpBranch %117 %117 = OpLabel %209 = OpPhi %7 %197 %109 %181 %118 %204 = OpPhi %13 %16 %109 %129 %118 %123 = OpSLessThan %26 %204 %45 OpLoopMerge %119 %118 None OpBranchConditional %123 %118 %119 %118 = OpLabel %125 = OpLoad %6 %124 %181 = OpCompositeInsert %7 %125 %209 3 %129 = OpIAdd %13 %204 %33 OpBranch %117 %119 = OpLabel OpBranch %131 %131 = OpLabel %208 = OpPhi %7 %209 %119 %183 %132 %205 = OpPhi %13 %16 %119 %141 %132 %137 = OpSLessThan %26 %205 %45 OpLoopMerge %133 %132 None OpBranchConditional %137 %132 %133 %132 = OpLabel %138 = OpLoad %6 %124 %183 = OpCompositeInsert %7 %138 %208 3 %141 = OpIAdd %13 %205 %33 OpBranch %131 %133 = OpLabel %143 = OpIAdd %13 %196 %33 OpBranch %108 %110 = OpLabel %145 = OpVectorShuffle %102 %197 %197 0 1 2 %147 = OpVectorShuffle %102 %100 %100 0 1 2 %148 = OpFAdd %102 %147 %145 %150 = OpVectorShuffle %7 %100 %148 4 5 6 3 OpBranch %152 %152 = OpLabel %200 = OpPhi %7 %150 %110 %203 %163 %199 = OpPhi %13 %16 %110 %174 %163 %159 = OpSLessThan %26 %199 %158 OpLoopMerge %154 %163 None OpBranchConditional %159 %153 %154 %153 = OpLabel OpBranch %161 %161 = OpLabel %203 = OpPhi %7 %200 %153 %170 %162 %201 = OpPhi %13 %16 %153 %172 %162 %167 = OpSLessThan %26 %201 %45 OpLoopMerge %163 %162 None OpBranchConditional %167 %162 %163 %162 = OpLabel %168 = OpLoad %6 %124 %170 = OpVectorTimesScalar %7 %203 %168 %172 = OpIAdd %13 %201 %33 OpBranch %161 %163 = OpLabel %174 = OpIAdd %13 %199 %45 OpBranch %152 %154 = OpLabel %178 = OpLoad %7 %84 %179 = OpFAdd %7 %200 %178 OpStore %176 %179 OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); Module* module = context->module(); EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" << text << std::endl; Function* f = &*module->begin(); LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); const RegisterLiveness* register_liveness = liveness_analysis->Get(f); LoopDescriptor& ld = *context->GetLoopDescriptor(f); { SCOPED_TRACE("Block 5"); auto live_sets = register_liveness->Get(5); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 24, // %24 = OpVariable %23 Input 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 24, // %24 = OpVariable %23 Input 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 17"); auto live_sets = register_liveness->Get(17); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 24, // %24 = OpVariable %23 Input 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 184, // %184 = OpPhi %13 %16 %5 %34 %18 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 184, // %184 = OpPhi %13 %16 %5 %34 %18 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 11u); } { SCOPED_TRACE("Block 18"); auto live_sets = register_liveness->Get(18); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 24, // %24 = OpVariable %23 Input 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 184, // %184 = OpPhi %13 %16 %5 %34 %18 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 24, // %24 = OpVariable %23 Input 28, // %28 = OpVariable %10 Input 31, // %31 = OpFAdd %7 %191 %29 34, // %34 = OpIAdd %13 %184 %33 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 12u); } { SCOPED_TRACE("Block 19"); auto live_sets = register_liveness->Get(19); std::unordered_set live_inout{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 39"); auto live_sets = register_liveness->Get(39); std::unordered_set live_inout{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 11u); } { SCOPED_TRACE("Block 40"); auto live_sets = register_liveness->Get(40); std::unordered_set live_inout{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 11u); } { SCOPED_TRACE("Block 50"); auto live_sets = register_liveness->Get(50); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 60, // %60 = OpConvertUToF %6 %59 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 12u); } { SCOPED_TRACE("Block 61"); auto live_sets = register_liveness->Get(61); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 65, // %65 = OpLoad %6 %64 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 12u); } { SCOPED_TRACE("Block 51"); auto live_sets = register_liveness->Get(51); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 210, // %210 = OpPhi %6 %60 %50 %65 %61 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 73, // %73 = OpFAdd %6 %188 %71 75, // %75 = OpIAdd %13 %185 %33 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 13u); } { SCOPED_TRACE("Block 41"); auto live_sets = register_liveness->Get(41); std::unordered_set live_inout{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 77"); auto live_sets = register_liveness->Get(77); std::unordered_set live_inout{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 186, // %186 = OpPhi %13 %16 %41 %94 %78 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 10u); } { SCOPED_TRACE("Block 78"); auto live_sets = register_liveness->Get(78); std::unordered_set live_in{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 186, // %186 = OpPhi %13 %16 %41 %94 %78 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 94, // %94 = OpIAdd %13 %186 %33 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 11u); } { SCOPED_TRACE("Block 79"); auto live_sets = register_liveness->Get(79); std::unordered_set live_in{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 106, // %106 = OpVectorShuffle %7 %195 %104 4 5 6 3 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 9u); } { SCOPED_TRACE("Block 108"); auto live_sets = register_liveness->Get(108); std::unordered_set live_in{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 197, // %197 = OpPhi %7 %106 %79 %208 %133 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 197, // %197 = OpPhi %7 %106 %79 %208 %133 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 109"); auto live_sets = register_liveness->Get(109); std::unordered_set live_inout{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 197, // %197 = OpPhi %7 %106 %79 %208 %133 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 7u); } { SCOPED_TRACE("Block 117"); auto live_sets = register_liveness->Get(117); std::unordered_set live_inout{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 204, // %204 = OpPhi %13 %16 %109 %129 %118 209, // %209 = OpPhi %7 %197 %109 %181 %118 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 9u); } { SCOPED_TRACE("Block 118"); auto live_sets = register_liveness->Get(118); std::unordered_set live_in{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 204, // %204 = OpPhi %13 %16 %109 %129 %118 209, // %209 = OpPhi %7 %197 %109 %181 %118 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 129, // %129 = OpIAdd %13 %204 %33 176, // %176 = OpVariable %175 Output 181, // %181 = OpCompositeInsert %7 %125 %209 3 196, // %196 = OpPhi %13 %16 %79 %143 %133 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 10u); } { SCOPED_TRACE("Block 119"); auto live_sets = register_liveness->Get(119); std::unordered_set live_inout{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 209, // %209 = OpPhi %7 %197 %109 %181 %118 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 7u); } { SCOPED_TRACE("Block 131"); auto live_sets = register_liveness->Get(131); std::unordered_set live_inout{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 205, // %205 = OpPhi %13 %16 %119 %141 %132 208, // %208 = OpPhi %7 %209 %119 %183 %132 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 9u); } { SCOPED_TRACE("Block 132"); auto live_sets = register_liveness->Get(132); std::unordered_set live_in{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 205, // %205 = OpPhi %13 %16 %119 %141 %132 208, // %208 = OpPhi %7 %209 %119 %183 %132 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 141, // %141 = OpIAdd %13 %205 %33 176, // %176 = OpVariable %175 Output 183, // %183 = OpCompositeInsert %7 %138 %208 3 196, // %196 = OpPhi %13 %16 %79 %143 %133 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 10u); } { SCOPED_TRACE("Block 133"); auto live_sets = register_liveness->Get(133); std::unordered_set live_in{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 196, // %196 = OpPhi %13 %16 %79 %143 %133 208, // %208 = OpPhi %7 %209 %119 %183 %132 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 25, // %25 = OpLoad %13 %24 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 143, // %143 = OpIAdd %13 %196 %33 176, // %176 = OpVariable %175 Output 208, // %208 = OpPhi %7 %209 %119 %183 %132 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 110"); auto live_sets = register_liveness->Get(110); std::unordered_set live_in{ 84, // %84 = OpVariable %8 Function 100, // %100 = OpFAdd %7 %191 %98 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 197, // %197 = OpPhi %7 %106 %79 %208 %133 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 150, // %150 = OpVectorShuffle %7 %100 %148 4 5 6 3 176, // %176 = OpVariable %175 Output }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 7u); } { SCOPED_TRACE("Block 152"); auto live_sets = register_liveness->Get(152); std::unordered_set live_inout{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 200, // %200 = OpPhi %7 %150 %110 %203 %163 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 6u); } { SCOPED_TRACE("Block 153"); auto live_sets = register_liveness->Get(153); std::unordered_set live_inout{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 200, // %200 = OpPhi %7 %150 %110 %203 %163 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 5u); } { SCOPED_TRACE("Block 161"); auto live_sets = register_liveness->Get(161); std::unordered_set live_inout{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 201, // %201 = OpPhi %13 %16 %153 %172 %162 203, // %203 = OpPhi %7 %200 %153 %170 %162 }; CompareSets(live_sets->live_in_, live_inout); CompareSets(live_sets->live_out_, live_inout); EXPECT_EQ(live_sets->used_registers_, 7u); } { SCOPED_TRACE("Block 162"); auto live_sets = register_liveness->Get(162); std::unordered_set live_in{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 201, // %201 = OpPhi %13 %16 %153 %172 %162 203, // %203 = OpPhi %7 %200 %153 %170 %162 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 170, // %170 = OpVectorTimesScalar %7 %203 %168 172, // %172 = OpIAdd %13 %201 %33 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 8u); } { SCOPED_TRACE("Block 163"); auto live_sets = register_liveness->Get(163); std::unordered_set live_in{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 199, // %199 = OpPhi %13 %16 %110 %174 %163 203, // %203 = OpPhi %7 %200 %153 %170 %162 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{ 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 174, // %174 = OpIAdd %13 %199 %45 176, // %176 = OpVariable %175 Output 203, // %203 = OpPhi %7 %200 %153 %170 %162 }; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 6u); } { SCOPED_TRACE("Block 154"); auto live_sets = register_liveness->Get(154); std::unordered_set live_in{ 84, // %84 = OpVariable %8 Function 176, // %176 = OpVariable %175 Output 200, // %200 = OpPhi %7 %150 %110 %203 %163 }; CompareSets(live_sets->live_in_, live_in); std::unordered_set live_out{}; CompareSets(live_sets->live_out_, live_out); EXPECT_EQ(live_sets->used_registers_, 4u); } { SCOPED_TRACE("Compute loop pressure"); RegisterLiveness::RegionRegisterLiveness loop_reg_pressure; register_liveness->ComputeLoopRegisterPressure(*ld[39], &loop_reg_pressure); // Generate(*context->cfg()->block(39), &loop_reg_pressure); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(loop_reg_pressure.live_in_, live_in); std::unordered_set live_out{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(loop_reg_pressure.live_out_, live_out); EXPECT_EQ(loop_reg_pressure.used_registers_, 13u); } { SCOPED_TRACE("Loop Fusion simulation"); RegisterLiveness::RegionRegisterLiveness simulation_resut; register_liveness->SimulateFusion(*ld[17], *ld[39], &simulation_resut); std::unordered_set live_in{ 11, // %11 = OpVariable %10 Input 12, // %12 = OpLoad %7 %11 24, // %24 = OpVariable %23 Input 25, // %25 = OpLoad %13 %24 28, // %28 = OpVariable %10 Input 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 184, // %184 = OpPhi %13 %16 %5 %34 %18 185, // %185 = OpPhi %13 %16 %19 %75 %51 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(simulation_resut.live_in_, live_in); std::unordered_set live_out{ 12, // %12 = OpLoad %7 %11 25, // %25 = OpLoad %13 %24 55, // %55 = OpVariable %54 Input 84, // %84 = OpVariable %8 Function 124, // %124 = OpVariable %63 Input 176, // %176 = OpVariable %175 Output 188, // %188 = OpPhi %6 %37 %19 %73 %51 191, // %191 = OpPhi %7 %12 %5 %31 %18 }; CompareSets(simulation_resut.live_out_, live_out); EXPECT_EQ(simulation_resut.used_registers_, 17u); } } TEST_F(PassClassTest, FissionSimulation) { const std::string source = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource GLSL 430 OpName %2 "main" OpName %3 "i" OpName %4 "A" OpName %5 "B" %6 = OpTypeVoid %7 = OpTypeFunction %6 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpConstant %8 0 %11 = OpConstant %8 10 %12 = OpTypeBool %13 = OpTypeFloat 32 %14 = OpTypeInt 32 0 %15 = OpConstant %14 10 %16 = OpTypeArray %13 %15 %17 = OpTypePointer Function %16 %18 = OpTypePointer Function %13 %19 = OpConstant %8 1 %2 = OpFunction %6 None %7 %20 = OpLabel %3 = OpVariable %9 Function %4 = OpVariable %17 Function %5 = OpVariable %17 Function OpBranch %21 %21 = OpLabel %22 = OpPhi %8 %10 %20 %23 %24 OpLoopMerge %25 %24 None OpBranch %26 %26 = OpLabel %27 = OpSLessThan %12 %22 %11 OpBranchConditional %27 %28 %25 %28 = OpLabel %29 = OpAccessChain %18 %5 %22 %30 = OpLoad %13 %29 %31 = OpAccessChain %18 %4 %22 OpStore %31 %30 %32 = OpAccessChain %18 %4 %22 %33 = OpLoad %13 %32 %34 = OpAccessChain %18 %5 %22 OpStore %34 %33 OpBranch %24 %24 = OpLabel %23 = OpIAdd %8 %22 %19 OpBranch %21 %25 = OpLabel OpStore %3 %22 OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); Module* module = context->module(); EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" << source << std::endl; Function* f = &*module->begin(); LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); const RegisterLiveness* register_liveness = liveness_analysis->Get(f); LoopDescriptor& ld = *context->GetLoopDescriptor(f); analysis::DefUseManager& def_use_mgr = *context->get_def_use_mgr(); { RegisterLiveness::RegionRegisterLiveness l1_sim_resut; RegisterLiveness::RegionRegisterLiveness l2_sim_resut; std::unordered_set moved_instructions{ def_use_mgr.GetDef(29), def_use_mgr.GetDef(30), def_use_mgr.GetDef(31), def_use_mgr.GetDef(31)->NextNode()}; std::unordered_set copied_instructions{ def_use_mgr.GetDef(22), def_use_mgr.GetDef(27), def_use_mgr.GetDef(27)->NextNode(), def_use_mgr.GetDef(23)}; register_liveness->SimulateFission(*ld[21], moved_instructions, copied_instructions, &l1_sim_resut, &l2_sim_resut); { SCOPED_TRACE("L1 simulation"); std::unordered_set live_in{ 3, // %3 = OpVariable %9 Function 4, // %4 = OpVariable %17 Function 5, // %5 = OpVariable %17 Function 22, // %22 = OpPhi %8 %10 %20 %23 %24 }; CompareSets(l1_sim_resut.live_in_, live_in); std::unordered_set live_out{ 3, // %3 = OpVariable %9 Function 4, // %4 = OpVariable %17 Function 5, // %5 = OpVariable %17 Function 22, // %22 = OpPhi %8 %10 %20 %23 %24 }; CompareSets(l1_sim_resut.live_out_, live_out); EXPECT_EQ(l1_sim_resut.used_registers_, 6u); } { SCOPED_TRACE("L2 simulation"); std::unordered_set live_in{ 3, // %3 = OpVariable %9 Function 4, // %4 = OpVariable %17 Function 5, // %5 = OpVariable %17 Function 22, // %22 = OpPhi %8 %10 %20 %23 %24 }; CompareSets(l2_sim_resut.live_in_, live_in); std::unordered_set live_out{ 3, // %3 = OpVariable %9 Function 22, // %22 = OpPhi %8 %10 %20 %23 %24 }; CompareSets(l2_sim_resut.live_out_, live_out); EXPECT_EQ(l2_sim_resut.used_registers_, 6u); } } } // Test that register liveness does not fail when there is an unreachable block. // We are not testing if the liveness is computed correctly because the specific // results do not matter for unreachable blocks. TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginLowerLeft OpSource GLSL 330 OpSourceExtension "GL_ARB_shading_language_420pack" %void = OpTypeVoid %4 = OpTypeFunction %void %2 = OpFunction %void None %4 %5 = OpLabel OpBranch %6 %6 = OpLabel OpLoopMerge %7 %8 None OpBranch %9 %9 = OpLabel OpBranch %7 %8 = OpLabel OpBranch %6 %7 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); Module* module = context->module(); EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" << text << std::endl; Function* f = &*module->begin(); LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); liveness_analysis->Get(f); } } // namespace } // namespace opt } // namespace spvtools