diff --git a/backends/dpdk/DpdkXfail.cmake b/backends/dpdk/DpdkXfail.cmake index 68295148a1b..6eb311bf358 100644 --- a/backends/dpdk/DpdkXfail.cmake +++ b/backends/dpdk/DpdkXfail.cmake @@ -142,3 +142,9 @@ p4c_add_xfail_reason("dpdk" testdata/p4_16_samples/pna-dpdk-direct-counter-err-4.p4 testdata/p4_16_samples/pna-dpdk-direct-meter-err-3.p4 ) + +p4c_add_xfail_reason("dpdk" + "Learner table .* must have all exact match keys" + testdata/p4_16_samples/pna-add-on-miss-err1.p4 + testdata/p4_16_samples/pna-dpdk-table-key-consolidation-learner-2.p4 + ) diff --git a/backends/dpdk/dpdkArch.cpp b/backends/dpdk/dpdkArch.cpp index 226f73dc063..b27cee6402a 100644 --- a/backends/dpdk/dpdkArch.cpp +++ b/backends/dpdk/dpdkArch.cpp @@ -1689,6 +1689,11 @@ const IR::Node* CopyMatchKeysToSingleStruct::preorder(IR::Key* keys) { CHECK_NULL(table); if (keyInfoInstance->isLearner) { + if (!keyInfoInstance->isExact) { + ::error(ErrorType::ERR_EXPECTED,"Learner table %1% must have all exact match keys", + table->name); + return keys; + } structure->table_type_map.emplace(table->name.name, InternalTableType::LEARNER); } else if (contiguous && keyInfoInstance->isExact){ structure->table_type_map.emplace(table->name.name, InternalTableType::REGULAR_EXACT); @@ -1699,10 +1704,6 @@ const IR::Node* CopyMatchKeysToSingleStruct::preorder(IR::Key* keys) { * Check remaining conditions to see if the copy is needed or not */ metaCopyNeeded = false; if (!copyNeeded) { - if (keyInfoInstance->isLearner && !keyInfoInstance->isExact) { - ::error(ErrorType::ERR_EXPECTED,"Learner table must have all exact match keys"); - return keys; - } if (!contiguous && ((keyInfoInstance->isLearner) || (keyInfoInstance->isExact && keyInfoInstance->numExistingMetaFields <= 5))) { metaCopyNeeded = true; diff --git a/backends/dpdk/dpdkArch.h b/backends/dpdk/dpdkArch.h index 3ef264dbfe5..85728a0655e 100644 --- a/backends/dpdk/dpdkArch.h +++ b/backends/dpdk/dpdkArch.h @@ -605,7 +605,7 @@ class InjectInternetChecksumIntermediateValue : public Transform { std::vector *csum_vec; public: - explicit InjectInternetChecksumIntermediateValue(DpdkProgramStructure *structure, + InjectInternetChecksumIntermediateValue(DpdkProgramStructure *structure, std::vector *csum_vec) : structure(structure), csum_vec(csum_vec) {} diff --git a/backends/dpdk/dpdkContext.cpp b/backends/dpdk/dpdkContext.cpp index 092a4a1d2ba..a7d97f031ee 100644 --- a/backends/dpdk/dpdkContext.cpp +++ b/backends/dpdk/dpdkContext.cpp @@ -126,7 +126,7 @@ void DpdkContextGenerator::CollectTablesAndSetAttributes() { struct externAttributes externAttr; externAttr.externalName = ed->controlPlaneName(); externAttr.externType = externTypeName; - if (externTypeName == "Counter" || "DirectCounter") { + if (externTypeName == "Counter" || externTypeName == "DirectCounter") { unsigned maxArgNum = externTypeName == "Counter"? 2 : 1; int typeArgNum = maxArgNum - 1; if (ed->arguments->size() != maxArgNum) { @@ -150,7 +150,7 @@ void DpdkContextGenerator::CollectTablesAndSetAttributes() { break; } } - if (externTypeName == "DirectMeter" || "DirectCounter") { + if (externTypeName == "DirectMeter" || externTypeName == "DirectCounter") { auto ownerTable = ::get(structure->direct_resource_map, ed->name.name); auto tableAttr = ::get(tableAttrmap, ownerTable->name.originalName); externAttr.table_id = tableAttr.tableHandle; diff --git a/testdata/p4_16_samples/pna-add-on-miss-err1.p4 b/testdata/p4_16_samples/pna-add-on-miss-err1.p4 new file mode 100644 index 00000000000..1b4476296aa --- /dev/null +++ b/testdata/p4_16_samples/pna-add-on-miss-err1.p4 @@ -0,0 +1,202 @@ +/* +Copyright 2022 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "pna.p4" + + +typedef bit<48> EthernetAddress; + +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress srcAddr; + bit<16> etherType; +} + +header ipv4_t { + 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; +} + +struct empty_metadata_t { +} + +// BEGIN:Counter_Example_Part1 +typedef bit<48> ByteCounter_t; +typedef bit<32> PacketCounter_t; +typedef bit<80> PacketByteCounter_t; + +const bit<32> NUM_PORTS = 4; +// END:Counter_Example_Part1 + + +////////////////////////////////////////////////////////////////////// +// Struct types for holding user-defined collections of headers and +// metadata in the P4 developer's program. +// +// Note: The names of these struct types are completely up to the P4 +// developer, as are their member fields, with the only restriction +// being that the structs intended to contain headers should only +// contain members whose types are header, header stack, or +// header_union. +////////////////////////////////////////////////////////////////////// + +struct main_metadata_t { + // empty for this skeleton + ExpireTimeProfileId_t timeout; +} + +// User-defined struct containing all of those headers parsed in the +// main parser. +struct headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + +control PreControlImpl( + in headers_t hdr, + inout main_metadata_t meta, + in pna_pre_input_metadata_t istd, + inout pna_pre_output_metadata_t ostd) +{ + apply { + // Note: This program does not demonstrate all of the code + // that would be necessary if you were implementing IPsec + // packet decryption. + + // If it did, then this pre control implementation would do + // one or more table lookups in order to determine whether the + // packet was IPsec encapsulated, and if so, whether it is + // part of a security association that was established by the + // control plane software. + + // It would also likely perform anti-replay attack detection + // on the IPsec sequence number, which is in the unencrypted + // part of the packet. + + // Any headers parsed by the pre parser in pre_hdr will be + // forgotten after this point. The main parser will start + // parsing over from the beginning, either on the same packet + // if the inline extern block did nothing, or on the packet as + // modified by the inline extern block. + } +} + +parser MainParserImpl( + packet_in pkt, + out headers_t hdr, + inout main_metadata_t main_meta, + in pna_main_parser_input_metadata_t istd) +{ + state start { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// BEGIN:Counter_Example_Part2 +control MainControlImpl( + inout headers_t hdr, // from main parser + inout main_metadata_t user_meta, // from main parser, to "next block" + in pna_main_input_metadata_t istd, + inout pna_main_output_metadata_t ostd) +{ + action next_hop(PortId_t vport) { + send_to_port(vport); + } + action add_on_miss_action() { + bit<32> tmp = 0; + add_entry(action_name="next_hop", action_params = tmp, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da { + key = { + hdr.ipv4.dstAddr: exact; + hdr.ipv4.srcAddr: ternary; + } + actions = { + @tableonly next_hop; + @defaultonly add_on_miss_action; + } + add_on_miss = true; + const default_action = add_on_miss_action; + } + action next_hop2(PortId_t vport, bit<32> newAddr) { + send_to_port(vport); + hdr.ipv4.srcAddr = newAddr; + } + action add_on_miss_action2() { + add_entry(action_name="next_hop2", action_params = {32w0, 32w1234}, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da2 { + key = { + hdr.ipv4.dstAddr: exact; + } + actions = { + @tableonly next_hop2; + @defaultonly add_on_miss_action2; + } + add_on_miss = true; + const default_action = add_on_miss_action2; + } + apply { + if (hdr.ipv4.isValid()) { + ipv4_da.apply(); + ipv4_da2.apply(); + } + } +} +// END:Counter_Example_Part2 + +control MainDeparserImpl( + packet_out pkt, + in headers_t hdr, // from main control + in main_metadata_t user_meta, // from main control + in pna_main_output_metadata_t ostd) +{ + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +// BEGIN:Package_Instantiation_Example +PNA_NIC( + MainParserImpl(), + PreControlImpl(), + MainControlImpl(), + MainDeparserImpl() + // Hoping to make this optional parameter later, but not supported + // by p4c yet. + //, PreParserImpl() + ) main; +// END:Package_Instantiation_Example diff --git a/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-first.p4 b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-first.p4 new file mode 100644 index 00000000000..b90daa1c68c --- /dev/null +++ b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-first.p4 @@ -0,0 +1,114 @@ +#include +#include + +typedef bit<48> EthernetAddress; +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress srcAddr; + bit<16> etherType; +} + +header ipv4_t { + 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; +} + +struct empty_metadata_t { +} + +typedef bit<48> ByteCounter_t; +typedef bit<32> PacketCounter_t; +typedef bit<80> PacketByteCounter_t; +const bit<32> NUM_PORTS = 32w4; +struct main_metadata_t { + ExpireTimeProfileId_t timeout; +} + +struct headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + +control PreControlImpl(in headers_t hdr, inout main_metadata_t meta, in pna_pre_input_metadata_t istd, inout pna_pre_output_metadata_t ostd) { + apply { + } +} + +parser MainParserImpl(packet_in pkt, out headers_t hdr, inout main_metadata_t main_meta, in pna_main_parser_input_metadata_t istd) { + state start { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 16w0x800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control MainControlImpl(inout headers_t hdr, inout main_metadata_t user_meta, in pna_main_input_metadata_t istd, inout pna_main_output_metadata_t ostd) { + action next_hop(PortId_t vport) { + send_to_port(vport); + } + action add_on_miss_action() { + bit<32> tmp = 32w0; + add_entry>(action_name = "next_hop", action_params = tmp, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + hdr.ipv4.srcAddr: ternary @name("hdr.ipv4.srcAddr"); + } + actions = { + @tableonly next_hop(); + @defaultonly add_on_miss_action(); + } + add_on_miss = true; + const default_action = add_on_miss_action(); + } + action next_hop2(PortId_t vport, bit<32> newAddr) { + send_to_port(vport); + hdr.ipv4.srcAddr = newAddr; + } + action add_on_miss_action2() { + add_entry, bit<32>>>(action_name = "next_hop2", action_params = { 32w0, 32w1234 }, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da2 { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + } + actions = { + @tableonly next_hop2(); + @defaultonly add_on_miss_action2(); + } + add_on_miss = true; + const default_action = add_on_miss_action2(); + } + apply { + if (hdr.ipv4.isValid()) { + ipv4_da.apply(); + ipv4_da2.apply(); + } + } +} + +control MainDeparserImpl(packet_out pkt, in headers_t hdr, in main_metadata_t user_meta, in pna_main_output_metadata_t ostd) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +PNA_NIC(MainParserImpl(), PreControlImpl(), MainControlImpl(), MainDeparserImpl()) main; diff --git a/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-frontend.p4 b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-frontend.p4 new file mode 100644 index 00000000000..0700b377752 --- /dev/null +++ b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-frontend.p4 @@ -0,0 +1,111 @@ +#include +#include + +typedef bit<48> EthernetAddress; +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress srcAddr; + bit<16> etherType; +} + +header ipv4_t { + 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; +} + +struct empty_metadata_t { +} + +struct main_metadata_t { + ExpireTimeProfileId_t timeout; +} + +struct headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + +control PreControlImpl(in headers_t hdr, inout main_metadata_t meta, in pna_pre_input_metadata_t istd, inout pna_pre_output_metadata_t ostd) { + apply { + } +} + +parser MainParserImpl(packet_in pkt, out headers_t hdr, inout main_metadata_t main_meta, in pna_main_parser_input_metadata_t istd) { + state start { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 16w0x800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control MainControlImpl(inout headers_t hdr, inout main_metadata_t user_meta, in pna_main_input_metadata_t istd, inout pna_main_output_metadata_t ostd) { + @name("MainControlImpl.tmp") bit<32> tmp_0; + @name("MainControlImpl.next_hop") action next_hop(@name("vport") PortId_t vport) { + send_to_port(vport); + } + @name("MainControlImpl.add_on_miss_action") action add_on_miss_action() { + tmp_0 = 32w0; + add_entry>(action_name = "next_hop", action_params = tmp_0, expire_time_profile_id = user_meta.timeout); + } + @name("MainControlImpl.ipv4_da") table ipv4_da_0 { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + hdr.ipv4.srcAddr: ternary @name("hdr.ipv4.srcAddr"); + } + actions = { + @tableonly next_hop(); + @defaultonly add_on_miss_action(); + } + add_on_miss = true; + const default_action = add_on_miss_action(); + } + @name("MainControlImpl.next_hop2") action next_hop2(@name("vport") PortId_t vport_2, @name("newAddr") bit<32> newAddr) { + send_to_port(vport_2); + hdr.ipv4.srcAddr = newAddr; + } + @name("MainControlImpl.add_on_miss_action2") action add_on_miss_action2() { + add_entry, bit<32>>>(action_name = "next_hop2", action_params = { 32w0, 32w1234 }, expire_time_profile_id = user_meta.timeout); + } + @name("MainControlImpl.ipv4_da2") table ipv4_da2_0 { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + } + actions = { + @tableonly next_hop2(); + @defaultonly add_on_miss_action2(); + } + add_on_miss = true; + const default_action = add_on_miss_action2(); + } + apply { + if (hdr.ipv4.isValid()) { + ipv4_da_0.apply(); + ipv4_da2_0.apply(); + } + } +} + +control MainDeparserImpl(packet_out pkt, in headers_t hdr, in main_metadata_t user_meta, in pna_main_output_metadata_t ostd) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +PNA_NIC(MainParserImpl(), PreControlImpl(), MainControlImpl(), MainDeparserImpl()) main; diff --git a/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-midend.p4 b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-midend.p4 new file mode 100644 index 00000000000..267f2bb5ba3 --- /dev/null +++ b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1-midend.p4 @@ -0,0 +1,122 @@ +#include +#include + +header ethernet_t { + bit<48> dstAddr; + bit<48> srcAddr; + bit<16> etherType; +} + +header ipv4_t { + 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; +} + +struct empty_metadata_t { +} + +struct main_metadata_t { + bit<8> timeout; +} + +struct headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + +control PreControlImpl(in headers_t hdr, inout main_metadata_t meta, in pna_pre_input_metadata_t istd, inout pna_pre_output_metadata_t ostd) { + apply { + } +} + +parser MainParserImpl(packet_in pkt, out headers_t hdr, inout main_metadata_t main_meta, in pna_main_parser_input_metadata_t istd) { + state start { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 16w0x800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +struct tuple_0 { + bit<32> f0; + bit<32> f1; +} + +control MainControlImpl(inout headers_t hdr, inout main_metadata_t user_meta, in pna_main_input_metadata_t istd, inout pna_main_output_metadata_t ostd) { + @name("MainControlImpl.next_hop") action next_hop(@name("vport") bit<32> vport) { + send_to_port(vport); + } + @name("MainControlImpl.add_on_miss_action") action add_on_miss_action() { + add_entry>(action_name = "next_hop", action_params = 32w0, expire_time_profile_id = user_meta.timeout); + } + @name("MainControlImpl.ipv4_da") table ipv4_da_0 { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + hdr.ipv4.srcAddr: ternary @name("hdr.ipv4.srcAddr"); + } + actions = { + @tableonly next_hop(); + @defaultonly add_on_miss_action(); + } + add_on_miss = true; + const default_action = add_on_miss_action(); + } + @name("MainControlImpl.next_hop2") action next_hop2(@name("vport") bit<32> vport_2, @name("newAddr") bit<32> newAddr) { + send_to_port(vport_2); + hdr.ipv4.srcAddr = newAddr; + } + @name("MainControlImpl.add_on_miss_action2") action add_on_miss_action2() { + add_entry(action_name = "next_hop2", action_params = (tuple_0){f0 = 32w0,f1 = 32w1234}, expire_time_profile_id = user_meta.timeout); + } + @name("MainControlImpl.ipv4_da2") table ipv4_da2_0 { + key = { + hdr.ipv4.dstAddr: exact @name("hdr.ipv4.dstAddr"); + } + actions = { + @tableonly next_hop2(); + @defaultonly add_on_miss_action2(); + } + add_on_miss = true; + const default_action = add_on_miss_action2(); + } + apply { + if (hdr.ipv4.isValid()) { + ipv4_da_0.apply(); + ipv4_da2_0.apply(); + } + } +} + +control MainDeparserImpl(packet_out pkt, in headers_t hdr, in main_metadata_t user_meta, in pna_main_output_metadata_t ostd) { + @hidden action pnaaddonmisserr1l187() { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } + @hidden table tbl_pnaaddonmisserr1l187 { + actions = { + pnaaddonmisserr1l187(); + } + const default_action = pnaaddonmisserr1l187(); + } + apply { + tbl_pnaaddonmisserr1l187.apply(); + } +} + +PNA_NIC(MainParserImpl(), PreControlImpl(), MainControlImpl(), MainDeparserImpl()) main; diff --git a/testdata/p4_16_samples_outputs/pna-add-on-miss-err1.p4 b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1.p4 new file mode 100644 index 00000000000..f292eb5ea64 --- /dev/null +++ b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1.p4 @@ -0,0 +1,114 @@ +#include +#include + +typedef bit<48> EthernetAddress; +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress srcAddr; + bit<16> etherType; +} + +header ipv4_t { + 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; +} + +struct empty_metadata_t { +} + +typedef bit<48> ByteCounter_t; +typedef bit<32> PacketCounter_t; +typedef bit<80> PacketByteCounter_t; +const bit<32> NUM_PORTS = 4; +struct main_metadata_t { + ExpireTimeProfileId_t timeout; +} + +struct headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + +control PreControlImpl(in headers_t hdr, inout main_metadata_t meta, in pna_pre_input_metadata_t istd, inout pna_pre_output_metadata_t ostd) { + apply { + } +} + +parser MainParserImpl(packet_in pkt, out headers_t hdr, inout main_metadata_t main_meta, in pna_main_parser_input_metadata_t istd) { + state start { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 0x800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control MainControlImpl(inout headers_t hdr, inout main_metadata_t user_meta, in pna_main_input_metadata_t istd, inout pna_main_output_metadata_t ostd) { + action next_hop(PortId_t vport) { + send_to_port(vport); + } + action add_on_miss_action() { + bit<32> tmp = 0; + add_entry(action_name = "next_hop", action_params = tmp, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da { + key = { + hdr.ipv4.dstAddr: exact; + hdr.ipv4.srcAddr: ternary; + } + actions = { + @tableonly next_hop; + @defaultonly add_on_miss_action; + } + add_on_miss = true; + const default_action = add_on_miss_action; + } + action next_hop2(PortId_t vport, bit<32> newAddr) { + send_to_port(vport); + hdr.ipv4.srcAddr = newAddr; + } + action add_on_miss_action2() { + add_entry(action_name = "next_hop2", action_params = { 32w0, 32w1234 }, expire_time_profile_id = user_meta.timeout); + } + table ipv4_da2 { + key = { + hdr.ipv4.dstAddr: exact; + } + actions = { + @tableonly next_hop2; + @defaultonly add_on_miss_action2; + } + add_on_miss = true; + const default_action = add_on_miss_action2; + } + apply { + if (hdr.ipv4.isValid()) { + ipv4_da.apply(); + ipv4_da2.apply(); + } + } +} + +control MainDeparserImpl(packet_out pkt, in headers_t hdr, in main_metadata_t user_meta, in pna_main_output_metadata_t ostd) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +PNA_NIC(MainParserImpl(), PreControlImpl(), MainControlImpl(), MainDeparserImpl()) main; diff --git a/testdata/p4_16_samples_outputs/pna-add-on-miss-err1.p4-stderr b/testdata/p4_16_samples_outputs/pna-add-on-miss-err1.p4-stderr new file mode 100644 index 00000000000..e69de29bb2d