From fd902d15011671d42132178d273384ea69da0a50 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Jul 2026 20:31:23 -0700 Subject: [PATCH] Support prefix labels on procedural blocks SystemVerilog sequential and parallel blocks allow a block identifier before the `begin` or `fork` keyword: LABEL: begin statement; end LABEL: fork statement; join The parser currently only accepts the block identifier after `begin` and `fork`. Add a prefix-label rule using `identifier_name`. This accepts labels returned as either `IDENTIFIER` or `TYPE_IDENTIFIER` without duplicating the label grammar. The mixed procedural item list lets the parser use the following `:` to distinguish a visible type identifier used as a label from the start of a variable declaration. Use a shared optional prefix rule for sequential and parallel block forms. Resolve the prefix and post-keyword names before starting the common block path, and bind attributes placed between the label and block keyword. Require SystemVerilog mode when a prefix label is present. IEEE 1800-2023 section 9.3.5 does not allow a prefix label and a block name after `begin` or `fork` at the same time. Report an error for this form while keeping it in the grammar for error recovery. Keep the existing closing label handling, which allows a matching name after `end` or a join keyword. Signed-off-by: Lars-Peter Clausen --- parse.y | 78 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/parse.y b/parse.y index f978719f8..d269ea56e 100644 --- a/parse.y +++ b/parse.y @@ -603,6 +603,29 @@ static PBlock *pform_start_block(const YYLTYPE&loc, const char *name, return block; } +static char *pform_start_block_with_labels( + const YYLTYPE&loc, const YYLTYPE&name_loc, + char *prefix_label, char *block_name, + std::list *attributes, + PBlock::BL_TYPE block_type) +{ + std::unique_ptr prefix_label_ptr(prefix_label); + std::unique_ptr block_name_ptr(block_name); + + if (prefix_label_ptr && block_name_ptr) { + yyerror(name_loc, + "error: A block cannot have both a prefix label and a block name."); + } + + const char *name = block_name_ptr ? block_name_ptr.get() + : prefix_label_ptr.get(); + auto block = pform_start_block(loc, name, block_type); + pform_bind_attributes(block->attributes, attributes); + + return block_name_ptr ? block_name_ptr.release() + : prefix_label_ptr.release(); +} + static PBlock *pform_finish_block(const YYLTYPE&block_loc, const YYLTYPE&end_loc, const char *type, char *raw_name, char *end_label, @@ -798,6 +821,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, std::vector*statement_list; struct procedural_item_list_t *procedural_item_list; + struct { + char *label; + std::list *attributes; + } block_prefix; + decl_assignment_t*decl_assignment; std::list*decl_assignments; @@ -1088,6 +1116,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type block_item_or_statement_list_opt %type tf_item_or_statement_list %type tf_item_or_statement_list_opt +%type block_prefix_opt +%type sequential_block_start parallel_block_start %type analog_statement @@ -7315,6 +7345,34 @@ subroutine_call } ; +block_prefix_opt + : /* empty */ + { $$.label = nullptr; + $$.attributes = nullptr; + } + | identifier_name ':' attribute_list_opt + { pform_requires_sv(@1, "Block prefix label"); + $$.label = $1; + $$.attributes = $3; + } + ; + +sequential_block_start + : block_prefix_opt K_begin label_opt + { $$ = pform_start_block_with_labels(@2, @3, $1.label, $3, + $1.attributes, PBlock::BL_SEQ); + @$ = @2; + } + ; + +parallel_block_start + : block_prefix_opt K_fork label_opt + { $$ = pform_start_block_with_labels(@2, @3, $1.label, $3, + $1.attributes, PBlock::BL_PAR); + @$ = @2; + } + ; + statement_item /* This is roughly statement_item in the LRM */ /* assign and deassign statements are procedural code to do @@ -7356,14 +7414,12 @@ statement_item /* This is roughly statement_item in the LRM */ the declarations. The scope is popped at the end of the block. */ /* In SystemVerilog an unnamed block can contain variable declarations. */ - | K_begin label_opt - { pform_start_block(@1, $2, PBlock::BL_SEQ); } - block_item_or_statement_list_opt K_end label_opt - { if (!$2 && $4->has_decls) { + | sequential_block_start block_item_or_statement_list_opt K_end label_opt + { if (!$1 && $2->has_decls) { pform_block_decls_requires_sv(); } - $$ = pform_finish_block(@1, @6, "block", $2, $6, - PBlock::BL_SEQ, $4); + $$ = pform_finish_block(@1, @4, "block", $1, $4, + PBlock::BL_SEQ, $2); } /* fork-join blocks are very similar to begin-end blocks. In fact, @@ -7372,13 +7428,11 @@ statement_item /* This is roughly statement_item in the LRM */ code generator can do the right thing. */ /* In SystemVerilog an unnamed block can contain variable declarations. */ - | K_fork label_opt - { pform_start_block(@1, $2, PBlock::BL_PAR); } - block_item_or_statement_list_opt join_keyword label_opt - { if (!$2 && $4->has_decls) { - pform_requires_sv(@4, "Variable declaration in unnamed block"); + | parallel_block_start block_item_or_statement_list_opt join_keyword label_opt + { if (!$1 && $2->has_decls) { + pform_requires_sv(@2, "Variable declaration in unnamed block"); } - $$ = pform_finish_block(@1, @6, "fork", $2, $6, $5, $4); + $$ = pform_finish_block(@1, @4, "fork", $1, $4, $3, $2); } | K_disable hierarchy_identifier ';'