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