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:
parent
ca919b3ce0
commit
84c3c72563
|
|
@ -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 );
|
||||
|
||||
|
|
|
|||
3
ivl.def
3
ivl.def
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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&);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
1
t-dll.h
1
t-dll.h
|
|
@ -843,6 +843,7 @@ struct ivl_statement_s {
|
|||
|
||||
struct { /* IVL_ST_DISABLE */
|
||||
ivl_scope_t scope;
|
||||
bool flow_control;
|
||||
} disable_;
|
||||
|
||||
struct { /* IVL_ST_FOREVER */
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
Loading…
Reference in New Issue