p4lang / p4-spec

Apache License 2.0
178 stars 80 forks source link

PSA: Using virtual functions to specify action chosen in action_selector #446

Open jafingerhut opened 7 years ago

jafingerhut commented 7 years ago

The comments below are copied from another issue regarding action selectors in the PSA spec. I wanted to preserve these comments in an open issue, since it is more difficult to remember where they are if they only exist in closed PRs or issues.

jafingerhut commented 7 years ago

Copied from this comment by @antoninbas: https://github.com/p4lang/p4-spec/pull/445#issuecomment-337726250

Here is the proposal I wrote up a while ago for action selectors using abstract functions to define the hash algorithm:

struct H { };
struct M {
    bit<32> hash1;
    bit<32> hash2;
}

// my proposal
extern action_selector_psa<T> {
    action_selector_psa(bit<32> size);
    abstract T my_hash();
}

control IngressI(inout H hdr, inout M meta, inout standard_metadata_t smeta) {

    action drop() { smeta.drop = 1; }

    // an example instantiation
    action_selector_psa<bit<16> > (32w1024) as = {
        bit<16> my_hash() {
            bit<16> res;
            // meta is in scope, which is what we need
            hash(res, HashAlgorithm.crc16, 16w0, {meta.hash1, meta.hash2}, 16w65535);
            return res;
            // ideally return hash(...)
        }
    };

    table indirect_ws {
        key = { }
        actions = { drop; NoAction; }
        const default_action = NoAction();
        implementation = as;
    }

    apply {
        indirect_ws.apply();
    }

};
jafingerhut commented 7 years ago

Some follow-up comments to the sample code above copied from this PR: https://github.com/p4lang/p4-spec/pull/445

@mbudiu-vmw Abstract extern methods are an experimental feature that is already supported by the compiler. It probably should be considered for inclusion in P4-XX for XX > 16.

Using variables available in the context, however, should not be supported.

@jafingerhut By "variables available in the context", I am guessing you mean by that, in the sample code that @antoninbas published, the use of meta.hash1 and meta.hash2 inside of the function my_hash?

If so, how would you propose to write code with equivalent behavior that avoided doing this?

@mbudiu-vmw If you need to capture variables from the environment they have to be passed explicitly as arguments. The problem is that we don't know when the virtual method is going to be called.

jafingerhut commented 7 years ago

Note that this idea may require some extension to the P4_16 language spec as well.

jafingerhut commented 6 years ago

Regarding Mihai Budiu's comment that "we do not know when the virtual function is going to be called".

A possible response:

How does a compile know when to "read" the values of the table's search key fields? I think the answer to this is: The P4_16 language spec defines those values to be read at the same time in the execution as the table_name.apply(); call is made. That apply() call does not take those search key fields as parameters explicitly at the place in the source code where the apply() call occurs. It is understood by the compiler and P4 programmers that the search key fields will be read at that time.

The same could be defined for direction in or inout parameters of a virtual function that uses variables from the environment -- the values are read at the same time the table's search key field values are read.

Another approach would be to make these parameters explicit in the apply() call, between the parentheses after the apply. They would become parameters in the table definition explicitly.

mihaibudiu commented 6 years ago

P4 does not have a notion of action selectors. If we standardize action selectors and describe in the spec when they are executed and how then we can also describe when the virtual function gets called.

mihaibudiu commented 3 years ago

Unfortunately the code above, with action selectors, cannot be written in any other way without using captured variables. Since this code is useful, we need to teach the compiler how to reason about captured variables. Why is this important?

Let us consider the following variation on the example above:

control IngressI(inout H hdr, inout M meta, inout standard_metadata_t smeta) {
    apply {
        ...
        meta.hash1 = 0;  // the compiler does not understand that meta.hash1 will be accessed by the table.apply,
                                   // so it will treat this assignment as dead.
        indirect_ws.apply();
        meta.hash1 = 1;
    }
};

There is nothing in the code that ties the invocation of table indirect_ws with the execution of the method as.my_hash. P4 does not have a notion of action selectors, so the compiler cannot reason about the fact that the method is called at some point during table invocation.

For this purpose we need a new annotation, that can tie the table execution with the method invocation. This annotation is @synchronous. In the following simpler example:

extern A {
   void p();  // p calls q
   @synchronous p
   abstract void q();
}

the annotation informs the compiler that at some point during the invocation of p the method q will be invoked. Now if you write:

control c() {
   bit<32> x = 0;
   A() a = { 
      void q() { x = x + 1; }
   }
   apply {
       a.p();  // compiler understands that p calls q, because of the annotation.
                // compiler can now understand that x will be both read and written - perhaps more than once
                // compiler certainly knows that x is not dead and it's value cannot be assumed to be 0.
    }
}

Unfortunately this won't work for the above table, since we should probably annotate:

action_selector_psa<bit<16> > (32w1024) as = {
     @synchronous indirect_ws  // table indirect_ws not yet declared...
     bit<16> my_hash() {
        ...
     }
};
table indirect_ws { ... }

So we probably need a different annotation on the table declaration:

@synchronous_call as.my_hash
table indirect_ws { ... }