From bfc9cd8715dd5bc2711bf518b07a8843fbf20b2b Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 10 Jan 2016 17:09:33 -0800 Subject: [PATCH] Make real functions in processes use parent stack for return value. --- tgt-vvp/draw_ufunc.c | 7 ++---- tgt-vvp/stmt_assign.c | 12 +++++++++ tgt-vvp/vvp_scope.c | 10 ++++++++ vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/opcodes.txt | 14 ++++++++++- vvp/vthread.cc | 58 ++++++++++++++++++++++++++++++++++--------- 7 files changed, 85 insertions(+), 18 deletions(-) diff --git a/tgt-vvp/draw_ufunc.c b/tgt-vvp/draw_ufunc.c index 1101b192c..f5903fff3 100644 --- a/tgt-vvp/draw_ufunc.c +++ b/tgt-vvp/draw_ufunc.c @@ -205,11 +205,8 @@ void draw_ufunc_real(ivl_expr_t expr) /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); - /* Return value signal cannot be an array. */ - assert(ivl_signal_dimensions(retval) == 0); - - /* Load the result into a word. */ - fprintf(vvp_out, " %%load/real v%p_0;\n", retval); + /* The %callf/real function emitted by the preamble leaves + the result in the stack for us. */ draw_ufunc_epilogue(expr); } diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index 7a788e807..8804c6ca2 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -574,6 +574,18 @@ static int show_stmt_assign_sig_real(ivl_statement_t net) var = ivl_lval_sig(lval); assert(var != 0); + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + ivl_scope_t sig_scope = ivl_signal_scope(var); + if ((ivl_scope_type(sig_scope) == IVL_SCT_FUNCTION) + && (strcmp(ivl_signal_basename(var), ivl_scope_basename(sig_scope)) == 0)) { + assert(ivl_signal_dimensions(var) == 0); + fprintf(vvp_out, " %%ret/real 0; Assign to %s\n", + ivl_signal_basename(var)); + return 0; + } + if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); return 0; diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 65fe9938f..57459498f 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -449,6 +449,16 @@ static void draw_reg_in_scope(ivl_signal_t sig) break; } + /* Special Case: If this variable is the return value of a function, + then it need to exist as an actual variable. */ + if ((ivl_signal_data_type(sig)==IVL_VT_REAL) + && (ivl_scope_type(ivl_signal_scope(sig))==IVL_SCT_FUNCTION) + && (strcmp(ivl_signal_basename(sig),ivl_scope_basename(ivl_signal_scope(sig))) == 0)) { + fprintf(vvp_out, "; Variable %s is REAL return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + const char *datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; const char *local_flag = local_flag_str(sig); diff --git a/vvp/codes.h b/vvp/codes.h index 4fa0b560e..8af97689b 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -198,6 +198,7 @@ extern bool of_RELEASE_NET(vthread_t thr, vvp_code_t code); extern bool of_RELEASE_REG(vthread_t thr, vvp_code_t code); extern bool of_RELEASE_WR(vthread_t thr, vvp_code_t code); extern bool of_REPLICATE(vthread_t thr, vvp_code_t code); +extern bool of_RET_REAL(vthread_t thr, vvp_code_t code); extern bool of_SCOPY(vthread_t thr, vvp_code_t code); extern bool of_SET_DAR_OBJ_REAL(vthread_t thr, vvp_code_t code); extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index c0b25a171..9422c5619 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -249,6 +249,7 @@ static const struct opcode_table_s opcode_table[] = { { "%release/reg",of_RELEASE_REG,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} }, { "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} }, diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index d5adfa4a4..b82f88719 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -225,7 +225,7 @@ out, then this function is a no-op. * %callf/void , More directly implement function calling. This subsumes the %fork and -%join of the mroe general task and block calling, but is optimized for +%join of the more general task and block calling, but is optimized for functions, which are threads of a special, constrained sort. The different variants reflect the different return types for the @@ -1004,6 +1004,18 @@ Pop the vec4 value, replicate it times, then push the result. In other words, push the concatenation of copies. See also the %concat instruction. +* %ret/obj +* %ret/real +* %ret/str +* %ret/vec4 + +Write a value to the indexed function argument. The value is popped +from the appropriate stack and written into the argument. The return +value of a function is the first argument of the appropriate type, so +for example to store the return value for a real function, use +"%ret/real 0;". It is up to the function caller to set up the argument +references. + * %set/dar/obj/real * %set/dar/obj/str * %set/dar/obj/vec4 diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 032bce64c..df0714295 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -111,6 +111,11 @@ struct vthread_s { uint64_t w_uint; } words[WORDS_COUNT]; + // These vectors are depths within the parent thread's + // corresponding stack. This is how the %ret/* instructions + // get at parent thread arguments. + vector args_real; + private: vectorstack_vec4_; public: @@ -167,6 +172,12 @@ struct vthread_s { unsigned use_index = stack_real_.size()-1-depth; return stack_real_[use_index]; } + inline void poke_real(unsigned depth, double val) + { + assert(depth < stack_real_.size()); + unsigned use_index = stack_real_.size()-1-depth; + stack_real_[use_index] = val; + } inline void pop_real(unsigned cnt) { while (cnt > 0) { @@ -1270,11 +1281,10 @@ bool of_BREAKPOINT(vthread_t, vvp_code_t) * %callf/void , * Combine the %fork and %join steps for invoking a function. */ -static bool do_callf_void(vthread_t thr, vvp_code_t cp) +static bool do_callf_void(vthread_t thr, vthread_t child) { - vthread_t child = vthread_new(cp->cptr2, cp->scope); - if (cp->scope->is_automatic()) { + if (child->parent_scope->is_automatic()) { /* The context allocated for this child is the top entry on the write context stack */ child->wt_context = thr->wt_context; @@ -1289,7 +1299,7 @@ static bool do_callf_void(vthread_t thr, vvp_code_t cp) // Execute the function. This SHOULD run the function to completion, // but there are some exceptional situations where it won't. - assert(cp->scope->get_type_code() == vpiFunction); + assert(child->parent_scope->get_type_code() == vpiFunction); thr->task_func_children.insert(child); child->is_scheduled = 1; child->i_am_in_function = 1; @@ -1309,36 +1319,44 @@ static bool do_callf_void(vthread_t thr, vvp_code_t cp) bool of_CALLF_OBJ(vthread_t thr, vvp_code_t cp) { - return do_callf_void(thr, cp); + vthread_t child = vthread_new(cp->cptr2, cp->scope); + return do_callf_void(thr, child); // XXXX NOT IMPLEMENTED } bool of_CALLF_REAL(vthread_t thr, vvp_code_t cp) { - // XXXX Here, I should arrange for a reference to the destination variable - // XXXX as a place in my stack. The function will write to that place in - // XXXX my stack for me. - return do_callf_void(thr, cp); + vthread_t child = vthread_new(cp->cptr2, cp->scope); + + // This is the return value. Push a place-holder value. The function + // will replace this with the actual value using a %ret/real instruction. + thr->push_real(0.0); + child->args_real.push_back(0); + + return do_callf_void(thr, child); } bool of_CALLF_STR(vthread_t thr, vvp_code_t cp) { - return do_callf_void(thr, cp); + vthread_t child = vthread_new(cp->cptr2, cp->scope); + return do_callf_void(thr, child); // XXXX NOT IMPLEMENTED } bool of_CALLF_VEC4(vthread_t thr, vvp_code_t cp) { - return do_callf_void(thr, cp); + vthread_t child = vthread_new(cp->cptr2, cp->scope); + return do_callf_void(thr, child); // XXXX NOT IMPLEMENTED } bool of_CALLF_VOID(vthread_t thr, vvp_code_t cp) { - return do_callf_void(thr, cp); + vthread_t child = vthread_new(cp->cptr2, cp->scope); + return do_callf_void(thr, child); } /* @@ -4930,6 +4948,22 @@ bool of_REPLICATE(vthread_t thr, vvp_code_t cp) return true; } +/* + * %ret/real + */ +bool of_RET_REAL(vthread_t thr, vvp_code_t cp) +{ + size_t index = cp->number; + double val = thr->pop_real(); + + assert(index >= 0 && index < thr->args_real.size()); + unsigned depth = thr->args_real[index]; + // Use the depth to put the value into the stack of + // the parent thread. + thr->parent->poke_real(depth, val); + return true; +} + bool of_SCOPY(vthread_t thr, vvp_code_t) { vvp_object_t tmp;