mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-27 13:50:07 +00:00
7d01643132
When doing if-conversion, we do not currently move code out of the side nodes. The reason for this is that it can increase the number of instructions that get executed because both side nods will have to be executed now. In this commit, we add code to move an instruction, and all of the instructions it depends on, out of a side node and into the header of the selection construct. However to keep the cost down, we only do it when the two values in the OpPhi node compute the same value. This way we have to move only one of the instructions and the other becomes unused most of the time. So no real extra cost. Makes the value number table an alalysis in the ir context. Added more opcodes to list of code motion safe opcodes. Fixes #1526.
471 lines
13 KiB
C++
471 lines
13 KiB
C++
// 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 "assembly_builder.h"
|
|
#include "gmock/gmock.h"
|
|
#include "pass_fixture.h"
|
|
#include "pass_utils.h"
|
|
|
|
namespace {
|
|
|
|
using namespace spvtools;
|
|
|
|
using IfConversionTest = PassTest<::testing::Test>;
|
|
|
|
#ifdef SPIRV_EFFCEE
|
|
TEST_F(IfConversionTest, TestSimpleIfThenElse) {
|
|
const std::string text = R"(
|
|
; CHECK: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
|
|
; CHECK OpStore {{%\w+}} [[sel]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%11 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %11
|
|
%12 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %15 %16
|
|
%15 = OpLabel
|
|
OpBranch %14
|
|
%16 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpPhi %uint %uint_0 %15 %uint_1 %16
|
|
OpStore %2 %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, TestSimpleHalfIfTrue) {
|
|
const std::string text = R"(
|
|
; CHECK: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
|
|
; CHECK OpStore {{%\w+}} [[sel]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%11 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %11
|
|
%12 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %15 %14
|
|
%15 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpPhi %uint %uint_0 %15 %uint_1 %12
|
|
OpStore %2 %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, TestSimpleHalfIfExtraBlock) {
|
|
const std::string text = R"(
|
|
; CHECK: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
|
|
; CHECK OpStore {{%\w+}} [[sel]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%11 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %11
|
|
%12 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %15 %14
|
|
%15 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpPhi %uint %uint_0 %15 %uint_1 %12
|
|
OpStore %2 %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, TestSimpleHalfIfFalse) {
|
|
const std::string text = R"(
|
|
; CHECK: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
|
|
; CHECK OpStore {{%\w+}} [[sel]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%11 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %11
|
|
%12 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %14 %15
|
|
%15 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpPhi %uint %uint_0 %12 %uint_1 %15
|
|
OpStore %2 %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, TestVectorSplat) {
|
|
const std::string text = R"(
|
|
; CHECK: [[bool_vec:%\w+]] = OpTypeVector %bool 2
|
|
; CHECK: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: [[comp:%\w+]] = OpCompositeConstruct [[bool_vec]] %true %true
|
|
; CHECK: [[sel:%\w+]] = OpSelect {{%\w+}} [[comp]]
|
|
; CHECK OpStore {{%\w+}} [[sel]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%uint_vec2 = OpTypeVector %uint 2
|
|
%vec2_01 = OpConstantComposite %uint_vec2 %uint_0 %uint_1
|
|
%vec2_10 = OpConstantComposite %uint_vec2 %uint_1 %uint_0
|
|
%_ptr_Output_uint = OpTypePointer Output %uint_vec2
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%11 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %11
|
|
%12 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %15 %16
|
|
%15 = OpLabel
|
|
OpBranch %14
|
|
%16 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpPhi %uint_vec2 %vec2_01 %15 %vec2_10 %16
|
|
OpStore %2 %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, CodeMotionSameValue) {
|
|
const std::string text = R"(
|
|
; CHECK: [[var:%\w+]] = OpVariable
|
|
; CHECK: OpFunction
|
|
; CHECK: OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: [[add:%\w+]] = OpIAdd %uint %uint_0 %uint_1
|
|
; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None
|
|
; CHECK-NEXT: OpBranchConditional
|
|
; CHECK: [[merge_lab]] = OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: OpStore [[var]] [[add]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpSelectionMerge %12 None
|
|
OpBranchConditional %true %13 %15
|
|
%13 = OpLabel
|
|
%14 = OpIAdd %uint %uint_0 %uint_1
|
|
OpBranch %12
|
|
%15 = OpLabel
|
|
%16 = OpIAdd %uint %uint_0 %uint_1
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%17 = OpPhi %uint %16 %15 %14 %13
|
|
OpStore %2 %17
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, CodeMotionMultipleInstructions) {
|
|
const std::string text = R"(
|
|
; CHECK: [[var:%\w+]] = OpVariable
|
|
; CHECK: OpFunction
|
|
; CHECK: OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: [[a1:%\w+]] = OpIAdd %uint %uint_0 %uint_1
|
|
; CHECK: [[a2:%\w+]] = OpIAdd %uint [[a1]] %uint_1
|
|
; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None
|
|
; CHECK-NEXT: OpBranchConditional
|
|
; CHECK: [[merge_lab]] = OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: OpStore [[var]] [[a2]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpSelectionMerge %12 None
|
|
OpBranchConditional %true %13 %15
|
|
%13 = OpLabel
|
|
%a1 = OpIAdd %uint %uint_0 %uint_1
|
|
%a2 = OpIAdd %uint %a1 %uint_1
|
|
OpBranch %12
|
|
%15 = OpLabel
|
|
%b1 = OpIAdd %uint %uint_0 %uint_1
|
|
%b2 = OpIAdd %uint %b1 %uint_1
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%17 = OpPhi %uint %b2 %15 %a2 %13
|
|
OpStore %2 %17
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<opt::IfConversion>(text, true);
|
|
}
|
|
#endif // SPIRV_EFFCEE
|
|
|
|
TEST_F(IfConversionTest, NoCommonDominator) {
|
|
const std::string text = R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %8
|
|
%9 = OpLabel
|
|
OpBranch %10
|
|
%11 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%12 = OpPhi %uint %uint_0 %9 %uint_1 %11
|
|
OpStore %2 %12
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<opt::IfConversion>(text, text, true, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, LoopUntouched) {
|
|
const std::string text = R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%13 = OpPhi %uint %uint_0 %11 %uint_1 %12
|
|
OpLoopMerge %14 %12 None
|
|
OpBranchConditional %true %14 %12
|
|
%14 = OpLabel
|
|
OpStore %2 %13
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<opt::IfConversion>(text, text, true, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, TooManyPredecessors) {
|
|
const std::string text = R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpSelectionMerge %12 None
|
|
OpBranchConditional %true %13 %12
|
|
%13 = OpLabel
|
|
OpBranchConditional %true %14 %15
|
|
%14 = OpLabel
|
|
OpBranch %12
|
|
%15 = OpLabel
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%16 = OpPhi %uint %uint_0 %11 %uint_0 %14 %uint_1 %15
|
|
OpStore %2 %16
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<opt::IfConversion>(text, text, true, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, NoCodeMotion) {
|
|
const std::string text = R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpSelectionMerge %12 None
|
|
OpBranchConditional %true %13 %12
|
|
%13 = OpLabel
|
|
%14 = OpIAdd %uint %uint_0 %uint_1
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%15 = OpPhi %uint %uint_0 %11 %14 %13
|
|
OpStore %2 %15
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<opt::IfConversion>(text, text, true, true);
|
|
}
|
|
|
|
TEST_F(IfConversionTest, NoCodeMotionImmovableInst) {
|
|
const std::string text = R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %1 "func" %2
|
|
%void = OpTypeVoid
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%_ptr_Output_uint = OpTypePointer Output %uint
|
|
%2 = OpVariable %_ptr_Output_uint Output
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpFunction %void None %8
|
|
%11 = OpLabel
|
|
OpSelectionMerge %12 None
|
|
OpBranchConditional %true %13 %14
|
|
%13 = OpLabel
|
|
OpSelectionMerge %15 None
|
|
OpBranchConditional %true %16 %15
|
|
%16 = OpLabel
|
|
%17 = OpIAdd %uint %uint_0 %uint_1
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%18 = OpPhi %uint %uint_0 %13 %17 %16
|
|
%19 = OpIAdd %uint %18 %uint_1
|
|
OpBranch %12
|
|
%14 = OpLabel
|
|
OpSelectionMerge %20 None
|
|
OpBranchConditional %true %21 %20
|
|
%21 = OpLabel
|
|
%22 = OpIAdd %uint %uint_0 %uint_1
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpPhi %uint %uint_0 %14 %22 %21
|
|
%24 = OpIAdd %uint %23 %uint_1
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%25 = OpPhi %uint %24 %20 %19 %15
|
|
OpStore %2 %25
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<opt::IfConversion>(text, text, true, true);
|
|
}
|
|
|
|
} // anonymous namespace
|