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