From 9dd0390e9c017206358b0c5a2603a6e5674d33d0 Mon Sep 17 00:00:00 2001 From: WLDFA Date: Fri, 30 Oct 2026 09:55:47 +0800 Subject: [PATCH] fix(tileop): support mixed binary slot assembly --- docs/tileop-usage/b-subview-b-assemble.md | 23 +++ include/common/pto_tile_region_inline_asm.hpp | 193 ++++++++++++++++++ test/tileop_api/compile.all | 2 +- test/tileop_api/run_negatives.sh | 13 ++ .../src/Issue299MixedBinaryAssembly.cpp | 82 ++++++++ test/tileop_api/src/Issue299Negatives.cpp | 46 +++++ test/tileop_api/verify_target_cxx_frontend.sh | 68 ++++++ 7 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 test/tileop_api/src/Issue299MixedBinaryAssembly.cpp create mode 100644 test/tileop_api/src/Issue299Negatives.cpp diff --git a/docs/tileop-usage/b-subview-b-assemble.md b/docs/tileop-usage/b-subview-b-assemble.md index b4ecc54..d4adf35 100644 --- a/docs/tileop-usage/b-subview-b-assemble.md +++ b/docs/tileop-usage/b-subview-b-assemble.md @@ -105,6 +105,29 @@ unboxed `CUBE_M32` Local Tile;dtype、location、physical/valid shape 和 fragment capacity 必须一致。`TADD`、`TSUB`、`TMUL`、`TDIV`、`TREM`、 `TAND`、`TOR`、`TXOR`、`TSHL`、`TSHR`、`TMAX`、`TMIN` 共用此路径。 +二元 producer 也可以混用一个 `TPARTVIEW` fragment 和一个普通 +`CUBE_M32` Tile,两个源顺序都受支持。view 所在的 source binder 携带一个 +`B.SUBVIEW 0/1`,destination binder 同时携带 `B.ASSEMBLE`,不需要先用 +`TADDS(x, 0)`、`TCVT` 或 `TMOV` 把 view 物化: + +```cpp +Parent assemble_mixed(Parent &parent, Fragment &tile) { + auto view = TPARTVIEW(parent)[0][7]; + TileArray slots; + for (int col = 0; col < 8; ++col) { + if ((col & 1) == 0) + TADD(slots[0][col], view, tile); // source 0 携带 B.SUBVIEW + else + TADD(slots[0][col], tile, view); // source 1 携带 B.SUBVIEW + } + return TASSEMBLY(std::move(slots)); +} +``` + +mixed 路径同样要求 slot、view fragment 和普通 Tile 的 CUBE_M32 layout、 +dtype、location、physical/valid shape 与 fragment capacity 全部一致,并适用于 +上述完整二元操作族。 + source 侧对应物是 `TPARTVIEW`:`B.ASSEMBLE` 把 producer 的 destination 写进 parent 的某个 slot,`B.SUBVIEW` 则把 parent 的某段借出为 source fragment, 两者互为读写对偶。 diff --git a/include/common/pto_tile_region_inline_asm.hpp b/include/common/pto_tile_region_inline_asm.hpp index 046d4ab..0f8cfcd 100644 --- a/include/common/pto_tile_region_inline_asm.hpp +++ b/include/common/pto_tile_region_inline_asm.hpp @@ -2119,6 +2119,181 @@ PTO_REGION_ALWAYS_INLINE void pto_region_binary_tile_assemble_dispatch( } } +// A mixed binary assembly producer binds one parent-backed CUBE_M32 fragment +// through B.SUBVIEW and one ordinary CUBE_M32 Tile directly. The bool records +// the mathematical source position of the view so B.SUBVIEW selects the same +// source independently of the C++ parameter order used internally here. +template + requires(is_tile_data_v && !is_subtile_view_v && + !is_range_v) +PTO_REGION_ALWAYS_INLINE void pto_region_binary_mixed_assemble( + region::TileArrayOutputRef &dst, + region::SubTileView &view, Tile &tile) { + static_assert(SubTile::BFractal == BLayout::CubeM32 && + SourceSubTile::BFractal == BLayout::CubeM32 && + Tile::BFractal == BLayout::CubeM32, + "binary TileArray mixed-source assembly requires CUBE_M32 " + "destination and sources"); + static_assert(SubTile::SFractal == SLayout::NoneBox && + SourceSubTile::SFractal == SLayout::NoneBox && + Tile::SFractal == SLayout::NoneBox, + "binary TileArray mixed-source assembly requires unboxed " + "fragments"); + static_assert(SubTile::Loc == SourceSubTile::Loc && + SourceSubTile::Loc == Tile::Loc, + "binary TileArray slot and mixed sources must have matching " + "locations"); + static_assert(SubTile::Rows == SourceSubTile::Rows && + SubTile::Cols == SourceSubTile::Cols && + SourceSubTile::Rows == Tile::Rows && + SourceSubTile::Cols == Tile::Cols, + "binary TileArray slot requires matching physical shapes"); + static_assert(SubTile::ValidRow == SourceSubTile::ValidRow && + SubTile::ValidCol == SourceSubTile::ValidCol && + SourceSubTile::ValidRow == Tile::ValidRow && + SourceSubTile::ValidCol == Tile::ValidCol, + "binary TileArray slot requires matching valid shapes"); + static_assert( + std::is_same_v && + std::is_same_v, + "binary TileArray slot requires matching element types"); + static_assert(SubTile::LogicalTileBytes == + SourceSubTile::LogicalTileBytes && + SourceSubTile::LogicalTileBytes == Tile::LogicalTileBytes, + "binary TileArray slot and mixed sources must have matching " + "capacities"); + + constexpr int writer_size = + tile_type_traits::TilesizeCode; + constexpr int subview_size = + tile_type_traits::TilesizeCode; + static_assert(writer_size != 0, + "TileArray slot writer extent must be a nonzero size code"); + static_assert(subview_size != 0, + "mixed-source B.SUBVIEW extent must be a nonzero size code"); + const uintptr_t source_base_units = view.GetRangeBase(); + const uintptr_t destination_base_units = dst.range_base_units(); + +#define PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX \ + "BSTART.TEPL %c[Opcode], %D[Type]\n" PTO_REGION_ELEMENTWISE_LAYOUT_ASM \ + "B.DIM zero, %c[ValidCol], ->lb0\n" \ + "B.DIM zero, %c[ValidRow], ->lb1\n" \ + "B.DIM zero, %c[Cols], ->lb2\n" +#define PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS \ + [Type] "i"(type_traits::TypeCode), \ + [View] "Tr"(view.data()), [Tile] "Tr"(tile.data()), \ + [ValidCol] "i"(SourceSubTile::ValidCol), \ + [ValidRow] "i"(SourceSubTile::ValidRow), \ + [Cols] "i"(SourceSubTile::Cols), [ParentSize] "i"(ParentSize), \ + [SourceOffset] "r"(source_base_units), \ + [DestinationOffset] "r"(destination_base_units), [Opcode] "i"(Opcode), \ + [SubviewSize] "i"(subview_size), [WriterSize] "i"(writer_size), \ + [Init] "i"(Init), [Last] "i"(Last), \ + [ElemLayout] "i"(local_layout_code_v) + if constexpr (Init && ViewIsSrc0) { + asm volatile( + PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX + "B.IOT %[View], %[Tile], mask=1111, last, " + "->%[Dst]<%Z[ParentSize]>\n" + "B.SUBVIEW 0, %[SourceOffset], 0, %c[SubviewSize]\n" + "B.ASSEMBLE %c[Init], %c[Last], %[DestinationOffset], 0, " + "%c[WriterSize]\n" + : [Dst] "=Tr"(dst.template parent_data()) + : PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS + : "memory"); + } else if constexpr (Init) { + asm volatile( + PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX + "B.IOT %[Tile], %[View], mask=1111, last, " + "->%[Dst]<%Z[ParentSize]>\n" + "B.SUBVIEW 1, %[SourceOffset], 0, %c[SubviewSize]\n" + "B.ASSEMBLE %c[Init], %c[Last], %[DestinationOffset], 0, " + "%c[WriterSize]\n" + : [Dst] "=Tr"(dst.template parent_data()) + : PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS + : "memory"); + } else if constexpr (ViewIsSrc0) { + asm volatile(PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX + "B.IOT %[View], %[Tile], mask=1111\n" + "B.SUBVIEW 0, %[SourceOffset], 0, %c[SubviewSize]\n" + "B.IOT %[Dst], mask=1111, last\n" + "B.ASSEMBLE %c[Init], %c[Last], " + "%[DestinationOffset], 0, %c[WriterSize]\n" + : + : [Dst] "Tr"(dst.template parent_data()), + PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS + : "memory"); + } else { + asm volatile(PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX + "B.IOT %[Tile], %[View], mask=1111\n" + "B.SUBVIEW 1, %[SourceOffset], 0, %c[SubviewSize]\n" + "B.IOT %[Dst], mask=1111, last\n" + "B.ASSEMBLE %c[Init], %c[Last], " + "%[DestinationOffset], 0, %c[WriterSize]\n" + : + : [Dst] "Tr"(dst.template parent_data()), + PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS + : "memory"); + } +#undef PTO_REGION_BINARY_MIXED_ASSEMBLY_INPUTS +#undef PTO_REGION_BINARY_MIXED_ASSEMBLY_PREFIX +} + +template + requires(is_tile_data_v && !is_subtile_view_v && + !is_range_v) +PTO_REGION_ALWAYS_INLINE void pto_region_binary_mixed_assemble_phase( + region::TileArrayOutputRef &dst, + region::SubTileView &view, Tile &tile) { + if (dst.slot_count() == 1) + pto_region_binary_mixed_assemble(dst, view, tile); + else if (dst.ordinal() == 0) + pto_region_binary_mixed_assemble(dst, view, tile); + else if (dst.ordinal() == dst.slot_count() - 1) + pto_region_binary_mixed_assemble(dst, view, tile); + else + pto_region_binary_mixed_assemble(dst, view, tile); +} + +template + requires(is_tile_data_v && !is_subtile_view_v && + !is_range_v) +PTO_REGION_ALWAYS_INLINE void pto_region_binary_mixed_assemble_dispatch( + region::TileArrayOutputRef &dst, + region::SubTileView &view, Tile &tile) { + switch (dst.parent_size_code()) { +#define PTO_REGION_BINARY_MIXED_PARENT_CASE(N) \ + case N: \ + pto_region_binary_mixed_assemble_phase(dst, view, \ + tile); \ + break + PTO_REGION_BINARY_MIXED_PARENT_CASE(1); + PTO_REGION_BINARY_MIXED_PARENT_CASE(2); + PTO_REGION_BINARY_MIXED_PARENT_CASE(3); + PTO_REGION_BINARY_MIXED_PARENT_CASE(4); + PTO_REGION_BINARY_MIXED_PARENT_CASE(5); + PTO_REGION_BINARY_MIXED_PARENT_CASE(6); + PTO_REGION_BINARY_MIXED_PARENT_CASE(7); + PTO_REGION_BINARY_MIXED_PARENT_CASE(8); + PTO_REGION_BINARY_MIXED_PARENT_CASE(9); + PTO_REGION_BINARY_MIXED_PARENT_CASE(10); + PTO_REGION_BINARY_MIXED_PARENT_CASE(11); + PTO_REGION_BINARY_MIXED_PARENT_CASE(12); +#undef PTO_REGION_BINARY_MIXED_PARENT_CASE + default: + __builtin_trap(); + } +} + #define PTO_REGION_BINARY_DEST_WRAPPER(Name, Opcode) \ template \ @@ -2135,6 +2310,24 @@ PTO_REGION_ALWAYS_INLINE void pto_region_binary_tile_assemble_dispatch( PTO_REGION_ALWAYS_INLINE void Name(region::TileArrayOutputRef dst, \ Src0 &src0, Src1 &src1) { \ pto_region_binary_tile_assemble_dispatch(dst, src0, src1); \ + } \ + template \ + requires(is_tile_data_v && !is_subtile_view_v && \ + !is_range_v) \ + PTO_REGION_ALWAYS_INLINE void Name( \ + region::TileArrayOutputRef dst, \ + region::SubTileView &src0, Tile &src1) { \ + pto_region_binary_mixed_assemble_dispatch(dst, src0, src1); \ + } \ + template \ + requires(is_tile_data_v && !is_subtile_view_v && \ + !is_range_v) \ + PTO_REGION_ALWAYS_INLINE void Name( \ + region::TileArrayOutputRef dst, Tile &src0, \ + region::SubTileView &src1) { \ + pto_region_binary_mixed_assemble_dispatch(dst, src1, src0); \ } PTO_REGION_BINARY_DEST_WRAPPER(TADD, 0) diff --git a/test/tileop_api/compile.all b/test/tileop_api/compile.all index ced2058..ba31a45 100755 --- a/test/tileop_api/compile.all +++ b/test/tileop_api/compile.all @@ -30,7 +30,7 @@ ACTIVE_FIXTURES=( TMatmulAccFullOptions TMatmulAccCScale TMatmulAllOptions TGEMVAllOptions TGEMVOperandOrder PostProcessCombos SharedMatrixForms TCmpMode TColSumContract TSort TPrefetch MGatherCas MGatherAtomics MScatterAtomics TMrgSort TQuant TImg2Col GroupMatmul TStoreShared TStoreAtomic SharedTLoad TAbs - DirectCubeLayout TileRegionBinaryMixed Issue166RmsNormB128 Issue269ReductionCapacity Issue272PackPrefix Issue274PackViews Issue275PackAssemble Issue276ReinterpretMul Issue288CubeM32BinaryAssembly TCvtSameTypeRounding + DirectCubeLayout TileRegionBinaryMixed Issue166RmsNormB128 Issue269ReductionCapacity Issue272PackPrefix Issue274PackViews Issue275PackAssemble Issue276ReinterpretMul Issue288CubeM32BinaryAssembly Issue299MixedBinaryAssembly TCvtSameTypeRounding TAdd_mask TAdd TAdds TAnd TeplAss TeplAssSpecial TOrAssSubview TLoad TStore TCvt TDiv TDivs TExp TExpdif TMax TMaxs RangeSubview RangeAssemble SharedRange TileRegionSharedSubview TileArray TileArrayCube TileArrayAssemblyOffsets TileArrayRegionAsm TileArrayTCVTE8M0 TileArrayWriterSize TileRegionUnaryAssembly TileRegion TileRegionUnary TileRegionBinary TileRegionBinaryAssembly TileRegionUnarySubviewAssembly TileRegionScalarAssembly TileRegionTCVTSubviewAssembly TileRegionTCVTSubview TileRegionShift TileRegionCubeSubview SubviewVecCube Issue138Subview Issue160ReductionPrefix Issue241ReductionPrefixBinary Issue187DynamicPrefix Issue178CubeTcvtNorm Issue195ConvCompose Issue194Timg2colNchw SubviewVecCubeIssue144 ValidShapeImmediate ValidShapePerDim ValidShapePerDimSweep CoopGroupMOverloads CubeInternalAcc TSELCanonical Issue200TcmpsGpr Issue203CubeTcvtTileArray IssueA2CubeMxFp4Tcvt Issue111SharedSlot Issue290PartialCompareSelect diff --git a/test/tileop_api/run_negatives.sh b/test/tileop_api/run_negatives.sh index d3325a6..6aaff73 100755 --- a/test/tileop_api/run_negatives.sh +++ b/test/tileop_api/run_negatives.sh @@ -51,6 +51,7 @@ TCI2D_CASES="layout location dtype shape steps m16_rows" I64_CASES="fp64_element unsupported_parent m16 short_m32 subview_pair assemble_pair writer_limit" TCOLSUM_CASES="dest_rows dest_cols dtype" ISSUE288_CASES="layout dtype shape valid_shape" +ISSUE299_CASES="layout dtype shape valid_shape location capacity" ISSUE398399_CASES="atomic_add_dtype atomic_inc_dtype atomic_cube_n8 atomic_mask_rowmajor atomic_mask_shape atomic_mask_shared atomic_pe_mask cube_shared_dtype cube_shared_cols cube_shared_capacity cube_shared_writer_size" PASS=0; FAIL=0 @@ -120,6 +121,18 @@ for c in $ISSUE288_CASES; do esac expect_rejected "issue288_$c" "$define" Issue288Negatives.cpp "$pattern" done +for c in $ISSUE299_CASES; do + define=SHOULD_FAIL_ISSUE299_$(echo "$c" | tr '[:lower:]' '[:upper:]') + case "$c" in + layout) pattern='mixed-source assembly requires CUBE_M32' ;; + dtype) pattern='requires matching element types' ;; + shape) pattern='requires matching physical shapes' ;; + valid_shape) pattern='requires matching valid shapes' ;; + location) pattern='mixed sources must have matching locations' ;; + capacity) pattern='mixed sources must have matching capacities' ;; + esac + expect_rejected "issue299_$c" "$define" Issue299Negatives.cpp "$pattern" +done for c in $ISSUE398399_CASES; do define=SHOULD_FAIL_$(echo "$c" | tr '[:lower:]' '[:upper:]') pattern='atomic TSTORE|CUBE_N8|CUBE-to-Shared-ND|Shared ND|B\.ASSEMBLE|writer range' diff --git a/test/tileop_api/src/Issue299MixedBinaryAssembly.cpp b/test/tileop_api/src/Issue299MixedBinaryAssembly.cpp new file mode 100644 index 0000000..2ac3d87 --- /dev/null +++ b/test/tileop_api/src/Issue299MixedBinaryAssembly.cpp @@ -0,0 +1,82 @@ +#include + +#include +#include + +using namespace pto; + +using Fragment = VecTileM32; +using Parent = VecTileM32; + +static_assert(Fragment::LogicalTileBytes == 128); +static_assert(Parent::LogicalTileBytes == 1024); + +__attribute__((noinline)) Parent assemble_view_tile(Parent &parent, + Fragment &rhs) { + auto lhs = TPARTVIEW(parent)[0][7]; + TileArray slots; + TADD(slots[0][0], lhs, rhs); + TADD(slots[0][1], lhs, rhs); + TADD(slots[0][2], lhs, rhs); + TADD(slots[0][3], lhs, rhs); + TADD(slots[0][4], lhs, rhs); + TADD(slots[0][5], lhs, rhs); + TADD(slots[0][6], lhs, rhs); + TADD(slots[0][7], lhs, rhs); + return TASSEMBLY(std::move(slots)); +} + +__attribute__((noinline)) Parent assemble_tile_view(Fragment &lhs, + Parent &parent) { + auto rhs = TPARTVIEW(parent)[0][6]; + TileArray slots; + TADD(slots[0][0], lhs, rhs); + TADD(slots[0][1], lhs, rhs); + TADD(slots[0][2], lhs, rhs); + TADD(slots[0][3], lhs, rhs); + TADD(slots[0][4], lhs, rhs); + TADD(slots[0][5], lhs, rhs); + TADD(slots[0][6], lhs, rhs); + TADD(slots[0][7], lhs, rhs); + return TASSEMBLY(std::move(slots)); +} + +#define INSTANTIATE_MIXED_BINARY(Name) \ + do { \ + TileArray left_slots; \ + Name(left_slots[0][0], view, tile); \ + Fragment left = TASSEMBLY(std::move(left_slots)); \ + TileArray right_slots; \ + Name(right_slots[0][0], tile, view); \ + Fragment right = TASSEMBLY(std::move(right_slots)); \ + asm volatile("" : : "Tr"(left.data()), "Tr"(right.data())); \ + } while (false) + +__attribute__((noinline)) void instantiate_all_mixed_binary(Parent &parent, + Fragment &tile) { + auto view = TPARTVIEW(parent)[0][0]; + INSTANTIATE_MIXED_BINARY(TADD); + INSTANTIATE_MIXED_BINARY(TSUB); + INSTANTIATE_MIXED_BINARY(TMUL); + INSTANTIATE_MIXED_BINARY(TDIV); + INSTANTIATE_MIXED_BINARY(TREM); + INSTANTIATE_MIXED_BINARY(TAND); + INSTANTIATE_MIXED_BINARY(TOR); + INSTANTIATE_MIXED_BINARY(TXOR); + INSTANTIATE_MIXED_BINARY(TSHL); + INSTANTIATE_MIXED_BINARY(TSHR); + INSTANTIATE_MIXED_BINARY(TMAX); + INSTANTIATE_MIXED_BINARY(TMIN); +} + +#undef INSTANTIATE_MIXED_BINARY + +int main() { + Parent source_parent; + Fragment tile; + Parent left = assemble_view_tile(source_parent, tile); + Parent right = assemble_tile_view(tile, source_parent); + instantiate_all_mixed_binary(source_parent, tile); + asm volatile("" : : "Tr"(left.data()), "Tr"(right.data())); + return 0; +} diff --git a/test/tileop_api/src/Issue299Negatives.cpp b/test/tileop_api/src/Issue299Negatives.cpp new file mode 100644 index 0000000..fcdbde8 --- /dev/null +++ b/test/tileop_api/src/Issue299Negatives.cpp @@ -0,0 +1,46 @@ +#include + +#include + +using namespace pto; + +using Fragment = VecTileM32; +using Parent = VecTileM32; + +template void mixed_left(Parent &parent, Tile &tile) { + auto view = TPARTVIEW(parent)[0][0]; + TileArray slots; + TADD(slots[0][0], view, tile); +} + +template void mixed_right(Parent &parent, Tile &tile) { + auto view = TPARTVIEW(parent)[0][0]; + TileArray slots; + TADD(slots[0][0], tile, view); +} + +#if defined(SHOULD_FAIL_ISSUE299_LAYOUT) +using Bad = Tile; +#elif defined(SHOULD_FAIL_ISSUE299_DTYPE) +using Bad = VecTileM32; +#elif defined(SHOULD_FAIL_ISSUE299_SHAPE) +using Bad = VecTileM32; +#elif defined(SHOULD_FAIL_ISSUE299_VALID_SHAPE) +using Bad = VecTileM32; +#elif defined(SHOULD_FAIL_ISSUE299_LOCATION) +using Bad = CubeTileM32; +#elif defined(SHOULD_FAIL_ISSUE299_CAPACITY) +using Bad = VecTileM32; +#endif + +#if defined(SHOULD_FAIL_ISSUE299_LAYOUT) || \ + defined(SHOULD_FAIL_ISSUE299_DTYPE) || \ + defined(SHOULD_FAIL_ISSUE299_SHAPE) || \ + defined(SHOULD_FAIL_ISSUE299_VALID_SHAPE) || \ + defined(SHOULD_FAIL_ISSUE299_LOCATION) || \ + defined(SHOULD_FAIL_ISSUE299_CAPACITY) +void reject_left(Parent &parent, Bad &bad) { mixed_left(parent, bad); } +void reject_right(Parent &parent, Bad &bad) { mixed_right(parent, bad); } +#endif + +int main() { return 0; } diff --git a/test/tileop_api/verify_target_cxx_frontend.sh b/test/tileop_api/verify_target_cxx_frontend.sh index 346ec72..34ebfe4 100755 --- a/test/tileop_api/verify_target_cxx_frontend.sh +++ b/test/tileop_api/verify_target_cxx_frontend.sh @@ -50,6 +50,7 @@ for source in RangeSubview.cpp GMov.cpp TileRegionUnary.cpp \ Issue253RowExpandDualSubview.cpp \ Issue268CooperativeRowMax.cpp \ Issue288CubeM32BinaryAssembly.cpp \ + Issue299MixedBinaryAssembly.cpp \ TOrAssSubview.cpp \ TileArrayTCVTE8M0.cpp \ SharedTransposeNonSquare.cpp TCI.cpp TRowExpandMul.cpp; do @@ -112,6 +113,73 @@ expected += [(7, "false", "true")] if tails != expected: raise SystemExit(f"Issue288: incorrect offsets or lifecycle {tails}") PY_ISSUE288 +"$TC_DIR/clang++" "${FLAGS[@]}" -S -emit-llvm \ + "$ROOT/test/tileop_api/src/Issue299MixedBinaryAssembly.cpp" \ + -o "$OUT/Issue299MixedBinaryAssembly.ll" +"$TC_DIR/clang++" "${FLAGS[@]}" -c \ + "$ROOT/test/tileop_api/src/Issue299MixedBinaryAssembly.cpp" \ + -o "$OUT/Issue299MixedBinaryAssembly.o" +python3 - "$OUT/Issue299MixedBinaryAssembly.ll" <<'PY_ISSUE299' +import re +import sys +from pathlib import Path + +text = Path(sys.argv[1]).read_text(encoding="utf-8") +functions = re.split(r"(?=^define )", text, flags=re.MULTILINE) +for name, selector, source_offset in ( + ("assemble_view_tile", 0, 7), + ("assemble_tile_view", 1, 6)): + body = next((part for part in functions if name in part), None) + if body is None: + raise SystemExit(f"Issue299: missing mixed assembly fixture {name}") + ops = [line for line in body.splitlines() + if "asm sideeffect" in line and "BSTART.TEPL" in line] + if len(ops) != 8: + raise SystemExit(f"Issue299: {name} expected 8 producers, got {len(ops)}") + if any("B.DATR CUBE_M32" not in op for op in ops): + raise SystemExit(f"Issue299: {name} did not preserve CUBE_M32 layout") + if any(op.count("B.SUBVIEW") != 1 or + f"B.SUBVIEW {selector}," not in op for op in ops): + raise SystemExit(f"Issue299: {name} has an incorrect source selector") + if any("B.ASSEMBLE" not in op for op in ops): + raise SystemExit(f"Issue299: {name} is missing destination assembly") + if any(token in op for op in ops for token in ("TADDS", "TCVT", "TMOV")): + raise SystemExit(f"Issue299: {name} materialized a mixed source") + if "mask=1111, last, ->$0<${7:Z}>" not in ops[0]: + raise SystemExit(f"Issue299: {name} INIT did not allocate the parent") + for op in ops[1:]: + between = op.split("B.SUBVIEW", 1)[1].split("B.ASSEMBLE", 1)[0] + if between.count("B.IOT") != 1 or "mask=1111, last" not in between: + raise SystemExit( + f"Issue299: {name} continuation did not bind ParentRef last") + tails = [] + for op in ops: + match = re.search( + rf"i32 4, i64 {source_offset}, i64 ([0-7]), i32 0, " + r"i32 1, i32 1, i1 (true|false), i1 (true|false), i32 29\)", + op) + if match is None: + raise SystemExit( + f"Issue299: {name} lost parent, subview, or writer capacity") + tails.append((int(match.group(1)), match.group(2), match.group(3))) + expected = [(0, "true", "false")] + expected += [(offset, "false", "false") for offset in range(1, 7)] + expected += [(7, "false", "true")] + if tails != expected: + raise SystemExit(f"Issue299: {name} lifecycle mismatch: {tails}") + +all_ops = next((part for part in functions + if "instantiate_all_mixed_binary" in part), None) +if all_ops is None: + raise SystemExit("Issue299: missing all-opcode instantiation fixture") +ops = [line for line in all_ops.splitlines() + if "asm sideeffect" in line and "BSTART.TEPL" in line] +if len(ops) != 24: + raise SystemExit(f"Issue299: expected 24 mixed opcode instances, got {len(ops)}") +if sum("B.SUBVIEW 0," in op for op in ops) != 12 or \ + sum("B.SUBVIEW 1," in op for op in ops) != 12: + raise SystemExit("Issue299: all binary operations must support both mixed orders") +PY_ISSUE299 "$TC_DIR/clang++" "${FLAGS[@]}" -S -emit-llvm \ "$ROOT/test/tileop_api/src/RangeAssemble.cpp" -o "$OUT/RangeAssemble.ll" "$TC_DIR/clang++" "${FLAGS[@]}" -c \