#include "gpu/intel/jit/grf_usage.hpp"
#include "gemmstone/../../dsl/ir/codegen/allocation_size.hpp"
#include "gpu/intel/jit/ir/legacy.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace jit {
std::string to_string(grf_usage_label_t label) {
switch (label) {
#define CASE(l) \
case grf_usage_label_t::l: return #l;
CASE(unknown)
CASE(gmem_load)
CASE(out_buf)
CASE(reorder)
CASE(reserved)
CASE(reused_headers)
CASE(slm_load)
CASE(slm_store)
CASE(tmp_vars)
CASE(zero_points)
#undef CASE
default: gpu_error_not_expected();
}
return "";
}
std::ostream &operator<<(std::ostream &out, grf_usage_label_t label) {
out << to_string(label);
return out;
}
std::string grf_buf_usage_t::str() const {
ostringstream_t oss;
oss << "Buffers:";
for (auto label : all_grf_usage_labels()) {
int regs = total_regs(label);
if (regs == 0) continue;
oss << std::endl << " " << label << " (" << regs << "): ";
bool is_first = true;
for (auto &buf : sorted_bufs()) {
if (get_label(buf) != label) continue;
if (!is_first) oss << ", ";
is_first = false;
oss << buf << "[" << get_size(buf) << "]";
}
}
return oss.str();
}
std::string grf_usage_t::str() const {
using intel::jit::operator<<;
std::vector<std::string> headers = {"Label", "Regs"};
ir_utils::table_t table("GRF usage", headers);
int total = 0;
for (auto label : all_grf_usage_labels()) {
int regs = regs_.at(label);
if (regs == 0) continue;
table << to_string(label) << regs << std::endl;
total += regs;
}
table << "Total" << total << std::endl;
ostringstream_t oss;
oss << table << std::endl;
oss << buf_usage_;
return oss.str();
}
class ir_usage_analyzer_t : public ir_visitor_t {
public:
ir_usage_analyzer_t(int grf_size)
: grf_size_(grf_size), buf_usage_(grf_size) {}
void analyze(const stmt_t &stmt, bool allow_errors = false) {
visit(stmt);
if (!is_invalid_) {
if (peak_headers_ <= 1) {
std::vector<expr_t> header_bufs;
for (auto &buf : buf_usage_.bufs()) {
if (is_header(buf)) header_bufs.push_back(buf);
}
int peak_header_usage_ = 0;
for (auto &buf : header_bufs) {
peak_header_usage_ = std::max(
peak_header_usage_, buf_usage_.get_size(buf));
buf_usage_.remove(buf);
}
}
}
if (!verify(allow_errors)) is_invalid_ = true;
}
void _visit(const alloc_t &obj) override {
if (is_invalid_) return;
int size = (obj.kind == alloc_kind_t::grf ? obj.size : 0);
size = ir::register_size(obj, grf_size_);
ir::mem_usage_guard_t alloc_guard(
&alloc_usage_, &peak_alloc_usage_, size);
ir::mem_usage_guard_t guard(&grf_usage_, &peak_grf_usage_, size);
if (size > 0) {
buf_usage_.add(obj.buf, obj.size, grf_usage_label_t::unknown);
mark_known_bufs(obj.buf);
}
ir::mem_usage_guard_t header_guard;
if (is_header(obj.buf))
header_guard = ir::mem_usage_guard_t(&headers_, &peak_headers_, 1);
ir_visitor_t::_visit(obj);
}
void _visit(const func_call_t &obj) override {
if (is_invalid_) return;
auto &func = obj.func;
if (auto *reorder = func.as_ptr<reorder_t>()) {
auto &src = get_base(reorder_t::arg_src_buf(obj));
auto &dst = get_base(reorder_t::arg_dst_buf(obj));
mark_bufs(*reorder, src, dst);
} else if (auto *send = func.as_ptr<send_t>()) {
if (!send_t::arg_header_buf(obj).is<var_t>()) {
is_invalid_ = true;
return;
}
auto &buf = get_base(send_t::arg_reg_buf(obj));
auto &header = get_base(send_t::arg_header_buf(obj));
mark_bufs(*send, buf, header);
} else if (is_func_call<dpas_t>(obj)) {
auto &dst = get_base(dpas_t::arg_dst(obj));
auto &src1 = get_base(dpas_t::arg_src1(obj));
auto &src2 = get_base(dpas_t::arg_src2(obj));
mark_fma_bufs(dst, src1, src2);
} else if (is_func_call<mad_t>(obj)) {
auto &dst = get_base(mad_t::arg_dst(obj));
auto &src1 = get_base(mad_t::arg_src1(obj));
auto &src2 = get_base(mad_t::arg_src2(obj));
mark_fma_bufs(dst, src1, src2);
}
}
void _visit(const let_t &obj) override {
if (is_invalid_) return;
int size = (obj.value.is_empty() ? 0 : obj.var.type().size());
size = ir::register_size(obj);
ir::mem_usage_guard_t guard(&grf_usage_, &peak_grf_usage_, size);
ir_visitor_t::_visit(obj);
}
void _visit(const stmt_group_t &obj) override {
if (is_invalid_) return;
if (obj.label == stmt_label_t::c_store()) {
return;
}
ir_visitor_t::_visit(obj);
}
void _visit(const store_t &obj) override {
if (is_invalid_) return;
auto loads = find_objects<load_t>(obj);
for (auto &l : loads) {
if (obj.buf.is_same(l.as<load_t>().buf)) {
set_label(obj.buf, grf_usage_label_t::tmp_vars);
break;
}
}
ir_visitor_t::_visit(obj);
}
grf_usage_t get_grf_usage(int external_regs) const {
if (is_invalid_) return grf_usage_t();
grf_usage_t info(grf_size_);
info.add(buf_usage_);
info.add(grf_usage_label_t::reserved, external_regs);
info.add(grf_usage_label_t::tmp_vars,
utils::div_up(peak_grf_usage_ - peak_alloc_usage_, grf_size_));
if (peak_headers_ <= 1) {
info.add(grf_usage_label_t::reused_headers, peak_header_usage_);
}
return info;
}
private:
bool verify(bool allow_errors) const {
if (is_invalid_) {
if (!allow_errors)
gpu_error_not_expected() << "Can't collect GRF usage.";
return false;
}
for (auto &buf : buf_usage_.bufs()) {
if (buf_usage_.get_label(buf) != grf_usage_label_t::unknown)
continue;
if (!allow_errors)
gpu_error_not_expected()
<< "Buffer doesn't have label: " << buf;
return false;
}
return true;
}
bool is_buffer(const expr_t &buf) const { return buf_usage_.has(buf); }
bool is_header(const expr_t &buf) const {
if (!is_buffer(buf)) return false;
auto &name = buf.as<var_t>().name;
return name == "h" || name.find("h_") == 0;
}
bool should_skip_if_set(const expr_t &buf, grf_usage_label_t label) const {
if (is_known_buf(buf)) return true;
switch (label) {
case grf_usage_label_t::tmp_vars:
case grf_usage_label_t::slm_store: return true;
default: return false;
}
}
void set_label(const expr_t &buf, grf_usage_label_t label) {
if (is_invalid_) return;
bool skip_if_set = should_skip_if_set(buf, label);
auto buf_label = buf_usage_.get_label(buf);
if (utils::one_of(buf_label, grf_usage_label_t::unknown, label)) {
buf_usage_.set_label(buf, label);
} else {
if (skip_if_set) return;
gpu_error_not_expected()
<< "Label already set. Buffer: " << buf
<< ", old label: " << buf_label << ", new label: " << label;
}
}
void mark_known_bufs(const expr_t &buf) {
if (is_invalid_) return;
gpu_assert(is_buffer(buf));
auto &name = buf.as<var_t>().name;
if (name.find("x_reduce") == 0) {
set_label(buf, grf_usage_label_t::out_buf);
} else if (name.find("zp_") == 0) {
set_label(buf, grf_usage_label_t::zero_points);
}
}
bool is_known_buf(const expr_t &buf) const {
gpu_assert(is_buffer(buf));
auto &name = buf.as<var_t>().name;
if (name.find("zp_") == 0) return true;
if (name.find("x_reduce") == 0) return true;
return false;
}
void mark_bufs(
const reorder_t &reorder, const expr_t &src, const expr_t &dst) {
if (is_invalid_) return;
gpu_assert(is_buffer(src));
gpu_assert(is_buffer(dst));
if (src.is_same(dst)) return;
set_label(dst, grf_usage_label_t::reorder);
}
void mark_bufs(
const send_t &send, const expr_t &buf, const expr_t &header) {
if (is_invalid_) return;
if (buf) gpu_assert(is_buffer(buf));
gpu_assert(is_buffer(header));
gpu_assert(is_header(header));
grf_usage_label_t label = grf_usage_label_t::unknown;
if (buf.is_empty()) {
label = grf_usage_label_t::gmem_load;
} else if (buf_usage_.get_label(buf)
== grf_usage_label_t::zero_points) {
label = grf_usage_label_t::zero_points;
} else if (send.is_slm()) {
label = (send.is_load() ? grf_usage_label_t::slm_load
: grf_usage_label_t::slm_store);
} else {
if (!send.is_load() && !send.is_load_2d()) {
is_invalid_ = true;
return;
}
label = grf_usage_label_t::gmem_load;
}
if (buf) set_label(buf, label);
set_label(header, label);
}
void mark_fma_bufs(
const expr_t &dst, const expr_t &src1, const expr_t &src2) {
if (is_invalid_) return;
gpu_assert(is_buffer(dst));
gpu_assert(is_buffer(src1));
gpu_assert(is_buffer(src2));
set_label(dst, grf_usage_label_t::out_buf);
}
int grf_size_;
bool is_invalid_ = false;
grf_buf_usage_t buf_usage_;
int grf_usage_ = 0;
int alloc_usage_ = 0;
int peak_grf_usage_ = 0;
int peak_alloc_usage_ = 0;
int headers_ = 0;
int peak_headers_ = 0;
int peak_header_usage_ = 0; };
grf_usage_t get_grf_usage(
const stmt_t &body, int grf_size, int external_usage) {
ir_usage_analyzer_t analyzer(grf_size);
analyzer.visit(body);
return analyzer.get_grf_usage(external_usage);
}
} } } } }