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

Part 1 for PSA support #601

Merged
merged 67 commits into from
May 11, 2017
Merged

Part 1 for PSA support #601

merged 67 commits into from
May 11, 2017

Conversation

hanw
Copy link
Contributor

@hanw hanw commented May 10, 2017

This is a rather large commit to refactor bmv2 backend to enable easier support for PSA.

A few major changes in this commit:

  1. Separate JSON generation from IR manipulation. The jsonconverter.cpp had two jobs: traversing the IR and generating the corresponding JSON template. We introduced a new JsonObjects class that handles the JSON template generation and refactored jsonconverter.cpp into individual header.cpp, control.cpp, extern.cpp, action.cpp, parser.cpp, deparser.cpp files to limit each file to one aspect of the IR.
  2. Separate V1Model-specific code generation routine. primitives are handled differently by v1model and PSA. We separated out the v1model-specific primitive generation into v1model.cpp to be backward-compatible with bm2-ss.
  3. Preliminary extern generation for PSA.

As the code stands right now, it still generates valid JSON for bm2-ss. Our next step is to enable JSON generation for bm2-psa, and test with PSA behavior model.

In the interest of not keep a branch for too long, I hope we can fold this change in, to allow others to modify the refactored code, instead of adding changes to jsonconverter.cpp.

@sethfowler
Copy link
Contributor

sethfowler commented May 10, 2017

Yeah, we should get this reviewed and landed quickly, since it touches a huge number of files. It looks like you need to rebase already.


/// create a new field to an existing header type, returns a pointer to the field
void
JsonObjects::add_header_field(const cstring& name, Util::JsonArray** field) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function doesn't return anything, despite the comment. It also just uses *field, it doesn't try to modify it, so does it need the double pointer?

#ifndef _BACKENDS_BMV2_JSONOBJECTS_H_
#define _BACKENDS_BMV2_JSONOBJECTS_H_

namespace bm {
Copy link
Contributor

Choose a reason for hiding this comment

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

We generally use namespace BMV2 for bmv2 -- in any case we always use Capitalized names for namespaces.

auto km = k->to<IR::Mask>();
key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
auto trailing_zeros = [](unsigned long n) { return n ? __builtin_ctzl(n) : 0; };
auto count_ones = [](unsigned long n) { return n ? __builtin_popcountl(n) : 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 functions should probably be added to lib/bitops.h rather than being defined inline here.

Copy link
Contributor

Choose a reason for hiding this comment

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

or rather lib/gmputil.h

public:
explicit ConvertDeparser(Backend* backend) {
passes.push_back(new DoDeparserBlockConversion(backend));
setName("ConvertDeparser");
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 expect to want to add more passes here? If not, using a pass manager for a single pass is unnecessarily obscure.

@@ -401,7 +401,8 @@ class ComplexExpression : public Inspector {
const IR::PathExpression*
RemoveComplexExpressions::createTemporary(const IR::Expression* expression) {
auto type = typeMap->getType(expression, true);
auto name = refMap->newName("tmp");
LOG1("expression " << expression << " " << type);
Copy link
Contributor

Choose a reason for hiding this comment

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

LOG1 should only be used for messages that are self-contained and not too frequent, such as starting or ending a pass. This should probably be LOG3 or LOG4?

Copy link
Contributor

@cc10512 cc10512 left a comment

Choose a reason for hiding this comment

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

There are a few things that I think you should remove from this PR, there are not ready for mainstream, and they are unlikely to conflict with future changes (e.g. the p4d2 tests, the remap for field aliases).

}

/// append a json object r to a parent json array
/// insert a field 'op' with 'name' to r
Copy link
Contributor

Choose a reason for hiding this comment

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

s/r/parent


class ConvertActions : public Inspector {
Backend* backend;
P4::ReferenceMap* refMap;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a particular reason to have these as data members? You are initializing them from the backend data members.


namespace BMV2 {

void Backend::createFieldAliases(const char *remapFile) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed anymore, is it? We're not using remap files.

*/

/*
Copyright 2013-present Barefoot Networks, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

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

two copyright headers is one too many :)


namespace BMV2 {

} // end namespace BMV2
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not needed anymore.

/**
Set the size of the table that a meter is attached to.

@param meter
Copy link
Contributor

Choose a reason for hiding this comment

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

unless you comment the parameters themselves, it's not needed to just add them as a @param.


namespace BMV2 {

class DirectMeterMap final {
Copy link
Contributor

Choose a reason for hiding this comment

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

class comment?

@@ -0,0 +1,29 @@
# This file maps p4d2 metadata to the bmv2 simple switch standard and intrinsic metadata
Copy link
Contributor

Choose a reason for hiding this comment

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

remove this file too.

@@ -0,0 +1,159 @@
/* -*- P4_16 -*- */
#include <core.p4>
#include <p4d2model.p4>
Copy link
Contributor

Choose a reason for hiding this comment

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

is this test still failing? should we convert it back to v1model?

@@ -0,0 +1,8 @@
set_default ipv4_lpm drop
Copy link
Contributor

Choose a reason for hiding this comment

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

before checking these in, we should make sure they are correct. I'm not convinced that the packet data is going to work for this test.

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.

It will be impossible to review this patch in detail, given its size, and the fact that the diff tools do not work well across many files. I think that we will have to rely on the tests to a very large degree.

Copy link
Member

@antoninbas antoninbas left a comment

Choose a reason for hiding this comment

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

good work, hard to review thoroughly though

#define _BACKENDS_BMV2_JSONOBJECTS_H_

namespace bm {

Copy link
Member

Choose a reason for hiding this comment

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

this header should probably include its dependencies, like <map>, etc and forward declare Util::JsonArray, etc.

Copy link
Member

Choose a reason for hiding this comment

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

by the way this is important to people not using a unified build

BUG_CHECK(hash->is<IR::Declaration_ID>(), "%1%: expected a member", hash);
auto algo = convertHashAlgorithm(hash->to<IR::Declaration_ID>()->name);
selector->emplace("algo", algo);
// FIXME selector_check pass
Copy link
Member

Choose a reason for hiding this comment

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

why was this commented-out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch, I put it back.

if (type->is<IR::Type_StructLike>())
operation = "copy_header";
else
operation = "modify_field";
Copy link
Member

Choose a reason for hiding this comment

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

you should try to use the new bmv2 core primitives when possible given that they will be available on all archs: assign, assign_header... (see https://github.com/p4lang/behavioral-model/blob/master/include/bm/bm_sim/core/primitives.h). For example assign is strictly equivalent to the "old" modify_field.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can you point me to the mapping for copy_header, add_header, remove_header and exit?

Copy link
Member

Choose a reason for hiding this comment

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

there is no mapping (also some new core primitives have no old equivalent), you'll have to go by the names
for exit, you should keep using exit, the name is staying the same

Copy link
Contributor

@cc10512 cc10512 left a comment

Choose a reason for hiding this comment

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

Looks good.

Calin Cascaval and others added 21 commits May 10, 2017 16:57
  - Comments on how to handle the semantics of multiple PRE invocations
  - and design option of splitting the PRE into PRE and BQE (Buffering
  and Queueing Engine)
  - added PRE as argument to Ingress and Egress
  - added missing 'in' qualifiers and random distribution
  - add size types instead of indefinite int
  - update with @mbudiu-vmw comments to pass through compiler
  - ActionProfile and ActionSelector externs
This commit refactors bmv2 backend to support multiple pipelien
architectures. JsonConverter is refactored into a pass manager
which is more conformed to the rest of the compiler design.

Added files:
backend.cpp       - the new backend pass manager
convert*.cpp      - compiler passes for control, parser, deparser,
                    externs and headers
inferArchitecture - a new frontend pass to parse the architecture p4
                    into a data object which is then used by json
                    generator
model.cpp         - a v2model that oriented for p4-16 IR

TODO:
tov1model         - a pass to convert v2model to v1model to execute
                    on simple-switch
Also fixes issues with decl handling and match types.
@hanw hanw merged commit 0623eaa into p4lang:master May 11, 2017
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.

Although the code has been merged, I think that some things need to be fixed. This review is not comprehensive, because it is very difficult to read code that has been split across files. It would have been much better if this was provided as a set of separate patches. This should also live in an experimental branch, and not in master, since it has not been reviewed properly.

@@ -102,7 +102,8 @@ BUILT_SOURCES += \
# FIXME -- and check-install-headers: below need to be consistent
p4includedir = $(pkgdatadir)/p4include
p4_14includedir = $(pkgdatadir)/p4_14include
p4include_HEADERS += $(srcdir)/p4include/core.p4 $(srcdir)/p4include/v1model.p4
p4include_HEADERS += $(wildcard $(srcdir)/p4include/*.p4) \
$(wildcard $(srcdir)/p4include/*.map)
Copy link
Contributor

Choose a reason for hiding this comment

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

map files are something new; they deserve an explanation. They are also installed, so they seem quite important.

field_aliases = insert_array_field(toplevel, "field_aliases");
}

/// insert a json array to a parent object under key 'name'
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that we want phrase capitalization for such comments: start with an uppercase and end with a period.

void
JsonObjects::add_meta_info() {
auto info = new Util::JsonObject();
static constexpr int version_major = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually the dialect version of the JSON language supported. I think that these constants should be in a more prominent place, not in code.

}

/**
* create a header type in json
Copy link
Contributor

Choose a reason for hiding this comment

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

This function and the next ones should also have a @returns tag.

return id;
}

/// create a heder type with empty field list.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo in comment


/* P4-16 declaration of the P4-D2 (P4 Developer's Day) switch model */

#include "core.p4"
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be #include <core.p4>

@@ -38,6 +38,18 @@ struct standard_metadata_t {
bit<1> drop;
bit<16> recirculate_port;
bit<32> packet_length;
// flattening fields that exist in bmv2-ss
// queueing metadata
@alias("queueing_metadata.enq_timestamp") bit<32> enq_timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

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

The @alias annotation is not documented. What does it mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alias is used to generate the field_alias section of the BMV2 JSON. Field alias creates a mapping from the metadata name in P4 program to the behavioral model's internal metadata name. Here we use it to expose all metadata supported by behavior model to user through standard metadata, as requested by @vgurevich

Copy link
Contributor

Choose a reason for hiding this comment

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

You have to add this description to the v1model.p4 file itself.

@@ -133,14 +145,17 @@ parser Parser<H, M>(packet_in b,
inout standard_metadata_t standard_metadata);
control VerifyChecksum<H, M>(in H hdr,
inout M meta);
@pipeline
Copy link
Contributor

Choose a reason for hiding this comment

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

The @pipeline annotation is not documented anywhere. I think it should be removed.
You are essentially duplicating functionality provided by the package operation - the fact that something is a pipeline is given by the fact that it is used as an argument to the V1Switch package in the right position.

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 @pipeline annotation is needed because BMv2 JSON mandates only ingress and egress can be in the pipeline section. The original BMv2 backend hard-coded the name of ingress and egress in v1model when generating the pipeline section in JSON. When I wrote the visitor, I did not figure out a way to traverse only ingress and egress, and not verifychecksum and updatechecksum without hard-coding the names. They are all Controlblock from the visitor's point of view.

Copy link
Contributor

Choose a reason for hiding this comment

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

The annotation is on the type, not on the instance, so this way of doing it won't work.
For example, you could very well have multiple blocks with the same type required in the package, e.g., the same signature for checksum updates and for the main pipeline.

The way you distinguish them is by the argument of package they bind to. That's why there is a pass which takes a policy about which controls to skip. What you have to do is something similar for the P2D2 architecture: a list of controls to skip.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously, the skipControls policy receives the list of control blocks to skip from the hard-coded names in v1model.

auto ingress = main->getParameterValue(P4V1::V1Model::instance.sw.ingress.name);
I was looking for a way to generalize how skipControls (and other policies) computes the list of control blocks. The solution I came up with was to use special annotations in the architecture file. That way, at least I don't have to change the backend code for a different architecture. By putting the annotation on the type, only the person who designs the architecture needs to care about this detail, and it's not visible to the P4 programmer. It also affects the checksum passes, as I need a way to identify which control block is updateChecksum. It would be argument 5 in v1model or argument n ( n != 5) in PSA, It may not have the name updateChecksum in PSA, or other architecture that people come up with. I am not sure how to identify updateChecksum without using annotation?

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand what you are trying to do.
The problem comes from the fact that you are trying to support multiple architectures with a single back-end. That will be harder and harder as these architecture diverge in behaviors. For example, changes you make for PSA may break v1model programs. Also, the BMv2 back-end generates a program called p4c-bm2-ss, so you see that it targets a specific simulator on top of BMv2 (bmv2 can be used to implements lots of different simulators), and it it assumes a specific model for the input architecture, v1model. These things are tightly coupled in the code; the C++ code hardwires a lot of knowledge about v1model and BMv2 SS in particular. You are trying to de-entangle them, but that won't be easy.

However, v1model should be thought as a description of a fixed architecture, which cannot be changed. In general, you cannot assume you can edit it anymore after you have distributed it to customers.

Moreover, regarding the @pipeline annotations, all the information you need IS in the package. It is just annoying to extract. Maybe you can write some helper functions to make this easier. For v1model we know that the pipelines are arguments 3 and 4 of the main package, and the deparser is argument 6 (the code in fact uses the parameter names in v1model to retrieve them). This is a contract between the architecture and the compiler, and it can never change once you published the architecture, so you can rely on it. Similarly, for PSA you will identify the pipelines from the arguments to the main package. So what you have to do is write a generic class that can identify the blocks from the main package, provide two implementations for PSA and v1model and output the same data-structure that you can use from both. I would suggest not using annotations, but just set, the same way it was done in the BMv2 back-end.

* These types need to be defined before including the architecture file
* and the macro protecting them should be defined.
*/
#ifndef PSA_CORE_TYPES
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this file the most recent version?
It looks like this file cannot compile.

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 haven't fully tested psa.p4 yet. I will do it in the next PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you can then remove it from the PR?

/**
* PSA supported metadata types
*/
struct psa_parser_input_metadata_t {
Copy link
Contributor

Choose a reason for hiding this comment

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

indentation is wrong.

@hanw
Copy link
Contributor Author

hanw commented May 11, 2017

Thanks very much for the review, Mihai. I will address your comments today.

@hanw hanw deleted the hanw/psa branch May 11, 2017 22:15
@hanw hanw restored the hanw/psa branch May 11, 2017 23:54
@hanw hanw deleted the hanw/psa branch May 12, 2017 00:14
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.

6 participants