1fd4e5da5Sopenharmony_ci// Copyright (c) 2018 Google LLC. 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#ifndef SOURCE_OPT_REGISTER_PRESSURE_H_ 16fd4e5da5Sopenharmony_ci#define SOURCE_OPT_REGISTER_PRESSURE_H_ 17fd4e5da5Sopenharmony_ci 18fd4e5da5Sopenharmony_ci#include <unordered_map> 19fd4e5da5Sopenharmony_ci#include <unordered_set> 20fd4e5da5Sopenharmony_ci#include <utility> 21fd4e5da5Sopenharmony_ci#include <vector> 22fd4e5da5Sopenharmony_ci 23fd4e5da5Sopenharmony_ci#include "source/opt/function.h" 24fd4e5da5Sopenharmony_ci#include "source/opt/types.h" 25fd4e5da5Sopenharmony_ci 26fd4e5da5Sopenharmony_cinamespace spvtools { 27fd4e5da5Sopenharmony_cinamespace opt { 28fd4e5da5Sopenharmony_ci 29fd4e5da5Sopenharmony_ciclass IRContext; 30fd4e5da5Sopenharmony_ciclass Loop; 31fd4e5da5Sopenharmony_ciclass LoopDescriptor; 32fd4e5da5Sopenharmony_ci 33fd4e5da5Sopenharmony_ci// Handles the register pressure of a function for different regions (function, 34fd4e5da5Sopenharmony_ci// loop, basic block). It also contains some utilities to foresee the register 35fd4e5da5Sopenharmony_ci// pressure following code transformations. 36fd4e5da5Sopenharmony_ciclass RegisterLiveness { 37fd4e5da5Sopenharmony_ci public: 38fd4e5da5Sopenharmony_ci // Classification of SSA registers. 39fd4e5da5Sopenharmony_ci struct RegisterClass { 40fd4e5da5Sopenharmony_ci analysis::Type* type_; 41fd4e5da5Sopenharmony_ci bool is_uniform_; 42fd4e5da5Sopenharmony_ci 43fd4e5da5Sopenharmony_ci bool operator==(const RegisterClass& rhs) const { 44fd4e5da5Sopenharmony_ci return std::tie(type_, is_uniform_) == 45fd4e5da5Sopenharmony_ci std::tie(rhs.type_, rhs.is_uniform_); 46fd4e5da5Sopenharmony_ci } 47fd4e5da5Sopenharmony_ci }; 48fd4e5da5Sopenharmony_ci 49fd4e5da5Sopenharmony_ci struct RegionRegisterLiveness { 50fd4e5da5Sopenharmony_ci using LiveSet = std::unordered_set<Instruction*>; 51fd4e5da5Sopenharmony_ci using RegClassSetTy = std::vector<std::pair<RegisterClass, size_t>>; 52fd4e5da5Sopenharmony_ci 53fd4e5da5Sopenharmony_ci // SSA register live when entering the basic block. 54fd4e5da5Sopenharmony_ci LiveSet live_in_; 55fd4e5da5Sopenharmony_ci // SSA register live when exiting the basic block. 56fd4e5da5Sopenharmony_ci LiveSet live_out_; 57fd4e5da5Sopenharmony_ci 58fd4e5da5Sopenharmony_ci // Maximum number of required registers. 59fd4e5da5Sopenharmony_ci size_t used_registers_; 60fd4e5da5Sopenharmony_ci // Break down of the number of required registers per class of register. 61fd4e5da5Sopenharmony_ci RegClassSetTy registers_classes_; 62fd4e5da5Sopenharmony_ci 63fd4e5da5Sopenharmony_ci void Clear() { 64fd4e5da5Sopenharmony_ci live_out_.clear(); 65fd4e5da5Sopenharmony_ci live_in_.clear(); 66fd4e5da5Sopenharmony_ci used_registers_ = 0; 67fd4e5da5Sopenharmony_ci registers_classes_.clear(); 68fd4e5da5Sopenharmony_ci } 69fd4e5da5Sopenharmony_ci 70fd4e5da5Sopenharmony_ci void AddRegisterClass(const RegisterClass& reg_class) { 71fd4e5da5Sopenharmony_ci auto it = std::find_if( 72fd4e5da5Sopenharmony_ci registers_classes_.begin(), registers_classes_.end(), 73fd4e5da5Sopenharmony_ci [®_class](const std::pair<RegisterClass, size_t>& class_count) { 74fd4e5da5Sopenharmony_ci return class_count.first == reg_class; 75fd4e5da5Sopenharmony_ci }); 76fd4e5da5Sopenharmony_ci if (it != registers_classes_.end()) { 77fd4e5da5Sopenharmony_ci it->second++; 78fd4e5da5Sopenharmony_ci } else { 79fd4e5da5Sopenharmony_ci registers_classes_.emplace_back(std::move(reg_class), 80fd4e5da5Sopenharmony_ci static_cast<size_t>(1)); 81fd4e5da5Sopenharmony_ci } 82fd4e5da5Sopenharmony_ci } 83fd4e5da5Sopenharmony_ci 84fd4e5da5Sopenharmony_ci void AddRegisterClass(Instruction* insn); 85fd4e5da5Sopenharmony_ci }; 86fd4e5da5Sopenharmony_ci 87fd4e5da5Sopenharmony_ci RegisterLiveness(IRContext* context, Function* f) : context_(context) { 88fd4e5da5Sopenharmony_ci Analyze(f); 89fd4e5da5Sopenharmony_ci } 90fd4e5da5Sopenharmony_ci 91fd4e5da5Sopenharmony_ci // Returns liveness and register information for the basic block |bb|. If no 92fd4e5da5Sopenharmony_ci // entry exist for the basic block, the function returns null. 93fd4e5da5Sopenharmony_ci const RegionRegisterLiveness* Get(const BasicBlock* bb) const { 94fd4e5da5Sopenharmony_ci return Get(bb->id()); 95fd4e5da5Sopenharmony_ci } 96fd4e5da5Sopenharmony_ci 97fd4e5da5Sopenharmony_ci // Returns liveness and register information for the basic block id |bb_id|. 98fd4e5da5Sopenharmony_ci // If no entry exist for the basic block, the function returns null. 99fd4e5da5Sopenharmony_ci const RegionRegisterLiveness* Get(uint32_t bb_id) const { 100fd4e5da5Sopenharmony_ci RegionRegisterLivenessMap::const_iterator it = block_pressure_.find(bb_id); 101fd4e5da5Sopenharmony_ci if (it != block_pressure_.end()) { 102fd4e5da5Sopenharmony_ci return &it->second; 103fd4e5da5Sopenharmony_ci } 104fd4e5da5Sopenharmony_ci return nullptr; 105fd4e5da5Sopenharmony_ci } 106fd4e5da5Sopenharmony_ci 107fd4e5da5Sopenharmony_ci IRContext* GetContext() const { return context_; } 108fd4e5da5Sopenharmony_ci 109fd4e5da5Sopenharmony_ci // Returns liveness and register information for the basic block |bb|. If no 110fd4e5da5Sopenharmony_ci // entry exist for the basic block, the function returns null. 111fd4e5da5Sopenharmony_ci RegionRegisterLiveness* Get(const BasicBlock* bb) { return Get(bb->id()); } 112fd4e5da5Sopenharmony_ci 113fd4e5da5Sopenharmony_ci // Returns liveness and register information for the basic block id |bb_id|. 114fd4e5da5Sopenharmony_ci // If no entry exist for the basic block, the function returns null. 115fd4e5da5Sopenharmony_ci RegionRegisterLiveness* Get(uint32_t bb_id) { 116fd4e5da5Sopenharmony_ci RegionRegisterLivenessMap::iterator it = block_pressure_.find(bb_id); 117fd4e5da5Sopenharmony_ci if (it != block_pressure_.end()) { 118fd4e5da5Sopenharmony_ci return &it->second; 119fd4e5da5Sopenharmony_ci } 120fd4e5da5Sopenharmony_ci return nullptr; 121fd4e5da5Sopenharmony_ci } 122fd4e5da5Sopenharmony_ci 123fd4e5da5Sopenharmony_ci // Returns liveness and register information for the basic block id |bb_id| or 124fd4e5da5Sopenharmony_ci // create a new empty entry if no entry already existed. 125fd4e5da5Sopenharmony_ci RegionRegisterLiveness* GetOrInsert(uint32_t bb_id) { 126fd4e5da5Sopenharmony_ci return &block_pressure_[bb_id]; 127fd4e5da5Sopenharmony_ci } 128fd4e5da5Sopenharmony_ci 129fd4e5da5Sopenharmony_ci // Compute the register pressure for the |loop| and store the result into 130fd4e5da5Sopenharmony_ci // |reg_pressure|. The live-in set corresponds to the live-in set of the 131fd4e5da5Sopenharmony_ci // header block, the live-out set of the loop corresponds to the union of the 132fd4e5da5Sopenharmony_ci // live-in sets of each exit basic block. 133fd4e5da5Sopenharmony_ci void ComputeLoopRegisterPressure(const Loop& loop, 134fd4e5da5Sopenharmony_ci RegionRegisterLiveness* reg_pressure) const; 135fd4e5da5Sopenharmony_ci 136fd4e5da5Sopenharmony_ci // Estimate the register pressure for the |l1| and |l2| as if they were making 137fd4e5da5Sopenharmony_ci // one unique loop. The result is stored into |simulation_result|. 138fd4e5da5Sopenharmony_ci void SimulateFusion(const Loop& l1, const Loop& l2, 139fd4e5da5Sopenharmony_ci RegionRegisterLiveness* simulation_result) const; 140fd4e5da5Sopenharmony_ci 141fd4e5da5Sopenharmony_ci // Estimate the register pressure of |loop| after it has been fissioned 142fd4e5da5Sopenharmony_ci // according to |moved_instructions| and |copied_instructions|. The function 143fd4e5da5Sopenharmony_ci // assumes that the fission creates a new loop before |loop|, moves any 144fd4e5da5Sopenharmony_ci // instructions present inside |moved_instructions| and copies any 145fd4e5da5Sopenharmony_ci // instructions present inside |copied_instructions| into this new loop. 146fd4e5da5Sopenharmony_ci // The set |loop1_sim_result| store the simulation result of the loop with the 147fd4e5da5Sopenharmony_ci // moved instructions. The set |loop2_sim_result| store the simulation result 148fd4e5da5Sopenharmony_ci // of the loop with the removed instructions. 149fd4e5da5Sopenharmony_ci void SimulateFission( 150fd4e5da5Sopenharmony_ci const Loop& loop, 151fd4e5da5Sopenharmony_ci const std::unordered_set<Instruction*>& moved_instructions, 152fd4e5da5Sopenharmony_ci const std::unordered_set<Instruction*>& copied_instructions, 153fd4e5da5Sopenharmony_ci RegionRegisterLiveness* loop1_sim_result, 154fd4e5da5Sopenharmony_ci RegionRegisterLiveness* loop2_sim_result) const; 155fd4e5da5Sopenharmony_ci 156fd4e5da5Sopenharmony_ci private: 157fd4e5da5Sopenharmony_ci using RegionRegisterLivenessMap = 158fd4e5da5Sopenharmony_ci std::unordered_map<uint32_t, RegionRegisterLiveness>; 159fd4e5da5Sopenharmony_ci 160fd4e5da5Sopenharmony_ci IRContext* context_; 161fd4e5da5Sopenharmony_ci RegionRegisterLivenessMap block_pressure_; 162fd4e5da5Sopenharmony_ci 163fd4e5da5Sopenharmony_ci void Analyze(Function* f); 164fd4e5da5Sopenharmony_ci}; 165fd4e5da5Sopenharmony_ci 166fd4e5da5Sopenharmony_ci// Handles the register pressure of a function for different regions (function, 167fd4e5da5Sopenharmony_ci// loop, basic block). It also contains some utilities to foresee the register 168fd4e5da5Sopenharmony_ci// pressure following code transformations. 169fd4e5da5Sopenharmony_ciclass LivenessAnalysis { 170fd4e5da5Sopenharmony_ci using LivenessAnalysisMap = 171fd4e5da5Sopenharmony_ci std::unordered_map<const Function*, RegisterLiveness>; 172fd4e5da5Sopenharmony_ci 173fd4e5da5Sopenharmony_ci public: 174fd4e5da5Sopenharmony_ci LivenessAnalysis(IRContext* context) : context_(context) {} 175fd4e5da5Sopenharmony_ci 176fd4e5da5Sopenharmony_ci // Computes the liveness analysis for the function |f| and cache the result. 177fd4e5da5Sopenharmony_ci // If the analysis was performed for this function, then the cached analysis 178fd4e5da5Sopenharmony_ci // is returned. 179fd4e5da5Sopenharmony_ci const RegisterLiveness* Get(Function* f) { 180fd4e5da5Sopenharmony_ci LivenessAnalysisMap::iterator it = analysis_cache_.find(f); 181fd4e5da5Sopenharmony_ci if (it != analysis_cache_.end()) { 182fd4e5da5Sopenharmony_ci return &it->second; 183fd4e5da5Sopenharmony_ci } 184fd4e5da5Sopenharmony_ci return &analysis_cache_.emplace(f, RegisterLiveness{context_, f}) 185fd4e5da5Sopenharmony_ci .first->second; 186fd4e5da5Sopenharmony_ci } 187fd4e5da5Sopenharmony_ci 188fd4e5da5Sopenharmony_ci private: 189fd4e5da5Sopenharmony_ci IRContext* context_; 190fd4e5da5Sopenharmony_ci LivenessAnalysisMap analysis_cache_; 191fd4e5da5Sopenharmony_ci}; 192fd4e5da5Sopenharmony_ci 193fd4e5da5Sopenharmony_ci} // namespace opt 194fd4e5da5Sopenharmony_ci} // namespace spvtools 195fd4e5da5Sopenharmony_ci 196fd4e5da5Sopenharmony_ci#endif // SOURCE_OPT_REGISTER_PRESSURE_H_ 197