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          [&reg_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