Skip to content

fix(emitc): avoid invalid static-to-dynamic GlobalTensor cast for MGATHER/MSCATTER - #1180

Open
FangRui0 wants to merge 3 commits into
hw-native-sys:mainfrom
FangRui0:fix_issue1165_mgather_static_stride_cast
Open

fix(emitc): avoid invalid static-to-dynamic GlobalTensor cast for MGATHER/MSCATTER#1180
FangRui0 wants to merge 3 commits into
hw-native-sys:mainfrom
FangRui0:fix_issue1165_mgather_static_stride_cast

Conversation

@FangRui0

@FangRui0 FangRui0 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1165 — a v0.55 regression where PTO-to-EmitC lowering emits C++ that fails to compile for MGATHER / MSCATTER.

Root cause. TensorViewType / PartitionTensorViewType carry only shape + element type in the MLIR type — never strides. So PTOToEmitCTypeConverter can only map them to a GlobalTensor opaque type with a fully-dynamic Stride<-1,-1,-1,-1,-1> template. The static partition_view pattern, which does have access to the defining op, instead materializes a GlobalTensor with concrete static strides (e.g. Stride<2048,2048,2048,32,1>). Dialect conversion bridges the two mismatched GlobalTensor types with an unrealized_conversion_cast, and the cleanup lowered it to an emitc.cast — an invalid C-style cast between two GlobalTensor instantiations that have no converting constructor:

GlobalTensor<...,Stride<-1,-1,-1,-1,-1>,...> v19 =
    (GlobalTensor<...,Stride<-1,-1,-1,-1,-1>,...>)v18;   // no matching conversion
MGATHER<pto::Coalesce::Row>(dst, v19, idx);

This surfaced in v0.55 once MGATHER/MSCATTER became tensor-view-native and the partition-view GlobalTensor started flowing directly into them (commits 61ed749, 83901f6, 8d1d8b5).

Fix

In the unrealized-cast cleanup, recognize a GlobalTensorGlobalTensor bridge that is identical except for static-vs-dynamic Shape/Stride template params (areRefinableGlobalTensorTypes) and forward the value instead of emitting a cast. The static-stride GlobalTensor then flows straight into the templated MGATHER/MSCATTER call, which accepts any instantiation. A C-style cast between differing GlobalTensor template instantiations is never valid C++, so this cleanup path should never produce one.

Emitted C++ after the fix (no intermediate cast, static strides preserved):

GlobalTensor<float, pto::Shape<1,1,1,64,32>, pto::Stride<2048,2048,2048,32,1>, pto::Layout::ND> v18 = ...;
MGATHER<pto::Coalesce::Row>(v14, v18, v9);   // consumes v18 directly

Validation (remote A3, LLVM21 toolchain)

Test plan

The PTOToEmitCTypeConverter maps tensor_view / partition_tensor_view to a
GlobalTensor opaque type with fully-dynamic Stride<-1,...> template params,
because strides are not carried on the MLIR type. The static partition_view
pattern, however, materializes a GlobalTensor with concrete static strides.
Dialect conversion bridges the two with an unrealized_conversion_cast that the
cleanup lowered to an emitc.cast, i.e. an invalid C-style cast between two
GlobalTensor instantiations that have no converting constructor. The generated
C++ then failed to compile for MGATHER / MSCATTER (issue hw-native-sys#1165, a v0.55
regression exposed once these ops became tensor-view-native).

Recognize such structurally-compatible GlobalTensor-to-GlobalTensor bridges in
the cast cleanup (identical except static-vs-dynamic Shape/Stride template
params) and forward the value instead of emitting a cast, so the static-stride
GlobalTensor flows directly into the templated MGATHER / MSCATTER call.

Add a NOCAST FileCheck pass to the mgather/mscatter lit test guarding against
re-emission of the C-style GlobalTensor cast.
Forwarding the static-stride GlobalTensor into a func/emitc return breaks
verification because the enclosing function's result type is fixed to the
dynamic-stride form. Only forward when every consumer accepts a more-specific
template instantiation (e.g. MGATHER/MSCATTER); otherwise fall through to the
emitc.cast branch. Fixes a regression in issue31_partition_view_parser_compat.
…THER/MSCATTER

Per PR review: prefer fixing at the source over the sink. MGATHER/MSCATTER are
template intrinsics that accept the concrete descriptor directly, so peel the
type-converter materialization bridge on their mem/idx/dst (src) operands. The
static-stride GlobalTensor bridge then becomes dead and is dropped by the first
cast-cleanup rule (use_empty), so it never reaches the emitc.cast fallback and
no invalid C-style GlobalTensor<...> cast is emitted.

This removes the sink-side areRefinableGlobalTensorTypes helper and its
forward/feedsReturn branch, which only tolerated the inconsistency at the exit.
Bringing MGATHER/MSCATTER in line with the peel convention already used by the
other operand-consuming patterns keeps the consistency contract in one place.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Pass Bug] v0.55 emits invalid static-to-dynamic GlobalTensor cast for MGATHER/MSCATTER

1 participant