Support recursive functions using `return` statement

A `return` statement in a function gets translated into a vvp `%disable`
instruction. This works fine as long as no recursion is involved. The
`%disable` instruction will stop execution of all active threads of a
particular scope. For recursive functions this means as soon as the inner
most function returns all containing outer function calls get disabled as
well. This results in incorrect behavior.

To make recursive functions using the `return` statement work use the new
vvp `%disable/parent` instruction. This instruction will only disable the
closest thread in the thread hierarchy that matches the target scope.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-04-11 17:40:52 +02:00
parent ca919b3ce0
commit 84c3c72563
9 changed files with 30 additions and 8 deletions

View File

@ -5691,7 +5691,7 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
des->errors += 1;
return 0;
}
NetDisable*disa = new NetDisable(target);
NetDisable*disa = new NetDisable(target, true);
disa->set_line( *this );
return disa;
}
@ -5720,7 +5720,7 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
assn->set_line( *this );
proc->append(assn);
NetDisable*disa = new NetDisable(target);
NetDisable*disa = new NetDisable(target, true);
disa->set_line( *this );
proc->append( disa );

View File

@ -292,8 +292,9 @@ ivl_stmt_delay_expr
ivl_stmt_delay_val
ivl_stmt_events
ivl_stmt_file
ivl_stmt_lineno
ivl_stmt_flow_control
ivl_stmt_lexp
ivl_stmt_lineno
ivl_stmt_lval
ivl_stmt_lvals
ivl_stmt_lwidth

View File

@ -21,6 +21,7 @@
# include <inttypes.h>
# include <stddef.h>
# include <stdbool.h>
/* Re the _CLASS define: clang++ wants this to be class to match the
* definition, but clang (the C) compiler needs it to be a struct
@ -2259,6 +2260,8 @@ extern uint64_t ivl_stmt_delay_val(ivl_statement_t net);
extern unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net);
extern unsigned ivl_stmt_nevent(ivl_statement_t net);
extern ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx);
/* IVL_ST_DISABLE */
extern bool ivl_stmt_flow_control(ivl_statement_t net);
/* IVL_ST_CONTRIB */
extern ivl_expr_t ivl_stmt_lexp(ivl_statement_t net);
/* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN IVL_ST_DEASSIGN

View File

@ -174,8 +174,8 @@ void NetCase::prune()
}
}
NetDisable::NetDisable(NetScope*tgt)
: target_(tgt)
NetDisable::NetDisable(NetScope*tgt, bool flow_control)
: target_(tgt), flow_control_(flow_control)
{
}

View File

@ -3272,10 +3272,11 @@ class NetDeassign : public NetAssignBase {
class NetDisable : public NetProc {
public:
explicit NetDisable(NetScope*tgt);
explicit NetDisable(NetScope*tgt, bool flow_control = false);
~NetDisable();
const NetScope*target() const;
bool flow_control() const { return flow_control_; }
virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false,
bool nested_func = false) const;
@ -3288,6 +3289,11 @@ class NetDisable : public NetProc {
private:
NetScope*target_;
// If false all threads in the target_ scope are disabled. If true only
// the closest thread in thread hierachy of the target_ scope is
// disabled. The latter is used to implement flow control statements like
// `return`.
bool flow_control_;
private: // not implemented
NetDisable(const NetDisable&);

View File

@ -2713,6 +2713,11 @@ extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net)
}
}
extern "C" bool ivl_stmt_flow_control(ivl_statement_t net)
{
return net->u_.disable_.flow_control;
}
extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net)
{
assert(net);

View File

@ -657,6 +657,7 @@ bool dll_target::proc_disable(const NetDisable*net)
FILE_NAME(stmt_cur_, net);
stmt_cur_->type_ = IVL_ST_DISABLE;
stmt_cur_->u_.disable_.flow_control = net->flow_control();
const NetScope* dis_scope = net->target();
/* A normal disable. */
if (dis_scope) stmt_cur_->u_.disable_.scope = lookup_scope_(dis_scope);

View File

@ -843,6 +843,7 @@ struct ivl_statement_s {
struct { /* IVL_ST_DISABLE */
ivl_scope_t scope;
bool flow_control;
} disable_;
struct { /* IVL_ST_FOREVER */

View File

@ -1340,8 +1340,13 @@ static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope)
/* A normal disable statement. */
if (target) {
show_stmt_file_line(net, "Disable statement.");
fprintf(vvp_out, " %%disable S_%p;\n", target);
if (ivl_stmt_flow_control(net)) {
show_stmt_file_line(net, "Flow control disable statement.");
fprintf(vvp_out, " %%disable/flow S_%p;\n", target);
} else {
show_stmt_file_line(net, "Disable statement.");
fprintf(vvp_out, " %%disable S_%p;\n", target);
}
/* A SystemVerilog disable fork statement. */
} else {
show_stmt_file_line(net, "Disable fork statement.");