get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/98630/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 98630,
    "url": "http://patches.dpdk.org/api/patches/98630/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20210910123003.85448-22-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20210910123003.85448-22-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210910123003.85448-22-cristian.dumitrescu@intel.com",
    "date": "2021-09-10T12:30:01",
    "name": "[22/24] pipeline: generate custom instruction functions",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "244ba5829a3ffffcb29379ca0b43781b305130c3",
    "submitter": {
        "id": 19,
        "url": "http://patches.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": null,
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20210910123003.85448-22-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 18838,
            "url": "http://patches.dpdk.org/api/series/18838/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=18838",
            "date": "2021-09-10T12:29:44",
            "name": "[01/24] pipeline: move data structures to internal header file",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/18838/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/98630/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/98630/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 805FCA0547;\n\tFri, 10 Sep 2021 14:33:20 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 084D0411D3;\n\tFri, 10 Sep 2021 14:30:56 +0200 (CEST)",
            "from mga01.intel.com (mga01.intel.com [192.55.52.88])\n by mails.dpdk.org (Postfix) with ESMTP id E09F641153\n for <dev@dpdk.org>; Fri, 10 Sep 2021 14:30:27 +0200 (CEST)",
            "from orsmga001.jf.intel.com ([10.7.209.18])\n by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 10 Sep 2021 05:30:24 -0700",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.223.107])\n by orsmga001.jf.intel.com with ESMTP; 10 Sep 2021 05:30:24 -0700"
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6200,9189,10102\"; a=\"243386333\"",
            "E=Sophos;i=\"5.85,282,1624345200\"; d=\"scan'208\";a=\"243386333\"",
            "E=Sophos;i=\"5.85,282,1624345200\"; d=\"scan'208\";a=\"514279843\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Date": "Fri, 10 Sep 2021 13:30:01 +0100",
        "Message-Id": "<20210910123003.85448-22-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20210910123003.85448-1-cristian.dumitrescu@intel.com>",
        "References": "<20210910123003.85448-1-cristian.dumitrescu@intel.com>",
        "Subject": "[dpdk-dev] [PATCH 22/24] pipeline: generate custom instruction\n functions",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Generate a C function for each custom instruction, which essentially\nconsolidate multiple regular instructions into a single function call.\nThe pipeline program is split into groups of instructions, and a\ncustom instruction is generated for each group that has more than one\ninstruction. Special care is taken the instructions that can do thread\nyield (RX, extern) and for those that can change the instruction\npointer (TX, near/far jump).\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\n---\n lib/pipeline/rte_swx_pipeline.c | 649 +++++++++++++++++++++++++++++++-\n 1 file changed, 643 insertions(+), 6 deletions(-)",
    "diff": "diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c\nindex 019dbafbf3..0a4ac06467 100644\n--- a/lib/pipeline/rte_swx_pipeline.c\n+++ b/lib/pipeline/rte_swx_pipeline.c\n@@ -1436,6 +1436,24 @@ instruction_is_jmp(struct instruction *instr)\n \t}\n }\n \n+static int\n+instruction_does_thread_yield(struct instruction *instr)\n+{\n+\tswitch (instr->type) {\n+\tcase INSTR_RX:\n+\tcase INSTR_TABLE:\n+\tcase INSTR_TABLE_AF:\n+\tcase INSTR_SELECTOR:\n+\tcase INSTR_LEARNER:\n+\tcase INSTR_LEARNER_AF:\n+\tcase INSTR_EXTERN_OBJ:\n+\tcase INSTR_EXTERN_FUNC:\n+\t\treturn 1;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n static struct field *\n action_field_parse(struct action *action, const char *name);\n \n@@ -11515,15 +11533,621 @@ action_instr_codegen(struct action *a, FILE *f)\n \tfprintf(f, \"}\\n\\n\");\n }\n \n+struct instruction_group {\n+\tTAILQ_ENTRY(instruction_group) node;\n+\n+\tuint32_t group_id;\n+\n+\tuint32_t first_instr_id;\n+\n+\tuint32_t last_instr_id;\n+\n+\tinstr_exec_t func;\n+};\n+\n+TAILQ_HEAD(instruction_group_list, instruction_group);\n+\n+static struct instruction_group *\n+instruction_group_list_group_find(struct instruction_group_list *igl, uint32_t instruction_id)\n+{\n+\tstruct instruction_group *g;\n+\n+\tTAILQ_FOREACH(g, igl, node)\n+\t\tif ((g->first_instr_id <= instruction_id) && (instruction_id <= g->last_instr_id))\n+\t\t\treturn g;\n+\n+\treturn NULL;\n+}\n+\n+static void\n+instruction_group_list_free(struct instruction_group_list *igl)\n+{\n+\tif (!igl)\n+\t\treturn;\n+\n+\tfor ( ; ; ) {\n+\t\tstruct instruction_group *g;\n+\n+\t\tg = TAILQ_FIRST(igl);\n+\t\tif (!g)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(igl, g, node);\n+\t\tfree(g);\n+\t}\n+\n+\tfree(igl);\n+}\n+\n+static struct instruction_group_list *\n+instruction_group_list_create(struct rte_swx_pipeline *p)\n+{\n+\tstruct instruction_group_list *igl = NULL;\n+\tstruct instruction_group *g = NULL;\n+\tuint32_t n_groups = 0, i;\n+\n+\tif (!p || !p->instructions || !p->instruction_data || !p->n_instructions)\n+\t\tgoto error;\n+\n+\t/* List init. */\n+\tigl = calloc(1, sizeof(struct instruction_group_list));\n+\tif (!igl)\n+\t\tgoto error;\n+\n+\tTAILQ_INIT(igl);\n+\n+\t/* Allocate the first group. */\n+\tg = calloc(1, sizeof(struct instruction_group));\n+\tif (!g)\n+\t\tgoto error;\n+\n+\t/* Iteration 1: Separate the instructions into groups based on the thread yield\n+\t * instructions. Do not worry about the jump instructions at this point.\n+\t */\n+\tfor (i = 0; i < p->n_instructions; i++) {\n+\t\tstruct instruction *instr = &p->instructions[i];\n+\n+\t\t/* Check for thread yeld instructions. */\n+\t\tif (!instruction_does_thread_yield(instr))\n+\t\t\tcontinue;\n+\n+\t\t/* If the current group contains at least one instruction, then finalize it (with\n+\t\t * the previous instruction), add it to the list and allocate a new group (that\n+\t\t * starts with the current instruction).\n+\t\t */\n+\t\tif (i - g->first_instr_id) {\n+\t\t\t/* Finalize the group. */\n+\t\t\tg->last_instr_id = i - 1;\n+\n+\t\t\t/* Add the group to the list. Advance the number of groups. */\n+\t\t\tTAILQ_INSERT_TAIL(igl, g, node);\n+\t\t\tn_groups++;\n+\n+\t\t\t/* Allocate a new group. */\n+\t\t\tg = calloc(1, sizeof(struct instruction_group));\n+\t\t\tif (!g)\n+\t\t\t\tgoto error;\n+\n+\t\t\t/* Initialize the new group. */\n+\t\t\tg->group_id = n_groups;\n+\t\t\tg->first_instr_id = i;\n+\t\t}\n+\n+\t\t/* Finalize the current group (with the current instruction, therefore this group\n+\t\t * contains just the current thread yield instruction), add it to the list and\n+\t\t * allocate a new group (that starts with the next instruction).\n+\t\t */\n+\n+\t\t/* Finalize the group. */\n+\t\tg->last_instr_id = i;\n+\n+\t\t/* Add the group to the list. Advance the number of groups. */\n+\t\tTAILQ_INSERT_TAIL(igl, g, node);\n+\t\tn_groups++;\n+\n+\t\t/* Allocate a new group. */\n+\t\tg = calloc(1, sizeof(struct instruction_group));\n+\t\tif (!g)\n+\t\t\tgoto error;\n+\n+\t\t/* Initialize the new group. */\n+\t\tg->group_id = n_groups;\n+\t\tg->first_instr_id = i + 1;\n+\t}\n+\n+\t/* Handle the last group. */\n+\tif (i - g->first_instr_id) {\n+\t\t/* Finalize the group. */\n+\t\tg->last_instr_id = i - 1;\n+\n+\t\t/* Add the group to the list. Advance the number of groups. */\n+\t\tTAILQ_INSERT_TAIL(igl, g, node);\n+\t\tn_groups++;\n+\t} else\n+\t\tfree(g);\n+\n+\tg = NULL;\n+\n+\t/* Iteration 2: Handle jumps. If the current group contains an instruction which represents\n+\t * the destination of a jump instruction located in a different group (\"far jump\"), then the\n+\t * current group has to be split, so that the instruction representing the far jump\n+\t * destination is at the start of its group.\n+\t */\n+\tfor ( ; ; ) {\n+\t\tint is_modified = 0;\n+\n+\t\tfor (i = 0; i < p->n_instructions; i++) {\n+\t\t\tstruct instruction_data *data = &p->instruction_data[i];\n+\t\t\tstruct instruction_group *g;\n+\t\t\tuint32_t j;\n+\n+\t\t\t/* Continue when the current instruction is not a jump destination. */\n+\t\t\tif (!data->n_users)\n+\t\t\t\tcontinue;\n+\n+\t\t\tg = instruction_group_list_group_find(igl, i);\n+\t\t\tif (!g)\n+\t\t\t\tgoto error;\n+\n+\t\t\t/* Find out all the jump instructions with this destination. */\n+\t\t\tfor (j = 0; j < p->n_instructions; j++) {\n+\t\t\t\tstruct instruction *jmp_instr = &p->instructions[j];\n+\t\t\t\tstruct instruction_data *jmp_data = &p->instruction_data[j];\n+\t\t\t\tstruct instruction_group *jmp_g, *new_g;\n+\n+\t\t\t\t/* Continue when not a jump instruction. Even when jump instruction,\n+\t\t\t\t * continue when the jump destination is not this instruction.\n+\t\t\t\t */\n+\t\t\t\tif (!instruction_is_jmp(jmp_instr) ||\n+\t\t\t\t    strcmp(jmp_data->jmp_label, data->label))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tjmp_g = instruction_group_list_group_find(igl, j);\n+\t\t\t\tif (!jmp_g)\n+\t\t\t\t\tgoto error;\n+\n+\t\t\t\t/* Continue when both the jump instruction and the jump destination\n+\t\t\t\t * instruction are in the same group. Even when in different groups,\n+\t\t\t\t * still continue if the jump destination instruction is already the\n+\t\t\t\t * first instruction of its group.\n+\t\t\t\t */\n+\t\t\t\tif ((jmp_g->group_id == g->group_id) || (g->first_instr_id == i))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\t/* Split the group of the current jump destination instruction to\n+\t\t\t\t * make this instruction the first instruction of a new group.\n+\t\t\t\t */\n+\t\t\t\tnew_g = calloc(1, sizeof(struct instruction_group));\n+\t\t\t\tif (!new_g)\n+\t\t\t\t\tgoto error;\n+\n+\t\t\t\tnew_g->group_id = n_groups;\n+\t\t\t\tnew_g->first_instr_id = i;\n+\t\t\t\tnew_g->last_instr_id = g->last_instr_id;\n+\n+\t\t\t\tg->last_instr_id = i - 1;\n+\n+\t\t\t\tTAILQ_INSERT_AFTER(igl, g, new_g, node);\n+\t\t\t\tn_groups++;\n+\t\t\t\tis_modified = 1;\n+\n+\t\t\t\t/* The decision to split this group (to make the current instruction\n+\t\t\t\t * the first instruction of a new group) is already taken and fully\n+\t\t\t\t * implemented, so no need to search for more reasons to do it.\n+\t\t\t\t */\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Re-evaluate everything, as at least one group got split, so some jumps that were\n+\t\t * previously considered local (i.e. the jump destination is in the same group as\n+\t\t * the jump instruction) can now be \"far jumps\" (i.e. the jump destination is in a\n+\t\t * different group than the jump instruction). Wost case scenario: each instruction\n+\t\t * that is a jump destination ends up as the first instruction of its group.\n+\t\t */\n+\t\tif (!is_modified)\n+\t\t\tbreak;\n+\t}\n+\n+\t/* Re-assign the group IDs to be in incremental order. */\n+\ti = 0;\n+\tTAILQ_FOREACH(g, igl, node) {\n+\t\tg->group_id = i;\n+\n+\t\ti++;\n+\t}\n+\n+\treturn igl;\n+\n+error:\n+\tinstruction_group_list_free(igl);\n+\n+\tfree(g);\n+\n+\treturn NULL;\n+}\n+\n+static void\n+pipeline_instr_does_tx_codegen(struct rte_swx_pipeline *p __rte_unused,\n+\t\t\t       uint32_t instr_pos,\n+\t\t\t       struct instruction *instr,\n+\t\t\t       FILE *f)\n+{\n+\tfprintf(f,\n+\t\t\"%s(p, t, &pipeline_instructions[%u]);\\n\"\n+\t\t\"\\tthread_ip_reset(p, t);\\n\"\n+\t\t\"\\tinstr_rx_exec(p);\\n\"\n+\t\t\"\\treturn;\\n\",\n+\t\tinstr_type_to_func(instr),\n+\t\tinstr_pos);\n+}\n+\n+static int\n+pipeline_instr_jmp_codegen(struct rte_swx_pipeline *p,\n+\t\t\t   struct instruction_group_list *igl,\n+\t\t\t   uint32_t jmp_instr_id,\n+\t\t\t   struct instruction *jmp_instr,\n+\t\t\t   struct instruction_data *jmp_data,\n+\t\t\t   FILE *f)\n+{\n+\tstruct instruction_group *jmp_g, *g;\n+\tstruct instruction_data *data;\n+\tuint32_t instr_id;\n+\n+\tswitch (jmp_instr->type) {\n+\tcase INSTR_JMP:\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_VALID:\n+\t\tfprintf(f,\n+\t\t\t\"if (HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))\",\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_INVALID:\n+\t\tfprintf(f,\n+\t\t\t\"if (!HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))\",\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_HIT:\n+\t\tfprintf(f,\n+\t\t\t\"if (t->hit)\\n\");\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_MISS:\n+\t\tfprintf(f,\n+\t\t\t\"if (!t->hit)\\n\");\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_ACTION_HIT:\n+\t\tfprintf(f,\n+\t\t\t\"if (t->action_id == pipeline_instructions[%u].jmp.action_id)\",\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_ACTION_MISS:\n+\t\tfprintf(f,\n+\t\t\t\"if (t->action_id != pipeline_instructions[%u].jmp.action_id)\",\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_EQ:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_EQ_MH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_EQ_HM:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_EQ_HH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_EQ_I:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_NEQ:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_NEQ_MH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_NEQ_HM:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_NEQ_HH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_NEQ_I:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT_MH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT_HM:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT_HH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT_MI:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_LT_HI:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT_MH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT_HM:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT_HH:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT_MI:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tcase INSTR_JMP_GT_HI:\n+\t\tfprintf(f,\n+\t\t\t\"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > \"\n+\t\t\t\"pipeline_instructions[%u].jmp.b_val)\",\n+\t\t\tjmp_instr_id,\n+\t\t\tjmp_instr_id);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\t/* Find the instruction group of the jump instruction. */\n+\tjmp_g = instruction_group_list_group_find(igl, jmp_instr_id);\n+\tif (!jmp_g)\n+\t\treturn -EINVAL;\n+\n+\t/* Find the instruction group of the jump destination instruction. */\n+\tdata = label_find(p->instruction_data, p->n_instructions, jmp_data->jmp_label);\n+\tif (!data)\n+\t\treturn -EINVAL;\n+\n+\tinstr_id = data - p->instruction_data;\n+\n+\tg = instruction_group_list_group_find(igl, instr_id);\n+\tif (!g)\n+\t\treturn -EINVAL;\n+\n+\t/* Code generation for \"near\" jump (same instruction group) or \"far\" jump (different\n+\t * instruction group).\n+\t */\n+\tif (g->group_id == jmp_g->group_id)\n+\t\tfprintf(f,\n+\t\t\t\"\\n\\t\\tgoto %s;\\n\",\n+\t\t\tjmp_data->jmp_label);\n+\telse\n+\t\tfprintf(f,\n+\t\t\t\" {\\n\"\n+\t\t\t\"\\t\\tthread_ip_set(t, &p->instructions[%u]);\\n\"\n+\t\t\t\"\\t\\treturn;\\n\"\n+\t\t\t\"\\t}\\n\\n\",\n+\t\t\tg->group_id);\n+\n+\treturn 0;\n+}\n+\n+static void\n+instruction_group_list_codegen(struct instruction_group_list *igl, struct rte_swx_pipeline *p, FILE *f)\n+{\n+\tstruct instruction_group *g;\n+\tuint32_t i;\n+\tint is_required = 0;\n+\n+\t/* Check if code generation is required. */\n+\tTAILQ_FOREACH(g, igl, node)\n+\t\tif (g->first_instr_id < g->last_instr_id)\n+\t\t\tis_required = 1;\n+\n+\tif (!is_required)\n+\t\treturn;\n+\n+\t/* Generate the code for the pipeline instruction array. */\n+\tfprintf(f,\n+\t\t\"static const struct instruction pipeline_instructions[] = {\\n\");\n+\n+\tfor (i = 0; i < p->n_instructions; i++) {\n+\t\tstruct instruction *instr = &p->instructions[i];\n+\t\tinstruction_export_t func = export_table[instr->type];\n+\n+\t\tfunc(instr, f);\n+\t}\n+\n+\tfprintf(f, \"};\\n\\n\");\n+\n+\t/* Generate the code for the pipeline functions: one function for each instruction group\n+\t * that contains more than one instruction.\n+\t */\n+\tTAILQ_FOREACH(g, igl, node) {\n+\t\tstruct instruction *last_instr;\n+\t\tuint32_t j;\n+\n+\t\t/* Skip if group contains a single instruction. */\n+\t\tif (g->last_instr_id == g->first_instr_id)\n+\t\t\tcontinue;\n+\n+\t\t/* Generate new pipeline function. */\n+\t\tfprintf(f,\n+\t\t\t\"void\\n\"\n+\t\t\t\"pipeline_func_%u(struct rte_swx_pipeline *p)\\n\"\n+\t\t\t\"{\\n\"\n+\t\t\t\"\\tstruct thread *t = &p->threads[p->thread_id];\\n\"\n+\t\t\t\"\\n\",\n+\t\t\tg->group_id);\n+\n+\t\t/* Generate the code for each pipeline instruction. */\n+\t\tfor (j = g->first_instr_id; j <= g->last_instr_id; j++) {\n+\t\t\tstruct instruction *instr = &p->instructions[j];\n+\t\t\tstruct instruction_data *data = &p->instruction_data[j];\n+\n+\t\t\t/* Label, if present. */\n+\t\t\tif (data->label[0])\n+\t\t\t\tfprintf(f, \"\\n%s : \", data->label);\n+\t\t\telse\n+\t\t\t\tfprintf(f, \"\\n\\t\");\n+\n+\t\t\t/* TX instruction type. */\n+\t\t\tif (instruction_does_tx(instr)) {\n+\t\t\t\tpipeline_instr_does_tx_codegen(p, j, instr, f);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/* Jump instruction type. */\n+\t\t\tif (instruction_is_jmp(instr)) {\n+\t\t\t\tpipeline_instr_jmp_codegen(p, igl, j, instr, data, f);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/* Any other instruction type. */\n+\t\t\tfprintf(f,\n+\t\t\t\t\"%s(p, t, &pipeline_instructions[%u]);\\n\",\n+\t\t\t\tinstr_type_to_func(instr),\n+\t\t\t\tj);\n+\t\t}\n+\n+\t\t/* Finalize the generated pipeline function. For some instructions such as TX,\n+\t\t * emit-many-and-TX and unconditional jump, the next instruction has been already\n+\t\t * decided unconditionally and the instruction pointer of the current thread set\n+\t\t * accordingly; for all the other instructions, the instruction pointer must be\n+\t\t * incremented now.\n+\t\t */\n+\t\tlast_instr = &p->instructions[g->last_instr_id];\n+\n+\t\tif (!instruction_does_tx(last_instr) && (last_instr->type != INSTR_JMP))\n+\t\t\tfprintf(f,\n+\t\t\t\t\"thread_ip_inc(p);\\n\");\n+\n+\t\tfprintf(f,\n+\t\t\t\"}\\n\"\n+\t\t\t\"\\n\");\n+\t}\n+}\n+\n static int\n-pipeline_codegen(struct rte_swx_pipeline *p)\n+pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)\n {\n \tstruct action *a;\n \tFILE *f = NULL;\n \n-\tif (!p)\n-\t\treturn -EINVAL;\n-\n \t/* Create the .c file. */\n \tf = fopen(\"/tmp/pipeline.c\", \"w\");\n \tif (!f)\n@@ -11545,6 +12169,9 @@ pipeline_codegen(struct rte_swx_pipeline *p)\n \t\tfprintf(f, \"\\n\");\n \t}\n \n+\t/* Add the pipeline code. */\n+\tinstruction_group_list_codegen(igl, p, f);\n+\n \t/* Close the .c file. */\n \tfclose(f);\n \n@@ -11554,12 +12181,22 @@ pipeline_codegen(struct rte_swx_pipeline *p)\n static int\n pipeline_compile(struct rte_swx_pipeline *p)\n {\n+\tstruct instruction_group_list *igl = NULL;\n \tint status = 0;\n \n+\tigl = instruction_group_list_create(p);\n+\tif (!igl) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto free;\n+\t}\n+\n \t/* Code generation. */\n-\tstatus = pipeline_codegen(p);\n+\tstatus = pipeline_codegen(p, igl);\n \tif (status)\n-\t\treturn status;\n+\t\tgoto free;\n+\n+free:\n+\tinstruction_group_list_free(igl);\n \n \treturn status;\n }\n",
    "prefixes": [
        "22/24"
    ]
}