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 def_use/SimplifyDefUse to deal with extern methods properly (#1284) #1652
Conversation
But Functions are global-scoped, so they cannot refer to any control-locals... |
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.
Can you please add a test for the problem which caused the PR to be reverted?
frontends/p4/simplifyDefUse.cpp
Outdated
@@ -545,6 +551,13 @@ class RemoveUnused : public Transform { | |||
} | |||
return statement; | |||
} | |||
const IR::Node* preorder(IR::Function* func) override { |
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 you have an example where this is important?
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 extended the virtual.p4 test case to include the problematic case -- an abstract method with an inout arg where the implementation assigns to the arg (could also happen with an out arg) -- not skipping the method here would remove the assignment (leaving the method empty).
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.
With the rules I proposed this would be correct, because no one is calling this method.
We need to know who is calling the method to analyze it.
If we don't know the method is either asynchronous or dead.
} else if (mi->is<P4::ExternFunction>()) { | ||
LOG3("extern function call " << mc->method); | ||
// assume it has no side-effects on anything not explicitly passed to it? | ||
// maybe should have annotations if it does |
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.
even if it has side-effects it should be unable to change any named variables (locals). We do not have references, so you cannot change something you cannot name - due to copy-in copy-out.
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.
it cannot call any methods, since it cannot refer to instances of the allocated objects that have these methods.
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.
We don't know for certain that the architecture does not provide a back-door method of referring to other objects -- for example, the packet_in and packet_out objects that are passed to parser and deparsers might be affected by other methods in some way? Perhaps a clone object that can have a method invoked that triggers an additional copy of the packet?
If we want to allow such architectures, we really need to provide langauge that the architecture can be described in (in the arch.p4 file); I'm not sure annotations are good enough longer-term. Something for the language design group.
// abstract methods must be implemented | ||
// by the users | ||
void increment(); | ||
@synchronous(increment) abstract bit<16> f(in bit<16> ix); |
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.
So the meaning of this is that increment calls f?
Shouldn't the annotation be on increment instead?
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.
Yes, the annotation means that increment might call f (but is not guarenteed to). At the moment I have the annotations on the abstract methods, listing the other methods that might call them, but it could equivalently be done the opposite way (put @might_call annotations on methods listing the other methods it might call?)
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 we want to support the case where the same method may be called both synchronously and asynchronously it may make sense to put the annotation on the caller.
I propose these rules:
- a method (virtual or non-virtual) that may be called asynchronously must be annotated with
@async
. This means that the method may be called at any time, from any thread. Such methods cannot access any P4 locals, only their own parameters. Such methods can also be called synchronously. - a non-virtual method that may call
callee
is labeled with@calls(callee)
With these rules we have that asynchronous methods are always live, but can never reach into locals. We should always run def-use for them. Synchronous methods may use locals, but we should know all their callers, so if they are unreachable they are actually dead. Also, it is an error for an asynchronous method to call a synchronous one.
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.
Problem is that we can't run def_use on these methods as its currently structured, as it tends to crash with a BUG_CHECK in Definitions::getPoints because it can't figure out where the function is called from. We need some way of constructing a ProgramPoint corresponding to the (non-existant) call site before we can analyze the body.
As far a what the annotations should be and which functions they should be on, the two are equivalent -- you're now back to something more like the original suggestion before it was later suggested they should be on the abstract methods. The only really important point IMO is that the annotation MUST be on/in the extern declaration in the arch file and NOT on the user's code implementing the abstract function.
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.
We should be able to create a ProgramPoints for asynchronous functions (which have no explicit caller). The same problem appears in several other places. There's a "beforeStart" ProgramPoint which may work.
a5ef67a
to
a1a8579
Compare
Ok, so I've gone back to (more like) the old code -- def_use now (continues to) analyze all functions with an assumption that they're called from outside the control, and additionally analyzes directly called and @synchronous functions per the direct call site. Unfortunately it looks like Travis has begun failing the OSX CI build with |
How does one even debug a failing Travis build for a different OS? |
@fruffy : if you have some time can you try to figure out what's wrong with the Python testing script? |
@ChrisDodd : I am not sure whether this will make a difference, but maybe you can try to modify the file backends/ebpf/targets/target.py as follows:
The stars replace more specific names. |
Looking at the output log of travis it seems that it fails to install scapy. https://scapy.readthedocs.io/en/latest/installation.html#mac-os-x |
Actually the p4c-xdp build failed with the same error, so this does not seem to be OSX only: |
I just tried to install scapy on my local machine |
This sounds like a good idea. @ChrisDodd : can you please try this fix? |
Looks like scapy==2.4.0 does not work on Travis's OSX -- see #1656. I'm going to try just removing all the ebpf tests from the OSX build next. |
Actually by looking at the travis.yml file it looks to me like the ebpf tests should not be run on OSX.
But I don't know much about how travis works. |
At least that was the intention, I guess. |
The tests were working (on OSX) a week or so ago -- something changed (in the Travis setup?) that causes them all to fail now. |
I actually think it is a change in the scapy python module that breaks things. |
That's why I was hoping that moving to an old version would fix the problem. |
Making this change in the Dockerfile of the p4c-xdp project has fixed that build:
|
The build there was failing with the same error. |
Looking at #1656 it still seems to download scapy-2.4.2:
The file By the way, I think the scapy devs just pushed a broken version yesterday: |
Good catch! Thanks @fruffy! |
a1a8579
to
888151d
Compare
- Track extern method calls, assuming that any call to any non-abstract method might trigger any abstract method implementation - local_copyprop fixed for the same assumption. - propagate reads/writes from callee to caller properly. - @synchronized annotation for abstract methods - adjust virtual.p4 test to reflect problematic case of non-synchronous function
888151d
to
e4f92ba
Compare
e0bbdd8
to
4d6d029
Compare
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 you should bring this up in the LDWG. We should be prepared to change some of the ways annotations are used if the LDWG reaches a different conclusion. But it is great to have a working proof-of-concept.
non-abstract method might trigger any abstract method implementation
This is revised slightly from the previous change -- the additional change is to make RemoveUnused in simplifyDefUse ignore IR::Function nodes that have not been analyzed by FindUninitialized as there were no (direct or indirect) references to the function. The problem being that visiting such a function would remove all the assignments in the function as none of them were in
hasUses
. That would be ok if the function was in fact unused, but in the case of a non-@synchronous function, that would be a problem. We don't know how such functions are called (from the control plane?) so we can't really analyze them, so we just want to leave them unchanged.The problem is if such a function accesses a control local variable, the analysis won't see it, so might remove an (apparently unused) control local. Its not clear if such functions should be allowed to access control locals; we don't really have any example use cases for non-@synchronous functions that would want to.