From de846561bab69227e118af42409389bb340c5529 Mon Sep 17 00:00:00 2001 From: LinxISA Automation Date: Tue, 8 Sep 2026 00:35:15 +0800 Subject: [PATCH] TileOP: enforce CUBE InternalAcc CCTRL legality (spec#236) Complete the Phase-1 TileOP adaptation for the PTO-ISA CUBE InternalAcc contract (spec#236, merge 33645f4): - Wire validate_cube_ctrl_contract into the basic (non-Options) matrix emitters: matmul, matmul_acc, matmul_bias (3SRC), matmul_mx, and the MX 5SRC helper (IsAcc-parameterized). Previously the validator existed but had zero call sites. - Fix template-argument mispositioning in all 12 validate_matrix_postprocess_contract call sites: the bool argument landed on the MX parameter instead of IsAccForm, so every non-ACC emit path was validated as if it were an ACC form (CCTRL[1] hint check was inert) and two ACC emitters had MX erroneously set to true (forcing the FP32 accumulator code path). - Make FixpAttr::with_cube_ctrl a chainable const member (matching transpose_a/transpose_b style). As a static constructor it silently dropped every other configured attribute (PreQuant, Relu, ...) when combined, which let raw-D + post-process combinations slip through. - Golden fixture CubeInternalAcc.cpp: the four CCTRL values lower to the B.DATR PadValueOrByteId[1:0] union field (None->Zero, Raw->Max, Hint->Min, Raw|Hint->Null). - Negative runner CubeInternalAccNegatives.cpp: CCTRL[1] on non-ACC TMATMUL, CCTRL[0] raw D with f16 PreQuant (dtype-matching dst), and CCTRL[0] with Relu are all rejected at compile time (3/3). Verified: unittest 40/40; SharedMatmul/GroupMatmul/TMatmulAcc*, TGEMV*, MX*, and the per-dimension sweep fixtures all compile clean. --- include/common/pto_tile.hpp | 6 ++-- include/jcore/template_asm.hpp | 16 +++++---- test/tileop_api/compile.all | 2 +- test/tileop_api/src/CubeInternalAcc.cpp | 35 +++++++++++++++++++ .../src/CubeInternalAccNegatives.cpp | 33 +++++++++++++++++ 5 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 test/tileop_api/src/CubeInternalAcc.cpp create mode 100644 test/tileop_api/src/CubeInternalAccNegatives.cpp diff --git a/include/common/pto_tile.hpp b/include/common/pto_tile.hpp index 8d60c28..307a49e 100644 --- a/include/common/pto_tile.hpp +++ b/include/common/pto_tile.hpp @@ -156,8 +156,10 @@ struct FixpAttr { // (raw accumulator D) forbids final post-process; CCTRL[1] (ACC-only // transparent cache hint) is validated against the operation kind at the // wrapper layer. - static constexpr FixpAttr with_cube_ctrl(CubeControl Ctrl) { - FixpAttr Attr; + // Chainable: preserves all other attributes, unlike the parameter-free + // static constructors (f16/bf16/keep_acc) which start from defaults. + constexpr FixpAttr with_cube_ctrl(CubeControl Ctrl) const { + FixpAttr Attr = *this; Attr.CubeCtrl = Ctrl; return Attr; } diff --git a/include/jcore/template_asm.hpp b/include/jcore/template_asm.hpp index e35c6d7..a20e891 100644 --- a/include/jcore/template_asm.hpp +++ b/include/jcore/template_asm.hpp @@ -3944,6 +3944,7 @@ PTO_SHARED_INLINE void matmul(Dst &dst, A &a, B &b, size_t M, size_t N, // Local-A/Shared-B the A shard descriptor is per-PE (valid_rows == pe_m) // and cannot supply group_M, so the caller must pass it here. validate_matrix_contract(); + validate_cube_ctrl_contract(); if constexpr (!is_shared_tile_v && !is_shared_tile_v) { asm volatile( PTO_MATMUL_HEADER("TMATMUL", PTO_FIXP_ATTR) @@ -4058,6 +4059,7 @@ PTO_SHARED_INLINE void matmul_acc(Dst &dst, C &c, A &a, B &b, size_t M, size_t N, size_t K) { validate_matrix_contract(); validate_matrix_accumulator_contract(); + validate_cube_ctrl_contract(); if constexpr (!is_shared_tile_v && !is_shared_tile_v) { asm volatile( PTO_MATMUL_HEADER("TMATMUL.ACC", PTO_FIXP_ATTR) @@ -5590,7 +5592,7 @@ PTO_SHARED_INLINE void emit_fixp( uint64_t quant_gpr, uint64_t lrelu_gpr, size_t M, size_t N, size_t K) { validate_matrix_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, false>(); if constexpr (!is_shared_tile_v && !is_shared_tile_v) { PTO_FIXP_DISPATCH(PTO_FIXP_EMIT_LOCAL); } else if constexpr (is_shared_tile_v && !is_shared_tile_v) { @@ -5625,7 +5627,7 @@ PTO_SHARED_INLINE void emit_matmul_acc_fixp( validate_matrix_accumulator_contract(); validate_cscale_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, true>(); if constexpr (!is_shared_tile_v && !is_shared_tile_v) { PTO_FIXP_DISPATCH(PTO_FIXP_ACC_EMIT_LOCAL); } else if constexpr (is_shared_tile_v && !is_shared_tile_v) { @@ -5649,7 +5651,7 @@ PTO_SHARED_INLINE void emit_matmul_bias_fixp( validate_matrix_contract(); validate_matrix_bias_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, false>(); if constexpr (!is_shared_tile_v && !is_shared_tile_v) { PTO_FIXP_DISPATCH(PTO_FIXP_BIAS_EMIT_LOCAL); } else if constexpr (is_shared_tile_v && !is_shared_tile_v) { @@ -5839,7 +5841,7 @@ PTO_SHARED_INLINE void emit_gemv_fixp( uint64_t quant_gpr, uint64_t lrelu_gpr, size_t M, size_t N, size_t K) { validate_gemv_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, false>(); PTO_FIXP_DISPATCH(PTO_FIXP_GV_GV_EMIT_LOCAL); } @@ -5858,7 +5860,7 @@ PTO_SHARED_INLINE void emit_gemv_bias_fixp( validate_gemv_contract(); validate_matrix_bias_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, false>(); PTO_FIXP_DISPATCH(PTO_FIXP_GV_GVB_EMIT_LOCAL); } @@ -5877,7 +5879,7 @@ PTO_SHARED_INLINE void emit_gemv_acc_fixp( validate_gemv_contract(); validate_matrix_accumulator_contract(); validate_matrix_postprocess_contract(); + RowIn, QuantTile, ReluTile, RowOut, GroupOut, false, true>(); PTO_FIXP_DISPATCH(PTO_FIXP_GV_GVA_EMIT_LOCAL); } @@ -6025,6 +6027,7 @@ PTO_SHARED_INLINE void Name(Dst &dst, A &a, ScaleA &scale_a, B &b, \ constexpr bool HasScaleB = true; \ validate_matrix_contract(); \ validate_matrix_scale_contract(); \ + validate_cube_ctrl_contract(); \ if constexpr (!is_shared_tile_v && !is_shared_tile_v) { \ asm volatile( \ PTO_MATMUL_HEADER(Opcode, PTO_FIXP_ATTR) \ @@ -6145,6 +6148,7 @@ PTO_SHARED_INLINE void Name(Dst &dst, A &a, ScaleA &scale_a, B &b, \ validate_matrix_accumulator_contract(); \ else \ validate_matrix_bias_contract(); \ + validate_cube_ctrl_contract(); \ if constexpr (!is_shared_tile_v && !is_shared_tile_v) { \ asm volatile( \ PTO_MATMUL_HEADER(Opcode, PTO_FIXP_ATTR) \ diff --git a/test/tileop_api/compile.all b/test/tileop_api/compile.all index be64cfc..3b50dc8 100755 --- a/test/tileop_api/compile.all +++ b/test/tileop_api/compile.all @@ -25,7 +25,7 @@ ACTIVE_FIXTURES=( TAdd_mask TAdd TAdds TAnd TLoad TStore TCvt TDiv TDivs TExp TMax TMaxs TMov TMul TMuls TOr TRecip TRem TRowMax TRowSum TSqrt TSub TSubs TTrans RangeSubview RangeAssemble SharedRange TileRegion TileArray TileArrayCube TileArrayAssemblyOffsets TileArrayRegionAsm TileRegionUnaryAssembly TileRegionUnary TileRegionBinary TileRegionBinaryAssembly TileRegionUnarySubviewAssembly TileRegionScalarAssembly TileRegionTCVTSubviewAssembly TileRegionSharedSubview TileRegionCubeSubview - ValidShapeImmediate ValidShapePerDim ValidShapePerDimSweep TSELCanonical + ValidShapeImmediate ValidShapePerDim ValidShapePerDimSweep CubeInternalAcc TSELCanonical ) # Pre-v0.58 wrapper designs remain available to cpu_sim only and are not part diff --git a/test/tileop_api/src/CubeInternalAcc.cpp b/test/tileop_api/src/CubeInternalAcc.cpp new file mode 100644 index 0000000..0f89e9a --- /dev/null +++ b/test/tileop_api/src/CubeInternalAcc.cpp @@ -0,0 +1,35 @@ +// CUBE InternalAcc (PTO-ISA 0.58.6 spec#236) regression: CCTRL encoding and +// legality. Positive cases lower the four CCTRL values into the B.DATR +// PadValueOrByteId[1:0] union field; illegal combinations must be rejected +// at compile time (see the paired negative runner). +#include + +using namespace pto; + +using A = SharedMatrixLeft; +using B = SharedMatrixRight; +using C = CubeAccumulatorM16; +using Opt = fixp::Options; + +// CCTRL = 0 (None) -> B.DATR ..., Zero, ... +__attribute__((noinline)) void acc_none(C &d, C &c, A &a, B &b) { + auto sa = TMOV_L2S_INSERT(a); auto sb = TMOV_L2S_INSERT(b); + TMATMUL_ACC(d, c, sa, sb, Opt{}); +} +// CCTRL = 2 (InternalAccHint, ACC-only transparent cache hint) -> Min +__attribute__((noinline)) void acc_hint(C &d, C &c, A &a, B &b) { + auto sa = TMOV_L2S_INSERT(a); auto sb = TMOV_L2S_INSERT(b); + TMATMUL_ACC(d, c, sa, sb, Opt{}.acc_hint()); +} +// CCTRL = 1 (RawAccumulator, raw accumulator-type D) -> Max +__attribute__((noinline)) void acc_raw(C &d, C &c, A &a, B &b) { + auto sa = TMOV_L2S_INSERT(a); auto sb = TMOV_L2S_INSERT(b); + TMATMUL_ACC(d, c, sa, sb, Opt{}.raw_acc()); +} +// CCTRL = 3 (RawAccumulator | InternalAccHint) -> Null +__attribute__((noinline)) void acc_raw_hint(C &d, C &c, A &a, B &b) { + auto sa = TMOV_L2S_INSERT(a); auto sb = TMOV_L2S_INSERT(b); + TMATMUL_ACC(d, c, sa, sb, Opt{}.raw_acc().acc_hint()); +} + +int main() { return 0; } diff --git a/test/tileop_api/src/CubeInternalAccNegatives.cpp b/test/tileop_api/src/CubeInternalAccNegatives.cpp new file mode 100644 index 0000000..21c9fb3 --- /dev/null +++ b/test/tileop_api/src/CubeInternalAccNegatives.cpp @@ -0,0 +1,33 @@ +// CUBE InternalAcc negative contracts: each NEG_CASE must fail to compile. +// Driven by run_negatives-style invocations (-DNEG_CASE=N). +#include + +using namespace pto; + +using A = SharedMatrixLeft; +using B = SharedMatrixRight; +using C = CubeAccumulatorM16; +using CH = Tile; + +#ifdef NEG_CASE +__attribute__((noinline)) void neg( +#ifdef NEG_CASE2_DST + CH &d, +#else + C &d, +#endif + C &c, A &a, B &b) { + auto sa = TMOV_L2S_INSERT(a); auto sb = TMOV_L2S_INSERT(b); +#if NEG_CASE == 1 + // CCTRL[1] hint on a non-ACC TMATMUL: must be rejected. + TMATMUL(d, sa, sb, fixp::Options{}); +#elif NEG_CASE == 2 + // CCTRL[0] raw D + f16 PreQuant (dtype-matching dst): must be rejected. + TMATMUL_ACC(d, c, sa, sb, fixp::Options{}); +#elif NEG_CASE == 3 + // CCTRL[0] raw D + Relu: must be rejected. + TMATMUL_ACC(d, c, sa, sb, fixp::Options{}); +#endif +} +int main() { return 0; } +#endif