コンテンツにスキップ

12. Statements

Every statement in P4 except block statements must end with a semicolon. Statements can appear in several places:

  • Within parser states
  • Within a control block
  • Within an action

There are restrictions for the kinds of statements that can appear in each of these places. For example, returns are not supported in parsers, and switch statements are only supported in control blocks. We present here the most general case, for control blocks.

\~ Begin P4Grammar statement : assignmentOrMethodCallStatement | conditionalStatement | emptyStatement | blockStatement | exitStatement | returnStatement | switchStatement ;

  • [INCLUDE=grammar.mdk:assignmentOrMethodCallStatement]
    End P4Grammar

In addition, parsers support a transition statement (Section [#sec-transition]).

An assignment, written with the = sign, first evaluates its left sub-expression to an l-value, then evaluates its right sub-expression to a value, and finally copies the value into the l-value. Derived types (e.g. structs) are copied recursively, and all components of headers are copied, including “validity” bits. Assignment is not defined for extern values.

  • The empty statement, written ; is a no-op.
    Begin P4Grammar [INCLUDE=grammar.mdk:emptyStatement]

    End P4Grammar

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]

[INCLUDE=grammar.mdk:statOrDeclList]

  • [INCLUDE=grammar.mdk:statementOrDeclaration]
    End P4Grammar

The return statement immediately terminates the execution of the action, function or control containing it. return statements are not allowed within parsers. return statements followed by an expression are only allowed within functions that return values; in this case the type of the expression must match the return type of the function. Any copy-out behavior due to direction out or inout parameters of the enclosing action, function, or control are still performed after the execution of the return statement. See Section [#sec-calling-convention] for details on copy-out behavior.

\~ Begin P4Grammar [INCLUDE=grammar.mdk:returnStatement] \~ End P4Grammar

The exit statement immediately terminates the execution of all the blocks currently executing: the current action (if invoked within an action), the current control, and all its callers. exit statements are not allowed within parsers or functions.

Any copy-out behavior due to direction out or inout parameters of the enclosing action or control, and all of its callers, are still performed after the execution of the exit statement. See Section [#sec-calling-convention] for details on copy-out behavior.

\~ Begin P4Grammar [INCLUDE=grammar.mdk:exitStatement] \~ End P4Grammar

There are some expressions whose evaluation might cause an exit statement to be executed. Examples include:

  • table.apply().action_run, which can only appear as the expression of a switch statement (see Section [#sec-switch-stmt]), and when it appears there, it must be the entire expression.
  • Any expression containing table.apply().hit or table.apply().miss (see Section [#sec-invoke-mau]), which can be part of arbitrarily complex expressions in many places of a P4 program, such as:
    • the expression in an if statement.
    • the expression e1 in a conditional expression e1 ? e2 : e3.
    • in an assignment statement, in the left and/or right hand sides.
    • an argument passed to a function or method call.
    • an expression to calculate a table key (see Section [#sec-mau-semantics]).

This list is not intended to be exhaustive.

If applying the table causes an action to be executed, which in turn causes an exit statement to be executed, then evaluation of the expression ends immediately, and the rest of the current expression or statement does not complete its execution. See Section [#sec-expr-eval-order] for the order of evaluation of the parts of an expression. For the examples listed above, it also means the following behavior after the expression evaluation is interrupted.

  • For a switch statement, if table.apply() exits, then none of the blocks in the switch statement are executed.
  • If table.apply().hit or table.apply().miss cause an exit during the evaluation of an expression:
    • If it is the expression of an if statement, then neither the ‘then’ nor ‘else’ branches of the if statement are executed.
    • If it is the expression e1 in a conditional expression e1 ? e2 : e3, then neither expression e2 nor e3 are evaluated.
    • If the expression is the right hand side of an assignment statement, or part of the calculation of the L-value on the left hand side (e.g. the index expression of a header stack reference), then no assignment occurs.
    • If the expression is an argument passed to a function or method call, then the function/method call does not occur.
    • If the expression is a table key, then the table is not applied.

The conditional statement uses standard syntax and semantics familiar from many programming languages.

However, the condition expression in P4 is required to be a Boolean (and not an integer).

\~ Begin P4Grammar [INCLUDE=grammar.mdk:conditionalStatement] \~ End P4Grammar

When several if statements are nested, the else applies to the innermost if statement that does not have an else statement.

  • The switch statement can only be used within control blocks.
    Begin P4Grammar [INCLUDE=grammar.mdk:switchStatement]

[INCLUDE=grammar.mdk:switchCases]

[INCLUDE=grammar.mdk:switchCase]

[INCLUDE=grammar.mdk:switchLabel]

  • [INCLUDE=grammar.mdk:nonBraceExpression]
    End P4Grammar

The nonBraceExpression is the same as expression as defined in Section [#sec-exprs], except it does not include any cases that can begin with a left brace { character, to avoid syntactic ambiguity with a block statement.

There are two kinds of switch expressions allowed, described separately in the following two subsections.

Switch statement with action_run expression

For this variant of switch statement, the expression must be of the form t.apply().action_run, where t is the name of a table (see Section [#sec-invoke-mau]). All switch labels must be names of actions of the table t, or default.

\~ Begin P4Example switch (t.apply().action_run) { action1: // fall-through to action2: action2: { /* body omitted / } action3: { / body omitted / } // no fall-through from action2 to action3 labels default: { / body omitted */ } } \~ End P4Example

Note that the default label of the switch statement is used to match on the kind of action executed, no matter whether there was a table hit or miss. The default label does not indicate that the table missed and the default_action was executed.

Switch statement with integer or enumerated type expression

For this variant of switch statement, the expression must evaluate to a result with one of these types:

  • bit<W>
  • int<W>
  • enum, either with or without an underlying representation specified
  • error

All switch labels must be expressions with compile-time known values, and must have a type that can be implicitly cast to the type of the switch expression (see Section [#sec-implicit-casts]). Switch labels must not begin with a left brace character {, to avoid ambiguity with a block statement.

\~ Begin P4Example // Assume the expression hdr.ethernet.etherType has type bit\<16> switch (hdr.ethernet.etherType) { 0x86dd: { /* body omitted / } 0x0800: // fall-through to the next body 0x0802: { / body omitted / } 0xcafe: { / body omitted / } default: { / body omitted */ } } \~ End P4Example

Notes common to all switch statements

It is a compile-time error if two labels of a switch statement equal each other. The switch label values need not include all possible values of the switch expression. It is optional to have a switch case with the default label, but if one is present, it must be the last one in the switch statement.

If a switch label is not followed by a block statement, it falls through to the next label. However, if a block statement is present, it does not fall through. Note that this is different from C-style switch statements, where a break is needed to prevent fall-through. If the last switch label is not followed by a block statement, the behavior is the same as if the last switch label were followed by an empty block statement { }.

When a switch statement is executed, first the switch expression is evaluated, and any side effects from evaluating this expression are visible to any switch case that is executed. Among switch labels that are not default, at most one of them can equal the value of the switch expression. If one is equal, that switch case is executed.

If no labels are equal to the switch expression, then:

  • if there is a default label, the case with the default label is executed.
  • if there is no default label, then no switch case is executed, and execution continues after the end of the switch statement, with no side effects (except any that were caused by evaluating the switch expression).

See “Implementing generalized P4_16 switch statements” GeneralizedSwitchStatements for possible techniques that one might use to implement generalized switch statements.