From f7b8a19f2c832845536116afaa3a2c2d152bdbd0 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 9 Jan 2023 15:37:46 -0500 Subject: [PATCH 01/20] Introduce local compile-time known values --- p4-16/spec/P4-16-spec.mdk | 76 +++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index e9e12f8655..a9275ddd01 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1660,7 +1660,7 @@ directions: parameters in this case behave like `in` parameters. See Section [#sec-invoke-actions] for further details. - Default parameter values are only allowed for 'in' or direction-less parameters; - these values must evaluate to compile-time constants. + these values must evaluate to compile-time known values. ### Optional parameters { #sec-optional-parameters } @@ -1775,8 +1775,7 @@ P4 supports the following built-in base types: restricted circumstances. - The `error` type, which is used to convey errors in a target-independent, compiler-managed way. -- The `string` type, which can be used only for compile-time - constant string values. +- The `string` type, which can be used only for compile-time known constant string values. - The `match_kind` type, which is used for describing the implementation of table lookups, - `bool`, which represents Boolean values @@ -1866,7 +1865,7 @@ string values; one cannot declare variables with a `string` type. Parameters with type `string` can be only directionless (see Section [#sec-calling-convention]). P4 does not support string manipulation in the dataplane; the `string` type is only allowed for denoting -compile-time constant string values. These may be useful, for +compile-time known constant string values. These may be useful, for example, a specific target architecture may support an extern function for logging with the following signature: @@ -1937,7 +1936,7 @@ behavior should match this specification. An unsigned integer (which we also call a "bit-string") has an arbitrary width, expressed in bits. A bit-string of width `W` is declared as: `bit`. `W` must be an expression that evaluates to a -compile-time known value (see Section [#sec-ct-constants]) that is a +local compile-time known value (see Section [#sec-ct-constants]) that is a non-negative integer. When using an expression for the size, the expression must be parenthesized. Bitstrings with width 0 are allowed; they have no actual bits, and can only have the value 0. See @@ -1970,7 +1969,7 @@ described in Section [#sec-bit-ops]. Signed integers are represented using two's complement. An integer with `W` bits is declared as: `int`. `W` must be an expression that evaluates to -a compile-time known value that is a positive integer. +a local compile-time known value that is a positive integer. Bits within an integer are numbered from `0` to `W-1`. Bit `0` is the least significant, and bit `W-1` is the sign bit. @@ -1997,7 +1996,7 @@ values, P4 provides a special bit-string type whose size is set at runtime, called a `varbit`. The type `varbit` denotes a bit-string with a width of at most `W` -bits, where `W` must be a non-negative integer that is a compile-time +bits, where `W` must be a non-negative integer that is a local compile-time known value. For example, the type `varbit<120>` denotes the type of bit-string values that may have between 0 and 120 bits. Most operations that are applicable to fixed-size bit-strings (unsigned @@ -3377,7 +3376,7 @@ Bit-strings also support the following operations: produces a result where all bits are zero. - Extraction of a set of contiguous bits, also known as a slice, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to - non-negative compile-time known values, and `H >= L`. The types of `H` and `L` + non-negative, local compile-time known values, and `H >= L`. The types of `H` and `L` (which do not need to be identical) must be one of the following: - `int` - an arbitrary-precision integer @@ -3395,7 +3394,7 @@ Bit-strings also support the following operations: `0 <= L <= H < W` are checked statically (where `W` is the length of the source bit-string). Note that both endpoints of the extraction are inclusive. The bounds are required to be - known-at-compile-time values so that the result width can be computed + local, compile-time known values so that the result width can be computed at compile time. Slices are also l-values, which means that P4 supports assigning to a slice: ` e[H:L] = x `. The effect of this statement is to set bits `H` through `L` (inclusive of @@ -3474,7 +3473,7 @@ The `int` datatype also support the following operations: - all result bits are one when shifting a negative value right - Extraction of a set of contiguous bits, also known as a slice, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to - non-negative compile-time known values, and `H >= L` must be true. + non-negative, local compile-time known values, and `H >= L` must be true. The types of `H` and `L` (which do not need to be identical) must be one of the following: @@ -3525,7 +3524,7 @@ expressions of type `int` must be compile-time known values. The type The expression `a << b` is equal to $a \times 2^b$ while `a >> b` is equal to $\lfloor{a / 2^b}\rfloor$. - Bit slices, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to - non-negative compile-time known values, and `H >= L` must be true. + non-negative, local compile-time known values, and `H >= L` must be true. The types of `H` and `L` (which do not need to be identical) must be one of the following: @@ -4783,7 +4782,7 @@ H h3 = { ... }; // initialize h3 with a header that is valid, field f1 0, field The method calls `minSizeInBits`, `minSizeInBytes`, `maxSizeInBits`, and `maxSizeInBytes` can be applied to some expressions. Each of -these method calls evaluate to compile-time known values that return +these method calls evaluate to local compile-time known values that return the minimum size in bits required to store the expression (thus the result type of these methods has type `int`). None of these methods evaluates the expression itself. In consequence, the expression may @@ -7182,28 +7181,52 @@ The evaluation of a P4 program is done in two stages: executed to completion, in isolation, when it receives control from the architecture -## Compile-time known values { #sec-ct-constants } +## Compile-time known and local compile-time known values { #sec-ct-constants } + +Certain expressions in a P4 program have the property that their value +can be determined at compile time. Moreover, for some of these +expressions, their value can be determined only using information in +the current scope. We call these _compile-time known values_ and +_local compile-time known values_ respectively. The following are compile-time known values: - Integer literals, Boolean literals, and string literals. -- Identifiers declared in an `error`, `enum`, or `match_kind` - declaration. +- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. - The `default` identifier. - The `size` field of a value with type header stack. - The `_` identifier when used as a `select` expression label -- Identifiers that represent declared types, actions, tables, parsers, - controls, or packages. +- Identifiers that represent declared types, actions, tables, parsers, controls, or packages. - Tuple expression where all components are compile-time known values. -- Structure-valued expressions, where all fields are compile-time - known values. -- Instances constructed by instance declarations (Section - [#sec-instantiations]) and constructor invocations. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) - when their operands are all compile-time known values. +- Structure-valued expressions, where all fields are compile-time known values. +- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. - Identifiers declared as constants using the `const` keyword. -- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, - `e.maxSizeInBits()` and `e.maxSizeInBytes()` +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` + +The following are local compile-time known values: + +- Integer literals, Boolean literals, and string literals. +- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. +- The `default` identifier. +- The `size` field of a value with type header stack. +- The `_` identifier when used as a `select` expression label +- Identifiers that represent declared types, actions, tables, parsers, controls, or packages. +- Tuple expression where all components are local compile-time known values. +- Structure-valued expressions, where all fields are local compile-time known values. +- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. +- Identifiers declared as constants using the `const` keyword. +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` +- Constructor parameters + +Intuitively, the main difference between _compile-time known values_ +and _local compile-time known values_ is that the latter also contains +constructor parameters. The distinction is important when it comes to +defining the meaning of features like types. For example, in the type +`bit`, the expression `e` must be a local compile-time known value +as the value of a constructor parameter, is only determined when the +constructor is applied to a value. ## Compile-time Evaluation { #sec-ct-eval } @@ -7645,7 +7668,7 @@ extern bool static_assert(bool check, string message); extern bool static_assert(bool check); These functions both return boolean values. Since the parameters are -directionless, these functions require compile-time constant values as +directionless, these functions require compile-time known values as arguments, thus they can be used to enforce compile-time invariants. Since P4 does not allow statements at the program top-level (outside of apply blocks), these functions can be used at the top-level by @@ -8052,6 +8075,7 @@ The P4 compiler should provide: ## Summary of changes made in version 1.2.4 +* Introduced distinction between local compile-time known and compile-time known values (Section[#sec-ct-constants]). * Specified that algorithm for generating control-plane names for keys is optional (Section [#sec-cp-keys]). * Clarified types of expressions that may appear in `select` (Section [#sec-select]). * Explicitly disallow overloading of parsers, controls, and packages (Section [#sec-extern-objects]). From 0c7d00e9c7a838468336eb47fbedfe89751c85e5 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 9 Jan 2023 15:56:44 -0500 Subject: [PATCH 02/20] Fix bugs and streamline definitions with @apinski-cavium's input --- p4-16/spec/P4-16-spec.mdk | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index a9275ddd01..5b2880769e 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7189,21 +7189,6 @@ expressions, their value can be determined only using information in the current scope. We call these _compile-time known values_ and _local compile-time known values_ respectively. -The following are compile-time known values: - -- Integer literals, Boolean literals, and string literals. -- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. -- The `default` identifier. -- The `size` field of a value with type header stack. -- The `_` identifier when used as a `select` expression label -- Identifiers that represent declared types, actions, tables, parsers, controls, or packages. -- Tuple expression where all components are compile-time known values. -- Structure-valued expressions, where all fields are compile-time known values. -- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. -- Identifiers declared as constants using the `const` keyword. -- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` - The following are local compile-time known values: - Integer literals, Boolean literals, and string literals. @@ -7218,10 +7203,17 @@ The following are local compile-time known values: - The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. - Identifiers declared as constants using the `const` keyword. - Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` -- Constructor parameters + +The following are compile-time known values: + +- All local compile-time known values. +- Tuple expression where all components are compile-time known values. +- Structure-valued expressions, where all fields are compile-time known values. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. +- Constructor parameters. Intuitively, the main difference between _compile-time known values_ -and _local compile-time known values_ is that the latter also contains +and _local compile-time known values_ is that the former also contains constructor parameters. The distinction is important when it comes to defining the meaning of features like types. For example, in the type `bit`, the expression `e` must be a local compile-time known value From f631b9bd4e616a8f773303f78d33e6e91658d49a Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 30 Jan 2023 23:46:02 -0500 Subject: [PATCH 03/20] Add missing operators and fix formatting --- p4-16/spec/P4-16-spec.mdk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 5b2880769e..3acd78b57f 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7200,7 +7200,7 @@ The following are local compile-time known values: - Tuple expression where all components are local compile-time known values. - Structure-valued expressions, where all fields are local compile-time known values. - Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>>`, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. - Identifiers declared as constants using the `const` keyword. - Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` @@ -7209,7 +7209,7 @@ The following are compile-time known values: - All local compile-time known values. - Tuple expression where all components are compile-time known values. - Structure-valued expressions, where all fields are compile-time known values. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. - Constructor parameters. Intuitively, the main difference between _compile-time known values_ From d65650275ab8d9bec6d05f06957fb4e46c6e08fb Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Tue, 6 Jun 2023 10:45:10 -0400 Subject: [PATCH 04/20] Address comment from Jonathan --- p4-16/spec/P4-16-spec.mdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 3acd78b57f..0de3f7713c 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7207,10 +7207,10 @@ The following are local compile-time known values: The following are compile-time known values: - All local compile-time known values. +- Constructor parameters. - Tuple expression where all components are compile-time known values. - Structure-valued expressions, where all fields are compile-time known values. - The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. -- Constructor parameters. Intuitively, the main difference between _compile-time known values_ and _local compile-time known values_ is that the former also contains From 273f207a5427bf0c737db253d2960789fd0046a9 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Fri, 5 Jan 2024 22:09:28 +0100 Subject: [PATCH 05/20] Rebase and resolve conflicts --- p4-16/spec/P4-16-spec.mdk | 69 +++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 536082a86e..a2d0a42472 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1694,8 +1694,8 @@ directions: including values for the directionless parameters. The directionless parameters in this case behave like `in` parameters. See Section [#sec-invoke-actions] for further details. -- Default parameter values are only allowed for 'in' or direction-less parameters; - these values must evaluate to compile-time constants. +- Default parameter values are only allowed for 'in' or direction-less + parameters; these values must evaluate to compile-time known values. - If parameters with default values do not appear at the end of the list of parameters, invocations that use the default values must use named arguments, as in the following @@ -1826,8 +1826,7 @@ P4 supports the following built-in base types: restricted circumstances. - The `error` type, which is used to convey errors in a target-independent, compiler-managed way. -- The `string` type, which can be used only for compile-time - constant string values. +- The `string` type, which can be used only for compile-time known constant string values. - The `match_kind` type, which is used for describing the implementation of table lookups, - `bool`, which represents Boolean values @@ -1917,7 +1916,7 @@ string values; one cannot declare variables with a `string` type. Parameters with type `string` can be only directionless (see Section [#sec-calling-convention]). P4 does not support string manipulation in the dataplane; the `string` type is only allowed for denoting -compile-time constant string values. These may be useful, for +compile-time known constant string values. These may be useful, for example, a specific target architecture may support an extern function for logging with the following signature: @@ -1988,7 +1987,7 @@ behavior should match this specification. An unsigned integer (which we also call a "bit-string") has an arbitrary width, expressed in bits. A bit-string of width `W` is declared as: `bit`. `W` must be an expression that evaluates to a -compile-time known value (see Section [#sec-ct-constants]) that is a +local compile-time known value (see Section [#sec-ct-constants]) that is a non-negative integer. When using an expression for the size, the expression must be parenthesized and compile-time known. Bitstrings with width 0 are @@ -2024,9 +2023,9 @@ described in Section [#sec-bit-ops]. Signed integers are represented using two's complement. An integer with `W` bits is declared as: `int`. `W` must be an expression that evaluates to -a compile-time known value that is a positive integer. +a local compile-time known value that is a positive integer. Note that `int` type refers to both cases of `int` -and `int<(expression)>` where the width is compile-time known. +and `int<(expression)>` where the width is a local compile-time known value. Bits within an integer are numbered from `0` to `W-1`. Bit `0` is the least significant, and bit `W-1` is the sign bit. @@ -2053,7 +2052,7 @@ values, P4 provides a special bit-string type whose size is set at runtime, called a `varbit`. The type `varbit` denotes a bit-string with a width of at most `W` -bits, where `W` must be a non-negative integer that is a compile-time +bits, where `W` must be a non-negative integer that is a local compile-time known value. For example, the type `varbit<120>` denotes the type of bit-string values that may have between 0 and 120 bits. Most operations that are applicable to fixed-size bit-strings (unsigned @@ -3475,7 +3474,7 @@ Bit-strings also support the following operations: `0 <= L <= H < W` are checked statically (where `W` is the length of the source bit-string). Note that both endpoints of the extraction are inclusive. The bounds are required to be - known-at-compile-time values so that the result width can be computed + local, compile-time known values so that the result width can be computed at compile time. Slices are also l-values, which means that P4 supports assigning to a slice: ` e[H:L] = x `. The effect of this statement is to set bits `H` through `L` (inclusive of @@ -3557,7 +3556,7 @@ The `int` datatype also support the following operations: - all result bits are one when shifting a negative value right - Extraction of a set of contiguous bits, also known as a slice, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to - non-negative compile-time known values, and `H >= L` must be true. + non-negative, local compile-time known values, and `H >= L` must be true. The types of `H` and `L` (which do not need to be identical) must be numeric (Section [#sec-numeric-values]). The result is an unsigned bit-string of width `H - L + 1`, including the bits @@ -3601,7 +3600,7 @@ expressions of type `int` must be compile-time known values. The type The expression `a << b` is equal to $a \times 2^b$ while `a >> b` is equal to $\lfloor{a / 2^b}\rfloor$. - Bit slices, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to - non-negative compile-time known values, and `H >= L` must be true. + non-negative, local compile-time known values, and `H >= L` must be true. The types of `H` and `L` (which do not need to be identical) must be one of the following: @@ -4990,7 +4989,7 @@ H h3 = { ... }; // initialize h3 with a header that is valid, field f1 0, field The method calls `minSizeInBits`, `minSizeInBytes`, `maxSizeInBits`, and `maxSizeInBytes` can be applied to some expressions. Each of -these method calls evaluate to compile-time known values that return +these method calls evaluate to local compile-time known values that return the minimum size in bits required to store the expression (thus the result type of these methods has type `int`). None of these methods evaluates the expression itself. In consequence, the expression may @@ -7716,13 +7715,18 @@ The evaluation of a P4 program is done in two stages: executed to completion, in isolation, when it receives control from the architecture -## Compile-time known values { #sec-ct-constants } +## Compile-time known and local compile-time known values { #sec-ct-constants } + +Certain expressions in a P4 program have the property that their value +can be determined at compile time. Moreover, for some of these +expressions, their value can be determined only using information in +the current scope. We call these _compile-time known values_ and +_local compile-time known values_ respectively. The following are compile-time known values: - Integer literals, Boolean literals, and string literals. -- Identifiers declared in an `error`, `enum`, or `match_kind` - declaration. +- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. - The `default` identifier. - The `size` field of a value with type header stack. - The `_` identifier when used as a `select` expression label @@ -7738,11 +7742,33 @@ The following are compile-time known values: - Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. - A legal cast applied to a compile-time known value -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) - when their operands are all compile-time known values. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. - Identifiers declared as constants using the `const` keyword. -- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, - `e.maxSizeInBits()` and `e.maxSizeInBytes()` +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` + +The following are local compile-time known values: + +- Integer literals, Boolean literals, and string literals. +- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. +- The `default` identifier. +- The `size` field of a value with type header stack. +- The `_` identifier when used as a `select` expression label +- Identifiers that represent declared types, actions, tables, parsers, controls, or packages. +- Tuple expression where all components are local compile-time known values. +- Structure-valued expressions, where all fields are local compile-time known values. +- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. +- Identifiers declared as constants using the `const` keyword. +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` +- Constructor parameters + +Intuitively, the main difference between _compile-time known values_ +and _local compile-time known values_ is that the latter also contains +constructor parameters. The distinction is important when it comes to +defining the meaning of features like types. For example, in the type +`bit`, the expression `e` must be a local compile-time known value +as the value of a constructor parameter, is only determined when the +constructor is applied to a value. ## Compile-time Evaluation { #sec-ct-eval } @@ -8184,7 +8210,7 @@ extern bool static_assert(bool check, string message); extern bool static_assert(bool check); These functions both return boolean values. Since the parameters are -directionless, these functions require compile-time constant values as +directionless, these functions require compile-time known values as arguments, thus they can be used to enforce compile-time invariants. Since P4 does not allow statements at the program top-level (outside of apply blocks), these functions can be used at the top-level by @@ -8592,6 +8618,7 @@ The P4 compiler should provide: ## Summary of changes made in version 1.2.4 +* Introduced distinction between local compile-time known and compile-time known values (Section[#sec-ct-constants]). * Added header stack expressions (Section [#sec-hs-init]). * Allow casts from a type to itself (Section [#sec-casts]). * Added an invalid header or header union expression `{#}` (Sections [#sec-ops-on-hdrs] and [#sec-expr-hu]). From ad2e692b0b422341476f2681de008a63d375a2fe Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 9 Jan 2023 15:56:44 -0500 Subject: [PATCH 06/20] Fix bugs and streamline definitions with @apinski-cavium's input --- p4-16/spec/P4-16-spec.mdk | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index a2d0a42472..c10ec1fbd7 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7723,29 +7723,6 @@ expressions, their value can be determined only using information in the current scope. We call these _compile-time known values_ and _local compile-time known values_ respectively. -The following are compile-time known values: - -- Integer literals, Boolean literals, and string literals. -- Identifiers declared in an `error`, `enum`, or `match_kind` declaration. -- The `default` identifier. -- The `size` field of a value with type header stack. -- The `_` identifier when used as a `select` expression label -- The expression `{#}` representing an invalid header or header union - value. -- Identifiers that represent declared types, actions, tables, parsers, - controls, or packages. -- Tuple expression where all components are compile-time known values. -- Structure-valued expressions, where all fields are compile-time - known values. -- Expressions evaluating to a list type, where all elements are - compile-time known values. -- Instances constructed by instance declarations (Section - [#sec-instantiations]) and constructor invocations. -- A legal cast applied to a compile-time known value -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. -- Identifiers declared as constants using the `const` keyword. -- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` - The following are local compile-time known values: - Integer literals, Boolean literals, and string literals. @@ -7757,13 +7734,20 @@ The following are local compile-time known values: - Tuple expression where all components are local compile-time known values. - Structure-valued expressions, where all fields are local compile-time known values. - Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all local compile-time known values. - Identifiers declared as constants using the `const` keyword. - Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` -- Constructor parameters + +The following are compile-time known values: + +- All local compile-time known values. +- Tuple expression where all components are compile-time known values. +- Structure-valued expressions, where all fields are compile-time known values. +- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. +- Constructor parameters. Intuitively, the main difference between _compile-time known values_ -and _local compile-time known values_ is that the latter also contains +and _local compile-time known values_ is that the former also contains constructor parameters. The distinction is important when it comes to defining the meaning of features like types. For example, in the type `bit`, the expression `e` must be a local compile-time known value From f83ec5de5271fef94a5713f43cadac6007ad2851 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 30 Jan 2023 23:46:02 -0500 Subject: [PATCH 07/20] Add missing operators and fix formatting --- p4-16/spec/P4-16-spec.mdk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index c10ec1fbd7..f52afa7101 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7734,7 +7734,7 @@ The following are local compile-time known values: - Tuple expression where all components are local compile-time known values. - Structure-valued expressions, where all fields are local compile-time known values. - Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all local compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>>`, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all local compile-time known values. - Identifiers declared as constants using the `const` keyword. - Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` @@ -7743,7 +7743,7 @@ The following are compile-time known values: - All local compile-time known values. - Tuple expression where all components are compile-time known values. - Structure-valued expressions, where all fields are compile-time known values. -- The following expressions (`+`, `-`, `*`, `/ `, `%`, `!`, `&`, `|`, `&&`, `||`, `<< `, `>> `, `~` `, `\ `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. - Constructor parameters. Intuitively, the main difference between _compile-time known values_ From 82320a95d90468054b58920c4a5f1e978fa796fd Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Tue, 6 Jun 2023 10:45:10 -0400 Subject: [PATCH 08/20] Address comment from Jonathan --- p4-16/spec/P4-16-spec.mdk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index f52afa7101..db6b6fc305 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -7741,9 +7741,10 @@ The following are local compile-time known values: The following are compile-time known values: - All local compile-time known values. +- Constructor parameters. - Tuple expression where all components are compile-time known values. - Structure-valued expressions, where all fields are compile-time known values. -- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. - Constructor parameters. Intuitively, the main difference between _compile-time known values_ From 518cf10d1a3285ba16c97ece8f947f39bb5179a3 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 8 Jan 2024 18:55:15 +0100 Subject: [PATCH 09/20] Polish text for local compile-time known values. Drivebys: - Eliminate use of "constant" to mean literal and/or compile-time known value. - Replace "constant" to refer to components of "error" and "enum" with "element" instead. - Enforce consistent grammar and polish some awkward phrases. --- p4-16/spec/P4-16-spec.mdk | 223 +++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 3b86961dc0..77db824021 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1296,13 +1296,13 @@ literal. Here are some examples of numeric literals: #### String literals { #sec-string-literals } -String literals (string constants) are specified as an arbitrary -sequence of 8-bit characters, enclosed within double quote characters `"` -(ASCII code 34). Strings start with a double quote character -and extend to the first double quote sign which is not immediately -preceded by an odd number of backslash characters (ASCII code 92). P4 -does not make any validity checks on strings (i.e., it does not check -that strings represent legal UTF-8 encodings). +String literals are specified as an arbitrary sequence of 8-bit +characters, enclosed within double quote characters `"` (ASCII code +34). Strings start with a double quote character and extend to the +first double quote sign which is not immediately preceded by an odd +number of backslash characters (ASCII code 92). P4 does not make any +validity checks on strings (i.e., it does not check that strings +represent legal UTF-8 encodings). Since P4 does not provide any operations on strings, string literals are generally passed unchanged through the P4 compiler to @@ -1681,8 +1681,8 @@ directions: - All constructor parameters are evaluated at compilation-time, and in consequence they must all be directionless (they cannot be `in`, `out`, or `inout`); this applies to `package`, `control`, `parser`, - and `extern` objects. Values for these parameters must be specified - at compile-time, and must evaluate to compile-time known values. + and `extern` objects. Expressions for these parameters must be supplied + at compile-time, and they must evaluate to compile-time known values. See Section [#sec-parameterization] for further details. - For actions all directionless parameters must be at the end of the parameter list. When an action appears in a `table`'s `actions` @@ -1826,11 +1826,11 @@ P4 supports the following built-in base types: restricted circumstances. - The `error` type, which is used to convey errors in a target-independent, compiler-managed way. -- The `string` type, which can be used only for compile-time known constant string values. +- The `string` type, which can be used only with compile-time known values. - The `match_kind` type, which is used for describing the implementation of table lookups, - `bool`, which represents Boolean values -- `int`, which represents arbitrary-sized constant integer values +- `int`, which represents arbitrary-sized integer values - Bit-strings of fixed width, denoted by `bit<>` - Fixed-width signed integers represented using two's complement `int<>` - Bit-strings of dynamically-computed width with a fixed maximum width `varbit<>` @@ -1848,14 +1848,14 @@ restricted places in P4 programs. ### The error type The error type contains opaque distinct values that can be used to signal -errors. It is written as `error`. New constants of the error type +errors. It is written as `error`. New elements of the error type are defined with the syntax: ~ Begin P4Grammar [INCLUDE=grammar.mdk:errorDeclaration] ~ End P4Grammar -All `error` constants are inserted into the `error` +All elements of the `error` type are inserted into the `error` namespace, irrespective of the place where an error is defined. `error` is similar to an enumeration (`enum`) type in other languages. A program can contain multiple `error` declarations, which @@ -1863,7 +1863,7 @@ the compiler will merge together. It is an error to declare the same identifier multiple times. Expressions of type `error` are described in Section [#sec-error-exprs]. -For example, the following declaration creates two constants of `error` +For example, the following declaration creates two elements of the `error` type (these errors are declared in the P4 core library): ~ Begin P4Example @@ -1916,7 +1916,7 @@ string values; one cannot declare variables with a `string` type. Parameters with type `string` can be only directionless (see Section [#sec-calling-convention]). P4 does not support string manipulation in the dataplane; the `string` type is only allowed for denoting -compile-time known constant string values. These may be useful, for +compile-time known values. These may be useful, for example, a specific target architecture may support an extern function for logging with the following signature: @@ -1924,7 +1924,7 @@ for logging with the following signature: extern void log(string message); ~ End P4Example -The only strings that can appear in a P4 program are constant string +The only strings that can appear in a P4 program are string literals, described in Section [#sec-string-literals]. For example, the following annotation indicates that a specific name should be used for a table when generating the control-plane API: @@ -1972,7 +1972,7 @@ restrictions may include: - Alignment and padding constraints (e.g., arithmetic may only be supported on widths which are an integral number of bytes). - Constraints on some operands (e.g., some architectures may only - support multiplications with small constants, or shifts with small + support multiplications by small values, or shifts with small values). The documentation supplied with a target should clearly specify restrictions, and @@ -1989,12 +1989,12 @@ arbitrary width, expressed in bits. A bit-string of width `W` is declared as: `bit`. `W` must be an expression that evaluates to a local compile-time known value (see Section [#sec-ct-constants]) that is a non-negative integer. When using an expression for -the size, the expression must be parenthesized and compile-time known. +the size, the expression must be parenthesized and a compile-time known value. Bitstrings with width 0 are allowed; they have no actual bits, and can only have the value 0. See [#sec-uninitialized-values-and-writing-invalid-headers] for additional details. Note that `bit` type refers to both cases of `bit` -and `bit<(expression)>` where the width is compile-time known. +and `bit<(expression)>` where the width is a compile-time known value. ~ Begin P4Example const bit<32> x = 10; // 32-bit constant with value 10. @@ -2058,7 +2058,7 @@ of bit-string values that may have between 0 and 120 bits. Most operations that are applicable to fixed-size bit-strings (unsigned numbers) *cannot* be performed on dynamically sized bit-strings. Note that `varbit` type refers to both cases of `varbit` -and `varbit<(expression)>` where the width is compile-time known. +and `varbit<(expression)>` where the width is a compile-time known value. P4 architectures may impose additional constraints on varbit types: for example, they may limit the maximum size, or they may require `varbit` @@ -2099,9 +2099,9 @@ parameters. #### Integer literal types -The types of integer literals (constants) are as follows: +The types of integer literals are as follows: -- A simple integer constant has type `int`. +- An integer has type `int`. - A non-negative integer prefixed with an integer width `N` and the character `w` has type `bit`. - An integer prefixed with an integer width `N` and the character `s` @@ -2186,9 +2186,9 @@ enum Suits { Clubs, Diamonds, Hearths, Spades } ~ End P4Example introduces a new enumeration type, which contains four -constants---e.g., `Suits.Clubs`. An `enum` declaration +elements---e.g., `Suits.Clubs`. An `enum` declaration introduces a new identifier in the current scope for naming the -created type along with its distinct constants. +created type along with its distinct elements. The underlying representation of the `Suits` enum is not specified, so their "size" in bits is not specified (it is target-specific). @@ -2199,8 +2199,8 @@ allowed to have fields with such `enum` types. This requires the programmer prov an associated integer value for each symbolic entry in the enumeration. The symbol `typeRef` in the grammar above must be one of the following types: -- an unsigned integer, i.e. `bit` for some compile-time known `W`. -- a signed integer, i.e. `int` for some compile-time known `W`. +- an unsigned integer, i.e. `bit` for some compile-time known value `W`. +- a signed integer, i.e. `int` for some compile-time known value `W`. - a type name declared via `typedef`, where the base type of that type is either one of the types listed above, or another `typedef` name that meets these conditions. For example, the declaration @@ -2215,13 +2215,13 @@ enum bit<16> EtherType { } ~ End P4Example -introduces a new enumeration type, which contains five constants---e.g., +introduces a new enumeration type, which contains five elements---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>` +This kind 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, +`enum` are also defined as a part of this declaration. 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. @@ -2323,14 +2323,13 @@ header ipv6_t { } ~ End P4Example -Headers that do not contain any `varbit` field are "fixed -size." Headers containing `varbit` fields have "variable -size." The size (in bits) of a fixed-size header is a constant, and it -is simply the sum of the sizes of all component fields (without -counting the validity bit). There is no padding or alignment of the -header fields. Targets may impose additional constraints on -header types---e.g., restricting headers to sizes that are an integer -number of bytes. +Headers that do not contain any `varbit` field are "fixed size." +Headers containing `varbit` fields have "variable size." The size (in +bits) of a fixed-size header is simply the sum of the sizes of all +component fields (without counting the validity bit). There is no +padding or alignment of the header fields. Targets may impose +additional constraints on header types---e.g., restricting headers to +sizes that are an integer number of bytes. For example, the following declaration describes a typical Ethernet header: @@ -2393,12 +2392,13 @@ defined as: [INCLUDE=grammar.mdk:headerStackType] ~ End P4Grammar -where `typeName` is the name of a header or header union type. For a header stack `hs[n]`, -the term `n` is the maximum defined size, and must be -a positive integer that is a compile-time known value. Nested header -stacks are not supported. At runtime a stack contains `n` values -with type `typeName`, only some of which may be valid. Expressions -on header stacks are discussed in Section [#sec-expr-hs]. +where `typeName` is the name of a header or header union type. For a +header stack `hs[n]`, the term `n` is the maximum defined size, and +must be a compile-time known value that is a a positive +integer. Nested header stacks are not supported. At runtime a stack +contains `n` values with type `typeName`, only some of which may be +valid. Expressions on header stacks are discussed in Section +[#sec-expr-hs]. For example, the following declarations, @@ -3457,8 +3457,8 @@ to bit-strings of the same width: Bit-strings also support the following operations: -- Logical shift left and right by a (not-necessarily-known-at-compile-time) - non-negative integer value, denoted by `<<` and `>>` respectively. +- Logical shift left and right by a non-negative integer value (which need not be + a compile-time known value), denoted by `<<` and `>>` respectively. In a shift, the left operand is unsigned, and right operand must be either an expression of type `bit` or a non-negative integer value that is known at compile time. The result has the same type as the left operand. @@ -3474,7 +3474,7 @@ Bit-strings also support the following operations: `0 <= L <= H < W` are checked statically (where `W` is the length of the source bit-string). Note that both endpoints of the extraction are inclusive. The bounds are required to be - local, compile-time known values so that the result width can be computed + local compile-time known values so that the width of the result can be computed at compile time. Slices are also l-values, which means that P4 supports assigning to a slice: ` e[H:L] = x `. The effect of this statement is to set bits `H` through `L` (inclusive of @@ -3544,7 +3544,7 @@ The `int` datatype also support the following operations: - Arithmetic shift left and right denoted by `<<` and `>>`. The left operand is signed and the right operand must be either an - unsigned number of type `bit` or a non-negative integer compile-time known value. + unsigned number of type `bit` or a compile-time known value that is a non-negative integer. The result has the same type as the left operand. Shifting left produces the exact same bit pattern as a shift left of an unsigned value. Shift left can thus overflow, @@ -3596,7 +3596,7 @@ expressions of type `int` must be compile-time known values. The type - Truncating integer division between positive values, denoted by `/`. - Modulo between positive values, denoted by `%`. - Arithmetic shift left and right denoted by `<<` and `>>`. These operations produce an `int` result. - The right operand must be either an unsigned constant of type `bit` or a non-negative integer compile-time known value. + The right operand must be either an unsigned value of type `bit` or a compile-time known value that is a non-negative integer. The expression `a << b` is equal to $a \times 2^b$ while `a >> b` is equal to $\lfloor{a / 2^b}\rfloor$. - Bit slices, denoted by `[H:L]`, where `H` and `L` must be expressions that evaluate to @@ -3658,7 +3658,7 @@ result is taken from the left operand. The left operand of shifts can be any one out of unsigned bit-strings, signed bit-strings, and arbitrary-precision integers, and the right operand of shifts must be either -an expression of type `bit` or a non-negative integer compile-time known value. +an expression of type `bit` or a compile-time known value that is a non-negative integer. The result has the same type as the left operand. Shifts on signed and unsigned bit-strings deserve a special discussion @@ -3728,7 +3728,7 @@ finding this information in a section dedicated to type `varbit`. inserts a variable-sized bit-string with a known dynamic width into the packet being constructed. -Additionally, the size of a variable-length bit-string can be determined at +Additionally, the maximum size of a variable-length bit-string can be determined at compile-time (Section [#sec-minsizeinbits]). ## Casts { #sec-casts } @@ -3872,7 +3872,7 @@ tuple, bool> x = { 10, false }; ~ End P4Example The fields of a tuple can be accessed using array index syntax `x[0]`, -`x[1]`. The array indexes *must* be compile-time constants, to enable +`x[1]`. The indexes *must* be local compile-time known values, to enable the type-checker to identify the field types statically. Tuples can be compared for equality using `==` and `!=`; two tuples are @@ -4303,9 +4303,8 @@ expressions are legal: - `hs[index]`: produces a reference to the header at the specified position within the stack; if `hs` is an l-value, the result is also an l-value. The header may be invalid. Some implementations - may impose the constraint that the index expression evaluates to a - value that is known at compile time. A P4 compiler must give an error - if an index value that is a compile-time constant is out of range. + may impose the constraint that the index expression must be a compile-time known value. + A P4 compiler must give an error if an index that is a compile-time known value is out of range. Accessing a header stack ``hs`` with an index less than `0` or greater than or equal to `hs.size` results in an undefined value. See Section @@ -4315,7 +4314,7 @@ expressions are legal: The `index` is an expression that must be of numeric types (Section [#sec-numeric-values]). - `hs.size`: produces a 32-bit unsigned integer that returns the - size of the header stack (a compile-time constant). + size of the header stack (a local compile-time known value). - assignment from a header stack `hs` into another stack requires the stacks to have the same types and sizes. All components of `hs` @@ -4348,17 +4347,16 @@ Finally, P4 offers the following computations that can be used to manipulate the elements at the front and back of the stack: - `hs.push_front(int count)`: shifts `hs` "right" by `count`. The - first `count` elements become invalid. The last `count` - elements in the stack are discarded. The `hs.nextIndex` counter - is incremented by `count`. The `count` argument must be a - positive integer that is a compile-time known value. The return type - is `void`. + first `count` elements become invalid. The last `count` elements in + the stack are discarded. The `hs.nextIndex` counter is incremented + by `count`. The `count` argument must be a compile-time known value + that is a positive integer. The return type is `void`. - `hs.pop_front(int count)`: shifts `hs` "left" by `count` (i.e., element with index `count` is copied in stack at index `0`). The last `count` elements become invalid. The `hs.nextIndex` counter is decremented by `count`. The `count` argument must - be a positive integer that is a compile-time known value. The return + be a compile-time known value that is a positive integer. The return type is `void`. The following pseudocode defines the behavior of `push_front` and `pop_front`: @@ -4394,8 +4392,8 @@ void pop_front(int count) { } ~ End P4Pseudo -Similar to structs and headers, the size of a header stack can be determined at -compile-time (Section [#sec-minsizeinbits]). +Similar to structs and headers, the size of a header stack is a compile-time known value +(Section [#sec-minsizeinbits]). Two header stacks can be compared for equality (`==`) or inequality (`!=`) only if they have the same element type and the same length. Two @@ -4640,8 +4638,8 @@ parser Tcp_option_parser(packet_in b, out Tcp_option_stack vec) { } ~End P4Example -Similar to headers, the size of a header union can be determined at -compile-time (Section [#sec-minsizeinbits]). +Similar to headers, the size of a header union is a compile-time known +value (Section [#sec-minsizeinbits]). The expression `{#}` represents an invalid header union of some type, but it can be any header or header union type. A P4 compiler may require an @@ -4819,20 +4817,17 @@ limitation, such values are currently of limited utility. ## Reading uninitialized values and writing fields of invalid headers { #sec-uninitialized-values-and-writing-invalid-headers } As mentioned in Section [#sec-expr-hs], any reference to an element of -a header stack `hs[index]` where `index` is a compile-time constant -expression must give an error at compile time if the value of the -index is out of range. That section also defines the run time -behavior of the expressions `hs.next` and `hs.last`, and the behaviors -specified there take precedence over anything in this section for -those expressions. +a header stack `hs[index]` where `index` is a compile-time known value +must give an error at compile time if the value of the index is out of +range. That section also defines the run time behavior of the +expressions `hs.next` and `hs.last`, and the behaviors specified there +take precedence over anything in this section for those expressions. All mentions of header stack elements in this section only apply -for expressions `hs[index]` where `index` is a runtime variable -expression, i.e. not a compile-time constant value. +for expressions `hs[index]` where `index` is not a compile-time known value. A P4 implementation is allowed not to support `hs[index]` where -`index` is a runtime variable expression, but if it does support -these expressions, the implementation should conform to the -behaviors specified in this section. +`index` is not a compile-time known value, but if it does offer such support, +the implementation should conform to the behaviors specified in this section. The result of reading a value in any of the situations below is that some unspecified value will be used for that field. @@ -4988,14 +4983,16 @@ H h3 = { ... }; // initialize h3 with a header that is valid, field f1 0, field # Compile-time size determination { #sec-minsizeinbits } The method calls `minSizeInBits`, `minSizeInBytes`, `maxSizeInBits`, -and `maxSizeInBytes` can be applied to some expressions. Each of -these method calls evaluate to local compile-time known values that return -the minimum size in bits required to store the expression (thus the -result type of these methods has type `int`). None of these methods -evaluates the expression itself. In consequence, the expression may -be invalid (e.g., an out-of-bounds header stack access). - -The method `minSizeInBytes` always returns the result of +and `maxSizeInBytes` can be applied to certain expressions. These +method calls return the minimum size in bits required to store the +expression (thus the result type of these methods has type +`int`). Except in certain situations involving type variables, +discussed below, these method calls produce local compile-time known values; +otherwise they produce compile-time known values. None of these methods +evaluates the expression that is the receiver of the method call, so +it may be invalid (e.g., an out-of-bounds header stack access). + +The method `minSizeInBytes` returns the result of `minSizeInBits` rounded up to the next whole number of bytes. In other words, for any expression `e`, `e.minSizeInBytes()` is equal to `(e.minSizeInBits() + 7) >> 3`. @@ -5300,10 +5297,10 @@ The empty statement, written `;` is a no-op. ## Block statement { #sec-block-stmt } -A block statement is denoted by curly braces. It contains a -sequence of statements and declarations, which are executed -sequentially. The variables and constants within a -block statement are only visible within the block. +A block statement is denoted by curly braces. It contains a sequence +of statements and declarations, which are executed sequentially. The +declarations (e.g., variables and constants) within a block statement +are only visible within the block. ~ Begin P4Grammar [INCLUDE=grammar.mdk:blockStatement] @@ -5760,9 +5757,9 @@ This constitutes an important difference between `select` expressions and the `switch` statements found in many programming languages since the keysets of a `select` expression may "overlap". -The typical way to use a `select` expression is to compare -the value of a recently-extracted header field against a set of -constant values, as in the following example: +The typical way to use a `select` expression is to compare the value +of a recently-extracted header field against a set of values, as in +the following example: ~ Begin P4Example header IPv4_h { bit<8> protocol; /* more fields omitted */ } @@ -6473,18 +6470,19 @@ In addition, tables may contain architecture-specific properties (see Section [#sec-additional-table-properties]). A property marked as `const` cannot be changed dynamically by the -control plane. The `key`, `actions`, and `size` properties are always -constant, so the `const` keyword is not needed for these. +control plane. The `key`, `actions`, and `size` properties cannot be +modified so the `const` keyword is not needed for these. ### Table properties { #sec-table-props } #### Keys { #sec-table-keys } -The `key` is a table property which specifies the data-plane -values that should be used to look up an entry. A key is a list of -pairs of the form `(e : m)`, where `e` is an expression that describes -the data to be matched in the table, and `m` is a `match_kind` -constant that describes the algorithm used to perform the lookup (see Section [#sec-match-kind-type]). +The `key` is a table property which specifies the data-plane values +that should be used to look up an entry. A key is a list of pairs of +the form `(e : m)`, where `e` is an expression that describes the data +to be matched in the table, and `m` is a `match_kind` that describes +the algorithm used to perform the lookup (see Section +[#sec-match-kind-type]). ~ Begin P4Grammar [INCLUDE=grammar.mdk:keyElementList] @@ -6505,7 +6503,7 @@ table Fwd { ~ End P4Example Here the key comprises two fields from the `ipv4header` -header: `dstAddress` and `version`. The `match_kind` constants +header: `dstAddress` and `version`. The `match_kind` elements serve three purposes: - They specify the algorithm used to match data-plane values against @@ -7041,10 +7039,10 @@ because of the `@noWarn("duplicate_priorities")` annotation. #### Size {#sec-size-table-property} The `size` is an optional property of a table. When present, its -value must always be an integer compile-time known value. It is specified -in units of number of table entries. +value must always be an compile-time known value that is an integer. The `size` property +is specified in units of number of table entries. -If a table has a `size` value specified for it with value `N`, it is +If a table is specified with a `size` property of value `N`, it is recommended that a compiler should choose a data plane implementation that is capable of storing `N` table entries. This does not guarantee that an _arbitrary_ set of `N` entries can always be inserted in such @@ -7731,25 +7729,27 @@ The following are local compile-time known values: - The `size` field of a value with type header stack. - The `_` identifier when used as a `select` expression label - The expression `{#}` representing an invalid header or header union value. +- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. - Identifiers that represent declared types, actions, tables, parsers, controls, or packages. - Tuple expression where all components are local compile-time known values. - Structure-valued expressions, where all fields are local compile-time known values. - Expressions evaluating to a list type, where all elements are local compile-time known values. -- Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- A legal cast applied to a local compile-time known value. -- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>>`, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all local compile-time known values. -- Identifiers declared as constants using the `const` keyword. -- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` +- Legal casts applied to local compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>>`, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all local compile-time known values. +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` where the type of `e` is not generic. The following are compile-time known values: - All local compile-time known values. - Constructor parameters. +- Identifiers declared as constants using the `const` keyword. - Tuple expression where all components are compile-time known values. - Expressions evaluating to a list type, where all elements are compile-time known values. - Structure-valued expressions, where all fields are compile-time known values. -- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`, and casts) when their operands are all compile-time known values. -- Constructor parameters. +- Expressions evaluating to a list type, where all elements are compile-time known values. +- Legal casts applied to compile-time known values. +- The following expressions (`+`, `-`, `|+|`, `|-|`, `*`, `/ `, `%`, `cast`, `!`, `&`, `|`, `^`, `&&`, `||`, `<< `, `>> `, `~`, `/`, `>`, `<`, `==`, `!=`, `<=`, `>=`, `++`, `[:]`, `?:`) when their operands are all compile-time known values. +- Expressions of the form `e.minSizeInBits()`, `e.minSizeInBytes()`, `e.maxSizeInBits()` and `e.maxSizeInBytes()` where the the type of `e` is generic. Intuitively, the main difference between _compile-time known values_ and _local compile-time known values_ is that the former also contains @@ -8302,9 +8302,8 @@ a key and a value `expression`. Note the syntax for `expression` is rich, see Appendix [#sec-grammar] for details. All `expression`s within a `structuredAnnotationBody` must be compile-time known -values, either literals or expressions requiring compile-time evaluation, with a -result type that is one of: string literals, arbitrary-precision integer, or -boolean. Structured `expression`s (e.g. an `expression` containing an +values with a result type that is either: `string`, `int`, or `bool`. +In particular, structured `expression`s (e.g. an `expression` containing an `expressionList`, a `kvList`, etc.) are not allowed. Note that P4Runtime information (P4Info) may stipulate additional restrictions. For example, an integer expression might be limited to 64-bit values. From b518e6cca84752de54510188fbc1fec4ffc60ddf Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Tue, 9 Jan 2024 09:07:37 +0100 Subject: [PATCH 10/20] Address nits from Jonathan's review --- p4-16/spec/P4-16-spec.mdk | 72 +++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 77db824021..688464d53c 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1511,7 +1511,7 @@ Each parameter may be labeled with a direction: - For anything other than an action, e.g. a control, parser, or function, a directionless parameter means that the value supplied as an argument in a call must be a compile-time known value - (see Section [#sec-ct-constants]). + (see Section [#sec-compile-time-known]). - For an action, a directionless parameter indicates that it is "action data". See Section [#sec-actions] for the meaning of action data, but its meaning includes the following possibilities: @@ -1694,8 +1694,9 @@ directions: including values for the directionless parameters. The directionless parameters in this case behave like `in` parameters. See Section [#sec-invoke-actions] for further details. -- Default parameter values are only allowed for 'in' or direction-less - parameters; these values must evaluate to compile-time known values. +- Default expressions are only allowed for 'in' or direction-less + parameters, and the expressions supplied as defaults must be + compile-time known values. - If parameters with default values do not appear at the end of the list of parameters, invocations that use the default values must use named arguments, as in the following @@ -1826,7 +1827,8 @@ P4 supports the following built-in base types: restricted circumstances. - The `error` type, which is used to convey errors in a target-independent, compiler-managed way. -- The `string` type, which can be used only with compile-time known values. +- The `string` type, which can be used with compile-time known + values of type string. - The `match_kind` type, which is used for describing the implementation of table lookups, - `bool`, which represents Boolean values @@ -1915,19 +1917,18 @@ The type `string` represents strings. There are no operations on string values; one cannot declare variables with a `string` type. Parameters with type `string` can be only directionless (see Section [#sec-calling-convention]). P4 does not support string manipulation -in the dataplane; the `string` type is only allowed for denoting -compile-time known values. These may be useful, for -example, a specific target architecture may support an extern function -for logging with the following signature: +in the dataplane; the `string` type is only allowed for describing +compile-time known values (i.e., string literals, as discussed in +Section [#sec-string-literals]). Even so, the string type is useful, +for example, in giving the type signature for extern functions such as +the following: ~ Begin P4Example extern void log(string message); ~ End P4Example -The only strings that can appear in a P4 program are string -literals, described in Section [#sec-string-literals]. For example, -the following annotation indicates that a specific name should be used -for a table when generating the control-plane API: +As another example, the following annotation indicates that the specified +name should be used for a given table in the generated control-plane API: ~ Begin P4Example @name("acl") table t1 { /* body omitted */ } @@ -1987,7 +1988,7 @@ behavior should match this specification. An unsigned integer (which we also call a "bit-string") has an arbitrary width, expressed in bits. A bit-string of width `W` is declared as: `bit`. `W` must be an expression that evaluates to a -local compile-time known value (see Section [#sec-ct-constants]) that is a +local compile-time known value (see Section [#sec-compile-time-known]) that is a non-negative integer. When using an expression for the size, the expression must be parenthesized and a compile-time known value. Bitstrings with width 0 are @@ -4823,11 +4824,12 @@ range. That section also defines the run time behavior of the expressions `hs.next` and `hs.last`, and the behaviors specified there take precedence over anything in this section for those expressions. -All mentions of header stack elements in this section only apply -for expressions `hs[index]` where `index` is not a compile-time known value. -A P4 implementation is allowed not to support `hs[index]` where -`index` is not a compile-time known value, but if it does offer such support, -the implementation should conform to the behaviors specified in this section. +All mentions of header stack elements in this section only apply for +expressions `hs[index]` where `index` is not a compile-time known +value. A P4 implementation may elect not to support expressions of +the form `hs[index]` where `index` is not a compile-time known +value. However, it does support such expressions, the implementation +should conform to the behaviors specified in this section. The result of reading a value in any of the situations below is that some unspecified value will be used for that field. @@ -4984,13 +4986,14 @@ H h3 = { ... }; // initialize h3 with a header that is valid, field f1 0, field The method calls `minSizeInBits`, `minSizeInBytes`, `maxSizeInBits`, and `maxSizeInBytes` can be applied to certain expressions. These -method calls return the minimum size in bits required to store the -expression (thus the result type of these methods has type -`int`). Except in certain situations involving type variables, -discussed below, these method calls produce local compile-time known values; -otherwise they produce compile-time known values. None of these methods -evaluates the expression that is the receiver of the method call, so -it may be invalid (e.g., an out-of-bounds header stack access). +method calls return the minimum (or maximum) size in bits required to +store the expression. Thus, the result type of these methods has type +`int`. Except in certain situations involving type variables, +discussed below, these method calls produce local compile-time known +values; otherwise they produce compile-time known values. None of +these methods evaluate the expression that is the receiver of the +method call, so it may be invalid (e.g., an out-of-bounds header stack +access). The method `minSizeInBytes` returns the result of `minSizeInBits` rounded up to the next whole number of bytes. @@ -5157,7 +5160,7 @@ instantiation ~ End P4Grammar An instantiation is written as a constructor invocation followed by a name. -Instantiations are always executed at compilation time (Section [#sec-ct-constants]). +Instantiations are always executed at compilation time (Section [#sec-compile-time-known]). The effect is to allocate an object with the specified name, and to bind it to the result of the constructor invocation. Note that instantiation arguments can be specified by name. @@ -7039,7 +7042,7 @@ because of the `@noWarn("duplicate_priorities")` annotation. #### Size {#sec-size-table-property} The `size` is an optional property of a table. When present, its -value must always be an compile-time known value that is an integer. The `size` property +value must always be a compile-time known value that is an integer. The `size` property is specified in units of number of table entries. If a table is specified with a `size` property of value `N`, it is @@ -7713,7 +7716,7 @@ The evaluation of a P4 program is done in two stages: executed to completion, in isolation, when it receives control from the architecture -## Compile-time known and local compile-time known values { #sec-ct-constants } +## Compile-time known and local compile-time known values { #sec-compile-time-known } Certain expressions in a P4 program have the property that their value can be determined at compile time. Moreover, for some of these @@ -7755,9 +7758,12 @@ Intuitively, the main difference between _compile-time known values_ and _local compile-time known values_ is that the former also contains constructor parameters. The distinction is important when it comes to defining the meaning of features like types. For example, in the type -`bit`, the expression `e` must be a local compile-time known value -as the value of a constructor parameter, is only determined when the -constructor is applied to a value. +`bit`, the expression `e` must be a local compile-time known +value. Suppose instead that `e` were a constructor parameter---i.e., +merely a compile-time known value. In this situation, it would be +impossible to resolve `bit` to a concrete type using purely local +information---we would have to wait until the constructor was +instantiated and the value of `e` known. ## Compile-time Evaluation { #sec-ct-eval } @@ -8606,7 +8612,7 @@ The P4 compiler should provide: ## Summary of changes made in version 1.2.4 -* Introduced distinction between local compile-time known and compile-time known values (Section[#sec-ct-constants]). +* Introduced distinction between local compile-time known and compile-time known values (Section[#sec-compile-time-known]). * Added header stack expressions (Section [#sec-hs-init]). * Allow casts from a type to itself (Section [#sec-casts]). * Added an invalid header or header union expression `{#}` (Sections [#sec-ops-on-hdrs] and [#sec-expr-hu]). @@ -8655,7 +8661,7 @@ The P4 compiler should provide: * Added `match_kind` as a base type (Section [#sec-match-kind-type]). * Removed structure initializers as they are subsumed by structure-valued expressions (Section [#sec-structure-expressions]). * Specified operations on values typed as type variables (Section [#sec-type-variable-operations]). -* Clarified semantics of compile-time known values (Section [#sec-ct-constants]). +* Clarified semantics of compile-time known values (Section [#sec-compile-time-known]). * Clarified semantics of directionless parameters (Section [#sec-calling-convention]). * Clarified semantics of arbitrary precision integers (Section [#sec-arbitrary-precision-integers]). * Clarified semantics of bit slices, shifts, and concatenation (Section [#sec-bit-ops]). From 83313bf8db0479de7853e7c6c2e68679e7bb77f2 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:18:31 +0200 Subject: [PATCH 11/20] Update p4-16/spec/P4-16-spec.mdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vladimír Štill --- p4-16/spec/P4-16-spec.mdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 688464d53c..2ff7f9d17d 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1990,7 +1990,7 @@ arbitrary width, expressed in bits. A bit-string of width `W` is declared as: `bit`. `W` must be an expression that evaluates to a local compile-time known value (see Section [#sec-compile-time-known]) that is a non-negative integer. When using an expression for -the size, the expression must be parenthesized and a compile-time known value. +the size, the expression must be parenthesized. Bitstrings with width 0 are allowed; they have no actual bits, and can only have the value 0. See [#sec-uninitialized-values-and-writing-invalid-headers] for additional details. From 93f85f77971e449f30968111a7f7193a070d4ade Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:18:50 +0200 Subject: [PATCH 12/20] Update p4-16/spec/P4-16-spec.mdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vladimír Štill --- p4-16/spec/P4-16-spec.mdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 2ff7f9d17d..c40872a039 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -2395,7 +2395,7 @@ defined as: where `typeName` is the name of a header or header union type. For a header stack `hs[n]`, the term `n` is the maximum defined size, and -must be a compile-time known value that is a a positive +must be a local compile-time known value that is a positive integer. Nested header stacks are not supported. At runtime a stack contains `n` values with type `typeName`, only some of which may be valid. Expressions on header stacks are discussed in Section From ce69975f4ee7f79bcac5e25552f4918720013b5d Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:19:13 +0200 Subject: [PATCH 13/20] Update p4-16/spec/P4-16-spec.mdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vladimír Štill --- p4-16/spec/P4-16-spec.mdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index c40872a039..83679b4156 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -4986,7 +4986,7 @@ H h3 = { ... }; // initialize h3 with a header that is valid, field f1 0, field The method calls `minSizeInBits`, `minSizeInBytes`, `maxSizeInBits`, and `maxSizeInBytes` can be applied to certain expressions. These -method calls return the minimum (or maximum) size in bits required to +method calls return the minimum (or maximum) size in bits (or bytes) required to store the expression. Thus, the result type of these methods has type `int`. Except in certain situations involving type variables, discussed below, these method calls produce local compile-time known From 27cb1ab6c66b579ba64f2477e1ce91c6598a27c4 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:24:38 +0200 Subject: [PATCH 14/20] Remove spurious file from PR --- .../experimenting-with-p4c-bison-grammar-changes.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md index c315a6b7b2..69046a0d60 100644 --- a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md +++ b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md @@ -1,15 +1,5 @@ # Introduction -This article describes how to run `bison` on the P4_16 language -grammar, as implemented in the open source `p4c` compiler available -at: - -+ https://github.com/p4lang/p4c - -These steps can be useful when trying out variations of the `bison` -grammar, e.g. when working on making extensions to the P4_16 language -syntax for enhancements. - Prerequisites: You have a Linux system with the necessary software installed required to build `p4c` from source code. From 63260e8f00f14a4f8e893ef9e8dc1185aa8be204 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:26:33 +0200 Subject: [PATCH 15/20] Remove spurious file from PR --- ...imenting-with-p4c-bison-grammar-changes.md | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md diff --git a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md deleted file mode 100644 index 69046a0d60..0000000000 --- a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md +++ /dev/null @@ -1,44 +0,0 @@ -# Introduction - -Prerequisites: You have a Linux system with the necessary software -installed required to build `p4c` from source code. - -The following sequence of commands can be used to find the particular -`bison` command line arguments that are used while building `p4c`: - -```bash -git clone https://github.com/p4lang/p4c -cd p4c -mkdir build -cd build -cmake .. -find . \! -type d | xargs grep bison -``` - -As of 2023-Aug-20, this is the latest version of `p4c`: - -```bash -$ git log -n 1 | cat -commit 40ebd7eed1ff7e744c8f34fd06ce9ce061d8d88f -Author: Hoooao <93057312+Hoooao@users.noreply.github.com> -Date: Sat Aug 19 16:05:04 2023 -0400 -``` - -And below is the command that is used to run `bison` on the P4_16 -grammar used by `p4c`. - -Note: Assign a value to the shell variable `P4C` that is the directory -where your copy of the `p4c` git repo is located on your system. - -```bash -P4C=$HOME/forks/p4c -mkdir my-temp-dir -cd my-temp-dir -bison \ - -Werror=conflicts-sr \ - -Werror=conflicts-rr \ - -d \ - --verbose \ - -o p4parser.cpp \ - ${P4C}/frontends/parsers/p4/p4parser.ypp -``` From f3dc4b9587fd3a771d860a314ff838f4b5a105b5 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 13 May 2024 15:41:39 +0200 Subject: [PATCH 16/20] Fix comments from @vlstill's review --- p4-16/spec/P4-16-spec.mdk | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index 83679b4156..cf6639a8cd 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -2022,11 +2022,13 @@ described in Section [#sec-bit-ops]. #### Signed Integers {#sec-signed-integers} -Signed integers are represented using two's complement. An integer with `W` -bits is declared as: `int`. `W` must be an expression that evaluates to -a local compile-time known value that is a positive integer. -Note that `int` type refers to both cases of `int` -and `int<(expression)>` where the width is a local compile-time known value. +Signed integers are represented using two's complement. An integer +with `W` bits is declared as: `int`. `W` must be an expression that +evaluates to a local compile-time known (see Section +[#sec-compile-time-known]) value that is a non-negative integer. Note +that `int` type refers to both cases of `int` and +`int<(expression)>` where the width is a local compile-time known +value. Bits within an integer are numbered from `0` to `W-1`. Bit `0` is the least significant, and bit `W-1` is the sign bit. @@ -2053,13 +2055,14 @@ values, P4 provides a special bit-string type whose size is set at runtime, called a `varbit`. The type `varbit` denotes a bit-string with a width of at most `W` -bits, where `W` must be a non-negative integer that is a local compile-time -known value. For example, the type `varbit<120>` denotes the type -of bit-string values that may have between 0 and 120 bits. Most -operations that are applicable to fixed-size bit-strings (unsigned -numbers) *cannot* be performed on dynamically sized bit-strings. -Note that `varbit` type refers to both cases of `varbit` -and `varbit<(expression)>` where the width is a compile-time known value. +bits, where `W` is a local compile-time known value (see Section +[#sec-compile-time-known]) that is a non-negative integer. For +example, the type `varbit<120>` denotes the type of bit-string values +that may have between 0 and 120 bits. Most operations that are +applicable to fixed-size bit-strings (unsigned numbers) *cannot* be +performed on dynamically sized bit-strings. Note that `varbit` +type refers to both cases of `varbit` and `varbit<(expression)>` +where the width is a compile-time known value. P4 architectures may impose additional constraints on varbit types: for example, they may limit the maximum size, or they may require `varbit` @@ -4639,7 +4642,7 @@ parser Tcp_option_parser(packet_in b, out Tcp_option_stack vec) { } ~End P4Example -Similar to headers, the size of a header union is a compile-time known +Similar to headers, the size of a header union is a local compile-time known value (Section [#sec-minsizeinbits]). The expression `{#}` represents an invalid header union of some type, @@ -5053,7 +5056,9 @@ These methods are defined for: * for a type that does contain `varbit` fields, `maxSizeInBits` is the worst-case size of the serialized representation of the data and `minSizeInBits` is the "best" case. -Every other case is undefined and will produce a compile-time error. +Every other case is undefined and will produce a compile-time +error. In particular, cases involving type variables produce a +compile-time error. # Function declarations { #sec-functions } @@ -7733,7 +7738,7 @@ The following are local compile-time known values: - The `_` identifier when used as a `select` expression label - The expression `{#}` representing an invalid header or header union value. - Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations. -- Identifiers that represent declared types, actions, tables, parsers, controls, or packages. +- Identifiers that represent declared types, actions, functions, tables, parsers, controls, or packages. - Tuple expression where all components are local compile-time known values. - Structure-valued expressions, where all fields are local compile-time known values. - Expressions evaluating to a list type, where all elements are local compile-time known values. @@ -7744,7 +7749,7 @@ The following are local compile-time known values: The following are compile-time known values: - All local compile-time known values. -- Constructor parameters. +- Constructor parameters (i.e., the declared parameters for a `parser`, `control`, etc.) - Identifiers declared as constants using the `const` keyword. - Tuple expression where all components are compile-time known values. - Expressions evaluating to a list type, where all elements are compile-time known values. From 8fb033862aaa84dfb588a80edc6da5c51c8ea110 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 3 Jun 2024 23:51:32 +0200 Subject: [PATCH 17/20] Clarify note about calling convention For greater clarity, this commit moves the comment about the calling convention for directionless parameters of extern object type out of an itemized list. Instead, it is a standalone sentence below the list. --- p4-16/spec/P4-16-spec.mdk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p4-16/spec/P4-16-spec.mdk b/p4-16/spec/P4-16-spec.mdk index c9c3d28982..aa13a76dff 100644 --- a/p4-16/spec/P4-16-spec.mdk +++ b/p4-16/spec/P4-16-spec.mdk @@ -1506,8 +1506,7 @@ Each parameter may be labeled with a direction: - For anything other than an action, e.g. a control, parser, or function, a directionless parameter means that the value supplied as an argument in a call must be a compile-time known value - (see Section [#sec-compile-time-known]). Note that a directionless - parameter of extern object type is passed by reference. + (see Section [#sec-compile-time-known]). - For an action, a directionless parameter indicates that it is "action data". See Section [#sec-actions] for the meaning of action data, but its meaning includes the following possibilities: @@ -1518,6 +1517,8 @@ Each parameter may be labeled with a direction: when an entry is added to a table that uses that action. See Section [#sec-actions]. +A directionless parameter of extern object type is passed by reference. + Direction `out` parameters are always initialized at the beginning of execution of the portion of the program that has the `out` parameters, e.g. `control`, `parser`, `action`, function, etc. This From d5f5d02203a7d8836fbee2e9bd36987c42103093 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 3 Jun 2024 23:55:09 +0200 Subject: [PATCH 18/20] Restore spuriously deleted file --- ...imenting-with-p4c-bison-grammar-changes.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md diff --git a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md new file mode 100644 index 0000000000..69046a0d60 --- /dev/null +++ b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md @@ -0,0 +1,44 @@ +# Introduction + +Prerequisites: You have a Linux system with the necessary software +installed required to build `p4c` from source code. + +The following sequence of commands can be used to find the particular +`bison` command line arguments that are used while building `p4c`: + +```bash +git clone https://github.com/p4lang/p4c +cd p4c +mkdir build +cd build +cmake .. +find . \! -type d | xargs grep bison +``` + +As of 2023-Aug-20, this is the latest version of `p4c`: + +```bash +$ git log -n 1 | cat +commit 40ebd7eed1ff7e744c8f34fd06ce9ce061d8d88f +Author: Hoooao <93057312+Hoooao@users.noreply.github.com> +Date: Sat Aug 19 16:05:04 2023 -0400 +``` + +And below is the command that is used to run `bison` on the P4_16 +grammar used by `p4c`. + +Note: Assign a value to the shell variable `P4C` that is the directory +where your copy of the `p4c` git repo is located on your system. + +```bash +P4C=$HOME/forks/p4c +mkdir my-temp-dir +cd my-temp-dir +bison \ + -Werror=conflicts-sr \ + -Werror=conflicts-rr \ + -d \ + --verbose \ + -o p4parser.cpp \ + ${P4C}/frontends/parsers/p4/p4parser.ypp +``` From 037f6035ef12e95497d402fa863d1ac82b5355a6 Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 3 Jun 2024 23:56:37 +0200 Subject: [PATCH 19/20] Remove spurious edits to experimental bison notes --- .../experimenting-with-p4c-bison-grammar-changes.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md index 69046a0d60..d3ead182ad 100644 --- a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md +++ b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md @@ -1,5 +1,15 @@ # Introduction +This article describes how to run `bison` on the P4_16 language +grammar, as implemented in the open source `p4c` compiler available +at: + + https://github.com/p4lang/p4c + +These steps can be useful when trying out variations of the `bison` +grammar, e.g. when working on making extensions to the P4_16 language +syntax for enhancements. + Prerequisites: You have a Linux system with the necessary software installed required to build `p4c` from source code. From 630a54c8d57500fbb6b7a803e2df7d42cf17ee1a Mon Sep 17 00:00:00 2001 From: Nate Foster Date: Mon, 3 Jun 2024 23:57:11 +0200 Subject: [PATCH 20/20] Remove more spurious edits to experimental bison notes --- p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md index d3ead182ad..c315a6b7b2 100644 --- a/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md +++ b/p4-16/spec/docs/experimenting-with-p4c-bison-grammar-changes.md @@ -4,7 +4,7 @@ This article describes how to run `bison` on the P4_16 language grammar, as implemented in the open source `p4c` compiler available at: - https://github.com/p4lang/p4c ++ https://github.com/p4lang/p4c These steps can be useful when trying out variations of the `bison` grammar, e.g. when working on making extensions to the P4_16 language