Skip to content

fix(ir): Normalize tile/tensor scalar operand dtype - #2132

Merged
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
Hzfengsy:fix/scalar-operand-index-dtype
Jul 27, 2026
Merged

fix(ir): Normalize tile/tensor scalar operand dtype#2132
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
Hzfengsy:fix/scalar-operand-index-dtype

Conversation

@Hzfengsy

Copy link
Copy Markdown
Member

Summary

A bare int literal in the DSL is parsed to ConstInt(v, INDEX), but index is not a legal operand type for any pto.t*s instruction — so every tile/tensor × integer-literal op failed ptoas parse/verify:

pl.add(tile_i32, 5)     # -> pto.tadds ins(..., %c5_index : ..., index)
                        # error: 'pto.tadds' op operand #1 must be numeric (integer/float), but got 'index'
pl.ands(tile_i32, 255)  # error: custom op 'pto.tands' invalid kind of type specified

The tile wrappers already tried to fix this via _normalize_expr(..., int_dtype=INT32), but that call was a no-op: _normalize_expr returns early when the value is already an Expr, which it always is coming from the parser.

On the tensor path it was worse — the index scalar propagated through PromoteDataTypes and silently retyped the result tensor to index, so pl.add(x_i32, 5) produced an index tensor that then failed to rebind to its Out param.

Float literals were unaffected (5.0ConstFloat(FP32)), which is why this stayed hidden.

Approach

All tile/tensor scalar wrappers now route through one shared normalizer in python/pypto/ir/utils.py (18 tile wrappers, 14 tensor wrappers):

  • A constant scalar operand adopts the operand's element dtype. The node kind follows the target dtype (ConstInt vs ConstFloat) because codegen dispatches on it — an int literal on a float tile must become a ConstFloat, or MLIR receives arith.constant 5 : f32.
  • A float literal on an integer operand keeps FP32, preserving existing promotion semantics (int32_tensor * 2.5 -> fp32).
  • An explicit pl.const(v, dtype) is left untouched — it is a deliberate user annotation, not a placeholder. Same for typed pl.Scalar params and any non-constant expression.
  • A non-constant index value (loop var, pl.dim, block idx) is rejected with an actionable pl.cast hint instead of silently miscompiling.
expression before after
pl.add(tile_i32, 5) index → ptoas reject i32
pl.tensor.adds(x_i32, 5) result retyped to index result int32
pl.add(tile_i32, 2.5) f32 f32 (unchanged)
pl.const(42, pl.INT32) on i16 tile i32 i32 (unchanged)
pl.add(acc, i) (i = loop var) silent bad IR ValueError + pl.cast hint

Behaviour changes worth noting

  • tensor.adds(x_i32, 5) now yields an int32 result instead of fp32.
  • An int literal on a float tile becomes a matching-dtype ConstFloat (e.g. fp16) instead of fp32.
  • Passing a raw index value to a scalar operand now raises. Tests that did this were producing uncompilable IR and now cast explicitly.

Testing

  • New TestTileScalarOperandDtype (15) and TestTensorScalarOperandDtype (11) unit tests.
  • Two codegen-boundary tests in test_pto_codegen.py asserting the emitted pto.tadds operand is i32, never index — every IR-construction test stops before codegen, which is exactly why this class of bug shipped.
  • Updated tests that fed a raw index value to a scalar operand (unroll / SSA / tensor→tile lowering / parser control-flow, folding, closure) to cast it explicitly.
  • Full unit suite on the rebased tree: 7750 passed, 2 skipped, 0 failed.
  • ruff, ruff format, pyright, and all pre-commit hooks clean.
  • Docs updated in both docs/en and docs/zh-cn.

Copilot AI review requested due to automatic review settings July 24, 2026 06:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a227bec-0534-4da1-aa2b-3af70f6e7cb1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Scalar operand normalization is centralized for tensor and tile operators. Constants adopt compatible element dtypes, non-constant index operands require explicit casts, and operator, codegen, parser, transform, test, and documentation expectations are updated accordingly.

Changes

Scalar dtype normalization

Layer / File(s) Summary
Normalization helper contract
python/pypto/ir/utils.py
Adds shared helpers for scalar dtype inference, constant re-stamping, fallback dtypes, and rejection of non-constant index operands.
Tensor and tile operator wiring
python/pypto/ir/op/tensor_ops.py, python/pypto/ir/op/tile_ops.py
Routes scalar arithmetic, comparison, bitwise, selection, expansion, and fixed-mode operands through the shared normalization helpers.
Operator and codegen regression coverage
tests/ut/ir/operators/*, tests/ut/codegen/test_pto_codegen.py
Verifies literal typing, dtype preservation, promotion, index rejection, and i32 PTO scalar emission.
Parser, transform, and documentation updates
tests/ut/language/*, tests/ut/ir/transforms/*, docs/*/dev/ir/05-operators.md
Updates expectations and examples to cast index-valued loop, closure, and block-location scalars before arithmetic.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

I’m a rabbit with a typed little plan,
I stamp each scalar as neatly as I can.
Index hops now wear casts in the queue,
Tiles and tensors know what to do.
“i32!” I thump—then the tests all cheer.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: normalizing tile and tensor scalar operand dtypes.
Description check ✅ Passed The description directly explains the dtype normalization change and affected tile/tensor behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/en/dev/ir/05-operators.md`:
- Line 270: Clarify the scalar normalization contract in the operator-table
notes: document bare integer literals, float literals used with integer
operands, explicitly typed constants, and non-constant expressions, including
the required rejection or cast behavior. Update the English note at
docs/en/dev/ir/05-operators.md:270-270 and mirror the same clarification in
Chinese at docs/zh-cn/dev/ir/05-operators.md:264-264.

In `@python/pypto/ir/op/tile_ops.py`:
- Line 989: Keep shift-count operands as INT32 in both shls/shrs paths by
replacing lhs-based normalization with _normalize_const_to_dtype(rhs,
DataType.INT32, actual_span) at python/pypto/ir/op/tile_ops.py lines 989 and
1028. Add INT8 and INT16 shift-literal test cases in
tests/ut/ir/operators/test_tile_ops.py lines 3294-3306 asserting the normalized
operand uses INT32.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3de78e27-656c-4fbd-9496-0ce1cc79e462

📥 Commits

Reviewing files that changed from the base of the PR and between efb7837 and aa69000.

📒 Files selected for processing (14)
  • docs/en/dev/ir/05-operators.md
  • docs/zh-cn/dev/ir/05-operators.md
  • python/pypto/ir/op/tensor_ops.py
  • python/pypto/ir/op/tile_ops.py
  • python/pypto/ir/utils.py
  • tests/ut/codegen/test_pto_codegen.py
  • tests/ut/ir/operators/test_tensor_ops.py
  • tests/ut/ir/operators/test_tile_ops.py
  • tests/ut/ir/transforms/test_convert_tensor_to_tile_ops.py
  • tests/ut/ir/transforms/test_convert_to_ssa_pass.py
  • tests/ut/ir/transforms/test_unroll_loops_pass.py
  • tests/ut/language/parser/test_closure_var_resolution.py
  • tests/ut/language/parser/test_constant_folding.py
  • tests/ut/language/parser/test_control_flow.py

Comment thread docs/en/dev/ir/05-operators.md Outdated
Comment thread python/pypto/ir/op/tile_ops.py
Hzfengsy added a commit to Hzfengsy/pypto that referenced this pull request Jul 24, 2026
- Document the scalar normalization exceptions in the operator tables
  (EN + zh-CN): a float literal on an integer operand keeps FP32, and an
  explicit pl.const(v, dtype) is left as-is, alongside the existing
  index-rejection note.
- Correct the stale shls/shrs docstrings: the shift count is re-stamped to
  the lhs element dtype, not fixed at INT32. The IR permits any integer
  width for the shift operand (DeduceTileOpIntScalarBinaryType) and codegen
  casts it to i32.
- Pin that behaviour with INT8/INT16 shift-literal coverage.
Copilot AI review requested due to automatic review settings July 24, 2026 06:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Hzfengsy added 3 commits July 25, 2026 12:57
A bare int literal in the DSL is parsed to ConstInt(v, INDEX), but `index`
is not a legal operand type for any pto.t*s instruction, so every
tile/tensor x integer-literal op failed ptoas parse/verify. On the tensor
path it was worse: the index scalar propagated through PromoteDataTypes
and silently retyped the *result* tensor to index.

Route all tile/tensor scalar wrappers through a shared normalizer:

- A constant scalar operand adopts the operand's element dtype. The node
  kind follows the target dtype (ConstInt vs ConstFloat), since codegen
  dispatches on it -- an int literal on a float tile must be a ConstFloat
  or MLIR receives `arith.constant 5 : f32`.
- A float literal on an integer operand keeps FP32, preserving existing
  promotion semantics (int32_tensor * 2.5 -> fp32).
- An explicit pl.const(v, dtype) is a deliberate annotation and is left
  untouched, as are typed pl.Scalar params and any non-constant expr.
- A non-constant `index` value (loop var, pl.dim, block idx) is rejected
  with an actionable pl.cast hint rather than silently miscompiling.

Behaviour changes worth noting: tensor.adds(x_i32, 5) now yields an int32
result instead of fp32, and an int literal on a float tile becomes a
matching-dtype ConstFloat.

Covers 18 tile wrappers and 14 tensor wrappers. Tests that fed a raw index
value to a scalar operand were producing uncompilable IR and now cast it
explicitly.
- Document the scalar normalization exceptions in the operator tables
  (EN + zh-CN): a float literal on an integer operand keeps FP32, and an
  explicit pl.const(v, dtype) is left as-is, alongside the existing
  index-rejection note.
- Correct the stale shls/shrs docstrings: the shift count is re-stamped to
  the lhs element dtype, not fixed at INT32. The IR permits any integer
  width for the shift operand (DeduceTileOpIntScalarBinaryType) and codegen
  casts it to i32.
- Pin that behaviour with INT8/INT16 shift-literal coverage.
A non-constant index scalar is rejected with a pl.cast hint. An integer
scalar operand is accepted against a float tile (codegen narrows it), so
the hint can uniformly suggest pl.cast(<value>, pl.INT32) rather than the
two-step index->int->float form for float operands.
@Hzfengsy
Hzfengsy force-pushed the fix/scalar-operand-index-dtype branch from 8769cef to d726627 Compare July 25, 2026 05:01
Copilot AI review requested due to automatic review settings July 25, 2026 05:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lyfne123
lyfne123 merged commit 7456b15 into hw-native-sys:main Jul 27, 2026
12 checks passed
@Hzfengsy
Hzfengsy deleted the fix/scalar-operand-index-dtype branch July 27, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants