Compiler Bug: Inconsistent gress for registers
Compile the following P4 code under p4c 1.2.5.10 (SHA: cceb17da1 BUILD: Release) (current main HEAD).
p4c --target tofino2 --arch t2na test.p4
// test.p4
#include <core.p4>
#if __TARGET_TOFINO__ == 3
#include <t3na.p4>
#elif __TARGET_TOFINO__ == 2
#include <t2na.p4>
#else
#include <tna.p4>
#endif
#include "headers.p4"
#include "util.p4"
Register<bit<32>, bit<32>>(32w2) test;
struct metadata_t {}
// ---------------------------------------------------------------------------
// Ingress parser
// ---------------------------------------------------------------------------
parser SwitchIngressParser(
packet_in pkt,
out header_t hdr,
out metadata_t ig_md,
out ingress_intrinsic_metadata_t ig_intr_md) {
TofinoIngressParser() tofino_parser;
state start {
tofino_parser.apply(pkt, ig_intr_md);
transition parse_ethernet;
}
state parse_ethernet {
pkt.extract(hdr.ethernet);
transition select(hdr.ethernet.ether_type) {
ETHERTYPE_IPV4 : parse_ipv4;
default : reject;
}
}
state parse_ipv4 {
pkt.extract(hdr.ipv4);
transition select(hdr.ipv4.protocol) {
IP_PROTOCOLS_TCP : parse_tcp;
IP_PROTOCOLS_UDP : parse_udp;
default : accept;
}
}
state parse_tcp {
pkt.extract(hdr.tcp);
transition accept;
}
state parse_udp {
pkt.extract(hdr.udp);
transition accept;
}
}
// ---------------------------------------------------------------------------
// Ingress
// ---------------------------------------------------------------------------
control SwitchIngress(
inout header_t hdr,
inout metadata_t ig_md,
in ingress_intrinsic_metadata_t ig_intr_md,
in ingress_intrinsic_metadata_from_parser_t ig_prsr_md,
inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md,
inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) {
RegisterAction<bit<32>, bit<32>, bit<32>>(test) test_read = {
void apply(inout bit<32> value, out bit<32> read_value) {
read_value = value; // read the register in ingress
}
};
action set_output_port(PortId_t port_id) {
ig_tm_md.ucast_egress_port = port_id;
}
table output_port {
actions = {
set_output_port;
}
}
apply {
test_read.execute(0);
output_port.apply();
}
}
// ---------------------------------------------------------------------------
// Ingress Deparser
// ---------------------------------------------------------------------------
control SwitchIngressDeparser(packet_out pkt,
inout header_t hdr,
in metadata_t ig_md,
in ingress_intrinsic_metadata_for_deparser_t
ig_intr_dprsr_md
) {
apply {
pkt.emit(hdr);
}
}
// ---------------------------------------------------------------------------
// Egress parser
// ---------------------------------------------------------------------------
parser SwitchEgressParser(
packet_in pkt,
out header_t hdr,
out metadata_t eg_md,
out egress_intrinsic_metadata_t eg_intr_md) {
TofinoEgressParser() tofino_parser;
state start {
tofino_parser.apply(pkt, eg_intr_md);
transition parse_ethernet;
}
state parse_ethernet {
pkt.extract(hdr.ethernet);
transition select (hdr.ethernet.ether_type) {
ETHERTYPE_IPV4 : parse_ipv4;
default : reject;
}
}
state parse_ipv4 {
pkt.extract(hdr.ipv4);
transition select (hdr.ipv4.protocol) {
IP_PROTOCOLS_TCP : parse_tcp;
IP_PROTOCOLS_UDP : parse_udp;
default : reject;
}
}
state parse_tcp {
pkt.extract(hdr.tcp);
transition accept;
}
state parse_udp {
pkt.extract(hdr.udp);
transition accept;
}
}
// ---------------------------------------------------------------------------
// Switch Egress MAU
// ---------------------------------------------------------------------------
control SwitchEgress(
inout header_t hdr,
inout metadata_t eg_md,
in egress_intrinsic_metadata_t eg_intr_md,
in egress_intrinsic_metadata_from_parser_t eg_prsr_md,
inout egress_intrinsic_metadata_for_deparser_t eg_dprsr_md,
inout egress_intrinsic_metadata_for_output_port_t eg_oport_md) {
RegisterAction<bit<32>, PortId_t, void>(test) test_write = {
void apply(inout bit<32> value) {
value = 1; // write the register in egress
}
};
apply {
test_write.execute(0);
}
}
// ---------------------------------------------------------------------------
// Egress Deparser
// ---------------------------------------------------------------------------
control SwitchEgressDeparser(
packet_out pkt,
inout header_t hdr,
in metadata_t eg_md,
in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md) {
apply {
pkt.emit(hdr.ethernet);
pkt.emit(hdr.ipv4);
pkt.emit(hdr.tcp);
pkt.emit(hdr.udp);
}
}
Pipeline(SwitchIngressParser(),
SwitchIngress(),
SwitchIngressDeparser(),
SwitchEgressParser(),
SwitchEgress(),
SwitchEgressDeparser()) pipe;
Switch(pipe) main;
The compiler output is:
[Lots of warnings unrelated...]
1 error, 24 warnings generated.
In file: /home/yhr/p4c/backends/tofino/bf-p4c/phv/phv_fields.cpp:188
Compiler Bug: Inconsistent gress for test$index (ingress and egress)
Internal compiler error. Please submit a bug report with your code.
I think this should be a compile error, not a bug in the compiler.
When I compile this for tofino with a debug build (b652e51ac), it compiles successfully, but gives me an assembler error:
p4c-5243.tofino/pipe/p4c-5243.bfa:494: error: tbl_p4c5243l173$salu.test not in same thread as tbl_p4c5243l92
When I compile for tofino2, I see the error described. Not sure if/how the two failure modes are related.
In both cases, this is coming from the Register "test", which is accessed both in ingress and egress. (test_read in SwitchIngress and test_write in SwitchEgress). I believe that this should be flagged as an error somewhere, as I'm pretty sure the hardware can't handle it (RAM blocks on tofino need to be allocated strictly to egress or ingress/ghost and can't be shared due to timing reasons.)
It's quite reasonable for me. We cannot operate on the same extern register in both ingress and egress, IIUC. Otherwise, we do not bother Ghost Thread. So this should be noticed as an error when compiling, rather than an error in the compiler.