pipeline: support packet mirroring
[dpdk.git] / lib / pipeline / rte_swx_pipeline.c
index ae9b205..9406aa7 100644 (file)
@@ -4,7 +4,15 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
-#include <arpa/inet.h>
+#include <dlfcn.h>
+
+#include <rte_swx_port_ethdev.h>
+#include <rte_swx_port_fd.h>
+#include <rte_swx_port_ring.h>
+#include "rte_swx_port_source_sink.h"
+
+#include <rte_swx_table_em.h>
+#include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
 
@@ -415,6 +423,8 @@ rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,
        CHECK(ops->create, EINVAL);
        CHECK(ops->free, EINVAL);
        CHECK(ops->pkt_tx, EINVAL);
+       CHECK(ops->pkt_fast_clone_tx, EINVAL);
+       CHECK(ops->pkt_clone_tx, EINVAL);
        CHECK(ops->stats_read, EINVAL);
 
        CHECK(!port_out_type_find(p, name), EEXIST);
@@ -501,6 +511,8 @@ port_out_build(struct rte_swx_pipeline *p)
                struct port_out_runtime *out = &p->out[port->id];
 
                out->pkt_tx = port->type->ops.pkt_tx;
+               out->pkt_fast_clone_tx = port->type->ops.pkt_fast_clone_tx;
+               out->pkt_clone_tx = port->type->ops.pkt_clone_tx;
                out->flush = port->type->ops.flush;
                out->obj = port->obj;
        }
@@ -546,6 +558,81 @@ port_out_free(struct rte_swx_pipeline *p)
        }
 }
 
+/*
+ * Packet mirroring.
+ */
+int
+rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,
+                                 struct rte_swx_pipeline_mirroring_params *params)
+{
+       CHECK(p, EINVAL);
+       CHECK(params, EINVAL);
+       CHECK(params->n_slots, EINVAL);
+       CHECK(params->n_sessions, EINVAL);
+       CHECK(!p->build_done, EEXIST);
+
+       p->n_mirroring_slots = rte_align32pow2(params->n_slots);
+       if (p->n_mirroring_slots > 64)
+               p->n_mirroring_slots = 64;
+
+       p->n_mirroring_sessions = rte_align32pow2(params->n_sessions);
+
+       return 0;
+}
+
+static void
+mirroring_build_free(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
+
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+
+               /* mirroring_slots. */
+               free(t->mirroring_slots);
+               t->mirroring_slots = NULL;
+       }
+
+       /* mirroring_sessions. */
+       free(p->mirroring_sessions);
+       p->mirroring_sessions = NULL;
+}
+
+static void
+mirroring_free(struct rte_swx_pipeline *p)
+{
+       mirroring_build_free(p);
+}
+
+static int
+mirroring_build(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
+
+       if (!p->n_mirroring_slots || !p->n_mirroring_sessions)
+               return 0;
+
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+
+               /* mirroring_slots. */
+               t->mirroring_slots = calloc(p->n_mirroring_slots, sizeof(uint32_t));
+               if (!t->mirroring_slots)
+                       goto error;
+       }
+
+       /* mirroring_sessions. */
+       p->mirroring_sessions = calloc(p->n_mirroring_sessions, sizeof(struct mirroring_session));
+       if (!p->mirroring_sessions)
+               goto error;
+
+       return 0;
+
+error:
+       mirroring_build_free(p);
+       return -ENOMEM;
+}
+
 /*
  * Extern object.
  */
@@ -1369,6 +1456,7 @@ instruction_is_tx(enum instruction_type type)
        switch (type) {
        case INSTR_TX:
        case INSTR_TX_I:
+       case INSTR_DROP:
                return 1;
 
        default:
@@ -1376,6 +1464,27 @@ instruction_is_tx(enum instruction_type type)
        }
 }
 
+static int
+instruction_does_tx(struct instruction *instr)
+{
+       switch (instr->type) {
+       case INSTR_TX:
+       case INSTR_TX_I:
+       case INSTR_DROP:
+       case INSTR_HDR_EMIT_TX:
+       case INSTR_HDR_EMIT2_TX:
+       case INSTR_HDR_EMIT3_TX:
+       case INSTR_HDR_EMIT4_TX:
+       case INSTR_HDR_EMIT5_TX:
+       case INSTR_HDR_EMIT6_TX:
+       case INSTR_HDR_EMIT7_TX:
+       case INSTR_HDR_EMIT8_TX:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 static int
 instruction_is_jmp(struct instruction *instr)
 {
@@ -1416,6 +1525,24 @@ instruction_is_jmp(struct instruction *instr)
        }
 }
 
+static int
+instruction_does_thread_yield(struct instruction *instr)
+{
+       switch (instr->type) {
+       case INSTR_RX:
+       case INSTR_TABLE:
+       case INSTR_TABLE_AF:
+       case INSTR_SELECTOR:
+       case INSTR_LEARNER:
+       case INSTR_LEARNER_AF:
+       case INSTR_EXTERN_OBJ:
+       case INSTR_EXTERN_FUNC:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 static struct field *
 action_field_parse(struct action *action, const char *name);
 
@@ -1492,62 +1619,6 @@ struct_field_parse(struct rte_swx_pipeline *p,
        }
 }
 
-static inline void
-pipeline_port_inc(struct rte_swx_pipeline *p)
-{
-       p->port_id = (p->port_id + 1) & (p->n_ports_in - 1);
-}
-
-static inline void
-thread_ip_reset(struct rte_swx_pipeline *p, struct thread *t)
-{
-       t->ip = p->instructions;
-}
-
-static inline void
-thread_ip_set(struct thread *t, struct instruction *ip)
-{
-       t->ip = ip;
-}
-
-static inline void
-thread_ip_action_call(struct rte_swx_pipeline *p,
-                     struct thread *t,
-                     uint32_t action_id)
-{
-       t->ret = t->ip + 1;
-       t->ip = p->action_instructions[action_id];
-}
-
-static inline void
-thread_ip_inc(struct rte_swx_pipeline *p);
-
-static inline void
-thread_ip_inc(struct rte_swx_pipeline *p)
-{
-       struct thread *t = &p->threads[p->thread_id];
-
-       t->ip++;
-}
-
-static inline void
-thread_ip_inc_cond(struct thread *t, int cond)
-{
-       t->ip += cond;
-}
-
-static inline void
-thread_yield(struct rte_swx_pipeline *p)
-{
-       p->thread_id = (p->thread_id + 1) & (RTE_SWX_PIPELINE_THREADS_MAX - 1);
-}
-
-static inline void
-thread_yield_cond(struct rte_swx_pipeline *p, int cond)
-{
-       p->thread_id = (p->thread_id + cond) & (RTE_SWX_PIPELINE_THREADS_MAX - 1);
-}
-
 /*
  * rx.
  */
@@ -1573,44 +1644,6 @@ instr_rx_translate(struct rte_swx_pipeline *p,
        return 0;
 }
 
-static inline void
-instr_rx_exec(struct rte_swx_pipeline *p);
-
-static inline void
-instr_rx_exec(struct rte_swx_pipeline *p)
-{
-       struct thread *t = &p->threads[p->thread_id];
-       struct instruction *ip = t->ip;
-       struct port_in_runtime *port = &p->in[p->port_id];
-       struct rte_swx_pkt *pkt = &t->pkt;
-       int pkt_received;
-
-       /* Packet. */
-       pkt_received = port->pkt_rx(port->obj, pkt);
-       t->ptr = &pkt->pkt[pkt->offset];
-       rte_prefetch0(t->ptr);
-
-       TRACE("[Thread %2u] rx %s from port %u\n",
-             p->thread_id,
-             pkt_received ? "1 pkt" : "0 pkts",
-             p->port_id);
-
-       /* Headers. */
-       t->valid_headers = 0;
-       t->n_headers_out = 0;
-
-       /* Meta-data. */
-       METADATA_WRITE(t, ip->io.io.offset, ip->io.io.n_bits, p->port_id);
-
-       /* Tables. */
-       t->table_state = p->table_state;
-
-       /* Thread. */
-       pipeline_port_inc(p);
-       thread_ip_inc_cond(t, pkt_received);
-       thread_yield(p);
-}
-
 /*
  * tx.
  */
@@ -1646,7 +1679,7 @@ instr_tx_translate(struct rte_swx_pipeline *p,
 }
 
 static int
-instr_drop_translate(struct rte_swx_pipeline *p,
+instr_drop_translate(struct rte_swx_pipeline *p __rte_unused,
                     struct action *action __rte_unused,
                     char **tokens __rte_unused,
                     int n_tokens,
@@ -1655,118 +1688,98 @@ instr_drop_translate(struct rte_swx_pipeline *p,
 {
        CHECK(n_tokens == 1, EINVAL);
 
-       /* TX_I. */
-       instr->type = INSTR_TX_I;
-       instr->io.io.val = p->n_ports_out - 1;
+       /* DROP. */
+       instr->type = INSTR_DROP;
        return 0;
 }
 
 static inline void
-emit_handler(struct thread *t)
+instr_tx_exec(struct rte_swx_pipeline *p)
 {
-       struct header_out_runtime *h0 = &t->headers_out[0];
-       struct header_out_runtime *h1 = &t->headers_out[1];
-       uint32_t offset = 0, i;
-
-       /* No header change or header decapsulation. */
-       if ((t->n_headers_out == 1) &&
-           (h0->ptr + h0->n_bytes == t->ptr)) {
-               TRACE("Emit handler: no header change or header decap.\n");
-
-               t->pkt.offset -= h0->n_bytes;
-               t->pkt.length += h0->n_bytes;
-
-               return;
-       }
-
-       /* Header encapsulation (optionally, with prior header decasulation). */
-       if ((t->n_headers_out == 2) &&
-           (h1->ptr + h1->n_bytes == t->ptr) &&
-           (h0->ptr == h0->ptr0)) {
-               uint32_t offset;
-
-               TRACE("Emit handler: header encapsulation.\n");
-
-               offset = h0->n_bytes + h1->n_bytes;
-               memcpy(t->ptr - offset, h0->ptr, h0->n_bytes);
-               t->pkt.offset -= offset;
-               t->pkt.length += offset;
-
-               return;
-       }
-
-       /* Header insertion. */
-       /* TBD */
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       /* Header extraction. */
-       /* TBD */
+       __instr_tx_exec(p, t, ip);
 
-       /* For any other case. */
-       TRACE("Emit handler: complex case.\n");
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
+}
 
-       for (i = 0; i < t->n_headers_out; i++) {
-               struct header_out_runtime *h = &t->headers_out[i];
+static inline void
+instr_tx_i_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-               memcpy(&t->header_out_storage[offset], h->ptr, h->n_bytes);
-               offset += h->n_bytes;
-       }
+       __instr_tx_i_exec(p, t, ip);
 
-       if (offset) {
-               memcpy(t->ptr - offset, t->header_out_storage, offset);
-               t->pkt.offset -= offset;
-               t->pkt.length += offset;
-       }
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
-instr_tx_exec(struct rte_swx_pipeline *p);
-
-static inline void
-instr_tx_exec(struct rte_swx_pipeline *p)
+instr_drop_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t port_id = METADATA_READ(t, ip->io.io.offset, ip->io.io.n_bits);
-       struct port_out_runtime *port = &p->out[port_id];
-       struct rte_swx_pkt *pkt = &t->pkt;
 
-       TRACE("[Thread %2u]: tx 1 pkt to port %u\n",
-             p->thread_id,
-             (uint32_t)port_id);
-
-       /* Headers. */
-       emit_handler(t);
-
-       /* Packet. */
-       port->pkt_tx(port->obj, pkt);
+       __instr_drop_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_reset(p, t);
        instr_rx_exec(p);
 }
 
+/*
+ * mirror.
+ */
+static int
+instr_mirror_translate(struct rte_swx_pipeline *p,
+                      struct action *action,
+                      char **tokens,
+                      int n_tokens,
+                      struct instruction *instr,
+                      struct instruction_data *data __rte_unused)
+{
+       char *dst = tokens[1], *src = tokens[2];
+       struct field *fdst, *fsrc;
+       uint32_t dst_struct_id = 0, src_struct_id = 0;
+
+       CHECK(n_tokens == 3, EINVAL);
+
+       fdst = struct_field_parse(p, action, dst, &dst_struct_id);
+       CHECK(fdst, EINVAL);
+       CHECK(dst[0] != 'h', EINVAL);
+       CHECK(!fdst->var_size, EINVAL);
+
+       fsrc = struct_field_parse(p, action, src, &src_struct_id);
+       CHECK(fsrc, EINVAL);
+       CHECK(src[0] != 'h', EINVAL);
+       CHECK(!fsrc->var_size, EINVAL);
+
+       instr->type = INSTR_MIRROR;
+       instr->mirror.dst.struct_id = (uint8_t)dst_struct_id;
+       instr->mirror.dst.n_bits = fdst->n_bits;
+       instr->mirror.dst.offset = fdst->offset / 8;
+       instr->mirror.src.struct_id = (uint8_t)src_struct_id;
+       instr->mirror.src.n_bits = fsrc->n_bits;
+       instr->mirror.src.offset = fsrc->offset / 8;
+
+       return 0;
+}
+
 static inline void
-instr_tx_i_exec(struct rte_swx_pipeline *p)
+instr_mirror_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t port_id = ip->io.io.val;
-       struct port_out_runtime *port = &p->out[port_id];
-       struct rte_swx_pkt *pkt = &t->pkt;
-
-       TRACE("[Thread %2u]: tx (i) 1 pkt to port %u\n",
-             p->thread_id,
-             (uint32_t)port_id);
 
-       /* Headers. */
-       emit_handler(t);
-
-       /* Packet. */
-       port->pkt_tx(port->obj, pkt);
+       __instr_mirror_exec(p, t, ip);
 
        /* Thread. */
-       thread_ip_reset(p, t);
-       instr_rx_exec(p);
+       thread_ip_inc(p);
 }
 
 /*
@@ -1841,52 +1854,12 @@ instr_hdr_lookahead_translate(struct rte_swx_pipeline *p,
 }
 
 static inline void
-__instr_hdr_extract_exec(struct rte_swx_pipeline *p, uint32_t n_extract);
-
-static inline void
-__instr_hdr_extract_exec(struct rte_swx_pipeline *p, uint32_t n_extract)
+instr_hdr_extract_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t valid_headers = t->valid_headers;
-       uint8_t *ptr = t->ptr;
-       uint32_t offset = t->pkt.offset;
-       uint32_t length = t->pkt.length;
-       uint32_t i;
-
-       for (i = 0; i < n_extract; i++) {
-               uint32_t header_id = ip->io.hdr.header_id[i];
-               uint32_t struct_id = ip->io.hdr.struct_id[i];
-               uint32_t n_bytes = ip->io.hdr.n_bytes[i];
-
-               TRACE("[Thread %2u]: extract header %u (%u bytes)\n",
-                     p->thread_id,
-                     header_id,
-                     n_bytes);
-
-               /* Headers. */
-               t->structs[struct_id] = ptr;
-               valid_headers = MASK64_BIT_SET(valid_headers, header_id);
 
-               /* Packet. */
-               offset += n_bytes;
-               length -= n_bytes;
-               ptr += n_bytes;
-       }
-
-       /* Headers. */
-       t->valid_headers = valid_headers;
-
-       /* Packet. */
-       t->pkt.offset = offset;
-       t->pkt.length = length;
-       t->ptr = ptr;
-}
-
-static inline void
-instr_hdr_extract_exec(struct rte_swx_pipeline *p)
-{
-       __instr_hdr_extract_exec(p, 1);
+       __instr_hdr_extract_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1895,10 +1868,10 @@ instr_hdr_extract_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract2_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 2 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 2);
+       __instr_hdr_extract2_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1907,10 +1880,10 @@ instr_hdr_extract2_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract3_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 3 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 3);
+       __instr_hdr_extract3_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1919,10 +1892,10 @@ instr_hdr_extract3_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract4_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 4 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 4);
+       __instr_hdr_extract4_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1931,10 +1904,10 @@ instr_hdr_extract4_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract5_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 5 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 5);
+       __instr_hdr_extract5_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1943,10 +1916,10 @@ instr_hdr_extract5_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract6_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 6 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 6);
+       __instr_hdr_extract6_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1955,10 +1928,10 @@ instr_hdr_extract6_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract7_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 7 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 7);
+       __instr_hdr_extract7_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1967,10 +1940,10 @@ instr_hdr_extract7_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_hdr_extract8_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 8 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_hdr_extract_exec(p, 8);
+       __instr_hdr_extract8_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -1982,35 +1955,7 @@ instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       uint64_t valid_headers = t->valid_headers;
-       uint8_t *ptr = t->ptr;
-       uint32_t offset = t->pkt.offset;
-       uint32_t length = t->pkt.length;
-
-       uint32_t n_bytes_last = METADATA_READ(t, ip->io.io.offset, ip->io.io.n_bits);
-       uint32_t header_id = ip->io.hdr.header_id[0];
-       uint32_t struct_id = ip->io.hdr.struct_id[0];
-       uint32_t n_bytes = ip->io.hdr.n_bytes[0];
-
-       struct header_runtime *h = &t->headers[header_id];
-
-       TRACE("[Thread %2u]: extract header %u (%u + %u bytes)\n",
-             p->thread_id,
-             header_id,
-             n_bytes,
-             n_bytes_last);
-
-       n_bytes += n_bytes_last;
-
-       /* Headers. */
-       t->structs[struct_id] = ptr;
-       t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
-       h->n_bytes = n_bytes;
-
-       /* Packet. */
-       t->pkt.offset = offset + n_bytes;
-       t->pkt.length = length - n_bytes;
-       t->ptr = ptr + n_bytes;
+       __instr_hdr_extract_m_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2022,19 +1967,7 @@ instr_hdr_lookahead_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       uint64_t valid_headers = t->valid_headers;
-       uint8_t *ptr = t->ptr;
-
-       uint32_t header_id = ip->io.hdr.header_id[0];
-       uint32_t struct_id = ip->io.hdr.struct_id[0];
-
-       TRACE("[Thread %2u]: lookahead header %u\n",
-             p->thread_id,
-             header_id);
-
-       /* Headers. */
-       t->structs[struct_id] = ptr;
-       t->valid_headers = MASK64_BIT_SET(valid_headers, header_id);
+       __instr_hdr_lookahead_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2066,165 +1999,119 @@ instr_hdr_emit_translate(struct rte_swx_pipeline *p,
 }
 
 static inline void
-__instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit);
-
-static inline void
-__instr_hdr_emit_exec(struct rte_swx_pipeline *p, uint32_t n_emit)
+instr_hdr_emit_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t valid_headers = t->valid_headers;
-       uint32_t n_headers_out = t->n_headers_out;
-       struct header_out_runtime *ho = &t->headers_out[n_headers_out - 1];
-       uint8_t *ho_ptr = NULL;
-       uint32_t ho_nbytes = 0, first = 1, i;
 
-       for (i = 0; i < n_emit; i++) {
-               uint32_t header_id = ip->io.hdr.header_id[i];
-               uint32_t struct_id = ip->io.hdr.struct_id[i];
+       __instr_hdr_emit_exec(p, t, ip);
 
-               struct header_runtime *hi = &t->headers[header_id];
-               uint8_t *hi_ptr0 = hi->ptr0;
-               uint32_t n_bytes = hi->n_bytes;
-
-               uint8_t *hi_ptr = t->structs[struct_id];
+       /* Thread. */
+       thread_ip_inc(p);
+}
 
-               if (!MASK64_BIT_GET(valid_headers, header_id))
-                       continue;
+static inline void
+instr_hdr_emit_tx_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-               TRACE("[Thread %2u]: emit header %u\n",
-                     p->thread_id,
-                     header_id);
+       __instr_hdr_emit_tx_exec(p, t, ip);
 
-               /* Headers. */
-               if (first) {
-                       first = 0;
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
+}
 
-                       if (!t->n_headers_out) {
-                               ho = &t->headers_out[0];
+static inline void
+instr_hdr_emit2_tx_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-                               ho->ptr0 = hi_ptr0;
-                               ho->ptr = hi_ptr;
-
-                               ho_ptr = hi_ptr;
-                               ho_nbytes = n_bytes;
-
-                               n_headers_out = 1;
-
-                               continue;
-                       } else {
-                               ho_ptr = ho->ptr;
-                               ho_nbytes = ho->n_bytes;
-                       }
-               }
-
-               if (ho_ptr + ho_nbytes == hi_ptr) {
-                       ho_nbytes += n_bytes;
-               } else {
-                       ho->n_bytes = ho_nbytes;
-
-                       ho++;
-                       ho->ptr0 = hi_ptr0;
-                       ho->ptr = hi_ptr;
-
-                       ho_ptr = hi_ptr;
-                       ho_nbytes = n_bytes;
-
-                       n_headers_out++;
-               }
-       }
-
-       ho->n_bytes = ho_nbytes;
-       t->n_headers_out = n_headers_out;
-}
-
-static inline void
-instr_hdr_emit_exec(struct rte_swx_pipeline *p)
-{
-       __instr_hdr_emit_exec(p, 1);
+       __instr_hdr_emit2_tx_exec(p, t, ip);
 
        /* Thread. */
-       thread_ip_inc(p);
-}
-
-static inline void
-instr_hdr_emit_tx_exec(struct rte_swx_pipeline *p)
-{
-       TRACE("[Thread %2u] *** The next 2 instructions are fused. ***\n",
-             p->thread_id);
-
-       __instr_hdr_emit_exec(p, 1);
-       instr_tx_exec(p);
-}
-
-static inline void
-instr_hdr_emit2_tx_exec(struct rte_swx_pipeline *p)
-{
-       TRACE("[Thread %2u] *** The next 3 instructions are fused. ***\n",
-             p->thread_id);
-
-       __instr_hdr_emit_exec(p, 2);
-       instr_tx_exec(p);
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit3_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 4 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit3_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 3);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit4_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 5 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit4_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 4);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit5_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 6 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit5_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 5);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit6_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 7 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit6_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 6);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit7_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 8 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit7_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 7);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 static inline void
 instr_hdr_emit8_tx_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 9 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+
+       __instr_hdr_emit8_tx_exec(p, t, ip);
 
-       __instr_hdr_emit_exec(p, 8);
-       instr_tx_exec(p);
+       /* Thread. */
+       thread_ip_reset(p, t);
+       instr_rx_exec(p);
 }
 
 /*
@@ -2255,12 +2142,8 @@ instr_hdr_validate_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint32_t header_id = ip->valid.header_id;
-
-       TRACE("[Thread %2u] validate header %u\n", p->thread_id, header_id);
 
-       /* Headers. */
-       t->valid_headers = MASK64_BIT_SET(t->valid_headers, header_id);
+       __instr_hdr_validate_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2294,12 +2177,8 @@ instr_hdr_invalidate_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint32_t header_id = ip->valid.header_id;
 
-       TRACE("[Thread %2u] invalidate header %u\n", p->thread_id, header_id);
-
-       /* Headers. */
-       t->valid_headers = MASK64_BIT_CLR(t->valid_headers, header_id);
+       __instr_hdr_invalidate_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2407,6 +2286,62 @@ instr_table_exec(struct rte_swx_pipeline *p)
        thread_ip_action_call(p, t, action_id);
 }
 
+static inline void
+instr_table_af_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+       uint32_t table_id = ip->table.table_id;
+       struct rte_swx_table_state *ts = &t->table_state[table_id];
+       struct table_runtime *table = &t->tables[table_id];
+       struct table_statistics *stats = &p->table_stats[table_id];
+       uint64_t action_id, n_pkts_hit, n_pkts_action;
+       uint8_t *action_data;
+       action_func_t action_func;
+       int done, hit;
+
+       /* Table. */
+       done = table->func(ts->obj,
+                          table->mailbox,
+                          table->key,
+                          &action_id,
+                          &action_data,
+                          &hit);
+       if (!done) {
+               /* Thread. */
+               TRACE("[Thread %2u] table %u (not finalized)\n",
+                     p->thread_id,
+                     table_id);
+
+               thread_yield(p);
+               return;
+       }
+
+       action_id = hit ? action_id : ts->default_action_id;
+       action_data = hit ? action_data : ts->default_action_data;
+       action_func = p->action_funcs[action_id];
+       n_pkts_hit = stats->n_pkts_hit[hit];
+       n_pkts_action = stats->n_pkts_action[action_id];
+
+       TRACE("[Thread %2u] table %u (%s, action %u)\n",
+             p->thread_id,
+             table_id,
+             hit ? "hit" : "miss",
+             (uint32_t)action_id);
+
+       t->action_id = action_id;
+       t->structs[0] = action_data;
+       t->hit = hit;
+       stats->n_pkts_hit[hit] = n_pkts_hit + 1;
+       stats->n_pkts_action[action_id] = n_pkts_action + 1;
+
+       /* Thread. */
+       thread_ip_inc(p);
+
+       /* Action. */
+       action_func(p);
+}
+
 static inline void
 instr_selector_exec(struct rte_swx_pipeline *p)
 {
@@ -2503,6 +2438,68 @@ instr_learner_exec(struct rte_swx_pipeline *p)
        thread_ip_action_call(p, t, action_id);
 }
 
+static inline void
+instr_learner_af_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+       uint32_t learner_id = ip->table.table_id;
+       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
+               p->n_selectors + learner_id];
+       struct learner_runtime *l = &t->learners[learner_id];
+       struct learner_statistics *stats = &p->learner_stats[learner_id];
+       uint64_t action_id, n_pkts_hit, n_pkts_action, time;
+       uint8_t *action_data;
+       action_func_t action_func;
+       int done, hit;
+
+       /* Table. */
+       time = rte_get_tsc_cycles();
+
+       done = rte_swx_table_learner_lookup(ts->obj,
+                                           l->mailbox,
+                                           time,
+                                           l->key,
+                                           &action_id,
+                                           &action_data,
+                                           &hit);
+       if (!done) {
+               /* Thread. */
+               TRACE("[Thread %2u] learner %u (not finalized)\n",
+                     p->thread_id,
+                     learner_id);
+
+               thread_yield(p);
+               return;
+       }
+
+       action_id = hit ? action_id : ts->default_action_id;
+       action_data = hit ? action_data : ts->default_action_data;
+       action_func = p->action_funcs[action_id];
+       n_pkts_hit = stats->n_pkts_hit[hit];
+       n_pkts_action = stats->n_pkts_action[action_id];
+
+       TRACE("[Thread %2u] learner %u (%s, action %u)\n",
+             p->thread_id,
+             learner_id,
+             hit ? "hit" : "miss",
+             (uint32_t)action_id);
+
+       t->action_id = action_id;
+       t->structs[0] = action_data;
+       t->hit = hit;
+       t->learner_id = learner_id;
+       t->time = time;
+       stats->n_pkts_hit[hit] = n_pkts_hit + 1;
+       stats->n_pkts_action[action_id] = n_pkts_action + 1;
+
+       /* Thread. */
+       thread_ip_action_call(p, t, action_id);
+
+       /* Action */
+       action_func(p);
+}
+
 /*
  * learn.
  */
@@ -2512,6 +2509,9 @@ action_find(struct rte_swx_pipeline *p, const char *name);
 static int
 action_has_nbo_args(struct action *a);
 
+static int
+learner_action_args_check(struct rte_swx_pipeline *p, struct action *a, const char *mf_name);
+
 static int
 instr_learn_translate(struct rte_swx_pipeline *p,
                      struct action *action,
@@ -2521,16 +2521,31 @@ instr_learn_translate(struct rte_swx_pipeline *p,
                      struct instruction_data *data __rte_unused)
 {
        struct action *a;
+       const char *mf_name;
+       uint32_t mf_offset = 0;
 
        CHECK(action, EINVAL);
-       CHECK(n_tokens == 2, EINVAL);
+       CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
 
        a = action_find(p, tokens[1]);
        CHECK(a, EINVAL);
        CHECK(!action_has_nbo_args(a), EINVAL);
 
+       mf_name = (n_tokens > 2) ? tokens[2] : NULL;
+       CHECK(!learner_action_args_check(p, a, mf_name), EINVAL);
+
+       if (mf_name) {
+               struct field *mf;
+
+               mf = metadata_field_parse(p, mf_name);
+               CHECK(mf, EINVAL);
+
+               mf_offset = mf->offset / 8;
+       }
+
        instr->type = INSTR_LEARNER_LEARN;
        instr->learn.action_id = a->id;
+       instr->learn.mf_offset = mf_offset;
 
        return 0;
 }
@@ -2540,27 +2555,8 @@ instr_learn_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t action_id = ip->learn.action_id;
-       uint32_t learner_id = t->learner_id;
-       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
-               p->n_selectors + learner_id];
-       struct learner_runtime *l = &t->learners[learner_id];
-       struct learner_statistics *stats = &p->learner_stats[learner_id];
-       uint32_t status;
-
-       /* Table. */
-       status = rte_swx_table_learner_add(ts->obj,
-                                          l->mailbox,
-                                          t->time,
-                                          action_id,
-                                          l->action_data[action_id]);
-
-       TRACE("[Thread %2u] learner %u learn %s\n",
-             p->thread_id,
-             learner_id,
-             status ? "ok" : "error");
 
-       stats->n_pkts_learn[status] += 1;
+       __instr_learn_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2589,20 +2585,9 @@ static inline void
 instr_forget_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
-       uint32_t learner_id = t->learner_id;
-       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
-               p->n_selectors + learner_id];
-       struct learner_runtime *l = &t->learners[learner_id];
-       struct learner_statistics *stats = &p->learner_stats[learner_id];
-
-       /* Table. */
-       rte_swx_table_learner_delete(ts->obj, l->mailbox);
-
-       TRACE("[Thread %2u] learner %u forget\n",
-             p->thread_id,
-             learner_id);
+       struct instruction *ip = t->ip;
 
-       stats->n_pkts_forget += 1;
+       __instr_forget_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2657,18 +2642,10 @@ instr_extern_obj_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint32_t obj_id = ip->ext_obj.ext_obj_id;
-       uint32_t func_id = ip->ext_obj.func_id;
-       struct extern_obj_runtime *obj = &t->extern_objs[obj_id];
-       rte_swx_extern_type_member_func_t func = obj->funcs[func_id];
-
-       TRACE("[Thread %2u] extern obj %u member func %u\n",
-             p->thread_id,
-             obj_id,
-             func_id);
+       uint32_t done;
 
        /* Extern object member function execute. */
-       uint32_t done = func(obj->obj, obj->mailbox);
+       done = __instr_extern_obj_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc_cond(t, done);
@@ -2680,16 +2657,10 @@ instr_extern_func_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint32_t ext_func_id = ip->ext_func.ext_func_id;
-       struct extern_func_runtime *ext_func = &t->extern_funcs[ext_func_id];
-       rte_swx_extern_func_t func = ext_func->func;
-
-       TRACE("[Thread %2u] extern func %u\n",
-             p->thread_id,
-             ext_func_id);
+       uint32_t done;
 
        /* Extern function execute. */
-       uint32_t done = func(ext_func->mailbox);
+       done = __instr_extern_func_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc_cond(t, done);
@@ -2761,10 +2732,7 @@ instr_mov_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] mov\n",
-             p->thread_id);
-
-       MOV(t, ip);
+       __instr_mov_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2776,10 +2744,7 @@ instr_mov_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] mov (mh)\n",
-             p->thread_id);
-
-       MOV_MH(t, ip);
+       __instr_mov_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2791,10 +2756,7 @@ instr_mov_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] mov (hm)\n",
-             p->thread_id);
-
-       MOV_HM(t, ip);
+       __instr_mov_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2806,10 +2768,7 @@ instr_mov_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] mov (hh)\n",
-             p->thread_id);
-
-       MOV_HH(t, ip);
+       __instr_mov_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2821,11 +2780,7 @@ instr_mov_i_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] mov m.f %" PRIx64 "\n",
-             p->thread_id,
-             ip->mov.src_val);
-
-       MOV_I(t, ip);
+       __instr_mov_i_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2835,46 +2790,12 @@ instr_mov_i_exec(struct rte_swx_pipeline *p)
  * dma.
  */
 static inline void
-__instr_dma_ht_exec(struct rte_swx_pipeline *p, uint32_t n_dma);
-
-static inline void
-__instr_dma_ht_exec(struct rte_swx_pipeline *p, uint32_t n_dma)
+instr_dma_ht_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint8_t *action_data = t->structs[0];
-       uint64_t valid_headers = t->valid_headers;
-       uint32_t i;
 
-       for (i = 0; i < n_dma; i++) {
-               uint32_t header_id = ip->dma.dst.header_id[i];
-               uint32_t struct_id = ip->dma.dst.struct_id[i];
-               uint32_t offset = ip->dma.src.offset[i];
-               uint32_t n_bytes = ip->dma.n_bytes[i];
-
-               struct header_runtime *h = &t->headers[header_id];
-               uint8_t *h_ptr0 = h->ptr0;
-               uint8_t *h_ptr = t->structs[struct_id];
-
-               void *dst = MASK64_BIT_GET(valid_headers, header_id) ?
-                       h_ptr : h_ptr0;
-               void *src = &action_data[offset];
-
-               TRACE("[Thread %2u] dma h.s t.f\n", p->thread_id);
-
-               /* Headers. */
-               memcpy(dst, src, n_bytes);
-               t->structs[struct_id] = dst;
-               valid_headers = MASK64_BIT_SET(valid_headers, header_id);
-       }
-
-       t->valid_headers = valid_headers;
-}
-
-static inline void
-instr_dma_ht_exec(struct rte_swx_pipeline *p)
-{
-       __instr_dma_ht_exec(p, 1);
+       __instr_dma_ht_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2883,10 +2804,10 @@ instr_dma_ht_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht2_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 2 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 2);
+       __instr_dma_ht2_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2895,10 +2816,10 @@ instr_dma_ht2_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht3_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 3 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 3);
+       __instr_dma_ht3_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2907,10 +2828,10 @@ instr_dma_ht3_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht4_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 4 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 4);
+       __instr_dma_ht4_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2919,10 +2840,10 @@ instr_dma_ht4_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht5_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 5 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 5);
+       __instr_dma_ht5_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2931,10 +2852,10 @@ instr_dma_ht5_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht6_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 6 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 6);
+       __instr_dma_ht6_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2943,10 +2864,10 @@ instr_dma_ht6_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht7_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 7 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 7);
+       __instr_dma_ht7_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -2955,10 +2876,10 @@ instr_dma_ht7_exec(struct rte_swx_pipeline *p)
 static inline void
 instr_dma_ht8_exec(struct rte_swx_pipeline *p)
 {
-       TRACE("[Thread %2u] *** The next 8 instructions are fused. ***\n",
-             p->thread_id);
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
 
-       __instr_dma_ht_exec(p, 8);
+       __instr_dma_ht8_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3094,8 +3015,8 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
        CHECK(n_tokens == 3, EINVAL);
 
        fdst = header_field_parse(p, dst, &hdst);
-       CHECK(fdst && (fdst->n_bits == 16), EINVAL);
-       CHECK(!fdst->var_size, EINVAL);
+       CHECK(fdst, EINVAL);
+       CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
 
        /* CKADD_FIELD. */
        fsrc = header_field_parse(p, src, &hsrc);
@@ -3115,17 +3036,16 @@ instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
        /* CKADD_STRUCT, CKADD_STRUCT20. */
        hsrc = header_parse(p, src);
        CHECK(hsrc, EINVAL);
-       CHECK(!hsrc->st->var_size, EINVAL);
 
        instr->type = INSTR_ALU_CKADD_STRUCT;
-       if ((hsrc->st->n_bits / 8) == 20)
+       if (!hsrc->st->var_size && ((hsrc->st->n_bits / 8) == 20))
                instr->type = INSTR_ALU_CKADD_STRUCT20;
 
        instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
        instr->alu.dst.n_bits = fdst->n_bits;
        instr->alu.dst.offset = fdst->offset / 8;
        instr->alu.src.struct_id = (uint8_t)hsrc->struct_id;
-       instr->alu.src.n_bits = hsrc->st->n_bits;
+       instr->alu.src.n_bits = (uint8_t)hsrc->id; /* The src header ID is stored here. */
        instr->alu.src.offset = 0; /* Unused. */
        return 0;
 }
@@ -3145,8 +3065,8 @@ instr_alu_cksub_translate(struct rte_swx_pipeline *p,
        CHECK(n_tokens == 3, EINVAL);
 
        fdst = header_field_parse(p, dst, &hdst);
-       CHECK(fdst && (fdst->n_bits == 16), EINVAL);
-       CHECK(!fdst->var_size, EINVAL);
+       CHECK(fdst, EINVAL);
+       CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
 
        fsrc = header_field_parse(p, src, &hsrc);
        CHECK(fsrc, EINVAL);
@@ -3448,10 +3368,8 @@ instr_alu_add_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add\n", p->thread_id);
-
-       /* Structs. */
-       ALU(t, ip, +);
+       /* Structs */
+       __instr_alu_add_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3463,10 +3381,8 @@ instr_alu_add_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, +);
+       __instr_alu_add_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3478,10 +3394,8 @@ instr_alu_add_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM(t, ip, +);
+       __instr_alu_add_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3493,10 +3407,8 @@ instr_alu_add_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH(t, ip, +);
+       __instr_alu_add_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3508,10 +3420,8 @@ instr_alu_add_mi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add (mi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MI(t, ip, +);
+       __instr_alu_add_mi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3523,10 +3433,8 @@ instr_alu_add_hi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] add (hi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HI(t, ip, +);
+       __instr_alu_add_hi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3538,10 +3446,8 @@ instr_alu_sub_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, -);
+       __instr_alu_sub_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3553,10 +3459,8 @@ instr_alu_sub_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, -);
+       __instr_alu_sub_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3568,10 +3472,8 @@ instr_alu_sub_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM(t, ip, -);
+       __instr_alu_sub_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3583,10 +3485,8 @@ instr_alu_sub_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH(t, ip, -);
+       __instr_alu_sub_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3598,10 +3498,8 @@ instr_alu_sub_mi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub (mi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MI(t, ip, -);
+       __instr_alu_sub_mi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3613,10 +3511,8 @@ instr_alu_sub_hi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] sub (hi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HI(t, ip, -);
+       __instr_alu_sub_hi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3628,10 +3524,8 @@ instr_alu_shl_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, <<);
+       __instr_alu_shl_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3643,10 +3537,8 @@ instr_alu_shl_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, <<);
+       __instr_alu_shl_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3658,10 +3550,8 @@ instr_alu_shl_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM(t, ip, <<);
+       __instr_alu_shl_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3673,10 +3563,8 @@ instr_alu_shl_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH(t, ip, <<);
+       __instr_alu_shl_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3688,10 +3576,8 @@ instr_alu_shl_mi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl (mi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MI(t, ip, <<);
+       __instr_alu_shl_mi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3703,10 +3589,8 @@ instr_alu_shl_hi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shl (hi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HI(t, ip, <<);
+       __instr_alu_shl_hi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3718,10 +3602,8 @@ instr_alu_shr_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, >>);
+       __instr_alu_shr_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3733,10 +3615,8 @@ instr_alu_shr_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, >>);
+       __instr_alu_shr_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3748,10 +3628,8 @@ instr_alu_shr_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM(t, ip, >>);
+       __instr_alu_shr_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3763,10 +3641,8 @@ instr_alu_shr_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH(t, ip, >>);
+       __instr_alu_shr_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3778,10 +3654,8 @@ instr_alu_shr_mi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr (mi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MI(t, ip, >>);
+       __instr_alu_shr_mi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3793,10 +3667,8 @@ instr_alu_shr_hi_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] shr (hi)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HI(t, ip, >>);
+       __instr_alu_shr_hi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3808,10 +3680,8 @@ instr_alu_and_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] and\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, &);
+       __instr_alu_and_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3823,10 +3693,8 @@ instr_alu_and_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] and (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, &);
+       __instr_alu_and_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3838,10 +3706,8 @@ instr_alu_and_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] and (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM_FAST(t, ip, &);
+       __instr_alu_and_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3853,10 +3719,8 @@ instr_alu_and_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] and (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH_FAST(t, ip, &);
+       __instr_alu_and_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3868,10 +3732,8 @@ instr_alu_and_i_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] and (i)\n", p->thread_id);
-
        /* Structs. */
-       ALU_I(t, ip, &);
+       __instr_alu_and_i_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3883,10 +3745,8 @@ instr_alu_or_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] or\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, |);
+       __instr_alu_or_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3898,10 +3758,8 @@ instr_alu_or_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] or (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, |);
+       __instr_alu_or_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3913,10 +3771,8 @@ instr_alu_or_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] or (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM_FAST(t, ip, |);
+       __instr_alu_or_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3928,10 +3784,8 @@ instr_alu_or_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] or (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH_FAST(t, ip, |);
+       __instr_alu_or_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3943,10 +3797,8 @@ instr_alu_or_i_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] or (i)\n", p->thread_id);
-
        /* Structs. */
-       ALU_I(t, ip, |);
+       __instr_alu_or_i_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3958,10 +3810,8 @@ instr_alu_xor_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] xor\n", p->thread_id);
-
        /* Structs. */
-       ALU(t, ip, ^);
+       __instr_alu_xor_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3973,10 +3823,8 @@ instr_alu_xor_mh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] xor (mh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_MH(t, ip, ^);
+       __instr_alu_xor_mh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -3988,10 +3836,8 @@ instr_alu_xor_hm_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] xor (hm)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HM_FAST(t, ip, ^);
+       __instr_alu_xor_hm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4003,10 +3849,8 @@ instr_alu_xor_hh_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] xor (hh)\n", p->thread_id);
-
        /* Structs. */
-       ALU_HH_FAST(t, ip, ^);
+       __instr_alu_xor_hh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4018,10 +3862,8 @@ instr_alu_xor_i_exec(struct rte_swx_pipeline *p)
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
 
-       TRACE("[Thread %2u] xor (i)\n", p->thread_id);
-
        /* Structs. */
-       ALU_I(t, ip, ^);
+       __instr_alu_xor_i_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4032,55 +3874,9 @@ instr_alu_ckadd_field_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint8_t *dst_struct, *src_struct;
-       uint16_t *dst16_ptr, dst;
-       uint64_t *src64_ptr, src64, src64_mask, src;
-       uint64_t r;
-
-       TRACE("[Thread %2u] ckadd (field)\n", p->thread_id);
 
        /* Structs. */
-       dst_struct = t->structs[ip->alu.dst.struct_id];
-       dst16_ptr = (uint16_t *)&dst_struct[ip->alu.dst.offset];
-       dst = *dst16_ptr;
-
-       src_struct = t->structs[ip->alu.src.struct_id];
-       src64_ptr = (uint64_t *)&src_struct[ip->alu.src.offset];
-       src64 = *src64_ptr;
-       src64_mask = UINT64_MAX >> (64 - ip->alu.src.n_bits);
-       src = src64 & src64_mask;
-
-       r = dst;
-       r = ~r & 0xFFFF;
-
-       /* The first input (r) is a 16-bit number. The second and the third
-        * inputs are 32-bit numbers. In the worst case scenario, the sum of the
-        * three numbers (output r) is a 34-bit number.
-        */
-       r += (src >> 32) + (src & 0xFFFFFFFF);
-
-       /* The first input is a 16-bit number. The second input is an 18-bit
-        * number. In the worst case scenario, the sum of the two numbers is a
-        * 19-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* The first input is a 16-bit number (0 .. 0xFFFF). The second input is
-        * a 3-bit number (0 .. 7). Their sum is a 17-bit number (0 .. 0x10006).
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* When the input r is (0 .. 0xFFFF), the output r is equal to the input
-        * r, so the output is (0 .. 0xFFFF). When the input r is (0x10000 ..
-        * 0x10006), the output r is (0 .. 7). So no carry bit can be generated,
-        * therefore the output r is always a 16-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       r = ~r & 0xFFFF;
-       r = r ? r : 0xFFFF;
-
-       *dst16_ptr = (uint16_t)r;
+       __instr_alu_ckadd_field_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4091,67 +3887,9 @@ instr_alu_cksub_field_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint8_t *dst_struct, *src_struct;
-       uint16_t *dst16_ptr, dst;
-       uint64_t *src64_ptr, src64, src64_mask, src;
-       uint64_t r;
-
-       TRACE("[Thread %2u] cksub (field)\n", p->thread_id);
 
        /* Structs. */
-       dst_struct = t->structs[ip->alu.dst.struct_id];
-       dst16_ptr = (uint16_t *)&dst_struct[ip->alu.dst.offset];
-       dst = *dst16_ptr;
-
-       src_struct = t->structs[ip->alu.src.struct_id];
-       src64_ptr = (uint64_t *)&src_struct[ip->alu.src.offset];
-       src64 = *src64_ptr;
-       src64_mask = UINT64_MAX >> (64 - ip->alu.src.n_bits);
-       src = src64 & src64_mask;
-
-       r = dst;
-       r = ~r & 0xFFFF;
-
-       /* Subtraction in 1's complement arithmetic (i.e. a '- b) is the same as
-        * the following sequence of operations in 2's complement arithmetic:
-        *    a '- b = (a - b) % 0xFFFF.
-        *
-        * In order to prevent an underflow for the below subtraction, in which
-        * a 33-bit number (the subtrahend) is taken out of a 16-bit number (the
-        * minuend), we first add a multiple of the 0xFFFF modulus to the
-        * minuend. The number we add to the minuend needs to be a 34-bit number
-        * or higher, so for readability reasons we picked the 36-bit multiple.
-        * We are effectively turning the 16-bit minuend into a 36-bit number:
-        *    (a - b) % 0xFFFF = (a + 0xFFFF00000 - b) % 0xFFFF.
-        */
-       r += 0xFFFF00000ULL; /* The output r is a 36-bit number. */
-
-       /* A 33-bit number is subtracted from a 36-bit number (the input r). The
-        * result (the output r) is a 36-bit number.
-        */
-       r -= (src >> 32) + (src & 0xFFFFFFFF);
-
-       /* The first input is a 16-bit number. The second input is a 20-bit
-        * number. Their sum is a 21-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* The first input is a 16-bit number (0 .. 0xFFFF). The second input is
-        * a 5-bit number (0 .. 31). The sum is a 17-bit number (0 .. 0x1001E).
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* When the input r is (0 .. 0xFFFF), the output r is equal to the input
-        * r, so the output is (0 .. 0xFFFF). When the input r is (0x10000 ..
-        * 0x1001E), the output r is (0 .. 31). So no carry bit can be
-        * generated, therefore the output r is always a 16-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       r = ~r & 0xFFFF;
-       r = r ? r : 0xFFFF;
-
-       *dst16_ptr = (uint16_t)r;
+       __instr_alu_cksub_field_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4162,47 +3900,9 @@ instr_alu_ckadd_struct20_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint8_t *dst_struct, *src_struct;
-       uint16_t *dst16_ptr;
-       uint32_t *src32_ptr;
-       uint64_t r0, r1;
-
-       TRACE("[Thread %2u] ckadd (struct of 20 bytes)\n", p->thread_id);
 
        /* Structs. */
-       dst_struct = t->structs[ip->alu.dst.struct_id];
-       dst16_ptr = (uint16_t *)&dst_struct[ip->alu.dst.offset];
-
-       src_struct = t->structs[ip->alu.src.struct_id];
-       src32_ptr = (uint32_t *)&src_struct[0];
-
-       r0 = src32_ptr[0]; /* r0 is a 32-bit number. */
-       r1 = src32_ptr[1]; /* r1 is a 32-bit number. */
-       r0 += src32_ptr[2]; /* The output r0 is a 33-bit number. */
-       r1 += src32_ptr[3]; /* The output r1 is a 33-bit number. */
-       r0 += r1 + src32_ptr[4]; /* The output r0 is a 35-bit number. */
-
-       /* The first input is a 16-bit number. The second input is a 19-bit
-        * number. Their sum is a 20-bit number.
-        */
-       r0 = (r0 & 0xFFFF) + (r0 >> 16);
-
-       /* The first input is a 16-bit number (0 .. 0xFFFF). The second input is
-        * a 4-bit number (0 .. 15). The sum is a 17-bit number (0 .. 0x1000E).
-        */
-       r0 = (r0 & 0xFFFF) + (r0 >> 16);
-
-       /* When the input r is (0 .. 0xFFFF), the output r is equal to the input
-        * r, so the output is (0 .. 0xFFFF). When the input r is (0x10000 ..
-        * 0x1000E), the output r is (0 .. 15). So no carry bit can be
-        * generated, therefore the output r is always a 16-bit number.
-        */
-       r0 = (r0 & 0xFFFF) + (r0 >> 16);
-
-       r0 = ~r0 & 0xFFFF;
-       r0 = r0 ? r0 : 0xFFFF;
-
-       *dst16_ptr = (uint16_t)r0;
+       __instr_alu_ckadd_struct20_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4213,49 +3913,9 @@ instr_alu_ckadd_struct_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint8_t *dst_struct, *src_struct;
-       uint16_t *dst16_ptr;
-       uint32_t *src32_ptr;
-       uint64_t r = 0;
-       uint32_t i;
-
-       TRACE("[Thread %2u] ckadd (struct)\n", p->thread_id);
 
        /* Structs. */
-       dst_struct = t->structs[ip->alu.dst.struct_id];
-       dst16_ptr = (uint16_t *)&dst_struct[ip->alu.dst.offset];
-
-       src_struct = t->structs[ip->alu.src.struct_id];
-       src32_ptr = (uint32_t *)&src_struct[0];
-
-       /* The max number of 32-bit words in a 256-byte header is 8 = 2^3.
-        * Therefore, in the worst case scenario, a 35-bit number is added to a
-        * 16-bit number (the input r), so the output r is 36-bit number.
-        */
-       for (i = 0; i < ip->alu.src.n_bits / 32; i++, src32_ptr++)
-               r += *src32_ptr;
-
-       /* The first input is a 16-bit number. The second input is a 20-bit
-        * number. Their sum is a 21-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* The first input is a 16-bit number (0 .. 0xFFFF). The second input is
-        * a 5-bit number (0 .. 31). The sum is a 17-bit number (0 .. 0x1000E).
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       /* When the input r is (0 .. 0xFFFF), the output r is equal to the input
-        * r, so the output is (0 .. 0xFFFF). When the input r is (0x10000 ..
-        * 0x1001E), the output r is (0 .. 31). So no carry bit can be
-        * generated, therefore the output r is always a 16-bit number.
-        */
-       r = (r & 0xFFFF) + (r >> 16);
-
-       r = ~r & 0xFFFF;
-       r = r ? r : 0xFFFF;
-
-       *dst16_ptr = (uint16_t)r;
+       __instr_alu_ckadd_struct_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4565,134 +4225,14 @@ instr_regadd_translate(struct rte_swx_pipeline *p,
        return 0;
 }
 
-static inline uint64_t *
-instr_regarray_regarray(struct rte_swx_pipeline *p, struct instruction *ip)
-{
-       struct regarray_runtime *r = &p->regarray_runtime[ip->regarray.regarray_id];
-       return r->regarray;
-}
-
-static inline uint64_t
-instr_regarray_idx_hbo(struct rte_swx_pipeline *p, struct thread *t, struct instruction *ip)
-{
-       struct regarray_runtime *r = &p->regarray_runtime[ip->regarray.regarray_id];
-
-       uint8_t *idx_struct = t->structs[ip->regarray.idx.struct_id];
-       uint64_t *idx64_ptr = (uint64_t *)&idx_struct[ip->regarray.idx.offset];
-       uint64_t idx64 = *idx64_ptr;
-       uint64_t idx64_mask = UINT64_MAX >> (64 - ip->regarray.idx.n_bits);
-       uint64_t idx = idx64 & idx64_mask & r->size_mask;
-
-       return idx;
-}
-
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-
-static inline uint64_t
-instr_regarray_idx_nbo(struct rte_swx_pipeline *p, struct thread *t, struct instruction *ip)
-{
-       struct regarray_runtime *r = &p->regarray_runtime[ip->regarray.regarray_id];
-
-       uint8_t *idx_struct = t->structs[ip->regarray.idx.struct_id];
-       uint64_t *idx64_ptr = (uint64_t *)&idx_struct[ip->regarray.idx.offset];
-       uint64_t idx64 = *idx64_ptr;
-       uint64_t idx = (ntoh64(idx64) >> (64 - ip->regarray.idx.n_bits)) & r->size_mask;
-
-       return idx;
-}
-
-#else
-
-#define instr_regarray_idx_nbo instr_regarray_idx_hbo
-
-#endif
-
-static inline uint64_t
-instr_regarray_idx_imm(struct rte_swx_pipeline *p, struct instruction *ip)
-{
-       struct regarray_runtime *r = &p->regarray_runtime[ip->regarray.regarray_id];
-
-       uint64_t idx = ip->regarray.idx_val & r->size_mask;
-
-       return idx;
-}
-
-static inline uint64_t
-instr_regarray_src_hbo(struct thread *t, struct instruction *ip)
-{
-       uint8_t *src_struct = t->structs[ip->regarray.dstsrc.struct_id];
-       uint64_t *src64_ptr = (uint64_t *)&src_struct[ip->regarray.dstsrc.offset];
-       uint64_t src64 = *src64_ptr;
-       uint64_t src64_mask = UINT64_MAX >> (64 - ip->regarray.dstsrc.n_bits);
-       uint64_t src = src64 & src64_mask;
-
-       return src;
-}
-
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-
-static inline uint64_t
-instr_regarray_src_nbo(struct thread *t, struct instruction *ip)
-{
-       uint8_t *src_struct = t->structs[ip->regarray.dstsrc.struct_id];
-       uint64_t *src64_ptr = (uint64_t *)&src_struct[ip->regarray.dstsrc.offset];
-       uint64_t src64 = *src64_ptr;
-       uint64_t src = ntoh64(src64) >> (64 - ip->regarray.dstsrc.n_bits);
-
-       return src;
-}
-
-#else
-
-#define instr_regarray_src_nbo instr_regarray_src_hbo
-
-#endif
-
-static inline void
-instr_regarray_dst_hbo_src_hbo_set(struct thread *t, struct instruction *ip, uint64_t src)
-{
-       uint8_t *dst_struct = t->structs[ip->regarray.dstsrc.struct_id];
-       uint64_t *dst64_ptr = (uint64_t *)&dst_struct[ip->regarray.dstsrc.offset];
-       uint64_t dst64 = *dst64_ptr;
-       uint64_t dst64_mask = UINT64_MAX >> (64 - ip->regarray.dstsrc.n_bits);
-
-       *dst64_ptr = (dst64 & ~dst64_mask) | (src & dst64_mask);
-
-}
-
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-
-static inline void
-instr_regarray_dst_nbo_src_hbo_set(struct thread *t, struct instruction *ip, uint64_t src)
-{
-       uint8_t *dst_struct = t->structs[ip->regarray.dstsrc.struct_id];
-       uint64_t *dst64_ptr = (uint64_t *)&dst_struct[ip->regarray.dstsrc.offset];
-       uint64_t dst64 = *dst64_ptr;
-       uint64_t dst64_mask = UINT64_MAX >> (64 - ip->regarray.dstsrc.n_bits);
-
-       src = hton64(src) >> (64 - ip->regarray.dstsrc.n_bits);
-       *dst64_ptr = (dst64 & ~dst64_mask) | (src & dst64_mask);
-}
-
-#else
-
-#define instr_regarray_dst_nbo_src_hbo_set instr_regarray_dst_hbo_src_hbo_set
-
-#endif
-
 static inline void
 instr_regprefetch_rh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regprefetch (r[h])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       rte_prefetch0(&regarray[idx]);
+       __instr_regprefetch_rh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4703,14 +4243,9 @@ instr_regprefetch_rm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regprefetch (r[m])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       rte_prefetch0(&regarray[idx]);
+       __instr_regprefetch_rm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4721,14 +4256,9 @@ instr_regprefetch_ri_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regprefetch (r[i])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       rte_prefetch0(&regarray[idx]);
+       __instr_regprefetch_ri_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4739,14 +4269,9 @@ instr_regrd_hrh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regrd (h = r[h])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       instr_regarray_dst_nbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_hrh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4757,14 +4282,9 @@ instr_regrd_hrm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regrd (h = r[m])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       instr_regarray_dst_nbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_hrm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4775,14 +4295,9 @@ instr_regrd_mrh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regrd (m = r[h])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       instr_regarray_dst_hbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_mrh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4793,12 +4308,9 @@ instr_regrd_mrm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       instr_regarray_dst_hbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_mrm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4809,14 +4321,9 @@ instr_regrd_hri_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regrd (h = r[i])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       instr_regarray_dst_nbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_hri_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4827,14 +4334,9 @@ instr_regrd_mri_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx;
-
-       TRACE("[Thread %2u] regrd (m = r[i])\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       instr_regarray_dst_hbo_src_hbo_set(t, ip, regarray[idx]);
+       __instr_regrd_mri_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4845,15 +4347,9 @@ instr_regwr_rhh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[h] = h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rhh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4864,15 +4360,9 @@ instr_regwr_rhm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[h] = m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rhm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4883,15 +4373,9 @@ instr_regwr_rmh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[m] = h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rmh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4902,15 +4386,9 @@ instr_regwr_rmm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[m] = m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rmm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4921,15 +4399,9 @@ instr_regwr_rhi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[h] = i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] = src;
+       __instr_regwr_rhi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4940,15 +4412,9 @@ instr_regwr_rmi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[m] = i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] = src;
+       __instr_regwr_rmi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4959,15 +4425,9 @@ instr_regwr_rih_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[i] = h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rih_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4978,15 +4438,9 @@ instr_regwr_rim_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[i] = m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] = src;
+       __instr_regwr_rim_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -4997,15 +4451,9 @@ instr_regwr_rii_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regwr (r[i] = i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] = src;
+       __instr_regwr_rii_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5016,15 +4464,9 @@ instr_regadd_rhh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[h] += h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rhh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5035,15 +4477,9 @@ instr_regadd_rhm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[h] += m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rhm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5054,15 +4490,9 @@ instr_regadd_rmh_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[m] += h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rmh_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5073,15 +4503,9 @@ instr_regadd_rmm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[m] += m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rmm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5092,15 +4516,9 @@ instr_regadd_rhi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[h] += i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_nbo(p, t, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] += src;
+       __instr_regadd_rhi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5111,15 +4529,9 @@ instr_regadd_rmi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[m] += i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_hbo(p, t, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] += src;
+       __instr_regadd_rmi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5130,15 +4542,9 @@ instr_regadd_rih_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[i] += h)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = instr_regarray_src_nbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rih_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5149,15 +4555,9 @@ instr_regadd_rim_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[i] += m)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = instr_regarray_src_hbo(t, ip);
-       regarray[idx] += src;
+       __instr_regadd_rim_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5168,15 +4568,9 @@ instr_regadd_rii_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       uint64_t *regarray, idx, src;
-
-       TRACE("[Thread %2u] regadd (r[i] += i)\n", p->thread_id);
 
        /* Structs. */
-       regarray = instr_regarray_regarray(p, ip);
-       idx = instr_regarray_idx_imm(p, ip);
-       src = ip->regarray.dstsrc_val;
-       regarray[idx] += src;
+       __instr_regadd_rii_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5294,8 +4688,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = HMEFT, length = HMEFT, color_in = I, color_out = MEF. */
@@ -5330,8 +4722,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = I, length = HMEFT, color_in = MEFT, color_out = MEF. */
@@ -5362,8 +4752,6 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
        /* index = I, length = HMEFT, color_in = I, color_out = MEF. */
@@ -5393,112 +4781,9 @@ instr_meter_translate(struct rte_swx_pipeline *p,
                instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
                instr->meter.color_out.n_bits = fcout->n_bits;
                instr->meter.color_out.offset = fcout->offset / 8;
-
-               return 0;
        }
 
-       CHECK(0, EINVAL);
-}
-
-static inline struct meter *
-instr_meter_idx_hbo(struct rte_swx_pipeline *p, struct thread *t, struct instruction *ip)
-{
-       struct metarray_runtime *r = &p->metarray_runtime[ip->meter.metarray_id];
-
-       uint8_t *idx_struct = t->structs[ip->meter.idx.struct_id];
-       uint64_t *idx64_ptr = (uint64_t *)&idx_struct[ip->meter.idx.offset];
-       uint64_t idx64 = *idx64_ptr;
-       uint64_t idx64_mask = UINT64_MAX >> (64 - (ip)->meter.idx.n_bits);
-       uint64_t idx = idx64 & idx64_mask & r->size_mask;
-
-       return &r->metarray[idx];
-}
-
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-
-static inline struct meter *
-instr_meter_idx_nbo(struct rte_swx_pipeline *p, struct thread *t, struct instruction *ip)
-{
-       struct metarray_runtime *r = &p->metarray_runtime[ip->meter.metarray_id];
-
-       uint8_t *idx_struct = t->structs[ip->meter.idx.struct_id];
-       uint64_t *idx64_ptr = (uint64_t *)&idx_struct[ip->meter.idx.offset];
-       uint64_t idx64 = *idx64_ptr;
-       uint64_t idx = (ntoh64(idx64) >> (64 - ip->meter.idx.n_bits)) & r->size_mask;
-
-       return &r->metarray[idx];
-}
-
-#else
-
-#define instr_meter_idx_nbo instr_meter_idx_hbo
-
-#endif
-
-static inline struct meter *
-instr_meter_idx_imm(struct rte_swx_pipeline *p, struct instruction *ip)
-{
-       struct metarray_runtime *r = &p->metarray_runtime[ip->meter.metarray_id];
-
-       uint64_t idx =  ip->meter.idx_val & r->size_mask;
-
-       return &r->metarray[idx];
-}
-
-static inline uint32_t
-instr_meter_length_hbo(struct thread *t, struct instruction *ip)
-{
-       uint8_t *src_struct = t->structs[ip->meter.length.struct_id];
-       uint64_t *src64_ptr = (uint64_t *)&src_struct[ip->meter.length.offset];
-       uint64_t src64 = *src64_ptr;
-       uint64_t src64_mask = UINT64_MAX >> (64 - (ip)->meter.length.n_bits);
-       uint64_t src = src64 & src64_mask;
-
-       return (uint32_t)src;
-}
-
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-
-static inline uint32_t
-instr_meter_length_nbo(struct thread *t, struct instruction *ip)
-{
-       uint8_t *src_struct = t->structs[ip->meter.length.struct_id];
-       uint64_t *src64_ptr = (uint64_t *)&src_struct[ip->meter.length.offset];
-       uint64_t src64 = *src64_ptr;
-       uint64_t src = ntoh64(src64) >> (64 - ip->meter.length.n_bits);
-
-       return (uint32_t)src;
-}
-
-#else
-
-#define instr_meter_length_nbo instr_meter_length_hbo
-
-#endif
-
-static inline enum rte_color
-instr_meter_color_in_hbo(struct thread *t, struct instruction *ip)
-{
-       uint8_t *src_struct = t->structs[ip->meter.color_in.struct_id];
-       uint64_t *src64_ptr = (uint64_t *)&src_struct[ip->meter.color_in.offset];
-       uint64_t src64 = *src64_ptr;
-       uint64_t src64_mask = UINT64_MAX >> (64 - ip->meter.color_in.n_bits);
-       uint64_t src = src64 & src64_mask;
-
-       return (enum rte_color)src;
-}
-
-static inline void
-instr_meter_color_out_hbo_set(struct thread *t, struct instruction *ip, enum rte_color color_out)
-{
-       uint8_t *dst_struct = t->structs[ip->meter.color_out.struct_id];
-       uint64_t *dst64_ptr = (uint64_t *)&dst_struct[ip->meter.color_out.offset];
-       uint64_t dst64 = *dst64_ptr;
-       uint64_t dst64_mask = UINT64_MAX >> (64 - ip->meter.color_out.n_bits);
-
-       uint64_t src = (uint64_t)color_out;
-
-       *dst64_ptr = (dst64 & ~dst64_mask) | (src & dst64_mask);
+       return 0;
 }
 
 static inline void
@@ -5506,13 +4791,9 @@ instr_metprefetch_h_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-
-       TRACE("[Thread %2u] metprefetch (h)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_nbo(p, t, ip);
-       rte_prefetch0(m);
+       __instr_metprefetch_h_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5523,13 +4804,9 @@ instr_metprefetch_m_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-
-       TRACE("[Thread %2u] metprefetch (m)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_hbo(p, t, ip);
-       rte_prefetch0(m);
+       __instr_metprefetch_m_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5540,13 +4817,9 @@ instr_metprefetch_i_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-
-       TRACE("[Thread %2u] metprefetch (i)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_imm(p, ip);
-       rte_prefetch0(m);
+       __instr_metprefetch_i_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5557,35 +4830,9 @@ instr_meter_hhm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (hhm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_nbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_hhm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5596,35 +4843,9 @@ instr_meter_hhi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (hhi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_nbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_hhi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5635,73 +4856,22 @@ instr_meter_hmm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (hmm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_nbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_hmm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
 }
+
 static inline void
 instr_meter_hmi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (hmi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_nbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_hmi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5712,35 +4882,9 @@ instr_meter_mhm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (mhm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_hbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_mhm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5751,35 +4895,9 @@ instr_meter_mhi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (mhi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_hbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_mhi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5790,35 +4908,9 @@ instr_meter_mmm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (mmm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_hbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_mmm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5829,35 +4921,9 @@ instr_meter_mmi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (mmi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_hbo(p, t, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_mmi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5868,35 +4934,9 @@ instr_meter_ihm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (ihm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_imm(p, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_ihm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5907,35 +4947,9 @@ instr_meter_ihi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (ihi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_imm(p, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_nbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_ihi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -5946,73 +4960,22 @@ instr_meter_imm_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (imm)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_imm(p, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = instr_meter_color_in_hbo(t, ip);
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_imm_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
 }
+
 static inline void
 instr_meter_imi_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       struct meter *m;
-       uint64_t time, n_pkts, n_bytes;
-       uint32_t length;
-       enum rte_color color_in, color_out;
-
-       TRACE("[Thread %2u] meter (imi)\n", p->thread_id);
 
        /* Structs. */
-       m = instr_meter_idx_imm(p, ip);
-       rte_prefetch0(m->n_pkts);
-       time = rte_get_tsc_cycles();
-       length = instr_meter_length_hbo(t, ip);
-       color_in = (enum rte_color)ip->meter.color_in_val;
-
-       color_out = rte_meter_trtcm_color_aware_check(&m->m,
-               &m->profile->profile,
-               time,
-               length,
-               color_in);
-
-       color_out &= m->color_mask;
-
-       n_pkts = m->n_pkts[color_out];
-       n_bytes = m->n_bytes[color_out];
-
-       instr_meter_color_out_hbo_set(t, ip, color_out);
-
-       m->n_pkts[color_out] = n_pkts + 1;
-       m->n_bytes[color_out] = n_bytes + length;
+       __instr_meter_imi_exec(p, t, ip);
 
        /* Thread. */
        thread_ip_inc(p);
@@ -6819,6 +5782,14 @@ instr_translate(struct rte_swx_pipeline *p,
                                            instr,
                                            data);
 
+       if (!strcmp(tokens[tpos], "mirror"))
+               return instr_mirror_translate(p,
+                                             action,
+                                             &tokens[tpos],
+                                             n_tokens - tpos,
+                                             instr,
+                                             data);
+
        if (!strcmp(tokens[tpos], "extract"))
                return instr_hdr_extract_translate(p,
                                                   action,
@@ -7115,7 +6086,7 @@ instr_translate(struct rte_swx_pipeline *p,
                                              instr,
                                              data);
 
-       CHECK(0, EINVAL);
+       return -EINVAL;
 }
 
 static struct instruction_data *
@@ -7161,7 +6132,20 @@ instr_label_check(struct instruction_data *instruction_data,
                        continue;
 
                for (j = i + 1; j < n_instructions; j++)
-                       CHECK(strcmp(label, data[j].label), EINVAL);
+                       CHECK(strcmp(label, instruction_data[j].label), EINVAL);
+       }
+
+       /* Check that no jump instruction (either conditional or not) can jump to itself (loop). */
+       for (i = 0; i < n_instructions; i++) {
+               struct instruction_data *data = &instruction_data[i];
+               char *label = data->label;
+               char *jmp_label = data->jmp_label;
+
+               /* Continue if this instruction does not have a label or it is not a jump. */
+               if (!label[0] || !jmp_label[0])
+                       continue;
+
+               CHECK(strcmp(label, jmp_label), EINVAL);
        }
 
        /* Get users for each instruction label. */
@@ -7385,7 +6369,7 @@ instr_pattern_emit_many_tx_search(struct instruction *instr,
        if (!i)
                return 0;
 
-       if (!instruction_is_tx(instr[i].type))
+       if (instr[i].type != INSTR_TX)
                return 0;
 
        if (data[i].n_users)
@@ -7809,13 +6793,14 @@ instruction_config(struct rte_swx_pipeline *p,
 
        if (a) {
                a->instructions = instr;
+               a->instruction_data = data;
                a->n_instructions = n_instructions;
        } else {
                p->instructions = instr;
+               p->instruction_data = data;
                p->n_instructions = n_instructions;
        }
 
-       free(data);
        return 0;
 
 error:
@@ -7824,12 +6809,12 @@ error:
        return err;
 }
 
-typedef void (*instr_exec_t)(struct rte_swx_pipeline *);
-
 static instr_exec_t instruction_table[] = {
        [INSTR_RX] = instr_rx_exec,
        [INSTR_TX] = instr_tx_exec,
        [INSTR_TX_I] = instr_tx_i_exec,
+       [INSTR_DROP] = instr_drop_exec,
+       [INSTR_MIRROR] = instr_mirror_exec,
 
        [INSTR_HDR_EXTRACT] = instr_hdr_extract_exec,
        [INSTR_HDR_EXTRACT2] = instr_hdr_extract2_exec,
@@ -7970,8 +6955,10 @@ static instr_exec_t instruction_table[] = {
        [INSTR_METER_IMI] = instr_meter_imi_exec,
 
        [INSTR_TABLE] = instr_table_exec,
+       [INSTR_TABLE_AF] = instr_table_af_exec,
        [INSTR_SELECTOR] = instr_selector_exec,
        [INSTR_LEARNER] = instr_learner_exec,
+       [INSTR_LEARNER_AF] = instr_learner_af_exec,
        [INSTR_LEARNER_LEARN] = instr_learn_exec,
        [INSTR_LEARNER_FORGET] = instr_forget_exec,
        [INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
@@ -8014,12 +7001,41 @@ static instr_exec_t instruction_table[] = {
        [INSTR_RETURN] = instr_return_exec,
 };
 
+static int
+instruction_table_build(struct rte_swx_pipeline *p)
+{
+       p->instruction_table = calloc(RTE_SWX_PIPELINE_INSTRUCTION_TABLE_SIZE_MAX,
+                                     sizeof(struct instr_exec_t *));
+       if (!p->instruction_table)
+               return -EINVAL;
+
+       memcpy(p->instruction_table, instruction_table, sizeof(instruction_table));
+
+       return 0;
+}
+
+static void
+instruction_table_build_free(struct rte_swx_pipeline *p)
+{
+       if (!p->instruction_table)
+               return;
+
+       free(p->instruction_table);
+       p->instruction_table = NULL;
+}
+
+static void
+instruction_table_free(struct rte_swx_pipeline *p)
+{
+       instruction_table_build_free(p);
+}
+
 static inline void
 instr_exec(struct rte_swx_pipeline *p)
 {
        struct thread *t = &p->threads[p->thread_id];
        struct instruction *ip = t->ip;
-       instr_exec_t instr = instruction_table[ip->type];
+       instr_exec_t instr = p->instruction_table[ip->type];
 
        instr(p);
 }
@@ -8113,8 +7129,8 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
                               uint32_t n_instructions)
 {
        struct struct_type *args_struct_type = NULL;
-       struct action *a;
-       int err;
+       struct action *a = NULL;
+       int status = 0;
 
        CHECK(p, EINVAL);
 
@@ -8130,12 +7146,16 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
 
        /* Node allocation. */
        a = calloc(1, sizeof(struct action));
-       CHECK(a, ENOMEM);
+       if (!a) {
+               status = -ENOMEM;
+               goto error;
+       }
+
        if (args_struct_type) {
                a->args_endianness = calloc(args_struct_type->n_fields, sizeof(int));
                if (!a->args_endianness) {
-                       free(a);
-                       CHECK(0, ENOMEM);
+                       status = -ENOMEM;
+                       goto error;
                }
        }
 
@@ -8145,18 +7165,26 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
        a->id = p->n_actions;
 
        /* Instruction translation. */
-       err = instruction_config(p, a, instructions, n_instructions);
-       if (err) {
-               free(a->args_endianness);
-               free(a);
-               return err;
-       }
+       status = instruction_config(p, a, instructions, n_instructions);
+       if (status)
+               goto error;
 
        /* Node add to tailq. */
        TAILQ_INSERT_TAIL(&p->actions, a, node);
        p->n_actions++;
 
        return 0;
+
+error:
+       if (!a)
+               return status;
+
+       free(a->args_endianness);
+       free(a->instructions);
+       free(a->instruction_data);
+       free(a);
+
+       return status;
 }
 
 static int
@@ -8164,19 +7192,26 @@ action_build(struct rte_swx_pipeline *p)
 {
        struct action *action;
 
-       p->action_instructions = calloc(p->n_actions,
-                                       sizeof(struct instruction *));
+       /* p->action_instructions. */
+       p->action_instructions = calloc(p->n_actions, sizeof(struct instruction *));
        CHECK(p->action_instructions, ENOMEM);
 
        TAILQ_FOREACH(action, &p->actions, node)
                p->action_instructions[action->id] = action->instructions;
 
+       /* p->action_funcs. */
+       p->action_funcs = calloc(p->n_actions, sizeof(action_func_t));
+       CHECK(p->action_funcs, ENOMEM);
+
        return 0;
 }
 
 static void
 action_build_free(struct rte_swx_pipeline *p)
 {
+       free(p->action_funcs);
+       p->action_funcs = NULL;
+
        free(p->action_instructions);
        p->action_instructions = NULL;
 }
@@ -8194,7 +7229,9 @@ action_free(struct rte_swx_pipeline *p)
                        break;
 
                TAILQ_REMOVE(&p->actions, action, node);
+               free(action->args_endianness);
                free(action->instructions);
+               free(action->instruction_data);
                free(action);
        }
 }
@@ -8233,6 +7270,91 @@ action_arg_src_mov_count(struct action *a,
        return n_users;
 }
 
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+#define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))
+#define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))
+#else
+#define field_ntoh(val, n_bits) (val)
+#define field_hton(val, n_bits) (val)
+#endif
+
+#define ACTION_ARGS_TOKENS_MAX 256
+
+static int
+action_args_parse(struct action *a, const char *args, uint8_t *data)
+{
+       char *tokens[ACTION_ARGS_TOKENS_MAX], *s0 = NULL, *s;
+       uint32_t n_tokens = 0, offset = 0, i;
+       int status = 0;
+
+       /* Checks. */
+       if (!a->st || !args || !args[0]) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       /* Memory allocation. */
+       s0 = strdup(args);
+       if (!s0) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       /* Parse the string into tokens. */
+       for (s = s0; ; ) {
+               char *token;
+
+               token = strtok_r(s, " \f\n\r\t\v", &s);
+               if (!token)
+                       break;
+
+               if (n_tokens >= RTE_DIM(tokens)) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               tokens[n_tokens] = token;
+               n_tokens++;
+       }
+
+       /* More checks. */
+       if (n_tokens != a->st->n_fields * 2) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       /* Process the action arguments. */
+       for (i = 0; i < a->st->n_fields; i++) {
+               struct field *f = &a->st->fields[i];
+               char *arg_name = tokens[i * 2];
+               char *arg_val = tokens[i * 2 + 1];
+               uint64_t val;
+
+               if (strcmp(arg_name, f->name)) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               val = strtoull(arg_val, &arg_val, 0);
+               if (arg_val[0]) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               /* Endianness conversion. */
+               if (a->args_endianness[i])
+                       val = field_hton(val, f->n_bits);
+
+               /* Copy to entry. */
+               memcpy(&data[offset], (uint8_t *)&val, f->n_bits / 8);
+               offset += f->n_bits / 8;
+       }
+
+error:
+       free(s0);
+       return status;
+}
+
 /*
  * Table.
  */
@@ -8449,7 +7571,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                              uint32_t size)
 {
        struct table_type *type;
-       struct table *t;
+       struct table *t = NULL;
        struct action *default_action;
        struct header *header = NULL;
        uint32_t action_data_size_max = 0, i;
@@ -8476,6 +7598,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                const char *action_name = params->action_names[i];
                struct action *a;
                uint32_t action_data_size;
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
                CHECK_NAME(action_name, EINVAL);
 
@@ -8486,6 +7609,12 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                action_data_size = a->st ? a->st->n_bits / 8 : 0;
                if (action_data_size > action_data_size_max)
                        action_data_size_max = action_data_size;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+               CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
        }
 
        CHECK_NAME(params->default_action_name, EINVAL);
@@ -8494,9 +7623,12 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
                            params->default_action_name))
                        break;
        CHECK(i < params->n_actions, EINVAL);
+       CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
+             EINVAL);
+
        default_action = action_find(p, params->default_action_name);
-       CHECK((default_action->st && params->default_action_data) ||
-             !params->default_action_data, EINVAL);
+       CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
+             EINVAL);
 
        /* Table type checks. */
        if (recommended_table_type_name)
@@ -8517,31 +7649,43 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
 
        /* Memory allocation. */
        t = calloc(1, sizeof(struct table));
-       CHECK(t, ENOMEM);
+       if (!t) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        t->fields = calloc(params->n_fields, sizeof(struct match_field));
        if (!t->fields) {
-               free(t);
-               CHECK(0, ENOMEM);
+               status = -ENOMEM;
+               goto error;
        }
 
        t->actions = calloc(params->n_actions, sizeof(struct action *));
        if (!t->actions) {
-               free(t->fields);
-               free(t);
-               CHECK(0, ENOMEM);
+               status = -ENOMEM;
+               goto error;
        }
 
        if (action_data_size_max) {
                t->default_action_data = calloc(1, action_data_size_max);
                if (!t->default_action_data) {
-                       free(t->actions);
-                       free(t->fields);
-                       free(t);
-                       CHECK(0, ENOMEM);
+                       status = -ENOMEM;
+                       goto error;
                }
        }
 
+       t->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_table_entries) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       t->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!t->action_is_for_default_entry) {
+               status = -ENOMEM;
+               goto error;
+       }
+
        /* Node initialization. */
        strcpy(t->name, name);
        if (args && args[0])
@@ -8560,13 +7704,27 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        t->n_fields = params->n_fields;
        t->header = header;
 
-       for (i = 0; i < params->n_actions; i++)
+       for (i = 0; i < params->n_actions; i++) {
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+
                t->actions[i] = action_find(p, params->action_names[i]);
+               t->action_is_for_table_entries[i] = action_is_for_table_entries;
+               t->action_is_for_default_entry[i] = action_is_for_default_entry;
+       }
        t->default_action = default_action;
-       if (default_action->st)
-               memcpy(t->default_action_data,
-                      params->default_action_data,
-                      default_action->st->n_bits / 8);
+       if (default_action->st) {
+               status = action_args_parse(default_action,
+                                          params->default_action_args,
+                                          t->default_action_data);
+               if (status)
+                       goto error;
+       }
+
        t->n_actions = params->n_actions;
        t->default_action_is_const = params->default_action_is_const;
        t->action_data_size_max = action_data_size_max;
@@ -8579,6 +7737,19 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
        p->n_tables++;
 
        return 0;
+
+error:
+       if (!t)
+               return status;
+
+       free(t->action_is_for_default_entry);
+       free(t->action_is_for_table_entries);
+       free(t->default_action_data);
+       free(t->actions);
+       free(t->fields);
+       free(t);
+
+       return status;
 }
 
 static struct rte_swx_table_params *
@@ -9319,23 +8490,18 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        /* Action checks. */
        CHECK(params->n_actions, EINVAL);
-
        CHECK(params->action_names, EINVAL);
        for (i = 0; i < params->n_actions; i++) {
                const char *action_name = params->action_names[i];
-               const char *action_field_name = params->action_field_names[i];
                struct action *a;
                uint32_t action_data_size;
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
                CHECK_NAME(action_name, EINVAL);
 
                a = action_find(p, action_name);
                CHECK(a, EINVAL);
 
-               status = learner_action_args_check(p, a, action_field_name);
-               if (status)
-                       return status;
-
                status = learner_action_learning_check(p,
                                                       a,
                                                       params->action_names,
@@ -9346,6 +8512,12 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
                action_data_size = a->st ? a->st->n_bits / 8 : 0;
                if (action_data_size > action_data_size_max)
                        action_data_size_max = action_data_size;
+
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
+               CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
        }
 
        CHECK_NAME(params->default_action_name, EINVAL);
@@ -9354,10 +8526,12 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
                            params->default_action_name))
                        break;
        CHECK(i < params->n_actions, EINVAL);
+       CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
+             EINVAL);
 
        default_action = action_find(p, params->default_action_name);
-       CHECK((default_action->st && params->default_action_data) ||
-             !params->default_action_data, EINVAL);
+       CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
+             EINVAL);
 
        /* Any other checks. */
        CHECK(size, EINVAL);
@@ -9365,25 +8539,41 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        /* Memory allocation. */
        l = calloc(1, sizeof(struct learner));
-       if (!l)
-               goto nomem;
+       if (!l) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        l->fields = calloc(params->n_fields, sizeof(struct field *));
-       if (!l->fields)
-               goto nomem;
+       if (!l->fields) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        l->actions = calloc(params->n_actions, sizeof(struct action *));
-       if (!l->actions)
-               goto nomem;
-
-       l->action_arg = calloc(params->n_actions, sizeof(struct field *));
-       if (!l->action_arg)
-               goto nomem;
+       if (!l->actions) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        if (action_data_size_max) {
                l->default_action_data = calloc(1, action_data_size_max);
-               if (!l->default_action_data)
-                       goto nomem;
+               if (!l->default_action_data) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       l->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_table_entries) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       l->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
+       if (!l->action_is_for_default_entry) {
+               status = -ENOMEM;
+               goto error;
        }
 
        /* Node initialization. */
@@ -9402,19 +8592,27 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
        l->header = header;
 
        for (i = 0; i < params->n_actions; i++) {
-               const char *mf_name = params->action_field_names[i];
+               int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
-               l->actions[i] = action_find(p, params->action_names[i]);
+               if (params->action_is_for_table_entries)
+                       action_is_for_table_entries = params->action_is_for_table_entries[i];
+               if (params->action_is_for_default_entry)
+                       action_is_for_default_entry = params->action_is_for_default_entry[i];
 
-               l->action_arg[i] = mf_name ? metadata_field_parse(p, mf_name) : NULL;
+               l->actions[i] = action_find(p, params->action_names[i]);
+               l->action_is_for_table_entries[i] = action_is_for_table_entries;
+               l->action_is_for_default_entry[i] = action_is_for_default_entry;
        }
 
        l->default_action = default_action;
 
-       if (default_action->st)
-               memcpy(l->default_action_data,
-                      params->default_action_data,
-                      default_action->st->n_bits / 8);
+       if (default_action->st) {
+               status = action_args_parse(default_action,
+                                          params->default_action_args,
+                                          l->default_action_data);
+               if (status)
+                       goto error;
+       }
 
        l->n_actions = params->n_actions;
 
@@ -9434,16 +8632,18 @@ rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
 
        return 0;
 
-nomem:
+error:
        if (!l)
-               return -ENOMEM;
+               return status;
 
-       free(l->action_arg);
+       free(l->action_is_for_default_entry);
+       free(l->action_is_for_table_entries);
+       free(l->default_action_data);
        free(l->actions);
        free(l->fields);
        free(l);
 
-       return -ENOMEM;
+       return status;
 }
 
 static void
@@ -9533,7 +8733,6 @@ learner_build_free(struct rte_swx_pipeline *p)
                        struct learner_runtime *r = &t->learners[j];
 
                        free(r->mailbox);
-                       free(r->action_data);
                }
 
                free(t->learners);
@@ -9577,7 +8776,6 @@ learner_build(struct rte_swx_pipeline *p)
                TAILQ_FOREACH(l, &p->learners, node) {
                        struct learner_runtime *r = &t->learners[l->id];
                        uint64_t size;
-                       uint32_t j;
 
                        /* r->mailbox. */
                        size = rte_swx_table_learner_mailbox_size_get();
@@ -9593,21 +8791,6 @@ learner_build(struct rte_swx_pipeline *p)
                        r->key = l->header ?
                                &t->structs[l->header->struct_id] :
                                &t->structs[p->metadata_struct_id];
-
-                       /* r->action_data. */
-                       r->action_data = calloc(p->n_actions, sizeof(uint8_t *));
-                       if (!r->action_data) {
-                               status = -ENOMEM;
-                               goto error;
-                       }
-
-                       for (j = 0; j < l->n_actions; j++) {
-                               struct action *a = l->actions[j];
-                               struct field *mf = l->action_arg[j];
-                               uint8_t *m = t->structs[p->metadata_struct_id];
-
-                               r->action_data[a->id] = mf ? &m[mf->offset / 8] : NULL;
-                       }
                }
        }
 
@@ -9634,7 +8817,6 @@ learner_free(struct rte_swx_pipeline *p)
                TAILQ_REMOVE(&p->learners, l, node);
                free(l->fields);
                free(l->actions);
-               free(l->action_arg);
                free(l->default_action_data);
                free(l);
        }
@@ -9650,7 +8832,7 @@ table_state_build(struct rte_swx_pipeline *p)
        struct selector *s;
        struct learner *l;
 
-       p->table_state = calloc(p->n_tables + p->n_selectors,
+       p->table_state = calloc(p->n_tables + p->n_selectors + p->n_learners,
                                sizeof(struct rte_swx_table_state));
        CHECK(p->table_state, ENOMEM);
 
@@ -10086,17 +9268,145 @@ metarray_free(struct rte_swx_pipeline *p)
 /*
  * Pipeline.
  */
+void
+rte_swx_pipeline_free(struct rte_swx_pipeline *p)
+{
+       void *lib;
+
+       if (!p)
+               return;
+
+       lib = p->lib;
+
+       free(p->instruction_data);
+       free(p->instructions);
+
+       metarray_free(p);
+       regarray_free(p);
+       table_state_free(p);
+       learner_free(p);
+       selector_free(p);
+       table_free(p);
+       action_free(p);
+       instruction_table_free(p);
+       metadata_free(p);
+       header_free(p);
+       extern_func_free(p);
+       extern_obj_free(p);
+       mirroring_free(p);
+       port_out_free(p);
+       port_in_free(p);
+       struct_free(p);
+
+       free(p);
+
+       if (lib)
+               dlclose(lib);
+}
+
+static int
+port_in_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "ethdev",
+               &rte_swx_port_ethdev_reader_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "ring",
+               &rte_swx_port_ring_reader_ops);
+       if (status)
+               return status;
+
+#ifdef RTE_PORT_PCAP
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "source",
+               &rte_swx_port_source_ops);
+       if (status)
+               return status;
+#endif
+
+       status = rte_swx_pipeline_port_in_type_register(p,
+               "fd",
+               &rte_swx_port_fd_reader_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
+static int
+port_out_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "ethdev",
+               &rte_swx_port_ethdev_writer_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "ring",
+               &rte_swx_port_ring_writer_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "sink",
+               &rte_swx_port_sink_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_port_out_type_register(p,
+               "fd",
+               &rte_swx_port_fd_writer_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
+static int
+table_types_register(struct rte_swx_pipeline *p)
+{
+       int status;
+
+       status = rte_swx_pipeline_table_type_register(p,
+               "exact",
+               RTE_SWX_TABLE_MATCH_EXACT,
+               &rte_swx_table_exact_match_ops);
+       if (status)
+               return status;
+
+       status = rte_swx_pipeline_table_type_register(p,
+               "wildcard",
+               RTE_SWX_TABLE_MATCH_WILDCARD,
+               &rte_swx_table_wildcard_match_ops);
+       if (status)
+               return status;
+
+       return 0;
+}
+
 int
 rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 {
-       struct rte_swx_pipeline *pipeline;
+       struct rte_swx_pipeline *pipeline = NULL;
+       int status = 0;
 
        /* Check input parameters. */
        CHECK(p, EINVAL);
 
        /* Memory allocation. */
        pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
-       CHECK(pipeline, ENOMEM);
+       if (!pipeline) {
+               status = -ENOMEM;
+               goto error;
+       }
 
        /* Initialization. */
        TAILQ_INIT(&pipeline->struct_types);
@@ -10120,34 +9430,24 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
        pipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */
        pipeline->numa_node = numa_node;
 
-       *p = pipeline;
-       return 0;
-}
+       status = port_in_types_register(pipeline);
+       if (status)
+               goto error;
 
-void
-rte_swx_pipeline_free(struct rte_swx_pipeline *p)
-{
-       if (!p)
-               return;
+       status = port_out_types_register(pipeline);
+       if (status)
+               goto error;
 
-       free(p->instructions);
+       status = table_types_register(pipeline);
+       if (status)
+               goto error;
 
-       metarray_free(p);
-       regarray_free(p);
-       table_state_free(p);
-       learner_free(p);
-       selector_free(p);
-       table_free(p);
-       action_free(p);
-       metadata_free(p);
-       header_free(p);
-       extern_func_free(p);
-       extern_obj_free(p);
-       port_out_free(p);
-       port_in_free(p);
-       struct_free(p);
+       *p = pipeline;
+       return 0;
 
-       free(p);
+error:
+       rte_swx_pipeline_free(pipeline);
+       return status;
 }
 
 int
@@ -10172,9 +9472,15 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
        return 0;
 }
 
+static int
+pipeline_compile(struct rte_swx_pipeline *p);
+
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
+       struct rte_swx_port_sink_params drop_port_params = {
+               .file_name = NULL,
+       };
        int status;
 
        CHECK(p, EINVAL);
@@ -10184,10 +9490,22 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
        if (status)
                goto error;
 
+       /* Drop port. */
+       status = rte_swx_pipeline_port_out_config(p,
+                                                 p->n_ports_out,
+                                                 "sink",
+                                                 &drop_port_params);
+       if (status)
+               goto error;
+
        status = port_out_build(p);
        if (status)
                goto error;
 
+       status = mirroring_build(p);
+       if (status)
+               goto error;
+
        status = struct_build(p);
        if (status)
                goto error;
@@ -10208,6 +9526,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
        if (status)
                goto error;
 
+       status = instruction_table_build(p);
+       if (status)
+               goto error;
+
        status = action_build(p);
        if (status)
                goto error;
@@ -10237,6 +9559,9 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
                goto error;
 
        p->build_done = 1;
+
+       pipeline_compile(p);
+
        return 0;
 
 error:
@@ -10247,10 +9572,12 @@ error:
        selector_build_free(p);
        table_build_free(p);
        action_build_free(p);
+       instruction_table_build_free(p);
        metadata_build_free(p);
        header_build_free(p);
        extern_func_build_free(p);
        extern_obj_build_free(p);
+       mirroring_build_free(p);
        port_out_build_free(p);
        port_in_build_free(p);
        struct_build_free(p);
@@ -10302,6 +9629,8 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
 
        pipeline->n_ports_in = p->n_ports_in;
        pipeline->n_ports_out = p->n_ports_out;
+       pipeline->n_mirroring_slots = p->n_mirroring_slots;
+       pipeline->n_mirroring_sessions = p->n_mirroring_sessions;
        pipeline->n_actions = n_actions;
        pipeline->n_tables = n_tables;
        pipeline->n_selectors = p->n_selectors;
@@ -10430,6 +9759,9 @@ rte_swx_ctl_table_action_info_get(struct rte_swx_pipeline *p,
 
        table_action->action_id = t->actions[table_action_id]->id;
 
+       table_action->action_is_for_table_entries = t->action_is_for_table_entries[table_action_id];
+       table_action->action_is_for_default_entry = t->action_is_for_default_entry[table_action_id];
+
        return 0;
 }
 
@@ -10617,6 +9949,12 @@ rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
 
        learner_action->action_id = l->actions[learner_action_id]->id;
 
+       learner_action->action_is_for_table_entries =
+               l->action_is_for_table_entries[learner_action_id];
+
+       learner_action->action_is_for_default_entry =
+               l->action_is_for_default_entry[learner_action_id];
+
        return 0;
 }
 
@@ -10979,3 +10317,2671 @@ rte_swx_ctl_meter_stats_read(struct rte_swx_pipeline *p,
 
        return 0;
 }
+
+int
+rte_swx_ctl_pipeline_mirroring_session_set(struct rte_swx_pipeline *p,
+                                          uint32_t session_id,
+                                          struct rte_swx_pipeline_mirroring_session_params *params)
+{
+       struct mirroring_session *s;
+
+       CHECK(p, EINVAL);
+       CHECK(p->build_done, EEXIST);
+       CHECK(session_id < p->n_mirroring_sessions, EINVAL);
+       CHECK(params, EINVAL);
+       CHECK(params->port_id < p->n_ports_out, EINVAL);
+
+       s = &p->mirroring_sessions[session_id];
+       s->port_id = params->port_id;
+       s->fast_clone = params->fast_clone;
+       s->truncation_length = params->truncation_length ? params->truncation_length : UINT32_MAX;
+
+       return 0;
+}
+
+/*
+ * Pipeline compilation.
+ */
+static const char *
+instr_type_to_name(struct instruction *instr)
+{
+       switch (instr->type) {
+       case INSTR_RX: return "INSTR_RX";
+
+       case INSTR_TX: return "INSTR_TX";
+       case INSTR_TX_I: return "INSTR_TX_I";
+       case INSTR_DROP: return "INSTR_DROP";
+       case INSTR_MIRROR: return "INSTR_MIRROR";
+
+       case INSTR_HDR_EXTRACT: return "INSTR_HDR_EXTRACT";
+       case INSTR_HDR_EXTRACT2: return "INSTR_HDR_EXTRACT2";
+       case INSTR_HDR_EXTRACT3: return "INSTR_HDR_EXTRACT3";
+       case INSTR_HDR_EXTRACT4: return "INSTR_HDR_EXTRACT4";
+       case INSTR_HDR_EXTRACT5: return "INSTR_HDR_EXTRACT5";
+       case INSTR_HDR_EXTRACT6: return "INSTR_HDR_EXTRACT6";
+       case INSTR_HDR_EXTRACT7: return "INSTR_HDR_EXTRACT7";
+       case INSTR_HDR_EXTRACT8: return "INSTR_HDR_EXTRACT8";
+
+       case INSTR_HDR_EXTRACT_M: return "INSTR_HDR_EXTRACT_M";
+
+       case INSTR_HDR_LOOKAHEAD: return "INSTR_HDR_LOOKAHEAD";
+
+       case INSTR_HDR_EMIT: return "INSTR_HDR_EMIT";
+       case INSTR_HDR_EMIT_TX: return "INSTR_HDR_EMIT_TX";
+       case INSTR_HDR_EMIT2_TX: return "INSTR_HDR_EMIT2_TX";
+       case INSTR_HDR_EMIT3_TX: return "INSTR_HDR_EMIT3_TX";
+       case INSTR_HDR_EMIT4_TX: return "INSTR_HDR_EMIT4_TX";
+       case INSTR_HDR_EMIT5_TX: return "INSTR_HDR_EMIT5_TX";
+       case INSTR_HDR_EMIT6_TX: return "INSTR_HDR_EMIT6_TX";
+       case INSTR_HDR_EMIT7_TX: return "INSTR_HDR_EMIT7_TX";
+       case INSTR_HDR_EMIT8_TX: return "INSTR_HDR_EMIT8_TX";
+
+       case INSTR_HDR_VALIDATE: return "INSTR_HDR_VALIDATE";
+       case INSTR_HDR_INVALIDATE: return "INSTR_HDR_INVALIDATE";
+
+       case INSTR_MOV: return "INSTR_MOV";
+       case INSTR_MOV_MH: return "INSTR_MOV_MH";
+       case INSTR_MOV_HM: return "INSTR_MOV_HM";
+       case INSTR_MOV_HH: return "INSTR_MOV_HH";
+       case INSTR_MOV_I: return "INSTR_MOV_I";
+
+       case INSTR_DMA_HT: return "INSTR_DMA_HT";
+       case INSTR_DMA_HT2: return "INSTR_DMA_HT2";
+       case INSTR_DMA_HT3: return "INSTR_DMA_HT3";
+       case INSTR_DMA_HT4: return "INSTR_DMA_HT4";
+       case INSTR_DMA_HT5: return "INSTR_DMA_HT5";
+       case INSTR_DMA_HT6: return "INSTR_DMA_HT6";
+       case INSTR_DMA_HT7: return "INSTR_DMA_HT7";
+       case INSTR_DMA_HT8: return "INSTR_DMA_HT8";
+
+       case INSTR_ALU_ADD: return "INSTR_ALU_ADD";
+       case INSTR_ALU_ADD_MH: return "INSTR_ALU_ADD_MH";
+       case INSTR_ALU_ADD_HM: return "INSTR_ALU_ADD_HM";
+       case INSTR_ALU_ADD_HH: return "INSTR_ALU_ADD_HH";
+       case INSTR_ALU_ADD_MI: return "INSTR_ALU_ADD_MI";
+       case INSTR_ALU_ADD_HI: return "INSTR_ALU_ADD_HI";
+
+       case INSTR_ALU_SUB: return "INSTR_ALU_SUB";
+       case INSTR_ALU_SUB_MH: return "INSTR_ALU_SUB_MH";
+       case INSTR_ALU_SUB_HM: return "INSTR_ALU_SUB_HM";
+       case INSTR_ALU_SUB_HH: return "INSTR_ALU_SUB_HH";
+       case INSTR_ALU_SUB_MI: return "INSTR_ALU_SUB_MI";
+       case INSTR_ALU_SUB_HI: return "INSTR_ALU_SUB_HI";
+
+       case INSTR_ALU_CKADD_FIELD: return "INSTR_ALU_CKADD_FIELD";
+       case INSTR_ALU_CKADD_STRUCT20: return "INSTR_ALU_CKADD_STRUCT20";
+       case INSTR_ALU_CKADD_STRUCT: return "INSTR_ALU_CKADD_STRUCT";
+       case INSTR_ALU_CKSUB_FIELD: return "INSTR_ALU_CKSUB_FIELD";
+
+       case INSTR_ALU_AND: return "INSTR_ALU_AND";
+       case INSTR_ALU_AND_MH: return "INSTR_ALU_AND_MH";
+       case INSTR_ALU_AND_HM: return "INSTR_ALU_AND_HM";
+       case INSTR_ALU_AND_HH: return "INSTR_ALU_AND_HH";
+       case INSTR_ALU_AND_I: return "INSTR_ALU_AND_I";
+
+       case INSTR_ALU_OR: return "INSTR_ALU_OR";
+       case INSTR_ALU_OR_MH: return "INSTR_ALU_OR_MH";
+       case INSTR_ALU_OR_HM: return "INSTR_ALU_OR_HM";
+       case INSTR_ALU_OR_HH: return "INSTR_ALU_OR_HH";
+       case INSTR_ALU_OR_I: return "INSTR_ALU_OR_I";
+
+       case INSTR_ALU_XOR: return "INSTR_ALU_XOR";
+       case INSTR_ALU_XOR_MH: return "INSTR_ALU_XOR_MH";
+       case INSTR_ALU_XOR_HM: return "INSTR_ALU_XOR_HM";
+       case INSTR_ALU_XOR_HH: return "INSTR_ALU_XOR_HH";
+       case INSTR_ALU_XOR_I: return "INSTR_ALU_XOR_I";
+
+       case INSTR_ALU_SHL: return "INSTR_ALU_SHL";
+       case INSTR_ALU_SHL_MH: return "INSTR_ALU_SHL_MH";
+       case INSTR_ALU_SHL_HM: return "INSTR_ALU_SHL_HM";
+       case INSTR_ALU_SHL_HH: return "INSTR_ALU_SHL_HH";
+       case INSTR_ALU_SHL_MI: return "INSTR_ALU_SHL_MI";
+       case INSTR_ALU_SHL_HI: return "INSTR_ALU_SHL_HI";
+
+       case INSTR_ALU_SHR: return "INSTR_ALU_SHR";
+       case INSTR_ALU_SHR_MH: return "INSTR_ALU_SHR_MH";
+       case INSTR_ALU_SHR_HM: return "INSTR_ALU_SHR_HM";
+       case INSTR_ALU_SHR_HH: return "INSTR_ALU_SHR_HH";
+       case INSTR_ALU_SHR_MI: return "INSTR_ALU_SHR_MI";
+       case INSTR_ALU_SHR_HI: return "INSTR_ALU_SHR_HI";
+
+       case INSTR_REGPREFETCH_RH: return "INSTR_REGPREFETCH_RH";
+       case INSTR_REGPREFETCH_RM: return "INSTR_REGPREFETCH_RM";
+       case INSTR_REGPREFETCH_RI: return "INSTR_REGPREFETCH_RI";
+
+       case INSTR_REGRD_HRH: return "INSTR_REGRD_HRH";
+       case INSTR_REGRD_HRM: return "INSTR_REGRD_HRM";
+       case INSTR_REGRD_HRI: return "INSTR_REGRD_HRI";
+       case INSTR_REGRD_MRH: return "INSTR_REGRD_MRH";
+       case INSTR_REGRD_MRM: return "INSTR_REGRD_MRM";
+       case INSTR_REGRD_MRI: return "INSTR_REGRD_MRI";
+
+       case INSTR_REGWR_RHH: return "INSTR_REGWR_RHH";
+       case INSTR_REGWR_RHM: return "INSTR_REGWR_RHM";
+       case INSTR_REGWR_RHI: return "INSTR_REGWR_RHI";
+       case INSTR_REGWR_RMH: return "INSTR_REGWR_RMH";
+       case INSTR_REGWR_RMM: return "INSTR_REGWR_RMM";
+       case INSTR_REGWR_RMI: return "INSTR_REGWR_RMI";
+       case INSTR_REGWR_RIH: return "INSTR_REGWR_RIH";
+       case INSTR_REGWR_RIM: return "INSTR_REGWR_RIM";
+       case INSTR_REGWR_RII: return "INSTR_REGWR_RII";
+
+       case INSTR_REGADD_RHH: return "INSTR_REGADD_RHH";
+       case INSTR_REGADD_RHM: return "INSTR_REGADD_RHM";
+       case INSTR_REGADD_RHI: return "INSTR_REGADD_RHI";
+       case INSTR_REGADD_RMH: return "INSTR_REGADD_RMH";
+       case INSTR_REGADD_RMM: return "INSTR_REGADD_RMM";
+       case INSTR_REGADD_RMI: return "INSTR_REGADD_RMI";
+       case INSTR_REGADD_RIH: return "INSTR_REGADD_RIH";
+       case INSTR_REGADD_RIM: return "INSTR_REGADD_RIM";
+       case INSTR_REGADD_RII: return "INSTR_REGADD_RII";
+
+       case INSTR_METPREFETCH_H: return "INSTR_METPREFETCH_H";
+       case INSTR_METPREFETCH_M: return "INSTR_METPREFETCH_M";
+       case INSTR_METPREFETCH_I: return "INSTR_METPREFETCH_I";
+
+       case INSTR_METER_HHM: return "INSTR_METER_HHM";
+       case INSTR_METER_HHI: return "INSTR_METER_HHI";
+       case INSTR_METER_HMM: return "INSTR_METER_HMM";
+       case INSTR_METER_HMI: return "INSTR_METER_HMI";
+       case INSTR_METER_MHM: return "INSTR_METER_MHM";
+       case INSTR_METER_MHI: return "INSTR_METER_MHI";
+       case INSTR_METER_MMM: return "INSTR_METER_MMM";
+       case INSTR_METER_MMI: return "INSTR_METER_MMI";
+       case INSTR_METER_IHM: return "INSTR_METER_IHM";
+       case INSTR_METER_IHI: return "INSTR_METER_IHI";
+       case INSTR_METER_IMM: return "INSTR_METER_IMM";
+       case INSTR_METER_IMI: return "INSTR_METER_IMI";
+
+       case INSTR_TABLE: return "INSTR_TABLE";
+       case INSTR_TABLE_AF: return "INSTR_TABLE_AF";
+       case INSTR_SELECTOR: return "INSTR_SELECTOR";
+       case INSTR_LEARNER: return "INSTR_LEARNER";
+       case INSTR_LEARNER_AF: return "INSTR_LEARNER_AF";
+
+       case INSTR_LEARNER_LEARN: return "INSTR_LEARNER_LEARN";
+       case INSTR_LEARNER_FORGET: return "INSTR_LEARNER_FORGET";
+
+       case INSTR_EXTERN_OBJ: return "INSTR_EXTERN_OBJ";
+       case INSTR_EXTERN_FUNC: return "INSTR_EXTERN_FUNC";
+
+       case INSTR_JMP: return "INSTR_JMP";
+       case INSTR_JMP_VALID: return "INSTR_JMP_VALID";
+       case INSTR_JMP_INVALID: return "INSTR_JMP_INVALID";
+       case INSTR_JMP_HIT: return "INSTR_JMP_HIT";
+       case INSTR_JMP_MISS: return "INSTR_JMP_MISS";
+       case INSTR_JMP_ACTION_HIT: return "INSTR_JMP_ACTION_HIT";
+       case INSTR_JMP_ACTION_MISS: return "INSTR_JMP_ACTION_MISS";
+       case INSTR_JMP_EQ: return "INSTR_JMP_EQ";
+       case INSTR_JMP_EQ_MH: return "INSTR_JMP_EQ_MH";
+       case INSTR_JMP_EQ_HM: return "INSTR_JMP_EQ_HM";
+       case INSTR_JMP_EQ_HH: return "INSTR_JMP_EQ_HH";
+       case INSTR_JMP_EQ_I: return "INSTR_JMP_EQ_I";
+       case INSTR_JMP_NEQ: return "INSTR_JMP_NEQ";
+       case INSTR_JMP_NEQ_MH: return "INSTR_JMP_NEQ_MH";
+       case INSTR_JMP_NEQ_HM: return "INSTR_JMP_NEQ_HM";
+       case INSTR_JMP_NEQ_HH: return "INSTR_JMP_NEQ_HH";
+       case INSTR_JMP_NEQ_I: return "INSTR_JMP_NEQ_I";
+       case INSTR_JMP_LT: return "INSTR_JMP_LT";
+       case INSTR_JMP_LT_MH: return "INSTR_JMP_LT_MH";
+       case INSTR_JMP_LT_HM: return "INSTR_JMP_LT_HM";
+       case INSTR_JMP_LT_HH: return "INSTR_JMP_LT_HH";
+       case INSTR_JMP_LT_MI: return "INSTR_JMP_LT_MI";
+       case INSTR_JMP_LT_HI: return "INSTR_JMP_LT_HI";
+       case INSTR_JMP_GT: return "INSTR_JMP_GT";
+       case INSTR_JMP_GT_MH: return "INSTR_JMP_GT_MH";
+       case INSTR_JMP_GT_HM: return "INSTR_JMP_GT_HM";
+       case INSTR_JMP_GT_HH: return "INSTR_JMP_GT_HH";
+       case INSTR_JMP_GT_MI: return "INSTR_JMP_GT_MI";
+       case INSTR_JMP_GT_HI: return "INSTR_JMP_GT_HI";
+
+       case INSTR_RETURN: return "INSTR_RETURN";
+
+       default: return "INSTR_UNKNOWN";
+       }
+}
+
+typedef void
+(*instruction_export_t)(struct instruction *, FILE *);
+
+static void
+instr_io_export(struct instruction *instr, FILE *f)
+{
+       uint32_t n_io = 0, n_io_imm = 0, n_hdrs = 0, i;
+
+       /* n_io, n_io_imm, n_hdrs. */
+       if (instr->type == INSTR_RX ||
+           instr->type == INSTR_TX ||
+           instr->type == INSTR_HDR_EXTRACT_M ||
+           (instr->type >= INSTR_HDR_EMIT_TX && instr->type <= INSTR_HDR_EMIT8_TX))
+               n_io = 1;
+
+       if (instr->type == INSTR_TX_I)
+               n_io_imm = 1;
+
+       if (instr->type >= INSTR_HDR_EXTRACT && instr->type <= INSTR_HDR_EXTRACT8)
+               n_hdrs = 1 + (instr->type - INSTR_HDR_EXTRACT);
+
+       if (instr->type == INSTR_HDR_EXTRACT_M ||
+           instr->type == INSTR_HDR_LOOKAHEAD ||
+           instr->type == INSTR_HDR_EMIT)
+               n_hdrs = 1;
+
+       if (instr->type >= INSTR_HDR_EMIT_TX && instr->type <= INSTR_HDR_EMIT8_TX)
+               n_hdrs = 1 + (instr->type - INSTR_HDR_EMIT_TX);
+
+       /* instr. */
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n",
+               instr_type_to_name(instr));
+
+       /* instr.io. */
+       if (n_io || n_io_imm || n_hdrs)
+               fprintf(f,
+                       "\t\t.io = {\n");
+
+       /* instr.io.io. */
+       if (n_io)
+               fprintf(f,
+                       "\t\t\t.io = {\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t},\n",
+                       instr->io.io.offset,
+                       instr->io.io.n_bits);
+
+       if (n_io_imm)
+               fprintf(f,
+                       "\t\t\t.io = {\n"
+                       "\t\t\t\t.val = %u,\n"
+                       "\t\t\t},\n",
+                       instr->io.io.val);
+
+       /* instr.io.hdr. */
+       if (n_hdrs) {
+               fprintf(f,
+                       "\t\t.hdr = {\n");
+
+               /* instr.io.hdr.header_id. */
+               fprintf(f,
+                       "\t\t\t.header_id = {");
+
+               for (i = 0; i < n_hdrs; i++)
+                       fprintf(f,
+                               "%u, ",
+                               instr->io.hdr.header_id[i]);
+
+               fprintf(f,
+                       "},\n");
+
+               /* instr.io.hdr.struct_id. */
+               fprintf(f,
+                       "\t\t\t.struct_id = {");
+
+               for (i = 0; i < n_hdrs; i++)
+                       fprintf(f,
+                               "%u, ",
+                               instr->io.hdr.struct_id[i]);
+
+               fprintf(f,
+                       "},\n");
+
+               /* instr.io.hdr.n_bytes. */
+               fprintf(f,
+                       "\t\t\t.n_bytes = {");
+
+               for (i = 0; i < n_hdrs; i++)
+                       fprintf(f,
+                               "%u, ",
+                               instr->io.hdr.n_bytes[i]);
+
+               fprintf(f,
+                       "},\n");
+
+               /* instr.io.hdr - closing curly brace. */
+               fprintf(f,
+                       "\t\t\t}\n,");
+       }
+
+       /* instr.io - closing curly brace. */
+       if (n_io || n_io_imm || n_hdrs)
+               fprintf(f,
+                       "\t\t},\n");
+
+       /* instr - closing curly brace. */
+       fprintf(f,
+               "\t},\n");
+}
+
+static void
+instr_mirror_export(struct instruction *instr, FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.mirror = {\n"
+               "\t\t\t.dst = {\n"
+               "\t\t\t\t.struct_id = %u,\n"
+               "\t\t\t\t.n_bits = %u,\n"
+               "\t\t\t\t.offset = %u,\n"
+               "\t\t\t}\n,"
+               "\t\t\t.src = {\n"
+               "\t\t\t\t.struct_id = %u,\n"
+               "\t\t\t\t.n_bits = %u,\n"
+               "\t\t\t\t.offset = %u,\n"
+               "\t\t\t}\n,"
+               "\t\t},\n"
+               "\t},\n",
+               instr_type_to_name(instr),
+               instr->mirror.dst.struct_id,
+               instr->mirror.dst.n_bits,
+               instr->mirror.dst.offset,
+               instr->mirror.src.struct_id,
+               instr->mirror.src.n_bits,
+               instr->mirror.src.offset);
+}
+
+static void
+instr_hdr_validate_export(struct instruction *instr, FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.valid = {\n"
+               "\t\t\t.header_id = %u,\n"
+               "\t\t},\n"
+               "\t},\n",
+               instr_type_to_name(instr),
+               instr->valid.header_id);
+}
+
+static void
+instr_mov_export(struct instruction *instr, FILE *f)
+{
+       if (instr->type != INSTR_MOV_I)
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.mov = {\n"
+                       "\t\t\t.dst = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n"
+                       "\t\t\t.src = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->mov.dst.struct_id,
+                       instr->mov.dst.n_bits,
+                       instr->mov.dst.offset,
+                       instr->mov.src.struct_id,
+                       instr->mov.src.n_bits,
+                       instr->mov.src.offset);
+       else
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.mov = {\n"
+                       "\t\t\t.dst = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t}\n,"
+                       "\t\t\t.src_val = %" PRIu64 ",\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->mov.dst.struct_id,
+                       instr->mov.dst.n_bits,
+                       instr->mov.dst.offset,
+                       instr->mov.src_val);
+}
+
+static void
+instr_dma_ht_export(struct instruction *instr, FILE *f)
+{
+       uint32_t n_dma = 0, i;
+
+       /* n_dma. */
+       n_dma = 1 + (instr->type - INSTR_DMA_HT);
+
+       /* instr. */
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n",
+               instr_type_to_name(instr));
+
+       /* instr.dma. */
+       fprintf(f,
+               "\t\t.dma = {\n");
+
+       /* instr.dma.dst. */
+       fprintf(f,
+               "\t\t\t.dst = {\n");
+
+       /* instr.dma.dst.header_id. */
+       fprintf(f,
+               "\t\t\t\t.header_id = {");
+
+       for (i = 0; i < n_dma; i++)
+               fprintf(f,
+                       "%u, ",
+                       instr->dma.dst.header_id[i]);
+
+       fprintf(f,
+               "},\n");
+
+       /* instr.dma.dst.struct_id. */
+       fprintf(f,
+               "\t\t\t\t.struct_id = {");
+
+       for (i = 0; i < n_dma; i++)
+               fprintf(f,
+                       "%u, ",
+                       instr->dma.dst.struct_id[i]);
+
+       fprintf(f,
+               "},\n");
+
+       /* instr.dma.dst - closing curly brace. */
+       fprintf(f,
+               "\t\t\t},\n");
+
+       /* instr.dma.src. */
+       fprintf(f,
+               "\t\t\t.src = {\n");
+
+       /* instr.dma.src.offset. */
+       fprintf(f,
+               "\t\t\t\t.offset = {");
+
+       for (i = 0; i < n_dma; i++)
+               fprintf(f,
+                       "%u, ",
+                       instr->dma.src.offset[i]);
+
+       fprintf(f,
+               "},\n");
+
+       /* instr.dma.src - closing curly brace. */
+       fprintf(f,
+               "\t\t\t},\n");
+
+       /* instr.dma.n_bytes. */
+       fprintf(f,
+               "\t\t\t.n_bytes = {");
+
+       for (i = 0; i < n_dma; i++)
+               fprintf(f,
+                       "%u, ",
+                       instr->dma.n_bytes[i]);
+
+       fprintf(f,
+               "},\n");
+
+       /* instr.dma - closing curly brace. */
+       fprintf(f,
+               "\t\t},\n");
+
+       /* instr - closing curly brace. */
+       fprintf(f,
+               "\t},\n");
+}
+
+static void
+instr_alu_export(struct instruction *instr, FILE *f)
+{
+       int imm = 0;
+
+       if (instr->type == INSTR_ALU_ADD_MI ||
+           instr->type == INSTR_ALU_ADD_HI ||
+           instr->type == INSTR_ALU_SUB_MI ||
+           instr->type == INSTR_ALU_SUB_HI ||
+           instr->type == INSTR_ALU_SHL_MI ||
+           instr->type == INSTR_ALU_SHL_HI ||
+           instr->type == INSTR_ALU_SHR_MI ||
+           instr->type == INSTR_ALU_SHR_HI ||
+           instr->type == INSTR_ALU_AND_I ||
+           instr->type == INSTR_ALU_OR_I ||
+           instr->type == INSTR_ALU_XOR_I)
+               imm = 1;
+
+       if (!imm)
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.alu = {\n"
+                       "\t\t\t.dst = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n"
+                       "\t\t\t.src = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->alu.dst.struct_id,
+                       instr->alu.dst.n_bits,
+                       instr->alu.dst.offset,
+                       instr->alu.src.struct_id,
+                       instr->alu.src.n_bits,
+                       instr->alu.src.offset);
+       else
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.alu = {\n"
+                       "\t\t\t.dst = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t}\n,"
+                       "\t\t\t.src_val = %" PRIu64 ",\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->alu.dst.struct_id,
+                       instr->alu.dst.n_bits,
+                       instr->alu.dst.offset,
+                       instr->alu.src_val);
+}
+
+static void
+instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
+{
+       int prefetch  = 0, idx_imm = 0, src_imm = 0;
+
+       if (instr->type == INSTR_REGPREFETCH_RH ||
+           instr->type == INSTR_REGPREFETCH_RM ||
+           instr->type == INSTR_REGPREFETCH_RI)
+               prefetch = 1;
+
+       /* index is the 3rd operand for the regrd instruction and the 2nd
+        * operand for the regwr and regadd instructions.
+        */
+       if (instr->type == INSTR_REGPREFETCH_RI ||
+           instr->type == INSTR_REGRD_HRI ||
+           instr->type == INSTR_REGRD_MRI ||
+           instr->type == INSTR_REGWR_RIH ||
+           instr->type == INSTR_REGWR_RIM ||
+           instr->type == INSTR_REGWR_RII ||
+           instr->type == INSTR_REGADD_RIH ||
+           instr->type == INSTR_REGADD_RIM ||
+           instr->type == INSTR_REGADD_RII)
+               idx_imm = 1;
+
+       /* src is the 3rd operand for the regwr and regadd instructions. */
+       if (instr->type == INSTR_REGWR_RHI ||
+           instr->type == INSTR_REGWR_RMI ||
+           instr->type == INSTR_REGWR_RII ||
+           instr->type == INSTR_REGADD_RHI ||
+           instr->type == INSTR_REGADD_RMI ||
+           instr->type == INSTR_REGADD_RII)
+               src_imm = 1;
+
+       /* instr.regarray.regarray_id. */
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.regarray = {\n"
+               "\t\t\t.regarray_id = %u,\n",
+               instr_type_to_name(instr),
+               instr->regarray.regarray_id);
+
+       /* instr.regarray.idx / instr.regarray.idx_val. */
+       if (!idx_imm)
+               fprintf(f,
+                       "\t\t\t\t.idx = {\n"
+                       "\t\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t\t.offset = %u,\n"
+                       "\t\t\t\t},\n",
+                       instr->regarray.idx.struct_id,
+                       instr->regarray.idx.n_bits,
+                       instr->regarray.idx.offset);
+       else
+               fprintf(f,
+                       "\t\t\t\t.idx_val = %u,\n",
+                       instr->regarray.idx_val);
+
+       /* instr.regarray.dstsrc / instr.regarray.dstsrc_val. */
+       if (!prefetch) {
+               if (!src_imm)
+                       fprintf(f,
+                               "\t\t\t\t.dstsrc = {\n"
+                               "\t\t\t\t\t.struct_id = %u,\n"
+                               "\t\t\t\t\t.n_bits = %u,\n"
+                               "\t\t\t\t\t.offset = %u,\n"
+                               "\t\t\t\t},\n",
+                               instr->regarray.dstsrc.struct_id,
+                               instr->regarray.dstsrc.n_bits,
+                               instr->regarray.dstsrc.offset);
+               else
+                       fprintf(f,
+                               "\t\t\t\t.dstsrc_val = %" PRIu64 ",\n",
+                               instr->regarray.dstsrc_val);
+       }
+
+       /* instr.regarray and instr - closing curly braces. */
+       fprintf(f,
+               "\t\t},\n"
+               "\t},\n");
+}
+
+static void
+instr_meter_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
+{
+       int prefetch  = 0, idx_imm = 0, color_in_imm = 0;
+
+       if (instr->type == INSTR_METPREFETCH_H ||
+           instr->type == INSTR_METPREFETCH_M ||
+           instr->type == INSTR_METPREFETCH_I)
+               prefetch = 1;
+
+       /* idx_imm. */
+       if (instr->type == INSTR_METPREFETCH_I ||
+           instr->type == INSTR_METER_IHM ||
+           instr->type == INSTR_METER_IHI ||
+           instr->type == INSTR_METER_IMM ||
+           instr->type == INSTR_METER_IMI)
+               idx_imm = 1;
+
+       /* color_in_imm. */
+       if (instr->type == INSTR_METER_HHI ||
+           instr->type == INSTR_METER_HMI ||
+           instr->type == INSTR_METER_MHI ||
+           instr->type == INSTR_METER_MMI ||
+           instr->type == INSTR_METER_IHI ||
+           instr->type == INSTR_METER_IMI)
+               color_in_imm = 1;
+
+       /* instr.meter.metarray_id. */
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.meter = {\n"
+               "\t\t\t.metarray_id = %u,\n",
+               instr_type_to_name(instr),
+               instr->meter.metarray_id);
+
+       /* instr.meter.idx / instr.meter.idx_val. */
+       if (!idx_imm)
+               fprintf(f,
+                       "\t\t\t.idx = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n",
+                       instr->meter.idx.struct_id,
+                       instr->meter.idx.n_bits,
+                       instr->meter.idx.offset);
+       else
+               fprintf(f,
+                       "\t\t\t.idx_val = %u,\n",
+                       instr->meter.idx_val);
+
+       if (!prefetch) {
+               /* instr.meter.length. */
+               fprintf(f,
+                       "\t\t\t.length = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n",
+                       instr->meter.length.struct_id,
+                       instr->meter.length.n_bits,
+                       instr->meter.length.offset);
+
+               /* instr.meter.color_in / instr.meter.color_in_val. */
+               if (!color_in_imm)
+                       fprintf(f,
+                               "\t\t\t.color_in = {\n"
+                               "\t\t\t\t.struct_id = %u,\n"
+                               "\t\t\t\t.n_bits = %u,\n"
+                               "\t\t\t\t.offset = %u,\n"
+                               "\t\t\t},\n",
+                               instr->meter.color_in.struct_id,
+                               instr->meter.color_in.n_bits,
+                               instr->meter.color_in.offset);
+               else
+                       fprintf(f,
+                               "\t\t\t.color_in_val = %u,\n",
+                               (uint32_t)instr->meter.color_in_val);
+
+               /* instr.meter.color_out. */
+               fprintf(f,
+                       "\t\t\t.color_out = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n",
+                       instr->meter.color_out.struct_id,
+                       instr->meter.color_out.n_bits,
+                       instr->meter.color_out.offset);
+       }
+
+       /* instr.meter and instr - closing curly braces. */
+       fprintf(f,
+               "\t\t},\n"
+               "\t},\n");
+}
+
+static void
+instr_table_export(struct instruction *instr,
+               FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.table = {\n"
+               "\t\t\t.table_id = %u,\n"
+               "\t\t},\n"
+               "\t},\n",
+               instr_type_to_name(instr),
+               instr->table.table_id);
+}
+
+static void
+instr_learn_export(struct instruction *instr, FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.learn = {\n"
+               "\t\t\t\t.action_id = %u,\n"
+               "\t\t},\n"
+               "\t},\n",
+               instr_type_to_name(instr),
+               instr->learn.action_id);
+}
+
+static void
+instr_forget_export(struct instruction *instr, FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t},\n",
+               instr_type_to_name(instr));
+}
+
+static void
+instr_extern_export(struct instruction *instr, FILE *f)
+{
+       if (instr->type == INSTR_EXTERN_OBJ)
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.ext_obj = {\n"
+                       "\t\t\t.ext_obj_id = %u,\n"
+                       "\t\t\t.func_id = %u,\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->ext_obj.ext_obj_id,
+                       instr->ext_obj.func_id);
+       else
+               fprintf(f,
+                       "\t{\n"
+                       "\t\t.type = %s,\n"
+                       "\t\t.ext_func = {\n"
+                       "\t\t\t.ext_func_id = %u,\n"
+                       "\t\t},\n"
+                       "\t},\n",
+                       instr_type_to_name(instr),
+                       instr->ext_func.ext_func_id);
+}
+
+static void
+instr_jmp_export(struct instruction *instr, FILE *f __rte_unused)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n"
+               "\t\t.jmp = {\n"
+               "\t\t\t.ip = NULL,\n",
+               instr_type_to_name(instr));
+
+       switch (instr->type) {
+       case INSTR_JMP_VALID:
+       case INSTR_JMP_INVALID:
+               fprintf(f,
+                       "\t\t\t.header_id = %u,\n",
+                       instr->jmp.header_id);
+               break;
+
+       case INSTR_JMP_ACTION_HIT:
+       case INSTR_JMP_ACTION_MISS:
+               fprintf(f,
+                       "\t\t\t.action_id = %u,\n",
+                       instr->jmp.action_id);
+               break;
+
+       case INSTR_JMP_EQ:
+       case INSTR_JMP_EQ_MH:
+       case INSTR_JMP_EQ_HM:
+       case INSTR_JMP_EQ_HH:
+       case INSTR_JMP_NEQ:
+       case INSTR_JMP_NEQ_MH:
+       case INSTR_JMP_NEQ_HM:
+       case INSTR_JMP_NEQ_HH:
+       case INSTR_JMP_LT:
+       case INSTR_JMP_LT_MH:
+       case INSTR_JMP_LT_HM:
+       case INSTR_JMP_LT_HH:
+       case INSTR_JMP_GT:
+       case INSTR_JMP_GT_MH:
+       case INSTR_JMP_GT_HM:
+       case INSTR_JMP_GT_HH:
+               fprintf(f,
+                       "\t\t\t.a = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n"
+                       "\t\t\t.b = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t},\n",
+                       instr->jmp.a.struct_id,
+                       instr->jmp.a.n_bits,
+                       instr->jmp.a.offset,
+                       instr->jmp.b.struct_id,
+                       instr->jmp.b.n_bits,
+                       instr->jmp.b.offset);
+               break;
+
+       case INSTR_JMP_EQ_I:
+       case INSTR_JMP_NEQ_I:
+       case INSTR_JMP_LT_MI:
+       case INSTR_JMP_LT_HI:
+       case INSTR_JMP_GT_MI:
+       case INSTR_JMP_GT_HI:
+               fprintf(f,
+                       "\t\t\t.a = {\n"
+                       "\t\t\t\t.struct_id = %u,\n"
+                       "\t\t\t\t.n_bits = %u,\n"
+                       "\t\t\t\t.offset = %u,\n"
+                       "\t\t\t}\n,"
+                       "\t\t\t.b_val = %" PRIu64 ",\n",
+                       instr->jmp.a.struct_id,
+                       instr->jmp.a.n_bits,
+                       instr->jmp.a.offset,
+                       instr->jmp.b_val);
+               break;
+
+       default:
+               break;
+       }
+
+       fprintf(f,
+               "\t\t},\n"
+               "\t},\n");
+}
+
+static void
+instr_return_export(struct instruction *instr,
+               FILE *f)
+{
+       fprintf(f,
+               "\t{\n"
+               "\t\t.type = %s,\n",
+               instr_type_to_name(instr));
+
+       fprintf(f,
+               "\t},\n");
+}
+
+static instruction_export_t export_table[] = {
+       [INSTR_RX] = instr_io_export,
+
+       [INSTR_TX] = instr_io_export,
+       [INSTR_TX_I] = instr_io_export,
+       [INSTR_DROP] = instr_io_export,
+       [INSTR_MIRROR] = instr_mirror_export,
+
+       [INSTR_HDR_EXTRACT] = instr_io_export,
+       [INSTR_HDR_EXTRACT2] = instr_io_export,
+       [INSTR_HDR_EXTRACT3] = instr_io_export,
+       [INSTR_HDR_EXTRACT4] = instr_io_export,
+       [INSTR_HDR_EXTRACT5] = instr_io_export,
+       [INSTR_HDR_EXTRACT6] = instr_io_export,
+       [INSTR_HDR_EXTRACT7] = instr_io_export,
+       [INSTR_HDR_EXTRACT8] = instr_io_export,
+
+       [INSTR_HDR_EXTRACT_M] = instr_io_export,
+
+       [INSTR_HDR_LOOKAHEAD] = instr_io_export,
+
+       [INSTR_HDR_EMIT] = instr_io_export,
+       [INSTR_HDR_EMIT_TX] = instr_io_export,
+       [INSTR_HDR_EMIT2_TX] = instr_io_export,
+       [INSTR_HDR_EMIT3_TX] = instr_io_export,
+       [INSTR_HDR_EMIT4_TX] = instr_io_export,
+       [INSTR_HDR_EMIT5_TX] = instr_io_export,
+       [INSTR_HDR_EMIT6_TX] = instr_io_export,
+       [INSTR_HDR_EMIT7_TX] = instr_io_export,
+       [INSTR_HDR_EMIT8_TX] = instr_io_export,
+
+       [INSTR_HDR_VALIDATE] = instr_hdr_validate_export,
+       [INSTR_HDR_INVALIDATE] = instr_hdr_validate_export,
+
+       [INSTR_MOV] = instr_mov_export,
+       [INSTR_MOV_MH] = instr_mov_export,
+       [INSTR_MOV_HM] = instr_mov_export,
+       [INSTR_MOV_HH] = instr_mov_export,
+       [INSTR_MOV_I] = instr_mov_export,
+
+       [INSTR_DMA_HT]  = instr_dma_ht_export,
+       [INSTR_DMA_HT2] = instr_dma_ht_export,
+       [INSTR_DMA_HT3] = instr_dma_ht_export,
+       [INSTR_DMA_HT4] = instr_dma_ht_export,
+       [INSTR_DMA_HT5] = instr_dma_ht_export,
+       [INSTR_DMA_HT6] = instr_dma_ht_export,
+       [INSTR_DMA_HT7] = instr_dma_ht_export,
+       [INSTR_DMA_HT8] = instr_dma_ht_export,
+
+       [INSTR_ALU_ADD] = instr_alu_export,
+       [INSTR_ALU_ADD_MH] = instr_alu_export,
+       [INSTR_ALU_ADD_HM] = instr_alu_export,
+       [INSTR_ALU_ADD_HH] = instr_alu_export,
+       [INSTR_ALU_ADD_MI] = instr_alu_export,
+       [INSTR_ALU_ADD_HI] = instr_alu_export,
+
+       [INSTR_ALU_SUB] = instr_alu_export,
+       [INSTR_ALU_SUB_MH] = instr_alu_export,
+       [INSTR_ALU_SUB_HM] = instr_alu_export,
+       [INSTR_ALU_SUB_HH] = instr_alu_export,
+       [INSTR_ALU_SUB_MI] = instr_alu_export,
+       [INSTR_ALU_SUB_HI] = instr_alu_export,
+
+       [INSTR_ALU_CKADD_FIELD] = instr_alu_export,
+       [INSTR_ALU_CKADD_STRUCT] = instr_alu_export,
+       [INSTR_ALU_CKADD_STRUCT20] = instr_alu_export,
+       [INSTR_ALU_CKSUB_FIELD] = instr_alu_export,
+
+       [INSTR_ALU_AND] = instr_alu_export,
+       [INSTR_ALU_AND_MH] = instr_alu_export,
+       [INSTR_ALU_AND_HM] = instr_alu_export,
+       [INSTR_ALU_AND_HH] = instr_alu_export,
+       [INSTR_ALU_AND_I] = instr_alu_export,
+
+       [INSTR_ALU_OR] = instr_alu_export,
+       [INSTR_ALU_OR_MH] = instr_alu_export,
+       [INSTR_ALU_OR_HM] = instr_alu_export,
+       [INSTR_ALU_OR_HH] = instr_alu_export,
+       [INSTR_ALU_OR_I] = instr_alu_export,
+
+       [INSTR_ALU_XOR] = instr_alu_export,
+       [INSTR_ALU_XOR_MH] = instr_alu_export,
+       [INSTR_ALU_XOR_HM] = instr_alu_export,
+       [INSTR_ALU_XOR_HH] = instr_alu_export,
+       [INSTR_ALU_XOR_I] = instr_alu_export,
+
+       [INSTR_ALU_SHL] = instr_alu_export,
+       [INSTR_ALU_SHL_MH] = instr_alu_export,
+       [INSTR_ALU_SHL_HM] = instr_alu_export,
+       [INSTR_ALU_SHL_HH] = instr_alu_export,
+       [INSTR_ALU_SHL_MI] = instr_alu_export,
+       [INSTR_ALU_SHL_HI] = instr_alu_export,
+
+       [INSTR_ALU_SHR] = instr_alu_export,
+       [INSTR_ALU_SHR_MH] = instr_alu_export,
+       [INSTR_ALU_SHR_HM] = instr_alu_export,
+       [INSTR_ALU_SHR_HH] = instr_alu_export,
+       [INSTR_ALU_SHR_MI] = instr_alu_export,
+       [INSTR_ALU_SHR_HI] = instr_alu_export,
+
+       [INSTR_REGPREFETCH_RH] = instr_reg_export,
+       [INSTR_REGPREFETCH_RM] = instr_reg_export,
+       [INSTR_REGPREFETCH_RI] = instr_reg_export,
+
+       [INSTR_REGRD_HRH] = instr_reg_export,
+       [INSTR_REGRD_HRM] = instr_reg_export,
+       [INSTR_REGRD_MRH] = instr_reg_export,
+       [INSTR_REGRD_MRM] = instr_reg_export,
+       [INSTR_REGRD_HRI] = instr_reg_export,
+       [INSTR_REGRD_MRI] = instr_reg_export,
+
+       [INSTR_REGWR_RHH] = instr_reg_export,
+       [INSTR_REGWR_RHM] = instr_reg_export,
+       [INSTR_REGWR_RMH] = instr_reg_export,
+       [INSTR_REGWR_RMM] = instr_reg_export,
+       [INSTR_REGWR_RHI] = instr_reg_export,
+       [INSTR_REGWR_RMI] = instr_reg_export,
+       [INSTR_REGWR_RIH] = instr_reg_export,
+       [INSTR_REGWR_RIM] = instr_reg_export,
+       [INSTR_REGWR_RII] = instr_reg_export,
+
+       [INSTR_REGADD_RHH] = instr_reg_export,
+       [INSTR_REGADD_RHM] = instr_reg_export,
+       [INSTR_REGADD_RMH] = instr_reg_export,
+       [INSTR_REGADD_RMM] = instr_reg_export,
+       [INSTR_REGADD_RHI] = instr_reg_export,
+       [INSTR_REGADD_RMI] = instr_reg_export,
+       [INSTR_REGADD_RIH] = instr_reg_export,
+       [INSTR_REGADD_RIM] = instr_reg_export,
+       [INSTR_REGADD_RII] = instr_reg_export,
+
+       [INSTR_METPREFETCH_H] = instr_meter_export,
+       [INSTR_METPREFETCH_M] = instr_meter_export,
+       [INSTR_METPREFETCH_I] = instr_meter_export,
+
+       [INSTR_METER_HHM] = instr_meter_export,
+       [INSTR_METER_HHI] = instr_meter_export,
+       [INSTR_METER_HMM] = instr_meter_export,
+       [INSTR_METER_HMI] = instr_meter_export,
+       [INSTR_METER_MHM] = instr_meter_export,
+       [INSTR_METER_MHI] = instr_meter_export,
+       [INSTR_METER_MMM] = instr_meter_export,
+       [INSTR_METER_MMI] = instr_meter_export,
+       [INSTR_METER_IHM] = instr_meter_export,
+       [INSTR_METER_IHI] = instr_meter_export,
+       [INSTR_METER_IMM] = instr_meter_export,
+       [INSTR_METER_IMI] = instr_meter_export,
+
+       [INSTR_TABLE] = instr_table_export,
+       [INSTR_TABLE_AF] = instr_table_export,
+       [INSTR_SELECTOR] = instr_table_export,
+       [INSTR_LEARNER] = instr_table_export,
+       [INSTR_LEARNER_AF] = instr_table_export,
+
+       [INSTR_LEARNER_LEARN] = instr_learn_export,
+       [INSTR_LEARNER_FORGET] = instr_forget_export,
+
+       [INSTR_EXTERN_OBJ] = instr_extern_export,
+       [INSTR_EXTERN_FUNC] = instr_extern_export,
+
+       [INSTR_JMP] = instr_jmp_export,
+       [INSTR_JMP_VALID] = instr_jmp_export,
+       [INSTR_JMP_INVALID] = instr_jmp_export,
+       [INSTR_JMP_HIT] = instr_jmp_export,
+       [INSTR_JMP_MISS] = instr_jmp_export,
+       [INSTR_JMP_ACTION_HIT] = instr_jmp_export,
+       [INSTR_JMP_ACTION_MISS] = instr_jmp_export,
+
+       [INSTR_JMP_EQ] = instr_jmp_export,
+       [INSTR_JMP_EQ_MH] = instr_jmp_export,
+       [INSTR_JMP_EQ_HM] = instr_jmp_export,
+       [INSTR_JMP_EQ_HH] = instr_jmp_export,
+       [INSTR_JMP_EQ_I] = instr_jmp_export,
+
+       [INSTR_JMP_NEQ] = instr_jmp_export,
+       [INSTR_JMP_NEQ_MH] = instr_jmp_export,
+       [INSTR_JMP_NEQ_HM] = instr_jmp_export,
+       [INSTR_JMP_NEQ_HH] = instr_jmp_export,
+       [INSTR_JMP_NEQ_I] = instr_jmp_export,
+
+       [INSTR_JMP_LT] = instr_jmp_export,
+       [INSTR_JMP_LT_MH] = instr_jmp_export,
+       [INSTR_JMP_LT_HM] = instr_jmp_export,
+       [INSTR_JMP_LT_HH] = instr_jmp_export,
+       [INSTR_JMP_LT_MI] = instr_jmp_export,
+       [INSTR_JMP_LT_HI] = instr_jmp_export,
+
+       [INSTR_JMP_GT] = instr_jmp_export,
+       [INSTR_JMP_GT_MH] = instr_jmp_export,
+       [INSTR_JMP_GT_HM] = instr_jmp_export,
+       [INSTR_JMP_GT_HH] = instr_jmp_export,
+       [INSTR_JMP_GT_MI] = instr_jmp_export,
+       [INSTR_JMP_GT_HI] = instr_jmp_export,
+
+       [INSTR_RETURN] = instr_return_export,
+};
+
+static void
+action_data_codegen(struct action *a, FILE *f)
+{
+       uint32_t i;
+
+       fprintf(f,
+               "static const struct instruction action_%s_instructions[] = {\n",
+               a->name);
+
+       for (i = 0; i < a->n_instructions; i++) {
+               struct instruction *instr = &a->instructions[i];
+               instruction_export_t func = export_table[instr->type];
+
+               func(instr, f);
+       }
+
+       fprintf(f, "};\n");
+}
+
+static const char *
+instr_type_to_func(struct instruction *instr)
+{
+       switch (instr->type) {
+       case INSTR_RX: return NULL;
+
+       case INSTR_TX: return "__instr_tx_exec";
+       case INSTR_TX_I: return "__instr_tx_i_exec";
+       case INSTR_DROP: return "__instr_drop_exec";
+       case INSTR_MIRROR: return "__instr_mirror_exec";
+
+       case INSTR_HDR_EXTRACT: return "__instr_hdr_extract_exec";
+       case INSTR_HDR_EXTRACT2: return "__instr_hdr_extract2_exec";
+       case INSTR_HDR_EXTRACT3: return "__instr_hdr_extract3_exec";
+       case INSTR_HDR_EXTRACT4: return "__instr_hdr_extract4_exec";
+       case INSTR_HDR_EXTRACT5: return "__instr_hdr_extract5_exec";
+       case INSTR_HDR_EXTRACT6: return "__instr_hdr_extract6_exec";
+       case INSTR_HDR_EXTRACT7: return "__instr_hdr_extract7_exec";
+       case INSTR_HDR_EXTRACT8: return "__instr_hdr_extract8_exec";
+
+       case INSTR_HDR_EXTRACT_M: return "__instr_hdr_extract_m_exec";
+
+       case INSTR_HDR_LOOKAHEAD: return "__instr_hdr_lookahead_exec";
+
+       case INSTR_HDR_EMIT: return "__instr_hdr_emit_exec";
+       case INSTR_HDR_EMIT_TX: return "__instr_hdr_emit_tx_exec";
+       case INSTR_HDR_EMIT2_TX: return "__instr_hdr_emit2_tx_exec";
+       case INSTR_HDR_EMIT3_TX: return "__instr_hdr_emit3_tx_exec";
+       case INSTR_HDR_EMIT4_TX: return "__instr_hdr_emit4_tx_exec";
+       case INSTR_HDR_EMIT5_TX: return "__instr_hdr_emit5_tx_exec";
+       case INSTR_HDR_EMIT6_TX: return "__instr_hdr_emit6_tx_exec";
+       case INSTR_HDR_EMIT7_TX: return "__instr_hdr_emit7_tx_exec";
+       case INSTR_HDR_EMIT8_TX: return "__instr_hdr_emit8_tx_exec";
+
+       case INSTR_HDR_VALIDATE: return "__instr_hdr_validate_exec";
+       case INSTR_HDR_INVALIDATE: return "__instr_hdr_invalidate_exec";
+
+       case INSTR_MOV: return "__instr_mov_exec";
+       case INSTR_MOV_MH: return "__instr_mov_mh_exec";
+       case INSTR_MOV_HM: return "__instr_mov_hm_exec";
+       case INSTR_MOV_HH: return "__instr_mov_hh_exec";
+       case INSTR_MOV_I: return "__instr_mov_i_exec";
+
+       case INSTR_DMA_HT: return "__instr_dma_ht_exec";
+       case INSTR_DMA_HT2: return "__instr_dma_ht2_exec";
+       case INSTR_DMA_HT3: return "__instr_dma_ht3_exec";
+       case INSTR_DMA_HT4: return "__instr_dma_ht4_exec";
+       case INSTR_DMA_HT5: return "__instr_dma_ht5_exec";
+       case INSTR_DMA_HT6: return "__instr_dma_ht6_exec";
+       case INSTR_DMA_HT7: return "__instr_dma_ht7_exec";
+       case INSTR_DMA_HT8: return "__instr_dma_ht8_exec";
+
+       case INSTR_ALU_ADD: return "__instr_alu_add_exec";
+       case INSTR_ALU_ADD_MH: return "__instr_alu_add_mh_exec";
+       case INSTR_ALU_ADD_HM: return "__instr_alu_add_hm_exec";
+       case INSTR_ALU_ADD_HH: return "__instr_alu_add_hh_exec";
+       case INSTR_ALU_ADD_MI: return "__instr_alu_add_mi_exec";
+       case INSTR_ALU_ADD_HI: return "__instr_alu_add_hi_exec";
+
+       case INSTR_ALU_SUB: return "__instr_alu_sub_exec";
+       case INSTR_ALU_SUB_MH: return "__instr_alu_sub_mh_exec";
+       case INSTR_ALU_SUB_HM: return "__instr_alu_sub_hm_exec";
+       case INSTR_ALU_SUB_HH: return "__instr_alu_sub_hh_exec";
+       case INSTR_ALU_SUB_MI: return "__instr_alu_sub_mi_exec";
+       case INSTR_ALU_SUB_HI: return "__instr_alu_sub_hi_exec";
+
+       case INSTR_ALU_CKADD_FIELD: return "__instr_alu_ckadd_field_exec";
+       case INSTR_ALU_CKADD_STRUCT20: return "__instr_alu_ckadd_struct20_exec";
+       case INSTR_ALU_CKADD_STRUCT: return "__instr_alu_ckadd_struct_exec";
+       case INSTR_ALU_CKSUB_FIELD: return "__instr_alu_cksub_field_exec";
+
+       case INSTR_ALU_AND: return "__instr_alu_and_exec";
+       case INSTR_ALU_AND_MH: return "__instr_alu_and_mh_exec";
+       case INSTR_ALU_AND_HM: return "__instr_alu_and_hm_exec";
+       case INSTR_ALU_AND_HH: return "__instr_alu_and_hh_exec";
+       case INSTR_ALU_AND_I: return "__instr_alu_and_i_exec";
+
+       case INSTR_ALU_OR: return "__instr_alu_or_exec";
+       case INSTR_ALU_OR_MH: return "__instr_alu_or_mh_exec";
+       case INSTR_ALU_OR_HM: return "__instr_alu_or_hm_exec";
+       case INSTR_ALU_OR_HH: return "__instr_alu_or_hh_exec";
+       case INSTR_ALU_OR_I: return "__instr_alu_or_i_exec";
+
+       case INSTR_ALU_XOR: return "__instr_alu_xor_exec";
+       case INSTR_ALU_XOR_MH: return "__instr_alu_xor_mh_exec";
+       case INSTR_ALU_XOR_HM: return "__instr_alu_xor_hm_exec";
+       case INSTR_ALU_XOR_HH: return "__instr_alu_xor_hh_exec";
+       case INSTR_ALU_XOR_I: return "__instr_alu_xor_i_exec";
+
+       case INSTR_ALU_SHL: return "__instr_alu_shl_exec";
+       case INSTR_ALU_SHL_MH: return "__instr_alu_shl_mh_exec";
+       case INSTR_ALU_SHL_HM: return "__instr_alu_shl_hm_exec";
+       case INSTR_ALU_SHL_HH: return "__instr_alu_shl_hh_exec";
+       case INSTR_ALU_SHL_MI: return "__instr_alu_shl_mi_exec";
+       case INSTR_ALU_SHL_HI: return "__instr_alu_shl_hi_exec";
+
+       case INSTR_ALU_SHR: return "__instr_alu_shr_exec";
+       case INSTR_ALU_SHR_MH: return "__instr_alu_shr_mh_exec";
+       case INSTR_ALU_SHR_HM: return "__instr_alu_shr_hm_exec";
+       case INSTR_ALU_SHR_HH: return "__instr_alu_shr_hh_exec";
+       case INSTR_ALU_SHR_MI: return "__instr_alu_shr_mi_exec";
+       case INSTR_ALU_SHR_HI: return "__instr_alu_shr_hi_exec";
+
+       case INSTR_REGPREFETCH_RH: return "__instr_regprefetch_rh_exec";
+       case INSTR_REGPREFETCH_RM: return "__instr_regprefetch_rm_exec";
+       case INSTR_REGPREFETCH_RI: return "__instr_regprefetch_ri_exec";
+
+       case INSTR_REGRD_HRH: return "__instr_regrd_hrh_exec";
+       case INSTR_REGRD_HRM: return "__instr_regrd_hrm_exec";
+       case INSTR_REGRD_HRI: return "__instr_regrd_hri_exec";
+       case INSTR_REGRD_MRH: return "__instr_regrd_mrh_exec";
+       case INSTR_REGRD_MRM: return "__instr_regrd_mrm_exec";
+       case INSTR_REGRD_MRI: return "__instr_regrd_mri_exec";
+
+       case INSTR_REGWR_RHH: return "__instr_regwr_rhh_exec";
+       case INSTR_REGWR_RHM: return "__instr_regwr_rhm_exec";
+       case INSTR_REGWR_RHI: return "__instr_regwr_rhi_exec";
+       case INSTR_REGWR_RMH: return "__instr_regwr_rmh_exec";
+       case INSTR_REGWR_RMM: return "__instr_regwr_rmm_exec";
+       case INSTR_REGWR_RMI: return "__instr_regwr_rmi_exec";
+       case INSTR_REGWR_RIH: return "__instr_regwr_rih_exec";
+       case INSTR_REGWR_RIM: return "__instr_regwr_rim_exec";
+       case INSTR_REGWR_RII: return "__instr_regwr_rii_exec";
+
+       case INSTR_REGADD_RHH: return "__instr_regadd_rhh_exec";
+       case INSTR_REGADD_RHM: return "__instr_regadd_rhm_exec";
+       case INSTR_REGADD_RHI: return "__instr_regadd_rhi_exec";
+       case INSTR_REGADD_RMH: return "__instr_regadd_rmh_exec";
+       case INSTR_REGADD_RMM: return "__instr_regadd_rmm_exec";
+       case INSTR_REGADD_RMI: return "__instr_regadd_rmi_exec";
+       case INSTR_REGADD_RIH: return "__instr_regadd_rih_exec";
+       case INSTR_REGADD_RIM: return "__instr_regadd_rim_exec";
+       case INSTR_REGADD_RII: return "__instr_regadd_rii_exec";
+
+       case INSTR_METPREFETCH_H: return "__instr_metprefetch_h_exec";
+       case INSTR_METPREFETCH_M: return "__instr_metprefetch_m_exec";
+       case INSTR_METPREFETCH_I: return "__instr_metprefetch_i_exec";
+
+       case INSTR_METER_HHM: return "__instr_meter_hhm_exec";
+       case INSTR_METER_HHI: return "__instr_meter_hhi_exec";
+       case INSTR_METER_HMM: return "__instr_meter_hmm_exec";
+       case INSTR_METER_HMI: return "__instr_meter_hmi_exec";
+       case INSTR_METER_MHM: return "__instr_meter_mhm_exec";
+       case INSTR_METER_MHI: return "__instr_meter_mhi_exec";
+       case INSTR_METER_MMM: return "__instr_meter_mmm_exec";
+       case INSTR_METER_MMI: return "__instr_meter_mmi_exec";
+       case INSTR_METER_IHM: return "__instr_meter_ihm_exec";
+       case INSTR_METER_IHI: return "__instr_meter_ihi_exec";
+       case INSTR_METER_IMM: return "__instr_meter_imm_exec";
+       case INSTR_METER_IMI: return "__instr_meter_imi_exec";
+
+       case INSTR_TABLE: return NULL;
+       case INSTR_TABLE_AF: return NULL;
+       case INSTR_SELECTOR: return NULL;
+       case INSTR_LEARNER: return NULL;
+       case INSTR_LEARNER_AF: return NULL;
+
+       case INSTR_LEARNER_LEARN: return "__instr_learn_exec";
+       case INSTR_LEARNER_FORGET: return "__instr_forget_exec";
+
+       case INSTR_EXTERN_OBJ: return NULL;
+       case INSTR_EXTERN_FUNC: return NULL;
+
+       case INSTR_JMP: return NULL;
+       case INSTR_JMP_VALID: return NULL;
+       case INSTR_JMP_INVALID: return NULL;
+       case INSTR_JMP_HIT: return NULL;
+       case INSTR_JMP_MISS: return NULL;
+       case INSTR_JMP_ACTION_HIT: return NULL;
+       case INSTR_JMP_ACTION_MISS: return NULL;
+       case INSTR_JMP_EQ: return NULL;
+       case INSTR_JMP_EQ_MH: return NULL;
+       case INSTR_JMP_EQ_HM: return NULL;
+       case INSTR_JMP_EQ_HH: return NULL;
+       case INSTR_JMP_EQ_I: return NULL;
+       case INSTR_JMP_NEQ: return NULL;
+       case INSTR_JMP_NEQ_MH: return NULL;
+       case INSTR_JMP_NEQ_HM: return NULL;
+       case INSTR_JMP_NEQ_HH: return NULL;
+       case INSTR_JMP_NEQ_I: return NULL;
+       case INSTR_JMP_LT: return NULL;
+       case INSTR_JMP_LT_MH: return NULL;
+       case INSTR_JMP_LT_HM: return NULL;
+       case INSTR_JMP_LT_HH: return NULL;
+       case INSTR_JMP_LT_MI: return NULL;
+       case INSTR_JMP_LT_HI: return NULL;
+       case INSTR_JMP_GT: return NULL;
+       case INSTR_JMP_GT_MH: return NULL;
+       case INSTR_JMP_GT_HM: return NULL;
+       case INSTR_JMP_GT_HH: return NULL;
+       case INSTR_JMP_GT_MI: return NULL;
+       case INSTR_JMP_GT_HI: return NULL;
+
+       case INSTR_RETURN: return NULL;
+
+       default: return NULL;
+       }
+}
+
+static void
+action_instr_does_tx_codegen(struct action *a,
+                       uint32_t instr_pos,
+                       struct instruction *instr,
+                       FILE *f)
+{
+       fprintf(f,
+               "%s(p, t, &action_%s_instructions[%u]);\n"
+               "\tthread_ip_reset(p, t);\n"
+               "\tinstr_rx_exec(p);\n"
+               "\treturn;\n",
+               instr_type_to_func(instr),
+               a->name,
+               instr_pos);
+}
+
+static void
+action_instr_extern_obj_codegen(struct action *a,
+                               uint32_t instr_pos,
+                               FILE *f)
+{
+       fprintf(f,
+               "while (!__instr_extern_obj_exec(p, t, &action_%s_instructions[%u]));\n",
+               a->name,
+               instr_pos);
+}
+
+static void
+action_instr_extern_func_codegen(struct action *a,
+                                uint32_t instr_pos,
+                                FILE *f)
+{
+       fprintf(f,
+               "while (!__instr_extern_func_exec(p, t, &action_%s_instructions[%u]));\n",
+               a->name,
+               instr_pos);
+}
+
+static void
+action_instr_jmp_codegen(struct action *a,
+                        uint32_t instr_pos,
+                        struct instruction *instr,
+                        struct instruction_data *data,
+                        FILE *f)
+{
+       switch (instr->type) {
+       case INSTR_JMP:
+               fprintf(f,
+                       "goto %s;\n",
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_VALID:
+               fprintf(f,
+                       "if (HEADER_VALID(t, action_%s_instructions[%u].jmp.header_id))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_INVALID:
+               fprintf(f,
+                       "if (!HEADER_VALID(t, action_%s_instructions[%u].jmp.header_id))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_HIT:
+               fprintf(f,
+                       "if (t->hit)\n"
+                       "\t\tgoto %s;\n",
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_MISS:
+               fprintf(f,
+                       "if (!t->hit)\n"
+                       "\t\tgoto %s;\n",
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_ACTION_HIT:
+               fprintf(f,
+                       "if (t->action_id == action_%s_instructions[%u].jmp.action_id)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_ACTION_MISS:
+               fprintf(f,
+                       "if (t->action_id != action_%s_instructions[%u].jmp.action_id)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_EQ:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_EQ_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_EQ_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) == "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_EQ_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) == "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_EQ_I:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_NEQ:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_NEQ_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_NEQ_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) != "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_NEQ_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) != "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_NEQ_I:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT_MI:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_LT_HI:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT_MI:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       case INSTR_JMP_GT_HI:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
+                       "action_%s_instructions[%u].jmp.b_val)\n"
+                       "\t\tgoto %s;\n",
+                       a->name,
+                       instr_pos,
+                       a->name,
+                       instr_pos,
+                       data->jmp_label);
+               return;
+
+       default:
+               return;
+       }
+}
+
+static void
+action_instr_return_codegen(FILE *f)
+{
+       fprintf(f,
+               "return;\n");
+}
+
+static void
+action_instr_codegen(struct action *a, FILE *f)
+{
+       uint32_t i;
+
+       fprintf(f,
+               "void\n"
+               "action_%s_run(struct rte_swx_pipeline *p)\n"
+               "{\n"
+               "\tstruct thread *t = &p->threads[p->thread_id];\n"
+               "\n",
+               a->name);
+
+       for (i = 0; i < a->n_instructions; i++) {
+               struct instruction *instr = &a->instructions[i];
+               struct instruction_data *data = &a->instruction_data[i];
+
+               /* Label, if present. */
+               if (data->label[0])
+                       fprintf(f, "\n%s : ", data->label);
+               else
+                       fprintf(f, "\n\t");
+
+               /* TX instruction type. */
+               if (instruction_does_tx(instr)) {
+                       action_instr_does_tx_codegen(a, i, instr, f);
+                       continue;
+               }
+
+               /* Extern object/function instruction type. */
+               if (instr->type == INSTR_EXTERN_OBJ) {
+                       action_instr_extern_obj_codegen(a, i, f);
+                       continue;
+               }
+
+               if (instr->type == INSTR_EXTERN_FUNC) {
+                       action_instr_extern_func_codegen(a, i, f);
+                       continue;
+               }
+
+               /* Jump instruction type. */
+               if (instruction_is_jmp(instr)) {
+                       action_instr_jmp_codegen(a, i, instr, data, f);
+                       continue;
+               }
+
+               /* Return instruction type. */
+               if (instr->type == INSTR_RETURN) {
+                       action_instr_return_codegen(f);
+                       continue;
+               }
+
+               /* Any other instruction type. */
+               fprintf(f,
+                       "%s(p, t, &action_%s_instructions[%u]);\n",
+                       instr_type_to_func(instr),
+                       a->name,
+                       i);
+       }
+
+       fprintf(f, "}\n\n");
+}
+
+struct instruction_group {
+       TAILQ_ENTRY(instruction_group) node;
+
+       uint32_t group_id;
+
+       uint32_t first_instr_id;
+
+       uint32_t last_instr_id;
+
+       instr_exec_t func;
+};
+
+TAILQ_HEAD(instruction_group_list, instruction_group);
+
+static struct instruction_group *
+instruction_group_list_group_find(struct instruction_group_list *igl, uint32_t instruction_id)
+{
+       struct instruction_group *g;
+
+       TAILQ_FOREACH(g, igl, node)
+               if ((g->first_instr_id <= instruction_id) && (instruction_id <= g->last_instr_id))
+                       return g;
+
+       return NULL;
+}
+
+static void
+instruction_group_list_free(struct instruction_group_list *igl)
+{
+       if (!igl)
+               return;
+
+       for ( ; ; ) {
+               struct instruction_group *g;
+
+               g = TAILQ_FIRST(igl);
+               if (!g)
+                       break;
+
+               TAILQ_REMOVE(igl, g, node);
+               free(g);
+       }
+
+       free(igl);
+}
+
+static struct instruction_group_list *
+instruction_group_list_create(struct rte_swx_pipeline *p)
+{
+       struct instruction_group_list *igl = NULL;
+       struct instruction_group *g = NULL;
+       uint32_t n_groups = 0, i;
+
+       if (!p || !p->instructions || !p->instruction_data || !p->n_instructions)
+               goto error;
+
+       /* List init. */
+       igl = calloc(1, sizeof(struct instruction_group_list));
+       if (!igl)
+               goto error;
+
+       TAILQ_INIT(igl);
+
+       /* Allocate the first group. */
+       g = calloc(1, sizeof(struct instruction_group));
+       if (!g)
+               goto error;
+
+       /* Iteration 1: Separate the instructions into groups based on the thread yield
+        * instructions. Do not worry about the jump instructions at this point.
+        */
+       for (i = 0; i < p->n_instructions; i++) {
+               struct instruction *instr = &p->instructions[i];
+
+               /* Check for thread yield instructions. */
+               if (!instruction_does_thread_yield(instr))
+                       continue;
+
+               /* If the current group contains at least one instruction, then finalize it (with
+                * the previous instruction), add it to the list and allocate a new group (that
+                * starts with the current instruction).
+                */
+               if (i - g->first_instr_id) {
+                       /* Finalize the group. */
+                       g->last_instr_id = i - 1;
+
+                       /* Add the group to the list. Advance the number of groups. */
+                       TAILQ_INSERT_TAIL(igl, g, node);
+                       n_groups++;
+
+                       /* Allocate a new group. */
+                       g = calloc(1, sizeof(struct instruction_group));
+                       if (!g)
+                               goto error;
+
+                       /* Initialize the new group. */
+                       g->group_id = n_groups;
+                       g->first_instr_id = i;
+               }
+
+               /* Finalize the current group (with the current instruction, therefore this group
+                * contains just the current thread yield instruction), add it to the list and
+                * allocate a new group (that starts with the next instruction).
+                */
+
+               /* Finalize the group. */
+               g->last_instr_id = i;
+
+               /* Add the group to the list. Advance the number of groups. */
+               TAILQ_INSERT_TAIL(igl, g, node);
+               n_groups++;
+
+               /* Allocate a new group. */
+               g = calloc(1, sizeof(struct instruction_group));
+               if (!g)
+                       goto error;
+
+               /* Initialize the new group. */
+               g->group_id = n_groups;
+               g->first_instr_id = i + 1;
+       }
+
+       /* Handle the last group. */
+       if (i - g->first_instr_id) {
+               /* Finalize the group. */
+               g->last_instr_id = i - 1;
+
+               /* Add the group to the list. Advance the number of groups. */
+               TAILQ_INSERT_TAIL(igl, g, node);
+               n_groups++;
+       } else
+               free(g);
+
+       g = NULL;
+
+       /* Iteration 2: Handle jumps. If the current group contains an instruction which represents
+        * the destination of a jump instruction located in a different group ("far jump"), then the
+        * current group has to be split, so that the instruction representing the far jump
+        * destination is at the start of its group.
+        */
+       for ( ; ; ) {
+               int is_modified = 0;
+
+               for (i = 0; i < p->n_instructions; i++) {
+                       struct instruction_data *data = &p->instruction_data[i];
+                       struct instruction_group *g;
+                       uint32_t j;
+
+                       /* Continue when the current instruction is not a jump destination. */
+                       if (!data->n_users)
+                               continue;
+
+                       g = instruction_group_list_group_find(igl, i);
+                       if (!g)
+                               goto error;
+
+                       /* Find out all the jump instructions with this destination. */
+                       for (j = 0; j < p->n_instructions; j++) {
+                               struct instruction *jmp_instr = &p->instructions[j];
+                               struct instruction_data *jmp_data = &p->instruction_data[j];
+                               struct instruction_group *jmp_g, *new_g;
+
+                               /* Continue when not a jump instruction. Even when jump instruction,
+                                * continue when the jump destination is not this instruction.
+                                */
+                               if (!instruction_is_jmp(jmp_instr) ||
+                                   strcmp(jmp_data->jmp_label, data->label))
+                                       continue;
+
+                               jmp_g = instruction_group_list_group_find(igl, j);
+                               if (!jmp_g)
+                                       goto error;
+
+                               /* Continue when both the jump instruction and the jump destination
+                                * instruction are in the same group. Even when in different groups,
+                                * still continue if the jump destination instruction is already the
+                                * first instruction of its group.
+                                */
+                               if ((jmp_g->group_id == g->group_id) || (g->first_instr_id == i))
+                                       continue;
+
+                               /* Split the group of the current jump destination instruction to
+                                * make this instruction the first instruction of a new group.
+                                */
+                               new_g = calloc(1, sizeof(struct instruction_group));
+                               if (!new_g)
+                                       goto error;
+
+                               new_g->group_id = n_groups;
+                               new_g->first_instr_id = i;
+                               new_g->last_instr_id = g->last_instr_id;
+
+                               g->last_instr_id = i - 1;
+
+                               TAILQ_INSERT_AFTER(igl, g, new_g, node);
+                               n_groups++;
+                               is_modified = 1;
+
+                               /* The decision to split this group (to make the current instruction
+                                * the first instruction of a new group) is already taken and fully
+                                * implemented, so no need to search for more reasons to do it.
+                                */
+                               break;
+                       }
+               }
+
+               /* Re-evaluate everything, as at least one group got split, so some jumps that were
+                * previously considered local (i.e. the jump destination is in the same group as
+                * the jump instruction) can now be "far jumps" (i.e. the jump destination is in a
+                * different group than the jump instruction). Wost case scenario: each instruction
+                * that is a jump destination ends up as the first instruction of its group.
+                */
+               if (!is_modified)
+                       break;
+       }
+
+       /* Re-assign the group IDs to be in incremental order. */
+       i = 0;
+       TAILQ_FOREACH(g, igl, node) {
+               g->group_id = i;
+
+               i++;
+       }
+
+       return igl;
+
+error:
+       instruction_group_list_free(igl);
+
+       free(g);
+
+       return NULL;
+}
+
+static void
+pipeline_instr_does_tx_codegen(struct rte_swx_pipeline *p __rte_unused,
+                              uint32_t instr_pos,
+                              struct instruction *instr,
+                              FILE *f)
+{
+       fprintf(f,
+               "%s(p, t, &pipeline_instructions[%u]);\n"
+               "\tthread_ip_reset(p, t);\n"
+               "\tinstr_rx_exec(p);\n"
+               "\treturn;\n",
+               instr_type_to_func(instr),
+               instr_pos);
+}
+
+static int
+pipeline_instr_jmp_codegen(struct rte_swx_pipeline *p,
+                          struct instruction_group_list *igl,
+                          uint32_t jmp_instr_id,
+                          struct instruction *jmp_instr,
+                          struct instruction_data *jmp_data,
+                          FILE *f)
+{
+       struct instruction_group *jmp_g, *g;
+       struct instruction_data *data;
+       uint32_t instr_id;
+
+       switch (jmp_instr->type) {
+       case INSTR_JMP:
+               break;
+
+       case INSTR_JMP_VALID:
+               fprintf(f,
+                       "if (HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))",
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_INVALID:
+               fprintf(f,
+                       "if (!HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))",
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_HIT:
+               fprintf(f,
+                       "if (t->hit)\n");
+               break;
+
+       case INSTR_JMP_MISS:
+               fprintf(f,
+                       "if (!t->hit)\n");
+               break;
+
+       case INSTR_JMP_ACTION_HIT:
+               fprintf(f,
+                       "if (t->action_id == pipeline_instructions[%u].jmp.action_id)",
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_ACTION_MISS:
+               fprintf(f,
+                       "if (t->action_id != pipeline_instructions[%u].jmp.action_id)",
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_EQ:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_EQ_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_EQ_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_EQ_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_EQ_I:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_NEQ:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_NEQ_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_NEQ_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_NEQ_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_NEQ_I:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT_MI:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_LT_HI:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT_MH:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT_HM:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT_HH:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT_MI:
+               fprintf(f,
+                       "if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       case INSTR_JMP_GT_HI:
+               fprintf(f,
+                       "if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
+                       "pipeline_instructions[%u].jmp.b_val)",
+                       jmp_instr_id,
+                       jmp_instr_id);
+               break;
+
+       default:
+               break;
+       }
+
+       /* Find the instruction group of the jump instruction. */
+       jmp_g = instruction_group_list_group_find(igl, jmp_instr_id);
+       if (!jmp_g)
+               return -EINVAL;
+
+       /* Find the instruction group of the jump destination instruction. */
+       data = label_find(p->instruction_data, p->n_instructions, jmp_data->jmp_label);
+       if (!data)
+               return -EINVAL;
+
+       instr_id = data - p->instruction_data;
+
+       g = instruction_group_list_group_find(igl, instr_id);
+       if (!g)
+               return -EINVAL;
+
+       /* Code generation for "near" jump (same instruction group) or "far" jump (different
+        * instruction group).
+        */
+       if (g->group_id == jmp_g->group_id)
+               fprintf(f,
+                       "\n\t\tgoto %s;\n",
+                       jmp_data->jmp_label);
+       else
+               fprintf(f,
+                       " {\n"
+                       "\t\tthread_ip_set(t, &p->instructions[%u]);\n"
+                       "\t\treturn;\n"
+                       "\t}\n\n",
+                       g->group_id);
+
+       return 0;
+}
+
+static void
+instruction_group_list_codegen(struct instruction_group_list *igl,
+                              struct rte_swx_pipeline *p,
+                              FILE *f)
+{
+       struct instruction_group *g;
+       uint32_t i;
+       int is_required = 0;
+
+       /* Check if code generation is required. */
+       TAILQ_FOREACH(g, igl, node)
+               if (g->first_instr_id < g->last_instr_id)
+                       is_required = 1;
+
+       if (!is_required)
+               return;
+
+       /* Generate the code for the pipeline instruction array. */
+       fprintf(f,
+               "static const struct instruction pipeline_instructions[] = {\n");
+
+       for (i = 0; i < p->n_instructions; i++) {
+               struct instruction *instr = &p->instructions[i];
+               instruction_export_t func = export_table[instr->type];
+
+               func(instr, f);
+       }
+
+       fprintf(f, "};\n\n");
+
+       /* Generate the code for the pipeline functions: one function for each instruction group
+        * that contains more than one instruction.
+        */
+       TAILQ_FOREACH(g, igl, node) {
+               struct instruction *last_instr;
+               uint32_t j;
+
+               /* Skip if group contains a single instruction. */
+               if (g->last_instr_id == g->first_instr_id)
+                       continue;
+
+               /* Generate new pipeline function. */
+               fprintf(f,
+                       "void\n"
+                       "pipeline_func_%u(struct rte_swx_pipeline *p)\n"
+                       "{\n"
+                       "\tstruct thread *t = &p->threads[p->thread_id];\n"
+                       "\n",
+                       g->group_id);
+
+               /* Generate the code for each pipeline instruction. */
+               for (j = g->first_instr_id; j <= g->last_instr_id; j++) {
+                       struct instruction *instr = &p->instructions[j];
+                       struct instruction_data *data = &p->instruction_data[j];
+
+                       /* Label, if present. */
+                       if (data->label[0])
+                               fprintf(f, "\n%s : ", data->label);
+                       else
+                               fprintf(f, "\n\t");
+
+                       /* TX instruction type. */
+                       if (instruction_does_tx(instr)) {
+                               pipeline_instr_does_tx_codegen(p, j, instr, f);
+                               continue;
+                       }
+
+                       /* Jump instruction type. */
+                       if (instruction_is_jmp(instr)) {
+                               pipeline_instr_jmp_codegen(p, igl, j, instr, data, f);
+                               continue;
+                       }
+
+                       /* Any other instruction type. */
+                       fprintf(f,
+                               "%s(p, t, &pipeline_instructions[%u]);\n",
+                               instr_type_to_func(instr),
+                               j);
+               }
+
+               /* Finalize the generated pipeline function. For some instructions such as TX,
+                * emit-many-and-TX and unconditional jump, the next instruction has been already
+                * decided unconditionally and the instruction pointer of the current thread set
+                * accordingly; for all the other instructions, the instruction pointer must be
+                * incremented now.
+                */
+               last_instr = &p->instructions[g->last_instr_id];
+
+               if (!instruction_does_tx(last_instr) && (last_instr->type != INSTR_JMP))
+                       fprintf(f,
+                               "thread_ip_inc(p);\n");
+
+               fprintf(f,
+                       "}\n"
+                       "\n");
+       }
+}
+
+static uint32_t
+instruction_group_list_custom_instructions_count(struct instruction_group_list *igl)
+{
+       struct instruction_group *g;
+       uint32_t n_custom_instr = 0;
+
+       /* Groups with a single instruction: no function is generated for this group, the group
+        * keeps its current instruction. Groups with more than two instructions: one function and
+        * the associated custom instruction get generated for each such group.
+        */
+       TAILQ_FOREACH(g, igl, node) {
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               n_custom_instr++;
+       }
+
+       return n_custom_instr;
+}
+
+static int
+pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
+{
+       struct action *a;
+       FILE *f = NULL;
+
+       /* Create the .c file. */
+       f = fopen("/tmp/pipeline.c", "w");
+       if (!f)
+               return -EIO;
+
+       /* Include the .h file. */
+       fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
+
+       /* Add the code for each action. */
+       TAILQ_FOREACH(a, &p->actions, node) {
+               fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
+
+               action_data_codegen(a, f);
+
+               fprintf(f, "\n");
+
+               action_instr_codegen(a, f);
+
+               fprintf(f, "\n");
+       }
+
+       /* Add the pipeline code. */
+       instruction_group_list_codegen(igl, p, f);
+
+       /* Close the .c file. */
+       fclose(f);
+
+       return 0;
+}
+
+#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
+#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
+#endif
+
+static int
+pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
+{
+       struct action *a;
+       struct instruction_group *g;
+       char *dir_in, *buffer = NULL;
+       const char *dir_out;
+       int status = 0;
+
+       /* Get the environment variables. */
+       dir_in = getenv("RTE_INSTALL_DIR");
+       if (!dir_in) {
+               status = -EINVAL;
+               goto free;
+       }
+
+       dir_out = "/tmp";
+
+       /* Memory allocation for the command buffer. */
+       buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
+       if (!buffer) {
+               status = -ENOMEM;
+               goto free;
+       }
+
+       snprintf(buffer,
+                RTE_SWX_PIPELINE_CMD_MAX_SIZE,
+                "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
+                "-I %s/lib/pipeline "
+                "-I %s/lib/eal/include "
+                "-I %s/lib/eal/x86/include "
+                "-I %s/lib/eal/include/generic "
+                "-I %s/lib/meter "
+                "-I %s/lib/port "
+                "-I %s/lib/table "
+                "-I %s/lib/pipeline "
+                "-I %s/config "
+                "-I %s/build "
+                "-I %s/lib/eal/linux/include "
+                ">%s/pipeline.log 2>&1 "
+                "&& "
+                "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
+                ">>%s/pipeline.log 2>&1",
+                dir_out,
+                dir_out,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_in,
+                dir_out,
+                dir_out,
+                dir_out,
+                dir_out);
+
+       /* Build the shared object library. */
+       status = system(buffer);
+       if (status)
+               goto free;
+
+       /* Open library. */
+       snprintf(buffer,
+                RTE_SWX_PIPELINE_CMD_MAX_SIZE,
+                "%s/libpipeline.so",
+                dir_out);
+
+       p->lib = dlopen(buffer, RTLD_LAZY);
+       if (!p->lib) {
+               status = -EIO;
+               goto free;
+       }
+
+       /* Get the action function symbols. */
+       TAILQ_FOREACH(a, &p->actions, node) {
+               snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
+
+               p->action_funcs[a->id] = dlsym(p->lib, buffer);
+               if (!p->action_funcs[a->id]) {
+                       status = -EINVAL;
+                       goto free;
+               }
+       }
+
+       /* Get the pipeline function symbols. */
+       TAILQ_FOREACH(g, igl, node) {
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
+
+               g->func = dlsym(p->lib, buffer);
+               if (!g->func) {
+                       status = -EINVAL;
+                       goto free;
+               }
+       }
+
+free:
+       if (status && p->lib) {
+               dlclose(p->lib);
+               p->lib = NULL;
+       }
+
+       free(buffer);
+
+       return status;
+}
+
+static int
+pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
+                     struct instruction_group_list *igl)
+{
+       uint32_t n_custom_instr = instruction_group_list_custom_instructions_count(igl);
+
+       /* Check that enough space is available within the pipeline instruction table to store all
+        * the custom instructions.
+        */
+       if (INSTR_CUSTOM_0 + n_custom_instr > RTE_SWX_PIPELINE_INSTRUCTION_TABLE_SIZE_MAX)
+               return -ENOSPC;
+
+       return 0;
+}
+
+static void
+pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
+{
+       struct instruction_group *g;
+       uint32_t i;
+
+       /* Pipeline table instructions. */
+       for (i = 0; i < p->n_instructions; i++) {
+               struct instruction *instr = &p->instructions[i];
+
+               if (instr->type == INSTR_TABLE)
+                       instr->type = INSTR_TABLE_AF;
+
+               if (instr->type == INSTR_LEARNER)
+                       instr->type = INSTR_LEARNER_AF;
+       }
+
+       /* Pipeline custom instructions. */
+       i = 0;
+       TAILQ_FOREACH(g, igl, node) {
+               struct instruction *instr = &p->instructions[g->first_instr_id];
+               uint32_t j;
+
+               if (g->first_instr_id == g->last_instr_id)
+                       continue;
+
+               /* Install a new custom instruction. */
+               p->instruction_table[INSTR_CUSTOM_0 + i] = g->func;
+
+               /* First instruction of the group: change its type to the new custom instruction. */
+               instr->type = INSTR_CUSTOM_0 + i;
+
+               /* All the subsequent instructions of the group: invalidate. */
+               for (j = g->first_instr_id + 1; j <= g->last_instr_id; j++) {
+                       struct instruction_data *data = &p->instruction_data[j];
+
+                       data->invalid = 1;
+               }
+
+               i++;
+       }
+
+       /* Remove the invalidated instructions. */
+       p->n_instructions = instr_compact(p->instructions, p->instruction_data, p->n_instructions);
+
+       /* Resolve the jump destination for any "standalone" jump instructions (i.e. those jump
+        * instructions that are the only instruction within their group, so they were left
+        * unmodified).
+        */
+       instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
+}
+
+static int
+pipeline_compile(struct rte_swx_pipeline *p)
+{
+       struct instruction_group_list *igl = NULL;
+       int status = 0;
+
+       igl = instruction_group_list_create(p);
+       if (!igl) {
+               status = -ENOMEM;
+               goto free;
+       }
+
+       /* Code generation. */
+       status = pipeline_codegen(p, igl);
+       if (status)
+               goto free;
+
+       /* Build and load the shared object library. */
+       status = pipeline_libload(p, igl);
+       if (status)
+               goto free;
+
+       /* Adjust instructions. */
+       status = pipeline_adjust_check(p, igl);
+       if (status)
+               goto free;
+
+       pipeline_adjust(p, igl);
+
+free:
+       instruction_group_list_free(igl);
+
+       return status;
+}