Add ability to read back return value / Add vec4 stacked user functions.

Also:
- handle functions as arguments to system tasks.
- Cleanup detect of signal as return value.
This commit is contained in:
Stephen Williams 2016-01-24 18:36:26 -08:00
parent 0c91a6b041
commit e435a879fc
15 changed files with 268 additions and 30 deletions

View File

@ -191,9 +191,6 @@ void draw_ufunc_vec4(ivl_expr_t expr)
/* Take in arguments to function and call function code. */ /* Take in arguments to function and call function code. */
draw_ufunc_preamble(expr); draw_ufunc_preamble(expr);
assert(ivl_signal_dimensions(retval) == 0);
fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval);
draw_ufunc_epilogue(expr); draw_ufunc_epilogue(expr);
} }

View File

@ -99,6 +99,12 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result,
/* This should never happen since we have IVL_EX_SELECT. */ /* This should never happen since we have IVL_EX_SELECT. */
return 0; return 0;
} else if (signal_is_return_value(ivl_expr_signal(expr))) {
/* If the signal is the return value of a function,
then this can't be handled as a true signal, so
fall back on general expression processing. */
return 0;
} else if (ivl_expr_signed(expr) != } else if (ivl_expr_signed(expr) !=
ivl_signal_signed(ivl_expr_signal(expr))) { ivl_signal_signed(ivl_expr_signal(expr))) {
return 0; return 0;

View File

@ -1005,6 +1005,15 @@ static void draw_signal_vec4(ivl_expr_t expr)
{ {
ivl_signal_t sig = ivl_expr_signal(expr); ivl_signal_t sig = ivl_expr_signal(expr);
/* Special Case: If the signal is the return value of the function,
then use a different opcode to get the value. */
if (signal_is_return_value(sig)) {
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, " %%retload/vec4 0; Load %s (draw_signal_vec4)\n",
ivl_signal_basename(sig));
return;
}
/* Handle the simple case, a signal expression that is a /* Handle the simple case, a signal expression that is a
simple vector, no array dimensions. */ simple vector, no array dimensions. */
if (ivl_signal_dimensions(sig) == 0) { if (ivl_signal_dimensions(sig) == 0) {

View File

@ -209,6 +209,17 @@ static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice,
ivl_signal_t sig = ivl_lval_sig(lval); ivl_signal_t sig = ivl_lval_sig(lval);
int part_off_idx; int part_off_idx;
/* 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. */
if (signal_is_return_value(sig)) {
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, " %%ret/vec4 0; Assign to %s\n",
ivl_signal_basename(sig));
return;
}
/* If the slice of the l-value is a BOOL variable, then cast /* If the slice of the l-value is a BOOL variable, then cast
the data to a BOOL vector so that the stores can be valid. */ the data to a BOOL vector so that the stores can be valid. */
if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { if (ivl_signal_data_type(sig) == IVL_VT_BOOL) {
@ -345,6 +356,16 @@ static void store_vec4_to_lval(ivl_statement_t net)
if (lidx+1 < ivl_stmt_lvals(net)) if (lidx+1 < ivl_stmt_lvals(net))
fprintf(vvp_out, " %%split/vec4 %u;\n", lwid); fprintf(vvp_out, " %%split/vec4 %u;\n", lwid);
/* 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. */
if (signal_is_return_value(lsig)) {
assert(ivl_signal_dimensions(lsig) == 0);
fprintf(vvp_out, " %%ret/vec4 0; Assign to %s (store_vec4_to_lval)\n",
ivl_signal_basename(lsig));
continue;
}
if (word_ex) { if (word_ex) {
/* Handle index into an array */ /* Handle index into an array */
int word_index = allocate_word(); int word_index = allocate_word();

View File

@ -208,6 +208,10 @@ extern void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix);
*/ */
extern int draw_eval_condition(ivl_expr_t expr); extern int draw_eval_condition(ivl_expr_t expr);
/*
* Return true if the signal is the return value of a function.
*/
extern int signal_is_return_value(ivl_signal_t sig);
extern int number_is_unknown(ivl_expr_t ex); extern int number_is_unknown(ivl_expr_t ex);
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok); extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok);

View File

@ -290,6 +290,16 @@ static unsigned is_netlist_signal(ivl_net_logic_t net, ivl_nexus_t nex)
return rtn; return rtn;
} }
int signal_is_return_value(ivl_signal_t sig)
{
ivl_scope_t sig_scope = ivl_signal_scope(sig);
if (ivl_scope_type(sig_scope) != IVL_SCT_FUNCTION)
return 0;
if (strcmp(ivl_signal_basename(sig), ivl_scope_basename(sig_scope)) == 0)
return 1;
return 0;
}
/* /*
* This tests a bufz device against an output receiver, and determines * This tests a bufz device against an output receiver, and determines
* if the device can be skipped. If this function returns false, then a * if the device can be skipped. If this function returns false, then a
@ -458,6 +468,13 @@ static void draw_reg_in_scope(ivl_signal_t sig)
ivl_signal_basename(sig), ivl_signal_scope(sig)); ivl_signal_basename(sig), ivl_signal_scope(sig));
return; return;
} }
if ((ivl_signal_data_type(sig)==IVL_VT_LOGIC)
&& (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 vec4 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" : const char *datatype_flag = ivl_signal_integer(sig) ? "/i" :
ivl_signal_signed(sig)? "/s" : ""; ivl_signal_signed(sig)? "/s" : "";

View File

@ -702,11 +702,14 @@ STRUCTURAL FUNCTION CALLS:
The .ufunc statements define a call to a user defined function. The .ufunc statements define a call to a user defined function.
<label> .ufunc <flabel>, <wid>, <label> .ufunc/real <flabel>, <wid>,
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>; <isymbols> ( <psymbols> ) <ssymbol>;
<label> .ufunc/vec4 <flabel>, <wid>,
<isymbols> ( <psymbols> ) <ssymbol>;
<label> .ufunc/e <flabel>, <wid>, <trigger>, <label> .ufunc/e <flabel>, <wid>, <trigger>,
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>; <isymbols> ( <psymbols> ) <ssymbol>;
The first variant is used for functions that only need to be called The first variant is used for functions that only need to be called
when one of their inputs changes value. The second variant is used when one of their inputs changes value. The second variant is used
@ -729,10 +732,6 @@ list. The <psymbols> are variables that represent the input ports for
the function. The ufunc performs an assignment to these variables the function. The ufunc performs an assignment to these variables
before calling the function. before calling the function.
The <rsymbol> is the variable within the function where the result
will be found when the function code ends. This value is picked up
and propagated to the output of the functor.
The <ssymbol> is the function scope name. The <ssymbol> is the function scope name.
THREAD STATEMENTS: THREAD STATEMENTS:

View File

@ -199,6 +199,9 @@ 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_RELEASE_WR(vthread_t thr, vvp_code_t code);
extern bool of_REPLICATE(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_RET_REAL(vthread_t thr, vvp_code_t code);
extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code);
extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code);
extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code);
extern bool of_SCOPY(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_REAL(vthread_t thr, vvp_code_t code);
extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code); extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code);

View File

@ -250,6 +250,9 @@ static const struct opcode_table_s opcode_table[] = {
{ "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} },
{ "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%ret/vec4", of_RET_VEC4, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%scopy", of_SCOPY, 0, {OA_NONE, 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/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} }, { "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} },

View File

@ -209,6 +209,7 @@ static char* strdupnew(char const *str)
".tranif1" { return K_TRANIF1; } ".tranif1" { return K_TRANIF1; }
".tranvp" { return K_TRANVP; } ".tranvp" { return K_TRANVP; }
".ufunc/real" { return K_UFUNC_REAL; } ".ufunc/real" { return K_UFUNC_REAL; }
".ufunc/vec4" { return K_UFUNC_VEC4; }
".ufunc/e" { return K_UFUNC_E; } ".ufunc/e" { return K_UFUNC_E; }
".var" { return K_VAR; } ".var" { return K_VAR; }
".var/cobj" { return K_VAR_COBJECT; } ".var/cobj" { return K_VAR_COBJECT; }

View File

@ -1016,6 +1016,11 @@ 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 "%ret/real 0;". It is up to the function caller to set up the argument
references. references.
* %retload/vec4 <index>
Read a value from the indexed function argument. The value is read
from the argument and pushed to the appropriate stack.
* %set/dar/obj/real <index> * %set/dar/obj/real <index>
* %set/dar/obj/str <index> * %set/dar/obj/str <index>
* %set/dar/obj/vec4 <index> * %set/dar/obj/vec4 <index>

View File

@ -95,7 +95,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS %token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS
%token K_SUBSTITUTE %token K_SUBSTITUTE
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP %token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
%token K_UFUNC_REAL K_UFUNC_E K_UDP K_UDP_C K_UDP_S %token K_UFUNC_REAL K_UFUNC_VEC4 K_UFUNC_E K_UDP K_UDP_C K_UDP_S
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY %token K_VAR K_VAR_COBJECT K_VAR_DARRAY
%token K_VAR_QUEUE %token K_VAR_QUEUE
%token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U %token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U
@ -257,6 +257,9 @@ statement
| T_LABEL K_UFUNC_REAL T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';' | T_LABEL K_UFUNC_REAL T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
{ compile_ufunc_real($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); } { compile_ufunc_real($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
| T_LABEL K_UFUNC_VEC4 T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
{ compile_ufunc_vec4($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' symbols '(' symbols ')' T_SYMBOL ';' | T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' symbols '(' symbols ')' T_SYMBOL ';'
{ compile_ufunc_vec4($1, $3, $5, $9.cnt, $9.vect, $11.cnt, $11.vect, $13, $7); } { compile_ufunc_vec4($1, $3, $5, $9.cnt, $9.vect, $11.cnt, $11.vect, $13, $7); }

View File

@ -38,6 +38,30 @@
#include <windows.h> #include <windows.h>
#endif #endif
class ufunc_real : public ufunc_core {
public:
ufunc_real(unsigned ow, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports,
vvp_code_t start_address,
__vpiScope*call_scope,
char*scope_label);
~ufunc_real();
void finish_thread();
};
class ufunc_vec4 : public ufunc_core {
public:
ufunc_vec4(unsigned ow, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports,
vvp_code_t start_address,
__vpiScope*call_scope,
char*scope_label);
~ufunc_vec4();
void finish_thread();
};
ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr, ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports, unsigned nports, vvp_net_t**ports,
vvp_code_t sa, __vpiScope*call_scope__, vvp_code_t sa, __vpiScope*call_scope__,
@ -114,6 +138,18 @@ void ufunc_core::finish_thread_real_()
thread_ = 0; thread_ = 0;
} }
void ufunc_core::finish_thread_vec4_()
{
assert(thread_);
vvp_vector4_t val = vthread_get_vec4_stack(thread_, 0);
vthread_pop_vec4(thread_, 1);
propagate_vec4(val);
thread_ = 0;
}
/* /*
* This method is only called when a trigger event occurs. Just arrange for * This method is only called when a trigger event occurs. Just arrange for
* the function to be called. * the function to be called.
@ -147,6 +183,24 @@ void ufunc_core::invoke_thread_()
} }
} }
ufunc_vec4::ufunc_vec4(unsigned ow, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports,
vvp_code_t start_address,
__vpiScope*call_scope,
char*scope_label)
: ufunc_core(ow, ptr, nports, ports, start_address, call_scope, scope_label)
{
}
ufunc_vec4::~ufunc_vec4()
{
}
void ufunc_vec4::finish_thread()
{
finish_thread_vec4_();
}
ufunc_real::ufunc_real(unsigned ow, vvp_net_t*ptr, ufunc_real::ufunc_real(unsigned ow, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports, unsigned nports, vvp_net_t**ports,
vvp_code_t start_address, vvp_code_t start_address,
@ -195,8 +249,8 @@ void compile_ufunc_real(char*label, char*code, unsigned wid,
last instruction is the usual %end. So the thread looks last instruction is the usual %end. So the thread looks
like this: like this:
%exec_ufunc <core>; %exec_ufunc/real <core>;
%reap_ufunc <core>; %reap_ufunc;
%end; %end;
The %exec_ufunc copies the input values into local regs The %exec_ufunc copies the input values into local regs
@ -251,8 +305,70 @@ void compile_ufunc_vec4(char*label, char*code, unsigned wid,
unsigned portc, struct symb_s*portv, unsigned portc, struct symb_s*portv,
char*scope_label, char*trigger_label) char*scope_label, char*trigger_label)
{ {
// XXXX NOT IMPLEMENTED /* The input argument list and port list must have the same
assert(0); sizes, since internally we will be mapping the inputs list
to the ports list. */
assert(argc == portc);
__vpiScope*call_scope = vpip_peek_current_scope();
assert(call_scope);
/* Construct some phantom code that is the thread of the
function call. The first instruction, at the start_address
of the function, loads the ports and calls the function.
The second instruction collects the function result. The
last instruction is the usual %end. So the thread looks
like this:
%exec_ufunc/vec4 <core>;
%reap_ufunc;
%end;
The %exec_ufunc copies the input values into local regs
and runs the function code. The %reap_ufunc then copies
the output value to the destination net functor. */
vvp_code_t exec_code = codespace_allocate();
exec_code->opcode = of_EXEC_UFUNC_VEC4;
code_label_lookup(exec_code, code, false);
vvp_code_t reap_code = codespace_allocate();
reap_code->opcode = of_REAP_UFUNC;
vvp_code_t end_code = codespace_allocate();
end_code->opcode = &of_END;
/* Run through the function ports (which are related to but
not the same as the input ports) and arrange for their
binding. */
vvp_net_t**ports = new vvp_net_t*[portc];
for (unsigned idx = 0 ; idx < portc ; idx += 1) {
functor_ref_lookup(&ports[idx], portv[idx].text);
}
/* Create the output functor and attach it to the label. Tell
it about the start address of the code stub, and the scope
that will contain the execution. */
vvp_net_t*ptr = new vvp_net_t;
ufunc_core*fcore = new ufunc_vec4(wid, ptr, portc, ports,
exec_code, call_scope,
scope_label);
ptr->fun = fcore;
define_functor_symbol(label, ptr);
free(label);
exec_code->ufunc_core_ptr = fcore;
reap_code->ufunc_core_ptr = fcore;
wide_inputs_connect(fcore, argc, argv);
/* If this function has a trigger event, connect the functor to
that event. */
if (trigger_label)
input_connect(ptr, 0, trigger_label);
free(argv);
free(portv);
} }
#ifdef CHECK_WITH_VALGRIND #ifdef CHECK_WITH_VALGRIND
static map<ufunc_core*, bool> ufunc_map; static map<ufunc_core*, bool> ufunc_map;

View File

@ -70,6 +70,7 @@ class ufunc_core : public vvp_wide_fun_core {
protected: protected:
void finish_thread_real_(); void finish_thread_real_();
void finish_thread_vec4_();
private: private:
void recv_vec4_from_inputs(unsigned port); void recv_vec4_from_inputs(unsigned port);
@ -94,16 +95,4 @@ class ufunc_core : public vvp_wide_fun_core {
vvp_code_t code_; vvp_code_t code_;
}; };
class ufunc_real : public ufunc_core {
public:
ufunc_real(unsigned ow, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports,
vvp_code_t start_address,
__vpiScope*call_scope,
char*scope_label);
~ufunc_real();
void finish_thread();
};
#endif /* IVL_ufunc_H */ #endif /* IVL_ufunc_H */

View File

@ -115,6 +115,7 @@ struct vthread_s {
// corresponding stack. This is how the %ret/* instructions // corresponding stack. This is how the %ret/* instructions
// get at parent thread arguments. // get at parent thread arguments.
vector<unsigned> args_real; vector<unsigned> args_real;
vector<unsigned> args_vec4;
private: private:
vector<vvp_vector4_t>stack_vec4_; vector<vvp_vector4_t>stack_vec4_;
@ -143,6 +144,12 @@ struct vthread_s {
assert(use_index >= 1); assert(use_index >= 1);
return stack_vec4_[use_index-1]; return stack_vec4_[use_index-1];
} }
inline void poke_vec4(unsigned depth, const vvp_vector4_t&val)
{
assert(depth < stack_vec4_.size());
unsigned use_index = stack_vec4_.size()-1-depth;
stack_vec4_[use_index] = val;
}
inline void pop_vec4(unsigned cnt) inline void pop_vec4(unsigned cnt)
{ {
while (cnt > 0) { while (cnt > 0) {
@ -1348,9 +1355,13 @@ bool of_CALLF_STR(vthread_t thr, vvp_code_t cp)
bool of_CALLF_VEC4(vthread_t thr, vvp_code_t cp) bool of_CALLF_VEC4(vthread_t thr, vvp_code_t cp)
{ {
vthread_t child = vthread_new(cp->cptr2, cp->scope); vthread_t child = vthread_new(cp->cptr2, cp->scope);
return do_callf_void(thr, child);
// XXXX NOT IMPLEMENTED // 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_vec4(vvp_vector4_t());
child->args_vec4.push_back(0);
return do_callf_void(thr, child);
} }
bool of_CALLF_VOID(vthread_t thr, vvp_code_t cp) bool of_CALLF_VOID(vthread_t thr, vvp_code_t cp)
@ -4964,6 +4975,47 @@ bool of_RET_REAL(vthread_t thr, vvp_code_t cp)
return true; return true;
} }
/*
* %ret/vec4 <index>
*/
bool of_RET_VEC4(vthread_t thr, vvp_code_t cp)
{
size_t index = cp->number;
vvp_vector4_t val = thr->pop_vec4();
assert(index >= 0 && index < thr->args_vec4.size());
unsigned depth = thr->args_vec4[index];
// Use the depth to put the value into the stack of
// the parent thread.
thr->parent->poke_vec4(depth, val);
return true;
}
/*
* %retload/real <index>
*/
bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t cp)
{
// NOT IMPLEMENTED
assert(0);
return true;
}
/*
* %retload/vec4 <index>
*/
bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t cp)
{
size_t index = cp->number;
assert(index >= 0 && index < thr->args_vec4.size());
unsigned depth = thr->args_vec4[index];
// Use the depth to put the value into the stack of
// the parent thread.
thr->push_vec4(thr->parent->peek_vec4(depth));
return true;
}
bool of_SCOPY(vthread_t thr, vvp_code_t) bool of_SCOPY(vthread_t thr, vvp_code_t)
{ {
vvp_object_t tmp; vvp_object_t tmp;
@ -5971,7 +6023,20 @@ bool of_EXEC_UFUNC_REAL(vthread_t thr, vvp_code_t cp)
/* Create a temporary thread and run it immediately. */ /* Create a temporary thread and run it immediately. */
vthread_t child = vthread_new(cp->cptr, child_scope); vthread_t child = vthread_new(cp->cptr, child_scope);
thr->push_real(0.0); thr->push_real(0.0);
child->args_real.push_back(0.0); child->args_real.push_back(0);
return do_exec_ufunc(thr, cp, child);
}
bool of_EXEC_UFUNC_VEC4(vthread_t thr, vvp_code_t cp)
{
__vpiScope*child_scope = cp->ufunc_core_ptr->func_scope();
assert(child_scope);
/* Create a temporary thread and run it immediately. */
vthread_t child = vthread_new(cp->cptr, child_scope);
thr->push_vec4(vvp_vector4_t());
child->args_vec4.push_back(0);
return do_exec_ufunc(thr, cp, child); return do_exec_ufunc(thr, cp, child);
} }