Skip to content

Commit

Permalink
(Part 1) PSA support (#601)
Browse files Browse the repository at this point in the history
A few major changes in this commit:
1. Adds PSA specification.
2. BMV2 backend refactoring.
 - 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.
 - 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.
  • Loading branch information
hanw authored May 11, 2017
1 parent b25ba2d commit 0623eaa
Show file tree
Hide file tree
Showing 57 changed files with 6,107 additions and 3,255 deletions.
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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)

#### For testing, install headers in the build directory

Expand Down
350 changes: 350 additions & 0 deletions backends/bmv2/JsonObjects.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
/*
Copyright 2013-present Barefoot Networks, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "lib/json.h"
#include "JsonObjects.h"

namespace BMV2 {

JsonObjects::JsonObjects() {
toplevel = new Util::JsonObject();
header_types = insert_array_field(toplevel, "header_types");
headers = insert_array_field(toplevel, "headers");
header_stacks = insert_array_field(toplevel, "header_stacks");
field_lists = insert_array_field(toplevel, "field_lists");
errors = insert_array_field(toplevel, "errors");
enums = insert_array_field(toplevel, "enums");
parsers = insert_array_field(toplevel, "parsers");
deparsers = insert_array_field(toplevel, "deparsers");
meter_arrays = insert_array_field(toplevel, "meter_arrays");
counters = insert_array_field(toplevel, "counter_arrays");
register_arrays = insert_array_field(toplevel, "register_arrays ");
calculations = insert_array_field(toplevel, "calculations");
learn_lists = insert_array_field(toplevel, "learn_lists");
actions = insert_array_field(toplevel, "actions");
pipelines = insert_array_field(toplevel, "pipelines");
checksums = insert_array_field(toplevel, "checksums");
force_arith = insert_array_field(toplevel, "force_arith");
externs = insert_array_field(toplevel, "extern_instances");
field_aliases = insert_array_field(toplevel, "field_aliases");
}

/// insert a json array to a parent object under key 'name'
Util::JsonArray*
JsonObjects::insert_array_field(Util::JsonObject* parent, cstring name) {
auto result = new Util::JsonArray();
parent->emplace(name, result);
return result;
}

/// append a json array to a parent json array
Util::JsonArray*
JsonObjects::append_array(Util::JsonArray* parent) {
auto result = new Util::JsonArray();
parent->append(result);
return result;
}

/// insert a json array named 'parameters' in a parent json object
Util::JsonArray*
JsonObjects::create_parameters(Util::JsonObject* object) {
return insert_array_field(object, "parameters");
}

/// append a json object r to a parent json array
/// insert a field 'op' with 'name' to parent
Util::JsonObject*
JsonObjects::create_primitive(Util::JsonArray* parent, cstring name) {
auto result = new Util::JsonObject();
result->emplace("op", name);
parent->append(result);
return result;
}

void
JsonObjects::add_program_info(const cstring& name) {
toplevel->emplace("program", name);
}

void
JsonObjects::add_meta_info() {
auto info = new Util::JsonObject();
static constexpr int version_major = 2;
static constexpr int version_minor = 7;
auto version = insert_array_field(info, "version");
version->append(version_major);
version->append(version_minor);
info->emplace("compiler", "https://github.com/p4lang/p4c");
toplevel->emplace("__meta__", info);
meta = info; // TODO(hanw): remove
}

/**
* create a header type in json
* @param name header name
* @param type header type
* @param fields a JsonArray for the fields in the header
*/
unsigned
JsonObjects::add_header_type(const cstring& name, Util::JsonArray*& fields) {
auto header_type = new Util::JsonObject();
unsigned id = BMV2::nextId("header_types");
header_type->emplace("name", name);
header_type->emplace("id", id);
if (fields != nullptr) {
header_type->emplace("fields", fields);
} else {
auto temp = new Util::JsonArray();
header_type->emplace("fields", temp);
}
header_types->append(header_type);
return id;
}

/// create a heder type with empty field list.
unsigned
JsonObjects::add_header_type(const cstring& name) {
auto header_type = new Util::JsonObject();
unsigned id = BMV2::nextId("header_types");
header_type->emplace("name", name);
header_type->emplace("id", id);
auto temp = new Util::JsonArray();
header_type->emplace("fields", temp);
header_types->append(header_type);
return id;
}

/// create a new field to an existing header type
void
JsonObjects::add_header_field(const cstring& name, Util::JsonArray*& field) {
CHECK_NULL(field);
Util::JsonArray* fields = nullptr;
for (auto h : *header_types) {
auto hdr = h->to<Util::JsonObject>();
auto hdrName = hdr->get("name")->to<Util::JsonValue>();
if (hdrName != nullptr && hdrName->isString() && hdrName->getString() == name) {
fields = hdr->get("fields")->to<Util::JsonArray>();
CHECK_NULL(fields);
break;
}
}
BUG_CHECK(fields != nullptr, "header '%1%' not found", name);
fields->append(field);
}

/// create a header instance in json
unsigned
JsonObjects::add_header(const cstring& type, const cstring& name) {
auto header = new Util::JsonObject();
unsigned id = BMV2::nextId("headers");
LOG1("add header id " << id);
header->emplace("name", name);
header->emplace("id", id);
header->emplace("header_type", type);
header->emplace("metadata", false);
header->emplace("pi_omit", true);
headers->append(header);
return id;
}

unsigned
JsonObjects::add_metadata(const cstring& type, const cstring& name) {
auto header = new Util::JsonObject();
unsigned id = BMV2::nextId("headers");
LOG1("add metadata header id " << id); header->emplace("name", name);
header->emplace("id", id);
header->emplace("header_type", type);
header->emplace("metadata", true);
header->emplace("pi_omit", true); // Don't expose in PI.
headers->append(header);
return id;
}

void
JsonObjects::add_header_stack(const cstring& type, const cstring& name,
const unsigned size, std::vector<unsigned>& ids) {
auto stack = new Util::JsonObject();
unsigned id = BMV2::nextId("stack");
stack->emplace("name", name);
stack->emplace("id", id);
stack->emplace("header_type", type);
stack->emplace("size", size);
auto members = new Util::JsonArray();
stack->emplace("header_ids", members);
for (auto id : ids) {
members->append(id);
}
header_stacks->append(stack);
}

/// Add an error to json.
void
JsonObjects::add_error(const cstring& name, const unsigned type) {
auto arr = append_array(errors);
arr->append(name);
arr->append(type);
}

/// Add a single enum entry to json.
/// A enum entry is identified with { enum_name, entry_name, entry_value }
void
JsonObjects::add_enum(const cstring& enum_name, const cstring& entry_name,
const unsigned entry_value) {
// look up enum in json by name
Util::JsonObject* enum_json = nullptr;
for (auto e : *enums) {
auto obj = e->to<Util::JsonObject>();
if (obj != nullptr) {
auto jname = obj->get("name")->to<Util::JsonValue>();
if (jname != nullptr && jname->isString() && jname->getString() == enum_name) {
enum_json = obj;
break;
}
}
}
if (enum_json == nullptr) { // first entry in a new enum
enum_json = new Util::JsonObject();
enum_json->emplace("name", enum_name);
auto entries = insert_array_field(enum_json, "entries");
auto entry = new Util::JsonArray();
entry->append(entry_name);
entry->append(entry_value);
entries->append(entry);
enums->append(enum_json);
LOG3("new enum object: " << enum_name << " " << entry_name << " " << entry_value);
} else { // add entry to existing enum
auto entries = enum_json->get("entries")->to<Util::JsonArray>();
auto entry = new Util::JsonArray();
entry->append(entry_name);
entry->append(entry_value);
entries->append(entry);
LOG3("new enum entry: " << enum_name << " " << entry_name << " " << entry_value);
}
}

///
unsigned
JsonObjects::add_parser(const cstring& name) {
auto parser = new Util::JsonObject();
unsigned id = BMV2::nextId("parser");
parser->emplace("name", name);
parser->emplace("id", id);
parser->emplace("init_state", IR::ParserState::start);
auto parse_states = new Util::JsonArray();
parser->emplace("parse_states", parse_states);
parsers->append(parser);

map_parser.emplace(id, parser);
return id;
}

unsigned
JsonObjects::add_parser_state(const unsigned id, const cstring& state_name) {
if (map_parser.find(id) != map_parser.end()) {
auto parser = map_parser[id];
auto states = parser->get("parse_states")->to<Util::JsonArray>();

auto state = new Util::JsonObject();
unsigned id = BMV2::nextId("parse_states");
state->emplace("name", state_name);
state->emplace("id", id);
auto operations = new Util::JsonArray();
state->emplace("parser_ops", operations);
auto transitions = new Util::JsonArray();
state->emplace("transitions", transitions);
states->append(state);
auto key = new Util::JsonArray();
state->emplace("transition_key", key);

map_parser_state.emplace(id, state);
return id;
} else {
BUG("%1% parser state not valid.", state_name);
}
}

void
JsonObjects::add_parser_transition(const unsigned id, Util::IJson* transition) {
if (map_parser_state.find(id) != map_parser_state.end()) {
auto state = map_parser_state[id];
auto transitions = state->get("transitions")->to<Util::JsonArray>();
CHECK_NULL(transitions);
auto trans = transition->to<Util::JsonObject>();
CHECK_NULL(trans);
transitions->append(trans); }
}

void
JsonObjects::add_parser_op(const unsigned id, Util::IJson* op) {
if (map_parser_state.find(id) != map_parser_state.end()) {
auto state = map_parser_state[id];
auto statements = state->get("parser_ops")->to<Util::JsonArray>();
CHECK_NULL(statements);
statements->append(op);
}
}

void
JsonObjects::add_parser_transition_key(const unsigned id, Util::IJson* newKey) {
if (map_parser_state.find(id) != map_parser_state.end()) {
auto state = map_parser_state[id];
auto keys = state->get("transition_key")->to<Util::JsonArray>();
CHECK_NULL(keys);
auto new_keys = newKey->to<Util::JsonArray>();
for (auto k : *new_keys) {
keys->append(k);
}
}
}

unsigned
JsonObjects::add_action(const cstring& name, Util::JsonArray*& params, Util::JsonArray*& body) {
CHECK_NULL(params);
CHECK_NULL(body);
auto action = new Util::JsonObject();
action->emplace("name", name);
unsigned id = BMV2::nextId("actions");
action->emplace("id", id);
action->emplace("runtime_data", params);
action->emplace("primitives", body);
actions->append(action);
return id;
}

void
JsonObjects::add_extern_attribute(const cstring& name, const cstring& type,
const cstring& value, Util::JsonArray* attributes) {
auto attr = new Util::JsonObject();
attr->emplace("name", name);
attr->emplace("type", type);
attr->emplace("value", value);
attributes->append(attr);
}

void
JsonObjects::add_extern(const cstring& name, const cstring& type,
Util::JsonArray*& attributes) {
auto extn = new Util::JsonObject();
unsigned id = BMV2::nextId("extern_instances");
extn->emplace("name", name);
extn->emplace("id", id);
extn->emplace("type", type);
extn->emplace("attribute_values", attributes);
externs->append(extn);
}

} // namespace BMV2

Loading

0 comments on commit 0623eaa

Please sign in to comment.