Movatterモバイル変換


[0]ホーム

URL:


P416 Language Specification
version 1.2.2
The P4 Language Consortium
2021-05-17

Abstract. P4 is a language for programming the data plane of networkdevices. This document provides a precise definition of the P416language, which is the 2016 revision of the P4 language(http://​p4.​org). The target audience for this document includesdevelopers who want to write compilers, simulators, IDEs, anddebuggers for P4 programs. This document may also be of interest to P4programmers who are interested in understanding the syntax andsemantics of the language at a deeper level.

1. Scope

This specification document defines the structure and interpretationof programs in the P416 language. It defines the syntax, semanticrules, and requirements for conformant implementations of thelanguage.

It does not define:

It is understood that some implementations may be unable to implementthe behavior defined here in all cases, or may provide options toeliminate some safety guarantees in exchange for better performance orhandling larger programs. They should document where they deviatefrom this specification.

2. Terms, definitions, and symbols

Throughout this document, the following terms will be used:

All terms defined explicitly in this document should not be understoodto refer implicitly to similar terms defined elsewhere. Conversely, any terms notdefined explicitly in this document should be interpreted according togenerally recognizable sourcese.g., IETF RFCs.

3. Overview

prgswitch


Figure 1. Traditional switches vs. programmable switches.

P4 is a language for expressing how packets are processed by the dataplane of a programmable forwarding element such as a hardware orsoftware switch, network interface card, router, or networkappliance. The name P4 comes from the original paper that introducedthe language,“Programming Protocol-independent Packet Processors,”https://​arxiv.​org/​pdf/​1312.​1719.​pdf. While P4 was initially designedfor programming switches, its scope has been broadened to cover alarge variety of devices. In the rest of this document we use thegeneric termtarget for all such devices.

Many targets implement both a control plane and a data plane. P4 isdesigned to specify only the data plane functionality of thetarget. P4 programs also partially define the interface by which thecontrol plane and the data-plane communicate, but P4 cannot be used todescribe the control-plane functionality of the target. In the rest ofthis document, when we talk about P4 as“programming a target”, wemean“programming the data plane of a target”.

As a concrete example of a target, Figure 1 illustratesthe difference between a traditional fixed-function switch and aP4-programmable switch. In a traditional switch the manufacturerdefines the data-plane functionality. The control-plane controls thedata plane by managing entries in tables (e.g. routing tables),configuring specialized objects (e.g. meters), and by processingcontrol-packets (e.g. routing protocol packets) or asynchronousevents, such as link state changes or learning notifications.

A P4-programmable switch differs from a traditional switch in twoessential ways:

Hence, P4 can be said to be protocol independent, but it enablesprogrammers to express a rich set of protocols and other data planebehaviors.

p4prg


Figure 2. Programming a target with P4.

The core abstractions provided by the P4 language are:

Figure 2 shows a typical tool workflow when programming atarget using P4.

Target manufacturers provide the hardware or software implementationframework, an architecture definition, and a P4 compiler for thattarget. P4 programmers write programs for a specific architecture,which defines a set of P4-programmable components on the target aswell as their external data plane interfaces.

Compiling a set of P4 programs produces two artifacts:

P4 is a domain-specific language that is designed to be implementableon a large variety of targets including programmable network interfacecards, FPGAs, software switches, and hardware ASICs. As such, thelanguage is restricted to constructs that can be efficientlyimplemented on all of these platforms.

Assuming a fixed cost for table lookup operations and interactionswith extern objects, all P4 programs (i.e., parsers and controls)execute a constant number of operations for each byte of an inputpacket received and analyzed. Although parsers may contain loops,provided some header is extracted on each cycle, the packet itselfprovides a bound on the total execution of the parser. In other words,under these assumptions, the computational complexity of a P4 programis linear in the total size of all headers, and never depends on thesize of the state accumulated while processing data (e.g., the numberof flows, or the total number of packets processed). These guaranteesare necessary (but not sufficient) for enabling fast packet processingacross a variety of targets.

P4 conformance of a target is defined as follows: if a specifictarget T supports only a subset of the P4 programming language, sayP4T, programs written in P4T executed on the target should providethe exact same behavior as is described in this document. Note that P4conformant targets can provide arbitrary P4 language extensions andexternelements.

3.1. Benefits of P4

Compared to state-of-the-art packet-processing systems (e.g., based onwriting microcode on top of custom hardware), P4 provides a number ofsignificant advantages:

3.2. P4 language evolution: comparison to previous versions (P4 v1.0/v1.1)

p4transition


Figure 3. Evolution of the language between versions P414 (versions 1.0 and 1.1) and P416.

Compared to P414, the earlier version of the language, P416 makesa number of significant, backwards-incompatible changes to the syntaxand semantics of the language. The evolution from the previous version(P414) to the current one (P416) is depicted in Figure3. In particular, a large number of languagefeatures have been eliminated from the language and moved intolibraries including counters, checksum units, meters, etc.

Hence, the language has been transformed from a complex language (more than 70keywords) into a relatively small core language (less than 40 keywords, shown inSection B) accompanied by a library of fundamentalconstructs that are needed for writing most P4.

The v1.1 version of P4 introduced a language construct calledextern thatcan be used to describe library elements. Many constructs defined in thev1.1 language specification will thus be transformed into suchlibrary elements (including constructs that have been eliminatedfrom the language, such as counters and meters). Some of theseextern objectsare expected to be standardized, and they will be in the scope of afuture document describing a standard library of P4 elements. Inthis document we provide several examples ofextern constructs.P416 also introduces and repurposes some v1.1 languageconstructs for describing the programmable parts of anarchitecture. These language constructs are:parser,state,control, andpackage.

One important goal of the P416 language revision is to provide astable language definition. In other words, we strive to ensure thatall programs written in P416 will remain syntactically correct andbehave identically when treated as programs for future versions of thelanguage. Moreover, if some future version of the language requiresbreaking backwards compatibility, we will seek to provide an easy pathfor migrating P416 programs to the new version.

4. Architecture Model

p4interface


Figure 4. P4 program interfaces.

TheP4 architecture identifies the P4-programmable blocks (e.g.,parser, ingress control flow, egress control flow, deparser, etc.) and theirdata plane interfaces.

The P4 architecture can be thought of as a contract between theprogram and the target.Each manufacturer must thereforeprovide both a P4 compiler as well as an accompanyingarchitecture definition for their target. (We expect that P4 compilerscan share a common front-end that handles all architectures). The architecturedefinition does not have to expose the entire programmable surface ofthe data planea manufacturer may even choose to provide multipledefinitions for the same hardware device, each with differentcapabilities (e.g., with or without multicast support).

Figure 4 illustrates the data plane interfaces between P4-programmableblocks. It shows a target that has two programmable blocks (#1 and#2).Each block is programmed through a separate fragment of P4 code. Thetarget interfaces with the P4 program through a set of controlregisters or signals. Input controls provide information to P4programs (e.g., the input port that a packet was received from), whileoutput controls can be written to by P4 programs to influence thetarget behavior (e.g., the output port where a packet has to bedirected). Control registers/signals are represented in P4 asintrinsic metadata.P4 programs can also store and manipulate data pertaining to eachpacket asuser-defined metadata.

The behavior of a P4 program can be fully described in terms oftransformations that map vectors of bits to vectors of bits. To actuallyprocess a packet, the architecturemodel interprets the bits that the P4 program writes to intrinsic metadata.For example, to cause a packet tobe forwarded on a specific output port, a P4 program may need to writethe index of an output port into a dedicated control register. Similarly,to cause a packet to be dropped, a P4 program may need to set a“drop” bit into another dedicated control register. Note that the details ofhow intrinsic metadata are interpreted is architecture-specific.

p4checksum


Figure 5. P4 program invoking the services of a fixed-function object.

P4 programs can invoke services implemented by extern objects and functions provided by the architecture.Figure 5 depicts a P4 program invoking the services of abuilt-in checksum computation unit on a target. The implementation of the checksumunit is not specified in P4, but its interface is. In general, the interface foran extern object describes each operation it provides, as well as their parameter and return types.

In general, P4 programs are not expected to be portable acrossdifferent architectures. For example, executing a P4 program thatbroadcasts packets by writing into a custom control registerwill not function correctly on a target that does not have the controlregister. However, P4 programs written for a given architecture shouldbe portable across all targets that faithfully implement thecorresponding model, provided there are sufficient resources.

4.1. Standard architectures

We expect that the P4 community will evolve a small set of standard architecturemodels pertaining to specific verticals. Wide adoption of such standardarchitectures will promote portability of P4 programs across different targets.However, defining thesestandard architectures is outside of the scope of this document.

4.2. Data plane interfaces

To describe a functional block that can be programmed in P4, thearchitecture includes a type declaration that specifies the interfacesbetween the block and the other components in the architecture.For example, the architecture might contain a declaration such as the following:

control MatchActionPipe<H>(inbit<4> inputPort,inout H parsedHeaders,outbit<4> outputPort);

This type declaration describes a block namedMatchActionPipethat can be programmed using a data-dependent sequence of match-actionunit invocations and other imperative constructs (indicated by thecontrolkeyword). The interface between theMatchActionPipe block andthe other components of the architecture can be read off from this declaration:

4.3. Extern objects and functions

P4 programs can also interact with objects and functions provided by the architecture.Such objects are described using theextern construct, whichdescribes the interfaces that such objects expose to the data-plane.

Anextern object describes a set of methods that are implementedby an object, but not the implementation of these methods (i.e., it is similarto an abstract class in an object-oriented language). For example,the following construct could be used to describe the operations offered by anincremental checksum unit:

extern Checksum16 {    Checksum16();// constructorvoid clear();// prepare unit for computationvoid update<T>(in T data);// add data to checksumvoid remove<T>(in T data);// remove data from existing checksumbit<16> get();// get the checksum for the data added since last clear}

5. Example: A very simple switch

As an example to illustrate the features of architectures, considerimplementing a very simple switch in P4. We will first describe thearchitecture of the switch and then write a complete P4 programthat specifies the data plane behavior of the switch. This exampledemonstrates many important features of the P4 programming language.

vssarch


Figure 6. The Very Simple Switch (VSS) architecture.

We call our architecture the“Very Simple Switch” (VSS). Figure6 is a diagram of this architecture. There is nothing inherentlyspecial about VSSit is just a pedagogical example thatillustrates how programmable switches can be described and programmedin P4. VSS has a number of fixed-function blocks (shown in cyan in ourexample), whose behavior is described in Section 5.2. Thewhite blocks are programmable using P4.

VSS receives packets through one of 8 input Ethernet ports, through arecirculation channel, or from a port connected directly to theCPU. VSS has one single parser, feeding into a single match-actionpipeline, which feeds into a single deparser. After exiting thedeparser, packets are emitted through one of 8 output Ethernet portsor one of 3“special” ports:

The white blocks in the figure are programmable, and the user mustprovide a corresponding P4 program to specify the behavior of eachsuch block. The red arrows indicate the flow of user-defined data. Thecyan blocks are fixed-function components. The green arrows are data planeinterfaces used to convey information between the fixed-functionblocks and the programmable blocksexposed in the P4 program asintrinsic metadata.

5.1. Very Simple Switch Architecture

The following P4 program provides a declaration of VSS in P4, as itwould be provided by the VSS manufacturer. The declaration containsseveral type declarations, constants, and finally declarations for thethree programmable blocks; the code uses syntax highlighting. Theprogrammable blocks are described by their types; the implementationof these blocks has to be provided by the switch programmer.

// File "very_simple_switch_model.p4"// Very Simple Switch P4 declaration// core library needed for packet_in and packet_out definitions# include <core.p4>/* Various constants and structure declarations*//* ports are represented using 4-bit values*/typedefbit<4> PortId;/* only 8 ports are "real"*/const PortId REAL_PORT_COUNT =4w8;// 4w8 is the number 8 in 4 bits/* metadata accompanying an input packet*/struct InControl {    PortId inputPort;}/* special input port values*/const PortId RECIRCULATE_IN_PORT =0xD;const PortId CPU_IN_PORT =0xE;/* metadata that must be computed for outgoing packets*/struct OutControl {    PortId outputPort;}/* special output port values for outgoing packet*/const PortId DROP_PORT =0xF;const PortId CPU_OUT_PORT =0xE;const PortId RECIRCULATE_OUT_PORT =0xD;/* Prototypes for all programmable blocks*//*** Programmable parser.* @param <H> type of headers; defined by user* @param b input packet* @param parsedHeaders headers constructed by parser*/parser Parser<H>(packet_in b,out H parsedHeaders);/*** Match-action pipeline* @param <H> type of input and output headers* @param headers headers received from the parser and sent to the deparser* @param parseError error that may have surfaced during parsing* @param inCtrl information from architecture, accompanying input packet* @param outCtrl information for architecture, accompanying output packet*/control Pipe<H>(inout H headers,inerror parseError,// parser errorin InControl inCtrl,// input portout OutControl outCtrl);// output port/*** VSS deparser.* @param <H> type of headers; defined by user* @param b output packet* @param outputHeaders headers for output packet*/control Deparser<H>(inout H outputHeaders,                    packet_out b);/*** Top-level package declaration - must be instantiated by user.* The arguments to the package indicate blocks that* must be instantiated by the user.* @param <H> user-defined type of the headers processed.*/package VSS<H>(Parser<H> p,               Pipe<H> map,               Deparser<H> d);// Architecture-specific objects that can be instantiated// Checksum unitextern Checksum16 {    Checksum16();// constructorvoid clear();// prepare unit for computationvoid update<T>(in T data);// add data to checksumvoid remove<T>(in T data);// remove data from existing checksumbit<16> get();// get the checksum for the data added since last clear}

Let us describe some of these elements:

The pipeline receives three inputs: the headersheaders, a parsererrorparseError, and theinCtrl control data. Figure6 indicates the different sources of these pieces ofinformation. The pipeline writes its outputs intooutCtrl, andit must update in place the headers to be consumed by the deparser.

A type variable indicates a type yet unknown that must be provided bythe user at a later time. In this caseH is the type of the setof headers that the user program will be processing; the parser willproduce the parsed representation of these headers, and thematch-action pipeline will update the input headers in place toproduce the output headers.

5.2. Very Simple Switch Architecture Description

In order to fully understand VSS's behavior and write meaningful P4programs for it, and for implementing a control plane, we also need afull behavioral description of the fixed-function blocks. This sectioncan be seen as a simple example illustrating all the details that haveto be handled when writing an architecture description. The P4language is not intended to cover the description of all suchfunctional blocksthe language can only describe the interfacesbetween programmable blocks and the architecture. For the current program,this interface is given by theParser,Pipe, andDeparserdeclarations. In practice we expect that the complete description of the architecturewill be provided as an executable program and/or diagrams and text; inthis document we will provide informal descriptions in English.

5.2.1. Arbiter block

The input arbiter block performs the following functions:

5.2.2. Parser runtime block

The parser runtime block works in concert with the parser. It providesan error code to the match-action pipeline, based on the parseractions, and it provides information about the packet payload (e.g.,the size of the remaining payload data) to the demux block. As soon asa packet's processing is completed by the parser, the match-actionpipeline is invoked with the associated metadata as inputs (packetheaders and user-defined metadata).

5.2.3. Demux block

The core functionality of the demux block is to receive the headersfor the outgoing packet from the deparser and the packet payload fromthe parser, to assemble them into a new packet and to send the resultto the correct output port. The output port is specified by the valueofoutCtrl.ouputPort, which is set by the match-action pipeline.

Please note that some of the behaviors of the demux block may beunexpectedwe have highlighted them in bold. We are not specifyinghere several important behaviors related to queue size, arbitration,and timing, which also influence the packet processing.

The arrow shown from the parser runtime to the demux block representsan additional information flow from the parser to the demux: thepacket being processed as well as the offset within the packet whereparsing ended (i.e., the start of the packet payload).

5.2.4. Available extern blocks

The VSS architecture provides an incremental checksum extern block,calledChecksum16. The checksum unit has a constructor and fourmethods:

5.3. A complete Very Simple Switch program

Here we provide a complete P4 program that implements basic forwarding forIPv4 packets on the VSS architecture. This program does not utilize all of thefeatures provided by the architecturee.g., recirculationbut it does usepreprocessor#include directives (see Section 6.2).

vssmau


Figure 7. Diagram of the match-action pipeline expressed by the VSS P4 program.

The parser attempts to recognize an Ethernet header followed by an IPv4 header.If either of these headers are missing, parsing terminates with anerror. Otherwise it extracts the information from these headers intoaParsed_packet structure. The match-action pipeline isshown in Figure 7; it comprises four match-action units(represented by the P4table keyword):

The deparser constructs the outgoing packet by reassembling the Ethernet andIPv4 headers as computed by the pipeline.

// Include P4 core library# include <core.p4>// Include very simple switch architecture declarations# include"very_simple_switch_model.p4"// This program processes packets comprising an Ethernet and an IPv4// header, and it forwards packets using the destination IP addresstypedefbit<48>  EthernetAddress;typedefbit<32>  IPv4Address;// Standard Ethernet headerheader Ethernet_h {    EthernetAddress dstAddr;    EthernetAddress srcAddr;bit<16>         etherType;}// IPv4 header (without options)header IPv4_h {bit<4>       version;bit<4>       ihl;bit<8>       diffserv;bit<16>      totalLen;bit<16>      identification;bit<3>       flags;bit<13>      fragOffset;bit<8>       ttl;bit<8>       protocol;bit<16>      hdrChecksum;    IPv4Address  srcAddr;    IPv4Address  dstAddr;}// Structure of parsed headersstruct Parsed_packet {    Ethernet_h ethernet;    IPv4_h     ip;}// Parser section// User-defined errors that may be signaled during parsingerror {    IPv4OptionsNotSupported,    IPv4IncorrectVersion,    IPv4ChecksumError}parser TopParser(packet_in b,out Parsed_packet p) {    Checksum16() ck;// instantiate checksum unitstate start {        b.extract(p.ethernet);transitionselect(p.ethernet.etherType) {0x0800: parse_ipv4;// no default rule: all other packets rejected        }    }state parse_ipv4 {        b.extract(p.ip);verify(p.ip.version ==4w4,error.IPv4IncorrectVersion);verify(p.ip.ihl ==4w5,error.IPv4OptionsNotSupported);        ck.clear();        ck.update(p.ip);// Verify that packet checksum is zeroverify(ck.get() ==16w0,error.IPv4ChecksumError);transition accept;    }}// Match-action pipeline sectioncontrol TopPipe(inout Parsed_packet headers,inerror parseError,// parser errorin InControl inCtrl,// input portout OutControl outCtrl) {     IPv4Address nextHop;// local variable/*** Indicates that a packet is dropped by setting the* output port to the DROP_PORT*/action Drop_action() {          outCtrl.outputPort = DROP_PORT;      }/*** Set the next hop and the output port.* Decrements ipv4 ttl field.* @param ivp4_dest ipv4 address of next hop* @param port output port*/action Set_nhop(IPv4Address ipv4_dest, PortId port) {          nextHop = ipv4_dest;          headers.ip.ttl = headers.ip.ttl -1;          outCtrl.outputPort = port;      }/*** Computes address of next IPv4 hop and output port* based on the IPv4 destination of the current packet.* Decrements packet IPv4 TTL.* @param nextHop IPv4 address of next hop*/table ipv4_match {         key = { headers.ip.dstAddr: lpm; }// longest-prefix match         actions = {              Drop_action;              Set_nhop;         }         size =1024;         default_action = Drop_action;     }/*** Send the packet to the CPU port*/action Send_to_cpu() {          outCtrl.outputPort = CPU_OUT_PORT;      }/*** Check packet TTL and send to CPU if expired.*/table check_ttl {         key = { headers.ip.ttl: exact; }         actions = { Send_to_cpu; NoAction; }const default_action = NoAction;// defined in core.p4     }/*** Set the destination MAC address of the packet* @param dmac destination MAC address.*/action Set_dmac(EthernetAddress dmac) {          headers.ethernet.dstAddr = dmac;      }/*** Set the destination Ethernet address of the packet* based on the next hop IP address.* @param nextHop IPv4 address of next hop.*/table dmac {          key = { nextHop: exact; }          actions = {               Drop_action;               Set_dmac;          }          size =1024;          default_action = Drop_action;      }/*** Set the source MAC address.* @param smac: source MAC address to use*/action Set_smac(EthernetAddress smac) {           headers.ethernet.srcAddr = smac;       }/*** Set the source mac address based on the output port.*/table smac {           key = { outCtrl.outputPort: exact; }           actions = {                Drop_action;                Set_smac;          }          size =16;          default_action = Drop_action;      }apply {if (parseError !=error.NoError) {              Drop_action();// invoke drop directlyreturn;          }          ipv4_match.apply();// Match result will go into nextHopif (outCtrl.outputPort == DROP_PORT)return;          check_ttl.apply();if (outCtrl.outputPort == CPU_OUT_PORT)return;          dmac.apply();if (outCtrl.outputPort == DROP_PORT)return;          smac.apply();    }}// deparser sectioncontrol TopDeparser(inout Parsed_packet p, packet_out b) {    Checksum16() ck;apply {        b.emit(p.ethernet);if (p.ip.isValid()) {            ck.clear();// prepare checksum unit            p.ip.hdrChecksum =16w0;// clear checksum            ck.update(p.ip);// compute new checksum.            p.ip.hdrChecksum = ck.get();        }        b.emit(p.ip);    }}// Instantiate the top-level VSS packageVSS(TopParser(),    TopPipe(),    TopDeparser()) main;

6. P4 language definition

The P4 language can be viewed as having several distinct components,which we describe separately:

6.1. Syntax and semantics

6.1.1. Grammar

The complete grammar of P416 is given in Appendix H,using Yacc/Bison grammar description language. This text is based onthe same grammar. We adopt several standard conventions when we provideexcerpts from the grammar:

Pseudo-code (mostly used for describing the semantics ofvarious P4 constructs) are shown with fixed-size fonts as in thefollowing example:

ParserModel.verify(bool condition,error err) {if (condition ==false) {        ParserModel.parserError = err;        goto reject;    }}

6.1.2. Semantics and the P4 abstract machines

We describe the semantics of P4 in terms of abstract machines executingtraditional imperative code. There is an abstract machine for each P4sub-language (parser, control). The abstract machines are described inthis text in pseudo-code and English.

P4 compilers are free to reorganize the code they generate in any way as long asthe externally visible behaviors of the P4 programs are preserved asdescribed by this specification where externally visible behavior is defined as:

6.2. Preprocessing

To aid composition of programs from multiple source files P4compilers should support the following subset of the C preprocessorfunctionality:

The preprocessor should also remove the sequence backslash newline (ASCII codes 92, 10)to facilitate splitting content across multiple lines when convenient for formatting.

Additional C preprocessor capabilities may be supported, butare not guaranteede.g., macros with arguments. Similar to C,#includecan specify a file name either within double quotes or within<>.

# include <system_file># include"user_file"

The difference between the two forms is the order in which thepreprocessor searches for header files when the path is incompletelyspecified.

P4 compilers should correctly handle#line directivesthat may be generated during preprocessing. This functionality allowsP4 programs to be built from multiple source files, potentiallyproduced by different programmers at different times:

6.2.1. P4 core library

The P4 language specification defines a core library that includesseveral common programming constructs. Adescription of the core library is provided in AppendixD. All P4 programs must include the corelibrary. Including the core library is done with

# include <core.p4>

6.3. Lexical constructs

All P4 keywords use only ASCII characters. All P4 identifiers must useonly ASCII characters. P4 compilers should handle correctly stringscontaining 8-bit characters in comments and string literals.P4 is case-sensitive.Whitespace characters, including newlines are treated as tokenseparators. Indentation is free-form; however, P4 has C-like blockconstructs, and all our examples use C-style indentation. Tabcharacters are treated as spaces.

The lexer recognizes the following kinds of terminals:

6.3.1. Identifiers

P4 identifiers may contain only letters, numbers, and the underscorecharacter_, and must start with a letter orunderscore. The special identifier consisting of a single underscore_is reserved to indicate a“don't care” value; itstype may vary depending on the context. Certain keywords (e.g.,apply)can be used as identifiers if the context makes it unambiguous.

nonTypeName    : IDENTIFIER    | APPLY    | KEY    | ACTIONS    | STATE    | ENTRIES    | TYPE    ;name    : nonTypeName    | TYPE_IDENTIFIER    ;

6.3.2. Comments

P4 supports several kinds of comments:

Use of Javadoc-style comments is strongly encouraged for the tables and actionsthat are used to synthesize the interface with the control-plane.

P4 treats comments as token separators and no comments are allowed within atokene.g.bi/**/t is parsed as two tokens,bi andt, andnot as a single tokenbit.

6.3.3. Literal constants

6.3.3.1. Boolean literals

There are two Boolean literal constants:true andfalse.

6.3.3.2. Integer literals

Integer literals are positive, arbitrary-precision integers. Bydefault, literals are represented in base 10. The following prefixesmust be employed to specify the base explicitly:

The width of a numeric literal in bits can be specified by an unsignednumber prefix consisting of a number of bits and a signednessindicator:

Note that a leading zero by itself does not indicate anoctal (base 8) constant. The underscore character is considered adigit within number literals but is ignored when computing thevalue of the parsed number. This allows long constant numbers to bemore easily read by grouping digits together. The underscore cannot beused in the width specification or as the first character of aninteger literal. No comments or whitespaces are allowed within aliteral. Here are some examples of numeric literals:

32w255// a 32-bit unsigned number with value 25532w0d255// same value as above32w0xFF// same value as above32s0xFF// a 32-bit signed number with value 2558w0b10101010// an 8-bit unsigned number with value 0xAA8w0b_1010_1010// same value as above8w170// same value as above8s0b1010_1010// an 8-bit signed number with value -8616w0377// 16-bit unsigned number with value 377 (not 255!)16w0o377// 16-bit unsigned number with value 255 (base 8)
6.3.3.3. String literals

String literals (string constants) are specified as an arbitrarysequence of 8-bit characters, enclosed within double quote signs"(ASCII code 34). Strings start with a double quote signand extend to the first double quote sign which is not immediatelypreceded by an odd number of backslash characters (ASCII code 92). P4does not make any validity checks on strings (i.e., it does not checkthat 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 toother third-party tools or compiler-backends, including theterminating quotes. These tools can define their own handling ofescape sequences (e.g., how to specify Unicode characters, or handleunprintable ASCII characters).

Here are 3 examples of string literals:

"simple string""string\" with\" embedded\" quotes""string with embeddedline terminator"

6.4. Naming conventions

P4 provides a rich assortment of types. Base types include bit-strings, numbers, and errors.There are also built-in types forrepresenting constructs such as parsers, pipelines, actions, andtables. Users canconstruct new types based on these: structures, enumerations, headers,header stacks, header unions, etc.

In this document we adopt the following conventions:

6.5. P4 programs

A P4 program is a list of declarations:

p4program    :/* empty*/    | p4program declaration    | p4program';'/* empty declaration*/    ;declaration    : constantDeclaration    | externDeclaration    | actionDeclaration    | parserDeclaration    | typeDeclaration    | controlDeclaration    | instantiation    | errorDeclaration    | matchKindDeclaration    | functionDeclaration    ;

An empty declarations is indicated with a single semicolon. (Allowing emptydeclarations accommodates thehabits of C/C++ and Java programmerse.g., certain constructs, likestruct,do not require a terminating semicolon).

6.5.1. Scopes

Some P4 constructs act as namespaces that create local scopes for names including:

extern E<H>(/* parameters omitted*/) {/* body omitted*/ }// scope of H ends here.

The order of declarations is important; with the exception of parserstates, all uses of a symbol must follow the symbol'sdeclaration. (This is a departure from P414, whichallows declarations in any order. This requirement significantly simplifies the implementation ofcompilers for P4, allowing compilers to use additional informationabout declared identifiers to resolve ambiguities.)

6.5.2. Stateful elements

Most P4 constructs are stateless: given some inputs they produce aresult that solely depends on these inputs. There are only two stateful constructsthat may retain information across packets:

In P4 all stateful elements must be explicitly allocated atcompilation-time through the process called“instantiation”.

In addition,parsers,control blocks, andpackagesmay contain stateful element instantiations. Thus, they are alsotreated as stateful elements, even if they appear to contain no state,and must be instantiated before they can be used. However, althoughthey are stateful,tables do not need to be instantiatedexplicitlydeclaring atable also creates an instance ofit. This convention is designed to support the common case, since mosttables are used just once. To have finer-grained control over whenatable is instantiated, a programmer can declare it withinacontrol.

Recall the example in Section 5.3:TopParser,TopPipe,TopDeparser,Checksum16,andSwitch are types. There are two instances ofChecksum16, one inTopParser andone inTopDeparser, both calledck. TheTopParser,TopDeparser,TopPipe,andSwitch are instantiated at the end of the program, in thedeclaration of themain instance object, which is an instance oftheSwitch type (apackage).

6.6. L-values

L-values are expressions that may appear on the left side of anassignment operation or as arguments corresponding toout andinoutfunction parameters. An l-value represents a storage reference. Thefollowing expressions are legal l-values:

prefixedNonTypeName    : nonTypeName    | dotPrefix nonTypeName    ;lvalue    : prefixedNonTypeName    | lvalue'.' member    | lvalue'[' expression']'    | lvalue'[' expression':' expression']'    ;

The following is a legal l-value:headers.stack[4].field. Notethat method and function calls cannot return l-values.

6.7. Calling convention: call by copy in/copy out

P4 provides multiple constructs for writing modular programs: externmethods, parsers, controls, actions. All these constructs behavesimilarly to procedures in standard general-purpose programming languages:

Invocations are executed using copy-in/copy-out semantics.

Each parameter may be labeled with a direction:

Directionout parameters are always initialized at the beginning ofexecution of the portion of the program that has theout parameters,e.g.control,parser,action, function, etc. Thisinitialization is not performed for parameters with any direction thatis notout.

For example, if a directionout parameter has types2_t namedp:

header h1_t {bit<8> f1;bit<8> f2;}struct s1_t {    h1_t h1a;bit<3> a;bit<7> b;}struct s2_t {    h1_t h1b;    s1_t s1;bit<5> c;}

then at the beginning of execution of the part of the program that hastheout parameterp, it must be initialized so thatp.h1b andandp.s1.h1a are invalid. No other parts ofp are required to beinitialized.

Arguments are evaluated from left to right prior to the invocation of thefunction itself. The order of evaluation is important when theexpression supplied for an argument can have side-effects. Considerthe following example:

externvoid f(inoutbit x,inbit y);externbit g(inoutbit z);bit a;f(a, g(a));

Note that the evaluation ofg may mutate its argumenta, so thecompiler has to ensure that the value passed tof for its firstparameter is not changed by the evaluation of the second argument. Thesemantics for evaluating a function call is given by the followingalgorithm (implementations can be different as long as they providethe same result):

  1. Arguments are evaluated from left to right as they appear in thefunction call expression.
  2. If a parameter has a default value and no corresponding argument issupplied, the default value is used as an argument.
  3. For eachout andinout argument the correspondingl-value is saved (so it cannot be changed by the evaluation ofthe following arguments). This is important if the argumentcontains indexing operations into a header stack.
  4. The value of each argument is saved into a temporary.
  5. The function is invoked with the temporaries as arguments. We areguaranteed that the temporaries that are passed as arguments arenever aliased to each other, so this“generated” function callcan be implemented using call-by-reference if supported by thearchitecture.
  6. On function return, the temporaries that correspond tooutorinout arguments are copied in order from left to rightinto the l-values saved in step 2.

According to this algorithm, the previous function call is equivalentto the following sequence of statements:

bit tmp1 = a;// evaluate a; save resultbit tmp2 = g(a);// evaluate g(a); save result; modifies af(tmp1, tmp2);// evaluate f; modifies tmp1a = tmp1;// copy inout result back into a

To see why Step 2 in the above algorithm is important, consider the followingexample:

header H {bit z; }H[2] s;f(s[a].z, g(a));

The evaluation of this call is equivalent to the following sequence ofstatements:

bit tmp1 = a;// save the value of abit tmp2 = s[tmp1].z;// evaluate first argumentbit tmp3 = g(a);// evaluate second argument; modifies af(tmp2, tmp3);// evaluate f; modifies tmp2s[tmp1].z = tmp2;// copy inout result back; dest is not s[a].z

When used as arguments,extern objects can only be passed asdirectionless parameterse.g., see the packet argument in thevery simple switch example.

6.7.1. Justification

The main reason for using copy-in/copy-out semantics (instead of the more commoncall-by-reference semantics) is for controlling the side-effects ofexternfunctions and methods.extern methods and functionsare the main mechanism by which a P4 program communicates with itsenvironment. With copy-in/copy-out semanticsextern functionscannot hold references to P4 program objects; this enables thecompiler to limit the side-effects thatextern functions mayhave on the P4 program both in space (they can only affectoutparameters) and in time (side-effects can only occur at function calltime).

In general,extern functions are arbitrarily powerful: they can storeinformation in global storage, spawn separate threads,“collude” witheach other to share information but they cannot access anyvariable in a P4 program. With copy-in/copy-out semantics the compilercan still reason about P4 programs that invokeexternfunctions.

There are additional benefits of using copy-in copy-out semantics:

parameterList    :/* empty*/    | nonEmptyParameterList    ;nonEmptyParameterList    : parameter    | nonEmptyParameterList',' parameter    ;parameter    : optAnnotations direction typeRef name    | optAnnotations direction typeRef name'=' expression    ;direction    : IN    | OUT    | INOUT    |/* empty*/    ;

Following is a summary of the constraints imposed by the parameterdirections:

6.7.2. Optional parameters

A parameter that is annotated with the@optional annotation isoptional: the user may omit the value for that parameter in aninvocation. Optional parameters can only appear for arguments of:packages, extern functions, extern methods, and extern objectconstructors. Optional parameters cannot have default values. If aprocedure-like construct has both optional parameters and default values then itcan only be called using named arguments. It is recommended, but notmandatory, for all optional parameters to be at the end of a parameterlist.

The implementation of such objects is not expressed in P4, so themeaning and implementation of optional parameters should be specifiedby the target architecture. For example, we can imagine a two-stageswitch architecture where the second stage is optional. This could bedeclared as a package with an optional parameter:

package pipeline(/* parameters omitted*/);packageswitch(pipeline first, @optional pipeline second);pipeline(/* arguments omitted*/) ingress;switch(ingress) main;// a switch with a single-stage pipeline

Here the target architecture could implement the elided optional argument using an empty pipeline.

The following example shows optional parameters and parameters withdefault values.

externvoid h(inbit<32> a,inbool b =true);// default value// function callsh(10);// same as h(10, true);h(a =10);// same as h(10, true);h(a =10, b =true);struct Empty {}control nothing(inout Empty h,inout Empty m) {apply {}}parser parserProto<H, M>(packet_in p,out H h,inout M m);control controlProto<H, M>(inout H h,inout M m);package pack<HP, MP, HC, MC>(@optional parserProto<HP, MP> _parser,// optional parameter                             controlProto<HC, MC> _control = nothing());// default parameter valuepack() main;// No value for _parser, _control is an instance of nothing()

6.8. Name resolution

P4 objects that introduce namespaces are organized in a hierarchicalfashion. There is a top-level unnamed namespace containing alltop-level declarations.

Identifiers prefixed with a dot are always resolved in the top-levelnamespace.

constbit<32> x =2;control c() {int<32> x =0;apply {       x = x + (int<32>).x;// x is the int<32> local variable,// .x is the top-level bit<32> variable   }}

References to resolve an identifier are attempted inside-out, startingwith the current scope and proceeding to all lexically enclosingscopes. The compiler may provide a warning if multiple resolutions arepossible for the same name (name shadowing).

constbit<4> x =1;control p() {constbit<8> x =8;// x declaration shadows global xconstbit<4> y = .x;// reference to top-level xconstbit<8> z = x;// reference to p's local xapply {}}

6.9. Visibility

Identifiers defined in the top-level namespace are globallyvisible. Declarations within aparser orcontrol areprivate and cannot be referred to from outside of the enclosingparserorcontrol.

7. P4 data types

P416 is a statically-typed language. Programs that do not passthe type checker are considered invalid and rejected by thecompiler. P4 provides a number of base types as well as type operators thatconstruct derived types. Some values can be converted to a different type usingcasts. However, to make user intents clear, implicit casts are only allowed ina few circumstances and the range of casts available is intentionallyrestricted.

7.1. Base types

P4 supports the following built-in base types:

baseType    : BOOL    | ERROR    | BIT    | INT    | STRING    | BIT'<' INTEGER'>'    | INT'<' INTEGER'>'    | VARBIT'<' INTEGER'>'    | BIT'<''(' expression')''>'    | INT'<''(' expression')''>'    | VARBIT'<''(' expression')''>'    ;

7.1.1. The void type

The void type is writtenvoid. It contains no values. It isnot included in the production rulebaseType as it can only appear in fewrestricted places in P4 programs.

7.1.2. The error type

The error type contains opaque values that can be used to signalerrors. It is written aserror. New constants of the error typeare defined with the syntax:

errorDeclaration    : ERROR'{' identifierList'}'    ;

Allerror constants are inserted into theerrornamespace, irrespective of the place where an error isdefined.error is similar to an enumeration (enum)type in other languages. A program can contain multipleerror declarations, whichthe compiler will merge together. It is an error to declare the sameidentifier multiple times. Expressions of typeerror aredescribed in Section 8.2.

For example, the following declaration creates two constants oferrortype (these errors are declared in the P4 core library):

error { ParseError, PacketTooShort }

The underlying representation of errors is target-dependent.

7.1.3. The match kind type

Thematch_kind type is very similar to theerror type andis used to declare a set of names that may beused in a table's key property (described in Section13.2.1).All identifiers are inserted into thetop-level namespace.It is an error to declare the samematch_kindidentifier multiple times.

matchKindDeclaration    : MATCH_KIND'{' identifierList'}'    ;

The P4 core library contains the following match_kind declaration:

match_kind {   exact,   ternary,   lpm}

Architectures may support additionalmatch_kinds. Thedeclaration of newmatch_kinds can only occur within modeldescription files; P4 programmers cannot declare new match kinds.

7.1.4. The Boolean type

The Boolean typebool contains just two values,false andtrue.Boolean values are not integers or bit-strings.

7.1.5. Strings

The typestring represents strings. There are no operations onstring values; one cannot declare variables with astring type.Parameters with typestring can be only directionless (see Section6.7). P4 does not support string manipulationin the dataplane; thestring type is only allowed for denotingcompile-time constant string values. These may be useful, forexample, a specific target architecture may support an extern functionfor logging with the following signature:

externvoid log(string message);

The only strings that can appear in a P4 program are constant stringliterals, described in Section 6.3.3.3. For example,the following annotation indicates that a specific name should be usedfor a table when generating the control-plane API:

@name("acl") table t1 { /* body omitted */ }

7.1.6. Integers (signed and unsigned)

P4 supports arbitrary-size integer values. The typing rules for theinteger types are chosen according to the following principles:

The priority of arithmetic operations is identical to Ce.g.,multiplication binds tighter than addition.

7.1.6.1. Portability

No P4 target can support all possible types and operations. Forexample, the typebit<23132312> is legal in P4, butit is highly unlikely to be supported on any target in practice. Hence,each target can impose restrictions on the types it can support. Suchrestrictions may include:

The documentation supplied with a target should clearly specify restrictions, andtarget-specific compilers should provide clear error messages whensuch restrictions are encountered. An architecture may reject awell-typed P4 program and still be conformant to the P4 spec. However,if an architecture accepts a P4 program as valid, the runtime programbehavior should match this specification.

7.1.6.2. Unsigned integers (bit-strings)

An unsigned integer (which we also call a“bit-string”) has anarbitrary width, expressed in bits. A bit-string of widthW isdeclared as:bit<W>.W must be an expression that evaluates to acompile-time known value (see Section 17.1) that is anon-negative integer. When using an expression forthe size, the epression must be parenthesized. Bitstrings with width 0 areallowed; they have no actual bits, and can only have the value 0. See{#sec-uninitialized-values-and-writing-invalid-headers } for additional details.

constbit<32> x =10;// 32-bit constant with value 10.constbit<(x +2)> y =15;// 12-bit constant with value 15.// expression for width must use ()

Bits within a bit-string are numbered from0 toW-1. Bit0is the least significant, and bitW-1 is the most significant.

For example, the typebit<128> denotes the type of bit-stringvalues with 128 bits numbered from 0 to 127, where bit 127 is the mostsignificant.

The typebit is a shorthand forbit<1>.

P4 architectures may impose additional constraints on bit types: forexample, they may limit the maximum size, or they may only supportsome arithmetic operations on certain sizes (e.g., 16-, 32-, and 64-bit values).

All operations that can be performed on unsigned integers aredescribed in Section 8.5.

7.1.6.3. Signed Integers

Signed integers are represented using two's complement. An integer withWbits is declared as:int<W>.W must be an expression that evaluates toa compile-time known value that is a positive integer.

Bits within an integer are numbered from0 toW-1. Bit0is the least significant, and bitW-1 is the sign bit.

For example, the typeint<64> describes the type of integersrepresented using exactly 64 bits with bits numbered from 0 to 63,where bit 63 is the most significant (sign) bit.

P4 architectures may impose additional constraints on signed types:for example, they may limit the maximum size, or they may only supportsome arithmetic operations on certain sizes (e.g., 16-, 32-, and 64-bit values).

All operations that can be performed on signed integers are describedin Section 8.6.

A signed integer with width 1 can only have two legal values: 0 and-1.

7.1.6.4. Dynamically-sized bit-strings

Some network protocols use fields whose size is only known at runtime(e.g., IPv4 options). To support restricted manipulations of suchvalues, P4 provides a special bit-string type whose size is set atruntime, called avarbit.

The typevarbit<W> denotes a bit-string with a width of at mostWbits, whereW must be a non-negative integer that is a compile-timeknown value. For example, the typevarbit<120> denotes the typeof bit-string values that may have between 0 and 120 bits. Mostoperations that are applicable to fixed-size bit-strings (unsignednumbers)cannot be performed on dynamically sized bit-strings.

P4 architectures may impose additional constraints on varbit types:for example, they may limit the maximum size, or they may requirevarbitvalues to always contain an integer number of bytes at runtime.

All operations that can be performed on varbits are described inSection 8.8.

7.1.6.5. Infinite-precision integers

The infinite-precision data type describes integers with an unlimitedprecision. This type is written asint.

This type is reserved for integer literals and expressions thatinvolve only literals. No P4 runtime value can have aninttype; at compile time the compiler will convert all int values thathave a runtime component to fixed-width types, according to the rulesdescribed below.

All operations that can be performed on infinite-precision integersare described in Section 8.7. The following exampleshows three constant definitions whose values are infinite-precisionintegers.

constint a =5;constint b =2 * a;constint c = b - a +3;
7.1.6.6. Integer literal types

The types of integer literals (constants) are as follows:

The table below shows several examples of integer literals and theirtypes. For additional examples of literals see Section6.3.3.

Literal Interpretation
10 Type isint, value is 10
8w10 Type isbit<8>, value is 10
8s10 Type isint<8>, value is 10
2s3 Type isint<2>, value is-1 (last 2 bits), overflow warning
1w10 Type isbit<1>, value is 0 (last bit), overflow warning
1s1 Type isint<1>, value is-1, overflow warning

7.2. Derived types

P4 provides a number of type constructors that can be used to deriveadditional types including:

The typesheader,header_union,enum,struct,extern,parser,control,andpackage can only be used in type declarations, where theyintroduce a new name for the type. The type can subsequently bereferred to using this identifier.

Other types cannot be declared, but are synthesized by the compilerinternally to represent the type of certain language constructs. Thesetypes are described in Section 7.2.8: set types andfunction types. For example, the programmer cannot declare a variablewith type“set”, but she can write an expression whose value evaluatesto aset type. These types are used during type-checking.

typeDeclaration    : derivedTypeDeclaration    | typedefDeclaration    | parserTypeDeclaration';'    | controlTypeDeclaration';'    | packageTypeDeclaration';'    ;derivedTypeDeclaration    : headerTypeDeclaration    | headerUnionDeclaration    | structTypeDeclaration    | enumDeclaration    ;typeRef    : baseType    | typeName    | specializedType    | headerStackType    | tupleType    ;namedType    : typeName    | specializedType    ;prefixedType    : TYPE_IDENTIFIER    | dotPrefix TYPE_IDENTIFIER    ;typeName    : prefixedType    ;

7.2.1. Enumeration types

An enumeration type is defined using the following syntax:

enumDeclaration    : optAnnotations ENUM name'{' identifierList'}'    | optAnnotations ENUM typeRef name'{' specifiedIdentifierList'}'    ;identifierList    : name    | identifierList',' name    ;specifiedIdentifierList    : specifiedIdentifier    | specifiedIdentifierList',' specifiedIdentifier    ;specifiedIdentifier    : name'=' initializer    ;

For example, the declaration

enum Suits { Clubs, Diamonds, Hearths, Spades }

introduces a new enumeration type, which contains fourconstantse.g.,Suits.Clubs. Anenum declarationintroduces a new identifier in the current scope for naming thecreated type. The underlying representation of theSuits enum is notspecified, so their“size” in bits is not specified (it istarget-specific).

It is also possible to specify anenum with an underlying representation.These are sometimes called serializableenums, because headers areallowed to have fields with suchenum types. This requires the programmer provide both the fixed-width unsigned (or signed) integer type andan associated integer value for each symbolic entry in the enumeration.The symboltypeRef in the grammar above must be one of the following types:

enumbit<16> EtherType {  VLAN      =0x8100,  QINQ      =0x9100,  MPLS      =0x8847,  IPV4      =0x0800,  IPV6      =0x86dd}

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

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

For example, the declaration

enumbit<8> FailingExample {  first           =1,  second          =2,  third           =3,  unrepresentable =300}

would raise an error because300, the value associated withFailingExample.unrepresentable cannot be represented as abit<8> value.

Theinitializer expression must be a compile-time known value.

Annotations, represented by the non-terminaloptAnnotations, aredescribed in Section 18.

Operations onenum values are described in Section8.3.

7.2.2. Header types

The declaration of aheader type is given by the followingsyntax:

headerTypeDeclaration    : optAnnotations HEADER name optTypeParameters'{' structFieldList'}'    ;structFieldList    :/* empty*/    | structFieldList structField    ;structField    : optAnnotations typeRef name';'    ;

where eachtypeRef is restricted to a bit-string type (fixed orvariable), a fixed-width signed integer type, a boolean type, or a struct thatitself contains other struct fields, nested arbitrarily, as long as all of the“leaf” types arebit<W>,int<W>, a serializableenum, or abool. If abool is used inside a P4 header, all implementations encode thebool as aone bit long field, with the value1 representingtrue and0 representingfalse.

A header declaration introduces a new identifier in thecurrent scope; the type can be referred to using this identifier. A header issimilar to astruct in C, containing all the specified fields. However, inaddition, a header also contains a hidden Boolean“validity” field. When the“validity” bit istrue we say that the“header is valid”. When a localvariable with a header type isdeclared, its“validity” bit is automatically set tofalse. The“validity”bit can be manipulated by using the header methodsisValid(),setValid(),andsetInvalid(), as described in Section 8.16.

Note, nesting of headers is not supported. One reason is that it leads tocomplications in defining the behavior of arbitrary sequences ofsetValid,setInvalid, andemit operations. Consider an example where headerh1contains headerh2 as a member, both currently valid. A program executesh2.setInvalid() followed bypacket.emit(h1). Should all fields ofh1be emitted, but skippingh2? Similarly, shouldh1.setInvalid()invalidate all headers contained withinh1, regardless of how deeplythey are nested?

Header types may be empty:

header Empty_h { }

Note that an empty header still contains a validity bit.

When a struct is inside of a header, the order of the fields for the purposesofextract andemit calls is the order of the fields as defined in the source code.An example of a header including a struct is included below.

struct ipv6_addr {bit<32> Addr0;bit<32> Addr1;bit<32> Addr2;bit<32> Addr3;}header ipv6_t {bit<4>    version;bit<8>    trafficClass;bit<20>   flowLabel;bit<16>   payloadLen;bit<8>    nextHdr;bit<8>    hopLimit;    ipv6_addr src;    ipv6_addr dst;}

Headers that do not contain anyvarbit field are“fixedsize.” Headers containingvarbit fields have“variablesize.” The size (in bits) of a fixed-size header is a constant, and itis simply the sum of the sizes of all component fields (withoutcounting the validity bit). There is no padding or alignment of theheader fields. Targets may impose additional constraints onheader typese.g., restricting headers to sizes that are an integernumber of bytes.

For example, the following declaration describes a typical Ethernetheader:

header Ethernet_h {bit<48> dstAddr;bit<48> srcAddr;bit<16> etherType;}

The following variable declaration uses the newly introduced typeEthernet_h:

Ethernet_h ethernetHeader;

P4's parser language provides anextract method that can be used to“fill in” the fields of a header from a network packet, as describedin Section 12.8. The successful execution ofanextract operation also sets the validity bit of the extractedheader totrue.

Here is an example of an IPv4 header with variable-sized options:

header IPv4_h {bit<4>       version;bit<4>       ihl;bit<8>       diffserv;bit<16>      totalLen;bit<16>      identification;bit<3>       flags;bit<13>      fragOffset;bit<8>       ttl;bit<8>       protocol;bit<16>      hdrChecksum;bit<32>      srcAddr;bit<32>      dstAddr;varbit<320>  options;}

As demonstrated by a code example in Section12.8.2, another way to support headers that containvariable-length fields is to define two headers one fixed length,one containing avarbit field and extract each part in separateparsing steps.

7.2.3. Header stacks

A header stack represents an array of headers. A header stack type isdefined as:

headerStackType    : typeName'[' expression']'    : specializedType'[' expression']'    ;

wheretypeName is the name of a header type. For a header stackhs[n],the termn is the maximum defined size, and must bea positive integer that is a compile-time known value. Nested headerstacks are not supported. At runtime a stack containsn valueswith typetypeName, only some of which may be valid. Expressionson header stacks are discussed in Section 8.17.

For example, the following declarations,

header Mpls_h {bit<20> label;bit<3>  tc;bit     bos;bit<8>  ttl;}Mpls_h[10] mpls;

introduce a header stack calledmpls containing ten entries, eachof typeMpls_h.

7.2.4. Header unions

A header union represents an alternative containing at most one of several differentheaders. Header unions can be used to represent“options” in protocolslike TCP and IP. They also provide hints to P4 compilers that only onealternative will be present, allowing them to conserve storage resources.

A header union is defined as:

headerUnionDeclaration    : optAnnotations HEADER_UNION name optTypeParameters'{' structFieldList'}'    ;

This declaration introduces a new type with the specified name in thecurrent scope. Each element of the list of fields used to declare aheader union must be a header type. However, the empty list of fieldsis legal.

As an example, the typeIp_h below represents the union of an IPv4and IPv6 headers:

header_union IP_h {  IPv4_h v4;  IPv6_h v6;}

A header union is not considered a type with fixed width.

7.2.5. Struct types

P4struct types are defined with the following syntax:

structTypeDeclaration    : optAnnotations STRUCT name optTypeParameters'{' structFieldList'}'    ;

This declaration introduces a new type with the specified name in thecurrent scope. An empty struct (with no fields) is legal. For example, the structureParsed_headersbelow contains the headers recognized by a simple parser:

header Tcp_h {/* fields omitted*/ }header Udp_h {/* fields omitted*/ }struct Parsed_headers {    Ethernet_h ethernet;    Ip_h       ip;    Tcp_h      tcp;    Udp_h      udp;}

7.2.6. Tuple types

A tuple is similar to astruct, in that it holds multiple values.The type of tuples with n component typesT1,,Tn is written as

tuple<T1,/* more fields omitted*/,Tn>
tupleType    : TUPLE'<' typeArgumentList'>'    ;

Operations that manipulate tuple types are described in Sections8.10 and 8.11.

The typetuple<> is a tuple type with no components.

7.2.7. Type nesting rules

The table below lists all types that may appear as members of headers,header unions, structs, and tuples. Note thatint means aninfinite-precision integer, without a width specified.

Container type
Element type header header_union struct or tuple
bit<W> allowed error allowed
int<W> allowed error allowed
varbit<W> allowed error allowed
int error error error
void error error error
error error error allowed
match_kind error error error
bool allowed error allowed
enum allowed1 error allowed
header error allowed allowed
header stack error error allowed
header_union error error allowed
struct allowed2 error allowed
tuple error error allowed

Rationale:int does not have precise storage requirements,unlikebit<> orint<> types.match_kindvalues are not useful to store in a variable, as theyare only used to specify how to match fields in table search keys,which are all declared at compile time.void is not useful aspart of another data structure. Headers must have precisely definedformats as sequences of bits in order for them to be parsed ordeparsed.

Note the two-argumentextract method (see Section12.8.2) on packets only supports a singlevarbitfield in a header.

The table below lists all types that may appear as base types in atypedef ortype declaration.

Base type Btypedef B <name>type B <name>
bit<W> allowed allowed
int<W> allowed allowed
varbit<W> allowed error
int allowed error
void error error
error allowed error
match_kind error error
bool allowed allowed
enum allowed error
header allowed error
header stack allowed error
header_union allowed error
struct allowed error
tuple allowed error
atypedef name allowed allowed3
atype name allowed allowed

7.2.8. Synthesized data types

For the purposes of type-checking the P4 compiler can synthesize sometype representations which cannot be directly expressed byusers. These are described in this section: set types and functiontypes.

7.2.8.1. Set types

The typeset<T> describessets of values of typeT. Settypes can only appear in restricted contexts in P4 programs. Forexample, the range expression8w5 ..8w8 describes a setcontaining the 8-bit numbers 5, 6, 7, and 8, so its type isset<bit<8>>;.This expression can be used as a label in aselect expression(see Section 12.6), matching any value in this range. Settypes cannot be named or declared by P4 programmers, they are onlysynthesized by the compiler internally and used fortype-checking. Expressions with set types are described in Section8.13.

7.2.8.2. Function types

Function types are created by the P4 compiler internally to represent the typesof functions (explicit functions or extern functions) and methods duringtype-checking. We also call the type of a function itssignature. Libraries can contain functions and extern function declarations.

For example, consider the following declarations:

externvoid random(inbit<5> logRange,outbit<32> value);bit<32> add(inbit<32> left,inbit<32> right) {return left + right;}

These declarations describe two objects:

7.2.9. Extern types

P4 supports extern object declarations and extern function declarations using the following syntax.

externDeclaration    : optAnnotations EXTERN nonTypeName optTypeParameters'{' methodPrototypes'}'    | optAnnotations EXTERN functionPrototype';'    ;
7.2.9.1. Extern functions

An extern function declaration describes the name and type signature of the function,but not its implementation.

functionPrototype    : typeOrVoid name optTypeParameters'(' parameterList')'    ;

For an example of anextern function declaration, see Section7.2.8.2.

7.2.9.2. Extern objects

An extern object declaration declares an object and all methods thatcan be invoked to perform computations and to alter the state of theobject. Extern object declarations can also optionally declareconstructor methods; these must have the same name as the enclosingexterntype, no type parameters, and no return type. Extern declarations mayonly appear as allowed by the architecture model and may be specificto a target.

methodPrototypes    :/* empty*/    | methodPrototypes methodPrototype    ;methodPrototype    : optAnnotations functionPrototype';'    | optAnnotations TYPE_IDENTIFIER'(' parameterList')'';'//constructor    | optAnnotations ABSTRACT functionPrototype";"    ;typeOrVoid    : typeRef    | VOID    | IDENTIFIER// may be a type variable    ;optTypeParameters    :/* empty*/    | typeParameters    ;typeParameters    :'<' typeParameterList'>'    ;typeParameterList    : name    | typeParameterList',' name    ;

For example, the P4 core library introduces two extern objectspacket_inandpacket_out used for manipulating packets(see Sections 12.8 and 15). Hereis an example showing how the methods of these objects can be invokedon a packet:

extern packet_out {void emit<T>(in T hdr);}control d(packet_out b,in Hdr h) {apply {        b.emit(h.ipv4);// write ipv4 header into output packet    }// by calling emit method}

Functions and methods are the only P4 constructs that supportoverloading: there can exist multiple methods with the same name inthe same scope. When overloading is used, the compiler must be able todisambiguate at compile-time which method or function is being called,either by the number of arguments or by the names of the arguments,when calls are specifying argument names. Argument type informationis not used in disambiguating calls.

Abstract methods

Typical extern object methods are built-in, and are implemented by thetarget architecture. P4 programmers can only call such methods.

However, some types of extern objects may provide methods that can beimplemented by the P4 programmers. Such methods are described withtheabstract keyword prior to the method definition. Here is anexample:

extern Balancer {    Balancer();// get the number of active flowsbit<32> getFlowCount();// return port index used for load-balancing// @param address: IPv4 source address of flow    abstractbit<4> on_new_flow(inbit<32> address);}

When such an object is instantiated the user has to supply animplementation of all theabstract methods (see 10.3.1).

7.2.10. Type specialization

A generic type may be specialized by specifying arguments for its typevariables. In cases where the compiler can infer type arguments typespecialization is not necessary. When a type is specialized all itstype variables must be bound.

specializedType    : prefixedType'<' typeArgumentList'>'    ;

For example, the following extern declaration describes a genericblock of registers, where the type of the elements stored in eachregister is an arbitraryT.

extern Register<T> {    Register(bit<32> size);    T read(bit<32> index);void write(bit<32> index, T value);}

The typeT has to be specified when instantiating a set ofregisters, by specializing the Register type:

Register<bit<32>>(128) registerBank;

The instantiation ofregisterBank is made using theRegistertype specialized with thebit<32> bound to theT type argument.

struct,header,header_union and header stack types can begeneric as well. In order to use such a generic type it must bespecialized with appropriate type arguments. For example

// generic structure typestruct S<T> {    T field;bool valid;}struct G<T> {    S<T> s;}// specialize S by replacing 'T' with 'bit<32>'const S<bit<32>> s = { field =32w0, valid =false };// Specialize G by replacing 'T' with 'bit<32>'const G<bit<32>> g = { s = { field =0, valid =false } };// generic header typeheader H<T> {    T field;}// Specialize H by replacing 'T' with 'bit<8>'const H<bit<8>> h = { field =1 };// Header stack produced from a specialization of a generic header typeH<bit<8>>[10] stack;// Generic header unionheader_union HU<T> {    H<bit<32>> h32;    H<bit<8>>  h8;    H<T>       ht;}// Header union with a type obtained by specializing a generic header union typeHU<bit> hu;

7.2.11. Parser and control blocks types

Parsers and control blocks types are similar to function types: theydescribe the signature of parsers and control blocks. Such functionshave no return values. Declarations of parsers and control block typesin architectures may be generic(i.e., have type parameters).

The typesparser,control, andpackage cannot beused as types of arguments for methods, parsers, controls, tables,actions. Theycan be used as types for the arguments passed toconstructors (see Section 14).

7.2.11.1. Parser type declarations

A parser type declaration describes the signature of a parser. Aparser should have at least one argument of typepacket_in,representing the received packet that is processed.

parserTypeDeclaration    : optAnnotations PARSER name optTypeParameters'(' parameterList')'    ;

For example, the following is a type declaration of a parser typenamedP that is parameterized on a type variableH. The parserthat receives as input apacket_in valueb and producestwo values:

struct Counters {/* Fields omitted*/ }parser P<H>(packet_in b,out H packetHeaders,out Counters counters);
7.2.11.2. Control type declarations

A control type declaration describes the signature of a control block.

controlTypeDeclaration    : optAnnotations CONTROL name optTypeParameters'(' parameterList')'    ;

Control type declarations are similar to parser typedeclarations.

7.2.12. Package types

A package type describes the signature of a package.

packageTypeDeclaration    : optAnnotations PACKAGE name optTypeParameters'(' parameterList')'    ;

All parameters of a package are evaluated at compilation-time, and inconsequence they must all be directionless (they cannot bein,out,orinout). Otherwise package types are very similar toparser type declarations. Packages can only be instantiated; there areno runtime behaviors associated with them.

7.2.13. Don't care types

A don't care (underscore,"_") can be used in some circumstances asa type. It should be only used in a position where one could write abound type variable. Theunderscore can be used to reduce code complexitywhen it is notimportant what the type variable binds to (during type unification thedon't care type can unify with any other type). An example is givenSection 16.1.

7.3. Default values

Some P4 types define a“default value,” which can be used toautomatically initialize values of that type. The default values areas follows:

Note that some types do not have default values, e.g.,match_kind,set types, function types, extern types, parser types, control types,package types.

7.4. typedef

Atypedef declaration can be used to give an alternative name toa type.

typedefDeclaration    : optAnnotations TYPEDEF typeRef name';'    | optAnnotations TYPEDEF derivedTypeDeclaration name';'    ;
typedefbit<32> u32;typedefstruct Point {int<32> x;int<32> y; } Pt;typedef Empty_h[32] HeaderStack;

The two types are treated as synonyms, and all operations that can beexecuted using the original type can be also executed using the newlycreated type.

7.5. Introducing new types

Similarly totypedef, the keywordtype can be used to introduce anew type.

    | optAnnotations TYPE typeRef name    | optAnnotations TYPE derivedTypeDeclaration name
typebit<32> U32;U32 x = (U32)0;

While similar totypedef, thetype keyword introduces in fact anew type, which is not a synonym with the original type: values of theoriginal type and the newly introduced type cannot be mixed inexpressions.

One important use of such types is in describing P4 values that needto be exchanged with the control-plane through communication channels(e.g., through the control-plane API or through network packets sentto the control-plane). For example, a P4 architecture may define atype for the switch ports:

typebit<9> PortId_t;

This declaration will preventPortId_t values from being used inarithmetic expressions. Moreover, this declaration may enable specialmanipulation or such values by software that lies outside of thedatapath (e.g., a target specific tool-chain could include softwarethat automatically converts values of typePortId_t to a differentrepresentation when exchanged with the control-plane software).

8. Expressions

This section describes all expressions that can be used in P4,grouped by the type of value they produce.

The grammar production rule for general expressions is as follows:

expression    : INTEGER    | TRUE    | FALSE    | STRING_LITERAL    | nonTypeName    | dotPrefix nonTypeName    | expression'[' expression']'    | expression'[' expression':' expression']'    |'{' expressionList'}'    |'{' kvList'}'    |'(' expression')'    |'!' expression    |'~' expression    |'-' expression    |'+' expression    | typeName'.' member    | ERROR'.' member    | expression'.' member    | expression'*' expression    | expression'/' expression    | expression'%' expression    | expression'+' expression    | expression'-' expression    | expression SHL expression// SHL is <<    | expression'>''>' expression// check that >> are contiguous    | expression LE expression// LE is <=    | expression GE expression    | expression'<' expression    | expression'>' expression    | expression NE expression// NE is !=    | expression EQ expression// EQ is ==    | expression'&' expression    | expression'^' expression    | expression'|' expression    | expression PP expression// PP is ++    | expression AND expression// AND is &&    | expression OR expression// OR is ||    | expression'?' expression':' expression    | expression'<' realTypeArgumentList'>''(' argumentList')'    | expression'(' argumentList')'    | namedType'(' argumentList')'    |'(' typeRef')' expression    ;expressionList    :/* empty*/    | expression    | expressionList',' expression    ;member    : name    ;argumentList    :/* empty*/    | nonEmptyArgList    ;nonEmptyArgList    : argument    | nonEmptyArgList',' argument    ;argument    : expression    ;typeArg    : DONTCARE    | typeRef    | nonTypeName    | VOID    ;typeArgumentList    :/* empty*/    | typeArg    | typeArgumentList',' typeArg    ;

See Appendix H for the complete P4 grammar.

This grammar does not indicate the precedence of the variousoperators. The precedence mostly follows the C precedencerules, with one change and some additions. The precedence ofthe bitwise operators&| and^ is higher than the precedenceof relation operators<,<=,>,>=. This is more natural giventhe addition of a true boolean type in the type system, as bitwiseoperators cannot be applied to boolean types.Concatenation (++) has the same precedence as infixaddition. Bit-slicinga[m:l] has the same precedence as arrayindexing (a[i]).

In addition to these expressions, P4 also supportsselect expressions (describedin Section 12.6), which may be used only in parsers.

8.1. Expression evaluation order

Given a compound expression, the order in which sub-expressions areevaluated is important when the sub-expressions haveside-effects. P4 expressions are evaluated as follows:

8.2. Operations onerror types

Symbolic names declared by anerror declaration belong to theerrornamespace. Theerror type only supports equality (==) and inequality (!=) comparisons.The result of such a comparison is a Boolean value.

For example, the following operation tests for the occurrence of anerror:

error errorFromParser;if (errorFromParser !=error.NoError) {/* code omitted*/ }

8.3. Operations onenum types

Symbolic names declared by an enum belong to the namespace introduced by the enum declaration rather than the top-level namespace.

enum X { v1, v2, v3 }X.v1// reference to v1v1// error - v1 is not in the top-level namespace

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

Anenum may also specify an underlying type, such as the following:

enumbit<8> E {  e1 =0,  e2 =1,  e3 =2}

More than one symbolic value in anenum may map to the same fixed-withinteger value.

enumbit<8> NonUnique {  b1 =0,  b2 =1,// Note, both b2 and b3 map to the same value.  b3 =1,  b4 =2}

Anenum with an underlying type also supports explicit casts to and from theunderlying type. For instance, the following code:

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

castsa, which was initialized toE.e2 to abit<8>, using the specifiedfixed-width unsigned integer representation forE.e2,1. The variableb is then set to thesymbolic valueE.e2, which corresponds to the fixed-width unsigned integer value1.

Because it is always safe to cast from anenum to its underlying fixed-width integer type,implicit casting from anenum to its fixed-width (signed or unsigned) integer type is also supported:

bit<8> x = E.e2;// sets x to 1 (E.e2 is automatically casted to bit<8>)E  a = E.e2bit<8> y = a <<3;// sets y to 8 (a is automatically casted to bit<8> and then shifted)

Implicit casting from an underlying fixed-width type to an enum isnot supported.

enumbit<8> E1 {   e1 =0, e2 =1, e3 =2}enumbit<8> E2 {   e1 =10, e2 =11, e3 =12}E1 a = E1.e1;E2 b = E2.e2;a = b;// Error: b is automatically casted to bit<8>,// but bit<8> cannot be automatically casted to E1a = (E1) b;// OKa = E1.e1 +1;// Error: E.e1 is automatically casted to bit<8>,// and the right-hand expression has// the type bit<8>, which cannot be casted to E automatically.a = (E1)(E1.e1 +1);// Final explicit casting makes the assignment legala = E1.e1 + E1.e2;// Error: both arguments to the addition are automatically// casted to bit<8>. Thus the addition itself is legal, but// the assignment is nota = (E1)(E2.e1 + E2.e2);//  Final explicit casting makes the assignment legal

A reasonable compiler might generate a warning in cases that involve multiple automatic casts.

E1     a = E1.e1;E2     b = E2.e2;bit<8> c;if (a > b) {// Potential warning: two automatic and different casts to bit<8>.// code omitted}c = a + b;// Legal, but a warning would be reasonable

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

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

setse to an unnamed value, since there is no symbol corresponding to thefixed-width unsigned integer value5.

For example, in the following code, theelse clause of theif/elseif/elseblock can be reached even though the matches onx are complete with respectto the symbols defined inMyPartialEnum_t:

enumbit<2> MyPartialEnum_t {    VALUE_A =2w0,    VALUE_B =2w1,    VALUE_C =2w2}bit<2> y = < some value >;MyPartialEnum_t x = (MyPartialEnum_t)y;if (x == MyPartialEnum_t.VALUE_A) {// some code here}elseif (x == MyPartialEnum_t.VALUE_B) {// some code here}elseif (x == MyPartialEnum_t.VALUE_C) {// some code here}else {// A P4 compiler MUST ASSUME that this branch can be executed// some code here}

Additionally, if an enumeration is used as a field of a header, we would expectthetransitionselect to matchdefault when the parsed integer value doesnot match one of the symbolic values ofEtherType in the following example:

enumbit<16> EtherType {  VLAN      =0x8100,  IPV4      =0x0800,  IPV6      =0x86dd}header ethernet {// Some fields omitted  EtherType etherType;}parser my_parser(/* parameters omitted*/) {state parse_ethernet {    packet.extract(hdr.ethernet);transitionselect(hdr.ethernet.etherType) {      EtherType.VLAN : parse_vlan;      EtherType.IPV4 : parse_ipv4;      EtherType.IPV6: parse_ipv6;default: reject;  }}

Any variable with anenum type that contains an unnamed value, whether as the result of a castto anenum with an underlying type, parse into the field of anenum with anunderlying type, or simply the declaration of anyenum without a specifiedinitial value will not be equal to any of the values defined for thattype. Such an unnamed value should still lead to predictablebehavior in cases where any legal value would match, e.g. it shouldmatch in any of these situations:

Note that if anenum value lacking an underlying type appears in the control-plane API, thecompiler must select a suitable serialization data type andrepresentation.Forenum values with an underlying type and representations, the compiler shoulduse the specified underlying type as the serialization data type andrepresentation.

8.4. Expressions on Booleans

The following operations are provided on Boolean expressions:- And, denoted by&&,- Or denoted by||,- Negation, denoted by!, and- Equality and inequality tests, denoted by== and!= respectively.

The precedence of these operators is similar to C and uses short-circuit evaluation.

P4 does not implicitly cast from bit-strings to Booleans orvice versa. As a consequence, a program that is valid in a language like C such as,

if (x)/* body omitted*/

(where x has an integer type) must instead be written in P4 as:

if (x !=0)/* body omitted*/

See the discussion on infinite-precision types and implicit castsin Section 8.9.2 for details on how the0 in thisexpression is evaluated.

8.4.1. Conditional operator

A conditional expression of the forme1 ? e2 : e3 behaves thesame as in languages like C. As described above, the expressione1 is evaluated first,and eithere2 ore3 is evaluated depending on the result.

The first sub-expressione1 must have type Boolean, and the secondand third sub-expressions must have the same type, which cannot bothbe infinite precision integers unless the condition itself can beevaluated at compilation time. This restriction is designed to ensurethat the width of the result of the conditional expression can beinferred statically at compile time.

8.5. Operations on bit types (unsigned integers)

This section discusses all operations that can be performed onexpressions of typebit<W> for some widthW, also known asbit-strings.

Arithmetic operations“wrap-around”, similar to C operations onunsigned values (i.e., representing a large value on W bits will onlykeep the least-significant W bits of the value). In particular, P4does not have arithmetic exceptionsthe result of an arithmeticoperation is defined for all possible inputs.

P4 target architectures may optionally support saturating arithmetic. All saturatingoperations are limited to a fixed range between a minimum and maximum value.Saturating arithmetic has advantages, in particular when used as counters. Thethe result of a saturating counter max-ing out is much closer to the realresult than a counter that overflows and wraps around. According to WikipediaSaturating Arithmetic saturating arithmetic isas numerically close to the true answer as possible; for 8-bit binary signedarithmetic, when the correct answer is 130, it is considerably less surprisingto get an answer of 127 from saturating arithmetic than to get an answer of−126 from modular arithmetic. Likewise, for 8-bit binary unsigned arithmetic,when the correct answer is 258, it is less surprising to get an answer of 255from saturating arithmetic than to get an answer of 2 from modular arithmetic.At this time, P4 defines saturating operations only for addition andsubtraction. For an unsigned integer with bit-width ofW, the minimum valueis0 and the maximum value is2^W-1.The precedence of saturating addition and subtraction operations is thesame as for modulo arithmetic addition and subtraction.

All binary operations (except shifts) require both operands to havethe same exact type and width; supplying operands with differentwidths produces an error at compile time. No implicit casts areinserted by the compiler to equalize the widths. There are no binaryoperations that combine signed and unsigned values (except shifts).The following operations are provided on bit-string expressions:

Each of the following operations produces a bit-string result when appliedto bit-strings of the same width:

Bit-strings also support the following operations:

8.6. Operations on fixed-width signed integers

This section discusses all operations that can be performed on expressions of typeint<W>for someW. Recall that theint<W> denotes signedW-bit integers,represented using two's complement.

In general, P4 arithmetic operations do not detect“underflow” or“overflow”:operations simply“wrap around”, similar to C operations on unsigned values.Hence, attempting to represent large values usingW bits will only keepthe least-significantW bits of the value.

P4 supports saturating arithmetic (addition and subtraction) for signedintegers. Targets may optionally reject programs using saturating arithmetic.For a signed integer with bit-width ofW, the minimum value is-2^(W-1) and the maximum value is2^(W-1)-1.

P4 also does not support arithmetic exceptions. The runtime result of anarithmetic operation is defined for all combinations of inputarguments.

All binary operations (except shifts) require both operands to havethe same exact type (signedness) and width and supplying operands withdifferent widths or signedness produces a compile-time error. Noimplicit casts are inserted by the compiler to equalize thetypes. With the exception of shifts, P4 does not have any binary operations that combine signed and unsignedvalues.

Note that bitwise operations on signed integers are well-defined, since therepresentation is mandated to be two's complement.

Theint<W> datatype supports the following operations; allbinary operations require both operands to have the exact sametype. The result always has the same width as the left operand.

8.6.1. Concatenation

Concatenation is applied to two bit-strings (signed or unsigned). Itis denoted by the infix operator++. The result is a bit-stringwhose length is the sum of the lengths of the inputs where the mostsignificant bits are taken from the left operand; the sign of theresult is taken from the left operand.

8.6.2. A note about shifts

Shifts (on signed and unsigned values) deserve a special discussionfor the following reasons:

Consider the following examples:

bit<8> x;bit<16> y;bit<16> z = y << x;bit<16> w = y <<1024;

As mentioned above, P4 gives a precise meaning shifting with an amountlarger than the size of the shifted value, unlike C.

P4 targets may impose additional restrictions on shift operations suchas forbidding shifts by non-constant expressions, or by expressionswhose width exceeds a certain bound. For example, a target may forbidshifting an 8-bit value by a non-constant value whose width is greaterthan 3 bits.

8.7. Operations on arbitrary-precision integers

The typeint denotes arbitrary-precision integers. In P4, allexpressions of typeint must be compile-time known values. The typeint supports the following operations:

Each operand that participates in any of these operation must havetypeint. Binary operations cannotbe used to combine values of typeint with values of a fixed-widthtype. However, the compiler automatically inserts casts fromintto fixed-width types in certain situationssee Section 8.9.

All computations onint values are carried out without loss ofinformation. For example, multiplying two 1024-bit values may producea 2048-bit value (note that concrete representation ofintvalues is not specified).int values can be cast tobit<w>andint<w> values. Casting anint value to a fixed-widthtype will preserve the least-significant bits. If truncation causessignificant bits to be lost, the compiler should emit a warning.

Note: bitwise-operations (|,&,^,~) are notdefined on expressions of typeint. In addition, it is illegalto apply division and modulo to negative values.

Note: saturating arithmetic is not supported for arbitrary-precision integers.

8.8. Operations on variable-size bit types

To support parsing headers with variable-length fields, P4 offers atypevarbit. Each occurrence of the typevarbit has astatically-declared maximum width, as well as a dynamic width, whichmust not exceed the static bound. Prior to initialization avariable-size bit-string has an unknown dynamic width.

Variable-length bit-strings support a limited set of operations:

The following operations arenot supported directly on a value oftypevarbit, but instead on any type for whichextract andemitoperations are supported (e.g. a value with type header) that maycontain a field of typevarbit. They are mentioned here only to easefinding this information in a section dedicated to typevarbit.

8.9. Casts

P4 provides a limited set of casts between types. A cast is written(t) e, wheret is a type ande is an expression. Casts are onlypermitted between base types. While this design is arguably more onerousfor programmers, it has several benefits:

8.9.1. Explicit casts

The following casts are legal in P4:

8.9.2. Implicit casts

To keep the language simple and avoid introducing hidden costs, P4only implicitly casts fromint to fixed-width types and from enumswith an underlying type to the underlying type. In particular,applying a binary operation to an expression of typeint and anexpression with a fixed-width type will implicitly cast theintexpression to the type of the other expression.

For example, given the following declarations,

enumbit<8> E {   a =5;}bit<8>  x;bit<16> y;int<8>  z;

the compiler will add implicit casts as follows:

8.9.3. Illegal arithmetic expressions

Many arithmetic expressions that would be allowed in other languagesare illegal in P4. To illustrate, consider the following declarations:

bit<8>  x;bit<16> y;int<8>  z;

The table below shows several expressions which are illegal becausethey do not obey the P4 typing rules. For each expression we provideseveral ways that the expression could be manually rewritten into alegal expression. Note that for some expression there are severallegal alternatives, which may produce different results! The compilercannot guess the user intent, so P4 requires the user to disambiguate.

Expression Why it is illegal Alternatives
x + y Different widths(bit<16>)x + y
x + (bit<8>)y
x + z Different signs(int<8>)x + z
x + (bit<8>)z
(int<8>)y Cannot change both sign and width(int<8>)(bit<8>)y
(int<8>)(int<16>)y
y + z Different widths and signs(int<8>)(bit<8>)y + z
y + (bit<16>)(bit<8>)z
(bit<8>)y + (bit<8>)z
(int<16>)y + (int<16>)z
x << z RHS of shift cannot be signedx << (bit<8>)z
x < z Different signsX < (bit<8>)z
(int<8>)x < z
1 << x Width of1 is unknown32w1 << x
~1 Bitwise operation on int~32w1
5 & -3 Bitwise operation on int32w5 & -3

8.10. Operations on tuples expressions

Tuples can be assigned to other tuples with the same type, passed asarguments and returned from functions, and can be initialized withlist expressions.

tuple<bit<32>,bool> x = {10,false };

The fields of a tuple can be accessed using array index syntaxx[0],x[1]. The array indexesmust be compile-time constants, to enablethe type-checker to identify the field types statically.

Currently tuple fields are not left-values, even if the tuple itselfis. (I.e. a tuple can only be assigned monolithically, and the fieldvalues cannot be changed individually.) This restriction may belifted in a future version of the language.

8.11. Operations on lists

A list expression is written using curly braces, with each elementseparated by a comma:

expression ...    |'{' expressionList'}'expressionList    :/* empty*/    | expression    | expressionList',' expression    ;

The type of a list expression is a tuple type (Section7.2.8). List expressions can be assigned to expressionsof typetuple,struct orheader, and can also bepassed as arguments to methods. Lists may be nested. However, listexpressions are not l-values.

For example, the following program fragment uses a list expression topass several header fields simultaneously to a learning provider:

extern LearningProvider {void learn<T>(in T data);}LearningProvider() lp;lp.learn( { hdr.ethernet.srcAddr, hdr.ipv4.src } );

A list may be used to initialize a structure if the list has the samenumber of elements as fields in the structure. The effect of such aninitializer is to assign to the ith element of the list to the ithfield in the structure:

struct S {bit<32> a;bit<32> b;}const S x = {10,20 };//a = 10, b = 20

List expressions can also be used to initialize variables whose typeis atuple type.

tuple<bit<32>,bool> x = {10,false };

The empty list expression has typetuple<>- a tuple with no components.

8.12. Structure-valued expressions

One can write expressions that evaluate to a structure or header. Thesyntax of these expressions is given by:

expression ...    |'{' kvList'}'    |'(' typeRef')' expression    ;kvList    : kvPair    | kvList"," kvPair    ;kvPair    : name"=" expression    ;

For a structure-valued expressiontypeRef is the name of astructorheader type. ThetypeRef can be omitted if it can be inferredfrom context, e.g., when initializing a variable with astruct type.The following example shows a structure-valued expression used in anequality comparison expression:

struct S {bit<32> a;bit<32> b;}S s;// Compare s with a structure-valued expressionbool b = s == (S) { a =1, b =2 };

Structure-valued expressions can be used in the right-hand side ofassignments, in comparisons, in field selection expressions, and asarguments to functions, method or actions. Structure-valuedexpressions are not left values.

8.13. Operations on sets

Some P4 expressions denote sets of values (set<T>, for some typeT;see Section 7.2.8.1). These expressions canappear only in a few contextsparsers and constant tableentries. For example, theselect expression (Section12.6) has the following structure:

select (expression) {   set1: state1;   set2: state2;// More labels omitted}

Here the expressionsset1, set2, etc. evaluate to sets of valuesand theselect expression tests whetherexpression belongs tothe sets used as labels.

keysetExpression    : tupleKeysetExpression    | simpleKeysetExpression    ;tupleKeysetExpression    :"(" simpleKeysetExpression"," simpleExpressionList")"    |"(" reducedSimpleKeysetExpression")"    ;simpleExpressionList    : simpleKeysetExpression    | simpleExpressionList',' simpleKeysetExpression    ;reducedSimpleKeysetExpression    : expression"&&&" expression    | expression".." expression    | DEFAULT    |"_"    ;simpleKeysetExpression    : expression    | DEFAULT    | DONTCARE    | expression MASK expression    | expression RANGE expression    ;

The mask (&&&) and range (..) operators have the sameprecedence, which is just higher than&.

8.13.1. Singleton sets

In a set context, expressions denote singleton sets. For example, inthe following program fragment,

select (hdr.ipv4.version) {4: continue;}

The label4 is denotes the singleton set containing4.

8.13.2. The universal set

In a set context, the expressionsdefault or_ denote theuniversal set, which contains all possible values of a given type:

select (hdr.ipv4.version) {4: continue;   _: reject;}

8.13.3. Masks

The infix operator&&& takes two arguments of typebit<W> orserializableenum, and creates a value of typeset<ltype>, whereltype is the type of the left argument. The right value is used as a“mask”, where each bit set to0 in the mask indicates a“don't care”bit. More formally, the set denoted bya &&& b is defined asfollows:

a &&& b = { c of typebit<W> where a & b = c & b }

For example:

8w0x0A &&&8w0x0F

denotes a set that contains 16 different 8-bit values, whosebit-pattern isXXXX1010, where the value of anX can beany bit. Note that there may be multiple ways to express a keysetusing a mask operatore.g.,8w0xFA &&&8w0x0F denotes the samekeyset as in the example above.

P4 architectures may impose additional restrictions on the expressionson the left and right-hand side of a mask operator: for example, theymay require that either or both sub-expressions be compile-time knownvalues.

8.13.4. Ranges

The infix operator.. takes two arguments of the same typeT,whereT is eitherbit<W> orint<W>, and creates a value oftypeset<T>. The set contains all values numerically between thefirst and the second, inclusively. For example:

4w5 ..4w8

denotes a set with values4w5,4w6,4w7, and4w8.

8.13.5. Products

Multiple sets can be combined using Cartesian product:

select(hdr.ipv4.ihl, hdr.ipv4.protocol) {     (4w0x5,8w0x1): parse_icmp;     (4w0x5,8w0x6): parse_tcp;     (4w0x5,8w0x11): parse_udp;     (_, _): accept; }

The type of a product of sets is a set of tuples.

8.14. Operations on struct types

The only operation defined on expressions whose type is astruct isfield access, written using dot (“.”) notatione.g.,s.field. Ifs is an l-value, thens.field is also an l-value. P4 also allowscopyingstructs using assignment when the source and target of theassignment have the same type. Finally,structs can be initializedwith a list expression, as discussed in Section 8.11, orwith a structure initializer, as described in8.15. Both these cases must initialize allfields of the structure.

Two structs can be compared for equality (==) or inequality (!=) onlyif they have the same type and all of their fields can be recursivelycompared for equality. Two structures are equal if and only if alltheir corresponding fields are equal.

8.15. Structure initializers

Structures can be initialized using structure-valued expression(8.12). The following example shows astructure initialized using a structure-valued expression:

struct S {bit<32> a;bit<32> b;}const S x = { a =10, b =20 };const S x = (S){ a =10, b =20 };// equivalent

The compiler must raise an error if a field name appears more thanonce in the same structure initializer.

See Section 8.22for a description of the behavior ifstruct fields are read withoutbeing initialized.

8.16. Operations on headers

Headers provide the same operations asstructs. Assignment betweenheaders also copies the“validity” header bit.

In addition, headers support the following methods:

The expressionh.minSizeInBits() is defined for any valueh that has aheader type. The expression is equal to the sum of the sizes of allof headerh's fields in bits, counting allvarbit fields as length0. An expressionh.minSizeInBits() is a compile-time constant with typeint.

The expressionh.minSizeInBytes() is similar toh.minSizeInBits(), exceptthat it returns the total size of all of the header's fields in bytes,rounding up to the next whole number of bytes if the header's size isnot a multiple of 8 bits long.h.minSizeInBytes() is equal to(h.minSizeInBits() +7) >>3.

Similar to astruct, a header object can be initialized with a listexpression 8.11 the list fields are assigned to theheader fields in the order they appear or with a structure initializerexpression 8.14. When initialized the headerautomatically becomes valid:

header H {bit<32> x;bit<32> y; }H h;h = {10,12 };// This also makes the header h validh = { y =12, x =10 };// Same effect as above

Two headers can be compared for equality (==) or inequality (!=) onlyif they have the same type. Two headers are equal if and only if theyare both invalid, or they are both valid and all their correspondingfields are equal.

See Section 8.22for a description of the behavior if header fields are read withoutbeing initialized, or header fields are written to a currently invalidheader.

8.17. Operations on header stacks

A header stack is a fixed-size array of headers with the sametype. The valid elements of a header stack need not be contiguous. P4provides a set of computations for manipulating header stacks. Aheader stackhs of typeh[n] can be understood in terms ofthe following pseudocode:

// type declarationstruct hs_t {bit<32> nextIndex;bit<32> size;  h[n] data;// Ordinary array}// instance declaration and initializationhs_t hs;hs.nextIndex =0;hs.size = n;

Intuitively, a header stack can be thought of as a struct containingan ordinary array of headershs and a counternextIndexthat can be used to simplify the construction of parsers for headerstacks, as discussed below. ThenextIndex counter is initializedto0.

Given a header stack valuehs of sizen, the followingexpressions are legal:

To help programmers write parsers for header stacks, P4 also offerscomputations that automatically advance through the stack as elementsare parsed:

Finally, P4 offers the following computations that can be used tomanipulate the elements at the front and back of the stack:

The following pseudocode defines the behavior ofpush_front andpop_front:

void push_front(int count) {    for (int i = this.size-1; i >=0; i -=1) {if (i >= count) {            this[i] = this[i-count];        }else {            this[i].setInvalid();        }    }    this.nextIndex = this.nextIndex + count;if (this.nextIndex > this.size) this.nextIndex = this.size;// Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex}void pop_front(int count) {    for (int i =0; i < this.size; i++) {if (i+count < this.size) {            this[i] = this[i+count];        }else {            this[i].setInvalid();        }    }if (this.nextIndex >= count) {        this.nextIndex = this.nextIndex - count;    }else {        this.nextIndex =0;    }// Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex}

Two header stacks can be compared for equality (==) or inequality (!=)only if they have the same element type and the same length. Twostacks are equal if and only if all their corresponding elements areequal. Note that thenextIndex value is not used in the equality comparison.

8.18. Operations on header unions

A variable declared with a union type is initially invalid. For example:

header H1 {bit<8> f;}header H2 {bit<16> g;}header_union U {  H1 h1;  H2 h2;}U u;// u invalid

This also implies that each of the headersh1 throughhn containedin a header union are also initially invalid. Unlike headers, a unioncannot be initialized. However, the validity of a header union can beupdated by assigning a valid header to one of its elements:

U u;H1 my_h1 = {8w0 };// my_h1 is validu.h1 = my_h1;// u and u.h1 are both valid

We can also assign a list to an element of a header union,

U u;u.h2 = {16w1 };// u and u.h2 are both valid

or set their validity bits directly.

U u;u.h1.setValid();// u and u.h1 are both validH1 my_h1 = u.h1;// my_h1 is now valid, but contains an undefined value

Note that reading an uninitialized header produces an undefined value,even if the header is itself valid.

More formally, ifu is an expression whose type is a header unionU with fields ranged over byhi, then the followingoperations can be used to manipulateu:

We can understand an assignment to a union

u.hi = e

as equivalent to

  u.hi.setValid();  u.hi = e;

ife is valid and

  u.hi.setInvalid();

otherwise.

Assignments between variables of the same type of header union arepermitted. The assignmentu1 = u2 copies the full state of headerunionu2 tou1. Ifu2 is valid, then there is some headeru2.hi that is valid. The assignment behaves the same asu1.hi = u2.hi.Ifu2 is not valid, thenu1 becomes invalid (i.e. if anyheader ofu1 was valid, it becomes invalid).

u.isValid() returns true if any member of the header unionu isvalid, otherwise it returns false.setValid() andsetInvalid()methods are not defined for header unions.

Supplying an expression with a union type toemit simply emits thesingle header that is valid, if any.

The following example shows how we can use header unions to representIPv4 and IPv6 headers uniformly:

header_union IP {    IPv4 ipv4;    IPv6 ipv6;}struct Parsed_packet {   Ethernet ethernet;   IP ip;}parser top(packet_in b,out Parsed_packet p) {state start {       b.extract(p.ethernet);transitionselect(p.ethernet.etherType) {16w0x0800 : parse_ipv4;16w0x86DD : parse_ipv6;       }   }state parse_ipv4 {       b.extract(p.ip.ipv4);transition accept;   }state parse_ipv6 {       b.extract(p.ip.ipv6);transition accept;   }}

As another example, we can also use unions to parse (selected) TCPoptions:

header Tcp_option_end_h {bit<8> kind;}header Tcp_option_nop_h {bit<8> kind;}header Tcp_option_ss_h {bit<8>  kind;bit<32> maxSegmentSize;}header Tcp_option_s_h {bit<8>  kind;bit<24> scale;}header Tcp_option_sack_h {bit<8>         kind;bit<8>         length;varbit<256>    sack;}header_union Tcp_option_h {    Tcp_option_end_h  end;    Tcp_option_nop_h  nop;    Tcp_option_ss_h   ss;    Tcp_option_s_h    s;    Tcp_option_sack_h sack;}typedef Tcp_option_h[10] Tcp_option_stack;struct Tcp_option_sack_top {bit<8> kind;bit<8> length;}parser Tcp_option_parser(packet_in b,out Tcp_option_stack vec) {state start {transitionselect(b.lookahead<bit<8>>()) {8w0x0 : parse_tcp_option_end;8w0x1 : parse_tcp_option_nop;8w0x2 : parse_tcp_option_ss;8w0x3 : parse_tcp_option_s;8w0x5 : parse_tcp_option_sack;        }    }state parse_tcp_option_end {        b.extract(vec.next.end);transition accept;    }state parse_tcp_option_nop {         b.extract(vec.next.nop);transition start;    }state parse_tcp_option_ss {         b.extract(vec.next.ss);transition start;    }state parse_tcp_option_s {         b.extract(vec.next.s);transition start;    }state parse_tcp_option_sack {bit<8> n = b.lookahead<Tcp_option_sack_top>().length;// n is the total length of the TCP SACK option in bytes.// The length of the varbit field 'sack' of the// Tcp_option_sack_h header is thus n-2 bytes.         b.extract(vec.next.sack, (bit<32>) (8 * n -16));transition start;    }}

Two header unions can be compared for equality (==) or inequality (!=)if they have the same type. The unions are equal if and only if alltheir corresponding fields are equal (i.e., either all fields areinvalid in both unions, or in both unions the same field is valid, andthe values of the valid fields are equal as headers).

8.19. Method invocations and function calls

Method invocations and function calls can be invoked using thefollowing syntax:

expression    : ...    | expression'<' realTypeArgumentList'>''(' argumentList')'    | expression'(' argumentList')'argumentList    :/* empty*/    | nonEmptyArgList    ;nonEmptyArgList    : argument    | nonEmptyArgList',' argument    ;argument    : expression/* positional argument*/    | name'=' expression/* named argument*/    | DONTCARE    ;realTypeArgumentList    : realTypeArg    | realTypeArgumentList',' typeArg    ;realTypeArg    : DONTCARE    | typeRef    | VOID    ;

A function call or method invocation can optionally specify for eachargument the corresponding parameter name. It is illegal to use namesonly for some arguments: either all or no arguments should specifythe parameter name. Function arguments are evaluated in the orderthey appear, left to right, before the function invocation takesplace.

externvoid f(inbit<32> x,outbit<16> y);bit<32> xa =0;bit<16> ya;f(xa, ya);// match arguments by positionf(x = xa, y = ya);// match arguments by namef(y = ya, x = xa);// match arguments by name in any order//f(x = xa);  -- error: enough arguments//f(x = xa, x = ya);  -- error: argument specified twice//f(x = xa, ya);  -- error: some arguments specified by name//f(z = xa, w = yz);  -- error: no parameter named z or w//f(x = xa, y = 0);  -- error: y must be a left-value

The calling convention is copy-in/copy-out (Section6.7). For generic functions the type argumentscan be explicitly specified in the function call. The compiler onlyinserts implicit casts for directionin arguments to methods orfunctions as described in Section 8.9. The types for allother arguments must match the parameter types exactly.

The result returned by a function call is discarded when the functioncall is used as a statement.

The“don't care” identifier (_) can only be used for anoutfunction/method argument, when the value of returned in that argumentis ignored by subsequent computations. When used in generic functionsor methods, the compiler may reject the program if it is unable toinfer a type for the don't care argument.

8.20. Constructor invocations

Several P4 constructs denote resources that are allocated atcompilation time:

Allocation of such objects can be performed in two ways:

The syntax for a constructor invocation is similar to a function call;constructors can also be called using named arguments. Constructorsare evaluated entirely at compilation-time (see Section17). In consequence, all constructor argumentsmust also be expressions that can be evaluated at compilation time.

The following example shows a constructor invocation for setting thetarget-dependent implementation property of a table:

extern ActionProfile {    ActionProfile(bit<32> size);// constructor}table tbl {    actions = {/* body omitted*/ }    implementation = ActionProfile(1024);// constructor invocation}

8.21. Operations on types introduced bytype

Values with a type introduced by thetype keyword provide only few operations:

typebit<32> U32;U32 x = (U32)0;// cast neededU32 y = (U32) ((bit<32>)x +1);// casts needed for arithmeticbit<32> z =1;bool b0 = x == (U32)z;// cast neededbool b1 = (bit<32>)x == z;// cast neededbool b2 = x == y;// no cast needed

8.22. Reading uninitialized values and writing fields of invalid headers

As mentioned in Section 8.17, any reference to an element ofa header stackhs[index] whereindex is a compile-time constantexpression must give an error at compile time if the value of theindex is out of range. That section also defines the run timebehavior of the expressionshs.next andhs.last, and the behaviorsspecified there take precedence over anything in this section forthose expressions.

All mentions of header stack elements in this section only apply forexpressionshs[index] whereindex is a run time variableexpression, i.e. not a compile-time constant value. A P4implementation is allowed not to supporths[index] whereindex isa run time variable expression, but if it does support theseexpressions, the implementation should conform to the behaviorsspecified in this section.

The result of reading a value in any of the situations below is thatsome unspecified value will be used for that field.

Calling theisValid() method on an element of a header stack, wherethe index is out of range, returns an undefined boolean value, i.e.,it is eithertrue orfalse, but the specification does not requireone or the other, nor that a consistent value is returned acrossmultiple such calls. Assigning an out of range header stack elementto another header variableh leads to a state whereh is undefinedin all of its field values, and its validity is also undefined.

Where a header is mentioned, it may be a member of aheader_union,an element in a header stack, or a normal header. This unspecifiedvalue could differ from one such read to another.

For an uninitialized field or variable with a type ofenum orerror, the unspecified value that is read might not be equal to anyof the values defined for that type. Such an unspecified value shouldstill lead to predictable behavior in cases where any legal valuewould match, e.g. it should match in any of these situations:

Consider a situation where aheader_unionu1 has member headersu1.h1 andu1.h2, and at a given point in the program's executionu1.h1 is valid andu1.h2 is invalid. If a write is attempted to afield of the invalid member headeru1.h2, then any or all of thefields of the valid member headeru1.h1 may change as a result.Such a write must not change the validity of any member headers ofu1, nor any other state that is currently defined in the system,whether it is defined state in header fields or anywhere else.

If any of these kinds of writes are performed:

then that write must not change any state that is currently defined inthe system, neither in header fields nor anywhere else. Inparticular, if an invalid header is involved in the write, it mustremain invalid.

Any writes to fields in a currently invalid header, or to header stackelements where the index is out of range, are allowed to modify statewhose values are not defined, e.g. the values of fields in headersthat are currently invalid.

For a top levelparser orcontrol in an architecture, it is up tothat architecture to specify whether parameters withdirectionin orinout are initialized when the control is called,and under what conditions they are initialized, and if so, what theirvalues will be.

Since P4 allows empty tuples and structs, one can construct typeswhose values carry no“useful” information, e.g.:

struct Empty {tuple<> t;}

We call the following“empty” types:

Values with empty types carry no useful information. In particular,they do not have to be explicitly initialized to have a legal value.

(Header types with no fields always have a validity bit.)

8.23. Initializing with default values

A left-value can be initialized automatically with a default value of thesuitable type using the syntax... (see Section 7.3). A valueof typestruct,header, ortuple can also be initialized using a mix ofexplicit values and default values by using the notation... in a listexpression initializer; in this case all fields not explicitlyinitialized are initialized with default values. When initializing astruct,header, andtuple with a value containing partially default valuesusing the... notation the three dots must appear last in the initializer.

struct S {bit<32> b32;bool b;}enumint<8> N0 {   one =1,   zero =0,   two =2}enum N1 {     A, B, C, F}struct T {    S s;    N0 n0;    N1 n1;}header H {bit<16> f1;bit<8> f2;}N0 n0 = ...;// initialize n0 with the default value 0N1 n1 = ...;// initialize n1 with the default value N1.AS s0 = ...;// initialize s0 with the default value { 0, false }S s1 = {1, ... };// initialize s1 with the value { 1, false }S s2 = { b =true, ... };// initialize s2 with the value { 0, true }T t0 = ...;// initialize t0 with the value { { 0, false }, 0, N1.A }T t1 = { s = ..., ... };// initialize t1 with the value { { 0, false }, 0, N1.A }T t2 = { s = ... };// error: no initializer specified for fields n0 and n1tuple<N0, N1> p = { ... };// initialize p with default value { 0, N1.A }T t3 = { ..., n0 =2};// error: ... must be lastH h1 = ...;// initialize h1 with a header that is invalidH h2 = { f2=5, ... };// initialize h2 with a header that is valid, field f1 0, field f2 5H h3 = { ... };// initialize h3 with a header that is valid, field f1 0, field f2 0

9. Function declarations

Functions can only be declared at the top-level and all parameters must have a direction.P4 functions are modeled after functions as found in most other programming languages,however, the language does not permit recursive functions.

functionDeclaration    : functionPrototype blockStatement    ;functionPrototype    : typeOrVoid name optTypeParameters'(' parameterList')'    ;

Here is an example of a function that returns the maximum of two 32-bit values:

bit<32> max(inbit<32> left,inbit<32> right) {if (left > right)return left;return right;}

A function returns a value using thereturn statement. A functionthat returnsvoid can simply use thereturn statement with noarguments. A function with a non-void return type must return a valueof the suitable type on all possible execution paths.

10. Constants and variable declarations

10.1. Constants

Constant values are defined with the syntax:

constantDeclaration    : optAnnotations CONST typeRef name'=' initializer';'    ;initializer    : expression    ;

Such a declaration introduces a constant whose value has the specified type. Thefollowing are all legal constant declarations:

constbit<32> COUNTER =32w0x0;struct Version {bit<32> major;bit<32> minor;}const Version version = {32w0,32w0 };

Theinitializer expression must be a compile-time known value.

10.2. Variables

Local variables are declared with a type, a name, and an optionalinitializer (as well as an optional annotation):

variableDeclaration    : annotations typeRef name optInitializer';'    | typeRef name optInitializer';'    ;optInitializer    :/* empty*/    |'=' initializer    ;

Variable declarations without an initializer are uninitialized (except forheaders and other header-related types, which are initialized to invalid in thesame way as described for directionout parameters in Section6.7). The language places few restrictions onthe types of the variables: most P4 types that can be writtenexplicitly can be used (e.g., base types,struct,header,header stack,tuple). However, it is impossible to declare variables with typesthat are only synthesized by the compiler (e.g.,set). In addition, variables of typeparser,control,package,orextern types must be declared using instantiations (see Section 10.3).

Reading the value of a variable that has not been initialized yieldsan undefined result. The compiler should attempt to detect and emit a warning in such situations.

Variables declarations can appear in the following locations within a P4program:

Variables have local scope, and behave like stack-allocated variables inlanguages such as C. The value of a variable is never preserved fromone invocation of its enclosing block to the next. In particular, variables cannotbe used to maintain state between different network packets.

10.3. Instantiations

Instantiations are similar to variable declarations, but arereserved for the types with constructors (extern objects,controlblocks,parsers, andpackages):

instantiation    : typeRef'(' argumentList')' name';'    | annotations typeRef'(' argumentList')' name';'    ;

An instantiation is written as a constructor invocation followed by aname. Instantiations are always executed at compilation-time (Section17.1). The effect is to allocate an object with thespecified name, and to bind it to the result of the constructorinvocation. Note that instantiation arguments can be specified by name.

For example, a hypothetical bank of counter objects can beinstantiated as follows:

// from target libraryenum CounterType {   Packets,   Bytes,   Both}extern Counter {    Counter(bit<32> size, CounterType type);void increment(inbit<32> index);}// user programcontrol c(/* parameters omitted*/) {    Counter(32w1024, CounterType.Both) ctr;// instantiationapply {/* body omitted*/ }}

10.3.1. Instantiating objects with abstract methods

When instantiating an extern type that hasabstract methods usershave to supply implementations for all such methods. This is done using object initializers:

lvalue:    ...    | THISexpression:    ...    | THISinstantiation:      ...      | annotations typeRef"(" argumentList")" name"=" objInitializer";"      | typeRef"(" argumentList")" name"=" objInitializer";"objInitializer    :"{" objDeclarations"}"    ;objDeclarations    :/* empty*/    | objDeclarations objDeclaration    ;objDeclaration    : functionDeclaration    | instantiation    ;

The abstract methods can only use the supplied arguments or refer tovalues that are in the top-level scope. When calling another methodof the same instance thethis keyword is used to indicate thecurrent object instance:

// Instantiate a balancerBalancer() b = {// provide an implementation for the abstract methodsbit<4> on_new_flow(inbit<32> address) {// uses the address and the number of flows to load balancebit<32> count = this.getFlowCount();// call method of the same instancereturn (address + count)[3:0];    }}

Abstract methods may be invoked by users explicitly, or they may beinvoked by the target architecture. The architectural description hasto specify when the abstract methods are invoked and what the meaningof their arguments and return values is; target architectures mayimpose additional constraints on abstract methods.

10.3.2. Restrictions on top-level instantiations

A P4 program may not instantiate controls and parsers at the top-levelpackage. This restriction is designed to ensure that most stateresides in the architecture itself, or is local to aparser orcontrol.For example, the following program is not valid:

// Programcontrol c(/* parameters omitted*/) {/* body omitted*/ }c() c1;// illegal top-level instantiation

because controlc1 is instantiated at the top-level. Note that top-level declarations ofconstants and instantiations of extern objects are permitted.

11. Statements

Every statement in P4 (except block statements) must end with asemicolon.Statements can appear in several places:

There are restrictions for the kinds of statements that can appear ineach of these places. For example,returns are not supported inparsers, andswitch statements are only supported in controlblocks. We present here the most general case, for control blocks.

statement    : assignmentOrMethodCallStatement    | conditionalStatement    | emptyStatement    | blockStatement    | exitStatement    | returnStatement    | switchStatement    ;assignmentOrMethodCallStatement    : lvalue'(' argumentList')'';'    | lvalue'<' typeArgumentList'>''(' argumentList')'';'    | lvalue'='  expression';'    ;

In addition, parsers support atransition statement (Section12.5).

11.1. Assignment statement

An assignment, written with the= sign, first evaluates its leftsub-expression to an l-value, then evaluates its right sub-expressionto a value, and finally copies the value into the l-value. Derivedtypes (e.g.structs) are copied recursively, and all componentsofheaders are copied, including“validity” bits. Assignment isnot defined forextern values.

11.2. Empty statement

The empty statement, written; is a no-op.

emptyStatement    :';'    ;

11.3. Block statement

A block statement is denoted by curly braces. It contains asequence of statements and declarations, which are executedsequentially. The variables, constants, and instantiations within ablock statement are only visible within the block.

blockStatement    : optAnnotations'{' statOrDeclList'}'    ;statOrDeclList    :/* empty*/    | statOrDeclList statementOrDeclaration    ;statementOrDeclaration    : variableDeclaration    | constantDeclaration    | statement    | instantiation    ;

11.4. Return statement

Thereturn statement immediately terminates the execution of theaction,function orcontrol containing it.return statementsare not allowed within parsers.return statements followed by anexpression are only allowed within functions that return values; inthis case the type of the expression must match the return type of thefunction. Any copy-out behavior due to directionout orinoutparameters of the enclosingaction,function, orcontrol arestill performed after the execution of thereturn statement. SeeSection 6.7 for details on copy-out behavior.

returnStatement    : RETURN';'    | RETURN expression';'    ;

11.5. Exit statement

Theexit statement immediately terminates the execution of allthe blocks currently executing: the currentaction (if invokedwithin anaction), the currentcontrol, and all itscallers.exit statements are not allowed within parsers orfunctions.

Any copy-out behavior due to directionout orinout parameters ofthe enclosingaction orcontrol, and all of its callers, are stillperformed after the execution of theexit statement. See Section6.7 for details on copy-out behavior.

exitStatement    : EXIT';'    ;

11.6. Conditional statement

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 notan integer). Conditional statements may not be used within aparser.

conditionalStatement    : IF'(' expression')' statement    | IF'(' expression')' statement ELSE statement    ;

When severalif statements are nested, theelse applies to theinnermostif statement that does not have anelse statement.

11.7. Switch statement

Theswitch statement can only be used withincontrol blocks.

switchStatement    : SWITCH'(' expression')''{' switchCases'}'    ;switchCases    :/* empty*/    | switchCases switchCase    ;switchCase    : switchLabel':' blockStatement    | switchLabel':'// fall-through    ;switchLabel    : DEFAULT    | nonBraceExpression    ;nonBraceExpression    : INTEGER    | TRUE    | FALSE    | STRING_LITERAL    | nonTypeName    | dotPrefix nonTypeName    | nonBraceExpression'[' expression']'    | nonBraceExpression'[' expression':' expression']'    |'(' expression')'    |'!' expression %prec PREFIX    |'~' expression %prec PREFIX    |'-' expression %prec PREFIX    |'+' expression %prec PREFIX    | typeName'.' member    | ERROR'.' member    | nonBraceExpression'.' member    | nonBraceExpression'*' expression    | nonBraceExpression'/' expression    | nonBraceExpression'%' expression    | nonBraceExpression'+' expression    | nonBraceExpression'-' expression    | nonBraceExpression'|+|' expression    | nonBraceExpression'|-|' expression    | nonBraceExpression'<<' expression    | nonBraceExpression'>>' expression    | nonBraceExpression'<=' expression    | nonBraceExpression'>=' expression    | nonBraceExpression'<' expression    | nonBraceExpression'>' expression    | nonBraceExpression'!=' expression    | nonBraceExpression'==' expression    | nonBraceExpression'&' expression    | nonBraceExpression'^' expression    | nonBraceExpression'|' expression    | nonBraceExpression'++' expression    | nonBraceExpression'&&' expression    | nonBraceExpression'||' expression    | nonBraceExpression'?' expression':' expression    | nonBraceExpression'<' realTypeArgumentList'>''(' argumentList')'    | nonBraceExpression'(' argumentList')'    | namedType'(' argumentList')'    |'(' typeRef')' expression    ;

ThenonBraceExpression is the same asexpression as defined inSection 8, except it does not include any cases that canbegin with a left brace{ character, to avoid syntactic ambiguitywith a block statement.

There are two kinds ofswitch expressions allowed, describedseparately in the following two subsections.

11.7.1. Switch statement withaction_run expression

For this variant ofswitch statement, the expression must be of theformt.apply().action_run, wheret is the name of a table (seeSection 13.2.2). All switch labels must be names ofactions of the tablet, ordefault.

switch (t.apply().action_run) {   action1:// fall-through to action2:   action2: {/* body omitted*/ }   action3: {/* body omitted*/ }// no fall-through from action2 to action3 labelsdefault: {/* body omitted*/ }}

Note that thedefault label of theswitch statement is used tomatch on the kind of action executed, no matter whether there was atable hit or miss. Thedefault label does not indicate that thetable missed and thedefault_action was executed.

11.7.2. Switch statement with integer or enumerated type expression

For this variant ofswitch statement, the expression must evaluateto a result with one of these types:

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 theswitch expression (see Section 8.9.2). Switchlabels must not begin with a left brace character{, to avoidambiguity with a block statement.

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

11.7.3. Notes common to all switch statements

It is a compile-time error if two labels of aswitch statement equaleach other. The switch label values need not include all possiblevalues of the switch expression. It is optional to have aswitchcase with thedefault label, but if one is present, it must be thelast one in theswitch statement.

If a switch label is not followed by a block statement, it fallsthrough to the next label. However, if a block statement is present,it does not fall through. Note that this is different from C-styleswitch statements, where abreak is needed to preventfall-through. If the last switch label is not followed by a blockstatement, the behavior is the same as if the last switch label werefollowed by an empty block statement{ }.

When aswitch statement is executed, first the switch expression isevaluated, and any side effects from evaluating this expression arevisible to anyswitch case that is executed. Among switch labelsthat are notdefault, at most one of them can equal the value of theswitch expression. If one is equal, that switch case is executed.

If no labels are equal to theswitch expression, then:

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

12. Packet parsing

This section describes the P4 constructs specific to parsing network packets.

12.1. Parser states

parserstatemachine


Figure 8. Parser FSM structure.

A P4 parser describes a state machine with one start state and twofinal states. The start state is always namedstart. The twofinal states are namedaccept (indicating successful parsing)andreject (indicating a parsing failure). Thestart stateis part of the parser, while theaccept andreject statesare distinct from the states provided by the programmer and are logically outside of the parser. Figure 8illustrates the general structure of a parser state machine.

12.2. Parser declarations

A parser declaration comprises a name, a list of parameters, anoptional list of constructor parameters, local elements, and parserstates (as well as optional annotations).

parserTypeDeclaration    : optAnnotations PARSER name optTypeParameters'(' parameterList')'    ;parserDeclaration    : parserTypeDeclaration optConstructorParameters'{' parserLocalElements parserStates'}'    ;parserLocalElements    :/* empty*/    | parserLocalElements parserLocalElement    ;parserStates    : parserState    | parserStates parserState    ;

For a description ofoptConstructorParameters, which are useful forbuilding parameterized parsers, see Section 14.

Unlike parser type declarations, parser declarations may not begenerice.g., the following declaration is illegal:

parser P<H>(inout H data) {/* body omitted*/ }

Hence, used in the context of aparserDeclaration the productionruleparserTypeDeclaration should not yield type parameters.

At least one state, namedstart, must be present in anyparser.A parser may not define two states with the same name.It is also illegal for a parser to give explicit definitions fortheaccept andreject statesthosestates are logically distinct from the states defined by the programmer.

State declarations are described below. Preceding the parser states, aparsermay also contain a list of local elements. These can be constants,variables, or instantiations of objects that may be used within theparser. Such objects may be instantiations ofextern objects, orotherparsers that may be invoked as subroutines. However, it is illegal toinstantiate acontrol block within aparser.

parserLocalElement    : constantDeclaration    | variableDeclaration    | valueSetDeclaration    | instantiation    ;

For an example containing a complete declaration of a parser seeSection 5.3.

12.3. The Parser abstract machine

The semantics of a P4 parser can be formulated in terms of an abstractmachine that manipulates aParserModel data structure. This section describesthis abstract machine in pseudo-code.

A parser starts execution in thestart state and ends executionwhen one of thereject oraccept states has been reached.

ParserModel {error       parseError;    onPacketArrival(packet p) {        ParserModel.parseError =error.NoError;        goto start;    }}

An architecture must specify the behavior when theacceptandreject states are reached. For example, an architecture mayspecify that all packets reaching thereject state are droppedwithout further processing. Alternatively, it may specify thatsuch packets are passed to the next block after theparser, with intrinsic metadata indicating that the parser reachedthereject state, along with the error recorded.

12.4. Parser states

A parser state is declared with the following syntax:

parserState    : optAnnotations STATE name'{' parserStatements transitionStatement'}'    ;

Each state has a name and a body. The body consists of a sequence ofstatements that describe the processing performed when the parsertransitions to that state including:

The syntax for parser statements is given by the following grammar rules:

parserStatements    :/* empty*/    | parserStatements parserStatement    ;parserStatement    : assignmentOrMethodCallStatement    | directApplication    | variableDeclaration    | constantDeclaration    | parserBlockStatement    | emptyStatement    | conditionalStatement    ;parserBlockStatement    : optAnnotations'{' parserStatements'}'    ;

Architectures may place restrictions on the expressions and statementsthat can be used in a parsere.g., they may forbid the use ofoperations such as multiplication or place restrictions on the numberof local variables that may be used.

In terms of theParserModel, the sequence of statements in a stateare executed sequentially.

12.5. Transition statements

The last statement in a parser state is an optionaltransitionstatement, which transfers control to another state, possiblyacceptorreject. Atransition statements is written using thefollowing syntax:

transitionStatement    :/* empty*/    | TRANSITION stateExpression    ;stateExpression    : name';'    | selectExpression    ;

The execution of the transition statement causesstateExpressionto be evaluated, and transfers control to the resulting state.

In terms of theParserModel, the semantics of atransitionstatement can be formalized as follows:

goto eval(stateExpression)

For example, this statement:

transition accept;

terminates execution of the current parser and transitions immediately totheaccept state.

If the body of a state block does not end with atransitionstatement, the implied statement is

transition reject;

12.6. Select expressions

Aselect expression evaluates to a state. The syntax for aselectexpression is as follows:

selectExpression    : SELECT'(' expressionList')''{' selectCaseList'}'    ;selectCaseList    :/* empty*/    | selectCaseList selectCase    ;selectCase    : keysetExpression':' name';'    ;

In aselect expression, if theexpressionList has typetuple<T>,then eachkeysetExpression must have typeset<tuple<T>>.

In terms of theParserModel, the meaning of a select expression:

select(e) {    ks[0]: s[0];    ks[1]: s[1];/* more labels omitted*/    ks[n-2]: s[n-1];    _ : sd;// ks[n-1] is default}

is defined in pseudo-code as:

key = eval(e);for (int i=0; i < n; i++) {    keyset = eval(ks[i]);if (keyset.contains(key))return s[i];}verify(false,error.NoMatch);

Some targets may require that all keyset expressions in a selectexpression be compile-time known values. Keysets are evaluated inorder, from top to bottom as implied by the pseudo-code above; thefirst keyset that includes the value in theselect argumentprovides the result state. If no label matches, the execution triggersa runtime error with the standard error codeerror.NoMatch.

Note that this implies that all cases after adefault or_label are unreachable; the compiler should emit a warning if it detects unreachable cases.This constitutes an important difference betweenselectexpressions and theswitch statements found in many programming languages since the keysets ofaselect expression may“overlap”.

The typical way to use aselect expression is to comparethe value of a recently-extracted header field against a set ofconstant values, as in the following example:

header IPv4_h {bit<8> protocol;/* more fields omitted*/ }struct P { IPv4_h ipv4;/* more fields omitted*/ }P headers;select (headers.ipv4.protocol) {8w6  : parse_tcp;8w17 : parse_udp;    _    : accept;}

For example, to detect TCP reserved ports (< 1024) one could write:

select (p.tcp.port) {16w0 &&&16w0xFC00: well_known_port;    _: other_port;}

The expression16w0 &&&16w0xFC00 describes the set of 16-bitvalues whose most significant six bits are zero.

Some targets may support parser value set, see Section 12.11. Givena typeT for the type parameter of the value set, the type of the value setisset<T>. The type of the value set must match to the type of all otherkeysetExpression in the sameselect expression. If there is a mismatch, thecompiler must raise an error. The type of the values in the set must be one ofbit<>, tuple, and struct.

For example, to allow the control plane API to specify TCP reserved ports atruntime, one could write:

struct vsk_t {    @match(ternary)bit<16> port;}value_set<vsk_t>(4) pvs;select (p.tcp.port) {    pvs: runtime_defined_port;    _: other_port;}

The above example allows the runtime API to populate up to 4 differentkeysetExpressions in thevalue_set. If thevalue_set takes a structas type parameter, the runtime API can use the struct field names toname the objects in the value set. The match type of the struct field isspecified with the@match annotation. If the@match annotation is notspecified on a struct field, by default it is assumed to be@match(exact).A single non-exact field must be placed into a struct by itself, with thedesired@match annotation.

12.7. verify

Theverify statement provides a simple form of errorhandling.verify can only be invoked within a parser; it is usedsyntactically as if it were a function with the following signature:

externvoidverify(inbool condition,inerror err);

If the first argument istrue, then executing the statementhas no side-effect. However, if the first argument isfalse, it causesan immediate transition toreject, which causesimmediate parsing termination; at the same time, theparserErrorassociated with the parser is set to the value of the second argument.

In terms of theParserModel the semantics of averifystatement is given by:

ParserModel.verify(bool condition,error err) {if (condition ==false) {        ParserModel.parserError = err;        goto reject;    }}

12.8. Data extraction

The P4 core library contains the following declaration of a built-inexterntype calledpacket_in that represents incoming networkpackets. Thepacket_in extern is special: it cannot beinstantiated by the user explicitly. Instead, the architecturesupplies a separate instance for eachpacket_in argument toaparser instantiation.

extern packet_in {void extract<T>(out T headerLvalue);void extract<T>(out T variableSizeHeader,inbit<32> varFieldSizeBits);    T lookahead<T>();bit<32> length();// This method may be unavailable in some architecturesvoid advance(bit<32> bits);}

To extract data from a packet represented by an argumentb withtypepacket_in, a parser invokes theextract methods ofb.There are two variants of theextract method: a one-argumentvariant for extracting fixed-size headers, and a two-argument variantfor extracting variable-sized headers. Because these operations cancause runtime verification failures (see below), these methods canonly be executed within parsers.

When extracting data into a bit-string or integer, the first packetbit is extracted to the most significant bit of the integer.

Some targets may perform cut-through packet processing, i.e., they maystart processing a packet before its length is known (i.e., before allbytes have been received). On such a target calls to thepacket_in.length()method cannot be implemented. Attempts to call this method should beflagged as errors (either at compilation time by the compilerback-end, or when attempting to load the compiled P4 program onto atarget that does not support this method).

In terms of theParserModel, the semantics ofpacket_incan be captured using the following abstract model of packets:

packet_in {    unsigned nextBitIndex;    byte[] data;    unsigned lengthInBits;void initialize(byte[] data) {        this.data = data;        this.nextBitIndex =0;        this.lengthInBits = data.sizeInBytes *8;    }bit<32> length() {return this.lengthInBits /8; }}

12.8.1. Fixed width extraction

The single-argumentextract method handles fixed-width headers,and is declared in P4 as follows:

void extract<T>(out T headerLeftValue);

The expressionheaderLeftValue must evaluate to a l-value (seeSection 6.6) of typeheader with a fixed width. Ifthis method executes successfully, on completion theheaderLvalueis filled with data from the packet and its validity bit is set totrue. Thismethod may fail in various wayse.g., if there are notenough bits left in the packet to fill the specified header.

For example, the following program fragment extracts an Ethernet header:

struct Result { Ethernet_h ethernet;/* more field omitted*/ }parser P(packet_in b,out Result r) {state start {        b.extract(r.ethernet);    }}

In terms of theParserModel, the semantics of thesingle-argumentextract is given in terms of the followingpseudo-code method, using data from thepacket class definedabove. We use the specialvalid$ identifier to indicate thehidden valid bit of a header,isNext$ to indicate that thel-value was obtained usingnext, andnextIndex$ toindicate the corresponding header stack properties.

void packet_in.extract<T>(out T headerLValue) {   bitsToExtract = sizeofInBits(headerLValue);   lastBitNeeded = this.nextBitIndex + bitsToExtract;   ParserModel.verify(this.lengthInBits >= lastBitNeeded,error.PacketTooShort);   headerLValue = this.data.extractBits(this.nextBitIndex, bitsToExtract);   headerLValue.valid$ =true;if headerLValue.isNext$ {verify(headerLValue.nextIndex$ < headerLValue.size,error.StackOutOfBounds);     headerLValue.nextIndex$ = headerLValue.nextIndex$ +1;   }   this.nextBitIndex += bitsToExtract;}

12.8.2. Variable width extraction

The two-argumentextract handles variable-width headers, and is declared in P4 as follows:

void extract<T>(out T headerLvalue,inbit<32> variableFieldSize);

The expressionheaderLvalue must be a l-value representing aheader that containsexactly onevarbit field. The expressionvariableFieldSizemust evaluate to abit<32> value that indicates the number ofbits to be extracted into the uniquevarbit field of the header(i.e., this size is not the size of the complete header, just thevarbit field).

In terms of theParserModel, the semantics of the two-argumentextractis captured by the following pseudo-code:

void packet_in.extract<T>(out T headerLvalue,inbit<32> variableFieldSize) {// targets are allowed to include the following line, but need not// verify(variableFieldSize[2:0] == 0, error.ParserInvalidArgument);   bitsToExtract = sizeOfFixedPart(headerLvalue) + variableFieldSize;   lastBitNeeded = this.nextBitIndex + bitsToExtract;   ParserModel.verify(this.lengthInBits >= lastBitNeeded,error.PacketTooShort);   ParserModel.verify(bitsToExtract <= headerLvalue.maxSize,error.HeaderTooShort);   headerLvalue = this.data.extractBits(this.nextBitIndex, bitsToExtract);   headerLvalue.varbitField.size = variableFieldSize;   headerLvalue.valid$ =true;if headerLValue.isNext$ {verify(headerLValue.nextIndex$ < headerLValue.size,error.StackOutOfBounds);     headerLValue.nextIndex$ = headerLValue.nextIndex$ +1;   }   this.nextBitIndex += bitsToExtract;}

The following example shows one way to parse IPv4 optionsbysplitting the IPv4 header into two separate headers:

// IPv4 header without optionsheader IPv4_no_options_h {bit<4>   version;bit<4>   ihl;bit<8>   diffserv;bit<16>  totalLen;bit<16>  identification;bit<3>   flags;bit<13>  fragOffset;bit<8>   ttl;bit<8>   protocol;bit<16>  hdrChecksum;bit<32>  srcAddr;bit<32>  dstAddr;}header IPv4_options_h {varbit<320> options;}struct Parsed_headers {// Some fields omitted    IPv4_no_options_h ipv4;    IPv4_options_h    ipv4options;}error { InvalidIPv4Header }parser Top(packet_in b,out Parsed_headers headers) {// Some states omittedstate parse_ipv4 {       b.extract(headers.ipv4);verify(headers.ipv4.ihl >=5,error.InvalidIPv4Header);transitionselect (headers.ipv4.ihl) {5: dispatch_on_protocol;           _: parse_ipv4_options;   }state parse_ipv4_options {// use information in the ipv4 header to compute the number// of bits to extract       b.extract(headers.ipv4options,                 (bit<32>)(((bit<16>)headers.ipv4.ihl -5) *32));transition dispatch_on_protocol;   }}

12.8.3. Lookahead

Thelookahead method provided by thepacket_in packetabstraction evaluates to a set of bits from the input packet withoutadvancing thenextBitIndex pointer. Similar toextract, itwill transition toreject and set the error if there are notenough bits in the packet. Thelookahead method can be invokedas follows,

b.lookahead<T>()

whereT must be a type with fixed width. In case of success theresult of the evaluation oflookahead returns a value of typeT.

In terms of theParserModel, the semantics oflookahead isgiven by the following pseudo-code:

T packet_in.lookahead<T>() {   bitsToExtract = sizeof(T);   lastBitNeeded = this.nextBitIndex + bitsToExtract;   ParserModel.verify(this.lengthInBits >= lastBitNeeded,error.PacketTooShort);   T tmp = this.data.extractBits(this.nextBitIndex, bitsToExtract);return tmp;}

The TCP options example from Section 8.18 also illustrates howlookahead can be used:

state start {transitionselect(b.lookahead<bit<8>>()) {0: parse_tcp_option_end;1: parse_tcp_option_nop;2: parse_tcp_option_ss;3: parse_tcp_option_s;5: parse_tcp_option_sack;    }}// Some states omittedstate parse_tcp_option_sack {bit<8> n = b.lookahead<Tcp_option_sack_top>().length;    b.extract(vec.next.sack, (bit<32>) (8 * n -16));transition start;}

12.8.4. Skipping bits

P4 provides two ways to skip over bits in an input packet withoutassigning them to a header:

One way is toextract to the underscore identifier, explicitlyspecifying the type of the data:

b.extract<T>(_)

Another way is to use theadvance method of the packet when thenumber of bits to skip is known.

In terms of theParserModel, the meaning ofadvance isgiven in pseudo-code as follows:

void packet_in.advance(bit<32> bits) {// targets are allowed to include the following line, but need not// verify(bits[2:0] == 0, error.ParserInvalidArgument);   lastBitNeeded = this.nextBitIndex + bits;   ParserModel.verify(this.lengthInBits >= lastBitNeeded,error.PacketTooShort);   this.nextBitIndex += bits;}

12.9. Header stacks

A header stack has two properties,next andlast, whichcan be used in parsing. Consider the following declaration, whichdefines a stack for representing the headers of a packet with at mostten MPLS headers:

header Mpls_h {bit<20> label;bit<3>  tc;bit     bos;bit<8>  ttl;}Mpls_h[10] mpls;

The expressionmpls.next represents an l-value of typeMpls_hthat references an element in themplsstack. Initially,mpls.next refers to the first element ofstack. It is automatically advanced on each successful call toextract.Thempls.last property refers to the elementimmediately precedingnext if such an element exists. Attemptingto accessmpls.next element when the stack'snextIndexcounter is greater than or equal tosize causes a transition torejectand sets the error toerror.StackOutOfBounds. Likewise,attempting to accessmpls.last when thenextIndex counteris equal to0 causes a transition toreject andsets the error toerror.StackOutOfBounds.

The following example shows a simplified parser for MPLS processing:

struct Pkthdr {   Ethernet_h ethernet;   Mpls_h[3] mpls;// other headers omitted}parser P(packet_in b,out Pkthdr p) {state start {        b.extract(p.ethernet);transitionselect(p.ethernet.etherType) {0x8847: parse_mpls;0x0800: parse_ipv4;        }    }state parse_mpls {         b.extract(p.mpls.next);transitionselect(p.mpls.last.bos) {0: parse_mpls;// This creates a loop1: parse_ipv4;         }    }// other states omitted}

12.10. Sub-parsers

P4 allows parsers to invoke the services of other parsers, similar tosubroutines. To invoke the services of another parser, the sub-parsermust be first instantiated; the services of an instance are invoked bycalling it using its apply method.

The following example shows a sub-parser invocation:

parser callee(packet_in packet,out IPv4 ipv4) {/* body omitted*/ }parser caller(packet_in packet,out Headers h) {     callee() subparser;// instance of calleestate subroutine {          subparser.apply(packet, h.ipv4);// invoke sub-parsertransition accept;// accept if sub-parser ends in accept state     }}

The semantics of a sub-parser invocation can be described as follows:

subparser


Figure 9. Semantics of invoking a sub-parser: top: original program, bottom: equivalent program.

Figure 9 shows a diagram of this process.

Note that since P4 requires declarations to precede uses, it is impossible tocreate recursive (or mutually recursive) parsers.

Architectures may impose (static or dynamic) constraints on thenumber of parser states that can be traversed for processing eachpacket. For example, a compiler for a specific target may rejectparsers containing loops that cannot be unrolled at compilation timeor that may contain cycles that do not advance the cursor.If a parser aborts execution dynamically because it exceededthe time budget allocated for parsing, the parser should transition toreject and setthe standard errorerror.ParserTimeout.

12.11. Parser Value Sets

In some cases, the values that determine the transition from one parser stateto another need to be determined at run time. MPLS is one example where thevalue of the MPLS label field is used to determine what headers follow the MPLStag and this mapping may change dynamically at run time. To support thisfunctionality, P4 supports the notion of a Parser Value Set. This is a namedset of values with a run time API to add and remove values from the set.

Value sets are declared locally within a parser. They should be declared beforebeing referenced in parserkeysetExpression and can be used as a label in aselect expression.

The syntax for declaring value sets is:

valueSetDeclaration  : optAnnotations      VALUESET'<' baseType'>''(' expression')' name';'  | optAnnotations      VALUESET'<' tupleType'>''(' expression')' name';'  | optAnnotations      VALUESET'<' typeName'>''(' expression')' name';'  ;

Parser Value Sets support asize argument to provide hints to the compiler toreserve hardware resource to implement the value set. For example, this parservalue set:

value_set<bit<16>>(4) pvs;

creates a value_set of size 4 with entries of typebit<16>.

The semantics of thesize argument is similar to thesize property of atable. If a value set has asize argument with valueN, it is recommendedthat a compiler should choose a data plane implementation that is capable ofstoringN value set entries. See“Size property of P4 tables and parser valuesets” P4SizeProperty for further discussion on the implementation of parservalue set size.

The value set is populated by the control-plane by methods specified in theP4Runtime specification.

13. Control blocks

P4 parsers are responsible for extracting bits from a packet intoheaders. These headers (and other metadata) can be manipulated and transformed withincontrolblocks. The body of a control blockresembles a traditional imperative program. Within the body of a control block,match-action units can be invoked to perform datatransformations. Match-action units are represented in P4 byconstructs calledtables.

Syntactically, acontrol block is declared with a name,parameters, optional type parameters, and a sequence of declarationsof constants, variables,actions,tables, and otherinstantiations:

controlDeclaration    : controlTypeDeclaration optConstructorParameters/* controlTypeDeclaration cannot contain type parameters*/'{' controlLocalDeclarations APPLY controlBody'}'    ;controlLocalDeclarations    :/* empty*/    | controlLocalDeclarations controlLocalDeclaration    ;controlLocalDeclaration    : constantDeclaration    | variableDeclaration    | actionDeclaration    | tableDeclaration    | instantiation    ;controlBody    : blockStatement    ;

It is illegal to instantiate aparser within acontrolblock.For a description of theoptConstructorParameters, whichcan be used to build parameterized control blocks, see Section14.

Unlike control type declarations, control declarations may not begenerice.g., the following declaration is illegal:

control C<H>(inout H data) {/* Body omitted*/ }

P4 does not support exceptional control-flow within acontrolblock.The only statement whichhas a non-local effect on control flow isexit, which causesexecution of the enclosing control block to immediately terminate.That is, there is no equivalent of theverify statementor thereject state from parsers. Hence, all error handling mustbe performed explicitly by the programmer.

The rest of this section describes the core components of acontrol block,starting with actions.

13.1. Actions

actions


Figure 10. Actions contain code and data. The code is in the P4 program, while the data is provided in the table entries, typically populated by the control plane. Other parameters are bound by the data plane.

Actions are code fragments that can read and write the data beingprocessed. Actions may contain data values that can be written by thecontrol plane and read by the data plane. Actions are the mainconstruct by which the control-plane can influence dynamically thebehavior of the data plane. Figure 10 shows the abstractmodel of anaction.

actionDeclaration    : optAnnotations ACTION name'(' parameterList')' blockStatement    ;

Syntactically actions resemble functions with no returnvalue. Actions may be declared within a control block; in this casethey can only be used within instances of that control block.

The following example shows an action declaration:

action Forward_a(outbit<9> outputPort,bit<9> port) {    outputPort = port;}

Action parameters may not haveextern types. Action parameters thathave no direction (e.g.,port in the previous example) indicate“action data.” All such parameters must appear at the end of theparameter list. When used in a match-action table (see Section13.2.1.2), these parameters will be provided by thetable entries (e.g., as specified by the control plane, thedefault_action table property, or theconstentries tableproperty).

The body of an action consists of a sequence of statements anddeclarations. Noswitch statements are allowed within anactionthe grammar permits them, but a semantic check should rejectthem. Some targets may impose additional restrictions on actionbodiese.g., only allowing straight-line code, with no conditionalstatements or expressions.

13.1.1. Invoking actions

Actions can be executed in two ways:

13.2. Tables

maudataflow


Figure 11. Match-Action Unit Dataflow.

Atable describes a match-action unit. Thestructure of a match-action unit is shown in Figure11. Processing a packet using a match-action table executes thefollowing steps:

Atable declaration introduces a table instance. To obtainmultiple instances of a table, it must be declared within a controlblock that is itself instantiated multiple times.

The look-up table is a finite map whose contents are manipulatedasynchronously (read/write) by the target control-plane, through aseparate control-plane API (see Figure 11). Note thatthe term“table” is overloaded: it can refer to the P4tableobjects that appear in P4 programs, as well as the internal look-uptables used in targets. We will use the term“match-action unit” whennecessary to disambiguate.

Syntactically a table is defined in terms of a set of key-valueproperties. Some of these properties are“standard” properties, butthe set of properties can be extended by target-specific compilers asneeded.

tableDeclaration    : optAnnotations TABLE name'{' tablePropertyList'}'    ;tablePropertyList    : tableProperty    | tablePropertyList tableProperty    ;tableProperty    : KEY'=''{' keyElementList'}'    | ACTIONS'=''{' actionList'}'    | optAnnotations CONST ENTRIES'=''{' entriesList'}'/* immutable entries*/    | optAnnotations CONST nonTableKwName'=' initializer';'    | optAnnotations nonTableKwName'=' initializer';'    ;nonTableKwName   : IDENTIFIER   | TYPE_IDENTIFIER   | APPLY   | STATE   | TYPE   ;

The standard table properties include:

In addition, the tables may optionally define the following properties,

The compiler must set thedefault_action toNoAction (and alsoinsert it into the list ofactions) for tables that do not definethedefault_action property. This is consistent with the semanticsgiven in Section 13.2.1.3. Hence, all tables can bethought of as having adefault_action property, either implicitly orexplicitly.

In addition, tables may contain architecture-specific properties (seeSection 13.2.1.6).

A property marked asconst cannot be changed dynamically by thecontrol-plane. Thekey,actions, andsize properties are alwaysconstant, so theconst keyword is not needed for these.

13.2.1. Table properties

13.2.1.1. Keys

Thekey is a table property which specifies the data planevalues that should be used to look up an entry. A key is a list ofpairs of the form(e : m), wheree is an expression that describesthe data to be matched in the table, andm is amatch_kindconstant that describes the algorithm used to perform the lookup (see Section 7.1.3).

keyElementList    :/* empty*/    | keyElementList keyElement    ;keyElement    : expression':' name optAnnotations';'    ;

For example, consider the following program fragment:

table Fwd {    key = {       ipv4header.dstAddress : ternary;       ipv4header.version    : exact;    }// more fields omitted}

Here the key comprises two fields from theipv4headerheader:dstAddress andversion. Thematch_kind constantsserve three purposes:

The P4 core library contains three predefinedmatch_kind identifiers:

match_kind {   exact,   ternary,   lpm}

These identifiers correspond to the P414 match kinds with the samenames. The semantics of these match kinds is actually not needed todescribe the behavior of the P4 abstract machine; how they are usedinfluences only the control-plane API and the implementation ofthe look-up table. From the point of view of the P4 program, a look-uptable is an abstract finite map that is given a key and produces as aresult either an action or a“miss” indication, as described inSection 13.2.3.

If a table has nokey property, then it contains no look-uptable, just a default actioni.e., the associated lookup table isalways the empty map.

Each key element can have an optional@name annotation which isused to synthesize the control-plane visible name for the key field.

13.2.1.2. Actions

A table must declare all possible actions that may appear within theassociated lookup table or in the default action. This is done withtheactions property; the value of this property is always anactionList:

actionList    :/* empty*/    | actionList optAnnotations actionRef';'    ;actionRef    : prefixedNonTypeName    | prefixedNonTypeName'(' argumentList')'    ;

To illustrate, recall the example Very Simple Switch program inSection 5.3:

action Drop_action() {  outCtrl.outputPort = DROP_PORT;}action Rewrite_smac(EthernetAddress sourceMac) {  headers.ethernet.srcAddr = sourceMac;}table smac {    key = { outCtrl.outputPort : exact; }    actions = {        Drop_action;        Rewrite_smac;    }}

Each action in the list of actions for a table must have a distinctnamee.g., the following program fragment is illegal:

action a() {}control c() {action a() {}// Illegal table: two actions with the same nametable t { actions = { a; .a; } }}

Each action parameter that has a direction (in,inout, orout)must be bound in theactions list specification; conversely, nodirectionless parameters may be bound in the list. The expressionssupplied as arguments to anaction are not evaluated until theaction is invoked.

action a(inbit<32> x) {/* body omitted*/ }bit<32> z;action b(inoutbit<32> x,bit<8> data) {/* body omitted*/ }table t {    actions = {// a; -- illegal, x parameter must be bound       a(5);// binding a's parameter x to 5       b(z);// binding b's parameter x to z// b(z, 3); -- illegal, cannot bind directionless data parameter// b(); -- illegal, x parameter must be bound    }}
13.2.1.3. Default action

The default action for a table is an action that is invokedautomatically by the match-action unit whenever the lookup table doesnot find a match for the supplied key.

If present, thedefault_action propertymust appear after theactionproperty. It may be declared asconst, indicating that it cannotbe changed dynamically by the control-plane. Thedefaultactionmust be one of the actions that appear in the actions list. Inparticular, the expressions passed asin,out, orinoutparameters must be syntactically identical to the expressions used inone of the elements of theactions list.

For example, in the abovetable we could set the default actionas follows (marking it also as constant):

const default_action = Rewrite_smac(48w0xAA_BB_CC_DD_EE_FF);

Note that the specified default action must supply arguments for thecontrol-plane bound parameters (i.e., the directionless parameters),since the action is synthesized at compilation time. The expressionssupplied as arguments for parameters with a direction (in,inout,orout) are evaluated when the action is invoked while theexpressions supplied as arguments for directionless parameters areevaluated at compile time.

Continuing the example from the previous section, following are several legaland illegal specifications of default actions for thetable t:

  default_action = a(5);// OK - no control-plane parameters// default_action = a(z); -- illegal, a's x parameter is already bound to 5  default_action = b(z,8w8);// OK - bind b's data parameter to 8w8// default_action = b(z); -- illegal, b's data parameter is not bound// default_action = b(x, 3); -- illegal: x parameter of b bound to x instead of z

If a table does not specify thedefault_action property and noentry matches a given packet, then the table does not affect thepacket and processing continues according to the imperative controlflow of the program.

13.2.1.4. Entries

While table entries are typically installed by the control plane,tables may also be initialized at compile-time with a set of entries.This is useful in situations where tables areused to implement fixed algorithmsdefining table entries staticallyenables expressing these algorithm directly in P4,which allows the compiler to infer how the table is actually used andpotentially make better allocation decisions for targets with limitedresources. Entries declared in the P4 source are installed in thetable when the program is loaded onto the target.

Table entries are defined using the following syntax:

tableProperty :const ENTRIES'=''{' entriesLlist'}'/* immutable entries*/entriesList : entry | entriesList entry ;entry : keysetExpression':' actionRef optAnnotations';' ;

Table entries are immutable (const)i.e., they can only be read andcannot be changed or removed by the control plane. It follows thattables that define entries in the P4 source are immutable. This designchoice has important ramifications for the P4 runtime since it doesnot have to keep track of different types of entries in one table(mutable and immutable). Future versions of P4 may add the ability tomix mutable and immutable entries in the same table, by declaringadditionalentries properties without theconst keyword.

ThekeysetExpression component of an entry is a tuple that mustprovide a field for each key in the table keys (see Sec.13.2.1). The table key type must match the type of theelement of the set. TheactionRef component must be an action whichappears in the table actions list, with all its arguments bound.

If the runtime API requires a priority for the entries of atablee.g. when using the P4 Runtime API, tables with at least oneternary search key fieldthen the entries are matched in programorder, stopping at the first matching entry. Architectures shoulddefine the significance of entry order (if any) for other kinds oftables.

Depending on thematch_kind of the keys, key set expressions may defineone or multiple entries. The compiler will synthesize the correct number ofentries to be installed in the table. Target constraints may further restrictthe ability of synthesizing entries. For example, if the number of synthesizedentries exceeds the table size, the compiler implementation may choose to issuea warning or an error, depending on target capabilities.

To illustrate, consider the following example:

header hdr {bit<8>  e;bit<16> t;bit<8>  l;bit<8>  r;bit<1>  v;}struct Header_t {    hdr h;}struct Meta_t {}control ingress(inout Header_t h,inout Meta_t m,inout standard_metadata_t standard_meta) {action a() { standard_meta.egress_spec =0; }action a_with_control_params(bit<9> x) { standard_meta.egress_spec = x; }table t_exact_ternary {      key = {            h.h.e : exact;            h.h.t : ternary;        }    actions = {            a;            a_with_control_params;        }    default_action = a;constentries = {            (0x01,0x1111 &&&0xF   ) : a_with_control_params(1);            (0x02,0x1181           ) : a_with_control_params(2);            (0x03,0x1111 &&&0xF000) : a_with_control_params(3);            (0x04,0x1211 &&&0x02F0) : a_with_control_params(4);            (0x04,0x1311 &&&0x02F0) : a_with_control_params(5);            (0x06, _                ) : a_with_control_params(6);            _                         : a;        }    }}

In this example we define a set of 7 entries, all of which invokeactiona_with_control_params except for the final entry whichinvokes actiona. Once the program is loaded, these entries areinstalled in the table in the order they are enumerated in theprogram.

13.2.1.5. Size

Thesize is an optional property of a table. When present, itsvalue is always an integer compile-time known value. It is specifiedin units of number of table entries.

If a table has asize value specified for it with valueN, it isrecommended that a compiler should choose a data plane implementationthat is capable of storingN table entries. This does not guaranteethat anarbitrary set ofN entries can always be inserted in sucha table, only that there issome set ofN entries that can beinserted. For example, attempts to add some combinations ofNentries may fail because the compiler selected a hash table withO(1) guaranteed search time. See“Size property of P4 tables andparser value sets” P4SizeProperty for further discussion on some P4table implementations and what they are able to guarantee.

If a P4 implementation must dimension table resources at compile time,they may treat it as an error if they encounter a table with nosizeproperty.

Some P4 implementations may be able to dynamically dimension tableresources at run time. If asize value is specified in the P4program, it is recommended that such an implementation uses thesizevalue as the initial capacity of the table.

13.2.1.6. Additional properties

Atable declaration defines its essential control and data planeinterfacesi.e., keys and actions. However, the best way toimplement a table may actually depend on the nature of the entriesthat will be installed at runtime (for example, tables could bedense or sparse, could be implemented as hash-tables, associativememories, tries, etc.) In addition, some architectures may support extra tableproperties whose semantics lies outside the scope of thisspecification. For example, in architectures where table resources arestatically allocated, programmers may be required to define asizetable property, which can be used by the compiler back-endto allocate storage resources. However, these architecture-specificproperties may not change the semantics of table lookups, which alwaysproduce either ahit and an action or amissthey canonly change how those results are interpreted on the state of the dataplane. This restriction is needed to ensure that it is possible toreason about the behavior of tables during compilation.

As another example, animplementation property could be used topass additional information to the compiler back-end. The value ofthis property could be an instance of anextern block chosenfrom a suitable library of components. For example, the corefunctionality of the P414 tableaction_profile constructscould be implemented on architectures that support this feature usinga construct such as the following:

extern ActionProfile {   ActionProfile(bit<32> size);// number of distinct actions expected}table t {    key = {/* body omitted*/ }    size =1024;    implementation = ActionProfile(32);// constructor invocation}

Here the action profile might be used to optimize for the case wherethe table has a large number of entries, but the actions associatedwith those entries are expected to range over a small number ofdistinct values. Introducing a layer of indirection enables sharingidentical entries, which can significantly reduce the table's storagerequirements.

13.2.2. Match-action unit invocation

Atable can be invoked by calling itsapplymethod. Calling an apply method on a table instance returns a valuewith astruct type with three fields. This structure issynthesized by the compiler automatically. For eachtable T, thecompiler synthesizes anenum and astruct, shown inpseudo-P4:

enum action_list(T) {// one field for each action in the actions list of table T}struct apply_result(T) {bool hit;bool miss;    action_list(T) action_run;}

The evaluation of theapply method sets thehit field totrueand the fieldmiss tofalse if a match is found in the lookup-table;if a match is not foundhit is set tofalse andmiss totrue.These bits can be used to drive the execution of the control-flow in thecontrol block that invoked the table:

if (ipv4_match.apply().hit) {// there was a hit}else {// there was a miss}if (ipv4_host.apply().miss) {    ipv4_lpm.apply();// Lookup the route only if host table missed}

Theaction_run field indicates which kind of action was executed(irrespective of whether it was a hit or a miss). It can be used in aswitch statement:

switch (dmac.apply().action_run) {    Drop_action: {return; }}

13.2.3. Match-action unit execution semantics

The semantics of a table invocation statement:

m.apply();

is given by the following pseudo-code (see also Figure11):

apply_result(m) m.apply() {    apply_result(m) result;    var lookupKey = m.buildKey(m.key);// using key blockaction RA = m.table.lookup(lookupKey);if (RA == null) {// miss in lookup table       result.hit =false;       RA = m.default_action;// use default action    }else {       result.hit =true;    }    result.miss = !result.hit;    result.action_run = action_type(RA);    evaluate_and_copy_in_RA_args(RA);    execute(RA);    copy_out_RA_args(RA);return result;}

The behavior of thebuildKey call in the pseudocode above is toevaluate each key expression in the order they appear in the table keydefinition. The behavior must be the same as if the result ofevaluating each key expression is assigned to a fresh temporaryvariable, before starting the evaluation of the following keyexpression. For example, this P4 table definition and apply call:

bit<8> f1 (inbit<8> a,inoutbit<8> b) {    b = a +5;return a >>1;}bit<8> x;bit<8> y;table t1 {    key = {        y &0x7  : exact @name("masked_y");        f1(x, y) : exact @name("f1");        y        : exact;    }// ... rest of table properties defined here, not relevant to example}apply {// assign values to x and y here, not relevant to example    t1.apply();}

is equivalent in behavior to the following table definition and applycall:

// same definition of f1, x, and y as before, so they are not repeated herebit<8> tmp_1;bit<8> tmp_2;bit<8> tmp_3;table t1 {    key = {        tmp_1 : exact @name("masked_y");        tmp_2 : exact @name("f1");        tmp_3 : exact @name("y");    }// ... rest of table properties defined here, not relevant to example}apply {// assign values to x and y here, not relevant to example    tmp_1 = y &0x7;    tmp_2 = f1(x, y);    tmp_3 = y;    t1.apply();}

Note that the second code example above is given in order to specifythe behavior of the first one. An implementation is free to chooseany technique that achieves this behavior4.

13.3. The Match-Action Pipeline Abstract Machine

We can describe the computational model of a match-action pipeline,embodied by a control block: the body of the control block isexecuted, similarly to the execution of a traditional imperativeprogram:

13.4. Invoking controls

P4 allows controls to invoke the services of other controls, similarto subroutines. To invoke the services of another control, it must befirst instantiated; the services of an instance are invoked by callingit using itsapply method.

The following example shows a control invocation:

control Callee(inout IPv4 ipv4) {/* body omitted*/ }control Caller(inout Headers h) {     Callee() instance;// instance of calleeapply {          instance.apply(h.ipv4);// invoke control     }}

14. Parameterization

In order to support libraries of useful P4 components, bothparsersandcontrol blocks can be additionally parameterized through theuse of constructor parameters.

Consider again the parser declaration syntax:

parserDeclaration    : parserTypeDeclaration optConstructorParameters'{' parserLocalElements parserStates'}'    ;optConstructorParameters    :/* empty*/    |'(' parameterList')'    ;

From this grammar fragment we infer that aparser declarationmay have two sets of parameters:

Constructor parameters must be directionless (i.e., they cannotbein,out, orinout) and when the parser isinstantiated, it must be possible to fully evaluate the expressionssupplied for these parameters at compilation time.

Consider the following example:

parser GenericParser(packet_in b,out Packet_header p)                    (bool udpSupport) {// constructor parametersstate start {        b.extract(p.ethernet);transitionselect(p.ethernet.etherType) {16w0x0800: ipv4;        }    }state ipv4 {        b.extract(p.ipv4);transitionselect(p.ipv4.protocol) {6: tcp;17: tryudp;        }    }state tryudp {transitionselect(udpSupport) {false: accept;true : udp;        }    }state udp {// body omitted    }}

When instantiating theGenericParser it is necessary to supply a value fortheudpSupport parameter, as in the following example:

// topParser is a GenericParser where udpSupport = falseGenericParser(false) topParser;

14.1. Direct type invocation

Controls and parsers are often instantiated exactly once. As a lightsyntactic sugar, control and parser declarations with no constructorparameters may be applied directly, as if they were an instance. Thishas the effect of creating and applying a local instance of that type.

control Callee(/* parameters omitted*/) {/* body omitted*/ }control Caller(/* parameters omitted*/)(/* parameters omitted*/) {apply {        Callee.apply(/* arguments omitted*/);// callee is treated as an instance    }}

The definition ofCaller is equivalent to the following.

control Caller(/* parameters omitted*/)(/* parameters omitted*/) {    @name("Callee") Callee() Callee_inst; // local instance of Calleeapply {        Callee_inst.apply(/* arguments omitted*/);// Callee_inst is applied    }}

This feature is intended to streamline the common case where a type is instantiatedexactly once. For completeness, the behavior of directly invoking thesame type more than once is defined as follows.

See Section 17.3.2 for details of@nameannotations.

15. Deparsing

The inverse of parsing is deparsing, or packet construction. P4 doesnot provide a separate language for packet deparsing; deparsing isdone in acontrol block that has at least one parameter of typepacket_out.

For example, the following code sequence writes first an Ethernetheader and then an IPv4 header into apacket_out:

control TopDeparser(inout Parsed_packet p, packet_out b) {apply {        b.emit(p.ethernet);        b.emit(p.ip);    }}

Emitting a header appends the header to thepacket_out only ifthe header is valid. Emitting a header stack will emit all elements ofthe stack in order of increasing indexes.

15.1. Data insertion into packets

Thepacket_out datatype is defined in the P4 core library, andreproduced below. It provides a method for appending data to anoutput packet calledemit:

extern packet_out {void emit<T>(in T data);}

Theemit method supports appending the data contained in aheader, header stack,struct, or header union to the output packet.

It is illegal to invokeemit on an expression whose type is a base type,enum, orerror.

We can define the meaning of theemit method in pseudo-code asfollows:

packet_out {    byte[] data;    unsigned lengthInBits;void initializeForWriting() {        this.data.clear();        this.lengthInBits =0;    }/// Append data to the packet. Type T must be a header, header/// stack, header union, or struct formed recursively from those typesvoid emit<T>(T data) {if (isHeader(T))if(data.valid$) {                this.data.append(data);                this.lengthInBits += data.lengthInBits;            }elseif (isHeaderStack(T))            for (e : data)                 emit(e);elseif (isHeaderUnion(T) || isStruct(T))            for (f : data.fields$)                 emit(e.f)// Other cases for T are illegal    }

Here we use the specialvalid$ identifier to indicate the hiddenvalid bit of headers andfields$ to indicate the list of fieldsfor a struct or header union. We also use standardfor notation toiterate through the elements of a stack(e : data) and list offields for header unions and structs(f : data.fields$). Theiteration order for a struct is the order those fields appear in thetype declaration.

16. Architecture description

The architecture description must be provided by the targetmanufacturer in the form of a library P4 source file that contains atleast one declaration for apackage; thispackage must beinstantiated by the user to construct a program for a target. For anexample see the Very Simple Switch declaration from Section5.1.

The architecture description file may pre-define data types,constants, helper package implementations, and errors. It must alsodeclare the types of all the programmable blocks that will appear inthe final target:parsers andcontrol blocks. Theprogrammable blocks may optionally be grouped together in packages,which can be nested.

Since some of the target components may manipulate user-defined types,which are unknown at the target declaration time, these are describedusing type variables, which must be used parametrically in theprogrami.e., type variables are checked similar to Java generics,not C++ templates.

16.1. Example architecture description

switcharch


Figure 12. Fragment of example switch architecture.

The following example describes a switch by using two packages, eachcontaining a parser, a match-action pipeline, and a deparser:

parser Parser<IH>(packet_in b,out IH parsedHeaders);// ingress match-action pipelinecontrol IPipe<T, IH, OH>(in IH inputHeaders,in InControl inCtrl,out OH outputHeaders,out T toEgress,out OutControl outCtrl);// egress match-action pipelinecontrol EPipe<T, IH, OH>(in IH inputHeaders,in InControl inCtrl,in T fromIngress,out OH outputHeaders,out OutControl outCtrl);control Deparser<OH>(in OH outputHeaders, packet_out b);package Ingress<T, IH, OH>(Parser<IH> p,                           IPipe<T, IH, OH> map,                           Deparser<OH> d);package Egress<T, IH, OH>(Parser<IH> p,                          EPipe<T, IH, OH> map,                          Deparser<OH> d);package Switch<T>(Ingress<T, _, _> ingress, Egress<T, _, _> egress);

Just from these declarations, even without reading a precisedescription of the target, the programmer can infer some usefulinformation about the architecture of the described switch, as shownin Figure 12:

Hence, this architecture models a target switch that contains twoseparate channels between the ingress and egress pipeline:

16.2. Example architecture program

To construct a program for the architecture, the P4 program mustinstantiate a top-levelpackage by passing values for all itsarguments creating a variable calledmain in the top-levelnamespace. The types of the arguments must match the types of theparametersafter a suitable substitution of the type variables. Thetype substitution can be expressed directly, using typespecialization, or can be inferred by a compiler, using a unificationalgorithm like Hindley-Milner.

For example, given the following type declarations:

parser Prs<T>(packet_in b,out T result);control Pipe<T>(in T data);package Switch<T>(Prs<T> p, Pipe<T> map);

and the following declarations:

parser P(packet_in b,outbit<32> index) {/* body omitted*/ }control Pipe1(inbit<32> data) {/* body omitted*/ }control Pipe2(inbit<8> data) {/* body omitted*/ }

The following is a legal declaration for the top-level target:

Switch(P(), Pipe1()) main;

And the following is illegal:

Switch(P(), Pipe2()) main;

The latter declaration is incorrect because the parserPrequiresT to bebit<32>, whilePipe2 requiresTto bebit<8>.

The user can also explicitly specify values for the type variables(otherwise the compiler has to infer values for these type variables):

Switch<bit<32>>(P(), Pipe1()) main;

16.3. A Packet Filter Model

packetfilter


Figure 13. A packet filter target model. The parser computes a Boolean value, which is used to decide whether the packet is dropped.

To illustrate the versatility of P4 architecture description language,we give an example of another architecture, which models a packetfilter that makes a drop/no drop decision based only on thecomputation in a P4 parser, as shown in Figure 13.

This model could be used to program packet filters running in theLinux kernel. For example, we could replace the TCP dump language withthe much more powerful P4 language; P4 can seamlessly support newprotocols, while providing complete“type safety” during packetprocessing. For such a target the P4 compiler could generate an eBPF(Extended Berkeley Packet Filter) program, which is injected by theTCP dump utility into the Linux kernel, and executed by the EBPFkernel JIT compiler/runtime.

In this case the target is the Linux kernel, and the architecturemodel is a packet filter.

The declaration for this architecture is as follows:

parser Parser<H>(packet_in packet,out H headers);control Filter<H>(inout H headers,outbool accept);package Program<H>(Parser<H> p, Filter<H> f);

17. P4 abstract machine: Evaluation

The evaluation of a P4 program is done in two stages:

17.1. Compile-time known values

The following are compile-time known values:

17.2. Compile-time Evaluation

Evaluation of a program proceeds in order of declarations, starting inthe top-level namespace:

Note that all stateful values are instantiated at compilation time.

As an example, consider the following program fragment:

// architecture declarationparser P(/* parameters omitted*/);control C(/* parameters omitted*/);control D(/* parameters omitted*/);package Switch(P prs, C ctrl, D dep);extern Checksum16 {/* body omitted*/}// user codeChecksum16() ck16;// checksum unit instanceparser TopParser(/* parameters omitted*/)(Checksum16 unit) {/* body omitted*/}control Pipe(/* parameters omitted*/) {/* body omitted*/}control TopDeparser(/* parameters omitted*/)(Checksum16 unit) {/* body omitted*/}Switch(TopParser(ck16),       Pipe(),       TopDeparser(ck16)) main;

The evaluation of this program proceeds as follows:

  1. The declarations ofP,C,D,Switch, andChecksum16 allevaluate to themselves.
  2. TheChecksum16() ck16 instantiation is evaluated and itproduces an object namedck16 with typeChecksum16.
  3. The declarations forTopParser,Pipe, andTopDeparserevaluate as themselves.
  4. Themain variable instantiation is evaluated:
    1. The arguments to the constructor are evaluated recursively
    2. TopParser(ck16) is a constructor invocation
    3. Its argument is evaluated recursively; it evaluates to theck16object
    4. The constructor itself is evaluated, leading to theinstantiation of an object of typeTopParser
    5. Similarly,Pipe() andTopDeparser(ck16) areevaluated as constructor calls.
    6. All the arguments of theSwitch package constructor havebeen evaluated (they are an instance ofTopParser, aninstance ofPipe, and an instance ofTopDeparser).Their signatures are matched with theSwitch declaration.
    7. Finally, theSwitch constructor can be evaluated. Theresult is an instance of theSwitch package (thatcontains aTopParser namedprs the firstparameter of theSwitch; aPipe namedctrl;and aTopDeparser nameddep).
  5. The result of the program evaluation is the value of themainvariable, which is the above instance of theSwitchpackage.

Figure 14 shows the result of the evaluation in agraphical form. The result is always a graph of instances. There isonly one instance ofChecksum16, calledck16, sharedbetween theTopParser andTopDeparser. Whether this ispossible is architecture-dependent. Specific target compilers mayrequire distinct checksum units to be used in distinct blocks.

compileeval


Figure 14. Evaluation result.

17.3. Control plane names

Every controllable entity exposed in a P4 program must be assigned aunique, fully-qualified name, which the control plane may use tointeract with that entity. The following entities are controllable.

A fully qualified name consists of the local name of a controllableentity prepended with the fully qualified name of its enclosingnamespace. Hence, the following program constructs, which enclosecontrollable entities, must themselves have unique, fully-qualifiednames.

Evaluation may create multiple instances from one type, each of whichmust have a unique, fully-qualified name.

17.3.1. Computing control names

The fully-qualified name of a construct is derived by concatenatingthe fully-qualified name of its enclosing construct with its localname. Constructs with no enclosing namespace, i.e. those defined atthe global scope, have the same local and fully-qualified names. Thelocal names of controllable entities and enclosing constructs arederived from the syntax of a P4 program as follows.

17.3.1.1. Tables

For eachtable construct, its syntactic name becomes the localname of the table. For example:

control c(/* parameters omitted*/)() {table t {/* body omitted*/ }}

This table's local name ist.

17.3.1.2. Keys

Syntactically, table keys are expressions. For simple expressions,the local key name can be generated from the expression itself. Inthe following example, the tablet has keys with namesdata.f1andhdrs[3].f2.

table t {    keys = {        data.f1 : exact;        hdrs[3].f2 : exact;    }    actions = {/* body omitted*/ }}

The following kinds of expressions have local names derived from theirsyntactic names:

Kind Example Name
TheisValid() method.h.isValid()"h.isValid()"
Array accesses.header_stack[1]"header_stack[1]"
Constants.1"1"
Field projections.data.f1"data.f1"
Slices.f1[3:0]"f1[3:0]"

All other kinds of expressionsmust be annotated with a@nameannotation (Section 18.3.3), as in thefollowing example.

table t {    keys = {        data.f1 +1 : exact @name("f1_mask");    }    actions = {/* body omitted*/ }}

Here, the@name("f1_mask") annotation assigns the local name"f1_mask"to this key.

17.3.1.3. Actions

For eachaction construct, its syntactic name is the local nameof the action. For example:

control c(/* parameters omitted*/)() {action a(...) {/* body omitted*/ }}

This action's local name isa.

17.3.1.4. Instances

The local names ofextern,parser, andcontrolinstances are derived based on how the instance is used. If theinstance is bound to a name, that name becomes its local control planename. For example, ifcontrolC is declared as,

control C(/* parameters omitted*/)() {/* body omitted*/ }

and instantiated as,

C() c_inst;

then the local name of the instance isc_inst.

Alternatively, if the instance is created as an actual argument, then itslocal name is the name of the formal parameter to which it will bebound. For example, ifexternE andcontrolC are declared as,

extern E {/* body omitted*/ }control C(/* parameters omitted*/ )(E e_in) {/* body omitted*/ }

and instantiated as,

C(E()) c_inst;

then the local name of the extern instance ise_in.

If the construct being instantiated is passed as an argument to apackage, the instance name is derived from the user-supplied typedefinition when possible. In the following example, the local name ofthe instance ofMyC isc, and the local name of theexternise2, note1.

extern E {/* body omitted*/ }control ArchC(E e1);package Arch(ArchC c);control MyC(E e2)() {/* body omitted*/ }Arch(MyC()) main;

Note that in this example, the architecture will supply an instance ofthe extern when it applies the instance ofMyC passed to theArchpackage. The fully-qualified name of that instance ismain.c.e2.

Next, consider a larger example that demonstrates name generation whenthere are multiple instances.

control Callee() {table t {/* body omitted*/ }apply { t.apply(); }}control Caller() {    Callee() c1;    Callee() c2;apply {       c1.apply();       c2.apply();    }}control Simple();package Top(Simple s);Top(Caller()) main;

The compile-time evaluation of this program produces the structure inFigure 15. Notice that there are two instances of thetable t.These instances must both be exposed to the control plane. To name anobject in this hierarchy, one uses a path composed of the names ofcontaining instances. In this case, the two tables have namess.c1.tands.c2.t, wheres is the name of the argument to thepackage instantiation, which is derived from the name of itscorresponding formal parameter.

evalmultiple


Figure 15. Evaluating a program that has several instantiations of the same component.

17.3.2. Annotations controlling naming

Control plane-related annotations (Section18.3.3) can alter the names exposed tothe control plane in the following ways.

Programs that yield the same fully-qualified name for two differentcontrollable entities are invalid.

17.3.3. Recommendations

The control plane may refer to a controllable entity by a postfix ofits fully qualified name when it is unambiguous in the context inwhich it is used. Consider the following example.

control c(/* parameters omitted*/ )() {action a (/* parameters omitted*/ ) {/* body omitted*/ }table t {        keys = {/* body omitted*/ }        actions = { a; } }}c() c_inst;

Control plane software may refer to actionc_inst.a asawhen inserting rules into tablec_inst.t, because it is clearfrom the definition of the table which actiona refers to.

Not all unambiguous postfix shortcuts are recommended. For instance, consider thefirst example in Section 17.3. One might be tempted torefer tos.c1 simply asc1, as no other instance namedc1appears in the program. However, this leads to a brittleprogram since future modifications can never introduce an instance namedc1,or include libraries of P4 code that contain instances with that name.

17.4. Dynamic evaluation

The dynamic evaluation of a P4 program is orchestrated by the architecturemodel. Each architecture model needs to specify the order and the conditionsunder which the various P4 component programs are dynamicallyexecuted. For example, in the Simple Switch example from Section5.1 the execution flow goesParser->Pipe->Deparser.

Once a P4 execution block is invoked its execution proceeds untiltermination according to the semantics defined in this document.

17.4.1. Concurrency model

A typical packet processing system needs to execute multiplesimultaneous logical“threads.” At the very least there is a threadexecuting the control plane, which can modify the contents of thetables. Architecture specifications should describe in detail theinteractions between the control-plane and the data-plane. The dataplane can exchange information with the control plane throughexternfunction and method calls. Moreover, high-throughput packet-processingsystems may be processing multiple packets simultaneously, e.g., in apipelined fashion, or concurrently parsing a first packet whileperforming match-action operations on a second packet. This sectionspecifies the semantics of P4 programs with respect to such concurrentexecutions.

Each top-levelparser orcontrol block is executed as aseparate thread when invoked by the architecture. All the parametersof the block and all local variables are thread-locali.e., eachthread has a private copy of these resources. This applies to thepacket_inandpacket_out parameters of parsers and deparsers.

As long as a P4 block uses only thread-local storage (e.g., metadata,packet headers, local variables), its behavior in the presence ofconcurrency is identical with the behavior in isolation, since anyinterleaving of statements from different threads must produce thesame output.

In contrast,extern blocks instantiated by a P4 program areglobal, shared across all threads. Ifextern blocks mediateaccess to state (e.g., counters, registers)i.e., the methods oftheextern block read and write state, these stateful operationsare subject to data races. P4 mandates thatexecution of a method call on an extern instance is atomic.

To allow users to express atomic execution of larger code blocks, P4provides an@atomic annotation, which can be applied to blockstatements, parser states, control blocks, or whole parsers.

Consider the following example:

extern Register {/* body omitted*/ }control Ingress() {  Register() r;table flowlet {/* read state of r in an action*/ }table new_flowlet {/* write state of r in an action*/ }apply {    @atomic {       flowlet.apply();if (ingress_metadata.flow_ipg > FLOWLET_INACTIVE_TIMEOUT)          new_flowlet.apply();}}}

This program accesses an extern objectr of typeRegisterin actions invoked from tablesflowlet (reading) andnew_flowlet(writing). Without the@atomic annotation these two operationswould not execute atomically: a second packet may read the state ofrbefore the first packet had a chance to update it.

Note that even within anaction definition, if the action doessomething like reading a register, modifying it, and writing it back,in a way that only the modified value should be visible to the nextpacket, then, to guarantee correct execution in all cases, thatportion of the action definition should be enclosed within a blockannotated with@atomic.

A compiler backend must reject a program containing@atomicblocks if it cannot implement the atomic execution of the instructionsequence. In such cases, the compiler should provide reasonablediagnostics.

18. Annotations

Annotations are similar to C# attributes and Java annotations. They are a simplemechanism for extending the P4 language to some limited degree without changingthe grammar. To some degree they subsume C#pragmas. Annotations are attachedto types, fields, variables, etc. using the@ syntax (as shown explicitly inthe P4 grammar). Unstructured annotations, or just“annotations,” have anoptional body; structured annotations have a mandatory body, containing at leasta pair of square brackets[].

optAnnotations    :/* empty*/    | annotations    ;annotations    : annotation    | annotations annotation    ;annotation    :'@' name    |'@' name'(' annotationBody')'    |'@' name'[' structuredAnnotationBody']'    ;

Structured annotations and unstructured annotations on any one element must notuse the samename. Thus, a givenname can only be applied to one type ofannotation or the other for any one element. An annotation used on one elementdoes not affect the annotation on another because they have different scope.

This is legal:

@my_anno(1) table T { /* body omitted */ }@my_anno[2] table U { /* body omitted */ } // OK - different scope than previous use of my_anno

This is illegal:

@my_anno(1)@my_anno[2] table U { /* body omitted */ } // Error - changed type of anno on an element

Multiple unstructured annotations using the samename can appear on a givenelement; they are cumulative. Each one will be bound to that element. Incontrast, only one structured annotation using a givenname may appear on anelement; multiple uses of the samename will produce an error.

This is legal:

@my_anno(1)@my_anno(2) table U { /* body omitted */ }  // OK - unstructured annos accumulate

This is illegal:

@my_anno[1]@my_anno[2] table U { /* body omitted */ } // Error - reused the same structured anno on an element

18.1. Bodies of Unstructured Annotations

The flexibility of P4 unstructured annotations comes from the minimal structuremandated by the P4 grammar: unstructured annotation bodies may contain anysequence of terminals, so long as parentheses are balanced. In the followinggrammar fragment, theannotationToken non-terminal represents any terminalproduced by the lexer, including keywords, identifiers, string and integerliterals, and symbols, but excluding parentheses.

annotationBody    :/* empty*/    | annotationBody'(' annotationBody')'    | annotationBody annotationToken

Unstructured annotations may impose additional structure on their bodies, andare not confined to the P4 language. For example, the P4Runtime specificationdefines a@pkginfo annotation that expects key-value pairs.

18.2. Bodies of Structured Annotations

Unlike unstructured annotations, structured annotations use square brackets[...] and have a restricted format. They are commonly used to declare custommetadata, consisting of expression lists or key-value lists but not both. AnexpressionList may be empty or contain a comma-separated list of memberexpressions. AkvList consists of one or morekvPairs, each consisting ofa key and a valueexpression. Note the syntax forexpression is rich, seeAppendix H for details.

Allexpressions within astructuredAnnotationBody must be compile-time knownvalues, either literals or expressions requiring compile-time evaluation, with aresult type that is one of: string literals, infinite-precision integer, orboolean. Structuredexpressions (e.g. anexpression containing anexpressionList, akvList, etc.) are not allowed. Note that P4Runtimeinformation (P4Info) may stipulate additional restrictions. For example, aninteger expression might be limited to 64-bit values.

It is illegal to duplicate akey within thekvList of a structuredannotation.

structuredAnnotationBody    : expressionList    | kvList    ;...expressionList    :/* empty*/    | expression    | expressionList',' expression    ;...kvList    : kvPair    | kvList',' kvPair    ;kvPair    : name'=' expression    ;

18.2.1. Structured Annotation Examples

Empty Expression List

The following example produces an empty annotation:

@Empty[]table t {/* body omitted*/}

Mixed Expression List

The following example will produce an effective expression list as follows:

[1,"hello",true,false,11]
#define TEXT_CONST "hello"#define NUM_CONST 6@MixedExprList[1,TEXT_CONST,true,1==2,5+NUM_CONST]table t {/* body omitted*/}

kvList of Strings

@Labels[short="Short Label", hover="My Longer Table Label to appear in hover-help"]table t {/* body omitted*/}

kvList of Mixed Expressions

The following example will produce an effective kvList as follows.

[label="text", my_bool=true, int_val=6]
@MixedKV[label="text", my_bool=true, int_val=2*3]table t {/* body omitted*/}

Illegal Mixing ofkvPair andexpressionList

The following example is invalid because the body contains both akvPair andanexpression:

@IllegalMixing[key=4, 5] // illegal mixingtable t {/* body omitted*/}

Illegal Duplicate Key

The following example is invalid because the same key occurs more than once:

@DupKey[k1=4,k1=5] // illegal duplicate keytable t {/* body omitted*/}

Illegal Duplicate Structured Annotation

The following example is invalid because the annotationname occurs morethan once on the same element, e.g.table t:

@DupAnno[k1=4]@DupAnno[k2=5] // illegal duplicate nametable t {/* body omitted*/}

Illegal Simultaneous Use of Both Structured and Unstructured Annotation

The following example is invalid because the annotationname is used by bothan unstructured and structured annotation on the same elementtable t:

@MixAnno("Anything")@MixAnno[k2=5] // illegal use in both annotation typestable t {/* body omitted*/}

18.3. Predefined annotations

Annotation names that start with lowercase letters are reserved forthe standard library and architecture. This document pre-defines aset of“standard” annotations in Appendix C.We expect that this list will grow.We encourage custom architectures to define annotations starting witha manufacturer prefix: e.g., an organization named X would useannotations named like@X_annotation

18.3.1. Optional parameter annotations

A parameter to a package, extern method, extern function or externobject constructor can be annotated with@optional to indicate thatthe user does not need to provide a corresponding argument for thatparameter. The meaning of a parameter with no supplied value istarget-dependent.

18.3.2. Annotations on the table action list

The following two annotations can be used to give additionalinformation to the compiler and control-plane about actions in atable. These annotations have no bodies.

table t {    actions = {       a,// can appear anywhere       @tableonly b,   // can only appear in the table       @defaultonly c, // can only appear in the default action    }/* body omitted*/}

18.3.3. Control-plane API annotations

The@name annotation directs the compiler to use a differentlocal name when generating the external APIs used to manipulate alanguage element from the control plane. This annotation takes a string literalbody. In thefollowing example, the fully-qualified name of the table isc_inst.t1.

control c(/* parameters omitted*/ )() {    @name("t1") table t { /* body omitted */ }apply {/* body omitted*/ }}c() c_inst;

The@hidden annotation hides a controllable entity, e.g. a table,key, action, or extern, from the control plane. This effectivelyremoves its fully-qualified name (Section 17.3). This annotationdoes not have a body.

18.3.3.1. Restrictions

Each element may be annotated with at most one@nameor@hidden annotation, and each control plane name must refer toat most one controllable entity. This is of special concern whenusing an absolute@name annotation: if a type containing a@nameannotation with an absolute pathname (i.e., one starting with a dot)is instantiated more than once, it will result in the samename referring to two controllable entities.

control noargs();package top(noargs c1, noargs c2);control c() {    @name(".foo.bar") table t { /* body omitted */ }apply {/* body omitted*/ }}top(c(), c()) main;

Without the@name annotation, this program would producetwo controllable entities with fully-qualified namesmain.c1.t andmain.c2.t.However, the@name(".foo.bar") annotation renames tabletin both instances tofoo.bar, resulting in one name that refersto two controllable entities, which is illegal.

18.3.4. Concurrency control annotations

The@atomic annotation, described in Section 17.4.1can be used to enforce the atomic execution of a code block.

18.3.5. Value set annotations

The@match annotation, described in Section 12.6, is usedto specify amatch_kind value other than the defaultmatch_kind ofexact for a field of avalue_set.

18.3.6. Extern function/method annotations

Various annotations may appear on extern function and methoddeclarations to describe limitations on the behavior and interactionsof those functions. By default extern functions might have any effecton the environment of the P4 program and might interact in non-trivialways (subject to a few limitations see section6.7.1). Since externs arearchitecture-specific and their behavior is known to the architecturedefinition, these annotations are not strictly necessary (animplementation can have knowledge of how externs interact based ontheir names built into it), but these annotations provide a uniformway of describing certain well-defined interactions (or theirabsence), allowing architecture-independent analysis of P4 programs.

18.3.7. Deprecated annotation

Thedeprecated annotation has a required string argument that is amessage that will be printed by a compiler when a program is using thedeprecated construct. This is mostly useful for annotating libraryconstructs, such as externs.

@deprecated("Please use the 'check' function instead")extern Checker {/* body omitted*/}

18.3.8. No warnings annotation

ThenoWarn annotation has a required string argument that indicatesa compiler warning that will be inhibited. For example@noWarn("unused") on a declaration will prevent a compiler warningif that declaration is not used.

18.4. Target-specific annotations

Each P4 compiler implementation can define additional annotationsspecific to the target of the compiler. The syntax of the annotationsshould conform to the above description. The semantics of suchannotations is target-specific. They could be used in a similar way topragmas in other languages.

The P4 compiler should provide:

A. Appendix: Revision History

ReleaseRelease DateSummary of Changes
1.0.0 May 17, 2017 Initial version.
1.1.0 November 26, 2018 Added top-level functions, optional and named parameters,
enum representations, parser value sets, type definitions,
saturating arithmetic, and structured annotations.
Removedglobalname annotation and added a tablesize property.
Clarified semantics of operations on invalid headers, added
restrictions on arguments to calls, and modified precedence of
bitwise operators.
1.2.0 October 14, 2019 Added errorParserInvalidArgument, order ofconst entries,
header size methods, 1-bit signed values, signed bit slices, empty
tuples,@deprecated annotation, free-form annotations,int type
table.apply().miss,string type.
1.2.1 June 11, 2020 Added structure-value expressions, default values, concatenation,
structured annotations; standardized several new annotations;
generalized typing rule for masks; restricted typing rule for
shifts with infinite-precistion operands; clarified evaluation
order for table keys; clarified copy-out behavior; clarified
semantics of invalid header stacks; clarified initialization
semantics; fixed several small issues in grammar.
1.2.2 May 17, 2021 Added support for tuple access, generic structures, additional
enumeration types, abstract methods, conditional and empty
statements in parsers, additional casts, and 0-width types;
clarified semantics of default actions, headers, empty types, and
action data; fixed typos and inconsistencies in the grammar.

A.1. Summary of changes made in version 1.2.2

A.2. Summary of changes made in version 1.2.1

A.3. Summary of changes made in version 1.2.0

A.4. Summary of changes made in version 1.1.0

B. Appendix: P4 reserved keywords

The following table shows all P4 reserved keywords. Some identifiersare treated as keywords only in specific contexts (e.g., the keywordactions).

‘abstract’actionapplybit
boolconstcontroldefault
elseenumerrorextern
exitfalseheaderheader_union
ifininoutint
match_kindpackageparserout
returnselectstatestruct
switchtablethistransition
truetupletypedefvarbit
verifyvoid

C. Appendix: P4 reserved annotations

The following table shows all P4 reserved annotations.

Annotation Purpose See Section
atomic specify atomic execution 17.4.1
defaultonly action can only appear in the default action 18.3.2
hidden hides a controllable entity from the control plane 17.3.2
match specifymatch_kind of a field in avalue_set 18.3.5
name assign local control-plane name 17.3.2
optional parameter is optional 18.3.1
tableonly action cannot be a default_action 18.3.2
deprecated Construct has been deprecated 18.3.7
pure pure function 18.3.6
noSideEffects function with no side effects 18.3.6
noWarn Has a string argument; inhibits compiler warnings 18.3.8

D. Appendix: P4 core library

The P4 core library contains declarations that are useful to mostprograms.

For example, the core library includes the declarations of thepredefinedpacket_in andpacket_out extern objects, usedin parsers and deparsers to access packet data.

/// Standard error codes.  New error codes can be declared by users.error {    NoError,/// No error.    PacketTooShort,/// Not enough bits in packet for 'extract'.    NoMatch,/// 'select' expression has no matches.    StackOutOfBounds,/// Reference to invalid element of a header stack.    HeaderTooShort,/// Extracting too many bits into a varbit field.    ParserTimeout,/// Parser execution time limit exceeded.    ParserInvalidArgument/// Parser operation was called with a value/// not supported by the implementation.}extern packet_in {/// Read a header from the packet into a fixed-sized header @hdr/// and advance the cursor./// May trigger error PacketTooShort or StackOutOfBounds./// @T must be a fixed-size header typevoid extract<T>(out T hdr);/// Read bits from the packet into a variable-sized header @variableSizeHeader/// and advance the cursor./// @T must be a header containing exactly 1 varbit field./// May trigger errors PacketTooShort, StackOutOfBounds, or HeaderTooShort.void extract<T>(out T variableSizeHeader,inbit<32> variableFieldSizeInBits);/// Read bits from the packet without advancing the cursor./// @returns: the bits read from the packet./// T may be an arbitrary fixed-size type.    T lookahead<T>();/// Advance the packet cursor by the specified number of bits.void advance(inbit<32> sizeInBits);/// @return packet length in bytes.  This method may be unavailable on/// some target architectures.bit<32> length();}extern packet_out {/// Write @data into the output packet, skipping invalid headers/// and advancing the cursor/// @T can be a header type, a header stack, a header_union, or a struct/// containing fields with such types.void emit<T>(in T data);}action NoAction() {}/// Standard match kinds for table key fields./// Some architectures may not support all these match kinds./// Architectures can declare additional match kinds.match_kind {/// Match bits exactly.    exact,/// Ternary match, using a mask.    ternary,/// Longest-prefix match.    lpm}

E. Appendix: Checksums

There are no built-in constructs in P416 for manipulating packetchecksums. We expect that checksum operations can be expressed asexternlibrary objects that are provided in target-specific libraries. Thestandard architecture library should provide such checksum units.

For example, one could provide an incremental checksum unitChecksum16(also described in the VSS example in Section 5.2.4) forcomputing 16-bit one's complement using anextern object with asignature such as:

extern Checksum16 {    Checksum16();// constructorvoid clear();// prepare unit for computationvoid update<T>(in T data);// add data to checksumvoid remove<T>(in T data);// remove data from existing checksumbit<16> get();// get the checksum for the data added since last clear}

IP checksum verification could be done in a parser as:

ck16.clear();// prepare checksum unitck16.update(h.ipv4);// write headerverify(ck16.get() ==16w0,error.IPv4ChecksumError);// check for 0 checksum

IP checksum generation could be done as:

h.ipv4.hdrChecksum =16w0;ck16.clear();ck16.update(h.ipv4);h.ipv4.hdrChecksum = ck16.get();

Moreover, some switch architectures do not perform checksumverification, but only update checksums incrementally to reflectpacket modifications. This could be achieved as well, as the followingP4 program fragments illustrates:

ck16.clear();ck16.update(h.ipv4.hdrChecksum);// original checksumck16.remove( { h.ipv4.ttl, h.ipv4.proto } );h.ipv4.ttl = h.ipv4.ttl -1;ck16.update( { h.ipv4.ttl, h.ipv4.proto } );h.ipv4.hdrChecksum = ck16.get();

F. Appendix: Restrictions on compile time and run time calls

This appendix summarizes restrictions on compile time and run timecalls that can be made. Many of them are described earlier in thisdocument, but are collected here for easy reference.

The stateful types of objects in P416 are packages, parsers,controls, externs, tables, and value-sets. P416 functions are alsoconsidered to be in that group, even if they happen to be purefunctions of their arguments. All other types are referred to as“value types” here.

Some guiding principles:

A note on recursion: It is expected that some architectures willdefine capabilities for recirculating a packet to be processed againas if it were a newly arriving packet, or to make“clones” of packetsthat are then processed by parsers and/or control blocks that theoriginal packet has already completed. This does not change the notesabove on recursion that apply while a parser or control is executing.

The first table lists restrictions on what types can be passed asconstructor parameters to other types.

can be a constructor parameter for this type
This type package parser control extern
package yes no no no
parser yes yes no no
control yes no yes no
extern yes yes yes yes
function no no no no
table no no no no
value-set no no no no
value types yes yes yes yes

The next table lists restrictions on where one may performinstantiations (see Section 10.3) of different types.The answer forpackage is always“no” because there is no“inside a package” where instantiations can be written in P416. Onecan definitely make constructor calls and use instances of statefultypes as parameters when instantiating a package, and restrictions onthose types are in the table above.

For externs, one can only specify their interface in P416, not theirimplementation. Thus there is no place to instantiate objects withinan extern.

You may declare variables and constants of any of the value typeswithin a parser, control, or function (see Section 10.2 for moredetails). Declaring a variable or constant is not the same asinstantiation, hence the answer“N/A” (for not applicable) in thosetable entries. Variables may not be declared at the top level of yourprogram, but constants may.

can be instantiated in this place
This type top level package parser control extern function
package yes no no no no no
parser no no yes no no no
control no no no yes no no
extern yes no yes yes no no
function yes no no no no no
table no no no yes no no
value-set yes no yes no no no
value types N/A N/A N/A N/A N/A N/A

The next table lists restrictions on what types can be passed asrun-time parameters to other callable things that have run-timeparameters: parsers, controls, extern methods, actions, and functions.

can be a run-time parameter to this callable thing
This type parser control method action function
package no no no no no
parser no no no no no
control no no no no no
extern yes yes yes no no
table no no no no no
value-set no no no no no
action no no no no no
function no no no no no
value types yes yes yes yes yes

Extern method calls may only return a value that is a valuetype, or no value at all (specified by a return type ofvoid).

The next table lists restrictions on what kinds of calls can be madefrom which places in a P4 program. Calling a parser, control, ortable means invoking itsapply() method. Calling a value-set means using itin a select expression. The row forextern describes where extern methodcalls can be made from.

One way that an extern can be called from the top level of a parser orcontrol is in an initializer expression for a declared variable,e.g.bit<32> x = rand.get();.

can be called at run time from this place in a P4 program
control parser or
parser apply control
This type state block top level action extern function
package N/A N/A N/A N/A N/A N/A
parser yes no no no no no
control no yes no no no no
extern yes yes yes yes no no
table no yes no no no no
value-set yes no no no no no
action no yes no yes no no
function yes yes no yes no yes
value types N/A N/A N/A N/A N/A N/A

There may not be any recursion in calls, neither by a thing callingitself directly, nor mutual recursion.

An extern can never cause any other type of P4 program object to becalled. See Section 6.7.1.

Actions may be called directly from a controlapply block.

Note that while the extern row shows that extern methods can be calledfrom many places, particular externs may have additional restrictionsnot listed in this table. Any such restrictions should be documentedin the description for each extern, as part of the documentation forthe architecture that defines the extern.

In many cases, the restriction will be“from a parser state only” or“from a control apply block or action only”, but it may be even morerestrictive, e.g. only from a particular kind of control blockinstantiated in a particular role in an architecture.

G. Appendix: Open Issues

There are a number of open issues that are currently under discussionin the P4 design working group. A brief summary of these issues ishighlighted in this section. We seek input on these issues from thecommunity, and encourage experimenting with different implementationsin the compiler before converging on the specification.

G.1. Generalized switch statement behavior

P416 includes bothswitch statements 11.7 andselectexpressions 12.6. There are real differences in the currentversion of the language expression vs. statement, and the lattermust evaluate to a state value.

We propose generalizingswitch statements to match the designused in most programming language: a multi-way conditional thatexecutes the first branch that matches from a list of cases.

switch(e1,/* parameters omitted*/,en) {  pat_1 : stmt1;/* body omitted*/  pat_m : stmtm;}

Here, the value being scrutinized is given by a tuple(e1,/* parameters omitted*/,en),and the patterns are given by expressions that denote sets ofvalues. The value matches a branch if it is an element of the setdenoted by the pattern. Unlike C and C++, there is no break statementso control“falls through” to the next case only when there is nostatement associated with the case label.

This design is intended to capture the standard semantics ofswitchstatements as well as a common idiom in P4 parsers where they are usedto control transitions to different parser states depending on thevalues of one or more previously-parsed values. Usingswitchstatements, we can also generalize the design for parsers, eliminatingselect and lifting most restrictions on which kinds of statements mayappear in a state. In particular, we allow conditional statements andselectstatements, which may be nested arbitrarily. This language can betranslated into more restricted versions, where the body of each statecomprised a sequence of variable declarations, assignments, and methodinvocations followed by a singletransition statement byintroducing new states.

We also generalize the design for processing of table hit/miss andactions in control blocks, by generating implicit types for actionsand results.

The counter-argument to this proposal is that the semantics ofselectin the parser is sufficiently distinct from theswitchstatement, and moreover these are constructs that network programmersare already familiar with, and they are typically mapped veryefficiently onto a variety of targets.

G.2. Undefined behaviors

The presence of undefined behavior has caused numerous problems inlanguages like C and HTML, including bugs and serious securityvulnerabilities. There are a few places where evaluating a P4 programcan result in undefined behaviors: out parameters, uninitializedvariables, accessing header fields of invalid headers, and accessingheader stacks with an out of bounds index. We think we should makeevery attempt to avoid undefined behaviors in P416, and therefore wepropose to strengthen the wording in the specification, such that bydefault, we rule out programs that exhibit the behaviors mentionedabove. Given the concern for performance, we propose to definecompiler flags and/or pragmas that can override the safebehavior. However, our expectation is that programmers should beguided toward writing safe programs, and encouraged to think harderwhen excepting from the safe behavior.

G.3. Structured Iteration

Introducing aforeach style iterator for operating over headerstacks will alleviate the need of using C preprocessor directives tospecify the size of header stacks.

For example:

foreach hdrin hdrs {/// operations over HDR}

Since the stacks are always known statically (at compile-time), thecompiler could transform theforeach statement into thereplicated code with explicit index references at compile-time. Thishas the advantage of allowing the code to be written without regard toa parameterized header stack length.

Since the compiler can statically determine the number of operationsthat would result from theforeach it can also reject a programif the result requires more action resources than are available, orcan split the action code up to fit available resources as needed.

H. Appendix: P4 grammar

This is the grammar of P416 written using the YACC/bisonlanguage. Absent from this grammar is the precedence of variousoperations.

The grammar is actually ambiguous, so the lexer and the parser mustcollaborate for parsing the language. In particular, the lexer must beable to distinguish two kinds of identifiers:

The parser has to use a symbol table to indicate to the lexer how toparse subsequent appearances of identifiers. For example, given thefollowing program fragment:

typedefbit<4> t;struct s {/* body omitted*/}t x;parser p(bit<8> b) {/* body omitted*/ }

The lexer has to return the following terminal kinds:

t - TYPE_IDENTIFIERs - TYPE_IDENTIFIERx - IDENTIFIERp - TYPE_IDENTIFIERb - IDENTIFIER

This grammar has been heavily influenced by limitations of the Bisonparser generator tool.

Several other constant terminals appear in these rules:

- MASK is &&&- RANGE is ..- DONTCARE is _

TheSTRING_LITERAL token corresponds to a string literalenclosed within double quotes, as described in Section6.3.3.3.

All other terminals are uppercase spellings of the correspondingkeywords. For example,RETURN is the terminal returned by thelexer when parsing the keyword return.

p4program    :/* empty*/    | p4program declaration    | p4program';'/* empty declaration*/    ;declaration    : constantDeclaration    | externDeclaration    | actionDeclaration    | parserDeclaration    | typeDeclaration    | controlDeclaration    | instantiation    | errorDeclaration    | matchKindDeclaration    | functionDeclaration    ;nonTypeName    : IDENTIFIER    | APPLY    | KEY    | ACTIONS    | STATE    | ENTRIES    | TYPE    ;name    : nonTypeName    | TYPE_IDENTIFIER    ;nonTableKwName   : IDENTIFIER   | TYPE_IDENTIFIER   | APPLY   | STATE   | TYPE   ;optAnnotations    :/* empty*/    | annotations    ;annotations    : annotation    | annotations annotation    ;annotation    :'@' name    |'@' name'(' annotationBody')'    |'@' name'[' structuredAnnotationBody']'    ;parameterList    :/* empty*/    | nonEmptyParameterList    ;nonEmptyParameterList    : parameter    | nonEmptyParameterList',' parameter    ;parameter    : optAnnotations direction typeRef name    | optAnnotations direction typeRef name'=' expression    ;direction    : IN    | OUT    | INOUT    |/* empty*/    ;packageTypeDeclaration    : optAnnotations PACKAGE name optTypeParameters'(' parameterList')'    ;instantiation    : typeRef'(' argumentList')' name';'    | annotations typeRef'(' argumentList')' name';'    | annotations typeRef'(' argumentList')' name'=' objInitializer';'    | typeRef'(' argumentList')' name'=' objInitializer';'    ;objInitializer    :'{' objDeclarations'}'    ;objDeclarations    :/* empty*/    | objDeclarations objDeclaration    ;objDeclaration    : functionDeclaration    | instantiation    ;optConstructorParameters    :/* empty*/    |'(' parameterList')'    ;dotPrefix    :'.'    ;/**************************** PARSER******************************/parserDeclaration    : parserTypeDeclaration optConstructorParameters/* no type parameters allowed in the parserTypeDeclaration*/'{' parserLocalElements parserStates'}'    ;parserLocalElements    :/* empty*/    | parserLocalElements parserLocalElement    ;parserLocalElement    : constantDeclaration    | variableDeclaration    | instantiation    | valueSetDeclaration    ;parserTypeDeclaration    : optAnnotations PARSER name optTypeParameters'(' parameterList')'    ;parserStates    : parserState    | parserStates parserState    ;parserState    : optAnnotations STATE name'{' parserStatements transitionStatement'}'    ;parserStatements    :/* empty*/    | parserStatements parserStatement    ;parserStatement    : assignmentOrMethodCallStatement    | directApplication    | parserBlockStatement    | constantDeclaration    | variableDeclaration    | emptyStatement    | conditionalStatement    ;parserBlockStatement    : optAnnotations'{' parserStatements'}'    ;transitionStatement    :/* empty*/    | TRANSITION stateExpression    ;stateExpression    : name';'    | selectExpression    ;selectExpression    : SELECT'(' expressionList')''{' selectCaseList'}'    ;selectCaseList    :/* empty*/    | selectCaseList selectCase    ;selectCase    : keysetExpression':' name';'    ;keysetExpression    : tupleKeysetExpression    | simpleKeysetExpression    ;tupleKeysetExpression    :"(" simpleKeysetExpression"," simpleExpressionList")"    |"(" reducedSimpleKeysetExpression")"    ;simpleExpressionList    : simpleKeysetExpression    | simpleExpressionList',' simpleKeysetExpression    ;reducedSimpleKeysetExpression    : expression"&&&" expression    | expression".." expression    | DEFAULT    |"_"    ;simpleKeysetExpression    : expression    | DEFAULT    | DONTCARE    | expression MASK expression    | expression RANGE expression    ;valueSetDeclaration  : optAnnotations      VALUESET'<' baseType'>''(' expression')' name';'  | optAnnotations      VALUESET'<' tupleType'>''(' expression')' name';'  | optAnnotations      VALUESET'<' typeName'>''(' expression')' name';'  ;/*************************** CONTROL************************/controlDeclaration    : controlTypeDeclaration optConstructorParameters/* no type parameters allowed in controlTypeDeclaration*/'{' controlLocalDeclarations APPLY controlBody'}'    ;controlTypeDeclaration    : optAnnotations CONTROL name optTypeParameters'(' parameterList')'    ;controlLocalDeclarations    :/* empty*/    | controlLocalDeclarations controlLocalDeclaration    ;controlLocalDeclaration    : constantDeclaration    | actionDeclaration    | tableDeclaration    | instantiation    | variableDeclaration    ;controlBody    : blockStatement    ;/*************************** EXTERN*************************/externDeclaration    : optAnnotations EXTERN nonTypeName optTypeParameters'{' methodPrototypes'}'    | optAnnotations EXTERN functionPrototype';'    ;methodPrototypes    :/* empty*/    | methodPrototypes methodPrototype    ;functionPrototype    : typeOrVoid name optTypeParameters'(' parameterList')'    ;methodPrototype    : optAnnotations functionPrototype';'    | optAnnotations TYPE_IDENTIFIER'(' parameterList')'';'    ;/************************** TYPES****************************/typeRef    : baseType    | typeName    | specializedType    | headerStackType    | tupleType    ;namedType    : typeName    | specializedType    ;prefixedType    : TYPE_IDENTIFIER    | dotPrefix TYPE_IDENTIFIER    ;typeName    : prefixedType    ;tupleType    : TUPLE'<' typeArgumentList'>'    ;headerStackType    : typeName'[' expression']'    | specializedType'[' expression']'    ;specializedType    : prefixedType'<' typeArgumentList'>'    ;baseType    : BOOL    | ERROR    | STRING    | INT    | BIT    | BIT'<' INTEGER'>'    | INT'<' INTEGER'>'    | VARBIT'<' INTEGER'>'    | BIT'<''(' expression')''>'    | INT'<''(' expression')''>'    | VARBIT'<''(' expression')''>'    ;typeOrVoid    : typeRef    | VOID    | IDENTIFIER// may be a type variable    ;optTypeParameters    :/* empty*/    | typeParameters    ;typeParameters    :'<' typeParameterList'>'    ;typeParameterList    : name    | typeParameterList',' name    ;realTypeArg    : DONTCARE    | typeRef    | VOID    ;typeArg    : DONTCARE    | typeRef    | nonTypeName    | VOID    ;realTypeArgumentList    : realTypeArg    | realTypeArgumentList COMMA typeArg    ;typeArgumentList    :/* empty*/    | typeArg    | typeArgumentList',' typeArg    ;typeDeclaration    : derivedTypeDeclaration    | typedefDeclaration    | parserTypeDeclaration';'    | controlTypeDeclaration';'    | packageTypeDeclaration';'    ;derivedTypeDeclaration    : headerTypeDeclaration    | headerUnionDeclaration    | structTypeDeclaration    | enumDeclaration    ;headerTypeDeclaration    : optAnnotations HEADER name optTypeParameters'{' structFieldList'}'    ;headerUnionDeclaration    : optAnnotations HEADER_UNION name optTypeParameters'{' structFieldList'}'    ;structTypeDeclaration    : optAnnotations STRUCT name optTypeParameters'{' structFieldList'}'    ;structFieldList    :/* empty*/    | structFieldList structField    ;structField    : optAnnotations typeRef name';'    ;enumDeclaration    : optAnnotations ENUM name'{' identifierList'}'    | optAnnotations ENUM typeRef name'{' specifiedIdentifierList'}'    ;errorDeclaration    : ERROR'{' identifierList'}'    ;matchKindDeclaration    : MATCH_KIND'{' identifierList'}'    ;identifierList    : name    | identifierList',' name    ;specifiedIdentifierList    : specifiedIdentifier    | specifiedIdentifierList',' specifiedIdentifier    ;specifiedIdentifier    : name'=' initializer    ;typedefDeclaration    : optAnnotations TYPEDEF typeRef name';'    | optAnnotations TYPEDEF derivedTypeDeclaration name';'    | optAnnotations TYPE typeRef name';'    | optAnnotations TYPE derivedTypeDeclaration name';'    ;/*************************** STATEMENTS*************************/assignmentOrMethodCallStatement    : lvalue'(' argumentList')'';'    | lvalue'<' typeArgumentList'>''(' argumentList')'';'    | lvalue'='  expression';'    ;emptyStatement    :';'    ;returnStatement    : RETURN';'    | RETURN expression';'    ;exitStatement    : EXIT';'    ;conditionalStatement    : IF'(' expression')' statement    | IF'(' expression')' statement ELSE statement    ;// To support direct invocation of a control or parser without instantiationdirectApplication    : typeName'.' APPLY'(' argumentList')'';'    ;statement    : assignmentOrMethodCallStatement    | directApplication    | conditionalStatement    | emptyStatement    | blockStatement    | exitStatement    | returnStatement    | switchStatement    ;blockStatement    : optAnnotations'{' statOrDeclList'}'    ;statOrDeclList    :/* empty*/    | statOrDeclList statementOrDeclaration    ;switchStatement    : SWITCH'(' expression')''{' switchCases'}'    ;switchCases    :/* empty*/    | switchCases switchCase    ;switchCase    : switchLabel':' blockStatement    | switchLabel':'    ;switchLabel    : DEFAULT    | nonBraceExpression    ;statementOrDeclaration    : variableDeclaration    | constantDeclaration    | statement    | instantiation    ;/************ TABLES*************/tableDeclaration    : optAnnotations TABLE name'{' tablePropertyList'}'    ;tablePropertyList    : tableProperty    | tablePropertyList tableProperty    ;tableProperty    : KEY'=''{' keyElementList'}'    | ACTIONS'=''{' actionList'}'    | optAnnotations CONST ENTRIES'=''{' entriesList'}'/* immutable entries*/    | optAnnotations CONST nonTableKwName'=' initializer';'    | optAnnotations nonTableKwName'=' initializer';'    ;keyElementList    :/* empty*/    | keyElementList keyElement    ;keyElement    : expression':' name optAnnotations';'    ;actionList    :/* empty*/    | actionList optAnnotations actionRef';'    ;actionRef    : prefixedNonTypeName    | prefixedNonTypeName'(' argumentList')'    ;entriesList    : entry    | entriesList entry    ;entry    : keysetExpression':' actionRef optAnnotations';'    ;/************************* ACTION********************************/actionDeclaration    : optAnnotations ACTION name'(' parameterList')' blockStatement    ;/************************* VARIABLES*****************************/variableDeclaration    : annotations typeRef name optInitializer';'    | typeRef name optInitializer';'    ;constantDeclaration    : optAnnotations CONST typeRef name'=' initializer';'    ;optInitializer    :/* empty*/    |'=' initializer    ;initializer    : expression    ;/************************* Expressions****************************/functionDeclaration    : functionPrototype blockStatement    ;argumentList    :/* empty*/    | nonEmptyArgList    ;nonEmptyArgList    : argument    | nonEmptyArgList',' argument    ;argument    : expression    | name'=' expression    | DONTCARE    ;kvList    : kvPair    | kvList',' kvPair    ;kvPair    : name'=' expression    ;expressionList    :/* empty*/    | expression    | expressionList',' expression    ;annotationBody    :/* empty*/    | annotationBody'(' annotationBody')'    | annotationBody annotationToken    ;structuredAnnotationBody    : expressionList    | kvList    ;annotationToken    : ABSTRACT    | ACTION    | ACTIONS    | APPLY    | BOOL    | BIT    | CONST    | CONTROL    | DEFAULT    | ELSE    | ENTRIES    | ENUM    | ERROR    | EXIT    | EXTERN    | FALSE    | HEADER    | HEADER_UNION    | IF    | IN    | INOUT    | INT    | KEY    | MATCH_KIND    | TYPE    | OUT    | PARSER    | PACKAGE    | PRAGMA    | RETURN    | SELECT    | STATE    | STRING    | STRUCT    | SWITCH    | TABLE    | TRANSITION    | TRUE    | TUPLE    | TYPEDEF    | VARBIT    | VALUESET    | VOID    |"_"    | IDENTIFIER    | TYPE_IDENTIFIER    | STRING_LITERAL    | INTEGER    |"&&&"    |".."    |"<<"    |"&&"    |"||"    |"=="    |"!="    |">="    |"<="    |"++"    |"+"    |"|+|"    |"-"    |"|-|"    |"*"    |"/"    |"%"    |"|"    |"&"    |"^"    |"~"    |"["    |"]"    |"{"    |"}"    |"<"    |">"    |"!"    |":"    |","    |"?"    |"."    |"="    |";"    |"@"    | UNKNOWN_TOKEN    ;member    : name    ;prefixedNonTypeName    : nonTypeName    | dotPrefix nonTypeName    ;lvalue    : prefixedNonTypeName    | THIS    | lvalue'.' member    | lvalue'[' expression']'    | lvalue'[' expression':' expression']'    ;%left','%nonassoc'?'%nonassoc':'%left'||'%left'&&'%left'==' '!='%left'<''>''<=' '>='%left'|'%left'^'%left'&'%left'<<' '>>'%left'++' '+' '-' '|+|' '|-|'%left'*''/''%'%right PREFIX%nonassoc']''(''['%left'.'// Additional precedences need to be specifiedexpression    : INTEGER    | TRUE    | FALSE    | THIS    | STRING_LITERAL    | nonTypeName    | dotPrefix nonTypeName    | expression'[' expression']'    | expression'[' expression':' expression']'    |'{' expressionList'}'    |'{' kvList'}'    |'(' expression')'    |'!' expression %prec PREFIX    |'~' expression %prec PREFIX    |'-' expression %prec PREFIX    |'+' expression %prec PREFIX    | typeName'.' member    | ERROR'.' member    | expression'.' member    | expression'*' expression    | expression'/' expression    | expression'%' expression    | expression'+' expression    | expression'-' expression    | expression'|+|' expression    | expression'|-|' expression    | expression'<<' expression    | expression'>>' expression    | expression'<=' expression    | expression'>=' expression    | expression'<' expression    | expression'>' expression    | expression'!=' expression    | expression'==' expression    | expression'&' expression    | expression'^' expression    | expression'|' expression    | expression'++' expression    | expression'&&' expression    | expression'||' expression    | expression'?' expression':' expression    | expression'<' realTypeArgumentList'>''(' argumentList')'    | expression'(' argumentList')'    | namedType'(' argumentList')'    |'(' typeRef')' expression    ;nonBraceExpression    : INTEGER    | STRING_LITERAL    | TRUE    | FALSE    | THIS    | nonTypeName    | dotPrefix nonTypeName    | nonBraceExpression'[' expression']'    | nonBraceExpression'[' expression':' expression']'    |'(' expression')'    |'!' expression %prec PREFIX    |'~' expression %prec PREFIX    |'-' expression %prec PREFIX    |'+' expression %prec PREFIX    | typeName'.' member    | ERROR'.' member    | nonBraceExpression'.' member    | nonBraceExpression'*' expression    | nonBraceExpression'/' expression    | nonBraceExpression'%' expression    | nonBraceExpression'+' expression    | nonBraceExpression'-' expression    | nonBraceExpression'|+|' expression    | nonBraceExpression'|-|' expression    | nonBraceExpression'<<' expression    | nonBraceExpression'>>' expression    | nonBraceExpression'<=' expression    | nonBraceExpression'>=' expression    | nonBraceExpression'<' expression    | nonBraceExpression'>' expression    | nonBraceExpression'!=' expression    | nonBraceExpression'==' expression    | nonBraceExpression'&' expression    | nonBraceExpression'^' expression    | nonBraceExpression'|' expression    | nonBraceExpression'++' expression    | nonBraceExpression'&&' expression    | nonBraceExpression'||' expression    | nonBraceExpression'?' expression':' expression    | nonBraceExpression'<' realTypeArgumentList'>''(' argumentList')'    | nonBraceExpression'(' argumentList')'    | namedType'(' argumentList')'    |'(' typeRef')' expression    ;

1.anenum type used as a field in aheader must specify aunderlying type and representation forenum elements.

2.astruct or nestedstruct type that has the same properties,used as a field in aheader must contain onlybit<W>,int<W>, a serializableenum, or abool.

3.type B <name> is allowed for a type nameBdefined viatypedef X B iftype X <name> is allowed.

4.Most existing P416 programs today donot use function or method calls in table key expressions, and theorder of evaluation of these key expressions makes no differencein the resulting lookup key value. In this overwhelmingly commoncase, if an implementation chooses to insert extra assignmentstatements to implement side-effecting key expressions, but doesnot insert them when there are no side-effecting key expressions,then in typical programs they will almost never be inserted.


[8]ページ先頭

©2009-2025 Movatter.jp