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