verilog: save and restore overwritten macro arguments
authorZachary Snow <zach@zachjs.com>
Thu, 15 Jul 2021 14:36:50 +0000 (10:36 -0400)
committerZachary Snow <zachary.j.snow@gmail.com>
Thu, 29 Jul 2021 01:52:16 +0000 (21:52 -0400)
frontends/verilog/preproc.cc
frontends/verilog/preproc.h
tests/verilog/macro_arg_tromp.sv [new file with mode: 0644]
tests/verilog/macro_arg_tromp.ys [new file with mode: 0644]

index 4b9ebe0aa3db6cb77f4944fdeab99425d242b31c..17f5675875269d6d8b16aa667bb81553ddf726f1 100644 (file)
@@ -36,6 +36,7 @@
 #include "verilog_frontend.h"
 #include "kernel/log.h"
 #include <assert.h>
+#include <stack>
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
@@ -334,6 +335,11 @@ define_map_t::add(const std::string &name, const std::string &txt, const arg_map
        defines[name] = std::unique_ptr<define_body_t>(new define_body_t(txt, args));
 }
 
+void define_map_t::add(const std::string &name, const define_body_t &body)
+{
+       defines[name] = std::unique_ptr<define_body_t>(new define_body_t(body));
+}
+
 void define_map_t::merge(const define_map_t &map)
 {
        for (const auto &pr : map.defines) {
@@ -440,7 +446,17 @@ static bool read_argument(std::string &dest)
        }
 }
 
-static bool try_expand_macro(define_map_t &defines, std::string &tok)
+using macro_arg_stack_t = std::stack<std::pair<std::string, define_body_t>>;
+
+static void restore_macro_arg(define_map_t &defines, macro_arg_stack_t &macro_arg_stack)
+{
+       log_assert(!macro_arg_stack.empty());
+       auto &overwritten_arg = macro_arg_stack.top();
+       defines.add(overwritten_arg.first, overwritten_arg.second);
+       macro_arg_stack.pop();
+}
+
+static bool try_expand_macro(define_map_t &defines, macro_arg_stack_t &macro_arg_stack, std::string &tok)
 {
        if (tok == "`\"") {
                std::string literal("\"");
@@ -450,7 +466,7 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok)
                        if (ntok == "`\"") {
                                insert_input(literal+"\"");
                                return true;
-                       } else if (!try_expand_macro(defines, ntok)) {
+                       } else if (!try_expand_macro(defines, macro_arg_stack, ntok)) {
                                        literal += ntok;
                        }
                }
@@ -495,6 +511,10 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok)
                        args.push_back(arg);
                }
                for (const auto &pr : body->args.get_vals(name, args)) {
+                       if (const define_body_t *existing = defines.find(pr.first)) {
+                               macro_arg_stack.push({pr.first, *existing});
+                               insert_input("`__restore_macro_arg ");
+                       }
                        defines.add(pr.first, pr.second);
                }
        } else {
@@ -725,6 +745,7 @@ frontend_verilog_preproc(std::istream                 &f,
        defines.merge(pre_defines);
        defines.merge(global_defines_cache);
 
+       macro_arg_stack_t macro_arg_stack;
        std::vector<std::string> filename_stack;
        // We are inside pass_level levels of satisfied ifdefs, and then within
        // fail_level levels of unsatisfied ifdefs.  The unsatisfied ones are
@@ -828,7 +849,7 @@ frontend_verilog_preproc(std::istream                 &f,
                if (tok == "`include") {
                        skip_spaces();
                        std::string fn = next_token(true);
-                       while (try_expand_macro(defines, fn)) {
+                       while (try_expand_macro(defines, macro_arg_stack, fn)) {
                                fn = next_token();
                        }
                        while (1) {
@@ -935,7 +956,12 @@ frontend_verilog_preproc(std::istream                 &f,
                        continue;
                }
 
-               if (try_expand_macro(defines, tok))
+               if (tok == "`__restore_macro_arg") {
+                       restore_macro_arg(defines, macro_arg_stack);
+                       continue;
+               }
+
+               if (try_expand_macro(defines, macro_arg_stack, tok))
                        continue;
 
                output_code.push_back(tok);
index e1048156c3c4abb6a22b8e11a2b8a577675dc53f..330855a92af96e81ab9a6b1f3edeca61892a44a2 100644 (file)
@@ -42,6 +42,7 @@ struct define_map_t
 
        // Add a definition, overwriting any existing definition for name.
        void add(const std::string &name, const std::string &txt, const arg_map_t *args = nullptr);
+       void add(const std::string &name, const define_body_t &body);
 
        // Merge in another map of definitions (which take precedence
        // over anything currently defined).
diff --git a/tests/verilog/macro_arg_tromp.sv b/tests/verilog/macro_arg_tromp.sv
new file mode 100644 (file)
index 0000000..a9c68a4
--- /dev/null
@@ -0,0 +1,21 @@
+// Taken from: https://github.com/YosysHQ/yosys/issues/2867
+
+`define MIN(x, y) ((x) < (y) ? (x) : (y))
+`define CEIL_DIV(x, y) (((x) / (y)) + `MIN((x) % (y), 1))
+
+module pad_msg1 (input logic [`MIN(512*`CEIL_DIV(64, 512), 64)-1:0] x,
+                output logic [`MIN(512*`CEIL_DIV(64, 512), 64)-1:0] y);
+   assign y[63:0] = x;
+endmodule
+
+module pad_msg2 (input logic [((512*`CEIL_DIV(64, 512)) < (64) ? (512*`CEIL_DIV(64,512)) : (64))-1:0] x,
+                output logic [((512*`CEIL_DIV(64, 512)) < (64) ? (512*`CEIL_DIV(64,512)) : (64))-1:0] y);
+   assign y[63:0] = x;
+endmodule
+
+module top(...);
+`define add(x) x +
+input [3:0] A;
+output [3:0] B;
+assign B = `add(`add(3)A)A;
+endmodule
diff --git a/tests/verilog/macro_arg_tromp.ys b/tests/verilog/macro_arg_tromp.ys
new file mode 100644 (file)
index 0000000..e8bd58e
--- /dev/null
@@ -0,0 +1,2 @@
+logger -expect-no-warnings
+read_verilog -sv macro_arg_tromp.sv