1fd4e5da5Sopenharmony_ci// Copyright (c) 2017 Google Inc.
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#include "source/opt/propagator.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include <map>
18fd4e5da5Sopenharmony_ci#include <memory>
19fd4e5da5Sopenharmony_ci#include <vector>
20fd4e5da5Sopenharmony_ci
21fd4e5da5Sopenharmony_ci#include "gmock/gmock.h"
22fd4e5da5Sopenharmony_ci#include "gtest/gtest.h"
23fd4e5da5Sopenharmony_ci#include "source/opt/build_module.h"
24fd4e5da5Sopenharmony_ci#include "source/opt/cfg.h"
25fd4e5da5Sopenharmony_ci#include "source/opt/ir_context.h"
26fd4e5da5Sopenharmony_ci
27fd4e5da5Sopenharmony_cinamespace spvtools {
28fd4e5da5Sopenharmony_cinamespace opt {
29fd4e5da5Sopenharmony_cinamespace {
30fd4e5da5Sopenharmony_ci
31fd4e5da5Sopenharmony_ciusing ::testing::UnorderedElementsAre;
32fd4e5da5Sopenharmony_ci
33fd4e5da5Sopenharmony_ciclass PropagatorTest : public testing::Test {
34fd4e5da5Sopenharmony_ci protected:
35fd4e5da5Sopenharmony_ci  virtual void TearDown() {
36fd4e5da5Sopenharmony_ci    ctx_.reset(nullptr);
37fd4e5da5Sopenharmony_ci    values_.clear();
38fd4e5da5Sopenharmony_ci    values_vec_.clear();
39fd4e5da5Sopenharmony_ci  }
40fd4e5da5Sopenharmony_ci
41fd4e5da5Sopenharmony_ci  void Assemble(const std::string& input) {
42fd4e5da5Sopenharmony_ci    ctx_ = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input);
43fd4e5da5Sopenharmony_ci    ASSERT_NE(nullptr, ctx_) << "Assembling failed for shader:\n"
44fd4e5da5Sopenharmony_ci                             << input << "\n";
45fd4e5da5Sopenharmony_ci  }
46fd4e5da5Sopenharmony_ci
47fd4e5da5Sopenharmony_ci  bool Propagate(const SSAPropagator::VisitFunction& visit_fn) {
48fd4e5da5Sopenharmony_ci    SSAPropagator propagator(ctx_.get(), visit_fn);
49fd4e5da5Sopenharmony_ci    bool retval = false;
50fd4e5da5Sopenharmony_ci    for (auto& fn : *ctx_->module()) {
51fd4e5da5Sopenharmony_ci      retval |= propagator.Run(&fn);
52fd4e5da5Sopenharmony_ci    }
53fd4e5da5Sopenharmony_ci    return retval;
54fd4e5da5Sopenharmony_ci  }
55fd4e5da5Sopenharmony_ci
56fd4e5da5Sopenharmony_ci  const std::vector<uint32_t>& GetValues() {
57fd4e5da5Sopenharmony_ci    values_vec_.clear();
58fd4e5da5Sopenharmony_ci    for (const auto& it : values_) {
59fd4e5da5Sopenharmony_ci      values_vec_.push_back(it.second);
60fd4e5da5Sopenharmony_ci    }
61fd4e5da5Sopenharmony_ci    return values_vec_;
62fd4e5da5Sopenharmony_ci  }
63fd4e5da5Sopenharmony_ci
64fd4e5da5Sopenharmony_ci  std::unique_ptr<IRContext> ctx_;
65fd4e5da5Sopenharmony_ci  std::map<uint32_t, uint32_t> values_;
66fd4e5da5Sopenharmony_ci  std::vector<uint32_t> values_vec_;
67fd4e5da5Sopenharmony_ci};
68fd4e5da5Sopenharmony_ci
69fd4e5da5Sopenharmony_ciTEST_F(PropagatorTest, LocalPropagate) {
70fd4e5da5Sopenharmony_ci  const std::string spv_asm = R"(
71fd4e5da5Sopenharmony_ci               OpCapability Shader
72fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
73fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
74fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %main "main" %outparm
75fd4e5da5Sopenharmony_ci               OpExecutionMode %main OriginUpperLeft
76fd4e5da5Sopenharmony_ci               OpSource GLSL 450
77fd4e5da5Sopenharmony_ci               OpName %main "main"
78fd4e5da5Sopenharmony_ci               OpName %x "x"
79fd4e5da5Sopenharmony_ci               OpName %y "y"
80fd4e5da5Sopenharmony_ci               OpName %z "z"
81fd4e5da5Sopenharmony_ci               OpName %outparm "outparm"
82fd4e5da5Sopenharmony_ci               OpDecorate %outparm Location 0
83fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
84fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %void
85fd4e5da5Sopenharmony_ci        %int = OpTypeInt 32 1
86fd4e5da5Sopenharmony_ci%_ptr_Function_int = OpTypePointer Function %int
87fd4e5da5Sopenharmony_ci      %int_4 = OpConstant %int 4
88fd4e5da5Sopenharmony_ci      %int_3 = OpConstant %int 3
89fd4e5da5Sopenharmony_ci      %int_1 = OpConstant %int 1
90fd4e5da5Sopenharmony_ci%_ptr_Output_int = OpTypePointer Output %int
91fd4e5da5Sopenharmony_ci    %outparm = OpVariable %_ptr_Output_int Output
92fd4e5da5Sopenharmony_ci       %main = OpFunction %void None %3
93fd4e5da5Sopenharmony_ci          %5 = OpLabel
94fd4e5da5Sopenharmony_ci          %x = OpVariable %_ptr_Function_int Function
95fd4e5da5Sopenharmony_ci          %y = OpVariable %_ptr_Function_int Function
96fd4e5da5Sopenharmony_ci          %z = OpVariable %_ptr_Function_int Function
97fd4e5da5Sopenharmony_ci               OpStore %x %int_4
98fd4e5da5Sopenharmony_ci               OpStore %y %int_3
99fd4e5da5Sopenharmony_ci               OpStore %z %int_1
100fd4e5da5Sopenharmony_ci         %20 = OpLoad %int %z
101fd4e5da5Sopenharmony_ci               OpStore %outparm %20
102fd4e5da5Sopenharmony_ci               OpReturn
103fd4e5da5Sopenharmony_ci               OpFunctionEnd
104fd4e5da5Sopenharmony_ci               )";
105fd4e5da5Sopenharmony_ci  Assemble(spv_asm);
106fd4e5da5Sopenharmony_ci
107fd4e5da5Sopenharmony_ci  const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) {
108fd4e5da5Sopenharmony_ci    *dest_bb = nullptr;
109fd4e5da5Sopenharmony_ci    if (instr->opcode() == spv::Op::OpStore) {
110fd4e5da5Sopenharmony_ci      uint32_t lhs_id = instr->GetSingleWordOperand(0);
111fd4e5da5Sopenharmony_ci      uint32_t rhs_id = instr->GetSingleWordOperand(1);
112fd4e5da5Sopenharmony_ci      Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id);
113fd4e5da5Sopenharmony_ci      if (rhs_def->opcode() == spv::Op::OpConstant) {
114fd4e5da5Sopenharmony_ci        uint32_t val = rhs_def->GetSingleWordOperand(2);
115fd4e5da5Sopenharmony_ci        values_[lhs_id] = val;
116fd4e5da5Sopenharmony_ci        return SSAPropagator::kInteresting;
117fd4e5da5Sopenharmony_ci      }
118fd4e5da5Sopenharmony_ci    }
119fd4e5da5Sopenharmony_ci    return SSAPropagator::kVarying;
120fd4e5da5Sopenharmony_ci  };
121fd4e5da5Sopenharmony_ci
122fd4e5da5Sopenharmony_ci  EXPECT_TRUE(Propagate(visit_fn));
123fd4e5da5Sopenharmony_ci  EXPECT_THAT(GetValues(), UnorderedElementsAre(4, 3, 1));
124fd4e5da5Sopenharmony_ci}
125fd4e5da5Sopenharmony_ci
126fd4e5da5Sopenharmony_ciTEST_F(PropagatorTest, PropagateThroughPhis) {
127fd4e5da5Sopenharmony_ci  const std::string spv_asm = R"(
128fd4e5da5Sopenharmony_ci               OpCapability Shader
129fd4e5da5Sopenharmony_ci          %1 = OpExtInstImport "GLSL.std.450"
130fd4e5da5Sopenharmony_ci               OpMemoryModel Logical GLSL450
131fd4e5da5Sopenharmony_ci               OpEntryPoint Fragment %main "main" %x %outparm
132fd4e5da5Sopenharmony_ci               OpExecutionMode %main OriginUpperLeft
133fd4e5da5Sopenharmony_ci               OpSource GLSL 450
134fd4e5da5Sopenharmony_ci               OpName %main "main"
135fd4e5da5Sopenharmony_ci               OpName %x "x"
136fd4e5da5Sopenharmony_ci               OpName %outparm "outparm"
137fd4e5da5Sopenharmony_ci               OpDecorate %x Flat
138fd4e5da5Sopenharmony_ci               OpDecorate %x Location 0
139fd4e5da5Sopenharmony_ci               OpDecorate %outparm Location 0
140fd4e5da5Sopenharmony_ci       %void = OpTypeVoid
141fd4e5da5Sopenharmony_ci          %3 = OpTypeFunction %void
142fd4e5da5Sopenharmony_ci        %int = OpTypeInt 32 1
143fd4e5da5Sopenharmony_ci       %bool = OpTypeBool
144fd4e5da5Sopenharmony_ci%_ptr_Function_int = OpTypePointer Function %int
145fd4e5da5Sopenharmony_ci      %int_4 = OpConstant %int 4
146fd4e5da5Sopenharmony_ci      %int_3 = OpConstant %int 3
147fd4e5da5Sopenharmony_ci      %int_1 = OpConstant %int 1
148fd4e5da5Sopenharmony_ci%_ptr_Input_int = OpTypePointer Input %int
149fd4e5da5Sopenharmony_ci          %x = OpVariable %_ptr_Input_int Input
150fd4e5da5Sopenharmony_ci%_ptr_Output_int = OpTypePointer Output %int
151fd4e5da5Sopenharmony_ci    %outparm = OpVariable %_ptr_Output_int Output
152fd4e5da5Sopenharmony_ci       %main = OpFunction %void None %3
153fd4e5da5Sopenharmony_ci          %4 = OpLabel
154fd4e5da5Sopenharmony_ci          %5 = OpLoad %int %x
155fd4e5da5Sopenharmony_ci          %6 = OpSGreaterThan %bool %5 %int_3
156fd4e5da5Sopenharmony_ci               OpSelectionMerge %25 None
157fd4e5da5Sopenharmony_ci               OpBranchConditional %6 %22 %23
158fd4e5da5Sopenharmony_ci         %22 = OpLabel
159fd4e5da5Sopenharmony_ci          %7 = OpLoad %int %int_4
160fd4e5da5Sopenharmony_ci               OpBranch %25
161fd4e5da5Sopenharmony_ci         %23 = OpLabel
162fd4e5da5Sopenharmony_ci          %8 = OpLoad %int %int_4
163fd4e5da5Sopenharmony_ci               OpBranch %25
164fd4e5da5Sopenharmony_ci         %25 = OpLabel
165fd4e5da5Sopenharmony_ci         %35 = OpPhi %int %7 %22 %8 %23
166fd4e5da5Sopenharmony_ci               OpStore %outparm %35
167fd4e5da5Sopenharmony_ci               OpReturn
168fd4e5da5Sopenharmony_ci               OpFunctionEnd
169fd4e5da5Sopenharmony_ci               )";
170fd4e5da5Sopenharmony_ci
171fd4e5da5Sopenharmony_ci  Assemble(spv_asm);
172fd4e5da5Sopenharmony_ci
173fd4e5da5Sopenharmony_ci  Instruction* phi_instr = nullptr;
174fd4e5da5Sopenharmony_ci  const auto visit_fn = [this, &phi_instr](Instruction* instr,
175fd4e5da5Sopenharmony_ci                                           BasicBlock** dest_bb) {
176fd4e5da5Sopenharmony_ci    *dest_bb = nullptr;
177fd4e5da5Sopenharmony_ci    if (instr->opcode() == spv::Op::OpLoad) {
178fd4e5da5Sopenharmony_ci      uint32_t rhs_id = instr->GetSingleWordOperand(2);
179fd4e5da5Sopenharmony_ci      Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id);
180fd4e5da5Sopenharmony_ci      if (rhs_def->opcode() == spv::Op::OpConstant) {
181fd4e5da5Sopenharmony_ci        uint32_t val = rhs_def->GetSingleWordOperand(2);
182fd4e5da5Sopenharmony_ci        values_[instr->result_id()] = val;
183fd4e5da5Sopenharmony_ci        return SSAPropagator::kInteresting;
184fd4e5da5Sopenharmony_ci      }
185fd4e5da5Sopenharmony_ci    } else if (instr->opcode() == spv::Op::OpPhi) {
186fd4e5da5Sopenharmony_ci      phi_instr = instr;
187fd4e5da5Sopenharmony_ci      SSAPropagator::PropStatus retval;
188fd4e5da5Sopenharmony_ci      for (uint32_t i = 2; i < instr->NumOperands(); i += 2) {
189fd4e5da5Sopenharmony_ci        uint32_t phi_arg_id = instr->GetSingleWordOperand(i);
190fd4e5da5Sopenharmony_ci        auto it = values_.find(phi_arg_id);
191fd4e5da5Sopenharmony_ci        if (it != values_.end()) {
192fd4e5da5Sopenharmony_ci          EXPECT_EQ(it->second, 4u);
193fd4e5da5Sopenharmony_ci          retval = SSAPropagator::kInteresting;
194fd4e5da5Sopenharmony_ci          values_[instr->result_id()] = it->second;
195fd4e5da5Sopenharmony_ci        } else {
196fd4e5da5Sopenharmony_ci          retval = SSAPropagator::kNotInteresting;
197fd4e5da5Sopenharmony_ci          break;
198fd4e5da5Sopenharmony_ci        }
199fd4e5da5Sopenharmony_ci      }
200fd4e5da5Sopenharmony_ci      return retval;
201fd4e5da5Sopenharmony_ci    }
202fd4e5da5Sopenharmony_ci
203fd4e5da5Sopenharmony_ci    return SSAPropagator::kVarying;
204fd4e5da5Sopenharmony_ci  };
205fd4e5da5Sopenharmony_ci
206fd4e5da5Sopenharmony_ci  EXPECT_TRUE(Propagate(visit_fn));
207fd4e5da5Sopenharmony_ci
208fd4e5da5Sopenharmony_ci  // The propagator should've concluded that the Phi instruction has a constant
209fd4e5da5Sopenharmony_ci  // value of 4.
210fd4e5da5Sopenharmony_ci  EXPECT_NE(phi_instr, nullptr);
211fd4e5da5Sopenharmony_ci  EXPECT_EQ(values_[phi_instr->result_id()], 4u);
212fd4e5da5Sopenharmony_ci
213fd4e5da5Sopenharmony_ci  EXPECT_THAT(GetValues(), UnorderedElementsAre(4u, 4u, 4u));
214fd4e5da5Sopenharmony_ci}
215fd4e5da5Sopenharmony_ci
216fd4e5da5Sopenharmony_ci}  // namespace
217fd4e5da5Sopenharmony_ci}  // namespace opt
218fd4e5da5Sopenharmony_ci}  // namespace spvtools
219