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!)
Consider the following code:
The midend output for it is:
Note that any reference to
bt2in theelse if (bt != 1w1) {is lost. Why it is so? Here is the same output after theFrontend:Note that
y0 = retval_2 | retval_2which is obviously incorect. Why it is so? After first iteration of inliner we're having:Here we're having
y0_0 = foo(bt3) | retval_0;withretval_0beingPathExpressionreferencing variable by name.After second iteration of inliner we're having:
So, again we're substituting the
PathExpressionmatchingretval_0by name. However, oneretval_0is 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 outerretval_0is lost.The return value should be treated essentially as an
outargument and "exported" into outer scope via additional temporary (sic!)