-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Predication issue #2345 #2564
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am approving this PR, although I have to confess I don't understand the logic of the code anymore, but we need to fix this bug.
This is a case where I am relying fundamentally on Gauntlet and the Z3 verifier to ensure correctness.
@fruffy : I assume you will torture this logic further; your tool found the bug fixed here previously.
midend/predication.cpp
Outdated
// if the name of a current statement is the same as | ||
// the name of any in dependantNames. | ||
isStatementDependent[statementName] = false; | ||
for (auto it : dependantNames) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the find
method instead?
} | ||
isAssignmentPushed[liveAssignments[exprName]] = false; | ||
// Push assignments which are correctly aranged in liveAssigns vector on rv block. | ||
for (auto it : liveAssigns) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use vector::insert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vector::insert can't be used in this case because rv is a BlockStatement pointer, not a vector. Function push_back is defined for BlockStatements. The only way insert could be used here is if it would be defined for BlockStatements.
midend/predication.h
Outdated
// of if statements assignments from liveAssigns are pushed on rv block. | ||
std::vector<const IR::AssignmentStatement*> liveAssigns; | ||
// Map that shows if the current statement is dependant. | ||
// Bool value is true for dependant statements, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your spelling of dependa/ent is inconsistent
ethernet_t val1_eth_hdr; | ||
ethernet_t val_eth_hdr; | ||
@name("ingress.simple_action") action simple_action() { | ||
h.eth_hdr.src_addr = (!(h.eth_hdr.eth_type == 16w1 ? true : false) ? 48w1 : h.eth_hdr.src_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have filed #2565 to optimize such a pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try to handle this optimization.
Gauntlet does not seem to have any issues with the tests. I will fuzz test until tomorrow and report back. |
This test failed due to a network connectivity issue. |
Fuzz testing revealed an issue with header stacks: The predication pass collapses control ingress(inout Headers h) {
@name("ingress.simple_action") action simple_action() {
if (h.eth_hdr.src_addr != h.eth_hdr.dst_addr) {
h.h[0].a = 8w2;
} else {
h.h[1].a = 8w1;
}
}
apply {
simple_action();
}
} into control ingress(inout Headers h) {
@name("ingress.simple_action") action simple_action() {
{
bool cond;
cond = h.eth_hdr.src_addr != h.eth_hdr.dst_addr;
h.h[1].a = (cond ? h.h[1].a : 8w1);
}
}
apply {
simple_action();
}
} @mbudiu-vmw That failure seems to happen a lot recently because of Travis rate limiting. Maybe the Travis config can be simplified a little. I will take a look. |
Thank you both for your replies. bool a = true; Without the if block on line 203 the output of predication would look like: hdr.x = a ? hdr.x + 1 : hdr.x; Which is wrong because it adds 1 to hdr.x twice in case 'a' is true. The condition wasn't correct, so some assignments were falsely removed (in 2564.p4 test the line: h.h[0].a = (cond ? 8w2 : h.h[0].a);). In this example the condition didn't see the difference between h.h[0].a and h.h[1].a because toString() function converted the left sides of both statements to [].a. Now that the condition consists of the comparison of lvalue_names of these headers, this if block won't effect these statements, since their lvlaue_names differ. |
In general, you should not depend on toString(), it is designed for human consumption, not for compiler internals. |
This commit fixes issue p4lang#2345. The issue was the incorrect order of assignment statements with a function like: void dummy(inout Headers val) { } ... action simple_action() { if (h.eth_hdr.eth_type == 1) { return; } h.eth_hdr.src_addr = 48w1; // this serves as a barrier dummy(h); } Predication output now looks like the folowing code: @name("ingress.simple_action") action simple_action() { hasReturned = false; { bool cond; cond = h.eth_hdr.eth_type == 16w1; hasReturned = (cond ? true : hasReturned); } { bool cond_0; cond_0 = !hasReturned; h.eth_hdr.src_addr = (cond_0 ? 48w1 : h.eth_hdr.src_addr); val_eth_hdr = (cond_0 ? h.eth_hdr : val_eth_hdr); h.eth_hdr = (cond_0 ? val_eth_hdr : h.eth_hdr); } } and before these changes were implemented these three lines were falsely aranged: { val_eth_hdr = (cond_0 ? h.eth_hdr : val_eth_hdr); } h.eth_hdr.src_addr = (cond_0 ? 48w1 : h.eth_hdr.src_addr); h.eth_hdr = (cond_0 ? val_eth_hdr : h.eth_hdr); since h.eth_hdr.src_addr should be assigned to 1 bofore the function call. The cause of the problem was the need to push a block with dependencies before liveAssignments. Which is now not an issue because all assignments are pushed on rv block from the same vector (liveAssigns), in preorder of if statements. In preorder of assignment statements, statements are pushed on liveAssigns in order they have on the input of predication pass. The xor test was added since it first showed this problem. This test now also has a correct output. Also, some tests were added, which show adequate behaviour when mixing nested if else-es and dependencies.
Thank you for pointing that out. Since I replaced the condition with the comparison of lvalue_names there are no more toString function calls in the solution. |
This PR fixes issue #2345.
The issue was the incorrect order of assignment statements with a function like:
void dummy(inout Headers val) { }
...
action simple_action() {
if (h.eth_hdr.eth_type == 1) {
return;
}
h.eth_hdr.src_addr = 48w1;
// this serves as a barrier
dummy(h);
}
Predication output now looks like the folowing code:
@name("ingress.simple_action") action simple_action() {
hasReturned = false;
{
bool cond;
cond = h.eth_hdr.eth_type == 16w1;
hasReturned = (cond ? true : hasReturned);
}
{
bool cond_0;
cond_0 = !hasReturned;
h.eth_hdr.src_addr = (cond_0 ? 48w1 : h.eth_hdr.src_addr);
val_eth_hdr = (cond_0 ? h.eth_hdr : val_eth_hdr);
h.eth_hdr = (cond_0 ? val_eth_hdr : h.eth_hdr);
}
}
and before these changes were implemented these three lines were falsely aranged:
since h.eth_hdr.src_addr should be assigned to 1 bofore the function call.
The cause of the problem was the need to push a block with dependencies before liveAssignments. Which is now not an issue because all assignments are pushed on rv block from the same vector (liveAssigns), in preorder of if statements. In preorder of assignment statements, statements are pushed on liveAssigns in order they have on the input of predication pass.
The xor test was added since it first showed this problem. This test now also has a correct output. Also, some tests were added, which show adequate behavior when mixing nested if else-es and dependencies.