Skip to content

fix[venom]: track multi-output phi origins by variable#5091

Open
banteg wants to merge 5 commits into
vyperlang:masterfrom
banteg:fix/phi-elim-invoke-outputs
Open

fix[venom]: track multi-output phi origins by variable#5091
banteg wants to merge 5 commits into
vyperlang:masterfrom
banteg:fix/phi-elim-invoke-outputs

Conversation

@banteg

@banteg banteg commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What I did

Fixes #5039 by making PhiEliminationPass distinguish outputs of the same multi-output producer.

Current master already avoids the original #5039 crash and python -O miscompile via the conservative multi-output bailout added in #4922. This PR keeps the distinct-output safety, but replaces the bailout with precise origin tracking so trivial phis over the same output slot can still be eliminated.

How I did it

Phi origins are now tracked by root operand. The operand identifies both its producer and the produced output slot, so %r1 and %r2 from the same invoke remain distinct origins without storing an extra producer-instruction key. When all paths reach the same output operand, _process_phi assigns from that operand. When a phi is a real multi-origin join, its own output operand remains the barrier origin.

How to verify it

test_phi_elim_multi_output_invoke_distinct_outputs_kept covers the #5039 safety case: a phi over two different outputs of the same invoke survives.

test_phi_elim_multi_output_invoke_same_output_eliminated covers the master-distinguishing case: a phi over the same output variable of a multi-output invoke is eliminated. On current master, the same shape leaves phis_after_pass=1; this branch eliminates it.

Validation run:

uv run --python 3.12 -m pytest tests/unit/compiler/venom/test_phi_elimination.py -q
uv run --python 3.12 black --check vyper/venom/passes/phi_elimination.py tests/unit/compiler/venom/test_phi_elimination.py
git diff --check

Commit message

fix[venom]: track multi-output phi origins by variable

PhiEliminationPass used producer-instruction identity as the phi origin. That is safe only for single-output producers. For multi-output producers such as invoke, the produced SSA variable is part of the origin identity.

Track origins by root operand. Distinct outputs remain distinct roots because their operands are distinct, while phis that only join the same multi-output slot can still be folded.

Description for the changelog

Fix: Venom phi elimination now tracks multi-output origins by produced operand, keeping distinct-output phis while still eliminating trivial same-output phis.

_check_pre_post(pre, post, hevm=True)


def test_phi_elim_multi_output_invoke():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this test does not fail on master, there was a fix for similar issue in the dyn alloc PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, yes, the #4922 dyn-alloc merge added the conservative bailout, so the old distinct-output test no longer fails on current master.

I merged current master into this branch, removed that now-redundant bailout now that origins carry the produced variable, and added test_phi_elim_multi_output_invoke_same_output_eliminated. That new case distinguishes current master: the same shape leaves phis_after_pass=1 there, while this branch eliminates it. I kept the distinct-output safety test as coverage for #5039 and updated the PR title/body to describe the post-#4922 scope.

Validation:

  • uv run --python 3.12 -m pytest tests/unit/compiler/venom/test_phi_elimination.py -q
  • uv run --python 3.12 black --check vyper/venom/passes/phi_elimination.py tests/unit/compiler/venom/test_phi_elimination.py
  • git diff --check

@banteg banteg changed the title fix[venom]: keep phis over distinct outputs of one invoke fix[venom]: track multi-output phi origins by variable Jun 17, 2026
Comment thread vyper/venom/passes/phi_elimination.py Outdated
# are keyed by the produced variable and not just the instruction,
# since different outputs of one multi-output instruction (e.g.
# invoke) are distinct origins.
phi_to_origins: dict[IRInstruction, set[tuple[IRInstruction, IROperand]]]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it would be better to only keep account of operands since operand identifies the inst

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. phi_to_origins is now set[IROperand]: the root operand carries both producer identity and output slot identity, so we no longer store (inst, operand) pairs. Real multi-origin phis use their own output operand as the barrier marker, and _process_phi assigns from the sole root operand unless it is the phi output itself.

Validation:

  • uv run --python 3.12 -m pytest tests/unit/compiler/venom/test_phi_elimination.py -q
  • uv run --python 3.12 black --check vyper/venom/passes/phi_elimination.py tests/unit/compiler/venom/test_phi_elimination.py
  • git diff --check

I also updated the PR body to describe the operand-only representation.

@github-actions

Copy link
Copy Markdown

Gas Changes

No changes detected.

Summary

  • Total tests measured: 815
  • Changed: 0
  • Regressions (gas up): 0
  • Improvements (gas down): 0
  • New tests: 0
  • Deleted tests: 0
  • Newly failing: 0
  • Newly passing: 0

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

📊 Bytecode Size Changes (venom)

No changes detected.

Full bytecode sizes

Contract legacy-O2 legacy-Os -O2 -O3 -Os
curvefi/amm/stableswap/meta_implementation/meta_implementation_v_700.vy 23610 22805 20786 19755 19315
curvefi/legacy/CurveStableSwapMetaNG.vy 24952 23578 20490 19871 19175
curvefi/amm/stableswap/implementation/implementation_v_700.vy 24962 23769 20264 19393 18845
curvefi/legacy/CurveStableSwapNG.vy 24473 23298 19677 18844 18314
curvefi/amm/tricryptoswap/implementation/implementation_v_200.vy 20724 19959 18020 17518 16961
yearnfi/VaultV3.vy 19972 19063 17017 15131 14477
curvefi/amm/twocryptoswap/implementation/implementation_v_210.vy 17634 16894 15685 15132 14648
curvefi/legacy/CurveCryptoSwap2.vy 18947 18382 15634 15092 14711
yearnfi/VaultV2.vy 16676 15763 13863 13405 12574
curvefi/amm/stableswap/factory/factory_v_100.vy 14558 13978 13317 11825 11788
curvefi/gauge/child_gauge/implementation/implementation_v_110.vy 12338 11561 10270 9775 9182
curvefi/gauge/child_gauge/implementation/implementation_v_100.vy 12017 11249 9998 9502 8920
curvefi/amm/stableswap/views/views_v_120.vy 12784 12368 9970 9413 9532
curvefi/amm/tricryptoswap/math/math_v_200.vy 11189 11126 9520 8162 8424
curvefi/legacy/CurveCryptoMathOptimized3.vy 11188 11125 9519 8162 8424
curvefi/gauge/child_gauge/implementation/implementation_v_020.vy 10665 9947 9013 8565 8028
curvefi/helpers/router/router_v_110.vy 6717 6717 7003 6376 6607
curvefi/amm/tricryptoswap/views/views_v_200.vy 7821 7776 6697 6445 6514
curvefi/registries/metaregistry/metaregistry_v_110.vy 7590 6732 6651 5888 5727
curvefi/helpers/stable_swap_meta_zap/stable_swap_meta_zap_v_100.vy 7302 7067 6379 6012 6114
curvefi/amm/twocryptoswap/views/views_v_200.vy 6991 6946 6272 6026 6089
curvefi/amm/twocryptoswap/factory/factory_v_200.vy 5540 5252 5994 4697 4845
curvefi/amm/tricryptoswap/factory/factory_v_200.vy 5246 5021 5923 4926 5039
curvefi/registries/metaregistry/registry_handlers/stableswap/handler_v_110.vy 6633 6259 5859 5094 5562
curvefi/amm/twocryptoswap/math/math_v_210.vy 6800 6800 5581 5107 5112
curvefi/gauge/child_gauge/factory/factory_v_201.vy 4844 4547 4266 4014 3780
yearnfi/VaultFactory.vy 3765 3617 3985 2505 2965
curvefi/registries/metaregistry/registry_handlers/tricryptoswap/handler_v_110.vy 4241 3939 3838 3547 3547
curvefi/registries/metaregistry/registry_handlers/twocryptoswap/handler_v_110.vy 4186 3884 3777 3417 3433
curvefi/gauge/child_gauge/factory/factory_v_100.vy 4183 3914 3694 3428 3213
curvefi/helpers/rate_provider/rate_provider_v_101.vy 3260 3260 2779 2437 2450
curvefi/registries/address_provider/address_provider_v_201.vy 2973 2782 2713 2569 2424
curvefi/amm/stableswap/math/math_v_100.vy 3067 3046 2598 2403 2423
curvefi/helpers/rate_provider/rate_provider_v_100.vy 2847 2841 2357 2050 2058
curvefi/helpers/deposit_and_stake_zap/deposit_and_stake_zap_v_100.vy 2322 2316 2123 1889 1921
curvefi/governance/relayer/taiko/relayer_v_001.vy 2068 2064 1784 1579 1612
curvefi/governance/relayer/polygon_cdk/relayer_v_101.vy 1556 1523 1587 1405 1406
curvefi/governance/relayer/arb_orbit/relayer_v_101.vy 1266 1262 1280 1124 1155
curvefi/governance/relayer/op_stack/relayer_v_101.vy 1186 1182 1219 1066 1094
curvefi/governance/relayer/not_rollup/relayer_v_100.vy 1168 1153 1208 1061 1073
curvefi/governance/vault/vault_v_100.vy 964 941 885 866 864
curvefi/governance/relayer/relayer_v_100.vy 496 496 600 505 510
curvefi/governance/agent/agent_v_100.vy 541 541 443 415 419
curvefi/governance/agent/agent_v_101.vy 541 541 443 415 419

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.

Venom PhiElimination collapses phi over different outputs of one invoke

2 participants