Fix #1322: No warning on dynamic Out-Of-Bound array access

This commit is contained in:
Cookie 2026-06-01 17:08:05 +08:00
parent c7530dbcc1
commit 70f61664c1
18 changed files with 178 additions and 15 deletions

View File

@ -342,9 +342,9 @@ These flags affect the general behavior of the compiler.
* select-range
This enables warnings for constant out-of-bound selects. This includes
partial or fully out-of-bound select as well as a select containing a 'bx
or 'bz in the index.
This enables warnings for constant and runtime out-of-bound selects. This
includes partial or fully out-of-bound select as well as a select containing
a 'bx or 'bz in the index.
* timescale

View File

@ -421,8 +421,8 @@ will generate a warning.
.TP 8
.B select-range
This enables warnings for constant out of bound selects. This includes
partial or fully out of bound selects as well as a select containing
This enables warnings for constant and runtime out-of-bound selects. This
includes partial or fully out-of-bound select as well as a select containing
a 'bx or 'bz in the index.
.TP 8

View File

@ -0,0 +1 @@
PASSED

View File

@ -0,0 +1,7 @@
ivltests/unpacked_array_oob_write_warn.v:21: Warning: out-of-bounds unpacked array write index 4 for 'a[0:3]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:24: Warning: out-of-bounds unpacked array write index -1 for 'a[0:3]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:27: Warning: out-of-bounds unpacked array write index 5 for 'a[0:3]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:30: Warning: out-of-bounds unpacked array write index 3 for 'r[0:1]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:33: Warning: undefined unpacked array write index for 'a[0:3]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:36: Warning: undefined unpacked array write index for 'a[0:3]'; write ignored.
ivltests/unpacked_array_oob_write_warn.v:39: Warning: undefined unpacked array write index for 'r[0:1]'; write ignored.

View File

@ -0,0 +1 @@
PASSED

View File

@ -0,0 +1,57 @@
module main;
logic [7:0] a [0:3];
real r [0:1];
integer idx;
integer errors;
initial begin
errors = 0;
a[0] = 8'h11;
a[1] = 8'h22;
a[2] = 8'h33;
a[3] = 8'h44;
r[0] = 1.25;
r[1] = 2.50;
idx = 2;
a[idx] = 8'haa;
idx = 4;
a[idx] = 8'hff;
idx = -1;
a[idx] <= 8'hee;
idx = 5;
repeat (2) a[idx] = 8'h77;
idx = 3;
r[idx] <= 9.50;
idx = 'bx;
a[idx] = 8'h66;
idx = 'bz;
a[idx] <= 8'h55;
idx = 'bx;
r[idx] = 7.25;
#1;
if (a[0] !== 8'h11) errors = errors + 1;
if (a[1] !== 8'h22) errors = errors + 1;
if (a[2] !== 8'haa) errors = errors + 1;
if (a[3] !== 8'h44) errors = errors + 1;
if (r[0] != 1.25) errors = errors + 1;
if (r[1] != 2.50) errors = errors + 1;
if (errors) begin
$display("FAILED");
$finish(1);
end
$display("PASSED");
end
endmodule

View File

@ -380,6 +380,8 @@ test_va_math vvp_tests/test_va_math.json
test_vams_math vvp_tests/test_vams_math.json
timing_check_syntax vvp_tests/timing_check_syntax.json
timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json
unpacked_array_oob_write_nowarn vvp_tests/unpacked_array_oob_write_nowarn.json
unpacked_array_oob_write_warn vvp_tests/unpacked_array_oob_write_warn.json
uwire_fail2 vvp_tests/uwire_fail2.json
uwire_fail3 vvp_tests/uwire_fail3.json
value_range1 vvp_tests/value_range1.json

View File

@ -0,0 +1,6 @@
{
"type" : "normal",
"source" : "unpacked_array_oob_write_warn.v",
"iverilog-args" : [ "-g2005-sv", "-pfileline=1" ],
"gold" : "unpacked_array_oob_write_nowarn"
}

View File

@ -0,0 +1,6 @@
{
"type" : "normal",
"source" : "unpacked_array_oob_write_warn.v",
"iverilog-args" : [ "-g2005-sv", "-Wselect-range", "-pfileline=1" ],
"gold" : "unpacked_array_oob_write_warn"
}

View File

@ -749,6 +749,8 @@ static void read_iconfig_file(const char*ipath)
roots.push_back(lex_strings.make(cp));
} else if (strcmp(buf,"warnings") == 0) {
free(reinterpret_cast<void*>(const_cast<char*>(flags["warnings"])));
flags["warnings"] = strdup(cp);
/* Scan the warnings enable string for warning flags. */
for ( ; *cp ; cp += 1) switch (*cp) {
case 'f':

View File

@ -162,6 +162,7 @@ int target_design(ivl_design_t des)
* printed for procedural statements. (e.g. -pfileline=1).
* The default is no file/line information will be included. */
const char*fileline = ivl_design_flag(des, "fileline");
const char*warnings = ivl_design_flag(des, "warnings");
const char*debug_flags = ivl_design_flag(des, "debug_flags");
process_debug_string(debug_flags);
@ -210,6 +211,8 @@ int target_design(ivl_design_t des)
fprintf(vvp_out, ":ivl_delay_selection \"%s\";\n",
ivl_design_delay_sel(des));
if (warnings && *warnings)
fprintf(vvp_out, ":ivl_warnings \"%s\";\n", warnings);
{ int pre = ivl_design_time_precision(des);
char sign = '+';

View File

@ -40,7 +40,6 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix,
uint64_t delay,
ivl_expr_t dexp, unsigned nevents)
{
unsigned end_assign = transient_id++;
int word_ix_reg = 3;
/* if we have to evaluate a delay expression, evaluate word index into
@ -54,24 +53,23 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix,
!number_is_unknown(word_ix)) {
fprintf(vvp_out, " %%ix/load %d, %ld, 0; address\n",
word_ix_reg, get_number_immediate(word_ix));
fprintf(vvp_out, " %%flag_set/imm 4, 0;\n");
} else {
/* Calculate array word index into index register 3 */
draw_eval_expr_into_integer(word_ix, word_ix_reg);
/* Skip assignment if word expression is not defined. */
unsigned do_assign = transient_id++;
fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_assign);
fprintf(vvp_out, " %%pop/real 1;\n");
fprintf(vvp_out, " %%jmp t_%u;\n", end_assign);
fprintf(vvp_out, "t_%u ;\n", do_assign);
}
if (dexp != 0) {
/* Calculated delay... */
int delay_index = allocate_word();
int index_flag = allocate_flag();
fprintf(vvp_out, " %%flag_mov %d, 4;\n", index_flag);
draw_eval_expr_into_integer(dexp, delay_index);
fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg);
fprintf(vvp_out, " %%flag_mov 4, %d;\n", index_flag);
fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig,
delay_index);
clr_flag(index_flag);
clr_word(word_ix_reg);
clr_word(delay_index);
} else if (nevents != 0) {
@ -99,7 +97,6 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix,
}
}
fprintf(vvp_out, "t_%u ;\n", end_assign);
if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n");
}

View File

@ -47,6 +47,7 @@ extern void compile_init(void);
extern void compile_cleanup(void);
extern bool verbose_flag;
extern bool warn_array_write_oob;
/*
* If this file opened, then write debug information to this

View File

@ -77,6 +77,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base)
/* These are some special header/footer keywords. */
^":ivl_version" { return K_ivl_version; }
^":ivl_delay_selection" { return K_ivl_delay_selection; }
^":ivl_warnings" { return K_ivl_warnings; }
^":vpi_module" { return K_vpi_module; }
^":vpi_time_precision" { return K_vpi_time_precision; }
^":file_names" { return K_file_names; }

View File

@ -53,6 +53,7 @@ extern "C" const char*optarg;
#endif
bool verbose_flag = false;
bool warn_array_write_oob = false;
static int vvp_return_value = 0;
static int vvp_used = 0;
@ -214,6 +215,12 @@ void set_delay_selection(const char* sel)
delete[] sel;
}
void set_warning_flags(const char* sel)
{
warn_array_write_oob = strchr(sel, 's') != 0;
delete[] sel;
}
static void final_cleanup()
{
vvp_object::cleanup();

View File

@ -105,7 +105,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U
%token K_vpi_call K_vpi_call_w K_vpi_call_i
%token K_vpi_func K_vpi_func_r K_vpi_func_s
%token K_ivl_version K_ivl_delay_selection
%token K_ivl_version K_ivl_delay_selection K_ivl_warnings
%token K_vpi_module K_vpi_time_precision K_file_names K_file_line
%token K_PORT_INPUT K_PORT_OUTPUT K_PORT_INOUT K_PORT_MIXED K_PORT_NODIR
@ -153,6 +153,8 @@ header_line
{ verify_version($2, $3); }
| K_ivl_delay_selection T_STRING ';'
{ set_delay_selection($2); }
| K_ivl_warnings T_STRING ';'
{ set_warning_flags($2); }
| K_vpi_module T_STRING ';'
{ compile_load_vpi_module($2); }
| K_vpi_time_precision '+' T_NUMBER ';'

View File

@ -38,6 +38,11 @@ extern void verify_version(char *ivl_ver, char* commit);
*/
extern void set_delay_selection(const char* sel);
/*
* Set runtime warning classes embedded in the VVP input file.
*/
extern void set_warning_flags(const char* sel);
/*
* various functions shared by the lexor and the parser.
*/

View File

@ -20,6 +20,7 @@
# include "config.h"
# include "vthread.h"
# include "codes.h"
# include "compile.h"
# include "schedule.h"
# include "ufunc.h"
# include "event.h"
@ -1076,6 +1077,42 @@ bool of_ADD_WR(vthread_t thr, vvp_code_t)
return true;
}
static set<vvp_code_t> warned_array_write_oob;
static void maybe_warn_array_write_oob(vthread_t thr, vvp_code_t cp,
int64_t adr)
{
if (!warn_array_write_oob)
return;
assert(cp->array);
bool index_undef = thr->flags[4] == BIT4_1;
if (!index_undef && adr >= 0 && (uint64_t) adr < cp->array->get_size())
return;
if (!warned_array_write_oob.insert(cp).second)
return;
int left = cp->array->swap_addr ? cp->array->last_addr.get_value()
: cp->array->first_addr.get_value();
int right = cp->array->swap_addr ? cp->array->first_addr.get_value()
: cp->array->last_addr.get_value();
cerr << thr->get_fileline();
if (index_undef) {
cerr << "Warning: undefined unpacked array write index for '";
} else {
int64_t source_index = adr
+ (int64_t) cp->array->first_addr.get_value();
cerr << "Warning: out-of-bounds unpacked array write index "
<< source_index << " for '";
}
cerr << cp->array->name << "[" << left << ":" << right
<< "]'; write ignored." << endl;
}
/* %assign/ar <array>, <delay>
* Generate an assignment event to a real array. Index register 3
* contains the canonical address of the word in the memory. <delay>
@ -1088,6 +1125,11 @@ bool of_ASSIGN_AR(vthread_t thr, vvp_code_t cp)
unsigned delay = cp->bit_idx[0];
double value = thr->pop_real();
maybe_warn_array_write_oob(thr, cp, adr);
if (thr->flags[4] == BIT4_1)
return true;
if (adr >= 0) {
schedule_assign_array_word(cp->array, adr, value, delay);
}
@ -1105,6 +1147,11 @@ bool of_ASSIGN_ARD(vthread_t thr, vvp_code_t cp)
long adr = thr->words[3].w_int;
double value = thr->pop_real();
maybe_warn_array_write_oob(thr, cp, adr);
if (thr->flags[4] == BIT4_1)
return true;
if (adr >= 0) {
vvp_time64_t delay = thr->words[cp->bit_idx[0]].w_uint;
schedule_assign_array_word(cp->array, adr, value, delay);
@ -1125,6 +1172,11 @@ bool of_ASSIGN_ARE(vthread_t thr, vvp_code_t cp)
long adr = thr->words[3].w_int;
double value = thr->pop_real();
maybe_warn_array_write_oob(thr, cp, adr);
if (thr->flags[4] == BIT4_1)
return true;
if (adr >= 0) {
if (thr->ecount == 0) {
schedule_assign_array_word(cp->array, adr, value, 0);
@ -1210,6 +1262,8 @@ bool of_ASSIGN_VEC4_A_D(vthread_t thr, vvp_code_t cp)
vvp_vector4_t val = thr->pop_vec4();
maybe_warn_array_write_oob(thr, cp, adr);
// Abort if flags[4] is set. This can happen if the calculation
// into an index register failed.
if (thr->flags[4] == BIT4_1)
@ -1236,6 +1290,8 @@ bool of_ASSIGN_VEC4_A_E(vthread_t thr, vvp_code_t cp)
vvp_vector4_t val = thr->pop_vec4();
maybe_warn_array_write_oob(thr, cp, adr);
// Abort if flags[4] is set. This can happen if the calculation
// into an index register failed.
if (thr->flags[4] == BIT4_1)
@ -5770,6 +5826,11 @@ bool of_STORE_OBJA(vthread_t thr, vvp_code_t cp)
vvp_object_t val;
thr->pop_object(val);
maybe_warn_array_write_oob(thr, cp, adr);
if (thr->flags[4] == BIT4_1)
return true;
cp->array->set_word(adr, val);
return true;
@ -6089,8 +6150,10 @@ static bool storea(vthread_t thr, vvp_code_t cp)
ELEM val;
pop_value(thr, val, 0);
unsigned adr = thr->words[idx].w_int;
maybe_warn_array_write_oob(thr, cp, adr);
if (thr->flags[4] != BIT4_1) {
unsigned adr = thr->words[idx].w_int;
cp->array->set_word(adr, val);
}
@ -6191,6 +6254,8 @@ bool of_STORE_VEC4A(vthread_t thr, vvp_code_t cp)
long adr = adr_index? thr->words[adr_index].w_int : 0;
int64_t off = off_index ? thr->words[off_index].w_int : 0;
maybe_warn_array_write_oob(thr, cp, adr);
// Suppress action if flags-4 is true.
if (thr->flags[4] == BIT4_1) {
thr->pop_vec4(1);