Skip to content

Fix stale constant propagation across out/inout method arguments #5765

Description

@sanket-jadhav-cse

Bug Description

GlobalCopyPropagation incorrectly preserves stale constant values for variables that are subsequently modified through method calls.

When a variable is initialized with a constant and later passed as an out/inout argument to a method, the GlobalCopyPropagation pass may fail to invalidate the variable's tracked value. As a result, the optimizer can incorrectly replace the variable with its previous constant value.

For example:

bit<32> temp = 100;
m.mutate(temp);

where mutate is declared as:

void mutate(out bit<32> val);

The optimizer may transform the call into:

m.mutate(32w100);

This is incorrect because an out argument must remain a writable lvalue.

The issue also affects member method calls such as register reads. For example:

meta.val = 10;
my_reg.read(meta.val, 0);

After the register read, meta.val no longer has the compile-time value 10. However, GlobalCopyPropagation can continue treating it as 10 and propagate the stale constant into subsequent code.

Root Cause

There are two main problems in midend/global_copyprop.cpp:

  1. checkParametersForMap() uses the callee's formal parameter name instead of resolving the actual argument passed at the call site. Therefore, caller variables used as out/inout arguments are not removed from the constant-value map.

  2. MethodCallExpression handling skips member methods because of the IR::Member early return. Consequently, calls such as register.read() are not considered when invalidating tracked variable values.

Impact

This can result in:

  • Valid P4 programs being rejected with a type-checking error such as:
    Read-only value used for out/inout parameter.
  • More seriously, stale constants being propagated after a method has modified a variable, potentially resulting in incorrect generated data-plane behavior.

This is therefore a compiler correctness bug in the GlobalCopyPropagation optimization pass.

Reproduction

A minimal example is:

extern Mutator {
    Mutator();
    void mutate(out bit<32> val);
}

control c() {
    Mutator() m;

    apply {
        bit<32> temp = 100;
        m.mutate(temp);
    }
}

Before the fix, GlobalCopyPropagation can incorrectly rewrite:

m.mutate(temp);

to:

m.mutate(32w100);

which is subsequently rejected by type checking.

Expected Behavior

GlobalCopyPropagation should invalidate the tracked value of any variable passed to an out or inout parameter and must not replace such writable arguments with constants.

Member methods that modify or produce values for variables should likewise invalidate the corresponding tracked values.

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

    coreTopics concerning the core segments of the compiler (frontend, midend, parser)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions