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

Front-end support for header_unions #606

Merged
merged 7 commits into from
May 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backends/ebpf/ebpfType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ EBPFStructType::EBPFStructType(const IR::Type_StructLike* strct) :
kind = "struct";
else if (strct->is<IR::Type_Header>())
kind = "struct";
else if (strct->is<IR::Type_Union>())
else if (strct->is<IR::Type_HeaderUnion>())
kind = "union";
else
BUG("Unexpected struct type %1%", strct);
Expand Down Expand Up @@ -139,7 +139,7 @@ EBPFStructType::declare(CodeBuilder* builder, cstring id, bool asPointer) {

void EBPFStructType::emitInitializer(CodeBuilder* builder) {
builder->blockStart();
if (type->is<IR::Type_Struct>() || type->is<IR::Type_Union>()) {
if (type->is<IR::Type_Struct>() || type->is<IR::Type_HeaderUnion>()) {
for (auto f : fields) {
builder->emitIndent();
builder->appendFormat(".%s = ", f->field->name.name);
Expand Down
2 changes: 1 addition & 1 deletion control-plane/p4RuntimeSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ struct HeaderFieldPath {

// Top-level structs and unions don't show up in P4Runtime field names, so we use a
// blank path component name.
if (type->is<IR::Type_Struct>() || type->is<IR::Type_Union>()) {
if (type->is<IR::Type_Struct>() || type->is<IR::Type_HeaderUnion>()) {
return HeaderFieldPath::root("", type);
}
}
Expand Down
26 changes: 23 additions & 3 deletions frontends/p4/def_use.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ limitations under the License.

namespace P4 {

// name for header valid bit
// internal name for header valid bit; used only locally
const cstring StorageFactory::validFieldName = "$valid";
const cstring StorageFactory::indexFieldName = "$lastIndex";
const LocationSet* LocationSet::empty = new LocationSet();
Expand All @@ -46,8 +46,21 @@ StorageLocation* StorageFactory::create(const IR::Type* type, cstring name) cons
type = typeMap->getTypeType(type, true); // get the canonical version
auto st = type->to<IR::Type_StructLike>();
auto result = new StructLocation(type, name);

// For header unions we will model all of the valid fields
// for all components as a single shared field. The
// reason is that updating one of may change all of the
// other ones.
Copy link
Contributor

Choose a reason for hiding this comment

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

Expanding this overview to what is implemented below would be helpful. (Could be done later.)

StorageLocation* globalValid = nullptr;
if (type->is<IR::Type_HeaderUnion>())
globalValid = create(IR::Type_Boolean::get(), name + "." + validFieldName);

for (auto f : st->fields) {
auto sl = create(f->type, name + "." + f->name);
cstring fieldName = name + "." + f->name;
auto sl = create(f->type, fieldName);
if (globalValid != nullptr)
dynamic_cast<StructLocation*>(sl)->replaceField(
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 reason that dynamic_cast is used here instead of a to method?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would guess because the to methods currently all return const pointers. We should probably overload them to return non-const pointers when called on non-const objects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the single call of this method.
We have to rewrite this pass at some point: #151.

fieldName + "." + validFieldName, globalValid);
result->addField(f->name, sl);
}
if (st->is<IR::Type_Header>()) {
Expand Down Expand Up @@ -161,7 +174,14 @@ const LocationSet* LocationSet::getField(cstring field) const {
for (auto l : locations) {
if (l->is<StructLocation>()) {
auto strct = l->to<StructLocation>();
strct->addField(field, result);
if (field == StorageFactory::validFieldName && strct->isHeaderUnion()) {
// special handling for union.isValid()
for (auto f : strct->fields()) {
f->to<StructLocation>()->addField(field, result);
}
} else {
strct->addField(field, result);
}
} else {
BUG_CHECK(l->is<ArrayLocation>(), "%1%: expected an ArrayLocation", l);
auto array = l->to<ArrayLocation>();
Expand Down
97 changes: 55 additions & 42 deletions frontends/p4/def_use.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace P4 {
class StorageFactory;
class LocationSet;

// Abstraction for something that is has a left value (variable, parameter)
/// Abstraction for something that is has a left value (variable, parameter)
class StorageLocation : public IHasDbPrint {
public:
virtual ~StorageLocation() {}
Expand All @@ -49,38 +49,45 @@ class StorageLocation : public IHasDbPrint {
}
cstring toString() const { return name; }

// all locations inside that represent valid bits
/// @returns All locations inside that represent valid bits.
const LocationSet* getValidBits() const;
virtual void addValidBits(LocationSet* result) const = 0;
// set of locations if we exclude all headers
/// @returns All locations inside if we exclude all headers.
const LocationSet* removeHeaders() const;
virtual void removeHeaders(LocationSet* result) const = 0;
// all locations inside that represent the last index of an array
/// @returns All locations inside that represent the 'lastIndex' of an array.
const LocationSet* getLastIndexField() const;
virtual void addLastIndexField(LocationSet* result) const = 0;
};

/* Represents a storage location with a simple type.
It could be either a scalar variable, or a field of a struct, etc. */
/** Represents a storage location with a simple type or a tuple type.
It could be either a scalar variable, or a field of a struct, etc. */
class BaseLocation : public StorageLocation {
public:
// We can use this for tuples because tuples have no field accessors,
// so we treat them as monolithic objects.
BaseLocation(const IR::Type* type, cstring name) :
StorageLocation(type, name)
{ BUG_CHECK(type->is<IR::Type_Base>() || type->is<IR::Type_Enum>()
|| type->is<IR::Type_Var>() || type->is<IR::Type_Tuple>()
|| type-is<IR::Type_Error>() || type->is<IR::Type_Var>(),
{ BUG_CHECK(type->is<IR::Type_Bits>() || type->is<IR::Type_Enum>() ||
type->is<IR::Type_Boolean>() || type->is<IR::Type_Var>() ||
type->is<IR::Type_Tuple>() || type->is<IR::Type_Error>() ||
type->is<IR::Type_Varbits>(),
"%1%: unexpected type", type); }
void addValidBits(LocationSet*) const override {}
void addLastIndexField(LocationSet*) const override {}
void removeHeaders(LocationSet* result) const override;
};

/** Represents the locations for element of a struct, header or union */
class StructLocation : public StorageLocation {
std::map<cstring, const StorageLocation*> fieldLocations;
friend class StorageFactory;

void addField(cstring name, StorageLocation* field)
{ fieldLocations.emplace(name, field); CHECK_NULL(field); }
void replaceField(cstring field, StorageLocation* replacement)
{ fieldLocations[field] = replacement; }

public:
StructLocation(const IR::Type* type, cstring name) :
StorageLocation(type, name) {
Expand All @@ -97,6 +104,9 @@ class StructLocation : public StorageLocation {
void addValidBits(LocationSet* result) const override;
void removeHeaders(LocationSet* result) const override;
void addLastIndexField(LocationSet* result) const override;
bool isHeader() const { return type->is<IR::Type_Header>(); }
bool isHeaderUnion() const { return type->is<IR::Type_HeaderUnion>(); }
bool isStruct() const { return type->is<IR::Type_Struct>(); }
};

class ArrayLocation : public StorageLocation {
Expand Down Expand Up @@ -143,8 +153,8 @@ class StorageFactory {
static const cstring indexFieldName;
};

// A set of locations that may be read or written by a computation.
// In general this is a conservative approximation of the actual location set.
/// A set of locations that may be read or written by a computation.
/// In general this is a conservative approximation of the actual location set.
class LocationSet : public IHasDbPrint {
std::set<const StorageLocation*> locations;

Expand All @@ -163,8 +173,8 @@ class LocationSet : public IHasDbPrint {
void add(const StorageLocation* location)
{ locations.emplace(location); }
const LocationSet* join(const LocationSet* other) const;
// express this location set only in terms of BaseLocation;
// e.g., a StructLocation is expanded in all its fields.
/// @returns this location set expressed only in terms of BaseLocation;
/// e.g., a StructLocation is expanded in all its fields.
const LocationSet* canonicalize() const;
void addCanonical(const StorageLocation* location);
std::set<const StorageLocation*>::const_iterator begin() const { return locations.cbegin(); }
Expand All @@ -182,7 +192,7 @@ class LocationSet : public IHasDbPrint {
bool isEmpty() const { return locations.empty(); }
};

// For each declaration we keep the associated storage
/// Maps a declaration to its associated storage.
class StorageMap {
std::map<const IR::IDeclaration*, StorageLocation*> storage;
StorageFactory factory;
Expand Down Expand Up @@ -215,19 +225,19 @@ class StorageMap {
}
};

// Indicates a statement in the program.
// The stack is for representing calls: i.e.,
// table.apply() -> table -> action
/// Indicates a statement in the program.
class ProgramPoint : public IHasDbPrint {
// empty stack represents "beforeStart" (see below)
/// The stack is for representing calls for context-sensitive analyses: i.e.,
/// table.apply() -> table -> action.
/// The empty stack represents "beforeStart" (see below).
std::vector<const IR::Node*> stack;

public:
ProgramPoint() = default;
ProgramPoint(const ProgramPoint& other) : stack(other.stack) {}
explicit ProgramPoint(const IR::Node* node) { stack.push_back(node); }
ProgramPoint(const ProgramPoint& context, const IR::Node* node);
static ProgramPoint beforeStart; // a point logically before the program start
static ProgramPoint beforeStart; /// A point logically before the program start.
bool operator==(const ProgramPoint& other) const;
std::size_t hash() const;
void dbprint(std::ostream& out) const {
Expand Down Expand Up @@ -291,17 +301,17 @@ class ProgramPoints : public IHasDbPrint {
{ return points.cend(); }
};

// List of definers for each base storage (at a specific program point)
/// List of definers for each base storage (at a specific program point).
class Definitions : public IHasDbPrint {
// which program points have written last to each location
// (conservative approximation)
/// Set of program points that have written last to each location
/// (conservative approximation).
std::map<const BaseLocation*, const ProgramPoints*> definitions;

public:
Definitions() = default;
Definitions(const Definitions& other) : definitions(other.definitions) {}
Definitions* join(const Definitions* other) const;
// point writes the specified LocationSet
/// Point writes the specified LocationSet.
Definitions* writes(ProgramPoint point, const LocationSet* locations) const;
void set(const BaseLocation* loc, const ProgramPoints* point)
{ CHECK_NULL(loc); CHECK_NULL(point); definitions[loc] = point; }
Expand Down Expand Up @@ -330,9 +340,9 @@ class Definitions : public IHasDbPrint {
};

class AllDefinitions : public IHasDbPrint {
// These are the definitions available AFTER each ProgramPoint.
// However, for ProgramPoints representing P4Control, P4Action, and P4Table
// the definitions are BEFORE the ProgramPoint.
/// These are the definitions available AFTER each ProgramPoint.
/// However, for ProgramPoints representing P4Control, P4Action, and P4Table
/// the definitions are BEFORE the ProgramPoint.
std::unordered_map<ProgramPoint, Definitions*> atPoint;

public:
Expand All @@ -359,28 +369,31 @@ class AllDefinitions : public IHasDbPrint {
}
};

// This does not scan variable initializers, so it must be executed
// after these have been removed.
// Run for each parser and control separately.
// Computes the write set for each expression and statement.
// We are controlling precisely the visit order - to simulate a
// simbolic execution of the program.
/**
* Computes the write set for each expression and statement.
*
* This pass is run for each parser and control separately. It
* controls precisely the visit order --- to simulate a simbolic
* execution of the program.
*
* @pre Must be executed after variable initializers have been removed.
*
*/
class ComputeWriteSet : public Inspector {
protected:
AllDefinitions* definitions; // result
Definitions* currentDefinitions; // before statement currently processed
Definitions* returnedDefinitions; // set by return statements
Definitions* exitDefinitions; // set by exit statements
AllDefinitions* definitions; /// Result computed by this pass.
Definitions* currentDefinitions; /// Before statement currently processed.
Definitions* returnedDefinitions; /// Definitions after return statements.
Definitions* exitDefinitions; /// Definitions after exit statements.
ProgramPoint callingContext;
const StorageMap* storageMap;
bool lhs; // if true we are processing an
// expression on the lhs of an
// assignment
// For each expression the location set it writes
/// if true we are processing an expression on the lhs of an assignment
bool lhs;
/// For each expression the location set it writes
std::map<const IR::Expression*, const LocationSet*> writes;

// Create new visitor, but with same underlying data structures.
// Needed to visit some program fragments repeatedly.
/// Creates new visitor, but with same underlying data structures.
/// Needed to visit some program fragments repeatedly.
ComputeWriteSet(const ComputeWriteSet* source, ProgramPoint context, Definitions* definitions) :
definitions(source->definitions), currentDefinitions(definitions),
returnedDefinitions(nullptr), exitDefinitions(source->exitDefinitions),
Expand Down
5 changes: 4 additions & 1 deletion frontends/p4/methodInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ MethodInstance::resolve(const IR::MethodCallExpression* mce, ReferenceMap* refMa
else
BUG("Could not find type for %1%", mem->expr);
}
if (basetype->is<IR::Type_Header>()) {
if (basetype->is<IR::Type_HeaderUnion>()) {
if (mem->member == IR::Type_Header::isValid)
return new BuiltInMethod(mce, mem->member, mem->expr, mt->to<IR::Type_Method>());
} else if (basetype->is<IR::Type_Header>()) {
if (mem->member == IR::Type_Header::setValid ||
mem->member == IR::Type_Header::setInvalid ||
mem->member == IR::Type_Header::isValid)
Expand Down
1 change: 1 addition & 0 deletions frontends/p4/methodInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ These methods are:
- header.setValid(),
- header.setInvalid(),
- header.isValid(),
- union.isValid(),
- stack.push(int),
- stack.pop(int)
*/
Expand Down
2 changes: 1 addition & 1 deletion frontends/p4/resetHeaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void DoResetHeaders::generateResets(
const IR::Type* type,
const IR::Expression* expr,
IR::Vector<IR::StatOrDecl>* resets) {
if (type->is<IR::Type_Struct>() || type->is<IR::Type_Union>()) {
if (type->is<IR::Type_Struct>() || type->is<IR::Type_HeaderUnion>()) {
auto sl = type->to<IR::Type_StructLike>();
for (auto f : sl->fields) {
auto ftype = typeMap->getType(f, true);
Expand Down
2 changes: 1 addition & 1 deletion frontends/p4/toP4/toP4.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class ToP4 : public Inspector {
{ return process(t, "struct"); }
bool preorder(const IR::Type_Header* t) override
{ return process(t, "header"); }
bool preorder(const IR::Type_Union* t) override
bool preorder(const IR::Type_HeaderUnion* t) override
{ return process(t, "header_union"); }
bool preorder(const IR::Type_Package* t) override;
bool preorder(const IR::Type_Parser* t) override;
Expand Down
Loading