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

Merged
merged 1 commit into from Jan 6, 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.

@ChrisDodd ChrisDodd requested a review from mihaibudiu May 17, 2018 06:44
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.

This code is making some assumptions about when abstract methods may be called. An abstract method can be called even asynchronously, i.e., not by the P4 dataplane program, so it should not be able to reach into its environment at all.

Maybe we need to formalize the semantics of abstract methods. One possibility would be to consider adding a special annotation @synchronous(caller) to indicate that a specific abstract method can only be called by a specific extern method and never at some other time. For these methods we could allow perhaps capturing the environment.


// we control the traversal order manually, so we always 'prune()'
// (return false from preorder)

bool preorder(const IR::ParserState* state) override {
LOG3("FU Visiting " << dbp(state));
LOG3("FU Visiting state " << state->name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that dbp does which I found handy is to print the ID of the node besides the name. That makes it easy to cross-reference with a dump of a program generated using --top4 -v.

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 find the id rarely useful, and dbp also just prints the id and type and none of the contents of the node (like the state name), making it mostly useless in my experience. But of course, debugging methods differ, so YMMV.

} else if (mcd.instance->isApply()) {
auto am = mcd.instance->to<ApplyMethod>();
if (am->isTableApply()) {
auto table = am->object->to<IR::P4Table>();
callee = table;
}
}
} else if (auto em = mcd.instance->to<ExternMethod>()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems to be the same as in def_use.cpp. Maybe it can be refactored to be written only once?

// method in the object, so act as if all of them are applied
LOG3("extern method call " << name);
name = obj + '.';
for (auto it = methods.upper_bound(name); it != methods.end(); ++it) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is conservative enough. The virtual methods could also be invoked asynchronously at any time, e.g., by a request from the control-plane. We have not agreed that abstract methods are only invoked when some other extern method is invoked.

Also, consider action profiles: the action selector will be invoked when a table is invoked, without any other method being called explicitly.

So I think you should always consider the abstract methods as "live" and scan them. You know nothing about the context they execute in, though; they could all execute, or one of them could execute multiple times. This code should not be necessary, you should always visit them.


Virtual() cntr = { // implementation
bit<16> f(in bit<16> ix) { // abstract method implementation
return (ix + local);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ChrisDodd : I have an issue filed to forbid access to the context in an abstract method. We cannot give a semantics to this code. I don't think we should allow this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately we can't do that -- the whole point of abstract methods in per-control instances is to get access to the context -- if we disallow that, we might as well require all externs instances be global.

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 need an annotation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @synchronous annotation was added in to the abstract method declaration in the extern above.

@ChrisDodd
Copy link
Contributor Author

ChrisDodd commented May 17, 2018

I think what we really need is a hook for backends to provide per-extern (and per method) information about when the method(s) might be invoked, so as to allow proper flow analysis. So this is incomplete, but an improvement over the existing code which assumed that abstract methods could never be called in the middle of the control apply block.

@mihaibudiu
Copy link
Contributor

What is wrong with my proposal for a @synchronous annotation?

@ChrisDodd
Copy link
Contributor Author

I don't think @synchronous is enough. We could create a little language in annotations -- annotate each extern method with the set of other methods it could invoke. If an asynchronous method could be invoked anywhere, I'm not sure how to deal with it in these passes, however.

@ChrisDodd
Copy link
Contributor Author

So I've updated the code to use @synchronous annotations to indicate which methods might call abstract methods, so accesses to in-scope vars can be anayzed a bit better. I would like to check this in as it fixes some problems we see with dead-code eliminating metadata fields that are only used by abstract method implementations. @mbudiu-vmw any other comments?

@mihaibudiu
Copy link
Contributor

On vacation this week with no laptop

@ChrisDodd ChrisDodd force-pushed the cdodd-virtfn branch 3 times, most recently from 2417681 to 15d9933 Compare January 3, 2019 20:47
@@ -404,10 +404,15 @@ void ResolveReferences::postorder(const IR::Type_Method *t) {

bool ResolveReferences::preorder(const IR::Type_Extern *t) {
refMap->usedName(t->name.name);
addToContext(t->typeParameters); return true; }
// FIXME -- should the typeParamters be part of the extern's scope?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think it will work without them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently they're not part of the Type_Extern's scope, so need to be added as a separate scope (which is what this part of the change does)

@@ -932,3 +934,13 @@ bool ComputeWriteSet::preorder(const IR::MethodCallStatement* statement) {
}

} // namespace P4

// functions for calling from gdb
void dump(const P4::StorageLocation *s) { std::cout << *s << std::endl; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these implement IHasDbPrint there should already exist a dump, but indeed gdb sometimes has trouble finding the dump.

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, gdb seems to be unable to deal with virtual base classes properly (or at least to be able to call function overloads based on them), so I ended up adding all these overloads to make them callable.

@@ -129,6 +129,11 @@ class ExternMethod final : public MethodInstance {
const IR::Method* method;
const IR::Type_Extern* originalExternType; // type of object method is applied to
const IR::Type_Extern* actualExternType; // with type variables substituted

/// Set of IR::Method and IR::Function objects that may be called by this method.
// If this method is abstract, will consist of (just) the concrete implemenation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

midend/local_copyprop.cpp Outdated Show resolved Hide resolved

Virtual() cntr = { // implementation
bit<16> f(in bit<16> ix) { // abstract method implementation
return (ix + local);
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 need an annotation here?

@@ -6,18 +6,29 @@ extern bit<1> f(inout bit<1> x, in bit<1> b);
control c(out H[2] h);
package top(c _c);
control my(out H[2] s) {
bit<32> a_0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes suggest that some analysis has become less precise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code here changed because we're now assuming that the unknown method call f might depend on the locals. However, f is a global extern function, so we should probably assume it only depends on/modifies things passed to it explicitly.

@ChrisDodd ChrisDodd force-pushed the cdodd-virtfn branch 5 times, most recently from 93f070b to 0c7cf9c Compare January 4, 2019 23:26
- 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
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 thought a bit about it, and i think the way we should do some of this is to have an abstract base class for the current architecture which can, for example, tell you which functions are pure (e.g. Hash). The architecture should be initialized very early in the compilation.

@ChrisDodd
Copy link
Contributor Author

IMO we should be doing more in the architecture P4 file to describe how the architecture works -- perrhaps (simplified) bodies for extern methods and functions that summarize what they do and can be analyzed by common frontend/midend passes to drive their transformations. I'm not a big fan of duplicating stuff between C++ classes and architecture files in a way that needs to be carefully managed but is subject to breakage when someone makes a change -- all dependencies of this nature should be strongly delineated by automatic tests that will diagnose the problem automatically when someone breaks something.

@ChrisDodd ChrisDodd merged commit 2d00ef6 into p4lang:master Jan 6, 2019
ChrisDodd pushed a commit that referenced this pull request Jan 8, 2019
ChrisDodd added a commit that referenced this pull request Jan 8, 2019
ChrisDodd added a commit that referenced this pull request Jan 8, 2019
- 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
ChrisDodd added a commit that referenced this pull request Jan 10, 2019
- 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
ChrisDodd added a commit that referenced this pull request Jan 11, 2019
- 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
ChrisDodd added a commit that referenced this pull request Jan 11, 2019
- 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
ChrisDodd added a commit that referenced this pull request Jan 12, 2019
… (#1652)

* Fix def_use/SimplifyDefUse to deal with extern methods properly (#1284)

- 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
@apinski-cavium
Copy link

I just noticed @synchronous is not even documented anywhere except here and in the source of p4c.
Seems like there could be some user documentation missing here ...

@mihaibudiu
Copy link
Contributor

@apinski-cavium we have long wanted to do that in the spec, but never got around to it: p4lang/p4-spec#943

@ChrisDodd ChrisDodd deleted the cdodd-virtfn branch March 7, 2024 00:11
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