SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
Stephen McGroarty 1c2cbaf569 Add GetContinueBlock to loop class.
Previously, the loop class used the terms latch and continue block
interchangeably. This patch splits the two and corrects and tests some
uses of the old uses of GetLatchBlock.
2018-05-03 14:30:41 -04:00

2999 lines
75 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 <string>
#include <vector>
#include <gmock/gmock.h>
#include "../assembly_builder.h"
#include "../function_utils.h"
#include "../pass_fixture.h"
#include "../pass_utils.h"
#include "opt/loop_unroller.h"
#include "opt/loop_utils.h"
#include "opt/pass.h"
namespace {
using namespace spvtools;
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
float x[4];
for (int i = 0; i < 4; ++i) {
x[i] = 1.0f;
}
}
*/
TEST_F(PassClassTest, SimpleFullyUnrollTest) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %5 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 0
%11 = OpConstant %8 4
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 4
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpConstant %13 1
%19 = OpTypePointer Function %13
%20 = OpConstant %8 1
%21 = OpTypeVector %13 4
%22 = OpTypePointer Output %21
%3 = OpVariable %22 Output
%2 = OpFunction %6 None %7
%23 = OpLabel
%5 = OpVariable %17 Function
OpBranch %24
%24 = OpLabel
%35 = OpPhi %8 %10 %23 %34 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %12 %35 %11
OpBranchConditional %29 %30 %25
%30 = OpLabel
%32 = OpAccessChain %19 %5 %35
OpStore %32 %18
OpBranch %26
%26 = OpLabel
%34 = OpIAdd %8 %35 %20
OpBranch %24
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %4 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpConstant %7 4
%11 = OpTypeBool
%12 = OpTypeFloat 32
%13 = OpTypeInt 32 0
%14 = OpConstant %13 4
%15 = OpTypeArray %12 %14
%16 = OpTypePointer Function %15
%17 = OpConstant %12 1
%18 = OpTypePointer Function %12
%19 = OpConstant %7 1
%20 = OpTypeVector %12 4
%21 = OpTypePointer Output %20
%3 = OpVariable %21 Output
%2 = OpFunction %5 None %6
%22 = OpLabel
%4 = OpVariable %16 Function
OpBranch %23
%23 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpSLessThan %11 %9 %10
OpBranch %30
%30 = OpLabel
%31 = OpAccessChain %18 %4 %9
OpStore %31 %17
OpBranch %26
%26 = OpLabel
%25 = OpIAdd %7 %9 %19
OpBranch %32
%32 = OpLabel
OpBranch %34
%34 = OpLabel
%35 = OpSLessThan %11 %25 %10
OpBranch %36
%36 = OpLabel
%37 = OpAccessChain %18 %4 %25
OpStore %37 %17
OpBranch %38
%38 = OpLabel
%39 = OpIAdd %7 %25 %19
OpBranch %40
%40 = OpLabel
OpBranch %42
%42 = OpLabel
%43 = OpSLessThan %11 %39 %10
OpBranch %44
%44 = OpLabel
%45 = OpAccessChain %18 %4 %39
OpStore %45 %17
OpBranch %46
%46 = OpLabel
%47 = OpIAdd %7 %39 %19
OpBranch %48
%48 = OpLabel
OpBranch %50
%50 = OpLabel
%51 = OpSLessThan %11 %47 %10
OpBranch %52
%52 = OpLabel
%53 = OpAccessChain %18 %4 %47
OpStore %53 %17
OpBranch %54
%54 = OpLabel
%55 = OpIAdd %7 %47 %19
OpBranch %27
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
template <int factor>
class PartialUnrollerTestPass : public opt::Pass {
public:
PartialUnrollerTestPass() : Pass() {}
const char* name() const override { return "Loop unroller"; }
Status Process(ir::IRContext* context) override {
for (ir::Function& f : *context->module()) {
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(&f);
for (auto& loop : loop_descriptor) {
opt::LoopUtils loop_utils{context, &loop};
loop_utils.PartiallyUnroll(factor);
}
}
return Pass::Status::SuccessWithChange;
}
};
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
float x[10];
for (int i = 0; i < 10; ++i) {
x[i] = 1.0f;
}
}
*/
TEST_F(PassClassTest, SimplePartialUnroll) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %5 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 0
%11 = OpConstant %8 10
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 10
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpConstant %13 1
%19 = OpTypePointer Function %13
%20 = OpConstant %8 1
%21 = OpTypeVector %13 4
%22 = OpTypePointer Output %21
%3 = OpVariable %22 Output
%2 = OpFunction %6 None %7
%23 = OpLabel
%5 = OpVariable %17 Function
OpBranch %24
%24 = OpLabel
%35 = OpPhi %8 %10 %23 %34 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %12 %35 %11
OpBranchConditional %29 %30 %25
%30 = OpLabel
%32 = OpAccessChain %19 %5 %35
OpStore %32 %18
OpBranch %26
%26 = OpLabel
%34 = OpIAdd %8 %35 %20
OpBranch %24
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %4 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpConstant %7 10
%11 = OpTypeBool
%12 = OpTypeFloat 32
%13 = OpTypeInt 32 0
%14 = OpConstant %13 10
%15 = OpTypeArray %12 %14
%16 = OpTypePointer Function %15
%17 = OpConstant %12 1
%18 = OpTypePointer Function %12
%19 = OpConstant %7 1
%20 = OpTypeVector %12 4
%21 = OpTypePointer Output %20
%3 = OpVariable %21 Output
%2 = OpFunction %5 None %6
%22 = OpLabel
%4 = OpVariable %16 Function
OpBranch %23
%23 = OpLabel
%24 = OpPhi %7 %9 %22 %39 %38
OpLoopMerge %27 %38 DontUnroll
OpBranch %28
%28 = OpLabel
%29 = OpSLessThan %11 %24 %10
OpBranchConditional %29 %30 %27
%30 = OpLabel
%31 = OpAccessChain %18 %4 %24
OpStore %31 %17
OpBranch %26
%26 = OpLabel
%25 = OpIAdd %7 %24 %19
OpBranch %32
%32 = OpLabel
OpBranch %34
%34 = OpLabel
%35 = OpSLessThan %11 %25 %10
OpBranch %36
%36 = OpLabel
%37 = OpAccessChain %18 %4 %25
OpStore %37 %17
OpBranch %38
%38 = OpLabel
%39 = OpIAdd %7 %25 %19
OpBranch %23
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, output, false);
}
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
float x[10];
for (int i = 0; i < 10; ++i) {
x[i] = 1.0f;
}
}
*/
TEST_F(PassClassTest, SimpleUnevenPartialUnroll) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %5 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 0
%11 = OpConstant %8 10
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 10
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpConstant %13 1
%19 = OpTypePointer Function %13
%20 = OpConstant %8 1
%21 = OpTypeVector %13 4
%22 = OpTypePointer Output %21
%3 = OpVariable %22 Output
%2 = OpFunction %6 None %7
%23 = OpLabel
%5 = OpVariable %17 Function
OpBranch %24
%24 = OpLabel
%35 = OpPhi %8 %10 %23 %34 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %12 %35 %11
OpBranchConditional %29 %30 %25
%30 = OpLabel
%32 = OpAccessChain %19 %5 %35
OpStore %32 %18
OpBranch %26
%26 = OpLabel
%34 = OpIAdd %8 %35 %20
OpBranch %24
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %4 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpConstant %7 10
%11 = OpTypeBool
%12 = OpTypeFloat 32
%13 = OpTypeInt 32 0
%14 = OpConstant %13 10
%15 = OpTypeArray %12 %14
%16 = OpTypePointer Function %15
%17 = OpConstant %12 1
%18 = OpTypePointer Function %12
%19 = OpConstant %7 1
%20 = OpTypeVector %12 4
%21 = OpTypePointer Output %20
%3 = OpVariable %21 Output
%58 = OpConstant %13 1
%2 = OpFunction %5 None %6
%22 = OpLabel
%4 = OpVariable %16 Function
OpBranch %23
%23 = OpLabel
%24 = OpPhi %7 %9 %22 %25 %26
OpLoopMerge %32 %26 Unroll
OpBranch %28
%28 = OpLabel
%29 = OpSLessThan %11 %24 %58
OpBranchConditional %29 %30 %32
%30 = OpLabel
%31 = OpAccessChain %18 %4 %24
OpStore %31 %17
OpBranch %26
%26 = OpLabel
%25 = OpIAdd %7 %24 %19
OpBranch %23
%32 = OpLabel
OpBranch %33
%33 = OpLabel
%34 = OpPhi %7 %24 %32 %57 %56
OpLoopMerge %41 %56 DontUnroll
OpBranch %35
%35 = OpLabel
%36 = OpSLessThan %11 %34 %10
OpBranchConditional %36 %37 %41
%37 = OpLabel
%38 = OpAccessChain %18 %4 %34
OpStore %38 %17
OpBranch %39
%39 = OpLabel
%40 = OpIAdd %7 %34 %19
OpBranch %42
%42 = OpLabel
OpBranch %44
%44 = OpLabel
%45 = OpSLessThan %11 %40 %10
OpBranch %46
%46 = OpLabel
%47 = OpAccessChain %18 %4 %40
OpStore %47 %17
OpBranch %48
%48 = OpLabel
%49 = OpIAdd %7 %40 %19
OpBranch %50
%50 = OpLabel
OpBranch %52
%52 = OpLabel
%53 = OpSLessThan %11 %49 %10
OpBranch %54
%54 = OpLabel
%55 = OpAccessChain %18 %4 %49
OpStore %55 %17
OpBranch %56
%56 = OpLabel
%57 = OpIAdd %7 %49 %19
OpBranch %33
%41 = OpLabel
OpReturn
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
// By unrolling by a factor that doesn't divide evenly into the number of loop
// iterations we perfom an additional transform when partially unrolling to
// account for the remainder.
SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false);
}
/* Generated from
#version 410 core
layout(location=0) flat in int upper_bound;
void main() {
float x[10];
for (int i = 2; i < 8; i+=2) {
x[i] = i;
}
}
*/
TEST_F(PassClassTest, SimpleLoopIterationsCheck) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %5 "x"
OpName %3 "upper_bound"
OpDecorate %3 Flat
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 2
%11 = OpConstant %8 8
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 10
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpTypePointer Function %13
%19 = OpTypePointer Input %8
%3 = OpVariable %19 Input
%2 = OpFunction %6 None %7
%20 = OpLabel
%5 = OpVariable %17 Function
OpBranch %21
%21 = OpLabel
%34 = OpPhi %8 %10 %20 %33 %23
OpLoopMerge %22 %23 Unroll
OpBranch %24
%24 = OpLabel
%26 = OpSLessThan %12 %34 %11
OpBranchConditional %26 %27 %22
%27 = OpLabel
%30 = OpConvertSToF %13 %34
%31 = OpAccessChain %18 %5 %34
OpStore %31 %30
OpBranch %23
%23 = OpLabel
%33 = OpIAdd %8 %34 %10
OpBranch %21
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
ir::Function* f = spvtest::GetFunction(module, 2);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
ir::Loop& loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_TRUE(loop.HasUnrollLoopControl());
ir::BasicBlock* condition = loop.FindConditionBlock();
EXPECT_EQ(condition->id(), 24u);
ir::Instruction* induction = loop.FindConditionVariable(condition);
EXPECT_EQ(induction->result_id(), 34u);
opt::LoopUtils loop_utils{context.get(), &loop};
EXPECT_TRUE(loop_utils.CanPerformUnroll());
size_t iterations = 0;
EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
&iterations));
EXPECT_EQ(iterations, 3u);
}
/* Generated from
#version 410 core
void main() {
float x[10];
for (int i = -1; i < 6; i+=3) {
x[i] = i;
}
}
*/
TEST_F(PassClassTest, SimpleLoopIterationsCheckSignedInit) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %5 "x"
OpName %3 "upper_bound"
OpDecorate %3 Flat
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 -1
%11 = OpConstant %8 6
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 10
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpTypePointer Function %13
%19 = OpConstant %8 3
%20 = OpTypePointer Input %8
%3 = OpVariable %20 Input
%2 = OpFunction %6 None %7
%21 = OpLabel
%5 = OpVariable %17 Function
OpBranch %22
%22 = OpLabel
%35 = OpPhi %8 %10 %21 %34 %24
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%27 = OpSLessThan %12 %35 %11
OpBranchConditional %27 %28 %23
%28 = OpLabel
%31 = OpConvertSToF %13 %35
%32 = OpAccessChain %18 %5 %35
OpStore %32 %31
OpBranch %24
%24 = OpLabel
%34 = OpIAdd %8 %35 %19
OpBranch %22
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
ir::Function* f = spvtest::GetFunction(module, 2);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
ir::Loop& loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_FALSE(loop.HasUnrollLoopControl());
ir::BasicBlock* condition = loop.FindConditionBlock();
EXPECT_EQ(condition->id(), 25u);
ir::Instruction* induction = loop.FindConditionVariable(condition);
EXPECT_EQ(induction->result_id(), 35u);
opt::LoopUtils loop_utils{context.get(), &loop};
EXPECT_TRUE(loop_utils.CanPerformUnroll());
size_t iterations = 0;
EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
&iterations));
EXPECT_EQ(iterations, 3u);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[6];
for (uint i = 0; i < 2; i++) {
for (int x = 0; x < 3; ++x) {
out_array[x + i*3] = i;
}
}
}
*/
TEST_F(PassClassTest, UnrollNestedLoops) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %35 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 2
%17 = OpTypeBool
%19 = OpTypeInt 32 1
%20 = OpTypePointer Function %19
%22 = OpConstant %19 0
%29 = OpConstant %19 3
%31 = OpTypeFloat 32
%32 = OpConstant %6 6
%33 = OpTypeArray %31 %32
%34 = OpTypePointer Function %33
%39 = OpConstant %6 3
%44 = OpTypePointer Function %31
%47 = OpConstant %19 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%35 = OpVariable %34 Function
OpBranch %10
%10 = OpLabel
%51 = OpPhi %6 %9 %5 %50 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpULessThan %17 %51 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %23
%23 = OpLabel
%54 = OpPhi %19 %22 %11 %48 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%30 = OpSLessThan %17 %54 %29
OpBranchConditional %30 %24 %25
%24 = OpLabel
%37 = OpBitcast %6 %54
%40 = OpIMul %6 %51 %39
%41 = OpIAdd %6 %37 %40
%43 = OpConvertUToF %31 %51
%45 = OpAccessChain %44 %35 %41
OpStore %45 %43
OpBranch %26
%26 = OpLabel
%48 = OpIAdd %19 %54 %47
OpBranch %23
%25 = OpLabel
OpBranch %13
%13 = OpLabel
%50 = OpIAdd %6 %51 %47
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%8 = OpConstant %6 0
%9 = OpConstant %6 2
%10 = OpTypeBool
%11 = OpTypeInt 32 1
%12 = OpTypePointer Function %11
%13 = OpConstant %11 0
%14 = OpConstant %11 3
%15 = OpTypeFloat 32
%16 = OpConstant %6 6
%17 = OpTypeArray %15 %16
%18 = OpTypePointer Function %17
%19 = OpConstant %6 3
%20 = OpTypePointer Function %15
%21 = OpConstant %11 1
%2 = OpFunction %4 None %5
%22 = OpLabel
%3 = OpVariable %18 Function
OpBranch %23
%23 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpULessThan %10 %8 %9
OpBranch %30
%30 = OpLabel
OpBranch %31
%31 = OpLabel
OpBranch %36
%36 = OpLabel
%37 = OpSLessThan %10 %13 %14
OpBranch %38
%38 = OpLabel
%39 = OpBitcast %6 %13
%40 = OpIMul %6 %8 %19
%41 = OpIAdd %6 %39 %40
%42 = OpConvertUToF %15 %8
%43 = OpAccessChain %20 %3 %41
OpStore %43 %42
OpBranch %34
%34 = OpLabel
%33 = OpIAdd %11 %13 %21
OpBranch %44
%44 = OpLabel
OpBranch %46
%46 = OpLabel
%47 = OpSLessThan %10 %33 %14
OpBranch %48
%48 = OpLabel
%49 = OpBitcast %6 %33
%50 = OpIMul %6 %8 %19
%51 = OpIAdd %6 %49 %50
%52 = OpConvertUToF %15 %8
%53 = OpAccessChain %20 %3 %51
OpStore %53 %52
OpBranch %54
%54 = OpLabel
%55 = OpIAdd %11 %33 %21
OpBranch %56
%56 = OpLabel
OpBranch %58
%58 = OpLabel
%59 = OpSLessThan %10 %55 %14
OpBranch %60
%60 = OpLabel
%61 = OpBitcast %6 %55
%62 = OpIMul %6 %8 %19
%63 = OpIAdd %6 %61 %62
%64 = OpConvertUToF %15 %8
%65 = OpAccessChain %20 %3 %63
OpStore %65 %64
OpBranch %66
%66 = OpLabel
%67 = OpIAdd %11 %55 %21
OpBranch %35
%35 = OpLabel
OpBranch %26
%26 = OpLabel
%25 = OpIAdd %6 %8 %21
OpBranch %68
%68 = OpLabel
OpBranch %70
%70 = OpLabel
%71 = OpULessThan %10 %25 %9
OpBranch %72
%72 = OpLabel
OpBranch %73
%73 = OpLabel
OpBranch %74
%74 = OpLabel
%75 = OpSLessThan %10 %13 %14
OpBranch %76
%76 = OpLabel
%77 = OpBitcast %6 %13
%78 = OpIMul %6 %25 %19
%79 = OpIAdd %6 %77 %78
%80 = OpConvertUToF %15 %25
%81 = OpAccessChain %20 %3 %79
OpStore %81 %80
OpBranch %82
%82 = OpLabel
%83 = OpIAdd %11 %13 %21
OpBranch %84
%84 = OpLabel
OpBranch %85
%85 = OpLabel
%86 = OpSLessThan %10 %83 %14
OpBranch %87
%87 = OpLabel
%88 = OpBitcast %6 %83
%89 = OpIMul %6 %25 %19
%90 = OpIAdd %6 %88 %89
%91 = OpConvertUToF %15 %25
%92 = OpAccessChain %20 %3 %90
OpStore %92 %91
OpBranch %93
%93 = OpLabel
%94 = OpIAdd %11 %83 %21
OpBranch %95
%95 = OpLabel
OpBranch %96
%96 = OpLabel
%97 = OpSLessThan %10 %94 %14
OpBranch %98
%98 = OpLabel
%99 = OpBitcast %6 %94
%100 = OpIMul %6 %25 %19
%101 = OpIAdd %6 %99 %100
%102 = OpConvertUToF %15 %25
%103 = OpAccessChain %20 %3 %101
OpStore %103 %102
OpBranch %104
%104 = OpLabel
%105 = OpIAdd %11 %94 %21
OpBranch %106
%106 = OpLabel
OpBranch %107
%107 = OpLabel
%108 = OpIAdd %6 %25 %21
OpBranch %27
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[2];
for (int i = -3; i < -1; i++) {
out_array[3 + i] = i;
}
}
*/
TEST_F(PassClassTest, NegativeConditionAndInit) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %23 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 -3
%16 = OpConstant %6 -1
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 2
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%25 = OpConstant %6 3
%30 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%23 = OpVariable %22 Function
OpBranch %10
%10 = OpLabel
%32 = OpPhi %6 %9 %5 %31 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %32 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%26 = OpIAdd %6 %32 %25
%28 = OpAccessChain %7 %23 %26
OpStore %28 %32
OpBranch %13
%13 = OpLabel
%31 = OpIAdd %6 %32 %30
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string expected = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 -3
%9 = OpConstant %6 -1
%10 = OpTypeBool
%11 = OpTypeInt 32 0
%12 = OpConstant %11 2
%13 = OpTypeArray %6 %12
%14 = OpTypePointer Function %13
%15 = OpConstant %6 3
%16 = OpConstant %6 1
%2 = OpFunction %4 None %5
%17 = OpLabel
%3 = OpVariable %14 Function
OpBranch %18
%18 = OpLabel
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %10 %8 %9
OpBranch %25
%25 = OpLabel
%26 = OpIAdd %6 %8 %15
%27 = OpAccessChain %7 %3 %26
OpStore %27 %8
OpBranch %21
%21 = OpLabel
%20 = OpIAdd %6 %8 %16
OpBranch %28
%28 = OpLabel
OpBranch %30
%30 = OpLabel
%31 = OpSLessThan %10 %20 %9
OpBranch %32
%32 = OpLabel
%33 = OpIAdd %6 %20 %15
%34 = OpAccessChain %7 %3 %33
OpStore %34 %20
OpBranch %35
%35 = OpLabel
%36 = OpIAdd %6 %20 %16
OpBranch %22
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
// SinglePassRunAndCheck<opt::LoopUnroller>(text, expected, false);
ir::Function* f = spvtest::GetFunction(module, 4);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
ir::Loop& loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_TRUE(loop.HasUnrollLoopControl());
ir::BasicBlock* condition = loop.FindConditionBlock();
EXPECT_EQ(condition->id(), 14u);
ir::Instruction* induction = loop.FindConditionVariable(condition);
EXPECT_EQ(induction->result_id(), 32u);
opt::LoopUtils loop_utils{context.get(), &loop};
EXPECT_TRUE(loop_utils.CanPerformUnroll());
size_t iterations = 0;
EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
&iterations));
EXPECT_EQ(iterations, 2u);
SinglePassRunAndCheck<opt::LoopUnroller>(text, expected, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[9];
for (int i = -10; i < -1; i++) {
out_array[i] = i;
}
}
*/
TEST_F(PassClassTest, NegativeConditionAndInitResidualUnroll) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %23 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 -10
%16 = OpConstant %6 -1
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 9
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%25 = OpConstant %6 10
%30 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%23 = OpVariable %22 Function
OpBranch %10
%10 = OpLabel
%32 = OpPhi %6 %9 %5 %31 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %32 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%26 = OpIAdd %6 %32 %25
%28 = OpAccessChain %7 %23 %26
OpStore %28 %32
OpBranch %13
%13 = OpLabel
%31 = OpIAdd %6 %32 %30
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string expected = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 -10
%9 = OpConstant %6 -1
%10 = OpTypeBool
%11 = OpTypeInt 32 0
%12 = OpConstant %11 9
%13 = OpTypeArray %6 %12
%14 = OpTypePointer Function %13
%15 = OpConstant %6 10
%16 = OpConstant %6 1
%48 = OpConstant %6 -9
%2 = OpFunction %4 None %5
%17 = OpLabel
%3 = OpVariable %14 Function
OpBranch %18
%18 = OpLabel
%19 = OpPhi %6 %8 %17 %20 %21
OpLoopMerge %28 %21 Unroll
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %10 %19 %48
OpBranchConditional %24 %25 %28
%25 = OpLabel
%26 = OpIAdd %6 %19 %15
%27 = OpAccessChain %7 %3 %26
OpStore %27 %19
OpBranch %21
%21 = OpLabel
%20 = OpIAdd %6 %19 %16
OpBranch %18
%28 = OpLabel
OpBranch %29
%29 = OpLabel
%30 = OpPhi %6 %19 %28 %47 %46
OpLoopMerge %38 %46 DontUnroll
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %10 %30 %9
OpBranchConditional %32 %33 %38
%33 = OpLabel
%34 = OpIAdd %6 %30 %15
%35 = OpAccessChain %7 %3 %34
OpStore %35 %30
OpBranch %36
%36 = OpLabel
%37 = OpIAdd %6 %30 %16
OpBranch %39
%39 = OpLabel
OpBranch %41
%41 = OpLabel
%42 = OpSLessThan %10 %37 %9
OpBranch %43
%43 = OpLabel
%44 = OpIAdd %6 %37 %15
%45 = OpAccessChain %7 %3 %44
OpStore %45 %37
OpBranch %46
%46 = OpLabel
%47 = OpIAdd %6 %37 %16
OpBranch %29
%38 = OpLabel
OpReturn
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
ir::Function* f = spvtest::GetFunction(module, 4);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
ir::Loop& loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_TRUE(loop.HasUnrollLoopControl());
ir::BasicBlock* condition = loop.FindConditionBlock();
EXPECT_EQ(condition->id(), 14u);
ir::Instruction* induction = loop.FindConditionVariable(condition);
EXPECT_EQ(induction->result_id(), 32u);
opt::LoopUtils loop_utils{context.get(), &loop};
EXPECT_TRUE(loop_utils.CanPerformUnroll());
size_t iterations = 0;
EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
&iterations));
EXPECT_EQ(iterations, 9u);
SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, expected, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[10];
for (uint i = 0; i < 2; i++) {
for (int x = 0; x < 5; ++x) {
out_array[x + i*5] = i;
}
}
}
*/
TEST_F(PassClassTest, UnrollNestedLoopsValidateDescriptor) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %35 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 2
%17 = OpTypeBool
%19 = OpTypeInt 32 1
%20 = OpTypePointer Function %19
%22 = OpConstant %19 0
%29 = OpConstant %19 5
%31 = OpTypeFloat 32
%32 = OpConstant %6 10
%33 = OpTypeArray %31 %32
%34 = OpTypePointer Function %33
%39 = OpConstant %6 5
%44 = OpTypePointer Function %31
%47 = OpConstant %19 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%35 = OpVariable %34 Function
OpBranch %10
%10 = OpLabel
%51 = OpPhi %6 %9 %5 %50 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpULessThan %17 %51 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %23
%23 = OpLabel
%54 = OpPhi %19 %22 %11 %48 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%30 = OpSLessThan %17 %54 %29
OpBranchConditional %30 %24 %25
%24 = OpLabel
%37 = OpBitcast %6 %54
%40 = OpIMul %6 %51 %39
%41 = OpIAdd %6 %37 %40
%43 = OpConvertUToF %31 %51
%45 = OpAccessChain %44 %35 %41
OpStore %45 %43
OpBranch %26
%26 = OpLabel
%48 = OpIAdd %19 %54 %47
OpBranch %23
%25 = OpLabel
OpBranch %13
%13 = OpLabel
%50 = OpIAdd %6 %51 %47
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
{ // Test fully unroll
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 ushader:\n"
<< text << std::endl;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
ir::Function* f = spvtest::GetFunction(module, 4);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
ir::Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
ir::Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
{
opt::LoopUtils loop_utils{context.get(), &inner_loop};
loop_utils.FullyUnroll();
loop_utils.Finalize();
}
EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
EXPECT_EQ(outer_loop.GetBlocks().size(), 25u);
EXPECT_EQ(outer_loop.NumImmediateChildren(), 0u);
{
opt::LoopUtils loop_utils{context.get(), &outer_loop};
loop_utils.FullyUnroll();
loop_utils.Finalize();
}
EXPECT_EQ(loop_descriptor.NumLoops(), 0u);
}
{ // Test partially unroll
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 ushader:\n"
<< text << std::endl;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
ir::Function* f = spvtest::GetFunction(module, 4);
ir::LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
ir::Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
ir::Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
opt::LoopUtils loop_utils{context.get(), &inner_loop};
loop_utils.PartiallyUnroll(2);
loop_utils.Finalize();
// The number of loops should actually grow.
EXPECT_EQ(loop_descriptor.NumLoops(), 3u);
EXPECT_EQ(outer_loop.GetBlocks().size(), 18u);
EXPECT_EQ(outer_loop.NumImmediateChildren(), 2u);
}
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[3];
for (int i = 3; i > 0; --i) {
out_array[i] = i;
}
}
*/
TEST_F(PassClassTest, FullyUnrollNegativeStepLoopTest) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %24 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 3
%16 = OpConstant %6 0
%17 = OpTypeBool
%19 = OpTypeFloat 32
%20 = OpTypeInt 32 0
%21 = OpConstant %20 3
%22 = OpTypeArray %19 %21
%23 = OpTypePointer Function %22
%28 = OpTypePointer Function %19
%31 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%24 = OpVariable %23 Function
OpBranch %10
%10 = OpLabel
%33 = OpPhi %6 %9 %5 %32 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSGreaterThan %17 %33 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpConvertSToF %19 %33
%29 = OpAccessChain %28 %24 %33
OpStore %29 %27
OpBranch %13
%13 = OpLabel
%32 = OpISub %6 %33 %31
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 3
%9 = OpConstant %6 0
%10 = OpTypeBool
%11 = OpTypeFloat 32
%12 = OpTypeInt 32 0
%13 = OpConstant %12 3
%14 = OpTypeArray %11 %13
%15 = OpTypePointer Function %14
%16 = OpTypePointer Function %11
%17 = OpConstant %6 1
%2 = OpFunction %4 None %5
%18 = OpLabel
%3 = OpVariable %15 Function
OpBranch %19
%19 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpSGreaterThan %10 %8 %9
OpBranch %26
%26 = OpLabel
%27 = OpConvertSToF %11 %8
%28 = OpAccessChain %16 %3 %8
OpStore %28 %27
OpBranch %22
%22 = OpLabel
%21 = OpISub %6 %8 %17
OpBranch %29
%29 = OpLabel
OpBranch %31
%31 = OpLabel
%32 = OpSGreaterThan %10 %21 %9
OpBranch %33
%33 = OpLabel
%34 = OpConvertSToF %11 %21
%35 = OpAccessChain %16 %3 %21
OpStore %35 %34
OpBranch %36
%36 = OpLabel
%37 = OpISub %6 %21 %17
OpBranch %38
%38 = OpLabel
OpBranch %40
%40 = OpLabel
%41 = OpSGreaterThan %10 %37 %9
OpBranch %42
%42 = OpLabel
%43 = OpConvertSToF %11 %37
%44 = OpAccessChain %16 %3 %37
OpStore %44 %43
OpBranch %45
%45 = OpLabel
%46 = OpISub %6 %37 %17
OpBranch %23
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[3];
for (int i = 9; i > 0; i-=3) {
out_array[i] = i;
}
}
*/
TEST_F(PassClassTest, FullyUnrollNegativeNonOneStepLoop) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %24 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 9
%16 = OpConstant %6 0
%17 = OpTypeBool
%19 = OpTypeFloat 32
%20 = OpTypeInt 32 0
%21 = OpConstant %20 3
%22 = OpTypeArray %19 %21
%23 = OpTypePointer Function %22
%28 = OpTypePointer Function %19
%30 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%24 = OpVariable %23 Function
OpBranch %10
%10 = OpLabel
%33 = OpPhi %6 %9 %5 %32 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSGreaterThan %17 %33 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpConvertSToF %19 %33
%29 = OpAccessChain %28 %24 %33
OpStore %29 %27
OpBranch %13
%13 = OpLabel
%32 = OpISub %6 %33 %30
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 9
%9 = OpConstant %6 0
%10 = OpTypeBool
%11 = OpTypeFloat 32
%12 = OpTypeInt 32 0
%13 = OpConstant %12 3
%14 = OpTypeArray %11 %13
%15 = OpTypePointer Function %14
%16 = OpTypePointer Function %11
%17 = OpConstant %6 3
%2 = OpFunction %4 None %5
%18 = OpLabel
%3 = OpVariable %15 Function
OpBranch %19
%19 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpSGreaterThan %10 %8 %9
OpBranch %26
%26 = OpLabel
%27 = OpConvertSToF %11 %8
%28 = OpAccessChain %16 %3 %8
OpStore %28 %27
OpBranch %22
%22 = OpLabel
%21 = OpISub %6 %8 %17
OpBranch %29
%29 = OpLabel
OpBranch %31
%31 = OpLabel
%32 = OpSGreaterThan %10 %21 %9
OpBranch %33
%33 = OpLabel
%34 = OpConvertSToF %11 %21
%35 = OpAccessChain %16 %3 %21
OpStore %35 %34
OpBranch %36
%36 = OpLabel
%37 = OpISub %6 %21 %17
OpBranch %38
%38 = OpLabel
OpBranch %40
%40 = OpLabel
%41 = OpSGreaterThan %10 %37 %9
OpBranch %42
%42 = OpLabel
%43 = OpConvertSToF %11 %37
%44 = OpAccessChain %16 %3 %37
OpStore %44 %43
OpBranch %45
%45 = OpLabel
%46 = OpISub %6 %37 %17
OpBranch %23
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[3];
for (int i = 0; i < 7; i+=3) {
out_array[i] = i;
}
}
*/
TEST_F(PassClassTest, FullyUnrollNonDivisibleStepLoop) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %24 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 7
%17 = OpTypeBool
%19 = OpTypeFloat 32
%20 = OpTypeInt 32 0
%21 = OpConstant %20 3
%22 = OpTypeArray %19 %21
%23 = OpTypePointer Function %22
%28 = OpTypePointer Function %19
%30 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%24 = OpVariable %23 Function
OpBranch %10
%10 = OpLabel
%33 = OpPhi %6 %9 %5 %32 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %33 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpConvertSToF %19 %33
%29 = OpAccessChain %28 %24 %33
OpStore %29 %27
OpBranch %13
%13 = OpLabel
%32 = OpIAdd %6 %33 %30
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 0
%9 = OpConstant %6 7
%10 = OpTypeBool
%11 = OpTypeFloat 32
%12 = OpTypeInt 32 0
%13 = OpConstant %12 3
%14 = OpTypeArray %11 %13
%15 = OpTypePointer Function %14
%16 = OpTypePointer Function %11
%17 = OpConstant %6 3
%2 = OpFunction %4 None %5
%18 = OpLabel
%3 = OpVariable %15 Function
OpBranch %19
%19 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpSLessThan %10 %8 %9
OpBranch %26
%26 = OpLabel
%27 = OpConvertSToF %11 %8
%28 = OpAccessChain %16 %3 %8
OpStore %28 %27
OpBranch %22
%22 = OpLabel
%21 = OpIAdd %6 %8 %17
OpBranch %29
%29 = OpLabel
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %10 %21 %9
OpBranch %33
%33 = OpLabel
%34 = OpConvertSToF %11 %21
%35 = OpAccessChain %16 %3 %21
OpStore %35 %34
OpBranch %36
%36 = OpLabel
%37 = OpIAdd %6 %21 %17
OpBranch %38
%38 = OpLabel
OpBranch %40
%40 = OpLabel
%41 = OpSLessThan %10 %37 %9
OpBranch %42
%42 = OpLabel
%43 = OpConvertSToF %11 %37
%44 = OpAccessChain %16 %3 %37
OpStore %44 %43
OpBranch %45
%45 = OpLabel
%46 = OpIAdd %6 %37 %17
OpBranch %23
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
/*
Generated from the following GLSL
#version 410 core
void main() {
float out_array[4];
for (int i = 11; i > 0; i-=3) {
out_array[i] = i;
}
}
*/
TEST_F(PassClassTest, FullyUnrollNegativeNonDivisibleStepLoop) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %24 "out_array"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 11
%16 = OpConstant %6 0
%17 = OpTypeBool
%19 = OpTypeFloat 32
%20 = OpTypeInt 32 0
%21 = OpConstant %20 4
%22 = OpTypeArray %19 %21
%23 = OpTypePointer Function %22
%28 = OpTypePointer Function %19
%30 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%24 = OpVariable %23 Function
OpBranch %10
%10 = OpLabel
%33 = OpPhi %6 %9 %5 %32 %13
OpLoopMerge %12 %13 Unroll
OpBranch %14
%14 = OpLabel
%18 = OpSGreaterThan %17 %33 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpConvertSToF %19 %33
%29 = OpAccessChain %28 %24 %33
OpStore %29 %27
OpBranch %13
%13 = OpLabel
%32 = OpISub %6 %33 %30
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "out_array"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 11
%9 = OpConstant %6 0
%10 = OpTypeBool
%11 = OpTypeFloat 32
%12 = OpTypeInt 32 0
%13 = OpConstant %12 4
%14 = OpTypeArray %11 %13
%15 = OpTypePointer Function %14
%16 = OpTypePointer Function %11
%17 = OpConstant %6 3
%2 = OpFunction %4 None %5
%18 = OpLabel
%3 = OpVariable %15 Function
OpBranch %19
%19 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpSGreaterThan %10 %8 %9
OpBranch %26
%26 = OpLabel
%27 = OpConvertSToF %11 %8
%28 = OpAccessChain %16 %3 %8
OpStore %28 %27
OpBranch %22
%22 = OpLabel
%21 = OpISub %6 %8 %17
OpBranch %29
%29 = OpLabel
OpBranch %31
%31 = OpLabel
%32 = OpSGreaterThan %10 %21 %9
OpBranch %33
%33 = OpLabel
%34 = OpConvertSToF %11 %21
%35 = OpAccessChain %16 %3 %21
OpStore %35 %34
OpBranch %36
%36 = OpLabel
%37 = OpISub %6 %21 %17
OpBranch %38
%38 = OpLabel
OpBranch %40
%40 = OpLabel
%41 = OpSGreaterThan %10 %37 %9
OpBranch %42
%42 = OpLabel
%43 = OpConvertSToF %11 %37
%44 = OpAccessChain %16 %3 %37
OpStore %44 %43
OpBranch %45
%45 = OpLabel
%46 = OpISub %6 %37 %17
OpBranch %47
%47 = OpLabel
OpBranch %49
%49 = OpLabel
%50 = OpSGreaterThan %10 %46 %9
OpBranch %51
%51 = OpLabel
%52 = OpConvertSToF %11 %46
%53 = OpAccessChain %16 %3 %46
OpStore %53 %52
OpBranch %54
%54 = OpLabel
%55 = OpISub %6 %46 %17
OpBranch %23
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
// clang-format off
// With opt::LocalMultiStoreElimPass
static const std::string multiple_phi_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 410
OpName %4 "main"
OpName %8 "foo("
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%10 = OpTypePointer Function %6
%12 = OpConstant %6 0
%14 = OpConstant %6 3
%22 = OpConstant %6 6
%23 = OpTypeBool
%31 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%40 = OpFunctionCall %6 %8
OpReturn
OpFunctionEnd
%8 = OpFunction %6 None %7
%9 = OpLabel
OpBranch %16
%16 = OpLabel
%41 = OpPhi %6 %12 %9 %34 %19
%42 = OpPhi %6 %14 %9 %29 %19
%43 = OpPhi %6 %12 %9 %32 %19
OpLoopMerge %18 %19 Unroll
OpBranch %20
%20 = OpLabel
%24 = OpSLessThan %23 %43 %22
OpBranchConditional %24 %17 %18
%17 = OpLabel
%27 = OpIMul %6 %43 %41
%29 = OpIAdd %6 %42 %27
OpBranch %19
%19 = OpLabel
%32 = OpIAdd %6 %43 %31
%34 = OpISub %6 %41 %31
OpBranch %16
%18 = OpLabel
%37 = OpIAdd %6 %42 %41
OpReturnValue %37
OpFunctionEnd
)";
// clang-format on
TEST_F(PassClassTest, PartiallyUnrollResidualMultipleInductionVariables) {
// clang-format off
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "foo("
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%8 = OpTypePointer Function %6
%9 = OpConstant %6 0
%10 = OpConstant %6 3
%11 = OpConstant %6 6
%12 = OpTypeBool
%13 = OpConstant %6 1
%82 = OpTypeInt 32 0
%83 = OpConstant %82 2
%2 = OpFunction %4 None %5
%14 = OpLabel
%15 = OpFunctionCall %6 %3
OpReturn
OpFunctionEnd
%3 = OpFunction %6 None %7
%16 = OpLabel
OpBranch %17
%17 = OpLabel
%18 = OpPhi %6 %9 %16 %19 %20
%21 = OpPhi %6 %10 %16 %22 %20
%23 = OpPhi %6 %9 %16 %24 %20
OpLoopMerge %31 %20 Unroll
OpBranch %26
%26 = OpLabel
%27 = OpSLessThan %12 %23 %83
OpBranchConditional %27 %28 %31
%28 = OpLabel
%29 = OpIMul %6 %23 %18
%22 = OpIAdd %6 %21 %29
OpBranch %20
%20 = OpLabel
%24 = OpIAdd %6 %23 %13
%19 = OpISub %6 %18 %13
OpBranch %17
%31 = OpLabel
OpBranch %32
%32 = OpLabel
%33 = OpPhi %6 %18 %31 %81 %79
%34 = OpPhi %6 %21 %31 %78 %79
%35 = OpPhi %6 %23 %31 %80 %79
OpLoopMerge %44 %79 DontUnroll
OpBranch %36
%36 = OpLabel
%37 = OpSLessThan %12 %35 %11
OpBranchConditional %37 %38 %44
%38 = OpLabel
%39 = OpIMul %6 %35 %33
%40 = OpIAdd %6 %34 %39
OpBranch %41
%41 = OpLabel
%42 = OpIAdd %6 %35 %13
%43 = OpISub %6 %33 %13
OpBranch %46
%46 = OpLabel
OpBranch %50
%50 = OpLabel
%51 = OpSLessThan %12 %42 %11
OpBranch %52
%52 = OpLabel
%53 = OpIMul %6 %42 %43
%54 = OpIAdd %6 %40 %53
OpBranch %55
%55 = OpLabel
%56 = OpIAdd %6 %42 %13
%57 = OpISub %6 %43 %13
OpBranch %58
%58 = OpLabel
OpBranch %62
%62 = OpLabel
%63 = OpSLessThan %12 %56 %11
OpBranch %64
%64 = OpLabel
%65 = OpIMul %6 %56 %57
%66 = OpIAdd %6 %54 %65
OpBranch %67
%67 = OpLabel
%68 = OpIAdd %6 %56 %13
%69 = OpISub %6 %57 %13
OpBranch %70
%70 = OpLabel
OpBranch %74
%74 = OpLabel
%75 = OpSLessThan %12 %68 %11
OpBranch %76
%76 = OpLabel
%77 = OpIMul %6 %68 %69
%78 = OpIAdd %6 %66 %77
OpBranch %79
%79 = OpLabel
%80 = OpIAdd %6 %68 %13
%81 = OpISub %6 %69 %13
OpBranch %32
%44 = OpLabel
%45 = OpIAdd %6 %34 %33
OpReturnValue %45
%25 = OpLabel
%30 = OpIAdd %6 %34 %33
OpReturnValue %30
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< multiple_phi_shader << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<PartialUnrollerTestPass<4>>(multiple_phi_shader, output,
false);
}
TEST_F(PassClassTest, PartiallyUnrollMultipleInductionVariables) {
// clang-format off
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "foo("
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%8 = OpTypePointer Function %6
%9 = OpConstant %6 0
%10 = OpConstant %6 3
%11 = OpConstant %6 6
%12 = OpTypeBool
%13 = OpConstant %6 1
%2 = OpFunction %4 None %5
%14 = OpLabel
%15 = OpFunctionCall %6 %3
OpReturn
OpFunctionEnd
%3 = OpFunction %6 None %7
%16 = OpLabel
OpBranch %17
%17 = OpLabel
%18 = OpPhi %6 %9 %16 %42 %40
%21 = OpPhi %6 %10 %16 %39 %40
%23 = OpPhi %6 %9 %16 %41 %40
OpLoopMerge %25 %40 DontUnroll
OpBranch %26
%26 = OpLabel
%27 = OpSLessThan %12 %23 %11
OpBranchConditional %27 %28 %25
%28 = OpLabel
%29 = OpIMul %6 %23 %18
%22 = OpIAdd %6 %21 %29
OpBranch %20
%20 = OpLabel
%24 = OpIAdd %6 %23 %13
%19 = OpISub %6 %18 %13
OpBranch %31
%31 = OpLabel
OpBranch %35
%35 = OpLabel
%36 = OpSLessThan %12 %24 %11
OpBranch %37
%37 = OpLabel
%38 = OpIMul %6 %24 %19
%39 = OpIAdd %6 %22 %38
OpBranch %40
%40 = OpLabel
%41 = OpIAdd %6 %24 %13
%42 = OpISub %6 %19 %13
OpBranch %17
%25 = OpLabel
%30 = OpIAdd %6 %21 %18
OpReturnValue %30
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< multiple_phi_shader << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(multiple_phi_shader, output,
false);
}
TEST_F(PassClassTest, FullyUnrollMultipleInductionVariables) {
// clang-format off
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 410
OpName %2 "main"
OpName %3 "foo("
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%8 = OpTypePointer Function %6
%9 = OpConstant %6 0
%10 = OpConstant %6 3
%11 = OpConstant %6 6
%12 = OpTypeBool
%13 = OpConstant %6 1
%2 = OpFunction %4 None %5
%14 = OpLabel
%15 = OpFunctionCall %6 %3
OpReturn
OpFunctionEnd
%3 = OpFunction %6 None %7
%16 = OpLabel
OpBranch %17
%17 = OpLabel
OpBranch %26
%26 = OpLabel
%27 = OpSLessThan %12 %9 %11
OpBranch %28
%28 = OpLabel
%29 = OpIMul %6 %9 %9
%22 = OpIAdd %6 %10 %29
OpBranch %20
%20 = OpLabel
%24 = OpIAdd %6 %9 %13
%19 = OpISub %6 %9 %13
OpBranch %31
%31 = OpLabel
OpBranch %35
%35 = OpLabel
%36 = OpSLessThan %12 %24 %11
OpBranch %37
%37 = OpLabel
%38 = OpIMul %6 %24 %19
%39 = OpIAdd %6 %22 %38
OpBranch %40
%40 = OpLabel
%41 = OpIAdd %6 %24 %13
%42 = OpISub %6 %19 %13
OpBranch %43
%43 = OpLabel
OpBranch %47
%47 = OpLabel
%48 = OpSLessThan %12 %41 %11
OpBranch %49
%49 = OpLabel
%50 = OpIMul %6 %41 %42
%51 = OpIAdd %6 %39 %50
OpBranch %52
%52 = OpLabel
%53 = OpIAdd %6 %41 %13
%54 = OpISub %6 %42 %13
OpBranch %55
%55 = OpLabel
OpBranch %59
%59 = OpLabel
%60 = OpSLessThan %12 %53 %11
OpBranch %61
%61 = OpLabel
%62 = OpIMul %6 %53 %54
%63 = OpIAdd %6 %51 %62
OpBranch %64
%64 = OpLabel
%65 = OpIAdd %6 %53 %13
%66 = OpISub %6 %54 %13
OpBranch %67
%67 = OpLabel
OpBranch %71
%71 = OpLabel
%72 = OpSLessThan %12 %65 %11
OpBranch %73
%73 = OpLabel
%74 = OpIMul %6 %65 %66
%75 = OpIAdd %6 %63 %74
OpBranch %76
%76 = OpLabel
%77 = OpIAdd %6 %65 %13
%78 = OpISub %6 %66 %13
OpBranch %79
%79 = OpLabel
OpBranch %83
%83 = OpLabel
%84 = OpSLessThan %12 %77 %11
OpBranch %85
%85 = OpLabel
%86 = OpIMul %6 %77 %78
%87 = OpIAdd %6 %75 %86
OpBranch %88
%88 = OpLabel
%89 = OpIAdd %6 %77 %13
%90 = OpISub %6 %78 %13
OpBranch %25
%25 = OpLabel
%30 = OpIAdd %6 %87 %90
OpReturnValue %30
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< multiple_phi_shader << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(multiple_phi_shader, output, false);
}
/*
Generated from the following GLSL
#version 440 core
void main()
{
int j = 0;
for (int i = 0; i <= 2; ++i)
++j;
for (int i = 1; i >= 0; --i)
++j;
}
*/
TEST_F(PassClassTest, FullyUnrollEqualToOperations) {
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%17 = OpConstant %6 2
%18 = OpTypeBool
%21 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %11
%11 = OpLabel
%37 = OpPhi %6 %9 %5 %22 %14
%38 = OpPhi %6 %9 %5 %24 %14
OpLoopMerge %13 %14 Unroll
OpBranch %15
%15 = OpLabel
%19 = OpSLessThanEqual %18 %38 %17
OpBranchConditional %19 %12 %13
%12 = OpLabel
%22 = OpIAdd %6 %37 %21
OpBranch %14
%14 = OpLabel
%24 = OpIAdd %6 %38 %21
OpBranch %11
%13 = OpLabel
OpBranch %26
%26 = OpLabel
%39 = OpPhi %6 %37 %13 %34 %29
%40 = OpPhi %6 %21 %13 %36 %29
OpLoopMerge %28 %29 Unroll
OpBranch %30
%30 = OpLabel
%32 = OpSGreaterThanEqual %18 %40 %9
OpBranchConditional %32 %27 %28
%27 = OpLabel
%34 = OpIAdd %6 %39 %21
OpBranch %29
%29 = OpLabel
%36 = OpISub %6 %40 %21
OpBranch %26
%28 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 440
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpTypePointer Function %5
%7 = OpConstant %5 0
%8 = OpConstant %5 2
%9 = OpTypeBool
%10 = OpConstant %5 1
%2 = OpFunction %3 None %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpBranch %19
%19 = OpLabel
%20 = OpSLessThanEqual %9 %7 %8
OpBranch %21
%21 = OpLabel
%14 = OpIAdd %5 %7 %10
OpBranch %15
%15 = OpLabel
%17 = OpIAdd %5 %7 %10
OpBranch %41
%41 = OpLabel
OpBranch %44
%44 = OpLabel
%45 = OpSLessThanEqual %9 %17 %8
OpBranch %46
%46 = OpLabel
%47 = OpIAdd %5 %14 %10
OpBranch %48
%48 = OpLabel
%49 = OpIAdd %5 %17 %10
OpBranch %50
%50 = OpLabel
OpBranch %53
%53 = OpLabel
%54 = OpSLessThanEqual %9 %49 %8
OpBranch %55
%55 = OpLabel
%56 = OpIAdd %5 %47 %10
OpBranch %57
%57 = OpLabel
%58 = OpIAdd %5 %49 %10
OpBranch %18
%18 = OpLabel
OpBranch %22
%22 = OpLabel
OpBranch %29
%29 = OpLabel
%30 = OpSGreaterThanEqual %9 %10 %7
OpBranch %31
%31 = OpLabel
%24 = OpIAdd %5 %56 %10
OpBranch %25
%25 = OpLabel
%27 = OpISub %5 %10 %10
OpBranch %32
%32 = OpLabel
OpBranch %35
%35 = OpLabel
%36 = OpSGreaterThanEqual %9 %27 %7
OpBranch %37
%37 = OpLabel
%38 = OpIAdd %5 %24 %10
OpBranch %39
%39 = OpLabel
%40 = OpISub %5 %27 %10
OpBranch %28
%28 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
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 ushader:\n"
<< text << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(text, output, false);
}
// clang-format off
// With opt::LocalMultiStoreElimPass
const std::string condition_in_header = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %o
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpDecorate %o Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_n2 = OpConstant %int -2
%int_2 = OpConstant %int 2
%bool = OpTypeBool
%float = OpTypeFloat 32
%_ptr_Output_float = OpTypePointer Output %float
%o = OpVariable %_ptr_Output_float Output
%float_1 = OpConstant %float 1
%main = OpFunction %void None %6
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%27 = OpPhi %int %int_n2 %15 %26 %18
%21 = OpSLessThanEqual %bool %27 %int_2
OpLoopMerge %17 %18 Unroll
OpBranchConditional %21 %22 %17
%22 = OpLabel
%23 = OpLoad %float %o
%24 = OpFAdd %float %23 %float_1
OpStore %o %24
OpBranch %18
%18 = OpLabel
%26 = OpIAdd %int %27 %int_2
OpBranch %16
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
//clang-format on
TEST_F(PassClassTest, FullyUnrollConditionIsInHeaderBlock) {
// clang-format off
const std::string output =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpSource GLSL 430
OpDecorate %2 Location 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpConstant %5 -2
%7 = OpConstant %5 2
%8 = OpTypeBool
%9 = OpTypeFloat 32
%10 = OpTypePointer Output %9
%2 = OpVariable %10 Output
%11 = OpConstant %9 1
%1 = OpFunction %3 None %4
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%17 = OpSLessThanEqual %8 %6 %7
OpBranch %19
%19 = OpLabel
%20 = OpLoad %9 %2
%21 = OpFAdd %9 %20 %11
OpStore %2 %21
OpBranch %16
%16 = OpLabel
%15 = OpIAdd %5 %6 %7
OpBranch %22
%22 = OpLabel
%24 = OpSLessThanEqual %8 %15 %7
OpBranch %25
%25 = OpLabel
%26 = OpLoad %9 %2
%27 = OpFAdd %9 %26 %11
OpStore %2 %27
OpBranch %28
%28 = OpLabel
%29 = OpIAdd %5 %15 %7
OpBranch %30
%30 = OpLabel
%32 = OpSLessThanEqual %8 %29 %7
OpBranch %33
%33 = OpLabel
%34 = OpLoad %9 %2
%35 = OpFAdd %9 %34 %11
OpStore %2 %35
OpBranch %36
%36 = OpLabel
%37 = OpIAdd %5 %29 %7
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< condition_in_header << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<opt::LoopUnroller>(condition_in_header, output, false);
}
TEST_F(PassClassTest, PartiallyUnrollResidualConditionIsInHeaderBlock) {
// clang-format off
const std::string output =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpSource GLSL 430
OpDecorate %2 Location 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpConstant %5 -2
%7 = OpConstant %5 2
%8 = OpTypeBool
%9 = OpTypeFloat 32
%10 = OpTypePointer Output %9
%2 = OpVariable %10 Output
%11 = OpConstant %9 1
%40 = OpTypeInt 32 0
%41 = OpConstant %40 1
%1 = OpFunction %3 None %4
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpPhi %5 %6 %12 %15 %16
%17 = OpSLessThanEqual %8 %14 %41
OpLoopMerge %22 %16 Unroll
OpBranchConditional %17 %19 %22
%19 = OpLabel
%20 = OpLoad %9 %2
%21 = OpFAdd %9 %20 %11
OpStore %2 %21
OpBranch %16
%16 = OpLabel
%15 = OpIAdd %5 %14 %7
OpBranch %13
%22 = OpLabel
OpBranch %23
%23 = OpLabel
%24 = OpPhi %5 %14 %22 %39 %38
%25 = OpSLessThanEqual %8 %24 %7
OpLoopMerge %31 %38 DontUnroll
OpBranchConditional %25 %26 %31
%26 = OpLabel
%27 = OpLoad %9 %2
%28 = OpFAdd %9 %27 %11
OpStore %2 %28
OpBranch %29
%29 = OpLabel
%30 = OpIAdd %5 %24 %7
OpBranch %32
%32 = OpLabel
%34 = OpSLessThanEqual %8 %30 %7
OpBranch %35
%35 = OpLabel
%36 = OpLoad %9 %2
%37 = OpFAdd %9 %36 %11
OpStore %2 %37
OpBranch %38
%38 = OpLabel
%39 = OpIAdd %5 %30 %7
OpBranch %23
%31 = OpLabel
OpReturn
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< condition_in_header << std::endl;
opt::LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(condition_in_header, output,
false);
}
/*
Generated from following GLSL with latch block artificially inserted to be
seperate from continue.
#version 430
void main(void) {
float x[10];
for (int i = 0; i < 10; ++i) {
x[i] = i;
}
}
*/
TEST_F(PassClassTest, PartiallyUnrollLatchNotContinue) {
// clang-format off
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
OpName %2 "main"
OpName %3 "i"
OpName %4 "x"
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpConstant %7 10
%11 = OpTypeBool
%12 = OpTypeFloat 32
%13 = OpTypeInt 32 0
%14 = OpConstant %13 10
%15 = OpTypeArray %12 %14
%16 = OpTypePointer Function %15
%17 = OpTypePointer Function %12
%18 = OpConstant %7 1
%2 = OpFunction %5 None %6
%19 = OpLabel
%3 = OpVariable %8 Function
%4 = OpVariable %16 Function
OpStore %3 %9
OpBranch %20
%20 = OpLabel
%21 = OpPhi %7 %9 %19 %22 %30
OpLoopMerge %24 %23 Unroll
OpBranch %25
%25 = OpLabel
%26 = OpSLessThan %11 %21 %10
OpBranchConditional %26 %27 %24
%27 = OpLabel
%28 = OpConvertSToF %12 %21
%29 = OpAccessChain %17 %4 %21
OpStore %29 %28
OpBranch %23
%23 = OpLabel
%22 = OpIAdd %7 %21 %18
OpStore %3 %22
OpBranch %30
%30 = OpLabel
OpBranch %20
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string expected = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
OpName %2 "main"
OpName %3 "i"
OpName %4 "x"
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpConstant %7 10
%11 = OpTypeBool
%12 = OpTypeFloat 32
%13 = OpTypeInt 32 0
%14 = OpConstant %13 10
%15 = OpTypeArray %12 %14
%16 = OpTypePointer Function %15
%17 = OpTypePointer Function %12
%18 = OpConstant %7 1
%63 = OpConstant %13 1
%2 = OpFunction %5 None %6
%19 = OpLabel
%3 = OpVariable %8 Function
%4 = OpVariable %16 Function
OpStore %3 %9
OpBranch %20
%20 = OpLabel
%21 = OpPhi %7 %9 %19 %22 %23
OpLoopMerge %31 %25 Unroll
OpBranch %26
%26 = OpLabel
%27 = OpSLessThan %11 %21 %63
OpBranchConditional %27 %28 %31
%28 = OpLabel
%29 = OpConvertSToF %12 %21
%30 = OpAccessChain %17 %4 %21
OpStore %30 %29
OpBranch %25
%25 = OpLabel
%22 = OpIAdd %7 %21 %18
OpStore %3 %22
OpBranch %23
%23 = OpLabel
OpBranch %20
%31 = OpLabel
OpBranch %32
%32 = OpLabel
%33 = OpPhi %7 %21 %31 %61 %62
OpLoopMerge %42 %60 DontUnroll
OpBranch %34
%34 = OpLabel
%35 = OpSLessThan %11 %33 %10
OpBranchConditional %35 %36 %42
%36 = OpLabel
%37 = OpConvertSToF %12 %33
%38 = OpAccessChain %17 %4 %33
OpStore %38 %37
OpBranch %39
%39 = OpLabel
%40 = OpIAdd %7 %33 %18
OpStore %3 %40
OpBranch %41
%41 = OpLabel
OpBranch %43
%43 = OpLabel
OpBranch %45
%45 = OpLabel
%46 = OpSLessThan %11 %40 %10
OpBranch %47
%47 = OpLabel
%48 = OpConvertSToF %12 %40
%49 = OpAccessChain %17 %4 %40
OpStore %49 %48
OpBranch %50
%50 = OpLabel
%51 = OpIAdd %7 %40 %18
OpStore %3 %51
OpBranch %52
%52 = OpLabel
OpBranch %53
%53 = OpLabel
OpBranch %55
%55 = OpLabel
%56 = OpSLessThan %11 %51 %10
OpBranch %57
%57 = OpLabel
%58 = OpConvertSToF %12 %51
%59 = OpAccessChain %17 %4 %51
OpStore %59 %58
OpBranch %60
%60 = OpLabel
%61 = OpIAdd %7 %51 %18
OpStore %3 %61
OpBranch %62
%62 = OpLabel
OpBranch %32
%42 = OpLabel
OpReturn
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, expected, true);
// Make sure the latch block information is preserved and propagated correctly
// by the pass.
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
PartialUnrollerTestPass<3> unroller;
unroller.Process(context.get());
ir::Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const ir::Function* f = spvtest::GetFunction(module, 2);
ir::LoopDescriptor ld{f};
EXPECT_EQ(ld.NumLoops(), 2u);
ir::Loop& loop_1 = ld.GetLoopByIndex(0u);
EXPECT_NE(loop_1.GetLatchBlock(), loop_1.GetContinueBlock());
ir::Loop& loop_2 = ld.GetLoopByIndex(1u);
EXPECT_NE(loop_2.GetLatchBlock(), loop_2.GetContinueBlock());
}
} // namespace