Merge pull request #521 from antmicro/wsip/duplicated_ranges

duplicated ranges
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 0be44d7..048b36b 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -309,51 +309,58 @@
     }
 }
 
-static size_t add_multirange_attribute(AST::AstNode *wire_node, const std::vector<AST::AstNode *> ranges)
+// Sets the `wire_node->multirange_dimensions` attribute and returns the total sizes of packed and unpacked ranges.
+static std::pair<size_t, size_t> set_multirange_dimensions(AST::AstNode *wire_node, const std::vector<AST::AstNode *> packed_ranges,
+                                                           const std::vector<AST::AstNode *> unpacked_ranges)
 {
     // node->multirange_dimensions stores dimensions' offsets and widths.
     // It shall have even number of elements.
     // For a range of [A:B] it should be appended with {min(A,B)} and {max(A,B)-min(A,B)+1}
     // For a range of [A] it should be appended with {0} and {A}
 
-    size_t size = 1;
-    for (size_t i = 0; i < ranges.size(); i++) {
-        log_assert(AST_INTERNAL::current_ast_mod);
-        if (ranges[i]->children.size() == 1) {
-            ranges[i]->children.push_back(ranges[i]->children[0]->clone());
-        }
-        simplify_sv(ranges[i], wire_node);
-        while (simplify(ranges[i], true, false, false, 1, -1, false, false)) {
-        }
-        // this workaround case, where yosys doesn't follow id2ast and simplifies it to resolve constant
-        if (ranges[i]->children[0]->id2ast) {
-            simplify_sv(ranges[i]->children[0]->id2ast, ranges[i]->children[0]);
-            while (simplify(ranges[i]->children[0]->id2ast, true, false, false, 1, -1, false, false)) {
+    auto calc_range_size = [wire_node](const std::vector<AST::AstNode *> &ranges) -> size_t {
+        size_t size = 1;
+        for (size_t i = 0; i < ranges.size(); i++) {
+            log_assert(AST_INTERNAL::current_ast_mod);
+            simplify_sv(ranges[i], wire_node);
+            // If it's a range of [A], make it [A:A].
+            if (ranges[i]->children.size() == 1) {
+                ranges[i]->children.push_back(ranges[i]->children[0]->clone());
             }
-        }
-        if (ranges[i]->children[1]->id2ast) {
-            simplify_sv(ranges[i]->children[1]->id2ast, ranges[i]->children[1]);
-            while (simplify(ranges[i]->children[1]->id2ast, true, false, false, 1, -1, false, false)) {
+            while (simplify(ranges[i], true, false, false, 1, -1, false, false)) {
             }
-        }
-        simplify_sv(ranges[i], wire_node);
-        while (simplify(ranges[i], true, false, false, 1, -1, false, false)) {
-        }
-        log_assert(ranges[i]->children[0]->type == AST::AST_CONSTANT);
-        log_assert(ranges[i]->children[1]->type == AST::AST_CONSTANT);
+            // this workaround case, where yosys doesn't follow id2ast and simplifies it to resolve constant
+            if (ranges[i]->children[0]->id2ast) {
+                simplify_sv(ranges[i]->children[0]->id2ast, ranges[i]->children[0]);
+                while (simplify(ranges[i]->children[0]->id2ast, true, false, false, 1, -1, false, false)) {
+                }
+            }
+            if (ranges[i]->children[1]->id2ast) {
+                simplify_sv(ranges[i]->children[1]->id2ast, ranges[i]->children[1]);
+                while (simplify(ranges[i]->children[1]->id2ast, true, false, false, 1, -1, false, false)) {
+                }
+            }
+            simplify_sv(ranges[i], wire_node);
+            while (simplify(ranges[i], true, false, false, 1, -1, false, false)) {
+            }
+            log_assert(ranges[i]->children[0]->type == AST::AST_CONSTANT);
+            log_assert(ranges[i]->children[1]->type == AST::AST_CONSTANT);
 
-        const auto low = min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer);
-        const auto high = max(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer);
-        const auto elem_size = high - low + 1;
+            const auto low = min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer);
+            const auto high = max(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer);
+            const auto elem_size = high - low + 1;
 
-        wire_node->multirange_dimensions.push_back(low);
-        wire_node->multirange_dimensions.push_back(elem_size);
-        wire_node->multirange_swapped.push_back(ranges[i]->range_swapped);
-        size *= elem_size;
-    }
+            wire_node->multirange_dimensions.push_back(low);
+            wire_node->multirange_dimensions.push_back(elem_size);
+            wire_node->multirange_swapped.push_back(ranges[i]->range_swapped);
+            size *= elem_size;
+        }
+        return size;
+    };
+    size_t packed_size = calc_range_size(packed_ranges);
+    size_t unpacked_size = calc_range_size(unpacked_ranges);
     log_assert(wire_node->multirange_dimensions.size() % 2 == 0);
-
-    return size;
+    return {packed_size, unpacked_size};
 }
 
 static AST::AstNode *convert_range(AST::AstNode *id, int packed_ranges_size, int unpacked_ranges_size, int i)
@@ -669,8 +676,7 @@
     if (convert_node) {
         // if not already converted
         if (wire_node->multirange_dimensions.empty()) {
-            const size_t packed_size = add_multirange_attribute(wire_node, packed_ranges);
-            const size_t unpacked_size = add_multirange_attribute(wire_node, unpacked_ranges);
+            const auto [packed_size, unpacked_size] = set_multirange_dimensions(wire_node, packed_ranges, unpacked_ranges);
             if (packed_ranges.size() == 1 && unpacked_ranges.empty()) {
                 ranges.push_back(packed_ranges[0]->clone());
             } else if (unpacked_ranges.size() == 1 && packed_ranges.empty()) {
@@ -4762,28 +4768,27 @@
 void UhdmAst::process_net()
 {
     current_node = make_ast_node(AST::AST_WIRE);
-    std::vector<AST::AstNode *> packed_ranges;   // comes before wire name
-    std::vector<AST::AstNode *> unpacked_ranges; // comes after wire name
     auto net_type = vpi_get(vpiNetType, obj_h);
     current_node->is_reg = net_type == vpiReg;
     current_node->is_output = net_type == vpiOutput;
     current_node->is_logic = !current_node->is_reg;
     current_node->is_signed = vpi_get(vpiSigned, obj_h);
     visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) {
-        if (node && !node->str.empty()) {
+        if (!node)
+            return;
+        if (!node->str.empty()) {
             auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE);
             wiretype_node->str = node->str;
             // wiretype needs to be 1st node
             current_node->children.insert(current_node->children.begin(), wiretype_node);
             current_node->is_custom_type = true;
+        } else {
+            // Ranges from the typespec are copied to the current node as attributes.
+            // So that multiranges can be replaced with a single range as a node later.
+            copy_packed_unpacked_attribute(node, current_node);
         }
         delete node;
     });
-    if (vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h)) {
-        visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); });
-        vpi_release_handle(typespec_h);
-    }
-    add_multirange_wire(current_node, packed_ranges, unpacked_ranges);
 }
 
 void UhdmAst::process_parameter()