Skip to content

fix(cpp): emit casts using destination type - #1549

Open
y7nieSEl5 wants to merge 2 commits into
tracel-ai:mainfrom
y7nieSEl5:fix/metal-bf16-cast-codegen
Open

fix(cpp): emit casts using destination type#1549
y7nieSEl5 wants to merge 2 commits into
tracel-ai:mainfrom
y7nieSEl5:fix/metal-bf16-cast-codegen

Conversation

@y7nieSEl5

@y7nieSEl5 y7nieSEl5 commented Aug 23, 2026

Copy link
Copy Markdown

Summary

  • emit shared C++ casts using the CastOp destination type instead of reconstructing the source type
  • add a native Metal regression test for f32-to-bf16 conversion, including rounding boundaries, signed zero, subnormals, range extremes, infinities, and NaN

Background

The shared emitter declared the cast result with its destination type but formatted the right-hand side with the input type. For f32-to-bf16 this generated invalid MSL like:

bfloat const v18 = float(v17);

Using the result type restores the cast semantics and emits the explicit conversion Metal requires:

bfloat const v18 = bfloat(v17);

The destination-typed behavior existed before the Pliron migration in #1402; the source-typed constructor was introduced during that refactor.

Relationship to #1509

#1509 and this PR address separate layers. #1509 registers BF16 as a WGPU/MSL runtime capability, while this PR fixes the shared C++ CastOp emitter used by native Metal and WGPU/MSL.

On an Apple M5, locally enabling the same full BF16 registration produced by #1509 after its probe succeeds made WGPU report Conversion | Arithmetic | DotProduct | Buffer. An actual f32-to-bf16 kernel still emitted bfloat = float(...) and failed Metal compilation without this change. With this change it emitted bfloat = bfloat(...), compiled, and returned the expected values.

Testing

  • cargo fmt --all -- --check
  • cargo check -p cubecl-cpp --all-features
  • cargo test -p cubecl-metal f32_to_bf16_cast_compiles_and_runs -- --nocapture
    • passed on an Apple M5 MacBook Air with a 10-core GPU, macOS 26.5.2
    • non-NaN results matched half::bf16::from_f32 bit-for-bit; NaN was verified by classification
  • restoring the old emitter reproduced the Metal compilation error deterministically
  • enabling BF16 registration with the old emitter reproduced the same failure through WGPU/MSL

Performance

No measurable performance regression was observed on the Apple M5. Since f32-to-bf16 does not compile before the fix, the shared CastOp emitter was compared using f32-to-f16, which compiles on both revisions and exercises the same changed path.

The device-timed benchmark used warmed compilation and allocations, 11 alternating samples of 200 large-buffer launches, and an unchanged f32 copy as a drift control. The median normalized cast/copy ratio was 0.760752 before the fix and 0.759177 after it, a -0.21% difference within normal run-to-run dispersion.

Fixes #1522.

@y7nieSEl5 y7nieSEl5 reopened this Aug 24, 2026
@y7nieSEl5

Copy link
Copy Markdown
Author

Reopening after testing the interaction with #1509 more closely. The 2 PRs actually address separate layers. This PR fixes the shared C++ CastOp emitter used by both native Metal and WGPU/MSL.
On an M5, enabling #1509-equivalent full BF16 registration made WGPU report Conversion | Arithmetic | DotProduct | Buffer, but without #1549 an f32-to-bf16 kernel still emitted bfloat = float(...) and failed Metal compilation. With #1549 it emits bfloat = bfloat(...) and passes.
I originally closed this as a duplicate, but now I guess it's somehow worth reviewing.

@y7nieSEl5 y7nieSEl5 changed the title Fix native Metal f32-to-bf16 cast codegen fix(cpp): emit casts using destination type Aug 24, 2026
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.

Metal (native runtime): generated MSL for f32->bf16 conversion does not compile — 'cannot initialize const bfloat with an rvalue of type float'

2 participants