-
Notifications
You must be signed in to change notification settings - Fork 444
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
Split the start
state more conservatively when a parser contains decl initializers
#4902
Split the start
state more conservatively when a parser contains decl initializers
#4902
Conversation
Signed-off-by: Kyle Cripps <[email protected]>
Signed-off-by: Kyle Cripps <[email protected]>
Isn't there a pass which merges consecutive states that have no other in/out edges? |
Signed-off-by: Kyle Cripps <[email protected]>
I'd rather just avoid the split in the first place if possible (and it is easy enough to accomplish this in |
if (someInitializers) { | ||
// Check whether any of the parser's states loop back to the start state. |
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.
Isn't there a class which builds the reachability graph for parser states?
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.
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.
./frontends/p4/parserCallGraph.h
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.
CallGraph::reachable(start, reachable)
will always include start
in the set of reachable
states, so this will not really help simplify things.
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.
you can check the set of in-edge of the start state
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.
but frankly it would be useful to understand why the results are wrong. If there is a bug in the callgraph, it would be nice to know that. If the meaning of the isCalle() function is different than expected, it should be documented. If the rule for splitting is wrong, it would be good to know that too.
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.
@mihaibudiu Personally, I find it easier to initially analyze changes by looking at git diffs, which is why I shared the link to the diff with the C++ changes as well as corresponding updated tests outputs. If you prefer to initially analyze changes by checking out the changes locally, running the tests, and inspecting the tests failures locally, you can easily do this by recreating the changes minus tests outputs updates by fetching my fork and using git checkout
commands.
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.
but frankly it would be useful to understand why the results are wrong. If there is a bug in the callgraph, it would be nice to know that. If the meaning of the isCalle() function is different than expected, it should be documented. If the rule for splitting is wrong, it would be good to know that too.
If there's a problem with the ParserCallGraph
, that is outside the scope of this PR, and I'm not volunteering to spend time debugging this. But others may feel free to do so :)
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 no longer working on this project, but I will try to find time to fix bugs that I am responsible for.
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.
@mihaibudiu For example: 4b0ec77#diff-cd2b40893d904a1015f5d71d39014e909e148f5db324e0ebb77484e99c3cc6c7R2-R7
Split is expected in this case because start
is reachable from other states. So either I'm using ParserCallGraph
incorrectly, or it's not working properly.
I am no longer working on this project, but I will try to find time to fix bugs that I am responsible for.
I did not realize you were no longer working on this project. Please do not feel obligated to fix even the bugs you are responsible for. But any help is of course appreciated :)
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.
Minor comments, I am not quite sure about the toMove
logic since it is split across multiple visit methods which makes it harder to follow.
frontends/p4/moveDeclarations.cpp
Outdated
for (const auto *state : parser->states) { | ||
const auto *selectExpr = state->selectExpression; | ||
if (selectExpr == nullptr) { | ||
// implicit reject transition |
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 continue here instead?
frontends/p4/moveDeclarations.cpp
Outdated
// If this is the case, then the parser's decl initializers cannot be moved | ||
// to the start state. A new start state that is never looped back to must | ||
// be inserted at the head of the graph. | ||
for (const auto *state : parser->states) { |
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.
This probably could be a self-contained helper function which returns true if a loop is detected
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 would also make the control flow of the function simpler as you would just return on once you find any transtion to start.
return state; | ||
} | ||
|
||
const IR::Node *MoveInitializers::postorder(IR::Path *path) { | ||
if (!oldStart || !loopsBackToStart || path->name != IR::ParserState::start) return path; |
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.
if (!oldStart || !loopsBackToStart || path->name != IR::ParserState::start) return path; | |
if (oldStart == nullptr || !loopsBackToStart || path->name != IR::ParserState::start) return path; |
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 just a few code quality points.
frontends/p4/moveDeclarations.cpp
Outdated
// If this is the case, then the parser's decl initializers cannot be moved | ||
// to the start state. A new start state that is never looped back to must | ||
// be inserted at the head of the graph. | ||
for (const auto *state : parser->states) { |
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 would also make the control flow of the function simpler as you would just return on once you find any transtion to start.
frontends/p4/moveDeclarations.cpp
Outdated
oldStart = nullptr; | ||
loopsBackToStart = false; |
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.
Do we need this cleanup here? Would make more sense to do the cleanup when entering the parser so that it is clear what are initial values for each packet analysis.
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.
They are already initialized in the MoveInitializers
constructor. Are you suggesting to not initialize them there at all, or to initialize them twice (in both constructor and preorder function) the first time?
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 guess they are already being initialized twice for the first parser anyway, so I will move the initializations out of the constructor to avoid this.
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 think initialization in preorder makes sense, whether to initialize also in constructor is up to you. Some linters may complain about unitialized class fields if the constructor does not initialize it (because they simply have no way of proving that preorder
/init_apply
/etc. runs before anything else that uses the var), but I think having no initializer in constructor makes a little more sense as the value is then overwritten anyway.
parser Parser2() { | ||
state start { | ||
transition accept; | ||
} | ||
} |
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'd add some initializer here just to see that there is no confusion between the two parsers.
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'd prefer to not change this test (it is the case taken from #4901) but I added a new one.
Signed-off-by: kfcripps <[email protected]> Signed-off-by: Kyle Cripps <[email protected]>
Signed-off-by: Kyle Cripps <[email protected]>
Signed-off-by: Kyle Cripps <[email protected]>
Thanks @vlstill @fruffy @mihaibudiu |
@kfcripps I think this contains UB: https://github.com/p4lang/p4c/actions/runs/10839879711/job/30081116349 |
|
start
state always gets split when a parser contains declaration initializations. The following:always gets split into:
However, this is only necessary when
start
is reachable from some other state(s). This PR makes it so that the split only happens whenstart
is actually reachable from some other state(s).MoveInitializers
splitsstart
state for parsers that do not need this #4901