From 5dcd2e89570a7704027f17238ddced9bc710aa28 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Thu, 27 Feb 2014 19:20:20 +0000 Subject: [PATCH] Added width cap for unsized expressions. Unsized expressions can expand to extremely large widths. Usually this is actually a mistake in the source code, but it can lead to the compiler temporarily using extremely large amounts of memory, or in the worst case, crashing. This adds a cap on the width of unsized expressions (by default 65536 bits, but overridable by the user), and causes a warning message to be output when the cap is reached. --- compiler.h | 7 ++++++- driver/cflexor.lex | 4 +++- driver/cfparse.y | 10 +++++++++- driver/globals.h | 5 ++++- driver/iverilog.man.in | 10 ++++++++-- driver/main.c | 6 +++++- elab_expr.cc | 7 +++---- main.cc | 10 +++++++++- netmisc.cc | 23 ++++++++++++++++------- 9 files changed, 63 insertions(+), 19 deletions(-) diff --git a/compiler.h b/compiler.h index 6e4bf1cdf..63cc509b5 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef __compiler_H #define __compiler_H /* - * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -36,6 +36,11 @@ */ extern unsigned integer_width; +/* + * The width_cap is the width limit for unsized expressions. + */ +extern unsigned width_cap; + /* * This is the maximum number of recursive module loops allowed within * a generate block. diff --git a/driver/cflexor.lex b/driver/cflexor.lex index 08bb455c3..9d50c4c4f 100644 --- a/driver/cflexor.lex +++ b/driver/cflexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -92,6 +92,8 @@ int cmdfile_stack_ptr = 0; "+vhdl-libdir+" { BEGIN(PLUS_ARGS); return TOK_VHDL_LIBDIR; } +"+width-cap+" { BEGIN(PLUS_ARGS); return TOK_WIDTH_CAP; } + /* If it is not any known plus-flag, return the generic form. */ "+"[^\n \t\b\f\r+]* { cflval.text = strdup(yytext); diff --git a/driver/cfparse.y b/driver/cfparse.y index 22f127d26..13fcb85c9 100644 --- a/driver/cfparse.y +++ b/driver/cfparse.y @@ -1,6 +1,6 @@ %{ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -61,6 +61,7 @@ static void translate_file_name(char*text) %token TOK_Da TOK_Dc TOK_Dv TOK_Dy %token TOK_DEFINE TOK_INCDIR TOK_INTEGER_WIDTH TOK_LIBDIR TOK_LIBDIR_NOCASE %token TOK_LIBEXT TOK_PARAMETER TOK_TIMESCALE TOK_VHDL_WORK TOK_VHDL_LIBDIR +%token TOK_WIDTH_CAP %token TOK_PLUSARG TOK_PLUSWORD TOK_STRING %% @@ -191,6 +192,13 @@ item free(tmp); } + | TOK_WIDTH_CAP TOK_PLUSARG + { char*tmp = substitutions($2); + free($2); + width_cap = strtoul(tmp,0,10); + free(tmp); + } + /* The + tokens that are not otherwise matched, are ignored. The skip_args rule arranges for all the argument words to be consumed. */ diff --git a/driver/globals.h b/driver/globals.h index 1ef723315..23c72a380 100644 --- a/driver/globals.h +++ b/driver/globals.h @@ -1,7 +1,7 @@ #ifndef __globals_H #define __globals_H /* - * Copyright (c) 2000-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -24,6 +24,9 @@ /* This is the integer-width argument that will be passed to ivl. */ extern unsigned integer_width; + /* This is the width-cap argument that will be passed to ivl. */ +extern unsigned width_cap; + extern const char*vhdlpp_work; extern const char**vhdlpp_libdir; extern unsigned vhdlpp_libdir_cnt; diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index f6c33fa68..21d504728 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,4 +1,4 @@ -.TH iverilog 1 "April 2nd, 2013" "" "Version %M.%m.%n %E" +.TH iverilog 1 "February 26th, 2014" "" "Version %M.%m.%n %E" .SH NAME iverilog - Icarus Verilog compiler @@ -462,6 +462,12 @@ This allows the programmer to select the width for integer variables in the Verilog source. The default is 32, the value can be any desired integer value. +.TP 8 +.B +width-cap+\fIvalue\fP +This allows the programmer to select the width cap for unsized expressions. +If the calculated width for an unsized expression exceeds this value, the +compiler will issue a warning and limit the expression width to this value. + .SH "VARIABLES IN COMMAND FILES" In certain cases, iverilog supports variables in command files. These @@ -515,7 +521,7 @@ Tips on using, debugging, and developing the compiler can be found at .SH COPYRIGHT .nf -Copyright \(co 2002\-2011 Stephen Williams +Copyright \(co 2002\-2014 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff --git a/driver/main.c b/driver/main.c index b86bacba7..d0b286c47 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -141,6 +141,8 @@ char warning_flags[16] = "n"; unsigned integer_width = 32; +unsigned width_cap = 65536; + char*mod_list = 0; char*command_filename = 0; @@ -1192,6 +1194,8 @@ int main(int argc, char **argv) fprintf(iconfig_file, "iwidth:%u\n", integer_width); + fprintf(iconfig_file, "widthcap:%u\n", width_cap); + /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ diff --git a/elab_expr.cc b/elab_expr.cc index 836361199..3dc88164a 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -756,12 +756,11 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) r_val = rc->value().as_long(); // Clip to a sensible range to avoid underflow/overflow - // in the following calculations. 1024 bits should be - // enough for anyone... + // in the following calculations. if (r_val < 0) r_val = 0; - if (r_val > 1024) - r_val = 1024; + if (r_val > width_cap) + r_val = width_cap; // If the left operand is a simple unsized number, we // can calculate the actual width required for the power diff --git a/main.cc b/main.cc index 7b7e36001..5c7e040a1 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -191,6 +191,11 @@ bool verbose_flag = false; unsigned integer_width = 32; +/* + * Width limit for unsized expressions. + */ +unsigned width_cap = 65536; + int def_ts_units = 0; int def_ts_prec = 0; @@ -647,6 +652,9 @@ static void read_iconfig_file(const char*ipath) } else if (strcmp(buf, "iwidth") == 0) { integer_width = strtoul(cp,0,10); + } else if (strcmp(buf, "widthcap") == 0) { + width_cap = strtoul(cp,0,10); + } else if (strcmp(buf, "library_file") == 0) { perm_string path = filename_strings.make(cp); library_file_map[path] = true; diff --git a/netmisc.cc b/netmisc.cc index e6b414e61..91c974184 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -746,9 +746,9 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, // If context_width is positive, this is the RHS of an assignment, // so the LHS width must also be included in the width calculation. - if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) - && (expr_width < (unsigned)context_width)) - expr_width = context_width; + unsigned pos_context_width = context_width > 0 ? context_width : 0; + if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width)) + expr_width = pos_context_width; if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: test_width of " @@ -765,8 +765,8 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, // If we can get the same result using a smaller expression // width, do so. if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) - && (expr_width > (unsigned)context_width)) { - expr_width = max(pe->min_width(), (unsigned)context_width); + && (expr_width > pos_context_width)) { + expr_width = max(pe->min_width(), pos_context_width); if (debug_elaborate) { cerr << pe->get_fileline() << ": " @@ -774,6 +774,15 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, } } + if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap) + && (expr_width > pos_context_width)) { + cerr << pe->get_fileline() << ": warning: excessive unsized " + << "expression width detected." << endl; + cerr << pe->get_fileline() << ": : The expression width " + << "is capped at " << width_cap << " bits." << endl; + expr_width = width_cap; + } + unsigned flags = PExpr::NO_FLAGS; if (need_const) flags |= PExpr::NEED_CONST; @@ -789,10 +798,10 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, tmp = cast_to_real(tmp); break; case IVL_VT_BOOL: - tmp = cast_to_int2(tmp, context_width > 0 ? context_width : 0); + tmp = cast_to_int2(tmp, pos_context_width); break; case IVL_VT_LOGIC: - tmp = cast_to_int4(tmp, context_width > 0 ? context_width : 0); + tmp = cast_to_int4(tmp, pos_context_width); break; default: break;