From 1ec5994100510d6fb9e18ff7234ede496f831a51 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Thu, 25 Feb 2021 15:53:55 -0500 Subject: [PATCH] verilog: fix handling of nested ifdef directives - track depth so we know whether to consider higher-level elsifs - error on unmatched endif/elsif/else --- frontends/verilog/preproc.cc | 49 ++++++++++++++---- tests/simple/ifdef_1.v | 88 ++++++++++++++++++++++++++++++++ tests/simple/ifdef_2.v | 21 ++++++++ tests/verilog/include_self.v | 30 +++++++++++ tests/verilog/include_self.ys | 2 + tests/verilog/unmatched_else.ys | 6 +++ tests/verilog/unmatched_elsif.ys | 6 +++ tests/verilog/unmatched_endif.ys | 6 +++ 8 files changed, 197 insertions(+), 11 deletions(-) create mode 100644 tests/simple/ifdef_1.v create mode 100644 tests/simple/ifdef_2.v create mode 100644 tests/verilog/include_self.v create mode 100644 tests/verilog/include_self.ys create mode 100644 tests/verilog/unmatched_else.ys create mode 100644 tests/verilog/unmatched_elsif.ys create mode 100644 tests/verilog/unmatched_endif.ys diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index de707593f..84966e501 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -727,7 +727,8 @@ frontend_verilog_preproc(std::istream &f, std::vector 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 index 000000000..fa962355c --- /dev/null +++ b/tests/simple/ifdef_1.v @@ -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 index 000000000..6dd89efed --- /dev/null +++ b/tests/simple/ifdef_2.v @@ -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 index 000000000..23ffc7104 --- /dev/null +++ b/tests/verilog/include_self.v @@ -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 index 000000000..07d840d68 --- /dev/null +++ b/tests/verilog/include_self.ys @@ -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 index 000000000..413f413c3 --- /dev/null +++ b/tests/verilog/unmatched_else.ys @@ -0,0 +1,6 @@ +logger -expect error "Found `else outside of macro conditional branch!" 1 +read_verilog <