verilog: fix handling of nested ifdef directives
authorZachary Snow <zach@zachjs.com>
Thu, 25 Feb 2021 20:53:55 +0000 (15:53 -0500)
committerZachary Snow <zachary.j.snow@gmail.com>
Mon, 1 Mar 2021 17:28:33 +0000 (12:28 -0500)
- track depth so we know whether to consider higher-level elsifs
- error on unmatched endif/elsif/else

frontends/verilog/preproc.cc
tests/simple/ifdef_1.v [new file with mode: 0644]
tests/simple/ifdef_2.v [new file with mode: 0644]
tests/verilog/include_self.v [new file with mode: 0644]
tests/verilog/include_self.ys [new file with mode: 0644]
tests/verilog/unmatched_else.ys [new file with mode: 0644]
tests/verilog/unmatched_elsif.ys [new file with mode: 0644]
tests/verilog/unmatched_endif.ys [new file with mode: 0644]

index de707593f9ba022c66ed74c3134c456604c2b3af..84966e501905379ff996c6c612a0446dc37a1646 100644 (file)
@@ -727,7 +727,8 @@ frontend_verilog_preproc(std::istream                 &f,
 
        std::vector<std::string> filename_stack;
        int ifdef_fail_level = 0;
-       bool in_elseif = false;
+       int ifdef_pass_level = 0;
+       bool ifdef_already_satisfied = false;
 
        output_code.clear();
        input_buffer.clear();
@@ -743,42 +744,68 @@ frontend_verilog_preproc(std::istream                 &f,
                if (tok == "`endif") {
                        if (ifdef_fail_level > 0)
                                ifdef_fail_level--;
-                       if (ifdef_fail_level == 0)
-                               in_elseif = false;
+                       else if (ifdef_pass_level > 0)
+                               ifdef_already_satisfied = --ifdef_pass_level;
+                       else
+                               log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
                        continue;
                }
 
                if (tok == "`else") {
-                       if (ifdef_fail_level == 0)
+                       if (ifdef_fail_level == 0) {
+                               if (ifdef_pass_level == 0)
+                                       log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
+                               log_assert(ifdef_already_satisfied);
                                ifdef_fail_level = 1;
-                       else if (ifdef_fail_level == 1 && !in_elseif)
+                       } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) {
                                ifdef_fail_level = 0;
+                               ifdef_pass_level++;
+                               ifdef_already_satisfied = true;
+                       }
                        continue;
                }
 
                if (tok == "`elsif") {
                        skip_spaces();
                        std::string name = next_token(true);
-                       if (ifdef_fail_level == 0)
-                               ifdef_fail_level = 1, in_elseif = true;
-                       else if (ifdef_fail_level == 1 && defines.find(name))
-                               ifdef_fail_level = 0, in_elseif = true;
+                       if (ifdef_fail_level == 0) {
+                               if (ifdef_pass_level == 0)
+                                       log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
+                               log_assert(ifdef_already_satisfied);
+                               ifdef_fail_level = 1;
+                       } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) {
+                               ifdef_fail_level = 0;
+                               ifdef_pass_level++;
+                               ifdef_already_satisfied = true;
+                       }
                        continue;
                }
 
                if (tok == "`ifdef") {
                        skip_spaces();
                        std::string name = next_token(true);
-                       if (ifdef_fail_level > 0 || !defines.find(name))
+                       if (ifdef_fail_level > 0 || !defines.find(name)) {
                                ifdef_fail_level++;
+                       } else {
+                               ifdef_pass_level++;
+                               ifdef_already_satisfied = true;
+                       }
+                       if (ifdef_fail_level == 1)
+                               ifdef_already_satisfied = false;
                        continue;
                }
 
                if (tok == "`ifndef") {
                        skip_spaces();
                        std::string name = next_token(true);
-                       if (ifdef_fail_level > 0 || defines.find(name))
+                       if (ifdef_fail_level > 0 || defines.find(name)) {
                                ifdef_fail_level++;
+                       } else {
+                               ifdef_pass_level++;
+                               ifdef_already_satisfied = true;
+                       }
+                       if (ifdef_fail_level == 1)
+                               ifdef_already_satisfied = false;
                        continue;
                }
 
diff --git a/tests/simple/ifdef_1.v b/tests/simple/ifdef_1.v
new file mode 100644 (file)
index 0000000..fa96235
--- /dev/null
@@ -0,0 +1,88 @@
+module top(o1, o2, o3, o4);
+
+`define FAIL input wire not_a_port;
+
+`ifdef COND_1
+       `FAIL
+`elsif COND_2
+       `FAIL
+`elsif COND_3
+       `FAIL
+`elsif COND_4
+       `FAIL
+`else
+
+       `define COND_4
+       output wire o4;
+
+       `ifdef COND_1
+               `FAIL
+       `elsif COND_2
+               `FAIL
+       `elsif COND_3
+               `FAIL
+       `elsif COND_4
+
+               `define COND_3
+               output wire o3;
+
+               `ifdef COND_1
+                       `FAIL
+               `elsif COND_2
+                       `FAIL
+               `elsif COND_3
+
+                       `define COND_2
+                       output wire o2;
+
+                       `ifdef COND_1
+                               `FAIL
+                       `elsif COND_2
+
+                               `define COND_1
+                               output wire o1;
+
+                               `ifdef COND_1
+
+                                       `ifdef COND_1
+                                       `elsif COND_2
+                                               `FAIL
+                                       `elsif COND_3
+                                               `FAIL
+                                       `elsif COND_4
+                                               `FAIL
+                                       `else
+                                               `FAIL
+                                       `endif
+
+                               `elsif COND_2
+                                       `FAIL
+                               `elsif COND_3
+                                       `FAIL
+                               `elsif COND_4
+                                       `FAIL
+                               `else
+                                       `FAIL
+                               `endif
+
+                       `elsif COND_3
+                               `FAIL
+                       `elsif COND_4
+                               `FAIL
+                       `else
+                               `FAIL
+                       `endif
+
+               `elsif COND_4
+                       `FAIL
+               `else
+                       `FAIL
+               `endif
+
+       `else
+               `FAIL
+       `endif
+
+`endif
+
+endmodule
diff --git a/tests/simple/ifdef_2.v b/tests/simple/ifdef_2.v
new file mode 100644 (file)
index 0000000..6dd89ef
--- /dev/null
@@ -0,0 +1,21 @@
+module top(o1, o2, o3);
+
+output wire o1;
+
+`define COND_1
+`define COND_2
+`define COND_3
+
+`ifdef COND_1
+       output wire o2;
+`elsif COND_2
+       input wire dne1;
+`elsif COND_3
+       input wire dne2;
+`else
+       input wire dne3;
+`endif
+
+output wire o3;
+
+endmodule
diff --git a/tests/verilog/include_self.v b/tests/verilog/include_self.v
new file mode 100644 (file)
index 0000000..23ffc71
--- /dev/null
@@ -0,0 +1,30 @@
+`ifdef GUARD_5
+module top;
+       wire x;
+endmodule
+
+`elsif GUARD_4
+`define GUARD_5
+`include "include_self.v"
+
+`elsif GUARD_3
+`define GUARD_4
+`include "include_self.v"
+
+`elsif GUARD_2
+`define GUARD_3
+`include "include_self.v"
+
+`elsif GUARD_1
+`define GUARD_2
+`include "include_self.v"
+
+`elsif GUARD_0
+`define GUARD_1
+`include "include_self.v"
+
+`else
+`define GUARD_0
+`include "include_self.v"
+
+`endif
diff --git a/tests/verilog/include_self.ys b/tests/verilog/include_self.ys
new file mode 100644 (file)
index 0000000..07d840d
--- /dev/null
@@ -0,0 +1,2 @@
+read_verilog include_self.v
+select -assert-count 1 top/x
diff --git a/tests/verilog/unmatched_else.ys b/tests/verilog/unmatched_else.ys
new file mode 100644 (file)
index 0000000..413f413
--- /dev/null
@@ -0,0 +1,6 @@
+logger -expect error "Found `else outside of macro conditional branch!" 1
+read_verilog <<EOT
+module top;
+`else
+endmodule
+EOT
diff --git a/tests/verilog/unmatched_elsif.ys b/tests/verilog/unmatched_elsif.ys
new file mode 100644 (file)
index 0000000..e0ed0aa
--- /dev/null
@@ -0,0 +1,6 @@
+logger -expect error "Found `elsif outside of macro conditional branch!" 1
+read_verilog <<EOT
+module top;
+`elsif
+endmodule
+EOT
diff --git a/tests/verilog/unmatched_endif.ys b/tests/verilog/unmatched_endif.ys
new file mode 100644 (file)
index 0000000..39d6038
--- /dev/null
@@ -0,0 +1,6 @@
+logger -expect error "Found `endif outside of macro conditional branch!" 1
+read_verilog <<EOT
+module top;
+`endif
+endmodule
+EOT