mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 21:10:04 +00:00
4fa1a6f9b4
CCP does not want to fold an instruction unless it folds to a constant. There is an asser to check for this. The question if a spec constant counts as a constant. The constant folder considers a spec constant a constand, but CCP does not. I've fixed the assert in CCP to match what the folder does. It should not require any new changes to CCP.
1349 lines
45 KiB
C++
1349 lines
45 KiB
C++
// 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 <string>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "source/opt/ccp_pass.h"
|
|
#include "test/opt/pass_fixture.h"
|
|
#include "test/opt/pass_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
|
|
using CCPTest = PassTest<::testing::Test>;
|
|
|
|
TEST_F(CCPTest, PropagateThroughPhis) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %x %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %x "x"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %x Flat
|
|
OpDecorate %x Location 0
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_4 = OpConstant %int 4
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Input_int = OpTypePointer Input %int
|
|
%x = OpVariable %_ptr_Input_int Input
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%main = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
%5 = OpLoad %int %x
|
|
%9 = OpIAdd %int %int_1 %int_3
|
|
%6 = OpSGreaterThan %bool %5 %int_3
|
|
OpSelectionMerge %25 None
|
|
OpBranchConditional %6 %22 %23
|
|
%22 = OpLabel
|
|
|
|
; CHECK: OpCopyObject %int %int_4
|
|
%7 = OpCopyObject %int %9
|
|
|
|
OpBranch %25
|
|
%23 = OpLabel
|
|
%8 = OpCopyObject %int %int_4
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
|
|
; %int_4 should have propagated to both OpPhi operands.
|
|
; CHECK: OpPhi %int %int_4 {{%\d+}} %int_4 {{%\d+}}
|
|
%35 = OpPhi %int %7 %22 %8 %23
|
|
|
|
; This function always returns 4. DCE should get rid of everything else.
|
|
; CHECK OpStore %outparm %int_4
|
|
OpStore %outparm %35
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, SimplifyConditionals) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_4 = OpConstant %int 4
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%main = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
%9 = OpIAdd %int %int_4 %int_3
|
|
%6 = OpSGreaterThan %bool %9 %int_3
|
|
OpSelectionMerge %25 None
|
|
; CHECK: OpBranchConditional %true [[bb_taken:%\d+]] [[bb_not_taken:%\d+]]
|
|
OpBranchConditional %6 %22 %23
|
|
; CHECK: [[bb_taken]] = OpLabel
|
|
%22 = OpLabel
|
|
; CHECK: OpCopyObject %int %int_7
|
|
%7 = OpCopyObject %int %9
|
|
OpBranch %25
|
|
; CHECK: [[bb_not_taken]] = OpLabel
|
|
%23 = OpLabel
|
|
; CHECK: [[id_not_evaluated:%\d+]] = OpCopyObject %int %int_4
|
|
%8 = OpCopyObject %int %int_4
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
|
|
; %int_7 should have propagated to the first OpPhi operand. But the else branch
|
|
; is not executable (conditional is always true), so no values should be
|
|
; propagated there and the value of the OpPhi should always be %int_7.
|
|
; CHECK: OpPhi %int %int_7 [[bb_taken]] [[id_not_evaluated]] [[bb_not_taken]]
|
|
%35 = OpPhi %int %7 %22 %8 %23
|
|
|
|
; Only the true path of the conditional is ever executed. The output of this
|
|
; function is always %int_7.
|
|
; CHECK: OpStore %outparm %int_7
|
|
OpStore %outparm %35
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, SimplifySwitches) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_23 = OpConstant %int 23
|
|
%int_42 = OpConstant %int 42
|
|
%int_14 = OpConstant %int 14
|
|
%int_15 = OpConstant %int 15
|
|
%int_4 = OpConstant %int 4
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%main = OpFunction %void None %6
|
|
%15 = OpLabel
|
|
OpSelectionMerge %17 None
|
|
OpSwitch %int_23 %17 10 %18 13 %19 23 %20
|
|
%18 = OpLabel
|
|
OpBranch %17
|
|
%19 = OpLabel
|
|
OpBranch %17
|
|
%20 = OpLabel
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%24 = OpPhi %int %int_23 %15 %int_42 %18 %int_14 %19 %int_15 %20
|
|
|
|
; The switch will always jump to label %20, which carries the value %int_15.
|
|
; CHECK: OpIAdd %int %int_15 %int_4
|
|
%22 = OpIAdd %int %24 %int_4
|
|
|
|
; Consequently, the return value will always be %int_19.
|
|
; CHECK: OpStore %outparm %int_19
|
|
OpStore %outparm %22
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, SimplifySwitchesDefaultBranch) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_42 = OpConstant %int 42
|
|
%int_4 = OpConstant %int 4
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%main = OpFunction %void None %6
|
|
%13 = OpLabel
|
|
%15 = OpIAdd %int %int_42 %int_4
|
|
OpSelectionMerge %16 None
|
|
|
|
; CHECK: OpSwitch %int_46 {{%\d+}} 10 {{%\d+}}
|
|
OpSwitch %15 %17 10 %18
|
|
%18 = OpLabel
|
|
OpBranch %16
|
|
%17 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%22 = OpPhi %int %int_42 %18 %int_1 %17
|
|
|
|
; The switch will always jump to the default label %17. This carries the value
|
|
; %int_1.
|
|
; CHECK: OpIAdd %int %int_1 %int_4
|
|
%20 = OpIAdd %int %22 %int_4
|
|
|
|
; Resulting in a return value of %int_5.
|
|
; CHECK: OpStore %outparm %int_5
|
|
OpStore %outparm %20
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, SimplifyIntVector) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %OutColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %OutColor "OutColor"
|
|
OpDecorate %OutColor Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%v4int = OpTypeVector %int 4
|
|
%_ptr_Function_v4int = OpTypePointer Function %v4int
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%int_3 = OpConstant %int 3
|
|
%int_4 = OpConstant %int 4
|
|
%14 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%_ptr_Output_v4int = OpTypePointer Output %v4int
|
|
%OutColor = OpVariable %_ptr_Output_v4int Output
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4int Function
|
|
OpStore %v %14
|
|
%18 = OpAccessChain %_ptr_Function_int %v %uint_0
|
|
%19 = OpLoad %int %18
|
|
|
|
; The constant folder does not see through access chains. To get this, the
|
|
; vector would have to be scalarized.
|
|
; CHECK: [[result_id:%\d+]] = OpIAdd %int {{%\d+}} %int_1
|
|
%20 = OpIAdd %int %19 %int_1
|
|
%21 = OpAccessChain %_ptr_Function_int %v %uint_0
|
|
|
|
; CHECK: OpStore {{%\d+}} [[result_id]]
|
|
OpStore %21 %20
|
|
%24 = OpLoad %v4int %v
|
|
OpStore %OutColor %24
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, BadSimplifyFloatVector) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %OutColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %OutColor "OutColor"
|
|
OpDecorate %OutColor Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%float_3 = OpConstant %float 3
|
|
%float_4 = OpConstant %float 4
|
|
%14 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%_ptr_Function_float = OpTypePointer Function %float
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%OutColor = OpVariable %_ptr_Output_v4float Output
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpStore %v %14
|
|
%18 = OpAccessChain %_ptr_Function_float %v %uint_0
|
|
%19 = OpLoad %float %18
|
|
|
|
; NOTE: This test should start failing once floating point folding is
|
|
; implemented (https://github.com/KhronosGroup/SPIRV-Tools/issues/943).
|
|
; This should be checking that we are adding %float_1 + %float_1.
|
|
; CHECK: [[result_id:%\d+]] = OpFAdd %float {{%\d+}} %float_1
|
|
%20 = OpFAdd %float %19 %float_1
|
|
%21 = OpAccessChain %_ptr_Function_float %v %uint_0
|
|
|
|
; This should be checkint that we are storing %float_2 instead of result_it.
|
|
; CHECK: OpStore {{%\d+}} [[result_id]]
|
|
OpStore %21 %20
|
|
%24 = OpLoad %v4float %v
|
|
OpStore %OutColor %24
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, NoLoadStorePropagation) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %x "x"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_23 = OpConstant %int 23
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%x = OpVariable %_ptr_Function_int Function
|
|
OpStore %x %int_23
|
|
|
|
; int_23 should not propagate into this load.
|
|
; CHECK: [[load_id:%\d+]] = OpLoad %int %x
|
|
%12 = OpLoad %int %x
|
|
|
|
; Nor into this copy operation.
|
|
; CHECK: [[copy_id:%\d+]] = OpCopyObject %int [[load_id]]
|
|
%13 = OpCopyObject %int %12
|
|
|
|
; Likewise here.
|
|
; CHECK: OpStore %outparm [[copy_id]]
|
|
OpStore %outparm %13
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, HandleAbortInstructions) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 500
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
; CHECK: %true = OpConstantTrue %bool
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
%9 = OpIAdd %int %int_3 %int_1
|
|
%6 = OpSGreaterThan %bool %9 %int_3
|
|
OpSelectionMerge %23 None
|
|
; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
|
|
OpBranchConditional %6 %22 %23
|
|
%22 = OpLabel
|
|
OpKill
|
|
%23 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, SSAWebCycles) {
|
|
// Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159
|
|
// When there is a cycle in the SSA def-use web, the propagator was getting
|
|
// into an infinite loop. SSA edges for Phi instructions should not be
|
|
// added to the edges to simulate.
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_4 = OpConstant %int 4
|
|
%bool = OpTypeBool
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%29 = OpPhi %int %int_0 %5 %22 %14
|
|
%30 = OpPhi %int %int_0 %5 %25 %14
|
|
OpLoopMerge %13 %14 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%19 = OpSLessThan %bool %30 %int_4
|
|
; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
|
|
OpBranchConditional %19 %12 %13
|
|
%12 = OpLabel
|
|
; CHECK: OpIAdd %int %int_0 %int_0
|
|
%22 = OpIAdd %int %29 %30
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
; CHECK: OpPhi %int %int_0 {{%\d+}}
|
|
%25 = OpPhi %int %30 %12
|
|
OpBranch %11
|
|
%13 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, LoopInductionVariables) {
|
|
// Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1143
|
|
// We are failing to properly consider the induction variable for this loop
|
|
// as Varying.
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 430
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_10 = OpConstant %int 10
|
|
%bool = OpTypeBool
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %5
|
|
%12 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
|
|
; This Phi should not have all constant arguments:
|
|
; CHECK: [[phi_id:%\d+]] = OpPhi %int %int_0 {{%\d+}} {{%\d+}} {{%\d+}}
|
|
%22 = OpPhi %int %int_0 %12 %21 %15
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
|
|
; The Phi should never be considered to have the value %int_0.
|
|
; CHECK: [[branch_selector:%\d+]] = OpSLessThan %bool [[phi_id]] %int_10
|
|
%18 = OpSLessThan %bool %22 %int_10
|
|
|
|
; This conditional was wrongly converted into an always-true jump due to the
|
|
; bad meet evaluation of %22.
|
|
; CHECK: OpBranchConditional [[branch_selector]] {{%\d+}} {{%\d+}}
|
|
OpBranchConditional %18 %19 %14
|
|
%19 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
; CHECK: OpIAdd %int [[phi_id]] %int_1
|
|
%21 = OpIAdd %int %22 %int_1
|
|
OpBranch %13
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, HandleCompositeWithUndef) {
|
|
// Check to make sure that CCP does not crash when given a "constant" struct
|
|
// with an undef. If at a later time CCP is enhanced to optimize this case,
|
|
// it is not wrong.
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 500
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
%_struct_7 = OpTypeStruct %int %int
|
|
%int_1 = OpConstant %int 1
|
|
%9 = OpUndef %int
|
|
%10 = OpConstantComposite %_struct_7 %int_1 %9
|
|
%main = OpFunction %void None %4
|
|
%11 = OpLabel
|
|
%12 = OpCompositeExtract %int %10 0
|
|
%13 = OpCopyObject %int %12
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
|
|
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
|
|
}
|
|
|
|
TEST_F(CCPTest, SkipSpecConstantInstrucitons) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 500
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%10 = OpSpecConstantFalse %bool
|
|
%main = OpFunction %void None %4
|
|
%11 = OpLabel
|
|
OpBranchConditional %10 %L1 %L2
|
|
%L1 = OpLabel
|
|
OpReturn
|
|
%L2 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
|
|
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
|
|
}
|
|
|
|
TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) {
|
|
const std::string spv_asm = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %1 "main"
|
|
OpExecutionMode %1 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%v3bool = OpTypeVector %bool 3
|
|
%_struct_8 = OpTypeStruct %v3bool
|
|
%true = OpConstantTrue %bool
|
|
; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool
|
|
%11 = OpSpecConstantComposite %v3bool %true %true %true
|
|
%12 = OpConstantComposite %_struct_8 %11
|
|
; CHECK: OpFunction
|
|
%1 = OpFunction %void None %4
|
|
%29 = OpLabel
|
|
%31 = OpCompositeExtract %v3bool %12 0
|
|
; CHECK: OpCompositeExtract %bool [[spec_const]] 0
|
|
%32 = OpCompositeExtract %bool %31 0
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(spv_asm, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
TEST_F(CCPTest, UpdateSubsequentPhisToVarying) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func" %in
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%int = OpTypeInt 32 1
|
|
%false = OpConstantFalse %bool
|
|
%int0 = OpConstant %int 0
|
|
%int1 = OpConstant %int 1
|
|
%int6 = OpConstant %int 6
|
|
%int_ptr_Input = OpTypePointer Input %int
|
|
%in = OpVariable %int_ptr_Input Input
|
|
%undef = OpUndef %int
|
|
|
|
; Although no constants are propagated in this function, the propagator
|
|
; generates a new %true value while visiting conditional statements.
|
|
; CHECK: %true = OpConstantTrue %bool
|
|
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%outer_phi = OpPhi %int %int0 %1 %outer_add %15
|
|
%cond1 = OpSLessThanEqual %bool %outer_phi %int6
|
|
OpLoopMerge %3 %15 None
|
|
OpBranchConditional %cond1 %4 %3
|
|
%4 = OpLabel
|
|
%ld = OpLoad %int %in
|
|
%cond2 = OpSGreaterThanEqual %bool %int1 %ld
|
|
OpSelectionMerge %10 None
|
|
OpBranchConditional %cond2 %8 %9
|
|
%8 = OpLabel
|
|
OpBranch %10
|
|
%9 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%extra_phi = OpPhi %int %outer_phi %8 %outer_phi %9
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%inner_phi = OpPhi %int %int0 %10 %inner_add %13
|
|
%cond3 = OpSLessThanEqual %bool %inner_phi %int6
|
|
OpLoopMerge %14 %13 None
|
|
OpBranchConditional %cond3 %12 %14
|
|
%12 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%inner_add = OpIAdd %int %inner_phi %int1
|
|
OpBranch %11
|
|
%14 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%outer_add = OpIAdd %int %extra_phi %int1
|
|
OpBranch %2
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
TEST_F(CCPTest, UndefInPhi) {
|
|
const std::string text = R"(
|
|
; CHECK: [[uint1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: [[phi:%\w+]] = OpPhi
|
|
; CHECK: OpIAdd {{%\w+}} [[phi]] [[uint1]]
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpDecorate %1 LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%uint_1 = OpConstant %uint 1
|
|
%7 = OpUndef %uint
|
|
%8 = OpTypeFunction %void %bool
|
|
%1 = OpFunction %void None %8
|
|
%9 = OpFunctionParameter %bool
|
|
%10 = OpLabel
|
|
OpBranchConditional %9 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%12 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranchConditional %9 %13 %15
|
|
%15 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%16 = OpPhi %uint %uint_0 %11 %7 %14 %uint_1 %15
|
|
%17 = OpIAdd %uint %16 %uint_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
// Just test to make sure the constant fold rules are being used. Will rely on
|
|
// the folding test for specific testing of specific rules.
|
|
TEST_F(CCPTest, UseConstantFoldingRules) {
|
|
const std::string text = R"(
|
|
; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: OpReturnValue [[float1]]
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical GLSL450
|
|
OpDecorate %1 LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%float = OpTypeFloat 32
|
|
%float_0 = OpConstant %float 0
|
|
%float_1 = OpConstant %float 1
|
|
%8 = OpTypeFunction %float
|
|
%1 = OpFunction %float None %8
|
|
%10 = OpLabel
|
|
%17 = OpFAdd %float %float_0 %float_1
|
|
OpReturnValue %17
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
// Test for #1300. Previously value for %5 would not settle during simulation.
|
|
TEST_F(CCPTest, SettlePhiLatticeValue) {
|
|
const std::string text = R"(
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpBranchConditional %true %2 %3
|
|
%3 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%5 = OpPhi %bool %true %1 %false %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SinglePassRunToBinary<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, NullBranchCondition) {
|
|
const std::string text = R"(
|
|
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
|
|
; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%int = OpTypeInt 32 1
|
|
%null = OpConstantNull %bool
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpSelectionMerge %2 None
|
|
OpBranchConditional %null %2 %3
|
|
%3 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %int %int_1 %1 %int_2 %3
|
|
%add = OpIAdd %int %int_1 %phi
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, UndefBranchCondition) {
|
|
const std::string text = R"(
|
|
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: [[phi:%\w+]] = OpPhi
|
|
; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%int = OpTypeInt 32 1
|
|
%undef = OpUndef %bool
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpSelectionMerge %2 None
|
|
OpBranchConditional %undef %2 %3
|
|
%3 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %int %int_1 %1 %int_2 %3
|
|
%add = OpIAdd %int %int_1 %phi
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, NullSwitchCondition) {
|
|
const std::string text = R"(
|
|
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
|
|
; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%int = OpTypeInt 32 1
|
|
%null = OpConstantNull %int
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpSelectionMerge %2 None
|
|
OpSwitch %null %2 0 %3
|
|
%3 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %int %int_1 %1 %int_2 %3
|
|
%add = OpIAdd %int %int_1 %phi
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, UndefSwitchCondition) {
|
|
const std::string text = R"(
|
|
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
|
|
; CHECK: [[phi:%\w+]] = OpPhi
|
|
; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%int = OpTypeInt 32 1
|
|
%undef = OpUndef %int
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpSelectionMerge %2 None
|
|
OpSwitch %undef %2 0 %3
|
|
%3 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %int %int_1 %1 %int_2 %3
|
|
%add = OpIAdd %int %int_1 %phi
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
// Test for #1361.
|
|
TEST_F(CCPTest, CompositeConstructOfGlobalValue) {
|
|
const std::string text = R"(
|
|
; CHECK: [[phi:%\w+]] = OpPhi
|
|
; CHECK-NEXT: OpCompositeExtract {{%\w+}} [[phi]] 0
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func" %in
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
%functy = OpTypeFunction %void
|
|
%ptr_int_Input = OpTypePointer Input %int
|
|
%in = OpVariable %ptr_int_Input Input
|
|
%struct = OpTypeStruct %ptr_int_Input %ptr_int_Input
|
|
%struct_null = OpConstantNull %struct
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %struct %struct_null %1 %5 %4
|
|
%extract = OpCompositeExtract %ptr_int_Input %phi 0
|
|
OpLoopMerge %3 %4 None
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
%5 = OpCompositeConstruct %struct %in %in
|
|
OpBranch %2
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, FoldWithDecoration) {
|
|
const std::string text = R"(
|
|
; CHECK: OpCapability
|
|
; CHECK-NOT: OpDecorate
|
|
; CHECK: OpFunctionEnd
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %2 "main"
|
|
OpExecutionMode %2 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
OpDecorate %3 RelaxedPrecision
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%float_0 = OpConstant %float 0
|
|
%v4float = OpTypeVector %float 4
|
|
%10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%2 = OpFunction %void None %5
|
|
%11 = OpLabel
|
|
%3 = OpVectorShuffle %v3float %10 %10 0 1 2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, DebugSimpleFoldConstant) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
%ext = OpExtInstImport "OpenCL.DebugInfo.100"
|
|
OpMemoryModel Logical GLSL450
|
|
%file_name = OpString "test"
|
|
%float_name = OpString "float"
|
|
%main_name = OpString "main"
|
|
%f_name = OpString "f"
|
|
OpDecorate %1 LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%float = OpTypeFloat 32
|
|
%float_0 = OpConstant %float 0
|
|
|
|
; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
|
|
%float_1 = OpConstant %float 1
|
|
%uint = OpTypeInt 32 0
|
|
%uint_32 = OpConstant %uint 32
|
|
%8 = OpTypeFunction %float
|
|
%null_expr = OpExtInst %void %ext DebugExpression
|
|
%src = OpExtInst %void %ext DebugSource %file_name
|
|
%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
|
|
%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
|
|
%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
|
|
%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1
|
|
%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
|
|
%1 = OpFunction %float None %8
|
|
%10 = OpLabel
|
|
|
|
; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
|
|
; CHECK: OpLine [[file:%\w+]] 1 0
|
|
; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1
|
|
%s0 = OpExtInst %void %ext DebugScope %dbg_main
|
|
OpLine %file_name 1 0
|
|
%17 = OpFAdd %float %float_0 %float_1
|
|
%val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr
|
|
|
|
; CHECK: OpLine [[file]] 2 0
|
|
; CHECK: OpReturnValue [[float1]]
|
|
OpLine %file_name 2 0
|
|
OpReturnValue %17
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
%ext = OpExtInstImport "OpenCL.DebugInfo.100"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %outparm
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
%file_name = OpString "test"
|
|
%float_name = OpString "float"
|
|
%main_name = OpString "main"
|
|
%f_name = OpString "f"
|
|
OpName %main "main"
|
|
OpName %outparm "outparm"
|
|
OpDecorate %outparm Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%bool = OpTypeBool
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_4 = OpConstant %int 4
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%uint = OpTypeInt 32 0
|
|
%uint_32 = OpConstant %uint 32
|
|
%_ptr_Output_int = OpTypePointer Output %int
|
|
%outparm = OpVariable %_ptr_Output_int Output
|
|
%null_expr = OpExtInst %void %ext DebugExpression
|
|
%src = OpExtInst %void %ext DebugSource %file_name
|
|
%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
|
|
%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
|
|
%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
|
|
%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
|
|
%bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
|
|
%bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
|
|
%bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
|
|
%bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
|
|
%dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
|
|
%dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal
|
|
%dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal
|
|
%main = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
|
|
; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
|
|
; CHECK: OpLine [[file:%\w+]] 1 0
|
|
; CHECK: OpIAdd %int %int_4 %int_3
|
|
; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7
|
|
%s0 = OpExtInst %void %ext DebugScope %bb0
|
|
OpLine %file_name 1 0
|
|
%9 = OpIAdd %int %int_4 %int_3
|
|
%val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr
|
|
|
|
; CHECK: OpLine [[file]] 2 0
|
|
; CHECK: OpSGreaterThan %bool %int_7 %int_3
|
|
; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true
|
|
OpLine %file_name 2 0
|
|
%6 = OpSGreaterThan %bool %9 %int_3
|
|
%val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr
|
|
|
|
OpSelectionMerge %25 None
|
|
OpBranchConditional %6 %22 %23
|
|
%22 = OpLabel
|
|
%s1 = OpExtInst %void %ext DebugScope %bb1
|
|
%7 = OpCopyObject %int %9
|
|
%val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr
|
|
OpBranch %25
|
|
%23 = OpLabel
|
|
%s2 = OpExtInst %void %ext DebugScope %bb2
|
|
%8 = OpCopyObject %int %int_4
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
%s3 = OpExtInst %void %ext DebugScope %bb3
|
|
%35 = OpPhi %int %7 %22 %8 %23
|
|
OpStore %outparm %35
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<CCPPass>(text, true);
|
|
}
|
|
|
|
// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3636
|
|
TEST_F(CCPTest, CCPNoChangeFailure) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 320
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpConstant %6 2
|
|
%13 = OpConstant %6 4
|
|
%21 = OpConstant %6 1
|
|
%10 = OpTypeBool
|
|
%17 = OpTypePointer Function %6
|
|
|
|
; CCP is generating two new constants during propagation that end up being
|
|
; dead because they cannot be replaced anywhere in the IR. CCP was wrongly
|
|
; considering the IR to be unmodified because of this.
|
|
; CHECK: %true = OpConstantTrue %bool
|
|
; CHECK: %int_3 = OpConstant %int 3
|
|
|
|
%4 = OpFunction %2 None %3
|
|
%11 = OpLabel
|
|
OpBranch %5
|
|
%5 = OpLabel
|
|
%23 = OpPhi %6 %7 %11 %20 %15
|
|
%9 = OpSLessThan %10 %23 %13
|
|
OpLoopMerge %8 %15 None
|
|
OpBranchConditional %9 %15 %8
|
|
%15 = OpLabel
|
|
%20 = OpIAdd %6 %23 %21
|
|
OpBranch %5
|
|
%8 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3738
|
|
// Similar to the previous one but more than one constant is generated in a
|
|
// single call to the instruction folder.
|
|
TEST_F(CCPTest, CCPNoChangeFailureSeveralConstantsDuringFolding) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %2 "main"
|
|
OpExecutionMode %2 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%bool = OpTypeBool
|
|
%v3bool = OpTypeVector %bool 3
|
|
%float_0 = OpConstant %float 0
|
|
%12 = OpConstantComposite %v3float %float_0 %float_0 %float_0
|
|
%float_0_300000012 = OpConstant %float 0.300000012
|
|
%14 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012
|
|
|
|
; CCP is generating several constants during a single instruction evaluation.
|
|
; When folding %19, it generates the constants %true and %24. They are dead
|
|
; because they cannot be replaced anywhere in the IR. CCP was wrongly
|
|
; considering the IR to be unmodified because of this.
|
|
;
|
|
; CHECK: %true = OpConstantTrue %bool
|
|
; CHECK: %24 = OpConstantComposite %v3bool %true %true %true
|
|
; CHECK: %float_1 = OpConstant %float 1
|
|
; CHECK: %float_0_699999988 = OpConstant %float 0.699999988
|
|
|
|
%2 = OpFunction %void None %4
|
|
%15 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%17 = OpPhi %v3float %12 %15 %14 %18
|
|
%19 = OpFOrdLessThan %v3bool %17 %14
|
|
%20 = OpAll %bool %19
|
|
OpLoopMerge %21 %18 None
|
|
OpBranchConditional %20 %18 %21
|
|
%18 = OpLabel
|
|
OpBranch %16
|
|
%21 = OpLabel
|
|
%22 = OpExtInst %v3float %1 FMix %12 %17 %14
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3991
|
|
// Similar to the previous one but constants are created even when no
|
|
// instruction are ever folded during propagation.
|
|
TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %2 "main"
|
|
OpExecutionMode %2 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%bool = OpTypeBool
|
|
%float_0 = OpConstant %float 0
|
|
%11 = OpConstantComposite %v3float %float_0 %float_0 %float_0
|
|
%float_0_300000012 = OpConstant %float 0.300000012
|
|
%13 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012
|
|
|
|
; CCP generates two constants when trying to fold an instruction, which it
|
|
; ultimately fails to fold. The instruction folder in CCP was only
|
|
; checking for newly added constants if the instruction folds successfully.
|
|
;
|
|
; CHECK: %float_1 = OpConstant %float 1
|
|
; CHECK: %float_0_699999988 = OpConstant %float 0.69999998
|
|
|
|
%2 = OpFunction %void None %4
|
|
%14 = OpLabel
|
|
%15 = OpBitcast %uint %float_0_300000012
|
|
%16 = OpUGreaterThan %bool %15 %uint_0
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%18 = OpPhi %v3float %11 %14 %13 %19
|
|
OpLoopMerge %20 %19 None
|
|
OpBranchConditional %16 %19 %20
|
|
%19 = OpLabel
|
|
OpBranch %17
|
|
%20 = OpLabel
|
|
%21 = OpExtInst %v3float %1 FMix %11 %18 %13
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
TEST_F(CCPTest, FunctionDeclaration) {
|
|
// Make sure the pass works with a function declaration that is called.
|
|
const std::string text = R"(OpCapability Addresses
|
|
OpCapability Linkage
|
|
OpCapability Kernel
|
|
OpCapability Int8
|
|
%1 = OpExtInstImport "OpenCL.std"
|
|
OpMemoryModel Physical64 OpenCL
|
|
OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
|
|
OpExecutionMode %2 ContractionOff
|
|
OpSource Unknown 0
|
|
OpDecorate %3 LinkageAttributes "julia_error_7712" Import
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%3 = OpFunction %void None %5
|
|
OpFunctionEnd
|
|
%2 = OpFunction %void None %5
|
|
%6 = OpLabel
|
|
%7 = OpFunctionCall %void %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<CCPPass>(text, text, false);
|
|
}
|
|
|
|
// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462.
|
|
// The test was causing a lateral movement in the constant lattice, which was
|
|
// not being detected as varying by CCP. In this test, FClamp is evaluated
|
|
// twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which
|
|
// returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING)
|
|
// which returns 0.5.
|
|
//
|
|
// Both fold() computations are correct given the semantics of FClamp() but
|
|
// this causes a lateral transition in the constant lattice which was not being
|
|
// considered VARYING by CCP.
|
|
TEST_F(CCPTest, LateralLatticeTransition) {
|
|
const std::string text = R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource ESSL 310
|
|
OpName %main "main"
|
|
OpName %gl_FragCoord "gl_FragCoord"
|
|
OpName %outColor "outColor"
|
|
OpDecorate %gl_FragCoord BuiltIn FragCoord
|
|
OpDecorate %outColor Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
|
|
%uint = OpTypeInt 32 0
|
|
%uint_0 = OpConstant %uint 0
|
|
%_ptr_Input_float = OpTypePointer Input %float
|
|
%float_0 = OpConstant %float 0
|
|
%bool = OpTypeBool
|
|
%float_n1 = OpConstant %float -1
|
|
%float_1 = OpConstant %float 1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%outColor = OpVariable %_ptr_Output_v4float Output
|
|
|
|
; This constant is created during the first evaluation of the CompositeConstruct
|
|
; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1
|
|
|
|
%main = OpFunction %void None %6
|
|
%19 = OpLabel
|
|
%20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0
|
|
%21 = OpLoad %float %20
|
|
%22 = OpFOrdLessThan %bool %21 %float_0
|
|
OpSelectionMerge %23 None
|
|
OpBranchConditional %22 %24 %25
|
|
%24 = OpLabel
|
|
OpBranch %23
|
|
%25 = OpLabel
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
OpBranch %23
|
|
%23 = OpLabel
|
|
%27 = OpPhi %float %float_n1 %24 %float_0_5 %26
|
|
%28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27
|
|
|
|
; On first evaluation, the result from FClamp will return 0.5.
|
|
; But on second evaluation, FClamp should return VARYING. Check
|
|
; that CCP is not keeping the first result.
|
|
; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1
|
|
%29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1
|
|
|
|
; CHECK-NOT: OpCopyObject %v4float [[new_constant]]
|
|
%42 = OpCopyObject %v4float %29
|
|
|
|
; CHECK-NOT: OpStore %outColor [[new_constant]]
|
|
OpStore %outColor %42
|
|
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|