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

Adding serializable enums (or enums with backing types and specified representations). #615

Merged
merged 7 commits into from
May 25, 2018
196 changes: 190 additions & 6 deletions p4-16/spec/P4-16-spec.mdk
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Author: The P4 Language Consortium
Heading depth: 5
Pdf Latex: xelatex
Document Class: [11pt]article
Package: [top=1in, bottom=1.25in, left=1in, right=1in]{geometry}
Package: [top=1in, bottom=1.25in, left=1in, right=1in]geometry
Package: fancyhdr

Tex Header:
Expand Down Expand Up @@ -2026,12 +2026,22 @@ An enumeration type is defined using the following syntax:
~ Begin P4Grammar
enumDeclaration
: optAnnotations ENUM name '{' identifierList '}'
| optAnnotations ENUM BIT '<' INTEGER '>' name '{' specifiedIdentifierList '}'
;

identifierList
: name
| identifierList ',' name
;

specifiedIdentifierList
: specifiedIdentifier
| specifiedIdentifierList ', ' specifiedIdentifier
;

specifiedIdentifier
: name '=' initializer
;
~ End P4Grammar

For example, the declaration
Expand All @@ -2043,10 +2053,57 @@ enum Suits { Clubs, Diamonds, Hearths, Spades }
introduces a new enumeration type, which contains four
constants---e.g., `Suits.Clubs`. An `enum` declaration
introduces a new identifier in the current scope for naming the
created type. The underlying representation of such values is not
created type. The underlying representation of the `Suits` enum is not
specified, so their "size" in bits is not specified (it is
target-specific).

It is also possible to specify an `enum` with an underlying representation.
This requires the programmer provide both the fixed-width unsigned integer type and an associated
fixed-width unsigned integer value for each symbolic entry in the enumeration. For example, the
declaration

~ Begin P4Example
enum bit<16> EtherType {
VLAN = 0x8100,
QINQ = 0x9100,
MPLS = 0x8847,
IPV4 = 0x0800,
IPV6 = 0x86dd
// ...
}
~ End P4Example

introduces a new enumeration type, which contains five constants---e.g.,
`EtherType.IPV4`. This `enum` declaration specifies the fixed-width unsigned integer representation
for each entry in the `enum` and provides an underlying type: `bit<16>`.
This type of `enum` declaration can be thought of as declaring a new `bit<16>`
type, where variables or fields of this type are expected to be unsigned 16-bit
integer values, and the mapping of symbolic to numeric values defined by the
`enum` are effectively constants defined as a part of this type. In this way,
an `enum` with an underlying type can be thought of as being a type derived
from the underlying type carrying equality, assignment, and casts to/from the
underlying type.

Compiler implementations are expected to raise an error if the fixed-width unsigned integer representation
for an enumeration entry falls outside the representation range of the underlying
type.

For example, the declaration

~ Begin P4Example
enum bit<8> FailingExample {
first = 1,
second = 2,
third = 3,
unrepresentable = 300
}
~ End P4Example

would raise an error because `300`, the value associated with
`FailingExample.unrepresentable` cannot be represented as a `bit<8>` value.

The `initializer` expression must be a compile-time known value.

Annotations, represented by the non-terminal `optAnnotations`, are
described in Section [#sec-annotations].

Expand Down Expand Up @@ -2278,14 +2335,17 @@ infinite-precision integer, without a width specified.
| `error` | error | error | allowed |
| `match_kind` | error | error | error |
| `bool` | error | error | allowed |
| `enum` | error | error | allowed |
| `enum` | allowed[^enum_header] | error | allowed |
| `header` | error | allowed | allowed |
| header stack | error | error | allowed |
| `header_union` | error | error | allowed |
| `struct` | error | error | allowed |
| `tuple` | error | error | allowed |
|----------------|-----------|---------|----------|

[^enum_header]: an `enum` type used as a field in a `header` must specify a
underlying type and representation for `enum` elements.

Rationale: `int` does not have precise storage requirements,
unlike `bit<>` or `int<>` types. `match_kind`
values are not useful to store in a variable, as they
Expand Down Expand Up @@ -2717,13 +2777,137 @@ X.v1 // reference to v1
v1 // error - v1 is not in the top-level namespace
~ End P4Example

Similar to errors, `enum` expressions only support equality (`==`)
and inequality (`!=`) comparisons. Expressions whose type is an `enum`
Similar to errors, `enum` expressions without a specified underlying type only support equality (`==`)
and inequality (`!=`) comparisons. Expressions whose type is an `enum` without a specified underlying type
cannot be cast to or from any other type.

Note that if an `enum` value appears in the control-plane API, the
An `enum` may also specify an underlying type, such as the following:

~ Begin P4Example
enum bit<8> E {
e1 = 0,
e2 = 1,
e3 = 2
}
~ End P4Example

More than one symbolic value in an `enum` may map to the same fixed-with
integer value.

~ Begin P4Example
enum bit<8> NonUnique {
b1 = 0,
b2 = 1, // Note, both b2 and b3 map to the same value.
b3 = 1,
b4 = 2
}
~ End P4Example

An `enum` with an underlying type also supports explicit casts to and from the
underlying type. For instance, the following code:

~ Begin P4Example
bit<8> x;
E a = E.e2;
E b;

x = (bit<8>) a; // sets x to 1
b = (E) x; // sets b to E.e2
~ End P4Example

casts `a`, which was initialized to `E.e2` to a `bit<8>`, using the specified
fixed-width unsigned integer representation for `E.e2`, `1`. The variable `b` is then set to the
symbolic value `E.e2`, which corresponds to the fixed-width unsigned integer value `1`.

Note that while it is always safe to cast from an `enum` to its fixed-width unsigned integer type,
and vice versa, there may be cases where casting a fixed-width unsigned integer value to
its related `enum` type produces an unnamed value.

~ Begin P4Example
bit<8> x = 5;
E e = (E) x; // sets e to an unnamed value
~ End P4Example

sets `e` to an unnamed value, since there is no symbol corresponding to the
fixed-width unsigned integer value `5`.

For example, in the following code, the `else` clause of the `if/else if/else`
block can be reached even though the matches on `x` are complete with respect
to the symbols defined in `MyPartialEnum_t`:

~ Begin P4Example
enum bit<2> MyPartialEnum_t {
VALUE_A = 2w0,
VALUE_B = 2w1,
VALUE_C = 2w2
}

bit<2> y = < some value >;
MyPartialEnum_t x = (MyPartialEnum_t)y;

if (x == MyPartialEnum_t.VALUE_A) {
// some code here
} else if (x == MyPartialEnum_t.VALUE_B) {
// some code here
} else if (x == MyPartialEnum_t.VALUE_C) {
// some code here
} else {
// A P4 compiler MUST ASSUME that this branch can be executed
// some code here
}
~ End P4Example

Additionally, if an enumeration is used as a field of a header, we would expect
the `transition select` to match `default` when the parsed integer value does
not match one of the symbolic values of `EtherType` in the following example:

~ Begin P4Example
enum bit<16> EtherType {
VLAN = 0x8100,
IPV4 = 0x0800,
IPV6 = 0x86dd
}

header ethernet {
...
EtherType etherType;
...
}

parser my_parser(...) {
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
EtherType.VLAN : parse_vlan;
EtherType.IPV4 : parse_ipv4;
EtherType.IPV6: parse_ipv6;
default: reject;
}
}
~ End P4Example

Any variable with an `enum` type that contains an unnamed value, whether as the result of a cast
to an `enum` with an underlying type, parse into the field of an `enum` with an
underlying type, or simply the declaration of any `enum` without a specified
initial value will not be equal to any of the values defined for that
type. Such an unnamed value should still lead to predictable
behavior in cases where any legal value would match, e.g. it should
match in any of these situations:

- If used in a `select` expression, it should match `default` or `_`
in a key set expression.
- If used as a key with `match_kind` `ternary` in a table, it should
match a table entry where the field has all bit positions "don't
care".
- If used as a key with `match_kind` `lpm` in a table, it should match
a table entry where the field has a prefix length of 0.

Note that if an `enum` value lacking an underlying type appears in the control-plane API, the
compiler must select a suitable serialization data type and
representation.
For `enum` values with an underlying type and representations, the compiler should
use the specified underlying type as the serialization data type and
representation.

## Expressions on Booleans { #sec-bool-exprs }

Expand Down
10 changes: 10 additions & 0 deletions p4-16/spec/grammar.mdk
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ structField

enumDeclaration
: optAnnotations ENUM name '{' identifierList '}'
| optAnnotations ENUM BIT '<' INTEGER '>' name '{' specifiedIdentifierList '}'
;

errorDeclaration
Expand All @@ -349,6 +350,15 @@ identifierList
| identifierList ',' name
;

specifiedIdentifierList
: specifiedIdentifier
| specifiedIdentifierList ', ' specifiedIdentifier
;

specifiedIdentifier
: name '=' initializer
;

typedefDeclaration
: optAnnotations TYPEDEF typeRef name ';'
| optAnnotations TYPEDEF derivedTypeDeclaration name ';'
Expand Down