Skip to content

FunctionInliner emits wrong reference to return value when the same function is inlined multiple times #5162

@asl

Description

@asl

Consider the following code:

bit foo(in bit a) {
  return a + 1; 
}

control p(inout bit bt, in bit bt2, in bit bt3) {
    action a(inout bit y0, bit y1, bit y2) {
      bit y3 = y1 > 0 ? 1w1 : 0;
      if (y3 == 1) {
         y0 = 0;
      } else if (y0 != 1) {
         y0 = y2 | y1;
      }
    }

    action b() {
        a(bt, foo(bt2), foo(bt3));
    }

    table t {
        actions = { b; }
        default_action = b;
    }

    apply {
        t.apply();
    }
}

control simple<T>(inout T arg, in T brg, in T crg);
package m<T>(simple<T> pipe);

m(p()) main;

The midend output for it is:

control p(inout bit<1> bt, in bit<1> bt2, in bit<1> bt3) {
    @name("p.tmp") bit<1> tmp;
    @name("p.y0") bit<1> y0;
    @name("p.b") action b() {
        y0 = bt;
        if (bt2 + 1w1 > 1w0) {
            tmp = 1w1;
        } else {
            tmp = 1w0;
        }
        if (tmp == 1w1) {
            y0 = 1w0;
        } else if (bt != 1w1) {
            y0 = bt3 + 1w1;
        }
        bt = y0;
    }
    @name("p.t") table t_0 {
        actions = {
            b();
        }
        default_action = b();
    }
    apply {
        t_0.apply();
    }
}

control simple<T>(inout T arg, in T brg, in T crg);
package m<T>(simple<T> pipe);
m<bit<1>>(p()) main;

Note that any reference to bt2 in the else if (bt != 1w1) { is lost. Why it is so? Here is the same output after the Frontend:

    @name("p.b") action b() {
        y0 = bt;
        a = bt2;
        retval = a + 1w1;
        if (retval > 1w0) {
            tmp = 1w1;
        } else {
            tmp = 1w0;
        }
        y3_0 = tmp;
        if (y3_0 == 1w1) {
            y0 = 1w0;
        } else if (y0 != 1w1) @inlinedFrom("foo") {
            a_4 = bt3;
            retval_2 = a_4 + 1w1;
            y0 = retval_2 | retval_2;
        }
        bt = y0;
    }

Note that y0 = retval_2 | retval_2 which is obviously incorect. Why it is so? After first iteration of inliner we're having:

            } else if (y0_0 != 1w1) @inlinedFrom("foo") {
                bit<1> a_1;
                a_1 = bt2;
                @name("hasReturned") bool hasReturned_0;
                @name("retval") bit<1> retval_0;
                hasReturned_0 = false;
                {
                    hasReturned_0 = true;
                    retval_0 = a_1 + 1w1;
                }
                y0_0 = foo(bt3) | retval_0;
            }
            bt = y0_0;
        }

Here we're having y0_0 = foo(bt3) | retval_0; with retval_0 being PathExpression referencing variable by name.

After second iteration of inliner we're having:

            } else if (y0_0 != 1w1) @inlinedFrom("foo") {
                bit<1> a_1;
                a_1 = bt2;
                @name("hasReturned") bool hasReturned_0;
                @name("retval") bit<1> retval_0;
                hasReturned_0 = false;
                {
                    hasReturned_0 = true;
                    retval_0 = a_1 + 1w1;
                }
                @inlinedFrom("foo") {
                    bit<1> a_2;
                    a_2 = bt3;
                    @name("hasReturned") bool hasReturned_0;
                    @name("retval") bit<1> retval_0;
                    hasReturned_0 = false;
                    {
                        hasReturned_0 = true;
                        retval_0 = a_2 + 1w1;
                    }
                    y0_0 = retval_0 | retval_0;
                }
            }

So, again we're substituting the PathExpression matching retval_0 by name. However, one retval_0 is from inner scope and another one is from outer scope (!). So, here the leaked "reference by name" IR abstraction produces broken code as reference to outer retval_0 is lost.

The return value should be treated essentially as an out argument and "exported" into outer scope via additional temporary (sic!)

Metadata

Metadata

Assignees

Labels

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

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions