From ce9f3d5e591b38814d2f6a1b01fe337ae25908dc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 17 May 2022 11:04:25 +0200 Subject: [PATCH] vvp: Add helper function for vector (partial) out-of-bounds write There are a few functions that handle implement different kinds of vector writes that have to handle that the assigned value partially or completely out-of-bounds. Each function has similar, but not identical, code for this, sometimes with small bugs for corner cases. Add a helper function that takes care of handling of updating the width and offset of the assigned value if necessary. This ensure consistent and correct behavior and allow to remove some duplicated code. Signed-off-by: Lars-Peter Clausen --- vvp/vthread.cc | 202 ++++++++++++++++--------------------------------- 1 file changed, 66 insertions(+), 136 deletions(-) diff --git a/vvp/vthread.cc b/vvp/vthread.cc index eeaff8588..5733fbc0a 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1134,6 +1134,50 @@ bool of_ASSIGN_VEC4(vthread_t thr, vvp_code_t cp) return true; } +/* + * Resizes a vector value for a partial assignment so that the value is fully + * in-bounds of the target signal. Both `val` and `off` will be updated if + * necessary. + * + * Returns false if the value is fully out-of-bounds and the assignment should + * be skipped. Otherwise returns true. + */ +static bool resize_rval_vec(vvp_vector4_t &val, int64_t &off, + unsigned int sig_wid) +{ + unsigned int wid = val.size(); + + // Fully in bounds, most likely case + if (off >= 0 && (uint64_t)off + wid <= sig_wid) + return true; + + unsigned int base = 0; + if (off >= 0) { + // Fully out-of-bounds + if ((uint64_t)off >= sig_wid) + return false; + } else { + // Fully out-of-bounds */ + if ((uint64_t)(-off) >= wid) + return false; + + // If the index is below the vector, then only assign the high + // bits that overlap with the target + base = -off; + wid += off; + off = 0; + } + + // If the value is partly above the target, then only assign + // the bits that overlap + if ((uint64_t)off + wid > sig_wid) + wid = sig_wid - (uint64_t)off; + + val = val.subvalue(base, wid); + + return true; +} + /* * %assign/vec4/a/d , , */ @@ -1143,35 +1187,19 @@ bool of_ASSIGN_VEC4_A_D(vthread_t thr, vvp_code_t cp) int del_idx = cp->bit_idx[1]; int adr_idx = 3; - long off = off_idx? thr->words[off_idx].w_int : 0; + int64_t off = off_idx ? thr->words[off_idx].w_int : 0; vvp_time64_t del = del_idx? thr->words[del_idx].w_uint : 0; long adr = thr->words[adr_idx].w_int; vvp_vector4_t val = thr->pop_vec4(); - unsigned wid = val.size(); - const unsigned array_wid = cp->array->get_word_size(); // Abort if flags[4] is set. This can happen if the calculation // into an index register failed. if (thr->flags[4] == BIT4_1) return true; - if (off >= (long)array_wid) + if (!resize_rval_vec(val, off, cp->array->get_word_size())) return true; - if (off < 0) { - if ((unsigned)-off >= wid) - return true; - - int use_off = -off; - assert(wid > (unsigned)use_off); - unsigned use_wid = wid - use_off; - val = val.subvalue(use_off, use_wid); - off = 0; - wid = use_wid; - } - if (off+wid > array_wid) { - val = val.subvalue(0, array_wid-off); - } schedule_assign_array_word(cp->array, adr, off, val, del); @@ -1186,34 +1214,18 @@ bool of_ASSIGN_VEC4_A_E(vthread_t thr, vvp_code_t cp) int off_idx = cp->bit_idx[0]; int adr_idx = 3; - long off = off_idx? thr->words[off_idx].w_int : 0; + int64_t off = off_idx ? thr->words[off_idx].w_int : 0; long adr = thr->words[adr_idx].w_int; vvp_vector4_t val = thr->pop_vec4(); - unsigned wid = val.size(); - const unsigned array_wid = cp->array->get_word_size(); // Abort if flags[4] is set. This can happen if the calculation // into an index register failed. if (thr->flags[4] == BIT4_1) return true; - if (off >= (long)array_wid) + if (!resize_rval_vec(val, off, cp->array->get_word_size())) return true; - if (off < 0) { - if ((unsigned)-off >= wid) - return true; - - int use_off = -off; - assert(wid > (unsigned)use_off); - unsigned use_wid = wid - use_off; - val = val.subvalue(use_off, use_wid); - off = 0; - wid = use_wid; - } - if (off+wid > array_wid) { - val = val.subvalue(0, array_wid-off); - } if (thr->ecount == 0) { schedule_assign_array_word(cp->array, adr, off, val, 0); @@ -1233,9 +1245,8 @@ bool of_ASSIGN_VEC4_OFF_D(vthread_t thr, vvp_code_t cp) unsigned off_index = cp->bit_idx[0]; unsigned del_index = cp->bit_idx[1]; vvp_vector4_t val = thr->pop_vec4(); - unsigned wid = val.size(); - int off = thr->words[off_index].w_int; + int64_t off = thr->words[off_index].w_int; vvp_time64_t del = thr->words[del_index].w_uint; // Abort if flags[4] is set. This can happen if the calculation @@ -1246,22 +1257,8 @@ bool of_ASSIGN_VEC4_OFF_D(vthread_t thr, vvp_code_t cp) vvp_signal_value*sig = dynamic_cast (cp->net->fil); assert(sig); - if (off >= (long)sig->value_size()) + if (!resize_rval_vec(val, off, sig->value_size())) return true; - if (off < 0) { - if ((unsigned)-off >= wid) - return true; - - int use_off = -off; - assert(wid > (unsigned)use_off); - unsigned use_wid = wid - use_off; - val = val.subvalue(use_off, use_wid); - off = 0; - wid = use_wid; - } - if (off+wid > sig->value_size()) { - val = val.subvalue(0, sig->value_size()-off); - } schedule_assign_vector(ptr, off, sig->value_size(), val, del); return true; @@ -1275,9 +1272,8 @@ bool of_ASSIGN_VEC4_OFF_E(vthread_t thr, vvp_code_t cp) vvp_net_ptr_t ptr (cp->net, 0); unsigned off_index = cp->bit_idx[0]; vvp_vector4_t val = thr->pop_vec4(); - unsigned wid = val.size(); - int off = thr->words[off_index].w_int; + int64_t off = thr->words[off_index].w_int; // Abort if flags[4] is set. This can happen if the calculation // into an index register failed. @@ -1287,22 +1283,8 @@ bool of_ASSIGN_VEC4_OFF_E(vthread_t thr, vvp_code_t cp) vvp_signal_value*sig = dynamic_cast (cp->net->fil); assert(sig); - if (off >= (long)sig->value_size()) + if (!resize_rval_vec(val, off, sig->value_size())) return true; - if (off < 0) { - if ((unsigned)-off >= wid) - return true; - - int use_off = -off; - assert((int)wid > use_off); - unsigned use_wid = wid - use_off; - val = val.subvalue(use_off, use_wid); - off = 0; - wid = use_wid; - } - if (off+wid > sig->value_size()) { - val = val.subvalue(0, sig->value_size()-off); - } if (thr->ecount == 0) { schedule_assign_vector(ptr, off, sig->value_size(), val, 0); @@ -5455,58 +5437,31 @@ bool of_RET_VEC4(vthread_t thr, vvp_code_t cp) { size_t index = cp->number; unsigned off_index = cp->bit_idx[0]; - int wid = cp->bit_idx[1]; + unsigned int wid = cp->bit_idx[1]; vvp_vector4_t&val = thr->peek_vec4(); vthread_t fun_thr = get_func(thr); assert(index < get_max(fun_thr, val)); unsigned depth = get_depth(fun_thr, index, val); - int off = off_index? thr->words[off_index].w_int : 0; - const int sig_value_size = fun_thr->parent->peek_vec4(depth).size(); - - unsigned val_size = val.size(); + int64_t off = off_index ? thr->words[off_index].w_int : 0; + unsigned int sig_value_size = fun_thr->parent->peek_vec4(depth).size(); if (off_index!=0 && thr->flags[4] == BIT4_1) { thr->pop_vec4(1); return true; } - if (off <= -wid) { + if (!resize_rval_vec(val, off, sig_value_size)) { thr->pop_vec4(1); return true; } - if (off >= sig_value_size) { - thr->pop_vec4(1); - return true; - } - - // If the index is below the vector, then only assign the high - // bits that overlap with the target - if (off < 0) { - int use_off = -off; - wid -= use_off; - val = val.subvalue(use_off, wid); - val_size = wid; - off = 0; - } - - // If the value is partly above the target, then only assign - // the bits that overlap - if ((off+wid) > sig_value_size) { - wid = sig_value_size - off; - val = val.subvalue(0, wid); - val.resize(wid); - val_size = wid; - } - - if (off==0 && val_size==(unsigned)sig_value_size) { + if (off == 0 && val.size() == sig_value_size) { fun_thr->parent->poke_vec4(depth, val); - } else { vvp_vector4_t tmp_dst = fun_thr->parent->peek_vec4(depth); - assert(wid>=0 && val.size() == (unsigned)wid); + assert(val.size() == wid); tmp_dst.set_vec(off, val); fun_thr->parent->poke_vec4(depth, tmp_dst); } @@ -6247,23 +6202,22 @@ bool of_STORE_VEC4(vthread_t thr, vvp_code_t cp) vvp_net_ptr_t ptr(cp->net, 0); vvp_signal_value*sig = dynamic_cast (cp->net->fil); unsigned off_index = cp->bit_idx[0]; - int wid = cp->bit_idx[1]; + unsigned int wid = cp->bit_idx[1]; - int off = off_index? thr->words[off_index].w_int : 0; - const int sig_value_size = sig->value_size(); + int64_t off = off_index ? thr->words[off_index].w_int : 0; + unsigned int sig_value_size = sig->value_size(); vvp_vector4_t&val = thr->peek_vec4(); unsigned val_size = val.size(); - if ((int)val_size < wid) { + if (val_size < wid) { cerr << thr->get_fileline() << "XXXX Internal error: val.size()=" << val_size << ", expecting >= " << wid << endl; } - assert((int)val_size >= wid); - if ((int)val_size > wid) { + assert(val_size >= wid); + if (val_size > wid) { val.resize(wid); - val_size = wid; } // If there is a problem loading the index register, flags-4 @@ -6273,36 +6227,12 @@ bool of_STORE_VEC4(vthread_t thr, vvp_code_t cp) return true; } - if (off <= -wid) { - thr->pop_vec4(1); - return true; - } - if (off >= sig_value_size) { + if (!resize_rval_vec(val, off, sig_value_size)) { thr->pop_vec4(1); return true; } - // If the index is below the vector, then only assign the high - // bits that overlap with the target. - if (off < 0) { - int use_off = -off; - wid -= use_off; - val = val.subvalue(use_off, wid); - val_size = wid; - off = 0; - } - - // If the value is partly above the target, then only assign - // the bits that overlap. - if ((off+wid) > sig_value_size) { - wid = sig_value_size - off; - val = val.subvalue(0, wid); - val.resize(wid); - val_size = wid; - } - - - if (off==0 && val_size==(unsigned)sig_value_size) + if (off == 0 && val.size() == sig_value_size) vvp_send_vec4(ptr, val, thr->wt_context); else vvp_send_vec4_pv(ptr, val, off, sig_value_size, thr->wt_context);