Skip to content

Venom PhiElimination collapses phi over different outputs of one invoke #5039

@banteg

Description

@banteg

Summary

PhiEliminationPass can treat a phi over two different outputs of the same multi-output invoke as trivial because _get_phi_origins_r keys origins by producing instruction, not by produced variable.

For this shape:

%r1, %r2 = invoke @f, ...
%z = phi @t, %r1, @e, %r2

both operands trace to the same invoke instruction. _process_phi then sees a single origin and tries to assign from src.output. In normal Python this raises because the invoke has multiple outputs; under python -O it would silently pick the first output and miscompile the %r2 path.

Reproducer

This reproduces from plain Vyper with experimental codegen and -O gas. The callee needs to stay uninlined, and there need to be two call sites so the invoke survives.

import vyper
from vyper.compiler.settings import Settings, OptimizationLevel

body = "\n".join(f"    x{i}: uint256 = p + {i}" for i in range(40))
src = f"""
@internal
def two(p: uint256) -> (uint256, uint256):
{body}
    return p + x0, p * 2

@external
def f(c: bool, p: uint256) -> uint256:
    a: uint256 = 0
    b: uint256 = 0
    a, b = self.two(p)
    x: uint256 = b
    if c:
        x = a
    return x

@external
def g(p: uint256) -> uint256:
    a: uint256 = 0
    b: uint256 = 0
    a, b = self.two(p)
    return a + b
"""
vyper.compile_code(src, settings=Settings(experimental_codegen=True, optimize=OptimizationLevel.GAS))

Observed failure:

AssertionError: expected single output for %24, %25 = invoke @"internal 0 two(uint256)_runtime", %19

Root cause

vyper/venom/passes/phi_elimination.py:_get_phi_origins_r returns origins by instruction. For multi-output producers, instruction identity is not enough to distinguish %r1 from %r2.

Fix direction

Key phi origins by (producer_instruction, produced_variable), or conservatively bail out of _process_phi when the apparent single source instruction has num_outputs != 1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions