mem2reg: tolerate out of bounds constant accesses
authorZachary Snow <zach@zachjs.com>
Wed, 26 May 2021 22:22:31 +0000 (18:22 -0400)
committerZachary Snow <zachary.j.snow@gmail.com>
Tue, 8 Jun 2021 19:02:57 +0000 (15:02 -0400)
This brings the mem2reg behavior in line with the nomem2reg behavior.

frontends/ast/simplify.cc
tests/simple/mem2reg_bounds_tern.v [new file with mode: 0644]
tests/verilog/mem_bounds.sv [new file with mode: 0644]
tests/verilog/mem_bounds.ys [new file with mode: 0644]

index 517647afbf0e808b7836b2d6bd6d135ed106ed45..44b11da74059c2374b9a65a819d85f999db1ae5c 100644 (file)
@@ -1762,7 +1762,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        }
 
        // split memory access with bit select to individual statements
-       if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
+       if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2)
        {
                if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
                        log_file_error(filename, location.first_line, "Invalid bit-select on memory access!\n");
@@ -4501,11 +4501,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
                if (children[0]->children[0]->type == AST_CONSTANT)
                {
                        int id = children[0]->children[0]->integer;
-                       str = stringf("%s[%d]", str.c_str(), id);
+                       int left = id2ast->children[1]->children[0]->integer;
+                       int right = id2ast->children[1]->children[1]->integer;
+                       bool valid_const_access =
+                               (left <= id && id <= right) ||
+                               (right <= id && id <= left);
+                       if (valid_const_access)
+                       {
+                               str = stringf("%s[%d]", str.c_str(), id);
+                               delete_children();
+                               range_valid = false;
+                               id2ast = NULL;
+                       }
+                       else
+                       {
+                               int width;
+                               if (bit_part_sel)
+                               {
+                                       bit_part_sel->dumpAst(nullptr, "? ");
+                                       if (bit_part_sel->children.size() == 1)
+                                               width = 0;
+                                       else
+                                               width = bit_part_sel->children[0]->integer -
+                                                       bit_part_sel->children[1]->integer;
+                                       delete bit_part_sel;
+                                       bit_part_sel = nullptr;
+                               }
+                               else
+                               {
+                                       width = id2ast->children[0]->children[0]->integer -
+                                               id2ast->children[0]->children[1]->integer;
+                               }
+                               width = abs(width) + 1;
 
-                       delete_children();
-                       range_valid = false;
-                       id2ast = NULL;
+                               delete_children();
+
+                               std::vector<RTLIL::State> x_bits;
+                               for (int i = 0; i < width; i++)
+                                       x_bits.push_back(RTLIL::State::Sx);
+                               AstNode *constant = AstNode::mkconst_bits(x_bits, false);
+                               constant->cloneInto(this);
+                               delete constant;
+                       }
                }
                else
                {
diff --git a/tests/simple/mem2reg_bounds_tern.v b/tests/simple/mem2reg_bounds_tern.v
new file mode 100644 (file)
index 0000000..89d6dd3
--- /dev/null
@@ -0,0 +1,19 @@
+module top(
+    input clk,
+    input wire [1:0] sel,
+    input wire [7:0] base,
+    output reg [7:0] line
+);
+    reg [0:7] mem [0:2];
+
+    generate
+        genvar i;
+        for (i = 0; i < 4; i = i + 1) begin : gen
+            always @(posedge clk)
+                mem[i] <= i == 0 ? base : mem[i - 1] + 1;
+        end
+    endgenerate
+
+    always @(posedge clk)
+        line = mem[sel];
+endmodule
diff --git a/tests/verilog/mem_bounds.sv b/tests/verilog/mem_bounds.sv
new file mode 100644 (file)
index 0000000..7fb2fb0
--- /dev/null
@@ -0,0 +1,27 @@
+module top;
+    reg [0:7] mem [0:2];
+
+    initial mem[1] = '1;
+    wire [31:0] a, b, c, d;
+    assign a = mem[1];
+    assign b = mem[-1];
+    assign c = mem[-1][0];
+    assign d = mem[-1][0:1];
+
+    always @* begin
+
+       assert ($countbits(a, '0) == 24);
+       assert ($countbits(a, '1) == 8);
+       assert ($countbits(a, 'x) == 0);
+
+       assert ($countbits(b, '0) == 24);
+       assert ($countbits(b, 'x) == 8);
+
+       assert ($countbits(c, '0) == 31);
+       assert ($countbits(c, 'x) == 1);
+
+       assert ($countbits(d, '0) == 30);
+       assert ($countbits(d, 'x) == 2);
+
+    end
+endmodule
diff --git a/tests/verilog/mem_bounds.ys b/tests/verilog/mem_bounds.ys
new file mode 100644 (file)
index 0000000..42623ad
--- /dev/null
@@ -0,0 +1,6 @@
+read_verilog -sv -mem2reg mem_bounds.sv
+proc
+flatten
+opt -full
+select -module top
+sat -verify -seq 1 -tempinduct -prove-asserts -show-all -enable_undef