Merge pull request #1418 from muhammadjawadkhan/split/02-sv-chained-calls
SV: chained method calls a().b()
This commit is contained in:
commit
812c1cedcb
31
PExpr.cc
31
PExpr.cc
|
|
@ -271,26 +271,47 @@ PECallFunction::PECallFunction(perm_string n, const list<named_pexpr_t> &parms)
|
|||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method,
|
||||
const vector<named_pexpr_t> &parms)
|
||||
: path_(method), parms_(parms), chain_prefix_(chain_prefix), is_overridden_(false)
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method,
|
||||
const list<named_pexpr_t> &parms)
|
||||
: path_(method), parms_(parms.begin(), parms.end()),
|
||||
chain_prefix_(chain_prefix), is_overridden_(false)
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::~PECallFunction()
|
||||
{
|
||||
delete chain_prefix_;
|
||||
}
|
||||
|
||||
void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type)
|
||||
{
|
||||
if (chain_prefix_) {
|
||||
chain_prefix_->declare_implicit_nets(scope, type);
|
||||
}
|
||||
for (const auto &parm : parms_) {
|
||||
if (parm.parm)
|
||||
if (parm.parm) {
|
||||
parm.parm->declare_implicit_nets(scope, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const
|
||||
{
|
||||
bool flag = false;
|
||||
if (chain_prefix_ && chain_prefix_->has_aa_term(des, scope)) {
|
||||
return true;
|
||||
}
|
||||
for (const auto &parm : parms_) {
|
||||
if (parm.parm)
|
||||
flag |= parm.parm->has_aa_term(des, scope);
|
||||
if (parm.parm && parm.parm->has_aa_term(des, scope)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
return false;
|
||||
}
|
||||
|
||||
PEConcat::PEConcat(const list<PExpr*>&p, PExpr*r)
|
||||
|
|
|
|||
22
PExpr.h
22
PExpr.h
|
|
@ -920,8 +920,18 @@ class PECallFunction : public PExpr {
|
|||
explicit PECallFunction(const pform_name_t &n, const std::list<named_pexpr_t> &parms);
|
||||
explicit PECallFunction(perm_string n, const std::list<named_pexpr_t> &parms);
|
||||
|
||||
// SystemVerilog: prefix().method(args) — prefix elaborates to a class handle.
|
||||
explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method,
|
||||
const std::vector<named_pexpr_t> &parms);
|
||||
explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method,
|
||||
const std::list<named_pexpr_t> &parms);
|
||||
|
||||
~PECallFunction() override;
|
||||
|
||||
// For chained-call resolution (path is only the final method name).
|
||||
const pform_scoped_name_t& peek_path(void) const { return path_; }
|
||||
const PExpr* peek_chain_prefix(void) const { return chain_prefix_; }
|
||||
|
||||
virtual void dump(std::ostream &) const override;
|
||||
|
||||
virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type) override;
|
||||
|
|
@ -940,6 +950,8 @@ class PECallFunction : public PExpr {
|
|||
private:
|
||||
pform_scoped_name_t path_;
|
||||
std::vector<named_pexpr_t> parms_;
|
||||
// If non-null, this call is prefix().tail_name(...) (SV method chain).
|
||||
PExpr* chain_prefix_ = nullptr;
|
||||
|
||||
// For system functions.
|
||||
bool is_overridden_;
|
||||
|
|
@ -985,8 +997,18 @@ class PECallFunction : public PExpr {
|
|||
perm_string method_name,
|
||||
const std::vector<named_pexpr_t>*src_parms) const;
|
||||
|
||||
NetExpr* elaborate_class_method_net_this_(Design*des, NetScope*scope,
|
||||
NetExpr* this_expr,
|
||||
const netclass_t*class_type,
|
||||
perm_string method_name,
|
||||
const std::vector<named_pexpr_t>*src_parms) const;
|
||||
|
||||
NetExpr* elaborate_expr_method_chained_(Design*des, NetScope*scope,
|
||||
symbol_search_results&search_results) const;
|
||||
|
||||
NetExpr* elaborate_expr_chain_(Design*des, NetScope*scope, unsigned flags) const;
|
||||
|
||||
unsigned test_width_chain_(Design*des, NetScope*scope, width_mode_t&mode);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
114
elab_expr.cc
114
elab_expr.cc
|
|
@ -55,10 +55,51 @@ bool type_is_vectorable(ivl_variable_type_t type)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For a().b()… call chains, find the class type of the prefix expression
|
||||
* (the value before the final ".method(args)").
|
||||
*/
|
||||
static const netclass_t* resolve_call_chain_prefix_class(Design*des, NetScope*scope,
|
||||
PECallFunction* link)
|
||||
{
|
||||
if (link == 0) return 0;
|
||||
|
||||
if (link->peek_chain_prefix() == 0) {
|
||||
symbol_search_results sr;
|
||||
if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) {
|
||||
return 0;
|
||||
}
|
||||
if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) {
|
||||
return 0;
|
||||
}
|
||||
NetFuncDef* fundef = sr.scope->func_def();
|
||||
ivl_assert(*link, fundef);
|
||||
NetScope* dscope = fundef->scope();
|
||||
NetNet* res = dscope->find_signal(dscope->basename());
|
||||
if (res == 0) return 0;
|
||||
return dynamic_cast<const netclass_t*>(res->net_type());
|
||||
}
|
||||
|
||||
PECallFunction* inner = dynamic_cast<PECallFunction*>(
|
||||
const_cast<PExpr*>(link->peek_chain_prefix()));
|
||||
if (inner == 0) return 0;
|
||||
|
||||
const netclass_t* recv_class = resolve_call_chain_prefix_class(des, scope, inner);
|
||||
if (recv_class == 0) return 0;
|
||||
|
||||
perm_string mname = peek_tail_name(link->peek_path());
|
||||
NetScope* mscope = recv_class->method_from_name(mname);
|
||||
if (mscope == 0) return 0;
|
||||
NetFuncDef* mdef = mscope->func_def();
|
||||
if (mdef == 0) return 0;
|
||||
NetNet* mres = mscope->find_signal(mscope->basename());
|
||||
if (mres == 0) return 0;
|
||||
return dynamic_cast<const netclass_t*>(mres->net_type());
|
||||
}
|
||||
|
||||
static ivl_nature_t find_access_function(const pform_scoped_name_t &path)
|
||||
{
|
||||
if (path.package || path.name.size() != 1)
|
||||
return nullptr;
|
||||
if (path.package || path.name.size() != 1) return nullptr;
|
||||
return access_function_nature[peek_tail_name(path)];
|
||||
}
|
||||
|
||||
|
|
@ -1752,9 +1793,38 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*,
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned PECallFunction::test_width_chain_(Design*des, NetScope*scope,
|
||||
width_mode_t&)
|
||||
{
|
||||
expr_width_ = 0;
|
||||
PECallFunction* inner_pf = dynamic_cast<PECallFunction*>(chain_prefix_);
|
||||
if (inner_pf == 0) return 0;
|
||||
|
||||
const netclass_t* cls = resolve_call_chain_prefix_class(des, scope, inner_pf);
|
||||
if (cls == 0) return 0;
|
||||
|
||||
perm_string mname = peek_tail_name(path_);
|
||||
NetScope* mscope = cls->method_from_name(mname);
|
||||
if (mscope == 0) return 0;
|
||||
|
||||
NetFuncDef* mdef = mscope->func_def();
|
||||
if (mdef == 0 || mdef->is_void()) return 0;
|
||||
|
||||
NetNet* mres = mscope->find_signal(mscope->basename());
|
||||
if (mres == 0) return 0;
|
||||
|
||||
expr_type_ = mres->data_type();
|
||||
expr_width_ = mres->vector_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = mres->get_signed();
|
||||
return expr_width_;
|
||||
}
|
||||
|
||||
unsigned PECallFunction::test_width(Design*des, NetScope*scope,
|
||||
width_mode_t&mode)
|
||||
{
|
||||
if (chain_prefix_) return test_width_chain_(des, scope, mode);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PECallFunction::test_width: "
|
||||
<< "path_: " << path_ << endl;
|
||||
|
|
@ -2902,6 +2972,8 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope,
|
|||
{
|
||||
flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag
|
||||
|
||||
if (chain_prefix_) return elaborate_expr_chain_(des, scope, flags);
|
||||
|
||||
// Search for the symbol. This should turn up a scope.
|
||||
symbol_search_results search_results;
|
||||
bool search_flag = symbol_search(this, des, scope, path_, UINT_MAX, &search_results);
|
||||
|
|
@ -3267,6 +3339,18 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope,
|
|||
NetNet*net, const netclass_t*class_type,
|
||||
perm_string method_name,
|
||||
const vector<named_pexpr_t>*src_parms) const
|
||||
{
|
||||
NetESignal*ethis = new NetESignal(net);
|
||||
ethis->set_line(*this);
|
||||
return elaborate_class_method_net_this_(des, scope, ethis, class_type,
|
||||
method_name, src_parms);
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_class_method_net_this_(Design*des, NetScope*scope,
|
||||
NetExpr* this_expr,
|
||||
const netclass_t*class_type,
|
||||
perm_string method_name,
|
||||
const vector<named_pexpr_t>*src_parms) const
|
||||
{
|
||||
NetScope*method = class_type->method_from_name(method_name);
|
||||
|
||||
|
|
@ -3295,9 +3379,7 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope,
|
|||
vector<NetExpr*> parms(def->port_count());
|
||||
ivl_assert(*this, def->port_count() >= 1);
|
||||
|
||||
NetESignal*ethis = new NetESignal(net);
|
||||
ethis->set_line(*this);
|
||||
parms[0] = ethis;
|
||||
parms[0] = this_expr;
|
||||
|
||||
elaborate_arguments_(des, scope, def, false, parms, 1, src_parms);
|
||||
|
||||
|
|
@ -3307,6 +3389,28 @@ NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope,
|
|||
return call;
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_expr_chain_(Design*des, NetScope*scope,
|
||||
unsigned flags) const
|
||||
{
|
||||
ivl_assert(*this, chain_prefix_);
|
||||
NetExpr* inner = chain_prefix_->elaborate_expr(des, scope, -1, flags);
|
||||
if (inner == 0) return 0;
|
||||
|
||||
const netclass_t* cls = dynamic_cast<const netclass_t*>(inner->net_type());
|
||||
if (cls == 0) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "The prefix of a chained call (a().b()) must be an expression "
|
||||
<< "with class type." << endl;
|
||||
des->errors += 1;
|
||||
delete inner;
|
||||
return 0;
|
||||
}
|
||||
|
||||
perm_string method_name = peek_tail_name(path_);
|
||||
return elaborate_class_method_net_this_(des, scope, inner, cls,
|
||||
method_name, &parms_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle obj.m1().m2(args): arguments apply only to the last call; intermediate
|
||||
* methods must be class methods returning class handles.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
// Chained call: function returns class handle, then method on result (a().b()).
|
||||
|
||||
module test;
|
||||
|
||||
class C;
|
||||
function int f;
|
||||
f = 7;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
function C get_c;
|
||||
get_c = new;
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
int x;
|
||||
x = get_c().f();
|
||||
if (x !== 7) begin
|
||||
$display("FAILED");
|
||||
end else begin
|
||||
$display("PASSED");
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -273,6 +273,7 @@ sv_byte_array_string_fail2 vvp_tests/sv_byte_array_string_fail2.json
|
|||
sv_byte_array_string_fail3 vvp_tests/sv_byte_array_string_fail3.json
|
||||
sv_byte_array_string_fail4 vvp_tests/sv_byte_array_string_fail4.json
|
||||
sv_byte_array_string_fail5 vvp_tests/sv_byte_array_string_fail5.json
|
||||
sv_call_chain_method1 vvp_tests/sv_call_chain_method1.json
|
||||
sv_chained_constructor1 vvp_tests/sv_chained_constructor1.json
|
||||
sv_chained_constructor2 vvp_tests/sv_chained_constructor2.json
|
||||
sv_chained_constructor3 vvp_tests/sv_chained_constructor3.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_call_chain_method1.v",
|
||||
"iverilog-args" : [ "-g2012" ],
|
||||
"vlog95" : {
|
||||
"__comment" : "Classes are not supported",
|
||||
"type" : "CE"
|
||||
}
|
||||
}
|
||||
33
parse.y
33
parse.y
|
|
@ -931,7 +931,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
%type <spec_optional_args> timeskew_fullskew_opt_remain_active_flag
|
||||
|
||||
%type <expr> assignment_pattern expression expression_opt expr_mintypmax
|
||||
%type <expr> expr_primary_or_typename expr_primary
|
||||
%type <expr> expr_primary_or_typename expr_primary call_chain_expr
|
||||
%type <expr> class_new dynamic_array_new
|
||||
%type <expr> net_decl_initializer_opt var_decl_initializer_opt initializer_opt
|
||||
%type <expr> inc_or_dec_expression inside_expression lpvalue
|
||||
|
|
@ -4238,6 +4238,26 @@ expr_primary_or_typename
|
|||
|
||||
;
|
||||
|
||||
/* SystemVerilog: a().b() — call a function, then invoke a method on the
|
||||
returned class handle. Extends with further ".id(args)" as needed. */
|
||||
|
||||
call_chain_expr
|
||||
: hierarchy_identifier attribute_list_opt argument_list_parens
|
||||
{ PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3);
|
||||
delete $1;
|
||||
delete $2;
|
||||
delete $3;
|
||||
$$ = tmp;
|
||||
}
|
||||
| call_chain_expr '.' hierarchy_identifier attribute_list_opt argument_list_parens
|
||||
{ PECallFunction*tmp = pform_make_chained_call_function(@2, $1, *$3, *$5);
|
||||
delete $3;
|
||||
delete $4;
|
||||
delete $5;
|
||||
$$ = tmp;
|
||||
}
|
||||
;
|
||||
|
||||
expr_primary
|
||||
: number
|
||||
{ assert($1);
|
||||
|
|
@ -4297,6 +4317,10 @@ expr_primary
|
|||
/* The hierarchy_identifier rule matches simple identifiers as well as
|
||||
indexed arrays and part selects */
|
||||
|
||||
/* SV call chains get_c1().f() — must come before bare hierarchy_identifier
|
||||
so `id (` is not reduced as PEIdent + error. */
|
||||
| call_chain_expr
|
||||
{ $$ = $1; }
|
||||
| hierarchy_identifier
|
||||
{ PEIdent*tmp = pform_new_ident(@1, *$1);
|
||||
FILE_NAME(tmp, @1);
|
||||
|
|
@ -4347,13 +4371,6 @@ expr_primary
|
|||
function call. If a system identifier, then a system function
|
||||
call. It can also be a call to a class method (function). */
|
||||
|
||||
| hierarchy_identifier attribute_list_opt argument_list_parens
|
||||
{ PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3);
|
||||
delete $1;
|
||||
delete $2;
|
||||
delete $3;
|
||||
$$ = tmp;
|
||||
}
|
||||
| class_hierarchy_identifier argument_list_parens
|
||||
{ PECallFunction*tmp = pform_make_call_function(@1, *$1, *$2);
|
||||
delete $1;
|
||||
|
|
|
|||
18
pform.cc
18
pform.cc
|
|
@ -975,6 +975,24 @@ PECallFunction* pform_make_call_function(const struct vlltype&loc,
|
|||
return tmp;
|
||||
}
|
||||
|
||||
PECallFunction* pform_make_chained_call_function(const struct vlltype&loc,
|
||||
PExpr*prefix,
|
||||
const pform_name_t&method,
|
||||
const list<named_pexpr_t> &parms)
|
||||
{
|
||||
if (!gn_system_verilog()) {
|
||||
pform_requires_sv(loc, "Chained calls like a().b()");
|
||||
delete prefix;
|
||||
return new PECallFunction(method, parms);
|
||||
}
|
||||
|
||||
check_potential_imports(loc, method.front().name, true);
|
||||
|
||||
PECallFunction*tmp = new PECallFunction(prefix, method, parms);
|
||||
FILE_NAME(tmp, loc);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
PCallTask* pform_make_call_task(const struct vlltype&loc,
|
||||
const pform_name_t&name,
|
||||
const list<named_pexpr_t> &parms)
|
||||
|
|
|
|||
4
pform.h
4
pform.h
|
|
@ -333,6 +333,10 @@ extern void pform_set_type_referenced(const struct vlltype&loc, const char*name)
|
|||
extern PECallFunction* pform_make_call_function(const struct vlltype&loc,
|
||||
const pform_name_t&name,
|
||||
const std::list<named_pexpr_t> &parms);
|
||||
extern PECallFunction* pform_make_chained_call_function(const struct vlltype&loc,
|
||||
PExpr*prefix,
|
||||
const pform_name_t&method,
|
||||
const std::list<named_pexpr_t> &parms);
|
||||
extern PCallTask* pform_make_call_task(const struct vlltype&loc,
|
||||
const pform_name_t&name,
|
||||
const std::list<named_pexpr_t> &parms);
|
||||
|
|
|
|||
|
|
@ -421,6 +421,10 @@ void PEConcat::dump(ostream&out) const
|
|||
|
||||
void PECallFunction::dump(ostream &out) const
|
||||
{
|
||||
if (peek_chain_prefix()) {
|
||||
peek_chain_prefix()->dump(out);
|
||||
out << ".";
|
||||
}
|
||||
out << path_ << "(" << parms_ << ")";
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue