| // 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 <utility> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "source/opt/ir_builder.h" |
| #include "source/opt/loop_descriptor.h" |
| #include "source/opt/loop_peeling.h" |
| #include "test/opt/pass_fixture.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| class PeelingPassTest : public PassTest<::testing::Test> { |
| public: |
| // Generic routine to run the loop peeling pass and check |
| LoopPeelingPass::LoopPeelingStats AssembleAndRunPeelingTest( |
| const std::string& text_head, const std::string& text_tail, |
| spv::Op opcode, const std::string& res_id, const std::string& op1, |
| const std::string& op2) { |
| std::string opcode_str; |
| switch (opcode) { |
| case spv::Op::OpSLessThan: |
| opcode_str = "OpSLessThan"; |
| break; |
| case spv::Op::OpSGreaterThan: |
| opcode_str = "OpSGreaterThan"; |
| break; |
| case spv::Op::OpSLessThanEqual: |
| opcode_str = "OpSLessThanEqual"; |
| break; |
| case spv::Op::OpSGreaterThanEqual: |
| opcode_str = "OpSGreaterThanEqual"; |
| break; |
| case spv::Op::OpIEqual: |
| opcode_str = "OpIEqual"; |
| break; |
| case spv::Op::OpINotEqual: |
| opcode_str = "OpINotEqual"; |
| break; |
| default: |
| assert(false && "Unhandled"); |
| break; |
| } |
| std::string test_cond = |
| res_id + " = " + opcode_str + " %bool " + op1 + " " + op2 + "\n"; |
| |
| LoopPeelingPass::LoopPeelingStats stats; |
| SinglePassRunAndDisassemble<LoopPeelingPass>( |
| text_head + test_cond + text_tail, true, true, &stats); |
| |
| return stats; |
| } |
| |
| // Generic routine to run the loop peeling pass and check |
| LoopPeelingPass::LoopPeelingStats RunPeelingTest( |
| const std::string& text_head, const std::string& text_tail, |
| spv::Op opcode, const std::string& res_id, const std::string& op1, |
| const std::string& op2, size_t nb_of_loops) { |
| LoopPeelingPass::LoopPeelingStats stats = AssembleAndRunPeelingTest( |
| text_head, text_tail, opcode, res_id, op1, op2); |
| |
| Function& f = *context()->module()->begin(); |
| LoopDescriptor& ld = *context()->GetLoopDescriptor(&f); |
| EXPECT_EQ(ld.NumLoops(), nb_of_loops); |
| |
| return stats; |
| } |
| |
| using PeelTraceType = |
| std::vector<std::pair<LoopPeelingPass::PeelDirection, uint32_t>>; |
| |
| void BuildAndCheckTrace(const std::string& text_head, |
| const std::string& text_tail, spv::Op opcode, |
| const std::string& res_id, const std::string& op1, |
| const std::string& op2, |
| const PeelTraceType& expected_peel_trace, |
| size_t expected_nb_of_loops) { |
| auto stats = RunPeelingTest(text_head, text_tail, opcode, res_id, op1, op2, |
| expected_nb_of_loops); |
| |
| EXPECT_EQ(stats.peeled_loops_.size(), expected_peel_trace.size()); |
| if (stats.peeled_loops_.size() != expected_peel_trace.size()) { |
| return; |
| } |
| |
| PeelTraceType::const_iterator expected_trace_it = |
| expected_peel_trace.begin(); |
| decltype(stats.peeled_loops_)::const_iterator stats_it = |
| stats.peeled_loops_.begin(); |
| |
| while (expected_trace_it != expected_peel_trace.end()) { |
| EXPECT_EQ(expected_trace_it->first, std::get<1>(*stats_it)); |
| EXPECT_EQ(expected_trace_it->second, std::get<2>(*stats_it)); |
| ++expected_trace_it; |
| ++stats_it; |
| } |
| } |
| }; |
| |
| /* |
| Test are derivation of the following generated test from the following GLSL + |
| --eliminate-local-multi-store |
| |
| #version 330 core |
| void main() { |
| int a = 0; |
| for(int i = 1; i < 10; i += 2) { |
| if (i < 3) { |
| a += 2; |
| } |
| } |
| } |
| |
| The condition is interchanged to test < > <= >= == and peel before/after |
| opportunities. |
| */ |
| TEST_F(PeelingPassTest, PeelingPassBasic) { |
| const std::string text_head = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginLowerLeft |
| OpSource GLSL 330 |
| OpName %main "main" |
| OpName %a "a" |
| OpName %i "i" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %bool = OpTypeBool |
| %int_20 = OpConstant %int 20 |
| %int_19 = OpConstant %int 19 |
| %int_18 = OpConstant %int 18 |
| %int_17 = OpConstant %int 17 |
| %int_16 = OpConstant %int 16 |
| %int_15 = OpConstant %int 15 |
| %int_14 = OpConstant %int 14 |
| %int_13 = OpConstant %int 13 |
| %int_12 = OpConstant %int 12 |
| %int_11 = OpConstant %int 11 |
| %int_10 = OpConstant %int 10 |
| %int_9 = OpConstant %int 9 |
| %int_8 = OpConstant %int 8 |
| %int_7 = OpConstant %int 7 |
| %int_6 = OpConstant %int 6 |
| %int_5 = OpConstant %int 5 |
| %int_4 = OpConstant %int 4 |
| %int_3 = OpConstant %int 3 |
| %int_2 = OpConstant %int 2 |
| %int_1 = OpConstant %int 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %a = OpVariable %_ptr_Function_int Function |
| %i = OpVariable %_ptr_Function_int Function |
| OpStore %a %int_0 |
| OpStore %i %int_0 |
| OpBranch %11 |
| %11 = OpLabel |
| %31 = OpPhi %int %int_0 %5 %33 %14 |
| %32 = OpPhi %int %int_1 %5 %30 %14 |
| OpLoopMerge %13 %14 None |
| OpBranch %15 |
| %15 = OpLabel |
| %19 = OpSLessThan %bool %32 %int_20 |
| OpBranchConditional %19 %12 %13 |
| %12 = OpLabel |
| )"; |
| const std::string text_tail = R"( |
| OpSelectionMerge %24 None |
| OpBranchConditional %22 %23 %24 |
| %23 = OpLabel |
| %27 = OpIAdd %int %31 %int_2 |
| OpStore %a %27 |
| OpBranch %24 |
| %24 = OpLabel |
| %33 = OpPhi %int %31 %12 %27 %23 |
| OpBranch %14 |
| %14 = OpLabel |
| %30 = OpIAdd %int %32 %int_2 |
| OpStore %i %30 |
| OpBranch %11 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto run_test = [&text_head, &text_tail, this](spv::Op opcode, |
| const std::string& op1, |
| const std::string& op2) { |
| auto stats = |
| RunPeelingTest(text_head, text_tail, opcode, "%22", op1, op2, 2); |
| |
| EXPECT_EQ(stats.peeled_loops_.size(), 1u); |
| if (stats.peeled_loops_.size() != 1u) |
| return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{ |
| LoopPeelingPass::PeelDirection::kNone, 0}; |
| |
| return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{ |
| std::get<1>(*stats.peeled_loops_.begin()), |
| std::get<2>(*stats.peeled_loops_.begin())}; |
| }; |
| |
| // Test LT |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv < 4"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%32", "%int_4"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 4 > iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%int_4", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before iv < 5"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%32", "%int_5"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 5 > iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%int_5", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv < 16"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%32", "%int_16"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 16 > iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%int_16", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after iv < 17"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%32", "%int_17"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 17 > iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%int_17", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Test GT |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv > 5"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%32", "%int_5"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 5 < iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%int_5", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before iv > 4"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%32", "%int_4"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 4 < iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%int_4", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv > 16"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%32", "%int_16"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 16 < iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%int_16", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after iv > 17"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThan, "%32", "%int_17"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 17 < iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThan, "%int_17", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Test LE |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv <= 4"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%32", "%int_4"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 4 => iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_4", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before iv <= 3"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%32", "%int_3"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 3 => iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_3", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv <= 16"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%32", "%int_16"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 16 => iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_16", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after iv <= 15"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%32", "%int_15"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 15 => iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_15", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Test GE |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv >= 5"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_5"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 35 >= iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%int_5", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before iv >= 4"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_4"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel before 4 <= iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%int_4", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv >= 17"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_17"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 17 <= iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%int_17", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after iv >= 16"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_16"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| { |
| SCOPED_TRACE("Peel after 16 <= iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpSLessThanEqual, "%int_16", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 2u); |
| } |
| |
| // Test EQ |
| // Peel before by a factor of 1. |
| { |
| SCOPED_TRACE("Peel before iv == 1"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpIEqual, "%32", "%int_1"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| { |
| SCOPED_TRACE("Peel before 1 == iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpIEqual, "%int_1", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| |
| // Peel after by a factor of 1. |
| { |
| SCOPED_TRACE("Peel after iv == 19"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpIEqual, "%32", "%int_19"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| { |
| SCOPED_TRACE("Peel after 19 == iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpIEqual, "%int_19", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| |
| // Test NE |
| // Peel before by a factor of 1. |
| { |
| SCOPED_TRACE("Peel before iv != 1"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpINotEqual, "%32", "%int_1"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| { |
| SCOPED_TRACE("Peel before 1 != iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpINotEqual, "%int_1", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| |
| // Peel after by a factor of 1. |
| { |
| SCOPED_TRACE("Peel after iv != 19"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpINotEqual, "%32", "%int_19"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| { |
| SCOPED_TRACE("Peel after 19 != iv"); |
| |
| std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info = |
| run_test(spv::Op::OpINotEqual, "%int_19", "%32"); |
| EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); |
| EXPECT_EQ(peel_info.second, 1u); |
| } |
| |
| // No peel. |
| { |
| SCOPED_TRACE("No Peel: 20 => iv"); |
| |
| auto stats = RunPeelingTest(text_head, text_tail, spv::Op::OpSLessThanEqual, |
| "%22", "%int_20", "%32", 1); |
| |
| EXPECT_EQ(stats.peeled_loops_.size(), 0u); |
| } |
| } |
| |
| /* |
| Test are derivation of the following generated test from the following GLSL + |
| --eliminate-local-multi-store |
| |
| #version 330 core |
| void main() { |
| int a = 0; |
| for(int i = 0; i < 10; ++i) { |
| if (i < 3) { |
| a += 2; |
| } |
| if (i < 1) { |
| a += 2; |
| } |
| } |
| } |
| |
| The condition is interchanged to test < > <= >= == and peel before/after |
| opportunities. |
| */ |
| TEST_F(PeelingPassTest, MultiplePeelingPass) { |
| const std::string text_head = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginLowerLeft |
| OpSource GLSL 330 |
| OpName %main "main" |
| OpName %a "a" |
| OpName %i "i" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %bool = OpTypeBool |
| %int_10 = OpConstant %int 10 |
| %int_9 = OpConstant %int 9 |
| %int_8 = OpConstant %int 8 |
| %int_7 = OpConstant %int 7 |
| %int_6 = OpConstant %int 6 |
| %int_5 = OpConstant %int 5 |
| %int_4 = OpConstant %int 4 |
| %int_3 = OpConstant %int 3 |
| %int_2 = OpConstant %int 2 |
| %int_1 = OpConstant %int 1 |
| %int_0 = OpConstant %int 0 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %a = OpVariable %_ptr_Function_int Function |
| %i = OpVariable %_ptr_Function_int Function |
| OpStore %a %int_0 |
| OpStore %i %int_0 |
| OpBranch %11 |
| %11 = OpLabel |
| %37 = OpPhi %int %int_0 %5 %40 %14 |
| %38 = OpPhi %int %int_0 %5 %36 %14 |
| OpLoopMerge %13 %14 None |
| OpBranch %15 |
| %15 = OpLabel |
| %19 = OpSLessThan %bool %38 %int_10 |
| OpBranchConditional %19 %12 %13 |
| %12 = OpLabel |
| )"; |
| const std::string text_tail = R"( |
| OpSelectionMerge %24 None |
| OpBranchConditional %22 %23 %24 |
| %23 = OpLabel |
| %27 = OpIAdd %int %37 %int_2 |
| OpStore %a %27 |
| OpBranch %24 |
| %24 = OpLabel |
| %39 = OpPhi %int %37 %12 %27 %23 |
| %30 = OpSLessThan %bool %38 %int_1 |
| OpSelectionMerge %32 None |
| OpBranchConditional %30 %31 %32 |
| %31 = OpLabel |
| %34 = OpIAdd %int %39 %int_2 |
| OpStore %a %34 |
| OpBranch %32 |
| %32 = OpLabel |
| %40 = OpPhi %int %39 %24 %34 %31 |
| OpBranch %14 |
| %14 = OpLabel |
| %36 = OpIAdd %int %38 %int_1 |
| OpStore %i %36 |
| OpBranch %11 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto run_test = [&text_head, &text_tail, this]( |
| spv::Op opcode, const std::string& op1, |
| const std::string& op2, |
| const PeelTraceType& expected_peel_trace) { |
| BuildAndCheckTrace(text_head, text_tail, opcode, "%22", op1, op2, |
| expected_peel_trace, expected_peel_trace.size() + 1); |
| }; |
| |
| // Test LT |
| // Peel before by a factor of 3. |
| { |
| SCOPED_TRACE("Peel before iv < 3"); |
| |
| run_test(spv::Op::OpSLessThan, "%38", "%int_3", |
| {{LoopPeelingPass::PeelDirection::kBefore, 3u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 3 > iv"); |
| |
| run_test(spv::Op::OpSGreaterThan, "%int_3", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 3u}}); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv < 8"); |
| |
| run_test(spv::Op::OpSLessThan, "%38", "%int_8", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 8 > iv"); |
| |
| run_test(spv::Op::OpSGreaterThan, "%int_8", "%38", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| |
| // Test GT |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv > 2"); |
| |
| run_test(spv::Op::OpSGreaterThan, "%38", "%int_2", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 2 < iv"); |
| |
| run_test(spv::Op::OpSLessThan, "%int_2", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| |
| // Peel after by a factor of 3. |
| { |
| SCOPED_TRACE("Peel after iv > 7"); |
| |
| run_test(spv::Op::OpSGreaterThan, "%38", "%int_7", |
| {{LoopPeelingPass::PeelDirection::kAfter, 3u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 7 < iv"); |
| |
| run_test(spv::Op::OpSLessThan, "%int_7", "%38", |
| {{LoopPeelingPass::PeelDirection::kAfter, 3u}}); |
| } |
| |
| // Test LE |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv <= 1"); |
| |
| run_test(spv::Op::OpSLessThanEqual, "%38", "%int_1", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 1 => iv"); |
| |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_1", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv <= 7"); |
| |
| run_test(spv::Op::OpSLessThanEqual, "%38", "%int_7", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 7 => iv"); |
| |
| run_test(spv::Op::OpSGreaterThanEqual, "%int_7", "%38", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| |
| // Test GE |
| // Peel before by a factor of 2. |
| { |
| SCOPED_TRACE("Peel before iv >= 2"); |
| |
| run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_2", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 2 <= iv"); |
| |
| run_test(spv::Op::OpSLessThanEqual, "%int_2", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); |
| } |
| |
| // Peel after by a factor of 2. |
| { |
| SCOPED_TRACE("Peel after iv >= 8"); |
| |
| run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_8", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 8 <= iv"); |
| |
| run_test(spv::Op::OpSLessThanEqual, "%int_8", "%38", |
| {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); |
| } |
| // Test EQ |
| // Peel before by a factor of 1. |
| { |
| SCOPED_TRACE("Peel before iv == 0"); |
| |
| run_test(spv::Op::OpIEqual, "%38", "%int_0", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 0 == iv"); |
| |
| run_test(spv::Op::OpIEqual, "%int_0", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| |
| // Peel after by a factor of 1. |
| { |
| SCOPED_TRACE("Peel after iv == 9"); |
| |
| run_test(spv::Op::OpIEqual, "%38", "%int_9", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 9 == iv"); |
| |
| run_test(spv::Op::OpIEqual, "%int_9", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| |
| // Test NE |
| // Peel before by a factor of 1. |
| { |
| SCOPED_TRACE("Peel before iv != 0"); |
| |
| run_test(spv::Op::OpINotEqual, "%38", "%int_0", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| { |
| SCOPED_TRACE("Peel before 0 != iv"); |
| |
| run_test(spv::Op::OpINotEqual, "%int_0", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| |
| // Peel after by a factor of 1. |
| { |
| SCOPED_TRACE("Peel after iv != 9"); |
| |
| run_test(spv::Op::OpINotEqual, "%38", "%int_9", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| { |
| SCOPED_TRACE("Peel after 9 != iv"); |
| |
| run_test(spv::Op::OpINotEqual, "%int_9", "%38", |
| {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); |
| } |
| } |
| |
| /* |
| Test are derivation of the following generated test from the following GLSL + |
| --eliminate-local-multi-store |
| |
| #version 330 core |
| void main() { |
| int a = 0; |
| for (int i = 0; i < 10; i++) { |
| for (int j = 0; j < 10; j++) { |
| if (i < 3) { |
| a += 2; |
| } |
| } |
| } |
| } |
| */ |
| TEST_F(PeelingPassTest, PeelingNestedPass) { |
| const std::string text_head = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginLowerLeft |
| OpSource GLSL 330 |
| OpName %main "main" |
| OpName %a "a" |
| OpName %i "i" |
| OpName %j "j" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_0 = OpConstant %int 0 |
| %int_10 = OpConstant %int 10 |
| %bool = OpTypeBool |
| %int_7 = OpConstant %int 7 |
| %int_3 = OpConstant %int 3 |
| %int_2 = OpConstant %int 2 |
| %int_1 = OpConstant %int 1 |
| %43 = OpUndef %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %a = OpVariable %_ptr_Function_int Function |
| %i = OpVariable %_ptr_Function_int Function |
| %j = OpVariable %_ptr_Function_int Function |
| OpStore %a %int_0 |
| OpStore %i %int_0 |
| OpBranch %11 |
| %11 = OpLabel |
| %41 = OpPhi %int %int_0 %5 %45 %14 |
| %42 = OpPhi %int %int_0 %5 %40 %14 |
| %44 = OpPhi %int %43 %5 %46 %14 |
| OpLoopMerge %13 %14 None |
| OpBranch %15 |
| %15 = OpLabel |
| %19 = OpSLessThan %bool %42 %int_10 |
| OpBranchConditional %19 %12 %13 |
| %12 = OpLabel |
| OpStore %j %int_0 |
| OpBranch %21 |
| %21 = OpLabel |
| %45 = OpPhi %int %41 %12 %47 %24 |
| %46 = OpPhi %int %int_0 %12 %38 %24 |
| OpLoopMerge %23 %24 None |
| OpBranch %25 |
| %25 = OpLabel |
| %27 = OpSLessThan %bool %46 %int_10 |
| OpBranchConditional %27 %22 %23 |
| %22 = OpLabel |
| )"; |
| |
| const std::string text_tail = R"( |
| OpSelectionMerge %32 None |
| OpBranchConditional %30 %31 %32 |
| %31 = OpLabel |
| %35 = OpIAdd %int %45 %int_2 |
| OpStore %a %35 |
| OpBranch %32 |
| %32 = OpLabel |
| %47 = OpPhi %int %45 %22 %35 %31 |
| OpBranch %24 |
| %24 = OpLabel |
| %38 = OpIAdd %int %46 %int_1 |
| OpStore %j %38 |
| OpBranch %21 |
| %23 = OpLabel |
| OpBranch %14 |
| %14 = OpLabel |
| %40 = OpIAdd %int %42 %int_1 |
| OpStore %i %40 |
| OpBranch %11 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto run_test = |
| [&text_head, &text_tail, this]( |
| spv::Op opcode, const std::string& op1, const std::string& op2, |
| const PeelTraceType& expected_peel_trace, size_t nb_of_loops) { |
| BuildAndCheckTrace(text_head, text_tail, opcode, "%30", op1, op2, |
| expected_peel_trace, nb_of_loops); |
| }; |
| |
| // Peeling outer before by a factor of 3. |
| { |
| SCOPED_TRACE("Peel before iv_i < 3"); |
| |
| // Expect peel before by a factor of 3 and 4 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%42", "%int_3", |
| {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 4); |
| } |
| // Peeling outer loop after by a factor of 3. |
| { |
| SCOPED_TRACE("Peel after iv_i < 7"); |
| |
| // Expect peel after by a factor of 3 and 4 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%42", "%int_7", |
| {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 4); |
| } |
| |
| // Peeling inner loop before by a factor of 3. |
| { |
| SCOPED_TRACE("Peel before iv_j < 3"); |
| |
| // Expect peel before by a factor of 3 and 3 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%46", "%int_3", |
| {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 3); |
| } |
| // Peeling inner loop after by a factor of 3. |
| { |
| SCOPED_TRACE("Peel after iv_j < 7"); |
| |
| // Expect peel after by a factor of 3 and 3 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%46", "%int_7", |
| {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 3); |
| } |
| |
| // Not unworkable condition. |
| { |
| SCOPED_TRACE("No peel"); |
| |
| // Expect no peeling and 2 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%46", "%42", {}, 2); |
| } |
| |
| // Could do a peeling of 3, but the goes over the threshold. |
| { |
| SCOPED_TRACE("Over threshold"); |
| |
| size_t current_threshold = LoopPeelingPass::GetLoopPeelingThreshold(); |
| LoopPeelingPass::SetLoopPeelingThreshold(1u); |
| // Expect no peeling and 2 loops at the end. |
| run_test(spv::Op::OpSLessThan, "%46", "%int_7", {}, 2); |
| LoopPeelingPass::SetLoopPeelingThreshold(current_threshold); |
| } |
| } |
| /* |
| Test are derivation of the following generated test from the following GLSL + |
| --eliminate-local-multi-store |
| |
| #version 330 core |
| void main() { |
| int a = 0; |
| for (int i = 0, j = 0; i < 10; j++, i++) { |
| if (i < j) { |
| a += 2; |
| } |
| } |
| } |
| */ |
| TEST_F(PeelingPassTest, PeelingNoChanges) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginLowerLeft |
| OpSource GLSL 330 |
| OpName %main "main" |
| OpName %a "a" |
| OpName %i "i" |
| OpName %j "j" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_0 = OpConstant %int 0 |
| %int_10 = OpConstant %int 10 |
| %bool = OpTypeBool |
| %int_2 = OpConstant %int 2 |
| %int_1 = OpConstant %int 1 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %a = OpVariable %_ptr_Function_int Function |
| %i = OpVariable %_ptr_Function_int Function |
| %j = OpVariable %_ptr_Function_int Function |
| OpStore %a %int_0 |
| OpStore %i %int_0 |
| OpStore %j %int_0 |
| OpBranch %12 |
| %12 = OpLabel |
| %34 = OpPhi %int %int_0 %5 %37 %15 |
| %35 = OpPhi %int %int_0 %5 %33 %15 |
| %36 = OpPhi %int %int_0 %5 %31 %15 |
| OpLoopMerge %14 %15 None |
| OpBranch %16 |
| %16 = OpLabel |
| %20 = OpSLessThan %bool %35 %int_10 |
| OpBranchConditional %20 %13 %14 |
| %13 = OpLabel |
| %23 = OpSLessThan %bool %35 %36 |
| OpSelectionMerge %25 None |
| OpBranchConditional %23 %24 %25 |
| %24 = OpLabel |
| %28 = OpIAdd %int %34 %int_2 |
| OpStore %a %28 |
| OpBranch %25 |
| %25 = OpLabel |
| %37 = OpPhi %int %34 %13 %28 %24 |
| OpBranch %15 |
| %15 = OpLabel |
| %31 = OpIAdd %int %36 %int_1 |
| OpStore %j %31 |
| %33 = OpIAdd %int %35 %int_1 |
| OpStore %i %33 |
| OpBranch %12 |
| %14 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| { |
| auto result = |
| SinglePassRunAndDisassemble<LoopPeelingPass>(text, true, false); |
| |
| EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| } |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |