Add support for migraphx.max#2429
Conversation
There was a problem hiding this comment.
Verdict: APPROVE -- submitted as COMMENT (automated reviews are advisory) · Findings: 0 (0 Critical, 0 Major, 0 Minor)
Scope
Adds a new migraphx.max elementwise binary op to support efficient leaky relu. The change registers the op in MIGraphX.td (with Commutative), wires up lowerings to linalg.max and tosa.maximum, and adds Lit conversion tests.
Findings
No blocking issues found.
Notes
- The op definition, both conversion registrations, and the tests all follow the established elementwise-binary-op patterns in each file exactly.
Commutativeis appropriate formax. - Dtype coverage on the TOSA path is good (f32/f16/i32 tests). The Linalg test covers only f32, but that is consistent with the convention used by the other elementwise ops in
mixr-to-linalg-ops.mlir, so no gap is introduced. - Consider (optional, out of scope) adding a negative/verifier Lit test in a follow-up if
migraphx.maxshould reject mismatched element types, thoughAllElementTypesMatchon the base class already provides this and mirrors sibling ops that also lack such a test.
CI status
No non-self CI checks are in the fail/cancel buckets. The passing premerge checks (C/C++, Python format/lint) are green; the remaining Jenkins/Build/MIGraphX checks are still pending. The auto-review pipeline's own review check being in-progress is expected and not a CI failure.
There was a problem hiding this comment.
Pull request overview
This PR adds a new MIGraphX dialect elementwise binary op (migraphx.max) and wires it into existing MIGraphX lowering pipelines so it can be converted to both TOSA and Linalg IR.
Changes:
- Introduces
MIGraphX_MaxOp(migraphx.max) in the MIGraphX dialect TableGen. - Adds MIGraphX→TOSA lowering via
tosa.maximum. - Adds MIGraphX→Linalg lowering via
linalg.maxand extends conversion tests accordingly.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| mlir/include/mlir/Dialect/MIGraphX/IR/MIGraphX.td | Adds MIGraphX_MaxOp as an elementwise binary op (commutative). |
| mlir/lib/Conversion/MIGraphXToTosa/MIGraphXToTosa.cpp | Registers migraphx.max lowering to tosa.maximum. |
| mlir/lib/Conversion/MIGraphXToLinalg/MIGraphXToLinalg.cpp | Registers migraphx.max lowering to linalg.max. |
| mlir/test/Conversion/MIGraphXToTosa/mixr-to-tosa-ops.mlir | Adds FileCheck coverage for max lowering (f32/f16/i32). |
| mlir/test/Conversion/MIGraphXToLinalg/mixr-to-linalg-ops.mlir | Adds FileCheck coverage for max lowering to linalg.max. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #2429 +/- ##
===========================================
+ Coverage 82.57% 83.66% +1.09%
===========================================
Files 120 120
Lines 42852 43017 +165
Branches 7110 7153 +43
===========================================
+ Hits 35381 35988 +607
+ Misses 4815 4482 -333
+ Partials 2656 2547 -109
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
pabloantoniom
left a comment
There was a problem hiding this comment.
What's the plan for backporting to rocmlirTriton?
umangyadav
left a comment
There was a problem hiding this comment.
This PR has some problem related to propagating signedness.
migraphx.max accepts every integer type, including ui8/ui32, but both paths eventually use signed maximum:
[MIGraphX→Linalg] creates linalg.max, whose integer implementation is arith.maxsi.
MIGraphX→TOSA also loses integer signedness, and TOSA→Linalg lowers integer tosa.maximum to arith.maxsi.
For example, max(ui8(255), ui8(1)) becomes signed max(-1, 1) and returns 1.
Define IEEE maximum behavior, preserve unsigned comparisons across both lowering paths, and cover numerical and layout edge cases. Co-authored-by: Cursor <[email protected]>
| Location loc = op.getLoc(); | ||
| auto resultType = cast<RankedTensorType>( | ||
| getTypeConverter()->convertType(op.getResult().getType())); | ||
| assert(llvm::all_of( |
There was a problem hiding this comment.
This asserts every operand equals the 2x4 result type, but the new broadcast test max_ui32_broadcast in mlir/test/Conversion/MIGraphXToTosa/migraphx-max-semantics.mlir:39 expects the resulting linalg.generic to have ins(... : tensor<2x4xi32>, tensor<1x4xi32>). These two expectations cannot both hold: if a broadcasted (rank-matched, unequal) operand actually reaches this converter, this assert fires in an assertions-enabled build; and the unsigned custom-op is then lowered in RocmlirCustomTosaToLinalg.cpp with identity indexing maps for all inputs, which requires every linalg.generic input to have the output shape — a 1x4 input against a 2x4 output would fail linalg.generic verification. Conversely, if operands are always equalized to 2x4 (as the Linalg ElementwiseConverter requires), then the 1x4 FileCheck expectation won't match. Please confirm the unsigned-broadcast path builds valid IR (ideally via an assertions build) and reconcile the assert with the test; if broadcasting can reach the custom op, the identity-map lowering needs broadcast-aware indexing maps rather than pure identity maps.
There was a problem hiding this comment.
This turns out to be a false positive — the unsigned-broadcast path builds valid IR, and the assert never sees unequal operand types. I verified against an assertions-enabled build (LLVM_ENABLE_ASSERTIONS=ON).
The key point is that a broadcasted migraphx.shaped<2x4xui32, 0x1> operand is materialized into a full 2x4 tensor before MaxConverter runs. Lowering func.func(migraphx-to-tosa) on the broadcast case gives:
%5 = tosa.mul %3, %2, %4 : (tensor<2x4xi32>, tensor<1x4xi32>, tensor<1xi8>) -> tensor<2x4xi32>
%8 = tosa.custom %7, %5 {... operator_name = "unsigned_max"} : (tensor<2x4xi32>, tensor<2x4xi32>) -> tensor<2x4xi32>
So both operands reaching the custom op are 2x4, assert(value.getType() == resultType) holds, and the subsequent identity-map lowering in RocmlirCustomTosaToLinalg.cpp is valid. After the full pipeline the two generics are:
#map1 = affine_map<(d0, d1) -> (0, d1)>
%6 = linalg.generic {indexing_maps = [#map, #map1, #map], ...} ins(%3, %transposed : tensor<2x4xi32>, tensor<1x4xi32>) ... arith.muli // broadcast materialization
%10 = linalg.generic {indexing_maps = [#map, #map, #map], ...} ins(%8, %6 : tensor<2x4xi32>, tensor<2x4xi32>) ... arith.maxui // the max
The tensor<1x4xi32> the comment referenced belongs to the mul-by-ones broadcast generic (which correctly uses the broadcast map (0, d1)), not to the arith.maxui generic. The two FileCheck lines refer to two different linalg.generic ops, so they don't conflict. Both migraphx-max-semantics.mlir lit tests pass.
The confusion is fair, though — the check was written ambiguously. It's now tightened to bind the broadcast result and anchor the shape check to the max generic (ins(..., %[[BROADCAST]] : tensor<2x4xi32>, tensor<2x4xi32>)), and I added an explanatory comment in cf7f698 documenting that broadcast expansion precedes the max. Resolving as addressed.
There was a problem hiding this comment.
Resolved -- addressed in this revision.
There was a problem hiding this comment.
Verdict: COMMENT · Findings: 1 (0 Critical, 1 Major, 0 Minor)
Scope
Adds migraphx.max (elementwise IEEE-754 maximum) to the MIGraphX dialect and lowers it through both the TOSA and Linalg boundaries, including an unsigned_max custom-op path for unsigned integers. Touches the op definition (.td), verifier, both converters, the custom-TOSA→Linalg lowering, and adds dialect, conversion, and e2e tests.
Findings
mlir/lib/Conversion/MIGraphXToTosa/MIGraphXToTosa.cpp:1119— the equal-shapeasserton the newMaxConverterappears to be in tension with the new broadcast semantics test; please confirm the unsigned-broadcast path produces valid IR (details inline).
Notes
- Op design is solid:
hasVerifier = 1+verify()present, and the verifier correctly relies on theAllShapesMatch/AllElementTypesMatchtraits (checked beforeverify()) so testing onlyinAfor element type / static shape is fine, while strides are checked on all three operands. - Semantics look correct: floats use
tosa.maximum(PROPAGATE) /linalg.max→arith.maximumf(IEEE maximum, NaN-propagating, +0 > -0); signed/signless ints use signed max; unsigned ints usearith.maxui. Matches the documented description. - The
UnsignedCastLoweringPattern→UnsignedOpLoweringPatternrename and updatednotifyMatchFailuremessage are consistent with the broadened responsibility. - Test coverage is thorough: negative cases in
invalid.mlir(mismatched shapes, unsupported/complex element type, dynamic shape, dynamic stride, i1), roundtrip inops.mlir, conversion checks, an e2e template registered intests.toml, and a PR-e2e IEEE test exercising NaN/±0/inf.
CI status
No non-self checks report fail/cancel; all functional checks (C/C++ premerge, Python, review) are still IN_PROGRESS at review time, so lit/e2e results were not available to confirm the broadcast case above.
Clarify that the broadcasted operand is expanded to a full 2x4 tensor by a separate mul-by-ones generic before the unsigned max, so the max runs on equal-shaped operands and the MaxConverter assert / identity-map custom-op lowering stay valid. Prevents misreading the 1x4 shape check as belonging to the max op. Co-authored-by: Cursor <[email protected]>
There was a problem hiding this comment.
Verdict: APPROVE -- submitted as COMMENT (automated reviews are advisory) · New findings: 0 (0 Critical, 0 Major, 0 Minor)
Scope
Adds a new migraphx.max op (elementwise IEEE-754 maximum) with a verifier, plus TOSA and Linalg lowering paths. Floating-point and signed/signless integers lower to tosa.maximum (PROPAGATE) / linalg.max; unsigned integers route through a rocmlir/unsigned_max custom op that lowers to arith.maxui. Touches the dialect def, both conversion passes, the custom-tosa-to-linalg pass, and adds unit, conversion, custom-op, and E2E tests.
Findings
No blocking issues found.
Notes
- Semantics are well documented (op description) and enforced: the verifier rejects dynamic shapes/strides, non int/float element types, and i1, each with a matching negative lit test.
AllShapesMatch+ the baseAllElementTypesMatchcover the operand/result shape and element-type constraints, so the verifier's single-operand checks are sufficient. - IEEE
maximum(NaN propagation, +0 > -0) is exercised across both lowerings viamigraphx-max-semantics.mlir(TOSA and Linalg) and a dedicated runtime E2Emixr-max-ieee.e2e.mlir. The unsigned path has good lit coverage (rocmlir-custom-tosa-to-linalg.mlir, semantics test). - Minor (non-blocking): the fusion E2E template
mixr-gemm-maxonly expands over f32/f16/bf16, so the integer / unsignedarith.maxuicustom-op path has lit coverage but no runtime E2E. Worth considering in a follow-up, not required here.
CI status
No non-self CI failures at the reviewed SHA: C/C++ premerge, Python format/lint, and performance-script checks pass; Jenkins/Build-and-Test are still pending. The auto-review pipeline's own review check being in-progress is expected.
Reconciliation
The prior review's concern about the assert in MaxConverter conflicting with the max_ui32_broadcast expectation was rebutted by a maintainer (broadcast is materialized before the max op runs, verified on an assertions build) and the test was tightened to bind the broadcast result. Fresh review agrees it is not a real issue; resolving that thread.
Motivation
The
migraphx.maxis needed to support leaky relu efficiently.Technical Details
Test Plan
Test Result
Submission Checklist