blob: 4393bfabb3af7b9bacd96907a615607131b6530b [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file src/lang/data_layout.cc
* \brief Data SLayout expression.
*/
#include <tvm/arith/analyzer.h>
#include <tvm/ffi/dtype.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/expr.h>
#include <tvm/runtime/logging.h>
#include <tvm/s_tir/data_layout.h>
#include <tvm/tirx/analysis.h>
#include <tvm/tirx/expr.h>
#include <tvm/tirx/stmt_functor.h>
#include <tvm/tirx/var.h>
#include <algorithm>
#include <cctype>
namespace tvm {
namespace tirx {
using tirx::IterVar;
using tirx::IterVarNode;
using tirx::Var;
TVM_FFI_STATIC_INIT_BLOCK() {
SLayoutNode::RegisterReflection();
SBijectiveLayoutNode::RegisterReflection();
}
const SLayoutAxis SLayoutAxis::UPPER_CASE[] = {
SLayoutAxis('A'), SLayoutAxis('B'), SLayoutAxis('C'), SLayoutAxis('D'), SLayoutAxis('E'),
SLayoutAxis('F'), SLayoutAxis('G'), SLayoutAxis('H'), SLayoutAxis('I'), SLayoutAxis('J'),
SLayoutAxis('K'), SLayoutAxis('L'), SLayoutAxis('M'), SLayoutAxis('N'), SLayoutAxis('O'),
SLayoutAxis('P'), SLayoutAxis('Q'), SLayoutAxis('R'), SLayoutAxis('S'), SLayoutAxis('T'),
SLayoutAxis('U'), SLayoutAxis('V'), SLayoutAxis('W'), SLayoutAxis('X'), SLayoutAxis('Y'),
SLayoutAxis('Z')};
const SLayoutAxis SLayoutAxis::LOWER_CASE[] = {
SLayoutAxis('a'), SLayoutAxis('b'), SLayoutAxis('c'), SLayoutAxis('d'), SLayoutAxis('e'),
SLayoutAxis('f'), SLayoutAxis('g'), SLayoutAxis('h'), SLayoutAxis('i'), SLayoutAxis('j'),
SLayoutAxis('k'), SLayoutAxis('l'), SLayoutAxis('m'), SLayoutAxis('n'), SLayoutAxis('o'),
SLayoutAxis('p'), SLayoutAxis('q'), SLayoutAxis('r'), SLayoutAxis('s'), SLayoutAxis('t'),
SLayoutAxis('u'), SLayoutAxis('v'), SLayoutAxis('w'), SLayoutAxis('x'), SLayoutAxis('y'),
SLayoutAxis('z')};
const SLayoutAxis& SLayoutAxis::Get(const char name) {
TVM_FFI_ICHECK((name >= 'A' && name <= 'Z') || (name >= 'a' && name <= 'z'))
<< "Invalid layout axis name: " << name << ". Has to be A-Z or a-z.";
return (name >= 'A' && name <= 'Z') ? SLayoutAxis::UPPER_CASE[name - 'A']
: SLayoutAxis::LOWER_CASE[name - 'a'];
}
const SLayoutAxis& SLayoutAxis::Get(const IterVar& itvar) {
const std::string axis = itvar->var.get()->name;
TVM_FFI_ICHECK_EQ(axis.size(), 1) << "Invalid layout axis " << axis;
return SLayoutAxis::Get(axis[0]);
}
const SLayoutAxis& SLayoutAxis::Get(const std::string& name) {
TVM_FFI_ICHECK_EQ(name.length(), 1) << "Invalid axis " << name;
return SLayoutAxis::Get(name[0]);
}
SLayout::SLayout(const ffi::Array<IterVar>& axes) {
auto node = ffi::make_object<SLayoutNode>();
node->axes = axes;
std::ostringstream repr;
for (const IterVar& packed_axis : axes) {
auto unpacked_axes = UnpackIterVar(packed_axis);
bool is_grouped = unpacked_axes.size() > 1;
if (is_grouped) repr << "[";
for (const IterVar& axis : unpacked_axes) {
if (const auto* factor = axis->dom->extent.as<IntImmNode>()) {
TVM_FFI_ICHECK_GT(factor->value, 0);
repr << factor->value;
} else {
TVM_FFI_ICHECK(!is_grouped)
<< "Only Subordinate Axes with extent is allowed within a packed dim";
}
TVM_FFI_ICHECK_EQ(axis->var.get()->name.size(), 1)
<< "Invalid layout axis " << axis->var.get()->name;
char c = axis->var.get()->name.operator std::string()[0];
TVM_FFI_ICHECK((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
<< "Invalid layout axis " << c;
repr << axis->var.get()->name;
}
if (is_grouped) repr << "]";
}
node->name = repr.str();
data_ = std::move(node);
}
SLayout::SLayout(const std::string& name, PrimType index_ty) { // NOLINT(*)
TVM_FFI_CHECK(index_ty.code() == DLDataTypeCode::kDLInt, TypeError)
<< "The input dtype should be integer type";
if (name == "__undef__") return;
auto node = ffi::make_object<SLayoutNode>();
node->name = name;
if (name.empty()) return; // scalar
// parse layout string
int32_t factor = 0;
bool in_packing = false;
std::vector<IterVar> unpacked_axes;
for (char c : name) {
if (c >= 'A' && c <= 'Z') {
TVM_FFI_ICHECK_EQ(factor, 0) << "Invalid layout " << name << ": invalid factor size "
<< factor << " before dimension " << c;
PrimVar axis_var(std::string(1, c), index_ty);
IterVar axis(Range(IntImm(index_ty, 0), axis_var), axis_var, tirx::kDataPar);
if (!in_packing) {
node->axes.push_back(axis);
} else {
unpacked_axes.push_back(axis);
}
} else if (c >= 'a' && c <= 'z') {
TVM_FFI_ICHECK_GT(factor, 0) << "Invalid layout " << name << ": invalid factor size "
<< factor << " for dimension " << c;
std::stringstream name;
name << factor << c;
IterVar axis(Range(IntImm(index_ty, 0), IntImm(index_ty, factor)),
PrimVar(name.str(), index_ty), tirx::kDataPar);
if (!in_packing) {
node->axes.push_back(axis);
} else {
unpacked_axes.push_back(axis);
}
factor = 0;
} else if (c >= '0' && c <= '9') {
TVM_FFI_ICHECK(factor >= 0) << "Invalid layout " << name << ": _ is adjacent to a number.";
factor = factor * 10 + c - '0';
} else if (c == '[') {
TVM_FFI_ICHECK(!in_packing) << "Invalid layout " << name << ": can't do nested packing";
in_packing = true;
} else if (c == ']') {
TVM_FFI_ICHECK(in_packing) << "Invalid layout " << name
<< ": encountered ] without matching bracket";
TVM_FFI_ICHECK(unpacked_axes.size() > 1)
<< "Invalid layout " << name << ": found empty/single packed axis";
std::stringstream ss;
int64_t extent = 1;
for (auto& axis : unpacked_axes) {
TVM_FFI_ICHECK(axis->dom->extent.as<IntImmNode>())
<< "Invalid SLayout " << name << ": can't have variable sized node(" << axis->var->name
<< ") within a packed axis";
auto axis_name = axis->var->name.operator std::string();
auto factor = axis->dom->extent.as<IntImm>().value();
ss << axis_name;
extent = extent * factor->value;
}
std::string grouped_name = ss.str();
IterVar grouped_axis(Range(IntImm(index_ty, 0), IntImm(index_ty, extent)),
PrimVar(grouped_name, index_ty), tirx::kDataPar);
node->axes.push_back(grouped_axis);
in_packing = false;
unpacked_axes.clear();
} else {
TVM_FFI_THROW(InternalError) << "Invalid layout " << name;
}
}
TVM_FFI_ICHECK(in_packing == false)
<< "Invalid SLayout " << name << ": haven't terminated the packing sequence";
// validate layout
std::vector<int> axis_cnt(256, 0);
for (const IterVar& pv : node->axes) {
for (const IterVar& v : UnpackIterVar(pv)) {
auto axis_str = v->var.get()->name.operator std::string();
TVM_FFI_ICHECK_EQ(axis_str.size(), 1);
char axis = axis_str[0];
TVM_FFI_ICHECK((axis >= 'a' && axis <= 'z') || (axis >= 'A' && axis <= 'Z'));
axis_cnt[axis] += 1;
}
}
for (const IterVar& pv : node->axes) {
for (const IterVar& v : UnpackIterVar(pv)) {
char axis = v->var.get()->name.operator std::string()[0];
if (axis >= 'a' && axis <= 'z') {
TVM_FFI_ICHECK(axis_cnt[axis - 'a' + 'A'])
<< "Invalid layout " << name << ": missing axis " << std::toupper(axis);
TVM_FFI_ICHECK(axis_cnt[axis] == 1)
<< "Invalid layout " << name << ": found more than one subordinate "
<< std::toupper(axis);
}
}
}
data_ = std::move(node);
}
SLayout SLayout::SubLayout(size_t pos, size_t len) const {
if (!defined() || pos > ndim()) return SLayout::Undef();
if (len == 0) return SLayout(ffi::Array<IterVar>());
if (pos + len > ndim()) len = ndim() - pos;
ffi::Array<IterVar> new_layout;
const auto axes = operator->()->axes;
for (size_t i = pos; i < pos + len; ++i) {
new_layout.push_back(axes[i]);
}
return SLayout(new_layout);
}
ffi::Array<IterVar> SLayout::UnpackIterVar(IterVar packed_iter) {
ffi::Array<IterVar> result;
int64_t factor = 0, final_factor = 1;
std::string name(packed_iter->var->name.c_str());
PrimType index_ty = packed_iter->var.ty();
for (auto ch : name) {
if (ch >= '0' && ch <= '9') {
factor = factor * 10 + (ch - '0');
} else if (ch >= 'a' && ch <= 'z') {
TVM_FFI_ICHECK(factor != 0) << "Invalid Factor Size";
result.push_back(IterVar(Range(IntImm(index_ty, 0), IntImm(index_ty, factor)),
PrimVar(std::string(1, ch), index_ty), tirx::kDataPar));
final_factor *= factor;
factor = 0;
} else if (ch >= 'A' && ch <= 'Z') {
TVM_FFI_ICHECK(factor == 0) << "Can't have non-zero factors for primal axis";
PrimVar axis_var(std::string(1, ch), index_ty);
result.push_back(IterVar(Range(IntImm(index_ty, 0), axis_var), axis_var, tirx::kDataPar));
}
}
return result;
}
IterVar SLayout::PackIterVar(ffi::Array<IterVar> iter_vars) {
std::stringstream name;
size_t extent = 1;
PrimType index_ty = iter_vars[0]->dom->extent.as<PrimExpr>().value().ty();
for (auto itvar : iter_vars) {
TVM_FFI_ICHECK(itvar->dom->extent.as<IntImm>())
<< "Packed Axis can contain only Subordinate Axes";
name << itvar->dom->extent.as<IntImm>().value() << itvar->var->name;
extent = extent * itvar->dom->extent.as<IntImm>().value()->value;
}
return IterVar(Range(IntImm(index_ty, 0), IntImm(index_ty, extent)),
PrimVar(name.str(), index_ty), tirx::kDataPar);
}
int32_t SLayout::FactorOf(const SLayoutAxis& axis) const {
if (!defined()) return -1;
const SLayoutAxis& sub = axis.ToSubordinate();
int32_t factor = 1;
bool has_sub = false;
for (const IterVar& packed_itvar : operator->()->axes) {
for (auto itvar : UnpackIterVar(packed_itvar)) {
if (sub == SLayoutAxis::Get(itvar)) {
has_sub = true;
int32_t val = itvar->dom->extent.as<IntImmNode>()->value;
factor *= val;
}
}
}
factor = has_sub ? factor : -1;
return factor;
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::TypeAttrDef<SLayoutNode>().def(refl::type_attr::kRepr,
[](SLayout l, ffi::Function) -> ffi::String {
return "SLayout(" + std::string(l->name) + ")";
});
}
inline bool GetStoreRule(ffi::Array<PrimExpr>* index_rule, ffi::Array<PrimExpr>* shape_rule,
const SLayout& src_layout, const SLayout& dst_layout) {
if (!src_layout.defined() || src_layout.name().empty()) {
LOG(WARNING) << "src layout '" << src_layout.name() << "' is invalid.";
return false;
}
if (!dst_layout.defined() || dst_layout.name().empty()) {
LOG(WARNING) << "dst layout '" << dst_layout.name() << "' is invalid.";
return false;
}
std::vector<bool> exists(128, false);
PrimExpr norm_indexes[128];
for (auto& it : norm_indexes) it = PrimExpr(0);
for (size_t i = 0; i < src_layout.ndim(); i++) {
auto factor = src_layout.PackedAxisAt(i)->dom->extent;
auto src_unpacked_axes = SLayout::UnpackIterVar(src_layout.PackedAxisAt(i));
if (src_unpacked_axes.size() == 1 && SLayoutAxis::Get(src_unpacked_axes[0]).IsPrimal()) {
const auto& prim_axis = SLayoutAxis::Get(src_unpacked_axes[0]);
int64_t offset = src_layout.FactorOf(prim_axis);
if (offset == -1)
norm_indexes[prim_axis.name()[0] - 'A'] =
norm_indexes[prim_axis.name()[0] - 'A'] + src_layout.PackedAxisAt(i);
else
norm_indexes[prim_axis.name()[0] - 'A'] =
norm_indexes[prim_axis.name()[0] - 'A'] +
src_layout.PackedAxisAt(i) * src_layout.FactorOf(prim_axis);
exists[prim_axis.name()[0]] = true;
} else {
int64_t value = 1;
std::vector<int> index_divs(src_unpacked_axes.size());
for (size_t j = 0; j < src_unpacked_axes.size(); j++) {
index_divs[j] = value;
const auto* extent = src_unpacked_axes[j]->dom->extent.as<IntImmNode>();
TVM_FFI_ICHECK(extent) << "Expected Integer Extents for Offset Calculation";
index_divs.push_back(value);
value = value * extent->value;
}
std::reverse(index_divs.begin(), index_divs.end());
for (size_t j = 0; j < src_unpacked_axes.size(); j++) {
const int extent = src_unpacked_axes[j]->dom->extent.as<IntImmNode>()->value;
const SLayoutAxis& store_axis_impl = SLayoutAxis::Get(src_unpacked_axes[j]);
const SLayoutAxis& sub_axis = store_axis_impl.ToSubordinate(); /* Not Needed */
const SLayoutAxis& prim_axis = store_axis_impl.ToPrimal();
PrimExpr factor_ij = indexdiv(src_layout.PackedAxisAt(i), index_divs[j]);
if (j != 0) factor_ij = indexmod(factor_ij, extent);
for (size_t k = i; k < src_layout.ndim(); k++) {
size_t l = 0;
if (k == i) l = j + 1;
auto inter_unpacked_axes = SLayout::UnpackIterVar(src_layout.PackedAxisAt(k));
for (; l < inter_unpacked_axes.size(); l++) {
const SLayoutAxis& axis = SLayoutAxis::Get(inter_unpacked_axes[l]);
if (axis == sub_axis) {
IntImm sub_extent = inter_unpacked_axes[l]->dom->extent.as_or_throw<IntImm>();
factor_ij = factor_ij * IntImm(sub_extent.ty(), sub_extent->value);
}
}
}
norm_indexes[prim_axis.name()[0] - 'A'] =
norm_indexes[prim_axis.name()[0] - 'A'] + factor_ij;
}
}
}
arith::Analyzer ana;
for (size_t i = 0; i < dst_layout.ndim(); i++) {
const auto dst_unpacked_axes = SLayout::UnpackIterVar(dst_layout.PackedAxisAt(i));
if (dst_unpacked_axes.size() == 1 && SLayoutAxis::Get(dst_unpacked_axes[0]).IsPrimal()) {
const auto& prim_axis = SLayoutAxis::Get(dst_unpacked_axes[0]);
if (!exists[prim_axis.name()[0]]) return false;
int64_t offset = dst_layout.FactorOf(prim_axis);
if (offset != -1) {
index_rule->push_back(
indexdiv(norm_indexes[prim_axis.name()[0] - 'A'], dst_layout.FactorOf(prim_axis)));
shape_rule->push_back(
indexdiv(norm_indexes[prim_axis.name()[0] - 'A'] + (dst_layout.FactorOf(prim_axis) - 1),
dst_layout.FactorOf(prim_axis)));
} else {
index_rule->push_back(norm_indexes[prim_axis.name()[0] - 'A']);
shape_rule->push_back(norm_indexes[prim_axis.name()[0] - 'A']);
}
} else {
PrimExpr factor(0);
for (size_t j = 0; j < dst_unpacked_axes.size(); j++) {
const auto& prim_axis = SLayoutAxis::Get(dst_unpacked_axes[j]).ToPrimal();
const auto& sub_axis = SLayoutAxis::Get(dst_unpacked_axes[j]).ToSubordinate();
const auto* extent = dst_unpacked_axes[j]->dom->extent.as<IntImmNode>();
TVM_FFI_ICHECK(extent) << "Expected extent to be IntImmNode";
size_t divfactor = 1;
for (size_t k = i; k < dst_layout.ndim(); k++) {
size_t l = 0;
if (k == i) l = j + 1;
const auto inter_unpacked_axes = SLayout::UnpackIterVar(dst_layout.PackedAxisAt(k));
for (; l < inter_unpacked_axes.size(); l++) {
const auto& axis = SLayoutAxis::Get(inter_unpacked_axes[l]);
if (sub_axis == axis) {
const auto* sub_extent = inter_unpacked_axes[l]->dom->extent.as<IntImmNode>();
TVM_FFI_ICHECK(sub_extent) << "Expected Integer Extents for Offset Calculation";
divfactor = divfactor * sub_extent->value;
}
}
}
factor = factor + indexmod(indexdiv(norm_indexes[prim_axis.name()[0] - 'A'], divfactor),
extent->value);
for (size_t k = j + 1; k < dst_unpacked_axes.size(); k++) {
factor = factor * dst_unpacked_axes[k]->dom->extent.as<IntImm>().value();
}
}
ana->Simplify(factor);
index_rule->push_back(factor);
shape_rule->push_back(factor);
}
}
std::stringstream ss;
ss << "index rule for " << src_layout.name() << "-->" << dst_layout.name() << ": [ ";
for (const auto& r : *index_rule) {
ss << r << ", ";
}
ss << "]" << std::endl;
ss << "shape rule for " << src_layout.name() << "-->" << dst_layout.name() << ": [ ";
for (const auto& r : *shape_rule) {
ss << r << ", ";
}
ss << "]" << std::endl;
VLOG(1) << ss.str() << std::endl;
return true;
}
inline ffi::Array<PrimExpr> TransformIndex(const ffi::Array<PrimExpr>& src_index,
const ffi::Array<IterVar>& src_axis,
const ffi::Array<PrimExpr>& transform_rule) {
arith::Analyzer ana;
ffi::Array<PrimExpr> result;
std::unordered_map<const tirx::VarNode*, PrimExpr> bind_map;
for (size_t i = 0; i < src_index.size(); ++i) {
bind_map[src_axis[i]->var.get()] = src_index[i];
}
for (PrimExpr rule : transform_rule) {
result.push_back(ana->Simplify(tirx::Substitute(rule, bind_map)));
}
return result;
}
ffi::Array<PrimExpr> SBijectiveLayout::ForwardIndex(const ffi::Array<PrimExpr>& src_index) const {
TVM_FFI_ICHECK(defined()) << "Cannot operate on an undefined bijective layout.";
const SBijectiveLayoutNode* self = operator->();
TVM_FFI_ICHECK_EQ(src_index.size(), self->src_layout->axes.size())
<< "Input mismatch with layout " << self->src_layout;
return TransformIndex(src_index, self->src_layout->axes, self->index_forward_rule);
}
ffi::Array<PrimExpr> SBijectiveLayout::BackwardIndex(const ffi::Array<PrimExpr>& dst_index) const {
TVM_FFI_ICHECK(defined()) << "Cannot operate on an undefined bijective layout.";
const SBijectiveLayoutNode* self = operator->();
TVM_FFI_ICHECK_EQ(dst_index.size(), self->dst_layout->axes.size())
<< "Output mismatch with layout " << self->dst_layout;
return TransformIndex(dst_index, self->dst_layout->axes, self->index_backward_rule);
}
inline ffi::Array<PrimExpr> TransformShape(const ffi::Array<PrimExpr>& src_shape,
const ffi::Array<IterVar>& src_axis,
const ffi::Array<IterVar>& target_axis,
const ffi::Array<PrimExpr>& transform_rule) {
arith::Analyzer ana;
TVM_FFI_ICHECK_EQ(src_shape.size(), src_axis.size())
<< "Input shape size " << src_shape.size() << " mismatch with the expected shape size "
<< src_axis.size();
// bind variables for original axes
// for major-axis, bind the corresponding size
// for minor-axis, simply bind it as 0, so that we can reuse forward/backward_rule,
// e.g., (C * 16 + c) / 32
std::unordered_map<const tirx::VarNode*, PrimExpr> bind_map;
for (size_t i = 0; i < src_shape.size(); ++i) {
PrimExpr orig_shape = src_shape[i];
IterVar orig_axis = src_axis[i];
auto layout = SLayout::UnpackIterVar(orig_axis);
if (layout.size() != 1 || !SLayoutAxis::Get(layout[0]).IsPrimal()) {
if (orig_shape.defined()) {
const auto* orig_shape_const = orig_shape.as<IntImmNode>();
const auto* orig_axis_extent = orig_axis->dom->extent.as<IntImmNode>();
if (orig_shape_const) {
TVM_FFI_ICHECK_EQ(orig_shape_const->value, orig_axis_extent->value)
<< "Input shape mismatch at index " << i << ". Expected " << orig_axis->dom->extent
<< ", get " << orig_shape;
}
}
bind_map[orig_axis->var.get()] = IntImm(orig_axis->var.ty(), 0);
} else {
bind_map[orig_axis->var.get()] = orig_axis->var.ty() == orig_shape.ty()
? orig_shape
: cast(orig_axis->var.ty(), orig_shape);
}
}
// infer the target shape,
// for major-axis, use the forward/backward_rule directly,
// for minor-axis, simply use the extent.
ffi::Array<PrimExpr> result;
TVM_FFI_ICHECK_EQ(transform_rule.size(), target_axis.size());
for (size_t i = 0; i < transform_rule.size(); ++i) {
PrimExpr rule = transform_rule[i];
IterVar axis = target_axis[i];
auto layout = SLayout::UnpackIterVar(axis);
if (layout.size() != 1 || !SLayoutAxis::Get(layout[0]).IsPrimal()) {
result.push_back(axis->dom->extent);
} else {
result.push_back(ana->Simplify(tirx::Substitute(rule, bind_map)));
}
}
std::stringstream ss;
ss << "shape rule for " << SLayout(src_axis).name() << "-->" << SLayout(target_axis).name()
<< ": [ ";
for (const auto& r : transform_rule) {
ss << r << ", ";
}
ss << "]" << std::endl;
ss << "shape transform: [ ";
for (const auto& s : src_shape) {
ss << s << ", ";
}
ss << "] --> [ ";
for (const auto& r : result) {
ss << r << ", ";
}
ss << "]" << std::endl;
VLOG(1) << std::endl << ss.str();
return result;
}
ffi::Array<PrimExpr> SBijectiveLayout::ForwardShape(const ffi::Array<PrimExpr>& shape) const {
TVM_FFI_ICHECK(defined()) << "Cannot operate on an undefined bijective layout.";
const SBijectiveLayoutNode* self = operator->();
return TransformShape(shape, self->src_layout->axes, self->dst_layout->axes,
self->shape_forward_rule);
}
ffi::Array<PrimExpr> SBijectiveLayout::BackwardShape(const ffi::Array<PrimExpr>& shape) const {
TVM_FFI_ICHECK(defined()) << "Cannot operate on an undefined bijective layout.";
const SBijectiveLayoutNode* self = operator->();
return TransformShape(shape, self->dst_layout->axes, self->src_layout->axes,
self->shape_backward_rule);
}
SBijectiveLayout::SBijectiveLayout(SLayout src_layout, SLayout dst_layout) {
auto n = ffi::make_object<SBijectiveLayoutNode>();
n->src_layout = std::move(src_layout);
n->dst_layout = std::move(dst_layout);
// To be consistent with previous behavior, a nullptr layout is created
// when argument is invalid.
if (GetStoreRule(&n->index_forward_rule, &n->shape_forward_rule, n->src_layout, n->dst_layout)) {
TVM_FFI_ICHECK(GetStoreRule(&n->index_backward_rule, &n->shape_backward_rule, n->dst_layout,
n->src_layout));
data_ = std::move(n);
}
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::TypeAttrDef<SBijectiveLayoutNode>().def(
refl::type_attr::kRepr, [](SBijectiveLayout bl, ffi::Function) -> ffi::String {
return "SBijectiveLayout(" + std::string(bl->src_layout.name()) + "->" +
std::string(bl->dst_layout.name()) + ")";
});
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def("s_tir.SLayout", [](std::string name, PrimType dtype) { return SLayout(name, dtype); })
.def("s_tir.SLayoutIndexOf",
[](SLayout layout, std::string axis) -> int { return layout.IndexOf(axis); })
.def("s_tir.SLayoutFactorOf",
[](SLayout layout, std::string axis) -> int {
return layout.FactorOf(SLayoutAxis::Get(axis));
})
.def("s_tir.SLayoutNdim", [](SLayout layout) -> int { return layout.ndim(); })
.def("s_tir.SLayoutGetItem",
[](SLayout layout, int idx) -> std::string {
const auto& axis = layout.PackedAxisAt(idx);
return axis->var->name;
})
.def("s_tir.SBijectiveLayout",
[](SLayout src_layout, SLayout dst_layout) -> SBijectiveLayout {
return SBijectiveLayout(src_layout, dst_layout);
})
.def_method("s_tir.SBijectiveLayoutForwardIndex", &SBijectiveLayout::ForwardIndex)
.def_method("s_tir.SBijectiveLayoutBackwardIndex", &SBijectiveLayout::BackwardIndex)
.def_method("s_tir.SBijectiveLayoutForwardShape", &SBijectiveLayout::ForwardShape)
.def_method("s_tir.SBijectiveLayoutBackwardShape", &SBijectiveLayout::BackwardShape);
}
} // namespace tirx
} // namespace tvm