| // 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 <string> |
| #include <unordered_set> |
| #include <vector> |
| |
| #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" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| using DependencyAnalysisHelpers = ::testing::Test; |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| void a() { |
| int[10][10] arr; |
| int i = 0; |
| int j = 0; |
| for (; i < 10 && j < 10; i++, j++) { |
| arr[i][j] = arr[i][j]; |
| } |
| } |
| void b() { |
| int[10] arr; |
| for (int i = 0; i < 10; i+=2) { |
| arr[i] = arr[i]; |
| } |
| } |
| void main(){ |
| a(); |
| b(); |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, UnsupportedLoops) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %6 "a(" |
| OpName %8 "b(" |
| OpName %12 "i" |
| OpName %14 "j" |
| OpName %32 "arr" |
| OpName %45 "i" |
| OpName %54 "arr" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %10 = OpTypeInt 32 1 |
| %11 = OpTypePointer Function %10 |
| %13 = OpConstant %10 0 |
| %21 = OpConstant %10 10 |
| %22 = OpTypeBool |
| %27 = OpTypeInt 32 0 |
| %28 = OpConstant %27 10 |
| %29 = OpTypeArray %10 %28 |
| %30 = OpTypeArray %29 %28 |
| %31 = OpTypePointer Function %30 |
| %41 = OpConstant %10 1 |
| %53 = OpTypePointer Function %29 |
| %60 = OpConstant %10 2 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %63 = OpFunctionCall %2 %6 |
| %64 = OpFunctionCall %2 %8 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| %12 = OpVariable %11 Function |
| %14 = OpVariable %11 Function |
| %32 = OpVariable %31 Function |
| OpStore %12 %13 |
| OpStore %14 %13 |
| OpBranch %15 |
| %15 = OpLabel |
| %65 = OpPhi %10 %13 %7 %42 %18 |
| %66 = OpPhi %10 %13 %7 %44 %18 |
| OpLoopMerge %17 %18 None |
| OpBranch %19 |
| %19 = OpLabel |
| %23 = OpSLessThan %22 %65 %21 |
| %25 = OpSLessThan %22 %66 %21 |
| %26 = OpLogicalAnd %22 %23 %25 |
| OpBranchConditional %26 %16 %17 |
| %16 = OpLabel |
| %37 = OpAccessChain %11 %32 %65 %66 |
| %38 = OpLoad %10 %37 |
| %39 = OpAccessChain %11 %32 %65 %66 |
| OpStore %39 %38 |
| OpBranch %18 |
| %18 = OpLabel |
| %42 = OpIAdd %10 %65 %41 |
| OpStore %12 %42 |
| %44 = OpIAdd %10 %66 %41 |
| OpStore %14 %44 |
| OpBranch %15 |
| %17 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %8 = OpFunction %2 None %3 |
| %9 = OpLabel |
| %45 = OpVariable %11 Function |
| %54 = OpVariable %53 Function |
| OpStore %45 %13 |
| OpBranch %46 |
| %46 = OpLabel |
| %67 = OpPhi %10 %13 %9 %62 %49 |
| OpLoopMerge %48 %49 None |
| OpBranch %50 |
| %50 = OpLabel |
| %52 = OpSLessThan %22 %67 %21 |
| OpBranchConditional %52 %47 %48 |
| %47 = OpLabel |
| %57 = OpAccessChain %11 %54 %67 |
| %58 = OpLoad %10 %57 |
| %59 = OpAccessChain %11 %54 %67 |
| OpStore %59 %58 |
| OpBranch %49 |
| %49 = OpLabel |
| %62 = OpIAdd %10 %67 %60 |
| OpStore %45 %62 |
| OpBranch %46 |
| %48 = 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; |
| { |
| // Function a |
| const Function* f = spvtest::GetFunction(module, 6); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* store[1] = {nullptr}; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 16)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| store[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| // 38 -> 39 |
| DistanceVector distance_vector{loops.size()}; |
| EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); |
| EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(38), |
| store[0], &distance_vector)); |
| EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, |
| DistanceEntry::DependenceInformation::UNKNOWN); |
| EXPECT_EQ(distance_vector.GetEntries()[0].direction, |
| DistanceEntry::Directions::ALL); |
| } |
| { |
| // Function b |
| const Function* f = spvtest::GetFunction(module, 8); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* store[1] = {nullptr}; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 47)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| store[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| // 58 -> 59 |
| DistanceVector distance_vector{loops.size()}; |
| EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); |
| EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(58), |
| store[0], &distance_vector)); |
| EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, |
| DistanceEntry::DependenceInformation::UNKNOWN); |
| EXPECT_EQ(distance_vector.GetEntries()[0].direction, |
| DistanceEntry::Directions::ALL); |
| } |
| } |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| void a() { |
| for (int i = -10; i < 0; i++) { |
| |
| } |
| } |
| void b() { |
| for (int i = -5; i < 5; i++) { |
| |
| } |
| } |
| void c() { |
| for (int i = 0; i < 10; i++) { |
| |
| } |
| } |
| void d() { |
| for (int i = 5; i < 15; i++) { |
| |
| } |
| } |
| void e() { |
| for (int i = -10; i <= 0; i++) { |
| |
| } |
| } |
| void f() { |
| for (int i = -5; i <= 5; i++) { |
| |
| } |
| } |
| void g() { |
| for (int i = 0; i <= 10; i++) { |
| |
| } |
| } |
| void h() { |
| for (int i = 5; i <= 15; i++) { |
| |
| } |
| } |
| void i() { |
| for (int i = 0; i > -10; i--) { |
| |
| } |
| } |
| void j() { |
| for (int i = 5; i > -5; i--) { |
| |
| } |
| } |
| void k() { |
| for (int i = 10; i > 0; i--) { |
| |
| } |
| } |
| void l() { |
| for (int i = 15; i > 5; i--) { |
| |
| } |
| } |
| void m() { |
| for (int i = 0; i >= -10; i--) { |
| |
| } |
| } |
| void n() { |
| for (int i = 5; i >= -5; i--) { |
| |
| } |
| } |
| void o() { |
| for (int i = 10; i >= 0; i--) { |
| |
| } |
| } |
| void p() { |
| for (int i = 15; i >= 5; i--) { |
| |
| } |
| } |
| void main(){ |
| a(); |
| b(); |
| c(); |
| d(); |
| e(); |
| f(); |
| g(); |
| h(); |
| i(); |
| j(); |
| k(); |
| l(); |
| m(); |
| n(); |
| o(); |
| p(); |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, loop_information) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %6 "a(" |
| OpName %8 "b(" |
| OpName %10 "c(" |
| OpName %12 "d(" |
| OpName %14 "e(" |
| OpName %16 "f(" |
| OpName %18 "g(" |
| OpName %20 "h(" |
| OpName %22 "i(" |
| OpName %24 "j(" |
| OpName %26 "k(" |
| OpName %28 "l(" |
| OpName %30 "m(" |
| OpName %32 "n(" |
| OpName %34 "o(" |
| OpName %36 "p(" |
| OpName %40 "i" |
| OpName %54 "i" |
| OpName %66 "i" |
| OpName %77 "i" |
| OpName %88 "i" |
| OpName %98 "i" |
| OpName %108 "i" |
| OpName %118 "i" |
| OpName %128 "i" |
| OpName %138 "i" |
| OpName %148 "i" |
| OpName %158 "i" |
| OpName %168 "i" |
| OpName %178 "i" |
| OpName %188 "i" |
| OpName %198 "i" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %38 = OpTypeInt 32 1 |
| %39 = OpTypePointer Function %38 |
| %41 = OpConstant %38 -10 |
| %48 = OpConstant %38 0 |
| %49 = OpTypeBool |
| %52 = OpConstant %38 1 |
| %55 = OpConstant %38 -5 |
| %62 = OpConstant %38 5 |
| %73 = OpConstant %38 10 |
| %84 = OpConstant %38 15 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %208 = OpFunctionCall %2 %6 |
| %209 = OpFunctionCall %2 %8 |
| %210 = OpFunctionCall %2 %10 |
| %211 = OpFunctionCall %2 %12 |
| %212 = OpFunctionCall %2 %14 |
| %213 = OpFunctionCall %2 %16 |
| %214 = OpFunctionCall %2 %18 |
| %215 = OpFunctionCall %2 %20 |
| %216 = OpFunctionCall %2 %22 |
| %217 = OpFunctionCall %2 %24 |
| %218 = OpFunctionCall %2 %26 |
| %219 = OpFunctionCall %2 %28 |
| %220 = OpFunctionCall %2 %30 |
| %221 = OpFunctionCall %2 %32 |
| %222 = OpFunctionCall %2 %34 |
| %223 = OpFunctionCall %2 %36 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| %40 = OpVariable %39 Function |
| OpStore %40 %41 |
| OpBranch %42 |
| %42 = OpLabel |
| %224 = OpPhi %38 %41 %7 %53 %45 |
| OpLoopMerge %44 %45 None |
| OpBranch %46 |
| %46 = OpLabel |
| %50 = OpSLessThan %49 %224 %48 |
| OpBranchConditional %50 %43 %44 |
| %43 = OpLabel |
| OpBranch %45 |
| %45 = OpLabel |
| %53 = OpIAdd %38 %224 %52 |
| OpStore %40 %53 |
| OpBranch %42 |
| %44 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %8 = OpFunction %2 None %3 |
| %9 = OpLabel |
| %54 = OpVariable %39 Function |
| OpStore %54 %55 |
| OpBranch %56 |
| %56 = OpLabel |
| %225 = OpPhi %38 %55 %9 %65 %59 |
| OpLoopMerge %58 %59 None |
| OpBranch %60 |
| %60 = OpLabel |
| %63 = OpSLessThan %49 %225 %62 |
| OpBranchConditional %63 %57 %58 |
| %57 = OpLabel |
| OpBranch %59 |
| %59 = OpLabel |
| %65 = OpIAdd %38 %225 %52 |
| OpStore %54 %65 |
| OpBranch %56 |
| %58 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %10 = OpFunction %2 None %3 |
| %11 = OpLabel |
| %66 = OpVariable %39 Function |
| OpStore %66 %48 |
| OpBranch %67 |
| %67 = OpLabel |
| %226 = OpPhi %38 %48 %11 %76 %70 |
| OpLoopMerge %69 %70 None |
| OpBranch %71 |
| %71 = OpLabel |
| %74 = OpSLessThan %49 %226 %73 |
| OpBranchConditional %74 %68 %69 |
| %68 = OpLabel |
| OpBranch %70 |
| %70 = OpLabel |
| %76 = OpIAdd %38 %226 %52 |
| OpStore %66 %76 |
| OpBranch %67 |
| %69 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %2 None %3 |
| %13 = OpLabel |
| %77 = OpVariable %39 Function |
| OpStore %77 %62 |
| OpBranch %78 |
| %78 = OpLabel |
| %227 = OpPhi %38 %62 %13 %87 %81 |
| OpLoopMerge %80 %81 None |
| OpBranch %82 |
| %82 = OpLabel |
| %85 = OpSLessThan %49 %227 %84 |
| OpBranchConditional %85 %79 %80 |
| %79 = OpLabel |
| OpBranch %81 |
| %81 = OpLabel |
| %87 = OpIAdd %38 %227 %52 |
| OpStore %77 %87 |
| OpBranch %78 |
| %80 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %2 None %3 |
| %15 = OpLabel |
| %88 = OpVariable %39 Function |
| OpStore %88 %41 |
| OpBranch %89 |
| %89 = OpLabel |
| %228 = OpPhi %38 %41 %15 %97 %92 |
| OpLoopMerge %91 %92 None |
| OpBranch %93 |
| %93 = OpLabel |
| %95 = OpSLessThanEqual %49 %228 %48 |
| OpBranchConditional %95 %90 %91 |
| %90 = OpLabel |
| OpBranch %92 |
| %92 = OpLabel |
| %97 = OpIAdd %38 %228 %52 |
| OpStore %88 %97 |
| OpBranch %89 |
| %91 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %16 = OpFunction %2 None %3 |
| %17 = OpLabel |
| %98 = OpVariable %39 Function |
| OpStore %98 %55 |
| OpBranch %99 |
| %99 = OpLabel |
| %229 = OpPhi %38 %55 %17 %107 %102 |
| OpLoopMerge %101 %102 None |
| OpBranch %103 |
| %103 = OpLabel |
| %105 = OpSLessThanEqual %49 %229 %62 |
| OpBranchConditional %105 %100 %101 |
| %100 = OpLabel |
| OpBranch %102 |
| %102 = OpLabel |
| %107 = OpIAdd %38 %229 %52 |
| OpStore %98 %107 |
| OpBranch %99 |
| %101 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %18 = OpFunction %2 None %3 |
| %19 = OpLabel |
| %108 = OpVariable %39 Function |
| OpStore %108 %48 |
| OpBranch %109 |
| %109 = OpLabel |
| %230 = OpPhi %38 %48 %19 %117 %112 |
| OpLoopMerge %111 %112 None |
| OpBranch %113 |
| %113 = OpLabel |
| %115 = OpSLessThanEqual %49 %230 %73 |
| OpBranchConditional %115 %110 %111 |
| %110 = OpLabel |
| OpBranch %112 |
| %112 = OpLabel |
| %117 = OpIAdd %38 %230 %52 |
| OpStore %108 %117 |
| OpBranch %109 |
| %111 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %20 = OpFunction %2 None %3 |
| %21 = OpLabel |
| %118 = OpVariable %39 Function |
| OpStore %118 %62 |
| OpBranch %119 |
| %119 = OpLabel |
| %231 = OpPhi %38 %62 %21 %127 %122 |
| OpLoopMerge %121 %122 None |
| OpBranch %123 |
| %123 = OpLabel |
| %125 = OpSLessThanEqual %49 %231 %84 |
| OpBranchConditional %125 %120 %121 |
| %120 = OpLabel |
| OpBranch %122 |
| %122 = OpLabel |
| %127 = OpIAdd %38 %231 %52 |
| OpStore %118 %127 |
| OpBranch %119 |
| %121 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %22 = OpFunction %2 None %3 |
| %23 = OpLabel |
| %128 = OpVariable %39 Function |
| OpStore %128 %48 |
| OpBranch %129 |
| %129 = OpLabel |
| %232 = OpPhi %38 %48 %23 %137 %132 |
| OpLoopMerge %131 %132 None |
| OpBranch %133 |
| %133 = OpLabel |
| %135 = OpSGreaterThan %49 %232 %41 |
| OpBranchConditional %135 %130 %131 |
| %130 = OpLabel |
| OpBranch %132 |
| %132 = OpLabel |
| %137 = OpISub %38 %232 %52 |
| OpStore %128 %137 |
| OpBranch %129 |
| %131 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %24 = OpFunction %2 None %3 |
| %25 = OpLabel |
| %138 = OpVariable %39 Function |
| OpStore %138 %62 |
| OpBranch %139 |
| %139 = OpLabel |
| %233 = OpPhi %38 %62 %25 %147 %142 |
| OpLoopMerge %141 %142 None |
| OpBranch %143 |
| %143 = OpLabel |
| %145 = OpSGreaterThan %49 %233 %55 |
| OpBranchConditional %145 %140 %141 |
| %140 = OpLabel |
| OpBranch %142 |
| %142 = OpLabel |
| %147 = OpISub %38 %233 %52 |
| OpStore %138 %147 |
| OpBranch %139 |
| %141 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %26 = OpFunction %2 None %3 |
| %27 = OpLabel |
| %148 = OpVariable %39 Function |
| OpStore %148 %73 |
| OpBranch %149 |
| %149 = OpLabel |
| %234 = OpPhi %38 %73 %27 %157 %152 |
| OpLoopMerge %151 %152 None |
| OpBranch %153 |
| %153 = OpLabel |
| %155 = OpSGreaterThan %49 %234 %48 |
| OpBranchConditional %155 %150 %151 |
| %150 = OpLabel |
| OpBranch %152 |
| %152 = OpLabel |
| %157 = OpISub %38 %234 %52 |
| OpStore %148 %157 |
| OpBranch %149 |
| %151 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %28 = OpFunction %2 None %3 |
| %29 = OpLabel |
| %158 = OpVariable %39 Function |
| OpStore %158 %84 |
| OpBranch %159 |
| %159 = OpLabel |
| %235 = OpPhi %38 %84 %29 %167 %162 |
| OpLoopMerge %161 %162 None |
| OpBranch %163 |
| %163 = OpLabel |
| %165 = OpSGreaterThan %49 %235 %62 |
| OpBranchConditional %165 %160 %161 |
| %160 = OpLabel |
| OpBranch %162 |
| %162 = OpLabel |
| %167 = OpISub %38 %235 %52 |
| OpStore %158 %167 |
| OpBranch %159 |
| %161 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %30 = OpFunction %2 None %3 |
| %31 = OpLabel |
| %168 = OpVariable %39 Function |
| OpStore %168 %48 |
| OpBranch %169 |
| %169 = OpLabel |
| %236 = OpPhi %38 %48 %31 %177 %172 |
| OpLoopMerge %171 %172 None |
| OpBranch %173 |
| %173 = OpLabel |
| %175 = OpSGreaterThanEqual %49 %236 %41 |
| OpBranchConditional %175 %170 %171 |
| %170 = OpLabel |
| OpBranch %172 |
| %172 = OpLabel |
| %177 = OpISub %38 %236 %52 |
| OpStore %168 %177 |
| OpBranch %169 |
| %171 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %32 = OpFunction %2 None %3 |
| %33 = OpLabel |
| %178 = OpVariable %39 Function |
| OpStore %178 %62 |
| OpBranch %179 |
| %179 = OpLabel |
| %237 = OpPhi %38 %62 %33 %187 %182 |
| OpLoopMerge %181 %182 None |
| OpBranch %183 |
| %183 = OpLabel |
| %185 = OpSGreaterThanEqual %49 %237 %55 |
| OpBranchConditional %185 %180 %181 |
| %180 = OpLabel |
| OpBranch %182 |
| %182 = OpLabel |
| %187 = OpISub %38 %237 %52 |
| OpStore %178 %187 |
| OpBranch %179 |
| %181 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %34 = OpFunction %2 None %3 |
| %35 = OpLabel |
| %188 = OpVariable %39 Function |
| OpStore %188 %73 |
| OpBranch %189 |
| %189 = OpLabel |
| %238 = OpPhi %38 %73 %35 %197 %192 |
| OpLoopMerge %191 %192 None |
| OpBranch %193 |
| %193 = OpLabel |
| %195 = OpSGreaterThanEqual %49 %238 %48 |
| OpBranchConditional %195 %190 %191 |
| %190 = OpLabel |
| OpBranch %192 |
| %192 = OpLabel |
| %197 = OpISub %38 %238 %52 |
| OpStore %188 %197 |
| OpBranch %189 |
| %191 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %36 = OpFunction %2 None %3 |
| %37 = OpLabel |
| %198 = OpVariable %39 Function |
| OpStore %198 %84 |
| OpBranch %199 |
| %199 = OpLabel |
| %239 = OpPhi %38 %84 %37 %207 %202 |
| OpLoopMerge %201 %202 None |
| OpBranch %203 |
| %203 = OpLabel |
| %205 = OpSGreaterThanEqual %49 %239 %62 |
| OpBranchConditional %205 %200 %201 |
| %200 = OpLabel |
| OpBranch %202 |
| %202 = OpLabel |
| %207 = OpISub %38 %239 %52 |
| OpStore %198 %207 |
| OpBranch %199 |
| %201 = 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; |
| { |
| // Function a |
| const Function* f = spvtest::GetFunction(module, 6); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -10); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -1); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(-10)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(-1)); |
| } |
| { |
| // Function b |
| const Function* f = spvtest::GetFunction(module, 8); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 4); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(-5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(4)); |
| } |
| { |
| // Function c |
| const Function* f = spvtest::GetFunction(module, 10); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 9); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(9)); |
| } |
| { |
| // Function d |
| const Function* f = spvtest::GetFunction(module, 12); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 14); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(14)); |
| } |
| { |
| // Function e |
| const Function* f = spvtest::GetFunction(module, 14); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -10); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(-10)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| } |
| { |
| // Function f |
| const Function* f = spvtest::GetFunction(module, 16); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(-5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| } |
| { |
| // Function g |
| const Function* f = spvtest::GetFunction(module, 18); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(10)); |
| } |
| { |
| // Function h |
| const Function* f = spvtest::GetFunction(module, 20); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 15); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(1)), |
| analysis.GetScalarEvolution()->CreateConstant(15)); |
| } |
| { |
| // Function i |
| const Function* f = spvtest::GetFunction(module, 22); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -9); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(-9)); |
| } |
| { |
| // Function j |
| const Function* f = spvtest::GetFunction(module, 24); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -4); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(-4)); |
| } |
| { |
| // Function k |
| const Function* f = spvtest::GetFunction(module, 26); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 1); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(10)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(1)); |
| } |
| { |
| // Function l |
| const Function* f = spvtest::GetFunction(module, 28); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 15); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 6); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(15)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(6)); |
| } |
| { |
| // Function m |
| const Function* f = spvtest::GetFunction(module, 30); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -10); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(-10)); |
| } |
| { |
| // Function n |
| const Function* f = spvtest::GetFunction(module, 32); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| -5); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(-5)); |
| } |
| { |
| // Function o |
| const Function* f = spvtest::GetFunction(module, 34); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 10); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 0); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(10)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(0)); |
| } |
| { |
| // Function p |
| const Function* f = spvtest::GetFunction(module, 36); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_EQ( |
| analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 15); |
| EXPECT_EQ( |
| analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 5); |
| |
| EXPECT_EQ( |
| analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), |
| 11); |
| |
| EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), |
| analysis.GetScalarEvolution()->CreateConstant(15)); |
| |
| EXPECT_EQ(analysis.GetFinalTripInductionNode( |
| loop, analysis.GetScalarEvolution()->CreateConstant(-1)), |
| analysis.GetScalarEvolution()->CreateConstant(5)); |
| } |
| } |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| void main(){ |
| for (int i = 0; i < 10; i++) { |
| |
| } |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, bounds_checks) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %8 "i" |
| %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 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| OpStore %8 %9 |
| OpBranch %10 |
| %10 = OpLabel |
| %22 = OpPhi %6 %9 %5 %21 %13 |
| OpLoopMerge %12 %13 None |
| OpBranch %14 |
| %14 = OpLabel |
| %18 = OpSLessThan %17 %22 %16 |
| OpBranchConditional %18 %11 %12 |
| %11 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %21 = OpIAdd %6 %22 %20 |
| OpStore %8 %21 |
| OpBranch %10 |
| %12 = 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; |
| // We need a shader that includes a loop for this test so we can build a |
| // LoopDependenceAnalaysis |
| const Function* f = spvtest::GetFunction(module, 4); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 0)); |
| EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 0)); |
| EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 1)); |
| EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 1)); |
| EXPECT_TRUE(analysis.IsWithinBounds(-2, -2, -2)); |
| EXPECT_TRUE(analysis.IsWithinBounds(-2, -3, 0)); |
| EXPECT_TRUE(analysis.IsWithinBounds(-2, 0, -3)); |
| EXPECT_TRUE(analysis.IsWithinBounds(2, 2, 2)); |
| EXPECT_TRUE(analysis.IsWithinBounds(2, 3, 0)); |
| |
| EXPECT_FALSE(analysis.IsWithinBounds(2, 3, 3)); |
| EXPECT_FALSE(analysis.IsWithinBounds(0, 1, 5)); |
| EXPECT_FALSE(analysis.IsWithinBounds(0, -1, -4)); |
| EXPECT_FALSE(analysis.IsWithinBounds(-2, -4, -3)); |
| } |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| layout(location = 0) in vec4 in_vec; |
| // Loop iterates from constant to symbolic |
| void a() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = 0; i < N; i++) { // Bounds are N - 0 - 1 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void b() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = 0; i <= N; i++) { // Bounds are N - 0 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void c() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = 9; i > N; i--) { // Bounds are 9 - N - 1 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void d() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = 9; i >= N; i--) { // Bounds are 9 - N |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void main(){ |
| a(); |
| b(); |
| c(); |
| d(); |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, const_to_symbolic) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" %20 |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %6 "a(" |
| OpName %8 "b(" |
| OpName %10 "c(" |
| OpName %12 "d(" |
| OpName %16 "N" |
| OpName %20 "in_vec" |
| OpName %27 "i" |
| OpName %41 "arr" |
| OpName %59 "N" |
| OpName %63 "i" |
| OpName %72 "arr" |
| OpName %89 "N" |
| OpName %93 "i" |
| OpName %103 "arr" |
| OpName %120 "N" |
| OpName %124 "i" |
| OpName %133 "arr" |
| OpDecorate %20 Location 0 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %14 = OpTypeInt 32 1 |
| %15 = OpTypePointer Function %14 |
| %17 = OpTypeFloat 32 |
| %18 = OpTypeVector %17 4 |
| %19 = OpTypePointer Input %18 |
| %20 = OpVariable %19 Input |
| %21 = OpTypeInt 32 0 |
| %22 = OpConstant %21 0 |
| %23 = OpTypePointer Input %17 |
| %28 = OpConstant %14 0 |
| %36 = OpTypeBool |
| %38 = OpConstant %21 10 |
| %39 = OpTypeArray %14 %38 |
| %40 = OpTypePointer Function %39 |
| %57 = OpConstant %14 1 |
| %94 = OpConstant %14 9 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %150 = OpFunctionCall %2 %6 |
| %151 = OpFunctionCall %2 %8 |
| %152 = OpFunctionCall %2 %10 |
| %153 = OpFunctionCall %2 %12 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| %16 = OpVariable %15 Function |
| %27 = OpVariable %15 Function |
| %41 = OpVariable %40 Function |
| %24 = OpAccessChain %23 %20 %22 |
| %25 = OpLoad %17 %24 |
| %26 = OpConvertFToS %14 %25 |
| OpStore %16 %26 |
| OpStore %27 %28 |
| OpBranch %29 |
| %29 = OpLabel |
| %154 = OpPhi %14 %28 %7 %58 %32 |
| OpLoopMerge %31 %32 None |
| OpBranch %33 |
| %33 = OpLabel |
| %37 = OpSLessThan %36 %154 %26 |
| OpBranchConditional %37 %30 %31 |
| %30 = OpLabel |
| %45 = OpIAdd %14 %154 %26 |
| %46 = OpAccessChain %15 %41 %45 |
| %47 = OpLoad %14 %46 |
| %48 = OpAccessChain %15 %41 %154 |
| OpStore %48 %47 |
| %51 = OpIAdd %14 %154 %26 |
| %53 = OpAccessChain %15 %41 %154 |
| %54 = OpLoad %14 %53 |
| %55 = OpAccessChain %15 %41 %51 |
| OpStore %55 %54 |
| OpBranch %32 |
| %32 = OpLabel |
| %58 = OpIAdd %14 %154 %57 |
| OpStore %27 %58 |
| OpBranch %29 |
| %31 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %8 = OpFunction %2 None %3 |
| %9 = OpLabel |
| %59 = OpVariable %15 Function |
| %63 = OpVariable %15 Function |
| %72 = OpVariable %40 Function |
| %60 = OpAccessChain %23 %20 %22 |
| %61 = OpLoad %17 %60 |
| %62 = OpConvertFToS %14 %61 |
| OpStore %59 %62 |
| OpStore %63 %28 |
| OpBranch %64 |
| %64 = OpLabel |
| %155 = OpPhi %14 %28 %9 %88 %67 |
| OpLoopMerge %66 %67 None |
| OpBranch %68 |
| %68 = OpLabel |
| %71 = OpSLessThanEqual %36 %155 %62 |
| OpBranchConditional %71 %65 %66 |
| %65 = OpLabel |
| %76 = OpIAdd %14 %155 %62 |
| %77 = OpAccessChain %15 %72 %76 |
| %78 = OpLoad %14 %77 |
| %79 = OpAccessChain %15 %72 %155 |
| OpStore %79 %78 |
| %82 = OpIAdd %14 %155 %62 |
| %84 = OpAccessChain %15 %72 %155 |
| %85 = OpLoad %14 %84 |
| %86 = OpAccessChain %15 %72 %82 |
| OpStore %86 %85 |
| OpBranch %67 |
| %67 = OpLabel |
| %88 = OpIAdd %14 %155 %57 |
| OpStore %63 %88 |
| OpBranch %64 |
| %66 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %10 = OpFunction %2 None %3 |
| %11 = OpLabel |
| %89 = OpVariable %15 Function |
| %93 = OpVariable %15 Function |
| %103 = OpVariable %40 Function |
| %90 = OpAccessChain %23 %20 %22 |
| %91 = OpLoad %17 %90 |
| %92 = OpConvertFToS %14 %91 |
| OpStore %89 %92 |
| OpStore %93 %94 |
| OpBranch %95 |
| %95 = OpLabel |
| %156 = OpPhi %14 %94 %11 %119 %98 |
| OpLoopMerge %97 %98 None |
| OpBranch %99 |
| %99 = OpLabel |
| %102 = OpSGreaterThan %36 %156 %92 |
| OpBranchConditional %102 %96 %97 |
| %96 = OpLabel |
| %107 = OpIAdd %14 %156 %92 |
| %108 = OpAccessChain %15 %103 %107 |
| %109 = OpLoad %14 %108 |
| %110 = OpAccessChain %15 %103 %156 |
| OpStore %110 %109 |
| %113 = OpIAdd %14 %156 %92 |
| %115 = OpAccessChain %15 %103 %156 |
| %116 = OpLoad %14 %115 |
| %117 = OpAccessChain %15 %103 %113 |
| OpStore %117 %116 |
| OpBranch %98 |
| %98 = OpLabel |
| %119 = OpISub %14 %156 %57 |
| OpStore %93 %119 |
| OpBranch %95 |
| %97 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %2 None %3 |
| %13 = OpLabel |
| %120 = OpVariable %15 Function |
| %124 = OpVariable %15 Function |
| %133 = OpVariable %40 Function |
| %121 = OpAccessChain %23 %20 %22 |
| %122 = OpLoad %17 %121 |
| %123 = OpConvertFToS %14 %122 |
| OpStore %120 %123 |
| OpStore %124 %94 |
| OpBranch %125 |
| %125 = OpLabel |
| %157 = OpPhi %14 %94 %13 %149 %128 |
| OpLoopMerge %127 %128 None |
| OpBranch %129 |
| %129 = OpLabel |
| %132 = OpSGreaterThanEqual %36 %157 %123 |
| OpBranchConditional %132 %126 %127 |
| %126 = OpLabel |
| %137 = OpIAdd %14 %157 %123 |
| %138 = OpAccessChain %15 %133 %137 |
| %139 = OpLoad %14 %138 |
| %140 = OpAccessChain %15 %133 %157 |
| OpStore %140 %139 |
| %143 = OpIAdd %14 %157 %123 |
| %145 = OpAccessChain %15 %133 %157 |
| %146 = OpLoad %14 %145 |
| %147 = OpAccessChain %15 %133 %143 |
| OpStore %147 %146 |
| OpBranch %128 |
| %128 = OpLabel |
| %149 = OpISub %14 %157 %57 |
| OpStore %124 %149 |
| OpBranch %125 |
| %127 = 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; |
| |
| { |
| // Function a |
| const Function* f = spvtest::GetFunction(module, 6); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 47 -> 48 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(47) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent and supported. |
| EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 54 -> 55 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(54) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function b |
| const Function* f = spvtest::GetFunction(module, 8); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 65)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 78 -> 79 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(78) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 85 -> 86 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(85) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function c |
| const Function* f = spvtest::GetFunction(module, 10); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 109 -> 110 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(109) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 116 -> 117 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(116) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function d |
| const Function* f = spvtest::GetFunction(module, 12); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 126)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 139 -> 140 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(139) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 146 -> 147 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(146) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| } |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| layout(location = 0) in vec4 in_vec; |
| // Loop iterates from symbolic to constant |
| void a() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = N; i < 9; i++) { // Bounds are 9 - N - 1 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void b() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = N; i <= 9; i++) { // Bounds are 9 - N |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void c() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = N; i > 0; i--) { // Bounds are N - 0 - 1 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void d() { |
| int N = int(in_vec.x); |
| int arr[10]; |
| for (int i = N; i >= 0; i--) { // Bounds are N - 0 |
| arr[i] = arr[i+N]; // |distance| = N |
| arr[i+N] = arr[i]; // |distance| = N |
| } |
| } |
| void main(){ |
| a(); |
| b(); |
| c(); |
| d(); |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, symbolic_to_const) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" %20 |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %6 "a(" |
| OpName %8 "b(" |
| OpName %10 "c(" |
| OpName %12 "d(" |
| OpName %16 "N" |
| OpName %20 "in_vec" |
| OpName %27 "i" |
| OpName %41 "arr" |
| OpName %59 "N" |
| OpName %63 "i" |
| OpName %72 "arr" |
| OpName %89 "N" |
| OpName %93 "i" |
| OpName %103 "arr" |
| OpName %120 "N" |
| OpName %124 "i" |
| OpName %133 "arr" |
| OpDecorate %20 Location 0 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %14 = OpTypeInt 32 1 |
| %15 = OpTypePointer Function %14 |
| %17 = OpTypeFloat 32 |
| %18 = OpTypeVector %17 4 |
| %19 = OpTypePointer Input %18 |
| %20 = OpVariable %19 Input |
| %21 = OpTypeInt 32 0 |
| %22 = OpConstant %21 0 |
| %23 = OpTypePointer Input %17 |
| %35 = OpConstant %14 9 |
| %36 = OpTypeBool |
| %38 = OpConstant %21 10 |
| %39 = OpTypeArray %14 %38 |
| %40 = OpTypePointer Function %39 |
| %57 = OpConstant %14 1 |
| %101 = OpConstant %14 0 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %150 = OpFunctionCall %2 %6 |
| %151 = OpFunctionCall %2 %8 |
| %152 = OpFunctionCall %2 %10 |
| %153 = OpFunctionCall %2 %12 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| %16 = OpVariable %15 Function |
| %27 = OpVariable %15 Function |
| %41 = OpVariable %40 Function |
| %24 = OpAccessChain %23 %20 %22 |
| %25 = OpLoad %17 %24 |
| %26 = OpConvertFToS %14 %25 |
| OpStore %16 %26 |
| OpStore %27 %26 |
| OpBranch %29 |
| %29 = OpLabel |
| %154 = OpPhi %14 %26 %7 %58 %32 |
| OpLoopMerge %31 %32 None |
| OpBranch %33 |
| %33 = OpLabel |
| %37 = OpSLessThan %36 %154 %35 |
| OpBranchConditional %37 %30 %31 |
| %30 = OpLabel |
| %45 = OpIAdd %14 %154 %26 |
| %46 = OpAccessChain %15 %41 %45 |
| %47 = OpLoad %14 %46 |
| %48 = OpAccessChain %15 %41 %154 |
| OpStore %48 %47 |
| %51 = OpIAdd %14 %154 %26 |
| %53 = OpAccessChain %15 %41 %154 |
| %54 = OpLoad %14 %53 |
| %55 = OpAccessChain %15 %41 %51 |
| OpStore %55 %54 |
| OpBranch %32 |
| %32 = OpLabel |
| %58 = OpIAdd %14 %154 %57 |
| OpStore %27 %58 |
| OpBranch %29 |
| %31 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %8 = OpFunction %2 None %3 |
| %9 = OpLabel |
| %59 = OpVariable %15 Function |
| %63 = OpVariable %15 Function |
| %72 = OpVariable %40 Function |
| %60 = OpAccessChain %23 %20 %22 |
| %61 = OpLoad %17 %60 |
| %62 = OpConvertFToS %14 %61 |
| OpStore %59 %62 |
| OpStore %63 %62 |
| OpBranch %65 |
| %65 = OpLabel |
| %155 = OpPhi %14 %62 %9 %88 %68 |
| OpLoopMerge %67 %68 None |
| OpBranch %69 |
| %69 = OpLabel |
| %71 = OpSLessThanEqual %36 %155 %35 |
| OpBranchConditional %71 %66 %67 |
| %66 = OpLabel |
| %76 = OpIAdd %14 %155 %62 |
| %77 = OpAccessChain %15 %72 %76 |
| %78 = OpLoad %14 %77 |
| %79 = OpAccessChain %15 %72 %155 |
| OpStore %79 %78 |
| %82 = OpIAdd %14 %155 %62 |
| %84 = OpAccessChain %15 %72 %155 |
| %85 = OpLoad %14 %84 |
| %86 = OpAccessChain %15 %72 %82 |
| OpStore %86 %85 |
| OpBranch %68 |
| %68 = OpLabel |
| %88 = OpIAdd %14 %155 %57 |
| OpStore %63 %88 |
| OpBranch %65 |
| %67 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %10 = OpFunction %2 None %3 |
| %11 = OpLabel |
| %89 = OpVariable %15 Function |
| %93 = OpVariable %15 Function |
| %103 = OpVariable %40 Function |
| %90 = OpAccessChain %23 %20 %22 |
| %91 = OpLoad %17 %90 |
| %92 = OpConvertFToS %14 %91 |
| OpStore %89 %92 |
| OpStore %93 %92 |
| OpBranch %95 |
| %95 = OpLabel |
| %156 = OpPhi %14 %92 %11 %119 %98 |
| OpLoopMerge %97 %98 None |
| OpBranch %99 |
| %99 = OpLabel |
| %102 = OpSGreaterThan %36 %156 %101 |
| OpBranchConditional %102 %96 %97 |
| %96 = OpLabel |
| %107 = OpIAdd %14 %156 %92 |
| %108 = OpAccessChain %15 %103 %107 |
| %109 = OpLoad %14 %108 |
| %110 = OpAccessChain %15 %103 %156 |
| OpStore %110 %109 |
| %113 = OpIAdd %14 %156 %92 |
| %115 = OpAccessChain %15 %103 %156 |
| %116 = OpLoad %14 %115 |
| %117 = OpAccessChain %15 %103 %113 |
| OpStore %117 %116 |
| OpBranch %98 |
| %98 = OpLabel |
| %119 = OpISub %14 %156 %57 |
| OpStore %93 %119 |
| OpBranch %95 |
| %97 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %2 None %3 |
| %13 = OpLabel |
| %120 = OpVariable %15 Function |
| %124 = OpVariable %15 Function |
| %133 = OpVariable %40 Function |
| %121 = OpAccessChain %23 %20 %22 |
| %122 = OpLoad %17 %121 |
| %123 = OpConvertFToS %14 %122 |
| OpStore %120 %123 |
| OpStore %124 %123 |
| OpBranch %126 |
| %126 = OpLabel |
| %157 = OpPhi %14 %123 %13 %149 %129 |
| OpLoopMerge %128 %129 None |
| OpBranch %130 |
| %130 = OpLabel |
| %132 = OpSGreaterThanEqual %36 %157 %101 |
| OpBranchConditional %132 %127 %128 |
| %127 = OpLabel |
| %137 = OpIAdd %14 %157 %123 |
| %138 = OpAccessChain %15 %133 %137 |
| %139 = OpLoad %14 %138 |
| %140 = OpAccessChain %15 %133 %157 |
| OpStore %140 %139 |
| %143 = OpIAdd %14 %157 %123 |
| %145 = OpAccessChain %15 %133 %157 |
| %146 = OpLoad %14 %145 |
| %147 = OpAccessChain %15 %133 %143 |
| OpStore %147 %146 |
| OpBranch %129 |
| %129 = OpLabel |
| %149 = OpISub %14 %157 %57 |
| OpStore %124 %149 |
| OpBranch %126 |
| %128 = 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; |
| { |
| // Function a |
| const Function* f = spvtest::GetFunction(module, 6); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 47 -> 48 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(47) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 54 -> 55 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(54) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function b |
| const Function* f = spvtest::GetFunction(module, 8); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 66)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 78 -> 79 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(78) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 85 -> 86 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(85) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function c |
| const Function* f = spvtest::GetFunction(module, 10); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 109 -> 110 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(109) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent and supported. |
| EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 116 -> 117 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(116) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Independent but not supported. |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| { |
| // Function d |
| const Function* f = spvtest::GetFunction(module, 12); |
| LoopDescriptor& ld = *context->GetLoopDescriptor(f); |
| Loop* loop = &ld.GetLoopByIndex(0); |
| std::vector<const Loop*> loops{loop}; |
| LoopDependenceAnalysis analysis{context.get(), loops}; |
| |
| const Instruction* stores[2]; |
| int stores_found = 0; |
| for (const Instruction& inst : *spvtest::GetBasicBlock(f, 127)) { |
| if (inst.opcode() == spv::Op::OpStore) { |
| stores[stores_found] = &inst; |
| ++stores_found; |
| } |
| } |
| |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_TRUE(stores[i]); |
| } |
| |
| // 139 -> 140 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(139) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[0]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| |
| // 146 -> 147 |
| { |
| // Analyse and simplify the instruction behind the access chain of this |
| // load. |
| Instruction* load_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(context->get_def_use_mgr() |
| ->GetDef(146) |
| ->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); |
| |
| // Analyse and simplify the instruction behind the access chain of this |
| // store. |
| Instruction* store_var = context->get_def_use_mgr()->GetDef( |
| context->get_def_use_mgr() |
| ->GetDef(stores[1]->GetSingleWordInOperand(0)) |
| ->GetSingleWordInOperand(1)); |
| SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); |
| |
| SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( |
| analysis.GetScalarEvolution()->CreateSubtraction(load, store)); |
| |
| // Dependent |
| EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( |
| loop, delta, store->AsSERecurrentNode()->GetCoefficient())); |
| } |
| } |
| } |
| |
| /* |
| Generated from the following GLSL fragment shader |
| with --eliminate-local-multi-store |
| #version 440 core |
| layout(location = 0) in vec4 in_vec; |
| // Loop iterates from symbolic to symbolic |
| void a() { |
| int M = int(in_vec.x); |
| int N = int(in_vec.y); |
| int arr[10]; |
| for (int i = M; i < N; i++) { // Bounds are N - M - 1 |
| arr[i+M+N] = arr[i+M+2*N]; // |distance| = N |
| arr[i+M+2*N] = arr[i+M+N]; // |distance| = N |
| } |
| } |
| void b() { |
| int M = int(in_vec.x); |
| int N = int(in_vec.y); |
| int arr[10]; |
| for (int i = M; i <= N; i++) { // Bounds are N - M |
| arr[i+M+N] = arr[i+M+2*N]; // |distance| = N |
| arr[i+M+2*N] = arr[i+M+N]; // |distance| = N |
| } |
| } |
| void c() { |
| int M = int(in_vec.x); |
| int N = int(in_vec.y); |
| int arr[10]; |
| for (int i = M; i > N; i--) { // Bounds are M - N - 1 |
| arr[i+M+N] = arr[i+M+2*N]; // |distance| = N |
| arr[i+M+2*N] = arr[i+M+N]; // |distance| = N |
| } |
| } |
| void d() { |
| int M = int(in_vec.x); |
| int N = int(in_vec.y); |
| int arr[10]; |
| for (int i = M; i >= N; i--) { // Bounds are M - N |
| arr[i+M+N] = arr[i+M+2*N]; // |distance| = N |
| arr[i+M+2*N] = arr[i+M+N]; // |distance| = N |
| } |
| } |
| void main(){ |
| a(); |
| b(); |
| c(); |
| d(); |
| } |
| */ |
| TEST(DependencyAnalysisHelpers, symbolic_to_symbolic) { |
| const std::string text = R"( OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" %20 |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource GLSL 440 |
| OpName %4 "main" |
| OpName %6 "a(" |
| OpName %8 "b(" |
| OpName %10 "c(" |
| OpName %12 "d(" |
| OpName %16 "M" |
| OpName %20 "in_vec" |
| OpName %27 "N" |
| OpName %32 "i" |
| OpName %46 "arr" |
| OpName %79 "M" |
| OpName %83 "N" |
| OpName %87 "i" |
| OpName %97 "arr" |
| OpName %128 "M" |
| OpName %132 "N" |
| OpName %136 "i" |
| OpName %146 "arr" |
| OpName %177 "M" |
| OpName %181 "N" |
| OpName %185 "i" |
| OpName %195 "arr" |
| OpDecorate %20 Location 0 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %14 = OpTypeInt 32 1 |
| %15 = OpTypePointer Function %14 |
| %17 = OpTypeFloat 32 |
| %18 = OpTypeVector %17 4 |
| %19 = OpTypePointer Input %18 |
| %20 = OpVariable %19 Input |
| %21 = OpTypeInt 32 0 |
| %22 = OpConstant %21 0 |
| %23 = OpTypePointer Input %17 |
| %28 = OpConstant %21 1 |
| %41 = OpTypeBool |
| %43 = OpConstant %21 10 |
| %44 = OpTypeArray %14 %43 |
| %45 = OpTypePointer Function %44 |
| %55 = OpConstant %14 2 |
| %77 = OpConstant %14 1 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %226 = OpFunctionCall %2 %6 |
| %227 = OpFunctionCall %2 %8 |
| %228 = OpFunctionCall %2 %10 |
| %229 = OpFunctionCall %2 %12 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| %16 = OpVariable %15 Function |
| %27 = OpVariable %15 Function |
| %32 = OpVariable %15 Function |
| %46 = OpVariable %45 Function |
| %24 = OpAccessChain %23 %20 %22 |
| %25 = OpLoad %17 %24 |
| %26 = OpConvertFToS %14 %25 |
| OpStore %16 %26 |
| %29 = OpAccessChain %23 %20 %28 |
| %30 = OpLoad %17 %29 |
| %31 = OpConvertFToS %14 %30 |
| OpStore %27 %31 |
| OpStore %32 %26 |
| OpBranch %34 |
| %34 = OpLabel |
| %230 = OpPhi %14 %26 %7 %78 %37 |
| OpLoopMerge %36 %37 None |
| OpBranch %38 |
| %38 = OpLabel |
| %42 = OpSLessThan %41 %230 %31 |
| OpBranchConditional %42 %35 %36 |
| %35 = OpLabel |
| %49 = OpIAdd %14 %230 %26 |
| %51 = OpIAdd %14 %49 %31 |
| %54 = OpIAdd %14 %230 %26 |
| %57 = OpIMul %14 %55 %31 |
| %58 = OpIAdd %14 %54 %57 |
| %59 = OpAccessChain %15 %46 %58 |
| %60 = OpLoad %14 %59 |
| %61 = OpAccessChain %15 %46 %51 |
| OpStore %61 %60 |
| %64 = OpIAdd %14 %230 %26 |
| %66 = OpIMul %14 %55 %31 |
| %67 = OpIAdd %14 %64 %66 |
| %70 = OpIAdd %14 %230 %26 |
| %72 = OpIAdd %14 %70 %31 |
| %73 = OpAccessChain %15 %46 %72 |
| %74 = OpLoad %14 %73 |
| %75 = OpAccessChain %15 %46 %67 |
| OpStore %75 %74 |
| OpBranch %37 |
| %37 = OpLabel |
| %78 = OpIAdd %14 %230 %77 |
| OpStore %32 %78 |
| OpBranch %34 |
| %36 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %8 = OpFunction %2 None %3 |
| %9 = OpLabel |
| %79 = OpVariable %15 Function |
| %83 = OpVariable %15 Function |
| %87 = OpVariable %15 Function |
| %97 = OpVariable %45 Function |
| %80 = OpAccessChain %23 %20 %22 |
| %81 = OpLoad %17 %80 |
| %82 = OpConvertFToS %14 %81 |
| OpStore %79 %82 |
| %84 = OpAccessChain %23 %20 %28 |
| %85 = OpLoad %17 %84 |
| %86 = OpConvertFToS %14 %85 |
| OpStore %83 %86 |
| OpStore %87 %82 |
| OpBranch %89 |
| %89 = OpLabel |
| %231 = OpPhi %14 %82 %9 %127 %92 |
| OpLoopMerge %91 %92 None |
| OpBranch %93 |
| %93 = OpLabel |
| %96 = OpSLessThanEqual %41 %231 %86 |
| OpBranchConditional %96 %90 %91 |
| %90 = OpLabel |
| %100 = OpIAdd %14 %231 %82 |
| %102 = OpIAdd %14 %100 %86 |
| %105 = OpIAdd %14 %231 %82 |
| %107 = OpIMul %14 %55 %86 |
| %108 = OpIAdd %14 %105 %107 |
| %109 = OpAccessChain %15 %97 %108 |
| %110 = OpLoad %14 %109 |
| %111 = OpAccessChain %15 %97 %102 |
| OpStore %111 %110 |
| %114 = OpIAdd %14 %231 %82 |
| %116 = OpIMul %14 %55 %86 |
| %117 = OpIAdd %14 %114 %116 |
| %120 = OpIAdd %14 %231 %82 |
| %122 = OpIAdd %14 %120 %86 |
| %123 = OpAccessChain %15 %97 %122 |
| %124 = OpLoad %14 %123 |
| %125 = OpAccessChain %15 %97 %117 |
| OpStore %125 %124 |
| OpBranch %92 |
| %92 = OpLabel |
| %127 = OpIAdd %14 %231 %77 |
| OpStore %87 %127 |
| OpBranch %89 |
| %91 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %10 = OpFunction %2 None %3 |
| %11 = OpLabel |
| %128 = OpVariable %15 Function |
| %132 = OpVariable %15 Function |
| %136 = OpVariable %15 Function |
| %146 = OpVariable %45 Function |
| %129 = OpAccessChain %23 %20 %22 |
| %130 = OpLoad %17 %129 |
| %131 = OpConvertFToS %14 %130 |
| OpStore %128 %131 |
| %133 = OpAccessChain %23 %20 %28 |
| %134 = OpLoad %17 %133 |
| %135 = OpConvertFToS %14 %134 |
| OpStore %132 %135 |
| OpStore %136 %131 |
| OpBranch %138 |
| %138 = OpLabel |
| %232 |