Skip to content
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

Merged
merged 2 commits into from Jan 12, 2019

Conversation

ChrisDodd
Copy link
Contributor

  • 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

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.

@mihaibudiu
Copy link
Contributor

But Functions are global-scoped, so they cannot refer to any control-locals...

Copy link
Contributor

@mihaibudiu mihaibudiu left a 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?

@@ -545,6 +551,13 @@ class RemoveUnused : public Transform {
}
return statement;
}
const IR::Node* preorder(IR::Function* func) override {
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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?)

Copy link
Contributor

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.

Copy link
Contributor Author

@ChrisDodd ChrisDodd Jan 9, 2019

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.

Copy link
Contributor

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.

@ChrisDodd ChrisDodd force-pushed the cdodd-virtfn branch 3 times, most recently from a5ef67a to a1a8579 Compare January 10, 2019 21:31
@ChrisDodd
Copy link
Contributor Author

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 *** cannot import name RawPcapWriter for all ebpf tests?

@mihaibudiu
Copy link
Contributor

How does one even debug a failing Travis build for a different OS?
RawPcapWriter should be part of scapy.

@mihaibudiu
Copy link
Contributor

@fruffy : if you have some time can you try to figure out what's wrong with the Python testing script?

@mihaibudiu
Copy link
Contributor

@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:

from scapy.utils import *
from scapy.layers.all import *

The stars replace more specific names.

@fruffy
Copy link
Collaborator

fruffy commented Jan 10, 2019

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
Maybe these steps are missing?

scapy.txt

@mihaibudiu
Copy link
Contributor

Actually the p4c-xdp build failed with the same error, so this does not seem to be OSX only:
https://travis-ci.org/vmware/p4c-xdp

@fruffy
Copy link
Collaborator

fruffy commented Jan 10, 2019

I just tried to install scapy on my local machine
pip install --upgrade --user scapy
and it failed with the same error.
pip install --upgrade --user scapy==2.4.0
worked fine. So this might be a quick and dirty workaround.

@mihaibudiu
Copy link
Contributor

This sounds like a good idea. @ChrisDodd : can you please try this fix?
We should indeed stick to specific versions of packages that we know are working.
I assume it's enough to replace in the Dockerfile scapy with scapy==2.4.0

@ChrisDodd
Copy link
Contributor Author

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.

@mihaibudiu
Copy link
Contributor

Actually by looking at the travis.yml file it looks to me like the ebpf tests should not be run on OSX.

script:
  - if [[ $TRAVIS_OS_NAME == 'linux' ]] ; then docker run -w /p4c/build -e CTEST_PARALLEL_LEVEL p4c ctest --output-on-failure --schedule-random ; fi
  - if [[ $TRAVIS_OS_NAME == 'osx' ]] ; then ctest --output-on-failure -j 2 --schedule-random -LE "ebpf$" ; fi

But I don't know much about how travis works.

@mihaibudiu
Copy link
Contributor

At least that was the intention, I guess.

@ChrisDodd
Copy link
Contributor Author

The tests were working (on OSX) a week or so ago -- something changed (in the Travis setup?) that causes them all to fail now.

@mihaibudiu
Copy link
Contributor

I actually think it is a change in the scapy python module that breaks things.
No one touched Travis or Docker lately.

@mihaibudiu
Copy link
Contributor

That's why I was hoping that moving to an old version would fix the problem.

@mihaibudiu
Copy link
Contributor

Making this change in the Dockerfile of the p4c-xdp project has fixed that build:

scapy==2.4.0

@mihaibudiu
Copy link
Contributor

The build there was failing with the same error.

@fruffy
Copy link
Collaborator

fruffy commented Jan 11, 2019

Looking at #1656 it still seems to download scapy-2.4.2:

Collecting scapy
  Downloading https://files.pythonhosted.org/packages/d0/04
/b8512e5126a1816581767bf95cbf525e0681a22c055bcd45f47ecff60170/scapy-2.4.2.tar.gz (795kB)

The file tools/install_os_deps.sh installs scapy before the dockerfile runs I think.
With OSX the dockerfile is skipped?

By the way, I think the scapy devs just pushed a broken version yesterday:
secdev/scapy#1783
Should be fixed soon.

@mihaibudiu
Copy link
Contributor

Good catch! Thanks @fruffy!

- 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
Copy link
Contributor

@mihaibudiu mihaibudiu left a 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.

@ChrisDodd ChrisDodd merged commit 0559c32 into master Jan 12, 2019
@ChrisDodd ChrisDodd deleted the cdodd-virtfn branch January 12, 2019 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants