2018-04-17 14:31:09 +00:00
|
|
|
// 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.
|
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/register_pressure.h"
|
2018-04-17 14:31:09 +00:00
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include <algorithm>
|
2018-04-17 14:31:09 +00:00
|
|
|
#include <iterator>
|
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/cfg.h"
|
|
|
|
#include "source/opt/def_use_manager.h"
|
|
|
|
#include "source/opt/dominator_tree.h"
|
|
|
|
#include "source/opt/function.h"
|
|
|
|
#include "source/opt/ir_context.h"
|
|
|
|
#include "source/opt/iterator.h"
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// Predicate for the FilterIterator to only consider instructions that are not
|
|
|
|
// phi instructions defined in the basic block |bb|.
|
|
|
|
class ExcludePhiDefinedInBlock {
|
|
|
|
public:
|
2018-07-12 19:14:43 +00:00
|
|
|
ExcludePhiDefinedInBlock(IRContext* context, const BasicBlock* bb)
|
2018-04-17 14:31:09 +00:00
|
|
|
: context_(context), bb_(bb) {}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool operator()(Instruction* insn) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
return !(insn->opcode() == spv::Op::OpPhi &&
|
2018-04-17 14:31:09 +00:00
|
|
|
context_->get_instr_block(insn) == bb_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-07-12 19:14:43 +00:00
|
|
|
IRContext* context_;
|
|
|
|
const BasicBlock* bb_;
|
2018-04-17 14:31:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Returns true if |insn| generates a SSA register that is likely to require a
|
|
|
|
// physical register.
|
2018-07-12 19:14:43 +00:00
|
|
|
bool CreatesRegisterUsage(Instruction* insn) {
|
2018-04-17 14:31:09 +00:00
|
|
|
if (!insn->HasResultId()) return false;
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn->opcode() == spv::Op::OpUndef) return false;
|
2018-07-12 19:14:43 +00:00
|
|
|
if (IsConstantInst(insn->opcode())) return false;
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn->opcode() == spv::Op::OpLabel) return false;
|
2018-04-17 14:31:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the register liveness for each basic block of a function. This also
|
|
|
|
// fill-up some information about the pick register usage and a break down of
|
|
|
|
// register usage. This implements: "A non-iterative data-flow algorithm for
|
|
|
|
// computing liveness sets in strict ssa programs" from Boissinot et al.
|
|
|
|
class ComputeRegisterLiveness {
|
|
|
|
public:
|
2018-07-12 19:14:43 +00:00
|
|
|
ComputeRegisterLiveness(RegisterLiveness* reg_pressure, Function* f)
|
2018-04-17 14:31:09 +00:00
|
|
|
: reg_pressure_(reg_pressure),
|
|
|
|
context_(reg_pressure->GetContext()),
|
|
|
|
function_(f),
|
|
|
|
cfg_(*reg_pressure->GetContext()->cfg()),
|
|
|
|
def_use_manager_(*reg_pressure->GetContext()->get_def_use_mgr()),
|
|
|
|
dom_tree_(
|
|
|
|
reg_pressure->GetContext()->GetDominatorAnalysis(f)->GetDomTree()),
|
|
|
|
loop_desc_(*reg_pressure->GetContext()->GetLoopDescriptor(f)) {}
|
|
|
|
|
|
|
|
// Computes the register liveness for |function_| and then estimate the
|
|
|
|
// register usage. The liveness algorithm works in 2 steps:
|
|
|
|
// - First, compute the liveness for each basic blocks, but will ignore any
|
|
|
|
// back-edge;
|
|
|
|
// - Second, walk loop forest to propagate registers crossing back-edges
|
|
|
|
// (add iterative values into the liveness set).
|
|
|
|
void Compute() {
|
2019-11-27 14:45:17 +00:00
|
|
|
for (BasicBlock& start_bb : *function_) {
|
|
|
|
if (reg_pressure_->Get(start_bb.id()) != nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
|
|
|
|
if (reg_pressure_->Get(bb->id()) == nullptr) {
|
|
|
|
ComputePartialLiveness(bb);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-04-17 14:31:09 +00:00
|
|
|
DoLoopLivenessUnification();
|
|
|
|
EvaluateRegisterRequirements();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Registers all SSA register used by successors of |bb| in their phi
|
|
|
|
// instructions.
|
2018-07-12 19:14:43 +00:00
|
|
|
void ComputePhiUses(const BasicBlock& bb,
|
2018-04-17 14:31:09 +00:00
|
|
|
RegisterLiveness::RegionRegisterLiveness::LiveSet* live) {
|
|
|
|
uint32_t bb_id = bb.id();
|
|
|
|
bb.ForEachSuccessorLabel([live, bb_id, this](uint32_t sid) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* succ_bb = cfg_.block(sid);
|
|
|
|
succ_bb->ForEachPhiInst([live, bb_id, this](const Instruction* phi) {
|
2018-04-17 14:31:09 +00:00
|
|
|
for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
|
|
|
|
if (phi->GetSingleWordInOperand(i + 1) == bb_id) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* insn_op =
|
2018-04-17 14:31:09 +00:00
|
|
|
def_use_manager_.GetDef(phi->GetSingleWordInOperand(i));
|
|
|
|
if (CreatesRegisterUsage(insn_op)) {
|
|
|
|
live->insert(insn_op);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Computes register liveness for each basic blocks but ignores all
|
|
|
|
// back-edges.
|
2018-07-12 19:14:43 +00:00
|
|
|
void ComputePartialLiveness(BasicBlock* bb) {
|
2018-04-17 14:31:09 +00:00
|
|
|
assert(reg_pressure_->Get(bb) == nullptr &&
|
|
|
|
"Basic block already processed");
|
|
|
|
|
|
|
|
RegisterLiveness::RegionRegisterLiveness* live_inout =
|
|
|
|
reg_pressure_->GetOrInsert(bb->id());
|
|
|
|
ComputePhiUses(*bb, &live_inout->live_out_);
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
const BasicBlock* cbb = bb;
|
2018-04-17 14:31:09 +00:00
|
|
|
cbb->ForEachSuccessorLabel([&live_inout, bb, this](uint32_t sid) {
|
|
|
|
// Skip back edges.
|
|
|
|
if (dom_tree_.Dominates(sid, bb->id())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* succ_bb = cfg_.block(sid);
|
2018-04-17 14:31:09 +00:00
|
|
|
RegisterLiveness::RegionRegisterLiveness* succ_live_inout =
|
|
|
|
reg_pressure_->Get(succ_bb);
|
|
|
|
assert(succ_live_inout &&
|
|
|
|
"Successor liveness analysis was not performed");
|
|
|
|
|
|
|
|
ExcludePhiDefinedInBlock predicate(context_, succ_bb);
|
2018-07-12 19:14:43 +00:00
|
|
|
auto filter =
|
|
|
|
MakeFilterIteratorRange(succ_live_inout->live_in_.begin(),
|
|
|
|
succ_live_inout->live_in_.end(), predicate);
|
2018-04-17 14:31:09 +00:00
|
|
|
live_inout->live_out_.insert(filter.begin(), filter.end());
|
|
|
|
});
|
|
|
|
|
|
|
|
live_inout->live_in_ = live_inout->live_out_;
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi) {
|
2018-04-17 14:31:09 +00:00
|
|
|
live_inout->live_in_.insert(&insn);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
live_inout->live_in_.erase(&insn);
|
|
|
|
insn.ForEachInId([live_inout, this](uint32_t* id) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* insn_op = def_use_manager_.GetDef(*id);
|
2018-04-17 14:31:09 +00:00
|
|
|
if (CreatesRegisterUsage(insn_op)) {
|
|
|
|
live_inout->live_in_.insert(insn_op);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagates the register liveness information of each loop iterators.
|
|
|
|
void DoLoopLivenessUnification() {
|
2020-07-29 17:50:58 +00:00
|
|
|
for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) {
|
2018-04-17 14:31:09 +00:00
|
|
|
DoLoopLivenessUnification(*loop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagates the register liveness information of loop iterators trough-out
|
|
|
|
// the loop body.
|
2018-07-12 19:14:43 +00:00
|
|
|
void DoLoopLivenessUnification(const Loop& loop) {
|
|
|
|
auto blocks_in_loop = MakeFilterIteratorRange(
|
2018-04-17 14:31:09 +00:00
|
|
|
loop.GetBlocks().begin(), loop.GetBlocks().end(),
|
|
|
|
[&loop, this](uint32_t bb_id) {
|
|
|
|
return bb_id != loop.GetHeaderBlock()->id() &&
|
|
|
|
loop_desc_[bb_id] == &loop;
|
|
|
|
});
|
|
|
|
|
|
|
|
RegisterLiveness::RegionRegisterLiveness* header_live_inout =
|
|
|
|
reg_pressure_->Get(loop.GetHeaderBlock());
|
|
|
|
assert(header_live_inout &&
|
|
|
|
"Liveness analysis was not performed for the current block");
|
|
|
|
|
|
|
|
ExcludePhiDefinedInBlock predicate(context_, loop.GetHeaderBlock());
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_loop =
|
|
|
|
MakeFilterIteratorRange(header_live_inout->live_in_.begin(),
|
|
|
|
header_live_inout->live_in_.end(), predicate);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
for (uint32_t bb_id : blocks_in_loop) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* bb = cfg_.block(bb_id);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
RegisterLiveness::RegionRegisterLiveness* live_inout =
|
|
|
|
reg_pressure_->Get(bb);
|
|
|
|
live_inout->live_in_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
live_inout->live_out_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
for (const Loop* inner_loop : loop) {
|
2018-04-17 14:31:09 +00:00
|
|
|
RegisterLiveness::RegionRegisterLiveness* live_inout =
|
|
|
|
reg_pressure_->Get(inner_loop->GetHeaderBlock());
|
|
|
|
live_inout->live_in_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
live_inout->live_out_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
|
|
|
|
DoLoopLivenessUnification(*inner_loop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the number of required registers for this each basic block.
|
|
|
|
void EvaluateRegisterRequirements() {
|
2018-07-12 19:14:43 +00:00
|
|
|
for (BasicBlock& bb : *function_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
RegisterLiveness::RegionRegisterLiveness* live_inout =
|
|
|
|
reg_pressure_->Get(bb.id());
|
|
|
|
assert(live_inout != nullptr && "Basic block not processed");
|
|
|
|
|
|
|
|
size_t reg_count = live_inout->live_out_.size();
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : live_inout->live_out_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
live_inout->AddRegisterClass(insn);
|
|
|
|
}
|
|
|
|
live_inout->used_registers_ = reg_count;
|
|
|
|
|
|
|
|
std::unordered_set<uint32_t> die_in_block;
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : make_range(bb.rbegin(), bb.rend())) {
|
2018-04-17 14:31:09 +00:00
|
|
|
// If it is a phi instruction, the register pressure will not change
|
|
|
|
// anymore.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi) {
|
2018-04-17 14:31:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn.ForEachInId(
|
|
|
|
[live_inout, &die_in_block, ®_count, this](uint32_t* id) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* op_insn = def_use_manager_.GetDef(*id);
|
2018-04-17 14:31:09 +00:00
|
|
|
if (!CreatesRegisterUsage(op_insn) ||
|
|
|
|
live_inout->live_out_.count(op_insn)) {
|
|
|
|
// already taken into account.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!die_in_block.count(*id)) {
|
|
|
|
live_inout->AddRegisterClass(def_use_manager_.GetDef(*id));
|
|
|
|
reg_count++;
|
|
|
|
die_in_block.insert(*id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
live_inout->used_registers_ =
|
|
|
|
std::max(live_inout->used_registers_, reg_count);
|
|
|
|
if (CreatesRegisterUsage(&insn)) {
|
|
|
|
reg_count--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RegisterLiveness* reg_pressure_;
|
2018-07-12 19:14:43 +00:00
|
|
|
IRContext* context_;
|
|
|
|
Function* function_;
|
|
|
|
CFG& cfg_;
|
2018-04-17 14:31:09 +00:00
|
|
|
analysis::DefUseManager& def_use_manager_;
|
|
|
|
DominatorTree& dom_tree_;
|
2018-07-12 19:14:43 +00:00
|
|
|
LoopDescriptor& loop_desc_;
|
2018-04-17 14:31:09 +00:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// Get the number of required registers for each basic block.
|
|
|
|
void RegisterLiveness::RegionRegisterLiveness::AddRegisterClass(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* insn) {
|
2018-04-17 14:31:09 +00:00
|
|
|
assert(CreatesRegisterUsage(insn) && "Instruction does not use a register");
|
|
|
|
analysis::Type* type =
|
|
|
|
insn->context()->get_type_mgr()->GetType(insn->type_id());
|
|
|
|
|
|
|
|
RegisterLiveness::RegisterClass reg_class{type, false};
|
|
|
|
|
|
|
|
insn->context()->get_decoration_mgr()->WhileEachDecoration(
|
2022-11-04 21:27:10 +00:00
|
|
|
insn->result_id(), uint32_t(spv::Decoration::Uniform),
|
2018-07-12 19:14:43 +00:00
|
|
|
[®_class](const Instruction&) {
|
2018-04-17 14:31:09 +00:00
|
|
|
reg_class.is_uniform_ = true;
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
AddRegisterClass(reg_class);
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void RegisterLiveness::Analyze(Function* f) {
|
2018-04-17 14:31:09 +00:00
|
|
|
block_pressure_.clear();
|
|
|
|
ComputeRegisterLiveness(this, f).Compute();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegisterLiveness::ComputeLoopRegisterPressure(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Loop& loop, RegionRegisterLiveness* loop_reg_pressure) const {
|
2018-04-17 14:31:09 +00:00
|
|
|
loop_reg_pressure->Clear();
|
|
|
|
|
|
|
|
const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock());
|
|
|
|
loop_reg_pressure->live_in_ = header_live_inout->live_in_;
|
|
|
|
|
|
|
|
std::unordered_set<uint32_t> exit_blocks;
|
|
|
|
loop.GetExitBlocks(&exit_blocks);
|
|
|
|
|
|
|
|
for (uint32_t bb_id : exit_blocks) {
|
|
|
|
const RegionRegisterLiveness* live_inout = Get(bb_id);
|
|
|
|
loop_reg_pressure->live_out_.insert(live_inout->live_in_.begin(),
|
|
|
|
live_inout->live_in_.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_set<uint32_t> seen_insn;
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : loop_reg_pressure->live_out_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
loop_reg_pressure->AddRegisterClass(insn);
|
|
|
|
seen_insn.insert(insn->result_id());
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : loop_reg_pressure->live_in_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
if (!seen_insn.count(insn->result_id())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
loop_reg_pressure->AddRegisterClass(insn);
|
|
|
|
seen_insn.insert(insn->result_id());
|
|
|
|
}
|
|
|
|
|
|
|
|
loop_reg_pressure->used_registers_ = 0;
|
|
|
|
|
|
|
|
for (uint32_t bb_id : loop.GetBlocks()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* bb = context_->cfg()->block(bb_id);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
const RegionRegisterLiveness* live_inout = Get(bb_id);
|
|
|
|
assert(live_inout != nullptr && "Basic block not processed");
|
|
|
|
loop_reg_pressure->used_registers_ = std::max(
|
|
|
|
loop_reg_pressure->used_registers_, live_inout->used_registers_);
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : *bb) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
|
2018-04-17 14:31:09 +00:00
|
|
|
seen_insn.count(insn.result_id())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
loop_reg_pressure->AddRegisterClass(&insn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegisterLiveness::SimulateFusion(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Loop& l1, const Loop& l2, RegionRegisterLiveness* sim_result) const {
|
2018-04-17 14:31:09 +00:00
|
|
|
sim_result->Clear();
|
|
|
|
|
|
|
|
// Compute the live-in state:
|
|
|
|
// sim_result.live_in = l1.live_in U l2.live_in
|
|
|
|
// This assumes that |l1| does not generated register that is live-out for
|
|
|
|
// |l1|.
|
|
|
|
const RegionRegisterLiveness* l1_header_live_inout = Get(l1.GetHeaderBlock());
|
|
|
|
sim_result->live_in_ = l1_header_live_inout->live_in_;
|
|
|
|
|
|
|
|
const RegionRegisterLiveness* l2_header_live_inout = Get(l2.GetHeaderBlock());
|
|
|
|
sim_result->live_in_.insert(l2_header_live_inout->live_in_.begin(),
|
|
|
|
l2_header_live_inout->live_in_.end());
|
|
|
|
|
|
|
|
// The live-out set of the fused loop is the l2 live-out set.
|
|
|
|
std::unordered_set<uint32_t> exit_blocks;
|
|
|
|
l2.GetExitBlocks(&exit_blocks);
|
|
|
|
|
|
|
|
for (uint32_t bb_id : exit_blocks) {
|
|
|
|
const RegionRegisterLiveness* live_inout = Get(bb_id);
|
|
|
|
sim_result->live_out_.insert(live_inout->live_in_.begin(),
|
|
|
|
live_inout->live_in_.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the register usage information.
|
|
|
|
std::unordered_set<uint32_t> seen_insn;
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : sim_result->live_out_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
sim_result->AddRegisterClass(insn);
|
|
|
|
seen_insn.insert(insn->result_id());
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : sim_result->live_in_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
if (!seen_insn.count(insn->result_id())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sim_result->AddRegisterClass(insn);
|
|
|
|
seen_insn.insert(insn->result_id());
|
|
|
|
}
|
|
|
|
|
|
|
|
sim_result->used_registers_ = 0;
|
|
|
|
|
|
|
|
// The loop fusion is injecting the l1 before the l2, the latch of l1 will be
|
|
|
|
// connected to the header of l2.
|
|
|
|
// To compute the register usage, we inject the loop live-in (union of l1 and
|
2022-01-13 00:04:13 +00:00
|
|
|
// l2 live-in header blocks) into the live in/out of each basic block of
|
2018-04-17 14:31:09 +00:00
|
|
|
// l1 to get the peak register usage. We then repeat the operation to for l2
|
|
|
|
// basic blocks but in this case we inject the live-out of the latch of l1.
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_loop = MakeFilterIteratorRange(
|
2018-04-17 14:31:09 +00:00
|
|
|
sim_result->live_in_.begin(), sim_result->live_in_.end(),
|
2018-07-12 19:14:43 +00:00
|
|
|
[&l1, &l2](Instruction* insn) {
|
|
|
|
BasicBlock* bb = insn->context()->get_instr_block(insn);
|
2018-04-17 14:31:09 +00:00
|
|
|
return insn->HasResultId() &&
|
2022-11-04 21:27:10 +00:00
|
|
|
!(insn->opcode() == spv::Op::OpPhi &&
|
2018-04-17 14:31:09 +00:00
|
|
|
(bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock()));
|
|
|
|
});
|
|
|
|
|
|
|
|
for (uint32_t bb_id : l1.GetBlocks()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* bb = context_->cfg()->block(bb_id);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
const RegionRegisterLiveness* live_inout_info = Get(bb_id);
|
|
|
|
assert(live_inout_info != nullptr && "Basic block not processed");
|
|
|
|
RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_;
|
|
|
|
live_out.insert(live_loop.begin(), live_loop.end());
|
|
|
|
sim_result->used_registers_ =
|
|
|
|
std::max(sim_result->used_registers_,
|
|
|
|
live_inout_info->used_registers_ + live_out.size() -
|
|
|
|
live_inout_info->live_out_.size());
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : *bb) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
|
2018-04-17 14:31:09 +00:00
|
|
|
seen_insn.count(insn.result_id())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sim_result->AddRegisterClass(&insn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const RegionRegisterLiveness* l1_latch_live_inout_info =
|
|
|
|
Get(l1.GetLatchBlock()->id());
|
|
|
|
assert(l1_latch_live_inout_info != nullptr && "Basic block not processed");
|
|
|
|
RegionRegisterLiveness::LiveSet l1_latch_live_out =
|
|
|
|
l1_latch_live_inout_info->live_out_;
|
|
|
|
l1_latch_live_out.insert(live_loop.begin(), live_loop.end());
|
|
|
|
|
|
|
|
auto live_loop_l2 =
|
2018-07-12 19:14:43 +00:00
|
|
|
make_range(l1_latch_live_out.begin(), l1_latch_live_out.end());
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
for (uint32_t bb_id : l2.GetBlocks()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* bb = context_->cfg()->block(bb_id);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
const RegionRegisterLiveness* live_inout_info = Get(bb_id);
|
|
|
|
assert(live_inout_info != nullptr && "Basic block not processed");
|
|
|
|
RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_;
|
|
|
|
live_out.insert(live_loop_l2.begin(), live_loop_l2.end());
|
|
|
|
sim_result->used_registers_ =
|
|
|
|
std::max(sim_result->used_registers_,
|
|
|
|
live_inout_info->used_registers_ + live_out.size() -
|
|
|
|
live_inout_info->live_out_.size());
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : *bb) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
|
2018-04-17 14:31:09 +00:00
|
|
|
seen_insn.count(insn.result_id())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sim_result->AddRegisterClass(&insn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegisterLiveness::SimulateFission(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Loop& loop, const std::unordered_set<Instruction*>& moved_inst,
|
|
|
|
const std::unordered_set<Instruction*>& copied_inst,
|
2018-04-17 14:31:09 +00:00
|
|
|
RegionRegisterLiveness* l1_sim_result,
|
|
|
|
RegionRegisterLiveness* l2_sim_result) const {
|
|
|
|
l1_sim_result->Clear();
|
|
|
|
l2_sim_result->Clear();
|
|
|
|
|
|
|
|
// Filter predicates: consider instructions that only belong to the first and
|
|
|
|
// second loop.
|
2018-07-12 19:14:43 +00:00
|
|
|
auto belong_to_loop1 = [&moved_inst, &copied_inst, &loop](Instruction* insn) {
|
2018-04-17 14:31:09 +00:00
|
|
|
return moved_inst.count(insn) || copied_inst.count(insn) ||
|
|
|
|
!loop.IsInsideLoop(insn);
|
|
|
|
};
|
2018-07-12 19:14:43 +00:00
|
|
|
auto belong_to_loop2 = [&moved_inst](Instruction* insn) {
|
2018-04-17 14:31:09 +00:00
|
|
|
return !moved_inst.count(insn);
|
|
|
|
};
|
|
|
|
|
|
|
|
const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock());
|
|
|
|
// l1 live-in
|
|
|
|
{
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_loop = MakeFilterIteratorRange(
|
2018-04-17 14:31:09 +00:00
|
|
|
header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(),
|
|
|
|
belong_to_loop1);
|
|
|
|
l1_sim_result->live_in_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
}
|
|
|
|
// l2 live-in
|
|
|
|
{
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_loop = MakeFilterIteratorRange(
|
2018-04-17 14:31:09 +00:00
|
|
|
header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(),
|
|
|
|
belong_to_loop2);
|
|
|
|
l2_sim_result->live_in_.insert(live_loop.begin(), live_loop.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_set<uint32_t> exit_blocks;
|
|
|
|
loop.GetExitBlocks(&exit_blocks);
|
|
|
|
|
|
|
|
// l2 live-out.
|
|
|
|
for (uint32_t bb_id : exit_blocks) {
|
|
|
|
const RegionRegisterLiveness* live_inout = Get(bb_id);
|
|
|
|
l2_sim_result->live_out_.insert(live_inout->live_in_.begin(),
|
|
|
|
live_inout->live_in_.end());
|
|
|
|
}
|
|
|
|
// l1 live-out.
|
|
|
|
{
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_out = MakeFilterIteratorRange(l2_sim_result->live_out_.begin(),
|
|
|
|
l2_sim_result->live_out_.end(),
|
|
|
|
belong_to_loop1);
|
2018-04-17 14:31:09 +00:00
|
|
|
l1_sim_result->live_out_.insert(live_out.begin(), live_out.end());
|
|
|
|
}
|
|
|
|
{
|
2018-07-12 19:14:43 +00:00
|
|
|
auto live_out =
|
|
|
|
MakeFilterIteratorRange(l2_sim_result->live_in_.begin(),
|
|
|
|
l2_sim_result->live_in_.end(), belong_to_loop1);
|
2018-04-17 14:31:09 +00:00
|
|
|
l1_sim_result->live_out_.insert(live_out.begin(), live_out.end());
|
|
|
|
}
|
|
|
|
// Lives out of l1 are live out of l2 so are live in of l2 as well.
|
|
|
|
l2_sim_result->live_in_.insert(l1_sim_result->live_out_.begin(),
|
|
|
|
l1_sim_result->live_out_.end());
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : l1_sim_result->live_in_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
l1_sim_result->AddRegisterClass(insn);
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction* insn : l2_sim_result->live_in_) {
|
2018-04-17 14:31:09 +00:00
|
|
|
l2_sim_result->AddRegisterClass(insn);
|
|
|
|
}
|
|
|
|
|
|
|
|
l1_sim_result->used_registers_ = 0;
|
|
|
|
l2_sim_result->used_registers_ = 0;
|
|
|
|
|
|
|
|
for (uint32_t bb_id : loop.GetBlocks()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* bb = context_->cfg()->block(bb_id);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
const RegisterLiveness::RegionRegisterLiveness* live_inout = Get(bb_id);
|
|
|
|
assert(live_inout != nullptr && "Basic block not processed");
|
2018-07-12 19:14:43 +00:00
|
|
|
auto l1_block_live_out =
|
|
|
|
MakeFilterIteratorRange(live_inout->live_out_.begin(),
|
|
|
|
live_inout->live_out_.end(), belong_to_loop1);
|
|
|
|
auto l2_block_live_out =
|
|
|
|
MakeFilterIteratorRange(live_inout->live_out_.begin(),
|
|
|
|
live_inout->live_out_.end(), belong_to_loop2);
|
2018-04-17 14:31:09 +00:00
|
|
|
|
|
|
|
size_t l1_reg_count =
|
|
|
|
std::distance(l1_block_live_out.begin(), l1_block_live_out.end());
|
|
|
|
size_t l2_reg_count =
|
|
|
|
std::distance(l2_block_live_out.begin(), l2_block_live_out.end());
|
|
|
|
|
|
|
|
std::unordered_set<uint32_t> die_in_block;
|
2018-07-12 19:14:43 +00:00
|
|
|
for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (insn.opcode() == spv::Op::OpPhi) {
|
2018-04-17 14:31:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool does_belong_to_loop1 = belong_to_loop1(&insn);
|
|
|
|
bool does_belong_to_loop2 = belong_to_loop2(&insn);
|
|
|
|
insn.ForEachInId([live_inout, &die_in_block, &l1_reg_count, &l2_reg_count,
|
|
|
|
does_belong_to_loop1, does_belong_to_loop2,
|
|
|
|
this](uint32_t* id) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* op_insn = context_->get_def_use_mgr()->GetDef(*id);
|
2018-04-17 14:31:09 +00:00
|
|
|
if (!CreatesRegisterUsage(op_insn) ||
|
|
|
|
live_inout->live_out_.count(op_insn)) {
|
|
|
|
// already taken into account.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!die_in_block.count(*id)) {
|
|
|
|
if (does_belong_to_loop1) {
|
|
|
|
l1_reg_count++;
|
|
|
|
}
|
|
|
|
if (does_belong_to_loop2) {
|
|
|
|
l2_reg_count++;
|
|
|
|
}
|
|
|
|
die_in_block.insert(*id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
l1_sim_result->used_registers_ =
|
|
|
|
std::max(l1_sim_result->used_registers_, l1_reg_count);
|
|
|
|
l2_sim_result->used_registers_ =
|
|
|
|
std::max(l2_sim_result->used_registers_, l2_reg_count);
|
|
|
|
if (CreatesRegisterUsage(&insn)) {
|
|
|
|
if (does_belong_to_loop1) {
|
|
|
|
if (!l1_sim_result->live_in_.count(&insn)) {
|
|
|
|
l1_sim_result->AddRegisterClass(&insn);
|
|
|
|
}
|
|
|
|
l1_reg_count--;
|
|
|
|
}
|
|
|
|
if (does_belong_to_loop2) {
|
|
|
|
if (!l2_sim_result->live_in_.count(&insn)) {
|
|
|
|
l2_sim_result->AddRegisterClass(&insn);
|
|
|
|
}
|
|
|
|
l2_reg_count--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|