Skip to content

Commit e389707

Browse files
xudoyuanclaude
andcommitted
expr/arith: add type-generic max()/min()
Add a single type-generic max()/min() in expr/arith.py, modeled on cutlass.max/min. Accepts any DSL numeric type (Float32/Int32/Int64/unsigned ...) and Python scalars, any number of args (max(a,b), max(a,b,c), max([a,b,...]), max(a,[x,y])). Return type follows the operands' static types. Dispatch reuses the shared numeric coercion (as_numeric + _coerce_operands) and the resulting type: float -> maximumf / minimumf (NaN-propagating, matches cutlass) int signed -> maxsi / minsi int unsigned -> maxui / minui Float fastmath follows the fast_fp_math compile hint. The maximum-vs-maxnum choice is not exposed (matches cutlass). Registered in __all__ (fx.max/fx.min); decorated with dsl_loc_tracing like the other builders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5f41e32 commit e389707

1 file changed

Lines changed: 63 additions & 0 deletions

File tree

python/flydsl/expr/arith.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
"xori",
3232
"cmpi",
3333
"cmpf",
34+
"max",
35+
"min",
3436
]
3537

3638
# Override star-import cmpi/cmpf to accept Numeric types (Int32, etc.)
3739
from .._mlir.dialects import arith as _mlir_arith
3840
from .meta import dsl_loc_tracing
3941
from .utils.arith import ( # noqa: F401
4042
ArithValue,
43+
_default_fastmath,
4144
_to_raw,
4245
andi,
4346
constant,
@@ -82,3 +85,63 @@ def cmpf(predicate, lhs, rhs, **kwargs):
8285
An ``i1`` comparison result.
8386
"""
8487
return _mlir_arith.cmpf(predicate, _to_raw(lhs), _to_raw(rhs), **kwargs)
88+
89+
90+
# ── Type-generic max / min ──────────────────────────────────────────────────
91+
# One entry point for any DSL numeric type (Float32/Int32/Int64/unsigned/...) and
92+
# Python scalars, any number of args. Reuses the shared numeric coercion
93+
# (as_numeric + _coerce_operands) and dispatches by the resulting type:
94+
# float -> maximumf / minimumf (NaN-propagating, matches cutlass.max)
95+
# int, signed -> maxsi / minsi
96+
# int, unsigned -> maxui / minui
97+
# The maximum-vs-maxnum choice is NOT exposed (matches cutlass). Return type
98+
# follows the operands' static type.
99+
100+
101+
def _minmax_pair(is_max, a, b):
102+
from .numeric import _coerce_operands, as_numeric
103+
104+
a, b, out_ty = _coerce_operands(as_numeric(a), as_numeric(b))
105+
lv, rv = a.ir_value(), b.ir_value()
106+
if out_ty.is_float:
107+
fn = _mlir_arith.maximumf if is_max else _mlir_arith.minimumf
108+
res = fn(lv, rv, fastmath=_default_fastmath())
109+
elif out_ty.signed:
110+
fn = _mlir_arith.maxsi if is_max else _mlir_arith.minsi
111+
res = fn(lv, rv)
112+
else:
113+
fn = _mlir_arith.maxui if is_max else _mlir_arith.minui
114+
res = fn(lv, rv)
115+
return out_ty(res)
116+
117+
118+
def _minmax(is_max, args):
119+
flat = []
120+
for a in args:
121+
if isinstance(a, (list, tuple)):
122+
flat.extend(a)
123+
else:
124+
flat.append(a)
125+
if not flat:
126+
raise ValueError("max()/min() requires at least one argument")
127+
acc = flat[0]
128+
for x in flat[1:]:
129+
acc = _minmax_pair(is_max, acc, x)
130+
return acc
131+
132+
133+
@dsl_loc_tracing
134+
def max(*args):
135+
"""Type-generic maximum over any number of DSL numeric args (and Python scalars).
136+
137+
Return type follows the operands' static types (not values). Accepts
138+
``max(a, b)``, ``max(a, b, c, ...)``, ``max([a, b, ...])``, ``max(a, [x, y])``.
139+
Float uses NaN-propagating ``maximumf``; signed/unsigned int use ``maxsi``/``maxui``.
140+
"""
141+
return _minmax(True, args)
142+
143+
144+
@dsl_loc_tracing
145+
def min(*args):
146+
"""Type-generic minimum. See :func:`max`."""
147+
return _minmax(False, args)

0 commit comments

Comments
 (0)