systemverilog-plugin: add support for custom type with multiranges inside struct/union

Signed-off-by: Kamil Rakoczy <krakoczy@antmicro.com>
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index ca8f32d..e456857 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -350,7 +350,8 @@
     // needs to have access to all already defined ids
     while (wire_node->simplify(true, false, false, 1, -1, false, false)) {
     }
-    if (wiretype_ast->children[0]->type == AST::AST_STRUCT && wire_node->type == AST::AST_WIRE) {
+    if ((wiretype_ast->children[0]->type == AST::AST_STRUCT || wiretype_ast->children[0]->type == AST::AST_UNION) &&
+        wire_node->type == AST::AST_WIRE) {
         auto struct_width = get_max_offset_struct(wiretype_ast->children[0]);
         wire_node->range_left = struct_width;
         wire_node->children[0]->range_left = struct_width;
@@ -606,22 +607,18 @@
     current_struct_elem = *struct_elem_it;
 
     AST::AstNode *left = nullptr, *right = nullptr;
-    if (current_struct_elem->type == AST::AST_STRUCT_ITEM) {
+    switch (current_struct_elem->type) {
+    case AST::AST_STRUCT_ITEM:
+    case AST::AST_STRUCT:
+    case AST::AST_UNION:
         left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true);
         right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true);
-    } else if (current_struct_elem->type == AST::AST_STRUCT) {
-        // Struct can have multiple range, so to get size of 1 struct,
-        // we get left range for first children, and right range for last children
-        left = AST::AstNode::mkconst_int(current_struct_elem->children.front()->range_left, true);
-        right = AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right, true);
-    } else if (current_struct_elem->type == AST::AST_UNION) {
-        left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true);
-        right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true);
-    } else {
+        break;
+    default:
         // Structs currently can only have AST_STRUCT, AST_STRUCT_ITEM, or AST_UNION.
         log_file_error(current_struct_elem->filename, current_struct_elem->location.first_line,
                        "Accessing struct member of type %s is unsupported.\n", type2str(current_struct_elem->type).c_str());
-    }
+    };
 
     auto elem_size =
       new AST::AstNode(AST::AST_ADD, new AST::AstNode(AST::AST_SUB, left->clone(), right->clone()), AST::AstNode::mkconst_int(1, true));
@@ -636,7 +633,9 @@
         }
         if (c->type == AST::AST_RANGE) {
             // Currently supporting only 1 range
-            log_assert(!struct_range);
+            if (struct_range) {
+                log_error("Currently support for dot-access is limited to single range\n");
+            }
             struct_range = c;
         }
     }
@@ -817,10 +816,6 @@
     std::vector<AST::AstNode *> ranges(it, snode->children.end());
     snode->children.erase(it, snode->children.end());
     if (!ranges.empty()) {
-        if (ranges.size() > 1) {
-            log_file_error(ranges[1]->filename, ranges[1]->location.first_line,
-                           "Currently support for custom-type with range is limited to single range\n");
-        }
         for (auto range : ranges) {
             snode->multirange_dimensions.push_back(min(range->range_left, range->range_right));
             snode->multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1);
@@ -835,9 +830,10 @@
             // embedded struct or union
             width = simplify_struct(node, base_offset + offset, parent_node);
             if (!node->multirange_dimensions.empty()) {
-                int number_of_structs = 1;
-                number_of_structs = node->multirange_dimensions.back();
-                width *= number_of_structs;
+                // reverse-iterate over multiranges, skip right_range
+                for (auto rit = node->multirange_dimensions.rbegin(); rit != node->multirange_dimensions.rend(); rit += 2) {
+                    width *= *rit;
+                }
             }
             // set range of struct
             node->range_right = base_offset + offset;