Fix for br948 : user-func call may return incorrect value in a CA context.

%exec_ufunc assumed that because a function can never block, a call to
vthread_run() on the function code would only return when the final %end
instruction had been executed. This is not true if the function contains
a named block, which will be executed via a %fork instruction, allowing
the main function thread to suspend after a %join instruction. The fix
is to break %exec_ufunc into two instructions, the first setting the
function inputs and executing the function code, the second collecting
the function result. This provides the opportunity for the parent thread
to suspend after the %exec_ufunc instruction until all its children have
completed.
This commit is contained in:
Martin Whitaker 2014-03-01 23:25:35 +00:00
parent 3c660d04f3
commit 3e41a93f3e
3 changed files with 39 additions and 15 deletions

View File

@ -218,6 +218,7 @@ extern bool of_XORR(vthread_t thr, vvp_code_t code);
extern bool of_ZOMBIE(vthread_t thr, vvp_code_t code);
extern bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t code);
extern bool of_REAP_UFUNC(vthread_t thr, vvp_code_t code);
extern bool of_CHUNK_LINK(vthread_t thr, vvp_code_t code);

View File

@ -173,20 +173,25 @@ void compile_ufunc(char*label, char*code, unsigned wid,
/* Construct some phantom code that is the thread of the
function call. The first instruction, at the start_address
of the function, loads the points and calls the function.
The last instruction is the usual %end. So the thread looks
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 <core>;
%reap_ufunc <core>;
%end;
The %exec_ufunc copies the input values into local regs,
runs the function code, then copies the output values to
the destination net functors. */
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 start_code = codespace_allocate();
start_code->opcode = of_EXEC_UFUNC;
code_label_lookup(start_code, code);
vvp_code_t exec_code = codespace_allocate();
exec_code->opcode = of_EXEC_UFUNC;
code_label_lookup(exec_code, code);
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;
@ -204,13 +209,14 @@ void compile_ufunc(char*label, char*code, unsigned wid,
that will contain the execution. */
vvp_net_t*ptr = new vvp_net_t;
ufunc_core*fcore = new ufunc_core(wid, ptr, portc, ports,
start_code, call_scope,
exec_code, call_scope,
retv.text, scope_label);
ptr->fun = fcore;
define_functor_symbol(label, ptr);
free(label);
start_code->ufunc_core_ptr = fcore;
exec_code->ufunc_core_ptr = fcore;
reap_code->ufunc_core_ptr = fcore;
wide_inputs_connect(fcore, argc, argv);

View File

@ -5755,23 +5755,40 @@ bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp)
atomically. */
cp->ufunc_core_ptr->assign_bits_to_ports(child_context);
/* Create a temporary thread and run it immediately. A function
may not contain any blocking statements, so vthread_run() can
only return when the %end opcode is reached. */
/* Create a temporary thread and run it immediately. */
vthread_t child = vthread_new(cp->cptr, child_scope);
child->wt_context = child_context;
child->rd_context = child_context;
child->parent = thr;
child->is_scheduled = 1;
vthread_run(child);
running_thread = thr;
/* Now copy the output from the result variable to the output
if (child->i_have_ended)
return true;
thr->children.insert(child);
thr->i_am_joining = 1;
return false;
}
/*
* This is a phantom opcode used to harvest the result of calling a user
* defined function. It is used in code generated by the .ufunc statement.
*/
bool of_REAP_UFUNC(vthread_t thr, vvp_code_t cp)
{
struct __vpiScope*child_scope = cp->ufunc_core_ptr->func_scope();
assert(child_scope);
/* Copy the output from the result variable to the output
ports of the .ufunc device. */
cp->ufunc_core_ptr->finish_thread();
/* If an automatic function, free the context for this call. */
if (child_scope->is_automatic) {
vthread_free_context(child_context, child_scope);
vthread_free_context(thr->rd_context, child_scope);
thr->wt_context = 0;
thr->rd_context = 0;
}