[Feat] Complete BasisAttr support in IntTupleBuilder (#574)#638
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds first-class support for scaled-basis stride leaves (“CuTe E”) in the Python front-end and MLIR lowering/tests, including validation to avoid crashes/segfaults for invalid inputs.
Changes:
- Introduces
fx.E(...)andfx.make_basis_stride(...)for constructing basis stride leaves in Python. - Extends C++ int-tuple building and algebra utilities to handle basis leaves (notably
eq/ne/div) and improves assertion diagnostics. - Adds MLIR + Python unit tests and documentation examples for basis strides and related lowering behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_layout_algebra.py | Adds unit tests for fx.E, basis stride layout IR, and invalid-input behavior. |
| tests/mlir/Transforms/layout_lowering.mlir | Adds FileCheck coverage for fly.equal folding on basis leaves. |
| tests/mlir/LayoutAlgebra/basis.mlir | New MLIR tests for basis leaves flowing through tuple ops and logical_divide. |
| python/flydsl/expr/primitive.py | Adds Python API (E, make_basis_stride) + int32 validation helper. |
| lib/Dialect/Fly/Utils/IntTupleUtils.cpp | Extends int-tuple ops with basis-aware behavior and better asserts. |
| lib/Bindings/Python/FlyExtension.cpp | Adds duck-typed basis-leaf ingestion and fixes type-name reporting to avoid segfault. |
| include/flydsl/Dialect/Fly/Utils/LayoutUtils.h | Improves assertion to prevent divide-by-zero for non-tiling divisors. |
| docs/layout_system_guide.md | Documents the new basis stride construction APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| assert(lhs.isLeaf() && rhs.isLeafInt() && | ||
| "div is undefined for a basis divisor; lower the identity layout first"); | ||
| if (lhs.isLeafInt()) { | ||
| return IntTupleAttr::get(lhs.getLeafAsInt() / rhs.getLeafAsInt()); | ||
| } | ||
| return IntTupleAttr::get(intSafeDiv(lhs.getLeafAsBasis(), rhs.getLeafAsInt())); |
There was a problem hiding this comment.
Good catch — fixed. The precondition now gates the dividend explicitly to isLeafInt() || isLeafBasis() (with rhs.isLeafInt()), so getLeafAsBasis() is only reached on the basis branch and a none/other leaf trips the named assert instead of crashing:
assert((lhs.isLeafInt() || lhs.isLeafBasis()) && rhs.isLeafInt() &&
"div is undefined for a basis divisor; lower the identity layout first");
if (lhs.isLeafInt()) {
return IntTupleAttr::get(lhs.getLeafAsInt() / rhs.getLeafAsInt());
}
return IntTupleAttr::get(intSafeDiv(lhs.getLeafAsBasis(), rhs.getLeafAsInt()));| } else if (nb::hasattr(args, "__fly_basis__") && | ||
| PyObject_IsTrue(nb::object(args.attr("__fly_basis__")).ptr()) == 1) { |
There was a problem hiding this comment.
Fixed. Extracted an isMarkerTruthy helper that propagates the -1 case as a nb::python_error (which captures the pending Python exception) instead of treating it as false:
static bool isMarkerTruthy(nb::handle obj) {
int truth = PyObject_IsTrue(obj.ptr());
if (truth < 0) {
throw nb::python_error();
}
return truth == 1;
}So a __fly_basis__ that raises in its getter now surfaces the real error rather than silently falling through to the generic int path.
Completes BasisAttr (scaled-basis / CuTe E<I>) leaf support in IntTupleBuilder<IntTupleAttr>, finishing what ROCm#195 started, plus a Python construction surface (fx.E / fx.make_basis_stride). Implemented (basis is well-defined and reachable): - div(Basis, Int) -> (value/k)E: divide the coefficient, keep modes (reuses intSafeDiv(BasisAttr, IntAttr)). - eq / ne: compare basis leaves by (coefficient, modes); a basis monomial never equals a plain integer leaf. Kept integer-only with precise assert messages (mod, lt/le/gt/ge, min/max, shapeDiv, logicalAnd/Or/Not, applySwizzle, applyCoordSwizzle): each is either ill-posed on free-module generators or structurally unreachable. div(Basis, Basis) (reachable via complement of a rank>=2 identity layout) has no quotient mode and is rejected with a named assert. compositionImpl rejects a non-tiling divisor (0-extent complement mode) before dividing by zero. Python: fx.E(mode, *, value=1) and fx.make_basis_stride(value, modes), wired through the int-tuple builder via a __fly_basis__ duck-type marker. Review fixes: - div precondition gated to isLeafInt() || isLeafBasis() so getLeafAsBasis() is never called on a none leaf. - FlyExtension marker truthiness propagates a -1 PyObject_IsTrue error as a nanobind python_error instead of silently treating it as false. - make_stride reports Py_TYPE(...)->tp_name to avoid a segfault on a non-stride object (nb::type_name on an instance). Tests: tests/mlir/LayoutAlgebra/basis.mlir (div + identity logical_divide rank-2/3), equal-folding in Transforms/layout_lowering.mlir, and the fx.E surface + validation + falsy-marker reject in tests/unit/test_layout_algebra.py. Closes ROCm#574 (the implementable core). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b3c06cf to
4215c2a
Compare
|
Rebased onto current Conflict. The restored branch sat on the old head's pre-squash history, so GitHub had no common ancestor with Review fixes (both Copilot points, replied inline):
Verify (assertions live): @sjfeng1999 this is the implementable core of #574 — |
Closes #574 (the implementable core — see the scope note in the issue thread).
What
Completes
BasisAttr(scaled-basis / CuTeE<I>) leaf support inIntTupleBuilder<IntTupleAttr>, finishing what #195 started (it extended onlymul/safeDiv/ceilDiv), plus a Python construction surface.Implemented — basis is well-defined and reachable:
div(Basis, Int)→(value/k)·E: divide the coefficient, keep modes. Reuses the existingintSafeDiv(BasisAttr, IntAttr); no new scalar overload.eq/ne: compare basis leaves by(coefficient, modes); a basis monomial never equals a plain integer leaf.Kept integer-only, now with precise assert messages (
mod,lt/le/gt/ge,min/max,shapeDiv,logicalAnd/Or/Not,applySwizzle,applyCoordSwizzle): for each, basis is either ill-posed (no total order on free-module generators; bit-XOR on a symbol; symmetric integer divisibility) or structurally unreachable. Implementing formulas there would be untested dead code — per-op reasoning is in the #574 thread.Python:
fx.E(mode, *, value=1)andfx.make_basis_stride(value, modes), wired through the int-tuple builder via a__fly_basis__duck-type marker.fx.make_layout(fx.make_shape(4, 8), fx.make_stride(fx.E(0), fx.E(1)))is byte-identical tomake_identity_layout((4, 8)).Two notes
div(Basis, Basis)is reachable for rank≥3 identity layouts viacomplement(LayoutUtils.h:lastStridebecomes basis aftermul(minStride, shape), so the nextdiv(minStride, lastStride)is basis-by-basis). It has no quotient mode, so it's rejected with a named assert rather than miscomputing a stride.emitOpErroris out of scope here.IntTupleBuilder<IntTupleAttr>carries only anMLIRContext*, noLocation, so located diagnostics aren't reachable at this layer — asserts are the deliberate invariant (theIntTupleValueAdaptortwin asserts too). The user-facing op-layer gate belongs to the sibling issue [Compiler]: Adding verification to the layout-algebra ops — what's the right approach? #583. This PR and [Compiler]: Adding verification to the layout-algebra ops — what's the right approach? #583 touch disjoint files (IntTupleUtils.cppasserts vsFlyOps.cppinference).Verification
bash scripts/build.shwith assertions live — clean.tests/mlirFileCheck tests pass, incl. the newLayoutAlgebra/basis.mlir(div + identitylogical_divideinference) andequalfolding appended toTransforms/layout_lowering.mlir.tests/unit/test_layout_algebra.py: 24 passed / 1 pre-existing skip, incl. the newfx.Esurface and a fullconvert-fly-to-rocdlpipeline test; broader unit sweep 398 passed, 0 failed.🤖 Generated with Claude Code