You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Issue #1405 (fixed by #1408) exposed a structural fragility in PTODSL's scalar lowering: integer values live in two type domains at once.
The MLIR arith domain only accepts signlessiN (per MLIR's design philosophy, signedness is a property of the operation — divsi vs divui — not of the type).
The authored PTODSL domain carries siN/uiN as value types throughout tracing.
The two domains are bridged by unrealized_conversion_cast strip/restore pairs (_strip_integer_signedness / _restore_integer_signedness in ptodsl/ptodsl/_types.py). This op is designed as a dialect-conversion placeholder ("this type will be legalized away later"), not as an everyday signedness bookkeeping device. Using it this way means every integer value can exist in a split form — a bare signless SSA value paired with an out-of-band signedness annotation — and any tracer path that caches, constant-folds, or otherwise reuses an intermediate SSA value with a stale annotation can emit invalid IR (in #1405: a si32 to i32 cast whose operand was actually an i32 constant).
#1408 fixes the literal-arithmetic instance of this, but the fix is symptomatic: it removes some strip/restore round trips rather than making the value/annotation mismatch structurally impossible. Other reuse paths (constant folding in _tracing/, control flow, sub-kernel boundaries) can still desync the two.
Proposal
Make signless the only integer identity inside tracing:
Value layer: during tracing, every integer SSA value is signless iN. siN/uiN never appear as intermediate value types, so _strip/_restore_integer_signedness disappear from hot paths.
Metadata layer: the frontend wrappers (_SurfaceValue / RuntimeValue in _surface_values.py) carry signedness as pure Python-side metadata whose only purpose is operator selection (FloorDivSIOp vs DivUIOp, slt vs ult, MaxSI vs MaxUI, ...). Sites that currently reverse-engineer signedness from value.type string prefixes (e.g. _runtime_scalar_ops.py) read the metadata instead.
Boundary layer: siN/uiN are still materialized at boundaries — kernel signatures/ABI, VMI storage — with exactly one strip on entry and one restore on exit per value. No cast chains.
After this, an SSA value has exactly one integer identity; signedness no longer lives in the IR at all, so there is nothing for the tracer to desync.
Add a signedness metadata field to the runtime-value wrappers; make _runtime_scalar_ops.py prefer metadata and fall back to the type. This step is independently testable.
Remove _strip/_restore from hot paths module by module (_scalar_adaptation.py, _scalar_coercion.py, _runtime_index_ops.py, control flow, sub-kernels), keeping them only at ABI/storage boundaries (consider renaming to _adapt_abi_signedness so misuse is visible in review).
Regression coverage: signed and unsigned runtime scalars mixed with literals across add/sub/mul/floordiv/mod/compare/min/max, including values >= 2^31 for the unsigned cases.
Non-goals
Changing the public DSL surface: pto.si32/pto.ui32 declarations and their numeric semantics stay as they are.
Background
Issue #1405 (fixed by #1408) exposed a structural fragility in PTODSL's scalar lowering: integer values live in two type domains at once.
arithdomain only accepts signlessiN(per MLIR's design philosophy, signedness is a property of the operation —divsivsdivui— not of the type).siN/uiNas value types throughout tracing.The two domains are bridged by
unrealized_conversion_caststrip/restore pairs (_strip_integer_signedness/_restore_integer_signednessinptodsl/ptodsl/_types.py). This op is designed as a dialect-conversion placeholder ("this type will be legalized away later"), not as an everyday signedness bookkeeping device. Using it this way means every integer value can exist in a split form — a bare signless SSA value paired with an out-of-band signedness annotation — and any tracer path that caches, constant-folds, or otherwise reuses an intermediate SSA value with a stale annotation can emit invalid IR (in #1405: asi32 to i32cast whose operand was actually ani32constant).#1408 fixes the literal-arithmetic instance of this, but the fix is symptomatic: it removes some strip/restore round trips rather than making the value/annotation mismatch structurally impossible. Other reuse paths (constant folding in
_tracing/, control flow, sub-kernel boundaries) can still desync the two.Proposal
Make signless the only integer identity inside tracing:
iN.siN/uiNnever appear as intermediate value types, so_strip/_restore_integer_signednessdisappear from hot paths._SurfaceValue/RuntimeValuein_surface_values.py) carry signedness as pure Python-side metadata whose only purpose is operator selection (FloorDivSIOpvsDivUIOp,sltvsult,MaxSIvsMaxUI, ...). Sites that currently reverse-engineer signedness fromvalue.typestring prefixes (e.g._runtime_scalar_ops.py) read the metadata instead.siN/uiNare still materialized at boundaries — kernel signatures/ABI, VMI storage — with exactly one strip on entry and one restore on exit per value. No cast chains.After this, an SSA value has exactly one integer identity; signedness no longer lives in the IR at all, so there is nothing for the tracer to desync.
Suggested migration path
signednessmetadata field to the runtime-value wrappers; make_runtime_scalar_ops.pyprefer metadata and fall back to the type. This step is independently testable._strip/_restorefrom hot paths module by module (_scalar_adaptation.py,_scalar_coercion.py,_runtime_index_ops.py, control flow, sub-kernels), keeping them only at ABI/storage boundaries (consider renaming to_adapt_abi_signednessso misuse is visible in review).Non-goals
pto.si32/pto.ui32declarations and their numeric semantics stay as they are.Refs: #1405, #1408