mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-19 03:20:14 +00:00
bdf421cf40
The loop peeler util takes a loop as input and create a new one before. The iterator of the duplicated loop then set to accommodate the number of iteration required for the peeling. The loop peeling pass that decided to do the peeling and profitability analysis is left for a follow-up PR.
1076 lines
36 KiB
C++
1076 lines
36 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 <gmock/gmock.h>
|
|
|
|
#ifdef SPIRV_EFFCEE
|
|
#include "effcee/effcee.h"
|
|
#endif
|
|
|
|
#include "../pass_fixture.h"
|
|
#include "opt/ir_builder.h"
|
|
#include "opt/loop_descriptor.h"
|
|
#include "opt/loop_peeling.h"
|
|
|
|
namespace {
|
|
|
|
using namespace spvtools;
|
|
|
|
using PeelingTest = PassTest<::testing::Test>;
|
|
|
|
bool Validate(const std::vector<uint32_t>& bin) {
|
|
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
|
|
spv_context spvContext = spvContextCreate(target_env);
|
|
spv_diagnostic diagnostic = nullptr;
|
|
spv_const_binary_t binary = {bin.data(), bin.size()};
|
|
spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
|
|
if (error != 0) spvDiagnosticPrint(diagnostic);
|
|
spvDiagnosticDestroy(diagnostic);
|
|
spvContextDestroy(spvContext);
|
|
return error == 0;
|
|
}
|
|
|
|
void Match(const std::string& checks, ir::IRContext* context) {
|
|
std::vector<uint32_t> bin;
|
|
context->module()->ToBinary(&bin, true);
|
|
EXPECT_TRUE(Validate(bin));
|
|
#ifdef SPIRV_EFFCEE
|
|
std::string assembly;
|
|
SpirvTools tools(SPV_ENV_UNIVERSAL_1_2);
|
|
EXPECT_TRUE(
|
|
tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER))
|
|
<< "Disassembling failed for shader:\n"
|
|
<< assembly << std::endl;
|
|
auto match_result = effcee::Match(assembly, checks);
|
|
EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
|
|
<< match_result.message() << "\nChecking result:\n"
|
|
<< assembly;
|
|
#endif // ! SPIRV_EFFCEE
|
|
}
|
|
|
|
/*
|
|
Generated from the following GLSL + --eliminate-local-multi-store
|
|
|
|
First test:
|
|
#version 330 core
|
|
void main() {
|
|
for(int i = 0; i < 10; ++i) {
|
|
if (i < 4)
|
|
break;
|
|
}
|
|
}
|
|
|
|
Second test (with a common sub-expression elimination):
|
|
#version 330 core
|
|
void main() {
|
|
for(int i = 0; i + 1 < 10; ++i) {
|
|
}
|
|
}
|
|
|
|
Third test:
|
|
#version 330 core
|
|
void main() {
|
|
int a[10];
|
|
for (int i = 0; a[i] != 0; i++) {}
|
|
}
|
|
|
|
Forth test:
|
|
#version 330 core
|
|
void main() {
|
|
for (long i = 0; i < 10; i++) {}
|
|
}
|
|
|
|
Fifth test:
|
|
#version 330 core
|
|
void main() {
|
|
for (float i = 0; i < 10; i++) {}
|
|
}
|
|
|
|
Sixth test:
|
|
#version 450
|
|
layout(location = 0)out float o;
|
|
void main() {
|
|
o = 0.0;
|
|
for( int i = 0; true; i++ ) {
|
|
o += 1.0;
|
|
if (i > 10) break;
|
|
}
|
|
}
|
|
*/
|
|
TEST_F(PeelingTest, CannotPeel) {
|
|
// Build the given SPIR-V program in |text|, take the first loop in the first
|
|
// function and test that it is not peelable. |loop_count_id| is the id
|
|
// representing the loop count, if equals to 0, then the function build a 10
|
|
// constant as loop count.
|
|
auto test_cannot_peel = [](const std::string& text, uint32_t loop_count_id) {
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
ir::Instruction* loop_count = nullptr;
|
|
if (loop_count_id) {
|
|
loop_count = context->get_def_use_mgr()->GetDef(loop_count_id);
|
|
} else {
|
|
opt::InstructionBuilder builder(context.get(), &*f.begin());
|
|
// Exit condition.
|
|
loop_count = builder.Add32BitSignedIntegerConstant(10);
|
|
}
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), loop_count);
|
|
EXPECT_FALSE(peel.CanPeelLoop());
|
|
};
|
|
{
|
|
SCOPED_TRACE("loop with break");
|
|
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = 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_4 = OpConstant %int 4
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%28 = OpPhi %int %int_0 %5 %27 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpSLessThan %bool %28 %int_10
|
|
OpBranchConditional %18 %11 %12
|
|
%11 = OpLabel
|
|
%21 = OpSLessThan %bool %28 %int_4
|
|
OpSelectionMerge %23 None
|
|
OpBranchConditional %21 %22 %23
|
|
%22 = OpLabel
|
|
OpBranch %12
|
|
%23 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%27 = OpIAdd %int %28 %int_1
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
test_cannot_peel(text, 0);
|
|
}
|
|
|
|
{
|
|
SCOPED_TRACE("Ambiguous iterator update");
|
|
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_1 = OpConstant %int 1
|
|
%int_10 = OpConstant %int 10
|
|
%bool = OpTypeBool
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%23 = OpPhi %int %int_0 %5 %17 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%17 = OpIAdd %int %23 %int_1
|
|
%20 = OpSLessThan %bool %17 %int_10
|
|
OpBranchConditional %20 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
test_cannot_peel(text, 0);
|
|
}
|
|
|
|
{
|
|
SCOPED_TRACE("No loop static bounds");
|
|
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
OpName %i "i"
|
|
OpName %a "a"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%uint = OpTypeInt 32 0
|
|
%uint_10 = OpConstant %uint 10
|
|
%_arr_int_uint_10 = OpTypeArray %int %uint_10
|
|
%_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
|
|
%bool = OpTypeBool
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
%a = OpVariable %_ptr_Function__arr_int_uint_10 Function
|
|
OpStore %i %int_0
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%28 = OpPhi %int %int_0 %5 %27 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%21 = OpAccessChain %_ptr_Function_int %a %28
|
|
%22 = OpLoad %int %21
|
|
%24 = OpINotEqual %bool %22 %int_0
|
|
OpBranchConditional %24 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%27 = OpIAdd %int %28 %int_1
|
|
OpStore %i %27
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
test_cannot_peel(text, 22);
|
|
}
|
|
{
|
|
SCOPED_TRACE("Int 64 type for conditions");
|
|
|
|
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
|
|
OpName %2 "main"
|
|
OpName %4 "i"
|
|
%6 = OpTypeVoid
|
|
%3 = OpTypeFunction %6
|
|
%7 = OpTypeInt 64 1
|
|
%8 = OpTypePointer Function %7
|
|
%9 = OpConstant %7 0
|
|
%15 = OpConstant %7 10
|
|
%16 = OpTypeBool
|
|
%17 = OpConstant %7 1
|
|
%2 = OpFunction %6 None %3
|
|
%5 = OpLabel
|
|
%4 = OpVariable %8 Function
|
|
OpStore %4 %9
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%22 = OpPhi %7 %9 %5 %21 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpSLessThan %16 %22 %15
|
|
OpBranchConditional %18 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%21 = OpIAdd %7 %22 %17
|
|
OpStore %4 %21
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
// %15 is a constant for a 64 int. Currently rejected.
|
|
test_cannot_peel(text, 15);
|
|
}
|
|
{
|
|
SCOPED_TRACE("Float type for conditions");
|
|
|
|
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
|
|
OpName %2 "main"
|
|
OpName %4 "i"
|
|
%6 = OpTypeVoid
|
|
%3 = OpTypeFunction %6
|
|
%7 = OpTypeFloat 32
|
|
%8 = OpTypePointer Function %7
|
|
%9 = OpConstant %7 0
|
|
%15 = OpConstant %7 10
|
|
%16 = OpTypeBool
|
|
%17 = OpConstant %7 1
|
|
%2 = OpFunction %6 None %3
|
|
%5 = OpLabel
|
|
%4 = OpVariable %8 Function
|
|
OpStore %4 %9
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%22 = OpPhi %7 %9 %5 %21 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpFOrdLessThan %16 %22 %15
|
|
OpBranchConditional %18 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%21 = OpFAdd %7 %22 %17
|
|
OpStore %4 %21
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
// %15 is a constant for a float. Currently rejected.
|
|
test_cannot_peel(text, 15);
|
|
}
|
|
{
|
|
SCOPED_TRACE("Side effect before exit");
|
|
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %o
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %o "o"
|
|
OpName %i "i"
|
|
OpDecorate %o Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%_ptr_Output_float = OpTypePointer Output %float
|
|
%o = OpVariable %_ptr_Output_float Output
|
|
%float_0 = OpConstant %float 0
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float_1 = OpConstant %float 1
|
|
%int_10 = OpConstant %int 10
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
OpStore %o %float_0
|
|
OpStore %i %int_0
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%33 = OpPhi %int %int_0 %5 %32 %17
|
|
OpLoopMerge %16 %17 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%22 = OpLoad %float %o
|
|
%23 = OpFAdd %float %22 %float_1
|
|
OpStore %o %23
|
|
%26 = OpSGreaterThan %bool %33 %int_10
|
|
OpSelectionMerge %28 None
|
|
OpBranchConditional %26 %27 %28
|
|
%27 = OpLabel
|
|
OpBranch %16
|
|
%28 = OpLabel
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%32 = OpIAdd %int %33 %int_1
|
|
OpStore %i %32
|
|
OpBranch %14
|
|
%16 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
test_cannot_peel(text, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Generated from the following GLSL + --eliminate-local-multi-store
|
|
|
|
#version 330 core
|
|
void main() {
|
|
int i = 0;
|
|
for (; i < 10; i++) {}
|
|
}
|
|
*/
|
|
TEST_F(PeelingTest, SimplePeeling) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = 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 %3
|
|
%5 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%22 = OpPhi %int %int_0 %5 %21 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%18 = OpSLessThan %bool %22 %int_10
|
|
OpBranchConditional %18 %11 %12
|
|
%11 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%21 = OpIAdd %int %22 %int_1
|
|
OpBranch %10
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// Peel before.
|
|
{
|
|
SCOPED_TRACE("Peel before");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
opt::InstructionBuilder builder(context.get(), &*f.begin());
|
|
// Exit condition.
|
|
ir::Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), ten_cst);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelBefore(2);
|
|
|
|
const std::string check = R"(
|
|
CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10
|
|
CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]]
|
|
CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]]
|
|
CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
|
|
CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
|
|
// Peel after.
|
|
{
|
|
SCOPED_TRACE("Peel after");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
opt::InstructionBuilder builder(context.get(), &*f.begin());
|
|
// Exit condition.
|
|
ir::Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), ten_cst);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelAfter(2);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
|
|
CHECK: [[BEFORE_LOOP]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
|
|
CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[IF_MERGE]] = OpLabel
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
|
|
CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Generated from the following GLSL + --eliminate-local-multi-store
|
|
|
|
#version 330 core
|
|
void main() {
|
|
int a[10];
|
|
int n = a[0];
|
|
for(int i = 0; i < n; ++i) {}
|
|
}
|
|
*/
|
|
TEST_F(PeelingTest, PeelingUncountable) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
OpName %a "a"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%uint = OpTypeInt 32 0
|
|
%uint_10 = OpConstant %uint 10
|
|
%_arr_int_uint_10 = OpTypeArray %int %uint_10
|
|
%_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
|
|
%int_0 = OpConstant %int 0
|
|
%bool = OpTypeBool
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%a = OpVariable %_ptr_Function__arr_int_uint_10 Function
|
|
%15 = OpAccessChain %_ptr_Function_int %a %int_0
|
|
%16 = OpLoad %int %15
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
%30 = OpPhi %int %int_0 %5 %29 %21
|
|
OpLoopMerge %20 %21 None
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
%26 = OpSLessThan %bool %30 %16
|
|
OpBranchConditional %26 %19 %20
|
|
%19 = OpLabel
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
%29 = OpIAdd %int %30 %int_1
|
|
OpBranch %18
|
|
%20 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// Peel before.
|
|
{
|
|
SCOPED_TRACE("Peel before");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
ir::Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
|
|
EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), loop_count);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelBefore(1);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
|
|
CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
|
|
CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
|
|
CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
|
|
// Peel after.
|
|
{
|
|
SCOPED_TRACE("Peel after");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
ir::Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
|
|
EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), loop_count);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelAfter(1);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
|
|
CHECK: [[BEFORE_LOOP]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
|
|
CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[IF_MERGE]] = OpLabel
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
|
|
CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Generated from the following GLSL + --eliminate-local-multi-store
|
|
|
|
#version 330 core
|
|
void main() {
|
|
int i = 0;
|
|
do {
|
|
i++;
|
|
} while (i < 10);
|
|
}
|
|
*/
|
|
TEST_F(PeelingTest, DoWhilePeeling) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 330
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_1 = OpConstant %int 1
|
|
%int_10 = OpConstant %int 10
|
|
%bool = OpTypeBool
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%21 = OpPhi %int %int_0 %5 %16 %13
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%16 = OpIAdd %int %21 %int_1
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%20 = OpSLessThan %bool %16 %int_10
|
|
OpBranchConditional %20 %10 %12
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// Peel before.
|
|
{
|
|
SCOPED_TRACE("Peel before");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
opt::InstructionBuilder builder(context.get(), &*f.begin());
|
|
// Exit condition.
|
|
ir::Instruction* ten_cst = builder.Add32BitUnsignedIntegerConstant(10);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), ten_cst);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelBefore(2);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
|
|
CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]]
|
|
CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
|
|
CHECK: [[BE]] = OpLabel
|
|
CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[DUMMY_IT_1]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[AFTER_LOOP_PREHEADER]]
|
|
|
|
CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[I_1]] [[AFTER_LOOP_PREHEADER]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
|
|
// Peel after.
|
|
{
|
|
SCOPED_TRACE("Peel after");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
opt::InstructionBuilder builder(context.get(), &*f.begin());
|
|
// Exit condition.
|
|
ir::Instruction* ten_cst = builder.Add32BitUnsignedIntegerConstant(10);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), ten_cst);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelAfter(2);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
|
|
CHECK: [[BEFORE_LOOP]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
|
|
CHECK: [[BE]] = OpLabel
|
|
CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: [[EXIT_VAL:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT_1]]
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[EXIT_VAL]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[BEFORE_LOOP_MERGE]]
|
|
|
|
CHECK: [[IF_MERGE]] = OpLabel
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I_1]] [[BEFORE_LOOP_MERGE]]
|
|
CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Generated from the following GLSL + --eliminate-local-multi-store
|
|
|
|
#version 330 core
|
|
void main() {
|
|
int a[10];
|
|
int n = a[0];
|
|
for(int i = 0; i < n; ++i) {}
|
|
}
|
|
*/
|
|
TEST_F(PeelingTest, PeelingLoopWithStore) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %o %n
|
|
OpExecutionMode %main OriginLowerLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %o "o"
|
|
OpName %end "end"
|
|
OpName %n "n"
|
|
OpName %i "i"
|
|
OpDecorate %o Location 0
|
|
OpDecorate %n Flat
|
|
OpDecorate %n Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%_ptr_Output_float = OpTypePointer Output %float
|
|
%o = OpVariable %_ptr_Output_float Output
|
|
%float_0 = OpConstant %float 0
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%_ptr_Input_int = OpTypePointer Input %int
|
|
%n = OpVariable %_ptr_Input_int Input
|
|
%int_0 = OpConstant %int 0
|
|
%bool = OpTypeBool
|
|
%float_1 = OpConstant %float 1
|
|
%int_1 = OpConstant %int 1
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%end = OpVariable %_ptr_Function_int Function
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
OpStore %o %float_0
|
|
%15 = OpLoad %int %n
|
|
OpStore %end %15
|
|
OpStore %i %int_0
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
%33 = OpPhi %int %int_0 %5 %32 %21
|
|
OpLoopMerge %20 %21 None
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
%26 = OpSLessThan %bool %33 %15
|
|
OpBranchConditional %26 %19 %20
|
|
%19 = OpLabel
|
|
%28 = OpLoad %float %o
|
|
%29 = OpFAdd %float %28 %float_1
|
|
OpStore %o %29
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
%32 = OpIAdd %int %33 %int_1
|
|
OpStore %i %32
|
|
OpBranch %18
|
|
%20 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// Peel before.
|
|
{
|
|
SCOPED_TRACE("Peel before");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
ir::Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
|
|
EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), loop_count);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelBefore(1);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
|
|
CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
|
|
CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
|
|
CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
|
|
// Peel after.
|
|
{
|
|
SCOPED_TRACE("Peel after");
|
|
|
|
std::unique_ptr<ir::IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ir::Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
ir::Function& f = *module->begin();
|
|
ir::LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
|
|
|
|
EXPECT_EQ(ld.NumLoops(), 1u);
|
|
|
|
ir::Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
|
|
EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
|
|
|
|
opt::LoopPeeling peel(context.get(), &*ld.begin(), loop_count);
|
|
EXPECT_TRUE(peel.CanPeelLoop());
|
|
peel.PeelAfter(1);
|
|
|
|
const std::string check = R"(
|
|
CHECK: OpFunction
|
|
CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
|
|
CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
|
|
CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
|
|
CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
|
|
CHECK: [[BEFORE_LOOP]] = OpLabel
|
|
CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
|
|
CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
|
|
CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
|
|
CHECK: [[COND_BLOCK:%\w+]] = OpLabel
|
|
CHECK-NEXT: OpSLessThan
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
|
|
CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
|
|
CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
|
|
CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
|
|
CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
|
|
CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
|
|
|
|
CHECK: [[IF_MERGE]] = OpLabel
|
|
CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
|
|
CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
|
|
|
|
CHECK: [[AFTER_LOOP]] = OpLabel
|
|
CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
|
|
CHECK-NEXT: OpLoopMerge
|
|
|
|
)";
|
|
|
|
Match(check, context.get());
|
|
}
|
|
}
|
|
|
|
} // namespace
|