#include <cctype>
#include <sstream>
#include <thread>
#include "gpu/intel/jit/ir/tensor.hpp"
#include "gpu/intel/jit/utils/type_bridge.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace jit {
tile_coord_t split(const layout_t &layout, const grid_info_t &grid_info,
grid_info_t *out_grid) {
tile_coord_t min_tile_coord = tile_coord_t::invalid();
std::vector<dim_t> cur_dims(grid_info.ndims(), 1);
for (int iter = 0; iter < grid_info.elems(); iter++) {
for (dim_idx_t i = 0; i < grid_info.ndims(); i++) {
if (++cur_dims[i] <= grid_info.dim(i)) break;
cur_dims[i] = 1;
}
auto sub_grid = grid_info.resize(cur_dims);
auto tile_coord = split_exact(layout, sub_grid);
if (tile_coord.is_invalid()) continue;
if (min_tile_coord.is_invalid()
|| tile_coord.elems() < min_tile_coord.elems()) {
min_tile_coord = std::move(tile_coord);
if (out_grid) { *out_grid = std::move(sub_grid); }
}
}
return min_tile_coord;
}
tile_coord_t split_exact(const layout_t &layout, const grid_info_t &grid) {
tile_t tile;
if (layout.elems() % grid.elems() != 0) return tile_coord_t::invalid();
dim_t cur_elems_per_tile = 1;
dim_t elems_per_tile = layout.elems() / grid.elems();
for (auto &b : layout.blocks()) {
dim_t block = std::min(b.size, elems_per_tile / cur_elems_per_tile);
tile[b.idx] = tile.get(b.idx, 1) * block;
cur_elems_per_tile *= block;
}
if (cur_elems_per_tile != elems_per_tile) return tile_coord_t::invalid();
return split(layout, tile, grid);
}
tile_coord_t split_exact(const layout_t &layout, int factor) {
if (factor == 1) return tile_coord_t(layout.tile());
if (layout.elems() % factor != 0) return tile_coord_t::invalid();
dim_t cur_elems = 1;
dim_t split_elems = layout.elems() / factor;
std::vector<layout_block_t> split_blocks;
for (auto &b : layout.blocks()) {
if (cur_elems * b.size > split_elems) {
if (split_elems % cur_elems != 0) return tile_coord_t::invalid();
auto bb = b;
bb.size = split_elems / cur_elems;
if (b.size % bb.size != 0) return tile_coord_t::invalid();
split_blocks.push_back(bb);
} else {
split_blocks.push_back(b);
}
cur_elems *= split_blocks.back().size;
if (cur_elems == split_elems) break;
}
tile_t split_tile;
for (auto &b : split_blocks)
split_tile[b.idx] = split_tile.get(b.idx, 1) * b.size;
return tile_coord_t(split_tile);
}
tile_coord_t split(const layout_t &layout, const tile_t &tile,
const grid_info_t &grid, std::vector<layout_block_t> *outer_blocks) {
if (outer_blocks) outer_blocks->resize(0);
if (grid.elems() == 1) return tile_coord_t(tile);
dim_t total_elems = layout.elems();
dim_t tile_elems = tile.elems();
grid_splitter_t grid_splitter(grid);
gpu_assert(tile_elems * grid.elems() == total_elems)
<< "Tile/grid dimensions do not match.";
MAYBE_UNUSED(total_elems);
MAYBE_UNUSED(tile_elems);
tile_t dims;
coord_t start;
auto rem_tile = tile;
for (auto &b : layout.blocks()) {
if (b.size == 1) continue;
dim_t &e = rem_tile[b.idx];
if (e > 1) {
if (e % b.size == 0) {
e /= b.size;
} else if (b.size % e == 0) {
auto tmp_layout = layout.split_block(b, e, b.size / e);
return split(tmp_layout, tile, grid, outer_blocks);
} else {
return tile_coord_t::invalid();
}
} else {
dim_t next_chunk = math::gcd(b.size, grid_splitter.cur_block());
if (b.size == next_chunk) {
auto idx = grid_splitter.pop_block(next_chunk);
start[b.idx] += idx * dims[b.idx];
if (outer_blocks) outer_blocks->push_back(b);
} else if (b.size % next_chunk == 0 && next_chunk != 1) {
auto tmp_layout = layout.split_block(
b, next_chunk, b.size / next_chunk);
return split(tmp_layout, tile, grid, outer_blocks);
} else {
return tile_coord_t::invalid();
}
}
dims[b.idx] *= b.size;
}
return tile_coord_t(tile, start);
}
memory_desc_t to_md(const layout_t &l, const memory_desc_t &md_hint) {
auto dims_hint = md_hint.dims;
auto ndims = md_hint.ndims;
memory_desc_t md = {};
md.ndims = ndims;
std::copy(dims_hint, dims_hint + ndims, md.dims);
md.data_type = jit::to_dnnl(l.type());
md.offset0 = to_cpp<dim_t>(l.offset());
md.format_kind = format_kind::blocked;
auto &blk = md.format_desc.blocking;
bool seen[DNNL_MAX_NDIMS] = {};
bool in_inner_block = false;
dim_t prev_stride = 0;
for (auto it = l.blocks().rbegin(); it != l.blocks().rend(); ++it) {
auto &b = *it;
if (!seen[b.idx]) {
gpu_assert(!in_inner_block);
MAYBE_UNUSED(in_inner_block);
blk.strides[b.idx] = dim_t(b.stride);
md.padded_dims[b.idx] = b.size;
} else {
md.padded_dims[b.idx] *= b.size;
blk.inner_idxs[blk.inner_nblks] = b.idx;
blk.inner_blks[blk.inner_nblks] = b.size;
blk.inner_nblks++;
if (prev_stride > 0) {
gpu_assert(prev_stride == b.size * dim_t(b.stride));
}
prev_stride = dim_t(b.stride);
in_inner_block = true;
}
seen[b.idx] = true;
}
for (int i = 0; i < ndims; i++) {
if (seen[i]) continue;
gpu_assert(md.dims[i] == 1);
md.padded_dims[i] = md.dims[i];
blk.strides[i] = l.elems();
}
return md;
}
layout_t reinterpret(const layout_t &layout, const dsl::type_t &new_type,
bool do_normalize) {
int old_size = layout.type().size();
int new_size = new_type.size();
if (new_size == old_size) return layout;
expr_t new_offset = 0;
if (!layout.offset().is(0)) {
gpu_assert(is_const(layout.offset())) << "Expected constant offset.";
int64_t off = to_cpp<int64_t>(layout.offset()) * old_size;
gpu_assert(off % new_size == 0);
new_offset = off / new_size;
}
if (old_size % new_size != 0 && new_size % old_size != 0) {
gpu_error_not_expected();
return layout_t();
}
auto new_blocks = layout.blocks();
if (new_blocks.empty()) {
gpu_error_not_expected() << "Can't reinterpret.";
return layout_t();
}
auto &b0 = new_blocks.front();
if (dim_t(b0.stride) != 1) {
gpu_error_not_expected();
return layout_t();
}
if (new_size < old_size) {
int factor = (old_size / new_size);
b0.size *= factor;
for (auto &b : new_blocks) {
if (&b == &b0) continue;
b.stride *= factor;
}
} else {
int factor = (new_size / old_size);
if (b0.size % factor != 0) {
gpu_error_not_expected();
return layout_t();
}
b0.size /= factor;
for (auto &b : new_blocks) {
if (&b == &b0) continue;
if (b.stride % factor != 0) {
gpu_error_not_expected();
return layout_t();
}
b.stride /= factor;
}
}
return layout_t(new_type, new_blocks, new_offset, layout.ndims(false),
do_normalize);
}
bool try_reinterpret_to_wider_type(layout_t &src, layout_t &dst,
const tile_t &tile, bool do_update, int *new_size_out) {
if (src.blocks().empty() || dst.blocks().empty()) return false;
if (src.type() != dst.type()) return false;
auto &s0 = src[0];
auto &d0 = dst[0];
if (s0.idx != d0.idx) return false;
if (int(s0.stride) != 1) return false;
if (int(d0.stride) != 1) return false;
int old_size = src.type().size();
int s0_old_size = int(s0.size) * old_size;
int d0_old_size = int(d0.size) * old_size;
int new_size = math::gcd(s0_old_size, d0_old_size);
new_size = math::gcd(new_size, 4); if (new_size <= old_size) return false;
auto tile_ok = [&](const layout_t &l) {
if (tile.is_empty()) return true;
int factor = new_size / old_size;
if (tile[l[0].idx] % factor != 0) return false;
return true;
};
auto strides_ok = [&](const layout_t &l) {
for (int i = 1; i < int(l.blocks().size()); i++) {
auto &b = l[i];
if (int(b.stride) * old_size % new_size != 0) return false;
}
return true;
};
while (new_size > old_size) {
bool ok = true;
ok &= (tile_ok(src) && tile_ok(dst));
ok &= (strides_ok(src) && strides_ok(dst));
if (ok) {
if (do_update) {
src = reinterpret(src, dsl::type_t::s(new_size * 8));
dst = reinterpret(dst, dsl::type_t::s(new_size * 8));
}
if (new_size_out) *new_size_out = new_size;
return true;
}
new_size /= 2;
}
return false;
}
void align_layouts(layout_t &a, layout_t &b) {
for (auto &d : a.tile()) {
auto a_blocks = a.blocks();
auto b_blocks = b.blocks();
int a_max = int(a_blocks.size());
int b_max = int(b_blocks.size());
int a_idx = 0;
int b_idx = 0;
for (;;) {
while (a_idx < a_max && a_blocks[a_idx].idx != d)
a_idx++;
while (b_idx < b_max && b_blocks[b_idx].idx != d)
b_idx++;
if (a_idx >= a_max || b_idx >= b_max) break;
auto &ab = a_blocks[a_idx];
auto &bb = b_blocks[b_idx];
dim_t common_size = math::gcd(ab.size, bb.size);
if (ab.size == common_size && bb.size == common_size) {
a_idx++;
b_idx++;
continue;
}
if (ab.size != common_size) {
a = a.split_block(a[a_idx], common_size, ab.size / common_size);
}
if (bb.size != common_size) {
b = b.split_block(b[b_idx], common_size, bb.size / common_size);
}
break;
}
}
}
expr_t grid_splitter_t::pop_block(dim_t size) {
gpu_assert(size > 1);
gpu_assert(can_pop_block(size));
dim_t new_stride = cur_stride_ * size;
auto idx_expr = grid_.idx(cur_idx_);
if (cur_stride_ != 1) idx_expr /= cur_stride_;
if (new_stride != grid_.dim(cur_idx_)) idx_expr %= size;
cur_stride_ = new_stride;
if (cur_stride_ == grid_.dim(cur_idx_)) {
cur_idx_--;
skip_size_1_dims();
cur_stride_ = 1;
}
return idx_expr;
}
stride_t tdim_t::compute_stride(
const expr_t &e, dim_idx_t idx, const expr_t &var) {
if (e.is_same(var)) return stride_t(1);
auto e1 = substitute(e, var, var + 1);
auto e_stride = simplify(e1 - e);
if (is_const(e_stride)) return stride_t(to_cpp<dim_t>(e_stride));
return stride_t::unknown();
}
view_t view_t::create_sub_view(const tile_t &tile, const coord_t &coord) const {
auto ret = *this;
for (dim_idx_t i = 0; i < nvdims(); i++) {
ret.vdims_[i] = tile.get(i);
if (!coord.has(i) || coord[i].is(0)) continue;
auto &i_start = coord[i];
auto &s = ret.vstart_[i];
s += i_start;
s = simplify(s);
}
return ret;
}
view_t view_t::substitute(const expr_t &from, const expr_t &to) const {
view_t ret = *this;
for (dim_idx_t i = 0; i < nvdims(); i++) {
ret.vstart_[i] = ir::substitute(ret.vstart_[i], from, to);
ret.vstart_[i] = simplify(ret.vstart_[i]);
}
return ret;
}
std::vector<expr_t> view_t::create_vvars(dim_idx_t nvdims) {
static const int max_nvdims = 128;
static thread_local std::vector<expr_t> _vvars([] {
std::vector<expr_t> ret;
ret.reserve(max_nvdims);
for (int i = 0; i < max_nvdims; i++)
ret.push_back(
var_t::make(dsl::type_t::s32(), "_" + std::to_string(i)));
return ret;
}());
gpu_assert(nvdims <= max_nvdims) << "Too many dimensions: " << nvdims;
return std::vector<expr_t>(_vvars.begin(), _vvars.begin() + nvdims);
}
layout_t view_t::create_pseudo_vlayout(
const layout_t &tlayout, bool init_offset) const {
gpu_assert(!tlayout.is_empty());
auto rem_vdims = vdims_;
std::vector<layout_block_t> blocks;
for (auto &tb : tlayout.blocks()) {
bool tb_is_outermost = tlayout.is_outermost(tb);
dim_t tsize = tb.size;
auto &tinfo = tdims_[tb.idx];
if (tb_is_outermost) {
dim_idx_t max_idx = dim_idx::invalid;
dim_idx_t max_vidx = dim_idx::invalid;
dim_t max_vdim = 1;
for (int i = tinfo.nvargs() - 1; i >= 0; i--) {
dim_idx_t vidx = tinfo.vidx(i);
if (rem_vdims[vidx] > max_vdim) {
max_idx = i;
max_vidx = vidx;
max_vdim = rem_vdims[vidx];
}
}
if (max_vdim > 1) {
stride_t stride = tinfo.vstride(max_idx);
blocks.emplace_back(
max_vidx, max_vdim, stride * stride_t(tb.stride));
rem_vdims[max_vidx] = 1;
}
for (int i = tinfo.nvargs() - 1; i >= 0; i--) {
dim_idx_t vidx = tinfo.vidx(i);
if (rem_vdims[vidx] == 1) continue;
stride_t stride = tinfo.vstride(i) * tb.stride;
blocks.emplace_back(vidx, rem_vdims[vidx], stride);
rem_vdims[vidx] = 1;
}
continue;
}
gpu_assert(tinfo.is_identity()) << "Can't create pseudo-layout.";
int vidx = tinfo.vidx(0);
dim_t &rem_vdim = rem_vdims[vidx];
if (rem_vdim == 1) continue;
if (rem_vdim % tsize == 0) {
rem_vdim /= tsize;
} else if (rem_vdim % tsize != 0) {
if (tsize % rem_vdim == 0) {
auto tmp_layout
= tlayout.split_block(tb, rem_vdim, tsize / rem_vdim);
return create_pseudo_vlayout(tmp_layout, init_offset);
}
gpu_except_not_implemented("Can't create pseudo-layout.");
}
blocks.emplace_back(tb.idx, tsize, tb.stride);
}
for (auto &d : rem_vdims) {
gpu_assert(rem_vdims[d] == 1) << "Can't create pseudo-layout.";
}
layout_t ret(tlayout.type(), blocks, 0, nvdims());
if (!init_offset) return ret;
auto targs = cvt_vargs_to_targs();
auto off = tlayout.offset(targs);
return layout_t(tlayout.type(), blocks, off, nvdims());
}
layout_t dim_assignment_t::map(const layout_t &layout) const {
std::vector<layout_block_t> new_blocks;
for (auto &b : layout.blocks()) {
size_t new_idx = assignments_[b.idx];
if (new_idx == dim_idx::invalid) continue; auto new_b = b;
new_b.idx = new_idx;
new_blocks.push_back(new_b);
}
new_blocks = merge_blocks(new_blocks);
auto ret = layout_t(layout.type(), new_blocks, layout.offset(), new_ndims(),
false);
gpu_assert(layout.elems() == ret.elems())
<< "Assignment doesn't preserve number of elements.";
return ret;
}
layout_t spatials_to_3d(const layout_t &layout, bool with_groups,
const std::array<int, 3> &dhw_map) {
const size_t old_ndims = layout.ndims();
const size_t old_sp_ndims = old_ndims - (with_groups ? 3 : 2);
const size_t new_ndims = old_ndims - old_sp_ndims + 3;
dim_assignment_t to_3d(old_ndims, new_ndims);
for (size_t i = 0; i < old_ndims; i++) {
if (i < old_ndims - old_sp_ndims) {
to_3d.assign(i, i);
} else {
size_t old_sp_idx = 3 - (old_ndims - i);
size_t new_sp_idx = dhw_map[old_sp_idx];
to_3d.assign(i, new_ndims - (3 - new_sp_idx));
}
}
return to_3d.map(layout);
}
} } } } }