spirv-tools: vendor as full-fork recipe (path=source, patches baked)

This commit is contained in:
2026-08-01 04:44:12 +03:00
parent 9695e81f9c
commit f0fa0fd58f
1744 changed files with 714530 additions and 3 deletions
@@ -0,0 +1,42 @@
# Copyright (c) 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_spvtools_unittest(TARGET opt_loops
SRCS ../function_utils.h
dependence_analysis.cpp
dependence_analysis_helpers.cpp
fusion_compatibility.cpp
fusion_illegal.cpp
fusion_legal.cpp
fusion_pass.cpp
hoist_access_chains.cpp
hoist_all_loop_types.cpp
hoist_double_nested_loops.cpp
hoist_from_independent_loops.cpp
hoist_simple_case.cpp
hoist_single_nested_loops.cpp
hoist_without_preheader.cpp
lcssa.cpp
loop_descriptions.cpp
loop_fission.cpp
nested_loops.cpp
peeling.cpp
peeling_pass.cpp
unroll_assumptions.cpp
unroll_simple.cpp
unswitch.cpp
LIBS SPIRV-Tools-opt
PCH_FILE pch_test_opt_loop
)
@@ -0,0 +1,717 @@
// 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 "effcee/effcee.h"
#include "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using FusionPassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
int[10] a;
int[10] b;
for (int i = 0; i < 10; i++) {
a[i] = a[i]*2;
}
for (int i = 0; i < 10; i++) {
b[i] = a[i]+2;
}
}
*/
TEST_F(FusionPassTest, SimpleFusion) {
const std::string text = R"(
; CHECK: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
; CHECK-NOT: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %23 "a"
OpName %34 "i"
OpName %42 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 10
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%28 = OpConstant %6 2
%32 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%23 = OpVariable %22 Function
%34 = OpVariable %7 Function
%42 = OpVariable %22 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%51 = OpPhi %6 %9 %5 %33 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %51 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%26 = OpAccessChain %7 %23 %51
%27 = OpLoad %6 %26
%29 = OpIMul %6 %27 %28
%30 = OpAccessChain %7 %23 %51
OpStore %30 %29
OpBranch %13
%13 = OpLabel
%33 = OpIAdd %6 %51 %32
OpStore %8 %33
OpBranch %10
%12 = OpLabel
OpStore %34 %9
OpBranch %35
%35 = OpLabel
%52 = OpPhi %6 %9 %12 %50 %38
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%41 = OpSLessThan %17 %52 %16
OpBranchConditional %41 %36 %37
%36 = OpLabel
%45 = OpAccessChain %7 %23 %52
%46 = OpLoad %6 %45
%47 = OpIAdd %6 %46 %28
%48 = OpAccessChain %7 %42 %52
OpStore %48 %47
OpBranch %38
%38 = OpLabel
%50 = OpIAdd %6 %52 %32
OpStore %34 %50
OpBranch %35
%37 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 20);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
int[10] a;
int[10] b;
int[10] c;
for (int i = 0; i < 10; i++) {
a[i] = b[i] + 1;
}
for (int i = 0; i < 10; i++) {
c[i] = a[i] + 2;
}
for (int i = 0; i < 10; i++) {
b[i] = c[i] + 10;
}
}
*/
TEST_F(FusionPassTest, ThreeLoopsFused) {
const std::string text = R"(
; CHECK: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
; CHECK-NOT: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
; CHECK-NOT: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %23 "a"
OpName %25 "b"
OpName %34 "i"
OpName %42 "c"
OpName %52 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 10
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%29 = OpConstant %6 1
%47 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%23 = OpVariable %22 Function
%25 = OpVariable %22 Function
%34 = OpVariable %7 Function
%42 = OpVariable %22 Function
%52 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%68 = OpPhi %6 %9 %5 %33 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %68 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpAccessChain %7 %25 %68
%28 = OpLoad %6 %27
%30 = OpIAdd %6 %28 %29
%31 = OpAccessChain %7 %23 %68
OpStore %31 %30
OpBranch %13
%13 = OpLabel
%33 = OpIAdd %6 %68 %29
OpStore %8 %33
OpBranch %10
%12 = OpLabel
OpStore %34 %9
OpBranch %35
%35 = OpLabel
%69 = OpPhi %6 %9 %12 %51 %38
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%41 = OpSLessThan %17 %69 %16
OpBranchConditional %41 %36 %37
%36 = OpLabel
%45 = OpAccessChain %7 %23 %69
%46 = OpLoad %6 %45
%48 = OpIAdd %6 %46 %47
%49 = OpAccessChain %7 %42 %69
OpStore %49 %48
OpBranch %38
%38 = OpLabel
%51 = OpIAdd %6 %69 %29
OpStore %34 %51
OpBranch %35
%37 = OpLabel
OpStore %52 %9
OpBranch %53
%53 = OpLabel
%70 = OpPhi %6 %9 %37 %67 %56
OpLoopMerge %55 %56 None
OpBranch %57
%57 = OpLabel
%59 = OpSLessThan %17 %70 %16
OpBranchConditional %59 %54 %55
%54 = OpLabel
%62 = OpAccessChain %7 %42 %70
%63 = OpLoad %6 %62
%64 = OpIAdd %6 %63 %16
%65 = OpAccessChain %7 %25 %70
OpStore %65 %64
OpBranch %56
%56 = OpLabel
%67 = OpIAdd %6 %70 %29
OpStore %52 %67
OpBranch %53
%55 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 20);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
int[10][10] a;
int[10][10] b;
int[10][10] c;
// Legal both
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
c[i][j] = a[i][j] + 2;
}
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
b[i][j] = c[i][j] + 10;
}
}
}
*/
TEST_F(FusionPassTest, NestedLoopsFused) {
const std::string text = R"(
; CHECK: OpPhi
; CHECK: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
; CHECK-NOT: OpPhi
; CHECK: OpLoad
; CHECK: OpStore
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %19 "j"
OpName %32 "c"
OpName %35 "a"
OpName %48 "i"
OpName %56 "j"
OpName %64 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%27 = OpTypeInt 32 0
%28 = OpConstant %27 10
%29 = OpTypeArray %6 %28
%30 = OpTypeArray %29 %28
%31 = OpTypePointer Function %30
%40 = OpConstant %6 2
%44 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %31 Function
%35 = OpVariable %31 Function
%48 = OpVariable %7 Function
%56 = OpVariable %7 Function
%64 = OpVariable %31 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%77 = OpPhi %6 %9 %5 %47 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %77 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
%81 = OpPhi %6 %9 %11 %45 %23
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%26 = OpSLessThan %17 %81 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
%38 = OpAccessChain %7 %35 %77 %81
%39 = OpLoad %6 %38
%41 = OpIAdd %6 %39 %40
%42 = OpAccessChain %7 %32 %77 %81
OpStore %42 %41
OpBranch %23
%23 = OpLabel
%45 = OpIAdd %6 %81 %44
OpStore %19 %45
OpBranch %20
%22 = OpLabel
OpBranch %13
%13 = OpLabel
%47 = OpIAdd %6 %77 %44
OpStore %8 %47
OpBranch %10
%12 = OpLabel
OpStore %48 %9
OpBranch %49
%49 = OpLabel
%78 = OpPhi %6 %9 %12 %76 %52
OpLoopMerge %51 %52 None
OpBranch %53
%53 = OpLabel
%55 = OpSLessThan %17 %78 %16
OpBranchConditional %55 %50 %51
%50 = OpLabel
OpStore %56 %9
OpBranch %57
%57 = OpLabel
%79 = OpPhi %6 %9 %50 %74 %60
OpLoopMerge %59 %60 None
OpBranch %61
%61 = OpLabel
%63 = OpSLessThan %17 %79 %16
OpBranchConditional %63 %58 %59
%58 = OpLabel
%69 = OpAccessChain %7 %32 %78 %79
%70 = OpLoad %6 %69
%71 = OpIAdd %6 %70 %16
%72 = OpAccessChain %7 %64 %78 %79
OpStore %72 %71
OpBranch %60
%60 = OpLabel
%74 = OpIAdd %6 %79 %44
OpStore %56 %74
OpBranch %57
%59 = OpLabel
OpBranch %52
%52 = OpLabel
%76 = OpIAdd %6 %78 %44
OpStore %48 %76
OpBranch %49
%51 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 20);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
// Can't fuse, different step
for (int i = 0; i < 10; i++) {}
for (int j = 0; j < 10; j=j+2) {}
}
*/
TEST_F(FusionPassTest, Incompatible) {
const std::string text = R"(
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %22 "j"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%20 = OpConstant %6 1
%31 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%22 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%33 = OpPhi %6 %9 %5 %21 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %33 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%21 = OpIAdd %6 %33 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpStore %22 %9
OpBranch %23
%23 = OpLabel
%34 = OpPhi %6 %9 %12 %32 %26
OpLoopMerge %25 %26 None
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %17 %34 %16
OpBranchConditional %29 %24 %25
%24 = OpLabel
OpBranch %26
%26 = OpLabel
%32 = OpIAdd %6 %34 %31
OpStore %22 %32
OpBranch %23
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 20);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
int[10] a;
int[10] b;
int[10] c;
// Illegal, loop-independent dependence will become a
// backward loop-carried antidependence
for (int i = 0; i < 10; i++) {
a[i] = b[i] + 1;
}
for (int i = 0; i < 10; i++) {
c[i] = a[i+1] + 2;
}
}
*/
TEST_F(FusionPassTest, Illegal) {
std::string text = R"(
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
; CHECK: OpLoad
; CHECK: OpStore
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
; CHECK: OpLoad
; CHECK: OpStore
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %23 "a"
OpName %25 "b"
OpName %34 "i"
OpName %42 "c"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 10
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%29 = OpConstant %6 1
%48 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%23 = OpVariable %22 Function
%25 = OpVariable %22 Function
%34 = OpVariable %7 Function
%42 = OpVariable %22 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%53 = OpPhi %6 %9 %5 %33 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %53 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpAccessChain %7 %25 %53
%28 = OpLoad %6 %27
%30 = OpIAdd %6 %28 %29
%31 = OpAccessChain %7 %23 %53
OpStore %31 %30
OpBranch %13
%13 = OpLabel
%33 = OpIAdd %6 %53 %29
OpStore %8 %33
OpBranch %10
%12 = OpLabel
OpStore %34 %9
OpBranch %35
%35 = OpLabel
%54 = OpPhi %6 %9 %12 %52 %38
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%41 = OpSLessThan %17 %54 %16
OpBranchConditional %41 %36 %37
%36 = OpLabel
%45 = OpIAdd %6 %54 %29
%46 = OpAccessChain %7 %23 %45
%47 = OpLoad %6 %46
%49 = OpIAdd %6 %47 %48
%50 = OpAccessChain %7 %42 %54
OpStore %50 %49
OpBranch %38
%38 = OpLabel
%52 = OpIAdd %6 %54 %29
OpStore %34 %52
OpBranch %35
%37 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 20);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
void main() {
int[10] a;
int[10] b;
for (int i = 0; i < 10; i++) {
a[i] = a[i]*2;
}
for (int i = 0; i < 10; i++) {
b[i] = a[i]+2;
}
}
*/
TEST_F(FusionPassTest, TooManyRegisters) {
const std::string text = R"(
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
; CHECK: OpLoad
; CHECK: OpStore
; CHECK: OpPhi
; CHECK-NEXT: OpLoopMerge
; CHECK: OpLoad
; CHECK: OpStore
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 440
OpName %4 "main"
OpName %8 "i"
OpName %23 "a"
OpName %34 "i"
OpName %42 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%19 = OpTypeInt 32 0
%20 = OpConstant %19 10
%21 = OpTypeArray %6 %20
%22 = OpTypePointer Function %21
%28 = OpConstant %6 2
%32 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%23 = OpVariable %22 Function
%34 = OpVariable %7 Function
%42 = OpVariable %22 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%51 = OpPhi %6 %9 %5 %33 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %51 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%26 = OpAccessChain %7 %23 %51
%27 = OpLoad %6 %26
%29 = OpIMul %6 %27 %28
%30 = OpAccessChain %7 %23 %51
OpStore %30 %29
OpBranch %13
%13 = OpLabel
%33 = OpIAdd %6 %51 %32
OpStore %8 %33
OpBranch %10
%12 = OpLabel
OpStore %34 %9
OpBranch %35
%35 = OpLabel
%52 = OpPhi %6 %9 %12 %50 %38
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%41 = OpSLessThan %17 %52 %16
OpBranchConditional %41 %36 %37
%36 = OpLabel
%45 = OpAccessChain %7 %23 %52
%46 = OpLoad %6 %45
%47 = OpIAdd %6 %46 %28
%48 = OpAccessChain %7 %42 %52
OpStore %48 %47
OpBranch %38
%38 = OpLabel
%50 = OpIAdd %6 %52 %32
OpStore %34 %50
OpBranch %35
%37 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopFusionPass>(text, true, 5);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,157 @@
// Copyright (c) 2023 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using PassClassTest = PassTest<::testing::Test>;
/*
Tests for the LICM pass to check it handles access chains correctly
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 460
void main() {
for (uint i = 0; i < 123u; ++i) {
vec2 do_not_hoist_store = vec2(0.0f);
float do_not_hoist_access_chain_load = do_not_hoist_store.x;
}
}
*/
TEST_F(PassClassTest, HoistAccessChains) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 460
OpName %main "main"
OpName %i "i"
OpName %do_not_hoist_store "do_not_hoist_store"
OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
%void = OpTypeVoid
%7 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%_ptr_Function_uint = OpTypePointer Function %uint
%uint_0 = OpConstant %uint 0
%uint_123 = OpConstant %uint 123
%bool = OpTypeBool
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%float_0 = OpConstant %float 0
%17 = OpConstantComposite %v2float %float_0 %float_0
%_ptr_Function_float = OpTypePointer Function %float
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%main = OpFunction %void None %7
%21 = OpLabel
%i = OpVariable %_ptr_Function_uint Function
%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
OpStore %i %uint_0
OpBranch %22
%22 = OpLabel
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%26 = OpLoad %uint %i
%27 = OpULessThan %bool %26 %uint_123
OpBranchConditional %27 %28 %23
%28 = OpLabel
OpStore %do_not_hoist_store %17
%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
%30 = OpLoad %float %29
OpStore %do_not_hoist_access_chain_load %30
OpBranch %24
%24 = OpLabel
%31 = OpLoad %uint %i
%32 = OpIAdd %uint %31 %int_1
OpStore %i %32
OpBranch %22
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 460
OpName %main "main"
OpName %i "i"
OpName %do_not_hoist_store "do_not_hoist_store"
OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
%void = OpTypeVoid
%7 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%_ptr_Function_uint = OpTypePointer Function %uint
%uint_0 = OpConstant %uint 0
%uint_123 = OpConstant %uint 123
%bool = OpTypeBool
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%float_0 = OpConstant %float 0
%17 = OpConstantComposite %v2float %float_0 %float_0
%_ptr_Function_float = OpTypePointer Function %float
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%main = OpFunction %void None %7
%21 = OpLabel
%i = OpVariable %_ptr_Function_uint Function
%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
OpStore %i %uint_0
%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
OpBranch %22
%22 = OpLabel
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%26 = OpLoad %uint %i
%27 = OpULessThan %bool %26 %uint_123
OpBranchConditional %27 %28 %23
%28 = OpLabel
OpStore %do_not_hoist_store %17
%30 = OpLoad %float %29
OpStore %do_not_hoist_access_chain_load %30
OpBranch %24
%24 = OpLabel
%31 = OpLoad %uint %i
%32 = OpIAdd %uint %31 %int_1
OpStore %i %32
OpBranch %22
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,285 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Tests that all loop types are handled appropriately by the LICM pass.
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int i_1 = 0;
for (i_1 = 0; i_1 < 10; i_1++) {
}
int i_2 = 0;
while (i_2 < 10) {
i_2++;
}
int i_3 = 0;
do {
i_3++;
} while (i_3 < 10);
int hoist = 0;
int i_4 = 0;
int i_5 = 0;
int i_6 = 0;
for (i_4 = 0; i_4 < 10; i_4++) {
while (i_5 < 10) {
do {
hoist = i_1 + i_2 + i_3;
i_6++;
} while (i_6 < 10);
i_5++;
}
}
}
*/
TEST_F(PassClassTest, AllLoopTypes) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = 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 %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
%13 = OpPhi %int %int_0 %11 %14 %15
OpLoopMerge %16 %15 None
OpBranch %17
%17 = OpLabel
%18 = OpSLessThan %bool %13 %int_10
OpBranchConditional %18 %19 %16
%19 = OpLabel
OpBranch %15
%15 = OpLabel
%14 = OpIAdd %int %13 %int_1
OpBranch %12
%16 = OpLabel
OpBranch %20
%20 = OpLabel
%21 = OpPhi %int %int_0 %16 %22 %23
OpLoopMerge %24 %23 None
OpBranch %25
%25 = OpLabel
%26 = OpSLessThan %bool %21 %int_10
OpBranchConditional %26 %27 %24
%27 = OpLabel
%22 = OpIAdd %int %21 %int_1
OpBranch %23
%23 = OpLabel
OpBranch %20
%24 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpPhi %int %int_0 %24 %30 %31
OpLoopMerge %32 %31 None
OpBranch %33
%33 = OpLabel
%30 = OpIAdd %int %29 %int_1
OpBranch %31
%31 = OpLabel
%34 = OpSLessThan %bool %30 %int_10
OpBranchConditional %34 %28 %32
%32 = OpLabel
OpBranch %35
%35 = OpLabel
%36 = OpPhi %int %int_0 %32 %37 %38
%39 = OpPhi %int %int_0 %32 %40 %38
%41 = OpPhi %int %int_0 %32 %42 %38
%43 = OpPhi %int %int_0 %32 %44 %38
OpLoopMerge %45 %38 None
OpBranch %46
%46 = OpLabel
%47 = OpSLessThan %bool %39 %int_10
OpBranchConditional %47 %48 %45
%48 = OpLabel
OpBranch %49
%49 = OpLabel
%37 = OpPhi %int %36 %48 %50 %51
%42 = OpPhi %int %41 %48 %52 %51
%44 = OpPhi %int %43 %48 %53 %51
OpLoopMerge %54 %51 None
OpBranch %55
%55 = OpLabel
%56 = OpSLessThan %bool %42 %int_10
OpBranchConditional %56 %57 %54
%57 = OpLabel
OpBranch %58
%58 = OpLabel
%59 = OpPhi %int %37 %57 %50 %60
%61 = OpPhi %int %44 %57 %53 %60
OpLoopMerge %62 %60 None
OpBranch %63
%63 = OpLabel
%64 = OpIAdd %int %13 %21
%50 = OpIAdd %int %64 %30
%53 = OpIAdd %int %61 %int_1
OpBranch %60
%60 = OpLabel
%65 = OpSLessThan %bool %53 %int_10
OpBranchConditional %65 %58 %62
%62 = OpLabel
%52 = OpIAdd %int %42 %int_1
OpBranch %51
%51 = OpLabel
OpBranch %49
%54 = OpLabel
OpBranch %38
%38 = OpLabel
%40 = OpIAdd %int %39 %int_1
OpBranch %35
%45 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = 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 %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
%13 = OpPhi %int %int_0 %11 %14 %15
OpLoopMerge %16 %15 None
OpBranch %17
%17 = OpLabel
%18 = OpSLessThan %bool %13 %int_10
OpBranchConditional %18 %19 %16
%19 = OpLabel
OpBranch %15
%15 = OpLabel
%14 = OpIAdd %int %13 %int_1
OpBranch %12
%16 = OpLabel
OpBranch %20
%20 = OpLabel
%21 = OpPhi %int %int_0 %16 %22 %23
OpLoopMerge %24 %23 None
OpBranch %25
%25 = OpLabel
%26 = OpSLessThan %bool %21 %int_10
OpBranchConditional %26 %27 %24
%27 = OpLabel
%22 = OpIAdd %int %21 %int_1
OpBranch %23
%23 = OpLabel
OpBranch %20
%24 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpPhi %int %int_0 %24 %30 %31
OpLoopMerge %32 %31 None
OpBranch %33
%33 = OpLabel
%30 = OpIAdd %int %29 %int_1
OpBranch %31
%31 = OpLabel
%34 = OpSLessThan %bool %30 %int_10
OpBranchConditional %34 %28 %32
%32 = OpLabel
%64 = OpIAdd %int %13 %21
%50 = OpIAdd %int %64 %30
OpBranch %35
%35 = OpLabel
%36 = OpPhi %int %int_0 %32 %37 %38
%39 = OpPhi %int %int_0 %32 %40 %38
%41 = OpPhi %int %int_0 %32 %42 %38
%43 = OpPhi %int %int_0 %32 %44 %38
OpLoopMerge %45 %38 None
OpBranch %46
%46 = OpLabel
%47 = OpSLessThan %bool %39 %int_10
OpBranchConditional %47 %48 %45
%48 = OpLabel
OpBranch %49
%49 = OpLabel
%37 = OpPhi %int %36 %48 %50 %51
%42 = OpPhi %int %41 %48 %52 %51
%44 = OpPhi %int %43 %48 %53 %51
OpLoopMerge %54 %51 None
OpBranch %55
%55 = OpLabel
%56 = OpSLessThan %bool %42 %int_10
OpBranchConditional %56 %57 %54
%57 = OpLabel
OpBranch %58
%58 = OpLabel
%59 = OpPhi %int %37 %57 %50 %60
%61 = OpPhi %int %44 %57 %53 %60
OpLoopMerge %62 %60 None
OpBranch %63
%63 = OpLabel
%53 = OpIAdd %int %61 %int_1
OpBranch %60
%60 = OpLabel
%65 = OpSLessThan %bool %53 %int_10
OpBranchConditional %65 %58 %62
%62 = OpLabel
%52 = OpIAdd %int %42 %int_1
OpBranch %51
%51 = OpLabel
OpBranch %49
%54 = OpLabel
OpBranch %38
%38 = OpLabel
%40 = OpIAdd %int %39 %int_1
OpBranch %35
%45 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,162 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Tests that the LICM pass will move invariants through multiple loops
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int a = 2;
int b = 1;
int hoist = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
// hoist 'hoist = a - b' out of both loops
hoist = a - b;
}
}
}
*/
TEST_F(PassClassTest, NestedDoubleHoist) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_2 = OpConstant %int 2
%int_1 = OpConstant %int 1
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%12 = OpUndef %int
%main = OpFunction %void None %4
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
%18 = OpPhi %int %int_0 %13 %19 %17
%20 = OpPhi %int %12 %13 %21 %17
OpLoopMerge %22 %17 None
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %bool %18 %int_10
OpBranchConditional %24 %25 %22
%25 = OpLabel
OpBranch %26
%26 = OpLabel
%16 = OpPhi %int %15 %25 %27 %28
%21 = OpPhi %int %int_0 %25 %29 %28
OpLoopMerge %30 %28 None
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %bool %21 %int_10
OpBranchConditional %32 %33 %30
%33 = OpLabel
%27 = OpISub %int %int_2 %int_1
OpBranch %28
%28 = OpLabel
%29 = OpIAdd %int %21 %int_1
OpBranch %26
%30 = OpLabel
OpBranch %17
%17 = OpLabel
%19 = OpIAdd %int %18 %int_1
OpBranch %14
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_2 = OpConstant %int 2
%int_1 = OpConstant %int 1
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%12 = OpUndef %int
%main = OpFunction %void None %4
%13 = OpLabel
%27 = OpISub %int %int_2 %int_1
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
%18 = OpPhi %int %int_0 %13 %19 %17
%20 = OpPhi %int %12 %13 %21 %17
OpLoopMerge %22 %17 None
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %bool %18 %int_10
OpBranchConditional %24 %25 %22
%25 = OpLabel
OpBranch %26
%26 = OpLabel
%16 = OpPhi %int %15 %25 %27 %28
%21 = OpPhi %int %int_0 %25 %29 %28
OpLoopMerge %30 %28 None
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %bool %21 %int_10
OpBranchConditional %32 %33 %30
%33 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpIAdd %int %21 %int_1
OpBranch %26
%30 = OpLabel
OpBranch %17
%17 = OpLabel
%19 = OpIAdd %int %18 %int_1
OpBranch %14
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,201 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Tests that the LICM pass will analyse multiple independent loops in a function
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int a = 1;
int b = 2;
int hoist = 0;
for (int i = 0; i < 10; i++) {
// invariant
hoist = a + b;
}
for (int i = 0; i < 10; i++) {
// invariant
hoist = a + b;
}
int c = 1;
int d = 2;
int hoist2 = 0;
for (int i = 0; i < 10; i++) {
// invariant
hoist2 = c + d;
}
}
*/
TEST_F(PassClassTest, HoistFromIndependentLoops) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%main = OpFunction %void None %4
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpPhi %int %int_0 %12 %15 %16
%17 = OpPhi %int %int_0 %12 %18 %16
OpLoopMerge %19 %16 None
OpBranch %20
%20 = OpLabel
%21 = OpSLessThan %bool %17 %int_10
OpBranchConditional %21 %22 %19
%22 = OpLabel
%15 = OpIAdd %int %int_1 %int_2
OpBranch %16
%16 = OpLabel
%18 = OpIAdd %int %17 %int_1
OpBranch %13
%19 = OpLabel
OpBranch %23
%23 = OpLabel
%24 = OpPhi %int %14 %19 %25 %26
%27 = OpPhi %int %int_0 %19 %28 %26
OpLoopMerge %29 %26 None
OpBranch %30
%30 = OpLabel
%31 = OpSLessThan %bool %27 %int_10
OpBranchConditional %31 %32 %29
%32 = OpLabel
%25 = OpIAdd %int %int_1 %int_2
OpBranch %26
%26 = OpLabel
%28 = OpIAdd %int %27 %int_1
OpBranch %23
%29 = OpLabel
OpBranch %33
%33 = OpLabel
%34 = OpPhi %int %int_0 %29 %35 %36
%37 = OpPhi %int %int_0 %29 %38 %36
OpLoopMerge %39 %36 None
OpBranch %40
%40 = OpLabel
%41 = OpSLessThan %bool %37 %int_10
OpBranchConditional %41 %42 %39
%42 = OpLabel
%35 = OpIAdd %int %int_1 %int_2
OpBranch %36
%36 = OpLabel
%38 = OpIAdd %int %37 %int_1
OpBranch %33
%39 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%main = OpFunction %void None %4
%12 = OpLabel
%15 = OpIAdd %int %int_1 %int_2
OpBranch %13
%13 = OpLabel
%14 = OpPhi %int %int_0 %12 %15 %16
%17 = OpPhi %int %int_0 %12 %18 %16
OpLoopMerge %19 %16 None
OpBranch %20
%20 = OpLabel
%21 = OpSLessThan %bool %17 %int_10
OpBranchConditional %21 %22 %19
%22 = OpLabel
OpBranch %16
%16 = OpLabel
%18 = OpIAdd %int %17 %int_1
OpBranch %13
%19 = OpLabel
%25 = OpIAdd %int %int_1 %int_2
OpBranch %23
%23 = OpLabel
%24 = OpPhi %int %14 %19 %25 %26
%27 = OpPhi %int %int_0 %19 %28 %26
OpLoopMerge %29 %26 None
OpBranch %30
%30 = OpLabel
%31 = OpSLessThan %bool %27 %int_10
OpBranchConditional %31 %32 %29
%32 = OpLabel
OpBranch %26
%26 = OpLabel
%28 = OpIAdd %int %27 %int_1
OpBranch %23
%29 = OpLabel
%35 = OpIAdd %int %int_1 %int_2
OpBranch %33
%33 = OpLabel
%34 = OpPhi %int %int_0 %29 %35 %36
%37 = OpPhi %int %int_0 %29 %38 %36
OpLoopMerge %39 %36 None
OpBranch %40
%40 = OpLabel
%41 = OpSLessThan %bool %37 %int_10
OpBranchConditional %41 %42 %39
%42 = OpLabel
OpBranch %36
%36 = OpLabel
%38 = OpIAdd %int %37 %int_1
OpBranch %33
%39 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,126 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
A simple test for the LICM pass
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int a = 1;
int b = 2;
int hoist = 0;
for (int i = 0; i < 10; i++) {
// invariant
hoist = a + b;
}
}
*/
TEST_F(PassClassTest, SimpleHoist) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%main = OpFunction %void None %4
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpPhi %int %int_0 %12 %15 %16
%17 = OpPhi %int %int_0 %12 %18 %16
OpLoopMerge %19 %16 None
OpBranch %20
%20 = OpLabel
%21 = OpSLessThan %bool %17 %int_10
OpBranchConditional %21 %22 %19
%22 = OpLabel
%15 = OpIAdd %int %int_1 %int_2
OpBranch %16
%16 = OpLabel
%18 = OpIAdd %int %17 %int_1
OpBranch %13
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%main = OpFunction %void None %4
%12 = OpLabel
%15 = OpIAdd %int %int_1 %int_2
OpBranch %13
%13 = OpLabel
%14 = OpPhi %int %int_0 %12 %15 %16
%17 = OpPhi %int %int_0 %12 %18 %16
OpLoopMerge %19 %16 None
OpBranch %20
%20 = OpLabel
%21 = OpSLessThan %bool %17 %int_10
OpBranchConditional %21 %22 %19
%22 = OpLabel
OpBranch %16
%16 = OpLabel
%18 = OpIAdd %int %17 %int_1
OpBranch %13
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,209 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Tests that the LICM pass will detect an move an invariant from a nested loop,
but not it's parent loop
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int a = 2;
int hoist = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
// hoist 'hoist = a - i' out of j loop, but not i loop
hoist = a - i;
}
}
}
*/
TEST_F(PassClassTest, NestedSingleHoist) {
const std::string before_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%12 = OpUndef %int
%main = OpFunction %void None %4
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
%18 = OpPhi %int %int_0 %13 %19 %17
%20 = OpPhi %int %12 %13 %21 %17
OpLoopMerge %22 %17 None
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %bool %18 %int_10
OpBranchConditional %24 %25 %22
%25 = OpLabel
OpBranch %26
%26 = OpLabel
%16 = OpPhi %int %15 %25 %27 %28
%21 = OpPhi %int %int_0 %25 %29 %28
OpLoopMerge %30 %28 None
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %bool %21 %int_10
OpBranchConditional %32 %33 %30
%33 = OpLabel
%27 = OpISub %int %int_2 %18
OpBranch %28
%28 = OpLabel
%29 = OpIAdd %int %21 %int_1
OpBranch %26
%30 = OpLabel
OpBranch %17
%17 = OpLabel
%19 = OpIAdd %int %18 %int_1
OpBranch %14
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after_hoist = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%12 = OpUndef %int
%main = OpFunction %void None %4
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
%18 = OpPhi %int %int_0 %13 %19 %17
%20 = OpPhi %int %12 %13 %21 %17
OpLoopMerge %22 %17 None
OpBranch %23
%23 = OpLabel
%24 = OpSLessThan %bool %18 %int_10
OpBranchConditional %24 %25 %22
%25 = OpLabel
%27 = OpISub %int %int_2 %18
OpBranch %26
%26 = OpLabel
%16 = OpPhi %int %15 %25 %27 %28
%21 = OpPhi %int %int_0 %25 %29 %28
OpLoopMerge %30 %28 None
OpBranch %31
%31 = OpLabel
%32 = OpSLessThan %bool %21 %int_10
OpBranchConditional %32 %33 %30
%33 = OpLabel
OpBranch %28
%28 = OpLabel
%29 = OpIAdd %int %21 %int_1
OpBranch %26
%30 = OpLabel
OpBranch %17
%17 = OpLabel
%19 = OpIAdd %int %18 %int_1
OpBranch %14
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
TEST_F(PassClassTest, PreHeaderIsAlsoHeader) {
// Move OpSLessThan out of the inner loop. The preheader for the inner loop
// is the header of the outer loop. The loop merge should not be separated
// from the branch in that block.
const std::string text = R"(
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpBranch [[header:%\w+]]
; CHECK: [[header]] = OpLabel
; CHECK-NEXT: OpSLessThan %bool %int_1 %int_1
; CHECK-NEXT: OpLoopMerge
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%bool = OpTypeBool
%2 = OpFunction %void None %4
%18 = OpLabel
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpSLessThan %bool %int_1 %int_1
OpLoopMerge %26 %27 None
OpBranchConditional %25 %27 %26
%27 = OpLabel
OpBranch %24
%26 = OpLabel
OpBranch %22
%23 = OpLabel
OpBranch %21
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LICMPass>(text, true);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,197 @@
// 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 "gmock/gmock.h"
#include "source/opt/licm_pass.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Tests that the LICM pass will generate a preheader when one is not present
Generated from the following GLSL fragment shader
--eliminate-local-multi-store has also been run on the spv binary
#version 440 core
void main(){
int a = 1;
int b = 2;
int hoist = 0;
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
}
for (int i = 0; i < 10; i++) {
hoist = a + b;
}
}
*/
TEST_F(PassClassTest, HoistWithoutPreheader) {
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_5 = OpConstant %int 5
%main = OpFunction %void None %4
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
; CHECK: OpLoopMerge [[preheader:%\w+]]
OpLoopMerge %25 %17 None
OpBranch %19
%19 = OpLabel
%20 = OpSLessThan %bool %15 %int_10
OpBranchConditional %20 %21 %25
%21 = OpLabel
%22 = OpIEqual %bool %15 %int_5
OpSelectionMerge %23 None
OpBranchConditional %22 %24 %23
%24 = OpLabel
OpBranch %25
%23 = OpLabel
OpBranch %17
%17 = OpLabel
%16 = OpIAdd %int %15 %int_1
OpBranch %14
; Check that we hoisted the code to the preheader
; CHECK: [[preheader]] = OpLabel
; CHECK-NEXT: OpPhi
; CHECK-NEXT: OpPhi
; CHECK-NEXT: OpIAdd
; CHECK-NEXT: OpBranch [[header:%\w+]]
; CHECK: [[header]] = OpLabel
; CHECK-NEXT: OpPhi
; CHECK-NEXT: OpPhi
; CHECK: OpLoopMerge
%25 = OpLabel
%26 = OpPhi %int %int_0 %24 %int_0 %19 %27 %28
%29 = OpPhi %int %int_0 %24 %int_0 %19 %30 %28
OpLoopMerge %31 %28 None
OpBranch %32
%32 = OpLabel
%33 = OpSLessThan %bool %29 %int_10
OpBranchConditional %33 %34 %31
%34 = OpLabel
%27 = OpIAdd %int %int_1 %int_2
OpBranch %28
%28 = OpLabel
%30 = OpIAdd %int %29 %int_1
OpBranch %25
%31 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LICMPass>(text, false);
}
TEST_F(PassClassTest, HoistWithoutPreheaderAtIdBound) {
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_5 = OpConstant %int 5
%main = OpFunction %void None %4
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%15 = OpPhi %int %int_0 %13 %16 %17
OpLoopMerge %25 %17 None
OpBranch %19
%19 = OpLabel
%20 = OpSLessThan %bool %15 %int_10
OpBranchConditional %20 %21 %25
%21 = OpLabel
%22 = OpIEqual %bool %15 %int_5
OpSelectionMerge %23 None
OpBranchConditional %22 %24 %23
%24 = OpLabel
OpBranch %25
%23 = OpLabel
OpBranch %17
%17 = OpLabel
%16 = OpIAdd %int %15 %int_1
OpBranch %14
%25 = OpLabel
%26 = OpPhi %int %int_0 %24 %int_0 %19 %27 %28
%29 = OpPhi %int %int_0 %24 %int_0 %19 %30 %28
OpLoopMerge %31 %28 None
OpBranch %32
%32 = OpLabel
%33 = OpSLessThan %bool %29 %int_10
OpBranchConditional %33 %34 %31
%34 = OpLabel
%27 = OpIAdd %int %int_1 %int_2
OpBranch %28
%28 = OpLabel
%30 = OpIAdd %int %29 %int_1
OpBranch %25
%31 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
uint32_t current_bound = context->module()->id_bound();
context->set_max_id_bound(current_bound);
auto pass = MakeUnique<LICMPass>();
auto result = pass->Run(context.get());
EXPECT_EQ(result, Pass::Status::Failure);
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
std::string optimized_asm;
SpirvTools tools_(SPV_ENV_UNIVERSAL_1_1);
tools_.Disassemble(binary, &optimized_asm);
std::cout << optimized_asm << std::endl;
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,605 @@
// 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 <memory>
#include <vector>
#include "effcee/effcee.h"
#include "gmock/gmock.h"
#include "source/opt/build_module.h"
#include "source/opt/loop_descriptor.h"
#include "source/opt/loop_utils.h"
#include "test/opt//assembly_builder.h"
#include "test/opt/function_utils.h"
namespace spvtools {
namespace opt {
namespace {
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& original, IRContext* context,
bool do_validation = true) {
std::vector<uint32_t> bin;
context->module()->ToBinary(&bin, true);
if (do_validation) {
EXPECT_TRUE(Validate(bin));
}
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, original);
EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
<< match_result.message() << "\nChecking result:\n"
<< assembly;
}
using LCSSATest = ::testing::Test;
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
for (; i < 10; i++) {
}
if (i != 0) {
i = 1;
}
}
*/
TEST_F(LCSSATest, SimpleLCSSA) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] %19 None
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %30 %20
; CHECK-NEXT: %27 = OpINotEqual {{%\w+}} [[phi]] %9
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 %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 = OpConstant %7 1
%13 = OpTypeFloat 32
%14 = OpTypeVector %13 4
%15 = OpTypePointer Output %14
%3 = OpVariable %15 Output
%2 = OpFunction %5 None %6
%16 = OpLabel
OpBranch %17
%17 = OpLabel
%30 = OpPhi %7 %9 %16 %25 %19
OpLoopMerge %18 %19 None
OpBranch %20
%20 = OpLabel
%22 = OpSLessThan %11 %30 %10
OpBranchConditional %22 %23 %18
%23 = OpLabel
OpBranch %19
%19 = OpLabel
%25 = OpIAdd %7 %30 %12
OpBranch %17
%18 = OpLabel
%27 = OpINotEqual %11 %30 %9
OpSelectionMerge %28 None
OpBranchConditional %27 %29 %28
%29 = OpLabel
OpBranch %28
%28 = OpLabel
%31 = OpPhi %7 %30 %18 %12 %29
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[17];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
for (; i < 10; i++) {
}
if (i != 0) {
i = 1;
}
}
*/
// Same test as above, but should reuse an existing phi.
TEST_F(LCSSATest, PhiReuseLCSSA) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] %19 None
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %30 %20
; CHECK-NEXT: %27 = OpINotEqual {{%\w+}} [[phi]] %9
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 %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 = OpConstant %7 1
%13 = OpTypeFloat 32
%14 = OpTypeVector %13 4
%15 = OpTypePointer Output %14
%3 = OpVariable %15 Output
%2 = OpFunction %5 None %6
%16 = OpLabel
OpBranch %17
%17 = OpLabel
%30 = OpPhi %7 %9 %16 %25 %19
OpLoopMerge %18 %19 None
OpBranch %20
%20 = OpLabel
%22 = OpSLessThan %11 %30 %10
OpBranchConditional %22 %23 %18
%23 = OpLabel
OpBranch %19
%19 = OpLabel
%25 = OpIAdd %7 %30 %12
OpBranch %17
%18 = OpLabel
%32 = OpPhi %7 %30 %20
%27 = OpINotEqual %11 %30 %9
OpSelectionMerge %28 None
OpBranchConditional %27 %29 %28
%29 = OpLabel
OpBranch %28
%28 = OpLabel
%31 = OpPhi %7 %30 %18 %12 %29
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[17];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
int j = 0;
for (; i < 10; i++) {}
for (; j < 10; j++) {}
if (j != 0) {
i = 1;
}
}
*/
TEST_F(LCSSATest, DualLoopLCSSA) {
const std::string text = R"(
; CHECK: %20 = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi %6 %17 %21
; CHECK: %33 = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} [[phi]] %28 %11 %34
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 %3 "c"
OpDecorate %3 Location 0
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 0
%9 = OpConstant %6 10
%10 = OpTypeBool
%11 = OpConstant %6 1
%12 = OpTypeFloat 32
%13 = OpTypeVector %12 4
%14 = OpTypePointer Output %13
%3 = OpVariable %14 Output
%2 = OpFunction %4 None %5
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%17 = OpPhi %6 %8 %15 %18 %19
OpLoopMerge %20 %19 None
OpBranch %21
%21 = OpLabel
%22 = OpSLessThan %10 %17 %9
OpBranchConditional %22 %23 %20
%23 = OpLabel
OpBranch %19
%19 = OpLabel
%18 = OpIAdd %6 %17 %11
OpBranch %16
%20 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpPhi %6 %8 %20 %26 %27
OpLoopMerge %28 %27 None
OpBranch %29
%29 = OpLabel
%30 = OpSLessThan %10 %25 %9
OpBranchConditional %30 %31 %28
%31 = OpLabel
OpBranch %27
%27 = OpLabel
%26 = OpIAdd %6 %25 %11
OpBranch %24
%28 = OpLabel
%32 = OpINotEqual %10 %25 %8
OpSelectionMerge %33 None
OpBranchConditional %32 %34 %33
%34 = OpLabel
OpBranch %33
%33 = OpLabel
%35 = OpPhi %6 %17 %28 %11 %34
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[16];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
if (i != 0) {
for (; i < 10; i++) {}
}
if (i != 0) {
i = 1;
}
}
*/
TEST_F(LCSSATest, PhiUserLCSSA) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] %22 None
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %20 %24
; CHECK: %17 = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} %8 %15 [[phi]] %23
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 %3 "c"
OpDecorate %3 Location 0
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstant %6 0
%9 = OpTypeBool
%10 = OpConstant %6 10
%11 = OpConstant %6 1
%12 = OpTypeFloat 32
%13 = OpTypeVector %12 4
%14 = OpTypePointer Output %13
%3 = OpVariable %14 Output
%2 = OpFunction %4 None %5
%15 = OpLabel
%16 = OpINotEqual %9 %8 %8
OpSelectionMerge %17 None
OpBranchConditional %16 %18 %17
%18 = OpLabel
OpBranch %19
%19 = OpLabel
%20 = OpPhi %6 %8 %18 %21 %22
OpLoopMerge %23 %22 None
OpBranch %24
%24 = OpLabel
%25 = OpSLessThan %9 %20 %10
OpBranchConditional %25 %26 %23
%26 = OpLabel
OpBranch %22
%22 = OpLabel
%21 = OpIAdd %6 %20 %11
OpBranch %19
%23 = OpLabel
OpBranch %17
%17 = OpLabel
%27 = OpPhi %6 %8 %15 %20 %23
%28 = OpINotEqual %9 %27 %8
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
OpBranch %29
%29 = OpLabel
%31 = OpPhi %6 %27 %17 %11 %30
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[19];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
void main() {
int i = 0;
if (i != 0) {
for (; i < 10; i++) {
if (i > 5) break;
}
}
if (i != 0) {
i = 1;
}
}
*/
TEST_F(LCSSATest, LCSSAWithBreak) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] %19 None
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %17 %21 %17 %26
; CHECK: %14 = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} %7 %12 [[phi]] [[merge]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpTypePointer Function %5
%7 = OpConstant %5 0
%8 = OpTypeBool
%9 = OpConstant %5 10
%10 = OpConstant %5 5
%11 = OpConstant %5 1
%2 = OpFunction %3 None %4
%12 = OpLabel
%13 = OpINotEqual %8 %7 %7
OpSelectionMerge %14 None
OpBranchConditional %13 %15 %14
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%17 = OpPhi %5 %7 %15 %18 %19
OpLoopMerge %20 %19 None
OpBranch %21
%21 = OpLabel
%22 = OpSLessThan %8 %17 %9
OpBranchConditional %22 %23 %20
%23 = OpLabel
%24 = OpSGreaterThan %8 %17 %10
OpSelectionMerge %25 None
OpBranchConditional %24 %26 %25
%26 = OpLabel
OpBranch %20
%25 = OpLabel
OpBranch %19
%19 = OpLabel
%18 = OpIAdd %5 %17 %11
OpBranch %16
%20 = OpLabel
OpBranch %14
%14 = OpLabel
%27 = OpPhi %5 %7 %12 %17 %20
%28 = OpINotEqual %8 %27 %7
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
OpBranch %29
%29 = OpLabel
%31 = OpPhi %5 %27 %14 %11 %30
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[19];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
void main() {
int i = 0;
for (; i < 10; i++) {}
for (int j = i; j < 10;) { j = i + j; }
}
*/
TEST_F(LCSSATest, LCSSAUseInNonEligiblePhi) {
const std::string text = R"(
; CHECK: %12 = OpLabel
; CHECK-NEXT: [[def_to_close:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: [[closing_phi:%\w+]] = OpPhi {{%\w+}} [[def_to_close]] %17
; CHECK: %16 = OpLabel
; CHECK-NEXT: [[use_in_phi:%\w+]] = OpPhi {{%\w+}} %21 %22 [[closing_phi]] [[merge]]
; CHECK: OpIAdd {{%\w+}} [[closing_phi]] [[use_in_phi]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpTypePointer Function %5
%7 = OpConstant %5 0
%8 = OpConstant %5 10
%9 = OpTypeBool
%10 = OpConstant %5 1
%2 = OpFunction %3 None %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
%13 = OpPhi %5 %7 %11 %14 %15
OpLoopMerge %16 %15 None
OpBranch %17
%17 = OpLabel
%18 = OpSLessThan %9 %13 %8
OpBranchConditional %18 %19 %16
%19 = OpLabel
OpBranch %15
%15 = OpLabel
%14 = OpIAdd %5 %13 %10
OpBranch %12
%16 = OpLabel
%20 = OpPhi %5 %13 %17 %21 %22
OpLoopMerge %23 %22 None
OpBranch %24
%24 = OpLabel
%25 = OpSLessThan %9 %20 %8
OpBranchConditional %25 %26 %23
%26 = OpLabel
%21 = OpIAdd %5 %13 %20
OpBranch %22
%22 = OpLabel
OpBranch %16
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
Loop* loop = ld[12];
EXPECT_FALSE(loop->IsLCSSA());
LoopUtils Util(context.get(), loop);
Util.MakeLoopClosedSSA();
EXPECT_TRUE(loop->IsLCSSA());
Match(text, context.get());
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,420 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "source/opt/loop_descriptor.h"
#include "source/opt/pass.h"
#include "test/opt/assembly_builder.h"
#include "test/opt/function_utils.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
for(; i < 10; ++i) {
}
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
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 "i"
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 = OpConstant %8 1
%14 = OpTypeFloat 32
%15 = OpTypeVector %14 4
%16 = OpTypePointer Output %15
%3 = OpVariable %16 Output
%2 = OpFunction %6 None %7
%17 = OpLabel
%5 = OpVariable %9 Function
OpStore %5 %10
OpBranch %18
%18 = OpLabel
OpLoopMerge %19 %20 None
OpBranch %21
%21 = OpLabel
%22 = OpLoad %8 %5
%23 = OpSLessThan %12 %22 %11
OpBranchConditional %23 %24 %19
%24 = OpLabel
OpBranch %20
%20 = OpLabel
%25 = OpLoad %8 %5
%26 = OpIAdd %8 %25 %13
OpStore %5 %26
OpBranch %18
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
EXPECT_EQ(ld.NumLoops(), 1u);
Loop& loop = ld.GetLoopByIndex(0);
EXPECT_EQ(loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 18));
EXPECT_EQ(loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 20));
EXPECT_EQ(loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 19));
EXPECT_FALSE(loop.HasNestedLoops());
EXPECT_FALSE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 1u);
}
/*
Generated from the following GLSL:
#version 330 core
layout(location = 0) out vec4 c;
void main() {
for(int i = 0; i < 10; ++i) {}
for(int i = 0; i < 10; ++i) {}
}
But it was "hacked" to make the first loop merge block the second loop header.
*/
TEST_F(PassClassTest, LoopWithNoPreHeader) {
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 %4 "i"
OpName %5 "i"
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 = OpConstant %8 1
%14 = OpTypeFloat 32
%15 = OpTypeVector %14 4
%16 = OpTypePointer Output %15
%3 = OpVariable %16 Output
%2 = OpFunction %6 None %7
%17 = OpLabel
%4 = OpVariable %9 Function
%5 = OpVariable %9 Function
OpStore %4 %10
OpStore %5 %10
OpBranch %18
%18 = OpLabel
OpLoopMerge %27 %20 None
OpBranch %21
%21 = OpLabel
%22 = OpLoad %8 %4
%23 = OpSLessThan %12 %22 %11
OpBranchConditional %23 %24 %27
%24 = OpLabel
OpBranch %20
%20 = OpLabel
%25 = OpLoad %8 %4
%26 = OpIAdd %8 %25 %13
OpStore %4 %26
OpBranch %18
%27 = OpLabel
OpLoopMerge %28 %29 None
OpBranch %30
%30 = OpLabel
%31 = OpLoad %8 %5
%32 = OpSLessThan %12 %31 %11
OpBranchConditional %32 %33 %28
%33 = OpLabel
OpBranch %29
%29 = OpLabel
%34 = OpLoad %8 %5
%35 = OpIAdd %8 %34 %13
OpStore %5 %35
OpBranch %27
%28 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
EXPECT_EQ(ld.NumLoops(), 2u);
Loop* loop = ld[27];
EXPECT_EQ(loop->GetPreHeaderBlock(), nullptr);
EXPECT_NE(loop->GetOrCreatePreHeaderBlock(), nullptr);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
return;
}
else {
return;
}
}
bool cond2 = i == 9;
}
*/
TEST_F(PassClassTest, NoLoop) {
const std::string text = R"(; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 3
; Bound: 47
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %16
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 330
OpName %4 "main"
OpName %16 "c"
OpDecorate %16 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%10 = OpTypeBool
%11 = OpTypePointer Function %10
%13 = OpTypeFloat 32
%14 = OpTypeVector %13 4
%15 = OpTypePointer Input %14
%16 = OpVariable %15 Input
%17 = OpTypeInt 32 0
%18 = OpConstant %17 0
%19 = OpTypePointer Input %13
%22 = OpConstant %13 0
%30 = OpConstant %6 10
%39 = OpConstant %6 1
%46 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%20 = OpAccessChain %19 %16 %18
%21 = OpLoad %13 %20
%23 = OpFOrdEqual %10 %21 %22
OpBranch %24
%24 = OpLabel
%45 = OpPhi %6 %9 %5 %40 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %10 %45 %30
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %36
%33 = OpLabel
OpReturn
%36 = OpLabel
OpReturn
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%40 = OpIAdd %6 %46 %39
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 4);
LoopDescriptor ld{context.get(), f};
EXPECT_EQ(ld.NumLoops(), 0u);
}
/*
Generated from following GLSL with latch block artificially inserted to be
separate from continue.
#version 430
void main(void) {
float x[10];
for (int i = 0; i < 10; ++i) {
x[i] = i;
}
}
*/
TEST_F(PassClassTest, LoopLatchNotContinue) {
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 None
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
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor ld{context.get(), f};
EXPECT_EQ(ld.NumLoops(), 1u);
Loop& loop = ld.GetLoopByIndex(0u);
EXPECT_NE(loop.GetLatchBlock(), loop.GetContinueBlock());
EXPECT_EQ(loop.GetContinueBlock()->id(), 23u);
EXPECT_EQ(loop.GetLatchBlock()->id(), 30u);
}
TEST_F(PassClassTest, UnreachableMerge) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%4 = OpLabel
OpBranch %5
%5 = OpLabel
OpLoopMerge %6 %7 None
OpBranch %8
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranch %7
%7 = OpLabel
OpBranch %5
%6 = OpLabel
OpUnreachable
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 1);
LoopDescriptor ld{context.get(), f};
EXPECT_EQ(ld.NumLoops(), 1u);
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,795 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "gmock/gmock.h"
#include "source/opt/iterator.h"
#include "source/opt/loop_descriptor.h"
#include "source/opt/pass.h"
#include "source/opt/tree_iterator.h"
#include "test/opt/assembly_builder.h"
#include "test/opt/function_utils.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::UnorderedElementsAre;
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;
}
using PassClassTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
for (; i < 10; ++i) {
int j = 0;
int k = 0;
for (; j < 11; ++j) {}
for (; k < 12; ++k) {}
}
}
*/
TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
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 %4 "i"
OpName %5 "j"
OpName %6 "k"
OpName %3 "c"
OpDecorate %3 Location 0
%7 = OpTypeVoid
%8 = OpTypeFunction %7
%9 = OpTypeInt 32 1
%10 = OpTypePointer Function %9
%11 = OpConstant %9 0
%12 = OpConstant %9 10
%13 = OpTypeBool
%14 = OpConstant %9 11
%15 = OpConstant %9 1
%16 = OpConstant %9 12
%17 = OpTypeFloat 32
%18 = OpTypeVector %17 4
%19 = OpTypePointer Output %18
%3 = OpVariable %19 Output
%2 = OpFunction %7 None %8
%20 = OpLabel
%4 = OpVariable %10 Function
%5 = OpVariable %10 Function
%6 = OpVariable %10 Function
OpStore %4 %11
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpLoad %9 %4
%26 = OpSLessThan %13 %25 %12
OpBranchConditional %26 %27 %22
%27 = OpLabel
OpStore %5 %11
OpStore %6 %11
OpBranch %28
%28 = OpLabel
OpLoopMerge %29 %30 None
OpBranch %31
%31 = OpLabel
%32 = OpLoad %9 %5
%33 = OpSLessThan %13 %32 %14
OpBranchConditional %33 %34 %29
%34 = OpLabel
OpBranch %30
%30 = OpLabel
%35 = OpLoad %9 %5
%36 = OpIAdd %9 %35 %15
OpStore %5 %36
OpBranch %28
%29 = OpLabel
OpBranch %37
%37 = OpLabel
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
%41 = OpLoad %9 %6
%42 = OpSLessThan %13 %41 %16
OpBranchConditional %42 %43 %38
%43 = OpLabel
OpBranch %39
%39 = OpLabel
%44 = OpLoad %9 %6
%45 = OpIAdd %9 %44 %15
OpStore %6 %45
OpBranch %37
%38 = OpLabel
OpBranch %23
%23 = OpLabel
%46 = OpLoad %9 %4
%47 = OpIAdd %9 %46 %15
OpStore %4 %47
OpBranch %21
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
EXPECT_EQ(ld.NumLoops(), 3u);
// Invalid basic block id.
EXPECT_EQ(ld[0u], nullptr);
// Not a loop header.
EXPECT_EQ(ld[20], nullptr);
Loop& parent_loop = *ld[21];
EXPECT_TRUE(parent_loop.HasNestedLoops());
EXPECT_FALSE(parent_loop.IsNested());
EXPECT_EQ(parent_loop.GetDepth(), 1u);
EXPECT_EQ(std::distance(parent_loop.begin(), parent_loop.end()), 2u);
EXPECT_EQ(parent_loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 21));
EXPECT_EQ(parent_loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 23));
EXPECT_EQ(parent_loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 22));
Loop& child_loop_1 = *ld[28];
EXPECT_FALSE(child_loop_1.HasNestedLoops());
EXPECT_TRUE(child_loop_1.IsNested());
EXPECT_EQ(child_loop_1.GetDepth(), 2u);
EXPECT_EQ(std::distance(child_loop_1.begin(), child_loop_1.end()), 0u);
EXPECT_EQ(child_loop_1.GetHeaderBlock(), spvtest::GetBasicBlock(f, 28));
EXPECT_EQ(child_loop_1.GetLatchBlock(), spvtest::GetBasicBlock(f, 30));
EXPECT_EQ(child_loop_1.GetMergeBlock(), spvtest::GetBasicBlock(f, 29));
Loop& child_loop_2 = *ld[37];
EXPECT_FALSE(child_loop_2.HasNestedLoops());
EXPECT_TRUE(child_loop_2.IsNested());
EXPECT_EQ(child_loop_2.GetDepth(), 2u);
EXPECT_EQ(std::distance(child_loop_2.begin(), child_loop_2.end()), 0u);
EXPECT_EQ(child_loop_2.GetHeaderBlock(), spvtest::GetBasicBlock(f, 37));
EXPECT_EQ(child_loop_2.GetLatchBlock(), spvtest::GetBasicBlock(f, 39));
EXPECT_EQ(child_loop_2.GetMergeBlock(), spvtest::GetBasicBlock(f, 38));
}
static void CheckLoopBlocks(Loop* loop,
std::unordered_set<uint32_t>* expected_ids) {
SCOPED_TRACE("Check loop " + std::to_string(loop->GetHeaderBlock()->id()));
for (uint32_t bb_id : loop->GetBlocks()) {
EXPECT_EQ(expected_ids->count(bb_id), 1u);
expected_ids->erase(bb_id);
}
EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
EXPECT_EQ(expected_ids->size(), 0u);
}
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
int i = 0;
for (; i < 10; ++i) {
for (int j = 0; j < 11; ++j) {
if (j < 5) {
for (int k = 0; k < 12; ++k) {}
}
else {}
for (int k = 0; k < 12; ++k) {}
}
}
}*/
TEST_F(PassClassTest, TripleNestedLoop) {
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 %4 "i"
OpName %5 "j"
OpName %6 "k"
OpName %7 "k"
OpName %3 "c"
OpDecorate %3 Location 0
%8 = OpTypeVoid
%9 = OpTypeFunction %8
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%12 = OpConstant %10 0
%13 = OpConstant %10 10
%14 = OpTypeBool
%15 = OpConstant %10 11
%16 = OpConstant %10 5
%17 = OpConstant %10 12
%18 = OpConstant %10 1
%19 = OpTypeFloat 32
%20 = OpTypeVector %19 4
%21 = OpTypePointer Output %20
%3 = OpVariable %21 Output
%2 = OpFunction %8 None %9
%22 = OpLabel
%4 = OpVariable %11 Function
%5 = OpVariable %11 Function
%6 = OpVariable %11 Function
%7 = OpVariable %11 Function
OpStore %4 %12
OpBranch %23
%23 = OpLabel
OpLoopMerge %24 %25 None
OpBranch %26
%26 = OpLabel
%27 = OpLoad %10 %4
%28 = OpSLessThan %14 %27 %13
OpBranchConditional %28 %29 %24
%29 = OpLabel
OpStore %5 %12
OpBranch %30
%30 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
%34 = OpLoad %10 %5
%35 = OpSLessThan %14 %34 %15
OpBranchConditional %35 %36 %31
%36 = OpLabel
%37 = OpLoad %10 %5
%38 = OpSLessThan %14 %37 %16
OpSelectionMerge %39 None
OpBranchConditional %38 %40 %39
%40 = OpLabel
OpStore %6 %12
OpBranch %41
%41 = OpLabel
OpLoopMerge %42 %43 None
OpBranch %44
%44 = OpLabel
%45 = OpLoad %10 %6
%46 = OpSLessThan %14 %45 %17
OpBranchConditional %46 %47 %42
%47 = OpLabel
OpBranch %43
%43 = OpLabel
%48 = OpLoad %10 %6
%49 = OpIAdd %10 %48 %18
OpStore %6 %49
OpBranch %41
%42 = OpLabel
OpBranch %39
%39 = OpLabel
OpStore %7 %12
OpBranch %50
%50 = OpLabel
OpLoopMerge %51 %52 None
OpBranch %53
%53 = OpLabel
%54 = OpLoad %10 %7
%55 = OpSLessThan %14 %54 %17
OpBranchConditional %55 %56 %51
%56 = OpLabel
OpBranch %52
%52 = OpLabel
%57 = OpLoad %10 %7
%58 = OpIAdd %10 %57 %18
OpStore %7 %58
OpBranch %50
%51 = OpLabel
OpBranch %32
%32 = OpLabel
%59 = OpLoad %10 %5
%60 = OpIAdd %10 %59 %18
OpStore %5 %60
OpBranch %30
%31 = OpLabel
OpBranch %25
%25 = OpLabel
%61 = OpLoad %10 %4
%62 = OpIAdd %10 %61 %18
OpStore %4 %62
OpBranch %23
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
EXPECT_EQ(ld.NumLoops(), 4u);
// Invalid basic block id.
EXPECT_EQ(ld[0u], nullptr);
// Not in a loop.
EXPECT_EQ(ld[22], nullptr);
// Check that we can map basic block to the correct loop.
// The following block ids do not belong to a loop.
for (uint32_t bb_id : {22, 24}) EXPECT_EQ(ld[bb_id], nullptr);
{
std::unordered_set<uint32_t> basic_block_in_loop = {
{23, 26, 29, 30, 33, 36, 40, 41, 44, 47, 43,
42, 39, 50, 53, 56, 52, 51, 32, 31, 25}};
Loop* loop = ld[23];
CheckLoopBlocks(loop, &basic_block_in_loop);
EXPECT_TRUE(loop->HasNestedLoops());
EXPECT_FALSE(loop->IsNested());
EXPECT_EQ(loop->GetDepth(), 1u);
EXPECT_EQ(std::distance(loop->begin(), loop->end()), 1u);
EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 22));
EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 23));
EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 25));
EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 24));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
}
{
std::unordered_set<uint32_t> basic_block_in_loop = {
{30, 33, 36, 40, 41, 44, 47, 43, 42, 39, 50, 53, 56, 52, 51, 32}};
Loop* loop = ld[30];
CheckLoopBlocks(loop, &basic_block_in_loop);
EXPECT_TRUE(loop->HasNestedLoops());
EXPECT_TRUE(loop->IsNested());
EXPECT_EQ(loop->GetDepth(), 2u);
EXPECT_EQ(std::distance(loop->begin(), loop->end()), 2u);
EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 29));
EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 30));
EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 32));
EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 31));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
}
{
std::unordered_set<uint32_t> basic_block_in_loop = {{41, 44, 47, 43}};
Loop* loop = ld[41];
CheckLoopBlocks(loop, &basic_block_in_loop);
EXPECT_FALSE(loop->HasNestedLoops());
EXPECT_TRUE(loop->IsNested());
EXPECT_EQ(loop->GetDepth(), 3u);
EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 40));
EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 41));
EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 43));
EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 42));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
}
{
std::unordered_set<uint32_t> basic_block_in_loop = {{50, 53, 56, 52}};
Loop* loop = ld[50];
CheckLoopBlocks(loop, &basic_block_in_loop);
EXPECT_FALSE(loop->HasNestedLoops());
EXPECT_TRUE(loop->IsNested());
EXPECT_EQ(loop->GetDepth(), 3u);
EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 39));
EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 50));
EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 52));
EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 51));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
}
// Make sure LoopDescriptor gives us the inner most loop when we query for
// loops.
for (const BasicBlock& bb : *f) {
if (Loop* loop = ld[&bb]) {
for (Loop& sub_loop :
make_range(++TreeDFIterator<Loop>(loop), TreeDFIterator<Loop>())) {
EXPECT_FALSE(sub_loop.IsInsideLoop(bb.id()));
}
}
}
}
/*
Generated from the following GLSL
#version 330 core
layout(location = 0) out vec4 c;
void main() {
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 11; ++j) {
for (int k = 0; k < 11; ++k) {}
}
for (int k = 0; k < 12; ++k) {}
}
}
*/
TEST_F(PassClassTest, LoopParentTest) {
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 %4 "i"
OpName %5 "j"
OpName %6 "k"
OpName %7 "k"
OpName %3 "c"
OpDecorate %3 Location 0
%8 = OpTypeVoid
%9 = OpTypeFunction %8
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%12 = OpConstant %10 0
%13 = OpConstant %10 10
%14 = OpTypeBool
%15 = OpConstant %10 11
%16 = OpConstant %10 1
%17 = OpConstant %10 12
%18 = OpTypeFloat 32
%19 = OpTypeVector %18 4
%20 = OpTypePointer Output %19
%3 = OpVariable %20 Output
%2 = OpFunction %8 None %9
%21 = OpLabel
%4 = OpVariable %11 Function
%5 = OpVariable %11 Function
%6 = OpVariable %11 Function
%7 = OpVariable %11 Function
OpStore %4 %12
OpBranch %22
%22 = OpLabel
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%26 = OpLoad %10 %4
%27 = OpSLessThan %14 %26 %13
OpBranchConditional %27 %28 %23
%28 = OpLabel
OpStore %5 %12
OpBranch %29
%29 = OpLabel
OpLoopMerge %30 %31 None
OpBranch %32
%32 = OpLabel
%33 = OpLoad %10 %5
%34 = OpSLessThan %14 %33 %15
OpBranchConditional %34 %35 %30
%35 = OpLabel
OpStore %6 %12
OpBranch %36
%36 = OpLabel
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%40 = OpLoad %10 %6
%41 = OpSLessThan %14 %40 %15
OpBranchConditional %41 %42 %37
%42 = OpLabel
OpBranch %38
%38 = OpLabel
%43 = OpLoad %10 %6
%44 = OpIAdd %10 %43 %16
OpStore %6 %44
OpBranch %36
%37 = OpLabel
OpBranch %31
%31 = OpLabel
%45 = OpLoad %10 %5
%46 = OpIAdd %10 %45 %16
OpStore %5 %46
OpBranch %29
%30 = OpLabel
OpStore %7 %12
OpBranch %47
%47 = OpLabel
OpLoopMerge %48 %49 None
OpBranch %50
%50 = OpLabel
%51 = OpLoad %10 %7
%52 = OpSLessThan %14 %51 %17
OpBranchConditional %52 %53 %48
%53 = OpLabel
OpBranch %49
%49 = OpLabel
%54 = OpLoad %10 %7
%55 = OpIAdd %10 %54 %16
OpStore %7 %55
OpBranch %47
%48 = OpLabel
OpBranch %24
%24 = OpLabel
%56 = OpLoad %10 %4
%57 = OpIAdd %10 %56 %16
OpStore %4 %57
OpBranch %22
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
EXPECT_EQ(ld.NumLoops(), 4u);
{
Loop& loop = *ld[22];
EXPECT_TRUE(loop.HasNestedLoops());
EXPECT_FALSE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 1u);
EXPECT_EQ(loop.GetParent(), nullptr);
}
{
Loop& loop = *ld[29];
EXPECT_TRUE(loop.HasNestedLoops());
EXPECT_TRUE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 2u);
EXPECT_EQ(loop.GetParent(), ld[22]);
}
{
Loop& loop = *ld[36];
EXPECT_FALSE(loop.HasNestedLoops());
EXPECT_TRUE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 3u);
EXPECT_EQ(loop.GetParent(), ld[29]);
}
{
Loop& loop = *ld[47];
EXPECT_FALSE(loop.HasNestedLoops());
EXPECT_TRUE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 2u);
EXPECT_EQ(loop.GetParent(), ld[22]);
}
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
The preheader of loop %33 and %41 were removed as well.
#version 330 core
void main() {
int a = 0;
for (int i = 0; i < 10; ++i) {
if (i == 0) {
a = 1;
} else {
a = 2;
}
for (int j = 0; j < 11; ++j) {
a++;
}
}
for (int k = 0; k < 12; ++k) {}
}
*/
TEST_F(PassClassTest, CreatePreheaderTest) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpTypePointer Function %5
%7 = OpConstant %5 0
%8 = OpConstant %5 10
%9 = OpTypeBool
%10 = OpConstant %5 1
%11 = OpConstant %5 2
%12 = OpConstant %5 11
%13 = OpConstant %5 12
%14 = OpUndef %5
%2 = OpFunction %3 None %4
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%17 = OpPhi %5 %7 %15 %18 %19
%20 = OpPhi %5 %7 %15 %21 %19
%22 = OpPhi %5 %14 %15 %23 %19
OpLoopMerge %41 %19 None
OpBranch %25
%25 = OpLabel
%26 = OpSLessThan %9 %20 %8
OpBranchConditional %26 %27 %41
%27 = OpLabel
%28 = OpIEqual %9 %20 %7
OpSelectionMerge %33 None
OpBranchConditional %28 %30 %31
%30 = OpLabel
OpBranch %33
%31 = OpLabel
OpBranch %33
%33 = OpLabel
%18 = OpPhi %5 %10 %30 %11 %31 %34 %35
%23 = OpPhi %5 %7 %30 %7 %31 %36 %35
OpLoopMerge %37 %35 None
OpBranch %38
%38 = OpLabel
%39 = OpSLessThan %9 %23 %12
OpBranchConditional %39 %40 %37
%40 = OpLabel
%34 = OpIAdd %5 %18 %10
OpBranch %35
%35 = OpLabel
%36 = OpIAdd %5 %23 %10
OpBranch %33
%37 = OpLabel
OpBranch %19
%19 = OpLabel
%21 = OpIAdd %5 %20 %10
OpBranch %16
%41 = OpLabel
%42 = OpPhi %5 %7 %25 %43 %44
OpLoopMerge %45 %44 None
OpBranch %46
%46 = OpLabel
%47 = OpSLessThan %9 %42 %13
OpBranchConditional %47 %48 %45
%48 = OpLabel
OpBranch %44
%44 = OpLabel
%43 = OpIAdd %5 %42 %10
OpBranch %41
%45 = OpLabel
OpReturn
OpFunctionEnd
)";
// clang-format on
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
<< text << std::endl;
const Function* f = spvtest::GetFunction(module, 2);
LoopDescriptor& ld = *context->GetLoopDescriptor(f);
// No invalidation of the cfg should occur during this test.
CFG* cfg = context->cfg();
EXPECT_EQ(ld.NumLoops(), 3u);
{
Loop& loop = *ld[16];
EXPECT_TRUE(loop.HasNestedLoops());
EXPECT_FALSE(loop.IsNested());
EXPECT_EQ(loop.GetDepth(), 1u);
EXPECT_EQ(loop.GetParent(), nullptr);
}
{
Loop& loop = *ld[33];
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
// Make sure the loop descriptor was properly updated.
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]);
{
const std::vector<uint32_t>& preds =
cfg->preds(loop.GetPreHeaderBlock()->id());
std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
EXPECT_EQ(pred_set.size(), 2u);
EXPECT_TRUE(pred_set.count(30));
EXPECT_TRUE(pred_set.count(31));
// Check the phi instructions.
loop.GetPreHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
}
});
}
{
const std::vector<uint32_t>& preds =
cfg->preds(loop.GetHeaderBlock()->id());
std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
EXPECT_EQ(pred_set.size(), 2u);
EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id()));
EXPECT_TRUE(pred_set.count(35));
// Check the phi instructions.
loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
}
});
}
}
{
Loop& loop = *ld[41];
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], nullptr);
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u);
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id())[0], 25u);
// Check the phi instructions.
loop.GetPreHeaderBlock()->ForEachPhiInst([](Instruction* phi) {
EXPECT_EQ(phi->NumInOperands(), 2u);
EXPECT_EQ(phi->GetSingleWordInOperand(1), 25u);
});
{
const std::vector<uint32_t>& preds =
cfg->preds(loop.GetHeaderBlock()->id());
std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
EXPECT_EQ(pred_set.size(), 2u);
EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id()));
EXPECT_TRUE(pred_set.count(44));
// Check the phi instructions.
loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
}
});
}
}
// Make sure pre-header insertion leaves the module valid.
std::vector<uint32_t> bin;
context->module()->ToBinary(&bin, true);
EXPECT_TRUE(Validate(bin));
}
} // namespace
} // namespace opt
} // namespace spvtools
@@ -0,0 +1,15 @@
// Copyright (c) 2018 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pch_test_opt_loop.h"
@@ -0,0 +1,25 @@
// Copyright (c) 2018 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "source/opt/iterator.h"
#include "source/opt/loop_dependence.h"
#include "source/opt/loop_descriptor.h"
#include "source/opt/pass.h"
#include "source/opt/scalar_analysis.h"
#include "source/opt/tree_iterator.h"
#include "test/opt/assembly_builder.h"
#include "test/opt/function_utils.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,967 @@
// 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 "effcee/effcee.h"
#include "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using UnswitchTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 450 core
uniform vec4 c;
void main() {
int i = 0;
int j = 0;
bool cond = c[0] == 0;
for (; i < 10; i++, j++) {
if (cond) {
i++;
}
else {
j++;
}
}
}
*/
TEST_F(UnswitchTest, SimpleUnswitch) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_j:%\w+]] [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; [[loop_body]] = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
; CHECK: OpBranchConditional %false [[bb1:%\w+]] [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_j:%\w+]] [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; [[loop_body]] = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
; CHECK: OpBranchConditional %true [[bb1:%\w+]] [[bb2:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginLowerLeft
OpSource GLSL 450
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %c DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_UniformConstant_v4float = OpTypePointer UniformConstant %v4float
%c = OpVariable %_ptr_UniformConstant_v4float UniformConstant
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%21 = OpAccessChain %_ptr_UniformConstant_float %c %uint_0
%22 = OpLoad %float %21
%24 = OpFOrdEqual %bool %22 %float_0
OpBranch %25
%25 = OpLabel
%46 = OpPhi %int %int_0 %5 %43 %28
%47 = OpPhi %int %int_0 %5 %45 %28
OpLoopMerge %27 %28 None
OpBranch %29
%29 = OpLabel
%32 = OpSLessThan %bool %46 %int_10
OpBranchConditional %32 %26 %27
%26 = OpLabel
OpSelectionMerge %35 None
OpBranchConditional %24 %34 %39
%34 = OpLabel
%38 = OpIAdd %int %46 %int_1
OpBranch %35
%39 = OpLabel
%41 = OpIAdd %int %47 %int_1
OpBranch %35
%35 = OpLabel
%48 = OpPhi %int %38 %34 %46 %39
%49 = OpPhi %int %47 %34 %41 %39
OpBranch %28
%28 = OpLabel
%43 = OpIAdd %int %48 %int_1
%45 = OpIAdd %int %49 %int_1
OpBranch %25
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
i++;
}
else {
return;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchExit) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK: OpReturn
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
; Check that we have i+=2.
; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %38
%33 = OpLabel
%37 = OpIAdd %int %42 %int_1
OpBranch %34
%38 = OpLabel
OpReturn
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%41 = OpIAdd %int %37 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
continue;
}
else {
i++;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchContinue) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body:%\w+]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %false
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body:%\w+]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %true
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %36
%33 = OpLabel
OpBranch %27
%36 = OpLabel
%39 = OpIAdd %int %42 %int_1
OpBranch %34
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%43 = OpPhi %int %42 %33 %39 %34
%41 = OpIAdd %int %43 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
i++;
}
else {
break;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchKillLoop) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
; Check that we have i+=2.
; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %38
%33 = OpLabel
%37 = OpIAdd %int %42 %int_1
OpBranch %34
%38 = OpLabel
OpBranch %26
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%41 = OpIAdd %int %37 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
int cond = int(c[0]);
for (; i < 10; i++) {
switch (cond) {
case 0:
return;
case 1:
discard;
case 2:
break;
default:
break;
}
}
bool cond2 = i == 9;
}
*/
TEST_F(UnswitchTest, UnswitchSwitch) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpConvertFToS
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpSwitch [[cst_cond]] [[default:%\w+]] 0 [[loop_0:%\w+]] 1 [[loop_1:%\w+]] 2 [[loop_2:%\w+]]
; Loop specialized for 2.
; CHECK: [[loop_2]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_2]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_2
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for 1.
; CHECK: [[loop_1]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_1]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for 0.
; CHECK: [[loop_0]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_0]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_0
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for the default case.
; CHECK: [[default]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[default]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %uint_3
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %20 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%_ptr_Function_bool = OpTypePointer Function %bool
%main = OpFunction %void None %3
%5 = OpLabel
%18 = OpAccessChain %_ptr_Input_float %c %uint_0
%19 = OpLoad %float %18
%20 = OpConvertFToS %int %19
OpBranch %21
%21 = OpLabel
%49 = OpPhi %int %int_0 %5 %43 %24
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%29 = OpSLessThan %bool %49 %int_10
OpBranchConditional %29 %22 %23
%22 = OpLabel
OpSelectionMerge %35 None
OpSwitch %20 %34 0 %31 1 %32 2 %33
%34 = OpLabel
OpBranch %35
%31 = OpLabel
OpReturn
%32 = OpLabel
OpKill
%33 = OpLabel
OpBranch %35
%35 = OpLabel
OpBranch %24
%24 = OpLabel
%43 = OpIAdd %int %49 %int_1
OpBranch %21
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
layout(location = 0)in vec4 c;
void main() {
int i = 0;
int j = 0;
int k = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
for (; j < 10; j++) {
if (cond) {
i++;
} else {
j++;
}
}
}
}
*/
TEST_F(UnswitchTest, UnSwitchNested) {
// Test that an branch can be unswitched out of two nested loops.
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK-NOT: [[merge]] = OpLabel
; CHECK: OpLoopMerge
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %false
; CHECK: [[merge]] = OpLabel
; Loop specialized for true. Same as first loop except the branch condition is true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK-NOT: [[merge]] = OpLabel
; CHECK: OpLoopMerge
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %true
; CHECK: [[merge]] = OpLabel
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %25 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%22 = OpAccessChain %_ptr_Input_float %c %uint_0
%23 = OpLoad %float %22
%25 = OpFOrdEqual %bool %23 %float_0
OpBranch %26
%26 = OpLabel
%67 = OpPhi %int %int_0 %5 %52 %29
%68 = OpPhi %int %int_0 %5 %70 %29
OpLoopMerge %28 %29 None
OpBranch %30
%30 = OpLabel
%33 = OpSLessThan %bool %67 %int_10
OpBranchConditional %33 %27 %28
%27 = OpLabel
OpBranch %34
%34 = OpLabel
%69 = OpPhi %int %67 %27 %46 %37
%70 = OpPhi %int %68 %27 %50 %37
OpLoopMerge %36 %37 None
OpBranch %38
%38 = OpLabel
%40 = OpSLessThan %bool %70 %int_10
OpBranchConditional %40 %35 %36
%35 = OpLabel
OpSelectionMerge %43 None
OpBranchConditional %25 %42 %47
%42 = OpLabel
%46 = OpIAdd %int %69 %int_1
OpBranch %43
%47 = OpLabel
OpReturn
%43 = OpLabel
OpBranch %37
%37 = OpLabel
%50 = OpIAdd %int %70 %int_1
OpBranch %34
%36 = OpLabel
OpBranch %29
%29 = OpLabel
%52 = OpIAdd %int %69 %int_1
OpBranch %26
%28 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
bool cond = false;
if (c[0] == 0) {
cond = c[1] == 0;
} else {
cond = c[2] == 0;
}
for (int i = 0; i < 10; i++) {
if (cond) {
i++;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchNotUniform) {
// Check that the unswitch is not triggered (condition loop invariant but not
// uniform)
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%17 = OpAccessChain %_ptr_Input_float %c %uint_0
%18 = OpLoad %float %17
%20 = OpFOrdEqual %bool %18 %float_0
OpSelectionMerge %22 None
OpBranchConditional %20 %21 %27
%21 = OpLabel
%24 = OpAccessChain %_ptr_Input_float %c %uint_1
%25 = OpLoad %float %24
%26 = OpFOrdEqual %bool %25 %float_0
OpBranch %22
%27 = OpLabel
%29 = OpAccessChain %_ptr_Input_float %c %uint_2
%30 = OpLoad %float %29
%31 = OpFOrdEqual %bool %30 %float_0
OpBranch %22
%22 = OpLabel
%52 = OpPhi %bool %26 %21 %31 %27
OpBranch %36
%36 = OpLabel
%53 = OpPhi %int %int_0 %22 %51 %39
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
%43 = OpSLessThan %bool %53 %int_10
OpBranchConditional %43 %37 %38
%37 = OpLabel
OpSelectionMerge %46 None
OpBranchConditional %52 %45 %46
%45 = OpLabel
%49 = OpIAdd %int %53 %int_1
OpBranch %46
%46 = OpLabel
%54 = OpPhi %int %53 %37 %49 %45
OpBranch %39
%39 = OpLabel
%51 = OpIAdd %int %54 %int_1
OpBranch %36
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(UnswitchTest, DontUnswitchLatch) {
// Check that the unswitch is not triggered for the latch branch.
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%void = OpTypeVoid
%3 = OpTypeFunction %void
%bool = OpTypeBool
%false = OpConstantFalse %bool
%4 = OpFunction %void None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %7
%7 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranchConditional %false %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(UnswitchTest, DontUnswitchConstantCondition) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginLowerLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%true = OpConstantTrue %bool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %4
%10 = OpLabel
OpBranch %11
%11 = OpLabel
%12 = OpPhi %int %int_0 %10 %13 %14
OpLoopMerge %15 %14 None
OpBranch %16
%16 = OpLabel
%17 = OpSLessThan %bool %12 %int_1
OpBranchConditional %17 %18 %15
%18 = OpLabel
OpSelectionMerge %19 None
OpBranchConditional %true %20 %19
%20 = OpLabel
%21 = OpIAdd %int %12 %int_1
OpBranch %19
%19 = OpLabel
%22 = OpPhi %int %21 %20 %12 %18
OpBranch %14
%14 = OpLabel
%13 = OpIAdd %int %22 %int_1
OpBranch %11
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
} // namespace
} // namespace opt
} // namespace spvtools