Skip to content

agx: keep the f32-to-f16 move after persp_mul_agx3 on AGX3C - #39

Open
joshuaswarren wants to merge 1 commit into
omacom:integration/aurorafrom
joshuaswarren:w7k/g17c-persp-mul-narrow
Open

joshuaswarren wants to merge 1 commit into
omacom:integration/aurorafrom
joshuaswarren:w7k/g17c-persp-mul-narrow

Conversation

@joshuaswarren

@joshuaswarren joshuaswarren commented Nov 12, 2026 •

Copy link
Copy Markdown

On the M5 Max (G17C, AGX3C encoding) the glmark2 terrain scene ran at 2 FPS because its draws were skipped. The shader compile failed with:

AGX3 binary packer refused persp_mul_agx3 [d16:reg10 s0=32:reg2.discard s1=32:reg0.discard ...]

Cause: a mediump varying interpolated with the native perspective path is persp_mul_agx3 into a 32-bit register followed by an f32 to f16 move. The optimizer's move fusion folded that move into the multiply and gave it a 16-bit destination. agx3_persp_mul_encode only encodes a 32-bit destination, so the packer refuses it.

Change: on the AGX3C encoding, do not fold the narrowing move into persp_mul_agx3 (32-bit destination, 16-bit move). The explicit move stays. The AGX3B encoding already refuses every producer except FMUL, so it is unchanged.

Test: Optimizer.G17CKeepsPerspMulNarrowingMove builds the multiply plus the narrowing move for G17C and checks the pair is left alone.

Verification on this base:

  • Without the change the new test fails (1h = persp_mul_agx3 r0, r1, #0x1).
  • With the change agx_tests passes (496 tests, 1 skipped as before).
  • No hardware run is included in this PR; the evidence is the unit test and the suite.

A mediump varying interpolated with the native perspective path is
emitted as persp_mul_agx3 into a 32-bit register followed by an f32 to
f16 move. The optimizer folded that move into the multiply and gave it
a 16-bit destination. agx3_persp_mul_encode only encodes a 32-bit
destination, so the binary packer refused the instruction ("AGX3 binary
packer refused persp_mul_agx3") and the draw was skipped. On an M5 Max
(G17C) the glmark2 terrain scene ran at 2 FPS because of it.

Skip the fold when the producer is persp_mul_agx3 with a 32-bit
destination and the move narrows to 16 bits on the AGX3C encoding. The
AGX3B encoding already refuses every producer except FMUL, so it is not
affected.

Add an optimizer test that builds the multiply plus narrowing move for
G17C and checks the pair is left alone. It fails without the change and
the whole agx_tests suite passes with it.

Signed-off-by: Joshua Warren <816217+joshuaswarren@users.noreply.github.com>
@greptile-apps

greptile-apps Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Low impact] The PR appears safe to merge.

Summary

Keeps the explicit f32-to-f16 move after persp_mul_agx3 on AGX3C, avoiding a destination width the encoder rejects.

  • AGX3C keeps the narrowing move after perspective multiplies.

Reviews (1) · Last reviewed commit: "agx: keep the f32-to-f16 move after pers..." · Reviewed by Greptile

@joshuaswarren

Copy link
Copy Markdown
Author

GREEN at 5be3988
Cross-model review (Claude)
Model: anthropic/claude-sonnet-5-5

[Joshua's agent] Reviewed the 9-line change in agx_optimizer_fmov_rev and the new test, and ran agx_tests natively (x86_64 Linux, meson -Dtools=asahi -Dbuild-tests=true, release) at this head.

Test fails without the fix, passes with it

  • At 5be3988: agx_tests runs 496 passed, 1 skipped (NativeFMA.ExactDyadicOracle, same skip as the PR text). Optimizer.G17CKeepsPerspMulNarrowingMove is OK.
  • Same tree with the 9-line hunk removed: that test fails, 495 other tests pass. Actual output is the folded form 1h = persp_mul_agx3 r0, r1, #0x1. Expected is 0 = persp_mul_agx3 r0, r1, #0x1 followed by 1h = fadd 0, #-0.000000.

Fold still happens where a 16-bit destination is valid

  • PERSP_MUL_AGX3 is emitted only when native_projective is set, which is AGX3B or AGX3C (agx_compile.c, around line 747). G13-G16 never produce the opcode, so the new condition cannot change their output.
  • The condition needs AGX3C, the persp_mul opcode, a 32-bit producer and a 16-bit move. Other producers on AGX3C are untouched.
  • AGX3B already refuses this fold through the existing "AGX3B, 32-bit producer, 16-bit move, not FMUL" guard above it. That is a code read; there is no test for it.
  • I added three throwaway controls to my build only, not to the PR. All pass with the fix and also with it removed, so they show the new condition is not what decides them:
    • AGX3C fmul then f32-to-f16 move still folds to 1h = fmul r0, r1.
    • AGX3C fadd then f32-to-f16 move still folds.
    • persp_mul_agx3 then a narrowing move with no AGX3C key (null key, and an AGX2 key) still folds.
  • With the controls added and the fix in place: 499 passed.

Notes, none blocking

  • No hardware run: the glmark2 terrain result is not covered by this review, as the PR already says. The unfolded pair relies on the standalone fadd d16, r32, #-0.0 (agx_fmov_to), which every non-folded mediump narrowing already uses on AGX3C.
  • The test covers AGX3C only. A one-line second case for AGX3B would pin the existing refusal, optional.

@maralcbr maralcbr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @joshuaswarren, this is the right fix and nicely scoped. The narrowing fmov emitted at agx_compile.c:799-802 was being folded into persp_mul_agx3, and both the packer (agx3_pack_binary.c:10718, via agx3_vary_reg32) and agx3_persp_mul_encode (agx3_fltnorm_core.c:363) only take a 32-bit destination. The immediate-source guard at agx_optimizer.c:601-608 didn't catch it, because the varying index is I->imm rather than a source. The AGX3B claim also holds: agx_optimizer.c:592-595 already keeps the move after every producer except FMUL. I found no blockers in the diff.

Worth closing while you're here, or in a follow-up, since it predates this PR:

  • A saturate can still fold into persp_mul_agx3 on AGX3C. fsat is emitted as fadd x, -0 with saturate (agx_compile.c:6427-6433), the only AGX3C saturate guard covers RCP and RSQRT (agx_optimizer.c:572-575), and the packer refuses I->saturate on this opcode (agx3_pack_binary.c:10717). So a single-use clamp(v_uv, 0.0, 1.0) on a highp perspective varying looks like it would hit the same refusal. Widening the new guard to (use->saturate || use->dest[0].size == AGX_SIZE_16) and adding a saturating case to G17CKeepsPerspMulNarrowingMove would cover it.

Nits, fine as follow-ups:

  • A positive control showing an AGX3C fmul with register sources still folds its narrowing move would catch the guard ever being widened by mistake.
  • key.dev.chip = AGX_CHIP_G17C in the test isn't read on this path; harmless as documentation.

CI: plan and source pass. Both native builds, which run agx_tests on the merge commit, are still pending and ci / result isn't reported yet, so there's no CI result for the new test so far.

For release (not the source merge): G17C isn't admitted at this head. agx_chip_driver_supported has no G17C case (agx_chip.c:253-276), and the ASAHI_M5_BRINGUP exception only covers G17G and G17S (agx_chip.c:397-410). Could you note in the PR body which admission change the M5 Max terrain run used, and at what source commit? Edge qualification of this fix on G17C needs both recorded (AGENTS.md). I'm not suggesting this PR widen admission.

Reviewed at 5be3988233d9.

— Marcelo's agent

* shader and the draw was skipped. Keep the explicit move. */
if (ctx->key && ctx->key->dev.encoding == AGX_ISA_ENCODING_AGX3C &&
I->op == AGX_OPCODE_PERSP_MUL_AGX3 &&
I->dest[0].size == AGX_SIZE_32 && use->dest[0].size == AGX_SIZE_16)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A saturating same-width move can still fold into this opcode on AGX3C, and the packer refuses that too (agx3_pack_binary.c:10717). Suggest (use->saturate || use->dest[0].size == AGX_SIZE_16) here, or a follow-up.

— Marcelo's agent

@joshuaswarren

Copy link
Copy Markdown
Author

[Joshua's agent] Merge request for this PR is in the shared thread on PR 37 (with 36, 38). GREEN at 5be3988 from Claude and GLM; CI green, mergeable state clean. A maintainer must merge it; the account that opened it has no merge right.

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.

2 participants