From 1cfa60a7e2f75090d106606b379244e18fef2434 Mon Sep 17 00:00:00 2001 From: LinxISA Automation Date: Tue, 8 Sep 2026 17:40:05 +0800 Subject: [PATCH] TileOP: enforce the ISA capacity-derived TCVT geometry contract (#42) The ordinary (non-CUBE) TCVT path required bitwise-identical C++ Rows/Cols between source and destination. That is stricter than the ISA: PTO-TILE-TCVT legality requires equal Row, Col, ValidRow, ValidCol where Row/Col are the capacity-derived physical dimensions (DerivedTileRows = capacity*8 / (Col * elementBits), asl/tile/model/shape/rows-columns.asl), and the capacities independently match each side's own DataType. For a narrowing conversion (bf16/fp32 -> e8m0 [Rows,1]) the 128B minimum TSize doubles the destination's derived rows (e.g. 64 -> 128), so the [Rows,1]/[Rows,1] encoding is illegal under the ISA and the emulator rightly rejects it (issue #42). The ISA-legal encoding declares a wider physical column on BOTH sides (e.g. [64,2] with ValidCol=1: bf16 256B -> 64 derived rows, e8m0 128B -> 64 derived rows). This replaces the bitwise Rows/Cols assertion with the exact ISA condition (equal capacity-derived rows), keeping ValidRow/ValidCol equality, and documents the narrowing pattern in the TCVT usage page. Verified: - issue #42 chain (half->fp32->bf16->e8m0, all [64,2] VC=1) compiles; the final bundle emits lb2=2 with the e8m0 destination at 128B (DerivedTileRows 64 == source 64) - the old [64,1]->[64,1] form is now rejected with a static_assert message that names the ISA contract and the [Rows,2] fix - full gate 64/74 (11 failures identical on baseline); unittest 40/40 --- .../format-conversion/TCVT.md | 20 +++++++++++- include/jcore/template_asm.hpp | 31 ++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/docs/tileop-usage/elementwise-tile-tile/format-conversion/TCVT.md b/docs/tileop-usage/elementwise-tile-tile/format-conversion/TCVT.md index a75c9a1..e480495 100644 --- a/docs/tileop-usage/elementwise-tile-tile/format-conversion/TCVT.md +++ b/docs/tileop-usage/elementwise-tile-tile/format-conversion/TCVT.md @@ -15,12 +15,30 @@ void TCVT(DstTile &dst, SrcTile &src); 对于 `RowMajor`、`ColMajor` 等普通布局,源 Tile 和目标 Tile 必须满足以下条件: -- 物理 `Rows` 和 `Cols` 相同; +- **容量推导的物理行相同**(PTO ISA `PTO-TILE-TCVT` legality:"Source and + destination have equal Row, Col, ValidRow, and ValidCol. Their capacities + and packing independently match their own DataTypes.",其中 Row/Col 是 + `DerivedTileRows = capacity × 8 / (物理Col × 元素位宽)` 的容量推导值,不是 + C++ Tile 类型声明的逻辑 `Rows`/`Cols`); - `ValidRow` 和 `ValidCol` 相同; - 有效区域均包含在对应的物理 Tile 中; - 源/目标 dtype 组合以及 Tile location 合法; - 目标容量足以容纳转换结果。除适用的 Tile 容量和 shape 规则外,普通布局没有额外的固定字节数限制。 +**窄化 dtype(如 bf16/fp32 → e8m0)注意**:B.IOT 目的 TSize 最小 128B,1 字节 +窄类型的 `[Rows,1]` 列向量会被 `round_capacity` 抬到 128B 档,容量推导行数随之 +翻倍(如 `[64,1]` e8m0 推导为 128 行),与 2 字节源(64 行)不再相等——这是 ISA +层面的非法编码(issue #42)。合法写法是把**两侧**的物理列声明为更大的 2 的幂 +(如 `[64,2]`,`ValidCol` 保持 1): + +```cpp +using SrcBf = Tile; +using DstE8 = Tile; +// bf16 256B → DerivedTileRows = 256*8/(2*16) = 64 +// e8m0 128B → DerivedTileRows = 128*8/(2*8) = 64 ✓ 相等,ISA 合法 +TCVT(dst_e8, src_bf); +``` + 示例: ```cpp diff --git a/include/jcore/template_asm.hpp b/include/jcore/template_asm.hpp index 79392b0..d33342a 100644 --- a/include/jcore/template_asm.hpp +++ b/include/jcore/template_asm.hpp @@ -156,10 +156,33 @@ asm volatile( } else { static_assert(!tile_shape_out::IsCubeLayout, "TCVT to a CUBE layout requires a CUBE_M16/M32 source"); - static_assert(tile_shape_out::Rows == tile_shape_in::Rows && - tile_shape_out::Cols == tile_shape_in::Cols, - "ordinary TCVT source and destination must have identical " - "physical Rows/Cols"); + // PTO ISA (PTO-TILE-TCVT legality): "Source and destination have equal + // Row, Col, ValidRow, and ValidCol. Their capacities and packing + // independently match their own DataTypes." Row/Col are the + // capacity-derived physical dimensions + // (DerivedTileRows = capacity*8 / (Col * elementBits), + // asl/tile/model/shape/rows-columns.asl), NOT the C++ logical Rows/Cols + // of the Tile type. A narrowing conversion (e.g. bf16 -> e8m0 [Rows,1]) + // pads the destination to the 128B minimum TSize, which doubles its + // capacity-derived rows; the ISA-legal encoding declares a wider + // physical column on both sides (Col=2 keeps the derived rows equal + // while ValidCol stays 1). Enforce the ISA condition directly instead + // of requiring bitwise-identical C++ Rows/Cols (issue #42). + constexpr int SrcDerivedRows = + (tile_shape_in::StorageBytes * 8) / + (tile_shape_in::Cols * + type_traits::bits); + constexpr int DstDerivedRows = + (tile_shape_out::StorageBytes * 8) / + (tile_shape_out::Cols * + type_traits::bits); + static_assert(SrcDerivedRows == DstDerivedRows, + "ordinary TCVT source and destination must have equal " + "capacity-derived physical Rows (ISA PTO-TILE-TCVT: " + "DerivedTileRows(capacity, Col, dtype) must match; for a " + "narrowing dtype, declare a wider physical Col on both " + "sides, e.g. [Rows,2] with ValidCol=1, so the 128B " + "minimum TSize does not double the destination rows)"); static_assert(tile_shape_out::ValidRow == tile_shape_in::ValidRow && tile_shape_out::ValidCol == tile_shape_in::ValidCol, "ordinary TCVT source and destination must have identical "