p4c icon indicating copy to clipboard operation
p4c copied to clipboard

Compiler Bug: Inconsistent gress for registers

Open HeRaNO opened this issue 3 months ago • 3 comments

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.

HeRaNO avatar Nov 20 '25 03:11 HeRaNO

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.

ChrisDodd avatar Nov 23 '25 04:11 ChrisDodd

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.)

ChrisDodd avatar Nov 24 '25 02:11 ChrisDodd

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.

HeRaNO avatar Nov 24 '25 02:11 HeRaNO