Program Listing for File layout.h#
↰ Return to documentation for file (tvm/tirx/layout.h)
/*
* 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.
*/
#ifndef TVM_TIRX_LAYOUT_H_
#define TVM_TIRX_LAYOUT_H_
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/tuple.h>
#include <tvm/ffi/enum.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/object.h>
#include <tvm/ir/expr.h>
#include <tvm/ir/module.h>
#include <tvm/ir/prim/op.h>
#include <tvm/sym/analyzer.h>
#include <tvm/target/target.h>
#include <tvm/tirx/tile/thread_scope.h>
#include <algorithm>
namespace tvm {
namespace tirx {
class Layout;
class TileLayout;
class Iter;
using ffi::Array;
using ffi::Tuple;
class LayoutObj : public ffi::Object {
public:
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO("tirx.Layout", LayoutObj, ffi::Object);
};
class Layout : public ffi::ObjectRef {
public:
TVM_TIRX_DLL void CheckWellFormed() const;
TVM_TIRX_DLL PrimExpr GetSize(ffi::Optional<ffi::String> axis_name = std::nullopt) const;
TVM_TIRX_DLL PrimExpr GetSpan(ffi::Optional<ffi::String> axis_name = std::nullopt) const;
TVM_TIRX_DLL ffi::Map<ffi::String, PrimExpr> Apply(ffi::Array<PrimExpr> coord,
sym::AnalyzerObj* analyzer) const;
TVM_TIRX_DLL ffi::Map<ffi::String, PrimExpr> Apply(PrimExpr coord,
sym::AnalyzerObj* analyzer) const;
TVM_TIRX_DLL ffi::Map<ffi::String, PrimExpr> Apply(const ffi::Array<PrimExpr>& coord,
const ffi::Array<PrimExpr>& shape,
sym::AnalyzerObj* analyzer) const;
TVM_TIRX_DLL Layout Canonicalize() const;
TVM_TIRX_DLL Layout Tile(const TileLayout& outer, const ffi::Array<PrimExpr>& outer_shape,
const ffi::Array<PrimExpr>& inner_shape) const;
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Layout, ffi::ObjectRef, LayoutObj);
};
// target, subscope, scope, iter -> fused_iter
using FAxisFuser = ffi::TypedFunction<ffi::Optional<Iter>(Target, ThreadScope, ThreadScope, Iter)>;
// Axis
class AxisObj : public ffi::EnumObj {
public:
bool IsThreadAxis() const {
static ffi::reflection::TypeAttrColumn state_column(ffi::type_attr::kEnumState);
static ffi::EnumState state =
ffi::Any(state_column[_GetOrAllocRuntimeTypeIndex()]).as_or_throw<ffi::EnumState>();
auto column = state->attrs.Get("thread");
auto thread = column ? column->Get(ffi::GetRef<ffi::ObjectRef>(this)) : std::nullopt;
TVM_FFI_ICHECK(thread.has_value())
<< "Axis '" << _str_index << "' has no thread classification";
return thread->cast<bool>();
}
bool IsMemoryAxis() const { return !IsThreadAxis(); }
TVM_TIRX_DLL ffi::Optional<ffi::String> GetBackend() const;
TVM_TIRX_DLL ffi::Optional<ThreadScope> GetScope() const;
TVM_TIRX_DLL ffi::Optional<ThreadScope> GetSubscope() const;
TVM_TIRX_DLL ffi::Optional<FAxisFuser> GetFuser() const;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.Axis", AxisObj, ffi::EnumObj);
};
class Axis : public ffi::Enum {
public:
const ffi::String& name() const { return get()->_str_index; }
TVM_TIRX_DLL static Axis Get(const ffi::String& name);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Axis, ffi::Enum, AxisObj);
};
class IterObj : public ffi::Object {
public:
explicit IterObj(ffi::UnsafeInit tag) : extent(tag), stride(tag), axis(tag) {}
IterObj(PrimExpr extent, PrimExpr stride, Axis axis)
: extent(std::move(extent)), stride(std::move(stride)), axis(std::move(axis)) {}
PrimExpr extent;
PrimExpr stride;
Axis axis;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<IterObj>()
.def_ro("extent", &IterObj::extent)
.def_ro("stride", &IterObj::stride)
.def_ro("axis", &IterObj::axis);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.Iter", IterObj, ffi::Object);
};
class Iter : public ffi::ObjectRef {
public:
explicit Iter(PrimExpr extent, PrimExpr stride, Axis axis) {
auto n = ffi::make_object<IterObj>(std::move(extent), std::move(stride), std::move(axis));
data_ = std::move(n);
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Iter, ffi::ObjectRef, IterObj);
};
class TileLayoutObj : public LayoutObj {
public:
ffi::Array<Iter> shard;
ffi::Array<Iter> replica;
ffi::Map<Axis, PrimExpr> offset;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TileLayoutObj>()
.def_ro("shard", &TileLayoutObj::shard)
.def_ro("replica", &TileLayoutObj::replica)
.def_ro("offset", &TileLayoutObj::offset);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.TileLayout", TileLayoutObj, LayoutObj);
};
class TileLayout : public Layout {
public:
explicit TileLayout(ffi::Array<Iter> shard, ffi::Array<Iter> replica,
ffi::Map<Axis, PrimExpr> offset)
: Layout(ffi::UnsafeInit{}) {
auto n = ffi::make_object<TileLayoutObj>();
n->shard = std::move(shard);
n->replica = std::move(replica);
n->offset = std::move(offset);
data_ = std::move(n);
}
ffi::Array<PrimExpr> GetShardShape() const {
return get()->shard.Map([](const Iter& iter) { return iter->extent; });
}
TVM_TIRX_DLL bool IsContiguous(ffi::Optional<sym::Analyzer> analyzer = std::nullopt) const;
bool IsTrivial() const {
if (get()->shard.size() > 1) return false;
if (get()->shard.size() == 1) {
if (!get()->shard[0]->axis->IsMemoryAxis() || !prim::IsOne(get()->shard[0]->stride))
return false;
}
return get()->replica.size() == 0 && get()->offset.size() == 0;
}
bool HasThreadAxis() const {
return std::any_of(get()->shard.begin(), get()->shard.end(),
[](const Iter& iter) { return iter->axis->IsThreadAxis(); });
}
TVM_TIRX_DLL ffi::Optional<Tuple<ThreadScope, ThreadScope>> GetScope() const;
TVM_TIRX_DLL static TileLayout DefaultLayout(ffi::Array<PrimExpr> shape);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TileLayout, Layout, TileLayoutObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(TileLayoutObj);
};
// ComposeLayout
class ComposeLayoutObj : public LayoutObj {
public:
explicit ComposeLayoutObj(TileLayout tile_layout) : tile_layout(std::move(tile_layout)) {}
explicit ComposeLayoutObj(ffi::UnsafeInit) : tile_layout(ffi::UnsafeInit{}) {}
int per_element;
int swizzle_len;
int atom_len;
bool swizzle_inner;
TileLayout tile_layout;
// Cached swizzle masks: inner_mask = (1 << swizzle_len) - 1;
// outer_mask = inner_mask << atom_len. Set by the ComposeLayout ctor.
int inner_mask;
int outer_mask;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<ComposeLayoutObj>()
.def_ro("per_element", &ComposeLayoutObj::per_element)
.def_ro("swizzle_len", &ComposeLayoutObj::swizzle_len)
.def_ro("atom_len", &ComposeLayoutObj::atom_len)
.def_ro("swizzle_inner", &ComposeLayoutObj::swizzle_inner)
.def_ro("inner_mask", &ComposeLayoutObj::inner_mask)
.def_ro("outer_mask", &ComposeLayoutObj::outer_mask)
.def_ro("tile_layout", &ComposeLayoutObj::tile_layout);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.ComposeLayout", ComposeLayoutObj, LayoutObj);
};
class ComposeLayout : public Layout {
public:
TVM_TIRX_DLL explicit ComposeLayout(int per_element, int swizzle_len, int atom_len,
TileLayout tile_layout, bool swizzle_inner = true);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ComposeLayout, Layout, ComposeLayoutObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(ComposeLayoutObj);
};
} // namespace tirx
} // namespace tvm
#endif // TVM_TIRX_LAYOUT_H_