/* * 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 tvm/tirx/exec_context.h * \brief Compile-time ExecContext state: the active thread set ``A`` as a * TileLayout and the (inter, intra) split under the current scope kind, * threaded through the IR walker so per-op lowerers see the precise execution * shape at each site. * * Mirrors the pure-Python implementation in python/tvm/tirx/exec_context.py. */ #ifndef TVM_TIRX_EXEC_CONTEXT_H_ #define TVM_TIRX_EXEC_CONTEXT_H_ #include #include #include #include #include #include namespace tvm { namespace tirx { /*! \brief Warpgroup size in warps (hardware-fixed). */ constexpr int kWgSize = 4; /*! \brief Active slice offset + stride * [0, extent) encoded on one TileLayout axis. */ struct AxisRange { PrimExpr extent; PrimExpr offset; PrimExpr stride; /*! \brief Intersect with [lo, hi). Returns false if the result is empty. */ bool Intersect(int64_t lo, int64_t hi, AxisRange* out) const; /*! \brief Intersect with values satisfying axis % modulus == residue. */ bool Modulo(int64_t modulus, int64_t residue, AxisRange* out) const; }; /*! * \brief Active thread set A. * The source of truth is ``layout``: * shard = active axes with extents * offset = per-axis lower bound, possibly a selector PrimExpr */ struct ActiveSet { TileLayout layout; int64_t size() const; bool GetAxis(const std::string& axis, AxisRange* out) const; bool HasAxis(const std::string& axis) const; ActiveSet WithAxis(const std::string& axis, const AxisRange& range) const; std::vector AxisNames() const; }; /*! * \brief One scope_switch split. Fields are sparse dicts keyed by active-set * axis name, e.g. laneid/warpid/cta_id/wid_in_wg/wgid or factorized CTA axes * such as cbx/cby/cbz. An empty map denotes the empty layout (e.g. intra under * scope_kind=thread). */ struct ExecSplit { std::unordered_map inter; std::unordered_map intra; }; /*! \brief Initial A at PrimFunc device entry: all threads active, offsets zero. */ TVM_DLL ActiveSet InitialActiveSet(int64_t lane_ext, int64_t warp_ext, int64_t cta_ext); TVM_DLL ActiveSet InitialActiveSet(int64_t lane_ext, int64_t warp_ext, int64_t cta_ext, const std::vector>& cta_axes); /*! * \brief Narrow A on the lane bound to ``binding``. * * The ScopeBinding maps directly to which native axis (laneid/warpid/cta_id) * to narrow, and for warpid whether to narrow the full axis (kCtaWarp), the * outer factor (kCtaWarpgroup), or the inner factor (kWarpgroupWarp). * * Bindings with no single-lane representation are conservative: cluster_id is * not a filter target; flat thread ids are accepted only when the range can be * represented as a rectangular lane/warp active set. */ TVM_DLL bool FilterNarrow(const ActiveSet& A, ScopeBinding binding, int64_t lo, int64_t hi, ActiveSet* out, std::string* err); /*! * \brief Factor A into (inter, intra) for target scope_kind. * * Returns false on factoring failure (warpgroup with warpid lane that * crosses a warpgroup boundary unaligned) and writes reason to *err. */ TVM_DLL bool ScopeSwitch(const ActiveSet& A, ScopeKind scope_kind, ExecSplit* out, std::string* err); /*! \brief Per-program-point ExecContext: active set + scope kind + split. */ struct ExecContext { ActiveSet A; ScopeKind scope_kind = ScopeKind::kThread; ExecSplit split; // (inter, intra) of current A under current scope_kind /*! \brief Kernel-entry ctor. */ static ExecContext AtKernelEntry(int64_t lane_ext, int64_t warp_ext, int64_t cta_ext); static ExecContext AtKernelEntry(int64_t lane_ext, int64_t warp_ext, int64_t cta_ext, const std::vector>& cta_axes); /*! \brief Apply filter; scope_kind preserved, split recomputed. */ bool WithFilter(ScopeBinding binding, int64_t lo, int64_t hi, ExecContext* out, std::string* err) const; /*! \brief Apply a unique-value selector filter on one scope id Var. */ bool WithSelector(ScopeBinding binding, PrimExpr selector, ExecContext* out, std::string* err) const; /*! \brief Apply filter on a factorized CTA axis such as cbx/cby/cbz. */ bool WithCtaAxisFilter(const std::string& axis, int64_t lo, int64_t hi, ExecContext* out, std::string* err) const; /*! \brief Apply modulo filter on a factorized CTA axis such as cbx/cby/cbz. */ bool WithCtaAxisModulo(const std::string& axis, int64_t modulus, int64_t residue, ExecContext* out, std::string* err) const; }; /*! * \brief Encode one side of an ExecSplit (inter or intra) as the FFI map used * by ``DispatchContextNode::{inter, intra}``: axis name -> [extent, offset] * for unit-stride axes, or [extent, offset, stride] for strided axes. */ TVM_DLL ffi::Map> EncodeSplitSide( const std::unordered_map& side); } // namespace tirx } // namespace tvm #endif // TVM_TIRX_EXEC_CONTEXT_H_