Skip to content

Incorrect fold across CFG join block argument after inlining #681

Description

@nkanazawa1989

A fold after inlining appears to risk using a pre-branch initializer instead of the CFG join block argument. The reduced pattern is:

x = default
if flag:
    x = dynamic_value
return use(x)

After inlining, the IR correctly represents x as a join block argument. Any fold of use(x) after the join must use that block argument, not the initializer from before the branch.
Here is a pure-Kirin regression test:

from kirin import passes
from kirin.passes.inline import InlinePass
from kirin.prelude import basic


@basic
def helper(flag: bool, dynamic_value: float) -> float:
    x = 10.0
    if flag:
        x = dynamic_value + 1.0
    return x - 2.0


@basic
def main(flag: bool, dynamic_value: float) -> float:
    return helper(flag, dynamic_value)


def test_inline_fold_preserves_cfg_join_argument():
    assert main(True, 3.0) == 2.0
    assert main(False, 3.0) == 8.0

    InlinePass(main.dialects)(main)
    passes.Fold(main.dialects)(main)

    assert main(True, 3.0) == 2.0
    assert main(False, 3.0) == 8.0

The key invariant is that a fold after a CFG join must respect the join block argument. It must not replace the joined value with the pre-branch initializer unless the branch value is proven identical on all incoming edges.

#322 is possible related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    category: bugCategory: this is a bug or something isn't working as expected.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions