MikePopoloski / slang

SystemVerilog compiler and language services
MIT License
558 stars 122 forks source link

Unexpected StatementBlock Member in Syntax Tree for Simple SystemVerilog Module Using slang --ast-json #874

Closed yanggeorge closed 5 months ago

yanggeorge commented 5 months ago

Describe the bug When analyzing the SystemVerilog code with slang --ast-json, the generated syntax tree for a simple module shows three members (Variable, StatementBlock, ProceduralBlock) instead of the expected two (Variable, ProceduralBlock). The presence of an additional StatementBlock seems redundant or unclear, leading to potential confusion regarding the structural representation of the module.

To Reproduce Steps to reproduce the behavior:

Use the following SystemVerilog code:

module top;
    logic a [4];

    always_comb begin
        for (int i = 0; i < 4; i++) begin
            a[2'(i)] = i;
        end
    end
endmodule

Run the command slang --ast-json to dump json .

Expected behavior: The syntax tree should ideally consist of two members for the top module – one for the variable a and another for the procedural block always_comb.

The additional StatementBlock member in the syntax tree is not clearly understood and seems to be an anomaly.

Additional context Here is the ast-dump json.

{
  "name": "$root",
  "kind": "Root",
  "addr": 2199024582784,
  "members": [
    {
      "name": "",
      "kind": "CompilationUnit",
      "addr": 2199025966624
    },
    {
      "name": "top",
      "kind": "Instance",
      "addr": 2199025969384,
      "body": {
        "name": "top",
        "kind": "InstanceBody",
        "addr": 2199025968144,
        "members": [
          {
            "name": "a",
            "kind": "Variable",
            "addr": 2199025968336,
            "type": "logic$[0:3]",
            "lifetime": "Static"
          },
          {
            "name": "",
            "kind": "StatementBlock",
            "addr": 2199025968840,
            "members": [
              {
                "name": "i",
                "kind": "Variable",
                "addr": 2199025969000,
                "type": "int",
                "initializer": {
                  "kind": "IntegerLiteral",
                  "type": "int",
                  "value": "0",
                  "constant": "0"
                },
                "lifetime": "Automatic"
              }
            ]
          },
          {
            "name": "",
            "kind": "ProceduralBlock",
            "addr": 2199025968712,
            "procedureKind": "AlwaysComb",
            "body": {
              "kind": "Block",
              "blockKind": "Sequential",
              "body": {
                "kind": "Block",
                "blockKind": "Sequential",
                "block": "2199025968840 ",
                "body": {
                  "kind": "List",
                  "list": [
                    {
                      "kind": "VariableDeclaration",
                      "symbol": "2199025969000 i"
                    },
                    {
                      "kind": "ForLoop",
                      "initializers": [
                      ],
                      "stopExpr": {
                        "kind": "BinaryOp",
                        "type": "bit",
                        "op": "LessThan",
                        "left": {
                          "kind": "NamedValue",
                          "type": "int",
                          "symbol": "2199025969000 i"
                        },
                        "right": {
                          "kind": "IntegerLiteral",
                          "type": "int",
                          "value": "4",
                          "constant": "4"
                        }
                      },
                      "steps": [
                        {
                          "kind": "UnaryOp",
                          "type": "int",
                          "op": "Postincrement",
                          "operand": {
                            "kind": "NamedValue",
                            "type": "int",
                            "symbol": "2199025969000 i"
                          }
                        }
                      ],
                      "body": {
                        "kind": "Block",
                        "blockKind": "Sequential",
                        "body": {
                          "kind": "ExpressionStatement",
                          "expr": {
                            "kind": "Assignment",
                            "type": "logic",
                            "left": {
                              "kind": "ElementSelect",
                              "type": "logic",
                              "value": {
                                "kind": "NamedValue",
                                "type": "logic$[0:3]",
                                "symbol": "2199025968336 a"
                              },
                              "selector": {
                                "kind": "Conversion",
                                "type": "bit signed[1:0]",
                                "operand": {
                                  "kind": "NamedValue",
                                  "type": "int",
                                  "symbol": "2199025969000 i"
                                }
                              }
                            },
                            "right": {
                              "kind": "Conversion",
                              "type": "logic",
                              "operand": {
                                "kind": "Conversion",
                                "type": "logic signed[31:0]",
                                "operand": {
                                  "kind": "NamedValue",
                                  "type": "int",
                                  "symbol": "2199025969000 i"
                                }
                              }
                            },
                            "isNonBlocking": false
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        ],
        "definition": "top"
      },
      "connections": [
      ]
    }
  ]
}
MikePopoloski commented 5 months ago

This is due to how SystemVerilog hierarchies work. ProceduralBlocks themselves are not scopes; they contain only statements, and possibly StatementBlocks (sequential or parallel), so the blocks inside each ProceduralBlock are actually visible to name lookup at one level above them in the hierarchy. For example, the following code is legal:

module m;
    always_comb 
        if (1) begin : foo
            int i;
        end
        else if (1) begin : bar
            int j;
        end

    int q = foo.i + bar.j;
endmodule

The always_comb here is not part of the hierarchy of scopes and variables, but the two blocks introduced by conditional statements are. Because of this, those blocks need to appear in the parent scope for name lookup to work.

In your particular example, the for loop's variable declaration is causing an anonymous block to be created (as specified by the LRM). It's not required that the block be in the parent for name lookup purposes since the block has no name, but it's still done that way for consistency and to maintain the invariant that all symbols (variables, nets, etc) can walk up the hierarchy through their parents to reach the root of the hierarchy tree.

yanggeorge commented 5 months ago

@MikePopoloski Thanks for your explanation; I almost understand what you mean.

When a label is added to the for-loop, everything becomes clearer.

module top;
    logic a [4];

    always_comb begin
        loop: for (int i = 0; i < 4; i++) begin
            a[2'(i)] = i;
        end
    end
endmodule

Additionally, the AlwaysComb Procedural Block is linked to a blockSymbol. Therefore, the AST (Abstract Syntax Tree) represents not only the hierarchical scope but also the syntax structure.