Support nested struct l-values.

This commit is contained in:
Stephen Williams 2019-09-16 13:50:17 -07:00
parent b639c4c9aa
commit 1c281c2d77
2 changed files with 287 additions and 217 deletions

View File

@ -433,10 +433,10 @@ class PEIdent : public PExpr {
bool need_const_idx) const; bool need_const_idx) const;
NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*, NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*,
NetNet*, NetNet*,
const perm_string&) const; pform_name_t) const;
bool elaborate_lval_net_packed_member_(Design*, NetScope*, bool elaborate_lval_net_packed_member_(Design*, NetScope*,
NetAssign_*, NetAssign_*,
const name_component_t&) const; pform_name_t member_path) const;
bool elaborate_lval_darray_bit_(Design*, NetScope*, bool elaborate_lval_darray_bit_(Design*, NetScope*,
NetAssign_*) const; NetAssign_*) const;
@ -530,8 +530,7 @@ class PEIdent : public PExpr {
NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, NetNet* elaborate_lnet_common_(Design*des, NetScope*scope,
bool bidirectional_flag) const; bool bidirectional_flag) const;
NetAssign_*scan_lname_for_nested_members_(Design*des, NetScope*scope,
const pform_name_t&path) const;
bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig, bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig,
long&midx, long&lidx) const; long&midx, long&lidx) const;
}; };

View File

@ -146,57 +146,6 @@ NetAssign_* PEConcat::elaborate_lval(Design*des,
return res; return res;
} }
NetAssign_*PEIdent::scan_lname_for_nested_members_(Design*des, NetScope*scope,
const pform_name_t&cur_path) const
{
if (cur_path.size() == 1)
return 0;
pform_name_t use_path = cur_path;
name_component_t tail = use_path.back();
use_path.pop_back();
NetNet* reg = 0;
const NetExpr*par = 0;
NetEvent* eve = 0;
symbol_search(this, des, scope, use_path, reg, par, eve);
if (reg == 0) {
NetAssign_*tmp = scan_lname_for_nested_members_(des, scope, use_path);
if (tmp == 0)
return 0;
tmp = new NetAssign_(tmp);
tmp->set_property(tail.name);
return tmp;
}
if (reg->struct_type() && reg->struct_type()->packed()) {
NetAssign_*tmp = new NetAssign_(reg);
elaborate_lval_net_packed_member_(des, scope, tmp, tail);
return tmp;
}
#if 0
if (reg->struct_type() && reg->struct_type()->packed()) {
cerr << get_fileline() << ": sorry: "
<< "I don't know what to do with packed struct " << use_path
<< " with member " << tail << "." << endl;
return 0;
}
#endif
if (reg->struct_type() && !reg->struct_type()->packed()) {
cerr << get_fileline() << ": sorry: "
<< "I don't know what to do with unpacked struct " << use_path
<< " with member " << tail << "." << endl;
return 0;
}
if (reg->class_type()) {
return elaborate_lval_net_class_member_(des, scope, reg, tail.name);
}
return 0;
}
/* /*
* Handle the ident as an l-value. This includes bit and part selects * Handle the ident as an l-value. This includes bit and part selects
@ -210,7 +159,6 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
NetNet* reg = 0; NetNet* reg = 0;
const NetExpr*par = 0; const NetExpr*par = 0;
NetEvent* eve = 0; NetEvent* eve = 0;
perm_string method_name;
if (debug_elaborate) { if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval: " cerr << get_fileline() << ": PEIdent::elaborate_lval: "
@ -231,32 +179,28 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
ivl_assert(*this, use_scope); ivl_assert(*this, use_scope);
} }
symbol_search(this, des, use_scope, path_, reg, par, eve); /* Try to find the base part of the path that names the
variable. The remainer is the member path. For example, if
/* If the signal is not found, check to see if this is a the path is a.b.c.d, and a.b is the path to a variable,
member of a struct. Take the name of the form "a.b.member", then a.b becomes the base_path and c.d becomes the
remove the member and store it into method_name, and retry member_path. If we cannot find the variable with any
the search with "a.b". */ prefix, then the base_path will be empty after this loop
if (reg == 0 && path_.size() >= 2) { and reg will remain nil. */
pform_name_t use_path = path_; pform_name_t base_path = path_;
perm_string tmp_name = peek_tail_name(use_path); pform_name_t member_path;
use_path.pop_back(); while (reg == 0 && base_path.size() > 0) {
symbol_search(this, des, use_scope, use_path, reg, par, eve); symbol_search(this, des, use_scope, base_path, reg, par, eve);
// Found it!
if (reg && reg->struct_type()) { if (reg != 0) break;
method_name = tmp_name; // Not found. Try to pop another name off the base_path
// and push it to the front of the member_path.
} else if (reg && reg->class_type()) { member_path.push_front( base_path.back() );
method_name = tmp_name; base_path.pop_back();
} else if (NetAssign_*subl = scan_lname_for_nested_members_(des, use_scope, path_)) {
return subl;
} else {
reg = 0;
}
} }
/* The l-value must be a variable. If not, then give up and
print a useful error message. */
if (reg == 0) { if (reg == 0) {
if (use_scope->type()==NetScope::FUNC if (use_scope->type()==NetScope::FUNC
&& use_scope->func_def()->return_sig()==0 && use_scope->func_def()->return_sig()==0
@ -278,7 +222,10 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
if (debug_elaborate) { if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval: " cerr << get_fileline() << ": PEIdent::elaborate_lval: "
<< "Found l-value as reg." << "Found l-value path_=" << path_
<< " as reg=" << reg->name()
<< " base_path=" << base_path
<< ", member_path=" << member_path
<< " unpacked_dimensions()=" << reg->unpacked_dimensions() << endl; << " unpacked_dimensions()=" << reg->unpacked_dimensions() << endl;
} }
@ -324,21 +271,27 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
return 0; return 0;
} }
if (reg->struct_type() && !method_name.nil()) {
// If we find that the matched variable is a packed struct,
// then we can handled it with the net_packed_member_ method.
if (reg->struct_type() && member_path.size() > 0) {
NetAssign_*lv = new NetAssign_(reg); NetAssign_*lv = new NetAssign_(reg);
name_component_t tmp_name (method_name); elaborate_lval_net_packed_member_(des, use_scope, lv, member_path);
elaborate_lval_net_packed_member_(des, use_scope, lv, tmp_name);
return lv; return lv;
} }
if (reg->class_type() && !method_name.nil() && gn_system_verilog()) { // If the variable is a class object, then handle it with the
NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, method_name); // net_class_member_ method.
if (reg->class_type() && member_path.size() > 0 && gn_system_verilog()) {
NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, member_path);
return lv; return lv;
} }
// Past this point, we should have taken care of the cases // Past this point, we should have taken care of the cases
// where the name is a member/method of a struct/class. // where the name is a member/method of a struct/class.
ivl_assert(*this, method_name.nil()); // XXXX ivl_assert(*this, method_name.nil());
ivl_assert(*this, member_path.size() == 0);
bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE); bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE);
@ -1063,20 +1016,50 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des,
return true; return true;
} }
/*
* When the l-value turns out to be a class object, this method is
* called with the bound variable, and the method path. For example,
* if path_=a.b.c and a.b binds to the variable, then sig is b, and
* member_path=c. if path_=obj.base.x, and base_path=obj, then sig is
* obj, and member_path=base.x.
*/
NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope, NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope,
NetNet*sig, const perm_string&method_name) const NetNet*sig, pform_name_t member_path) const
{ {
if (debug_elaborate) { if (debug_elaborate) {
cerr << get_fileline() << ": elaborate_lval_net_class_member_: " cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
<< "l-value is property " << method_name << "l-value is property " << member_path
<< " of " << sig->name() << "." << endl; << " of " << sig->name() << "." << endl;
} }
const netclass_t*class_type = sig->class_type(); const netclass_t*class_type = sig->class_type();
ivl_assert(*this, class_type); ivl_assert(*this, class_type);
/* Make sure the property is really present in the class. If // Iterate over the member_path. This handles nested class
not, then generate an error message and return an error. */ // object, by generating nested NetAssign_ object. We start
// with lv==0, so the front of the member_path is the member
// of the outermost class. This generates an lv from sig. Then
// iterate over the remaining of the member_path, replacing
// the outer lv with an lv that nests the lv from the previous
// iteration.
NetAssign_*lv = 0;
do {
// Start with the first component of the member path...
perm_string method_name = peek_head_name(member_path);
// Pull that component from the member_path. We need to
// know the current member being worked on, and will
// need to know if there are more members to be worked on.
name_component_t member_cur = member_path.front();
member_path.pop_front();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
<< "Processing member_cur=" << member_cur
<< endl;
}
// Make sure the property is really present in the class. If
// not, then generate an error message and return an error.
int pidx = class_type->property_idx_from_name(method_name); int pidx = class_type->property_idx_from_name(method_name);
if (pidx < 0) { if (pidx < 0) {
cerr << get_fileline() << ": error: Class " << class_type->get_name() cerr << get_fileline() << ": error: Class " << class_type->get_name()
@ -1101,7 +1084,7 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope
NetNet*psig = class_type->find_static_property(method_name); NetNet*psig = class_type->find_static_property(method_name);
ivl_assert(*this, psig); ivl_assert(*this, psig);
NetAssign_*lv = new NetAssign_(psig); lv = new NetAssign_(psig);
return lv; return lv;
} else if (qual.test_const()) { } else if (qual.test_const()) {
@ -1111,14 +1094,14 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope
des->errors += 1; des->errors += 1;
} }
NetAssign_*lv = new NetAssign_(sig); lv = lv? new NetAssign_(lv) : new NetAssign_(sig);
lv->set_property(method_name); lv->set_property(method_name);
// Now get the type of the property.
ivl_type_t ptype = class_type->get_prop_type(pidx); ivl_type_t ptype = class_type->get_prop_type(pidx);
const netdarray_t*mtype = dynamic_cast<const netdarray_t*> (ptype); const netdarray_t*mtype = dynamic_cast<const netdarray_t*> (ptype);
if (mtype) { if (mtype) {
const name_component_t&name_tail = path_.back(); if (! member_cur.index.empty()) {
if (! name_tail.index.empty()) {
cerr << get_fileline() << ": sorry: " cerr << get_fileline() << ": sorry: "
<< "Array index of array properties not supported." << "Array index of array properties not supported."
<< endl; << endl;
@ -1126,26 +1109,44 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope
} }
} }
// If the current member is a class object, then get the
// type. We may wind up iterating, and need the proper
// class type.
class_type = dynamic_cast<const netclass_t*>(ptype);
} while (member_path.size() > 0);
return lv; return lv;
} }
/*
* This method is caled to handle l-value identifiers that are packed
* structs. The lv is already attached to the variable, so this method
* calculates the part select that is defined by the member_path. For
* example, if the path_ is main.sub.sub_local, and the variable is
* main, then we know at this point that main is a packed struct, and
* lv contains the reference to the bound variable (main). In this
* case member_path==sub.sub_local, and it is up to this method to
* work out the part select that the member_path represents.
*/
bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
NetAssign_*lv, NetAssign_*lv,
const name_component_t&member_comp) const pform_name_t member_path) const
{ {
const perm_string&member_name = member_comp.name; if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "path_=" << path_
<< " member_path=" << member_path
<< endl;
}
NetNet*reg = lv->sig(); NetNet*reg = lv->sig();
ivl_assert(*this, reg); ivl_assert(*this, reg);
const netstruct_t*struct_type = reg->struct_type(); const netstruct_t*struct_type = reg->struct_type();
ivl_assert(*this, struct_type); ivl_assert(*this, struct_type);
if (debug_elaborate) {
cerr << get_fileline() << ": debug: elaborate lval packed member: "
<< "path_=" << path_ << endl;
}
if (! struct_type->packed()) { if (! struct_type->packed()) {
cerr << get_fileline() << ": sorry: Only packed structures " cerr << get_fileline() << ": sorry: Only packed structures "
<< "are supported in l-value." << endl; << "are supported in l-value." << endl;
@ -1153,46 +1154,98 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
return false; return false;
} }
// Looking for the base name. We need that to know about
// indices we may need to apply. This is to handle the case
// that the base is an array of structs, and not just a
// struct.
pform_name_t::const_reverse_iterator name_idx = path_.rbegin();
for (size_t idx = 1 ; idx < member_path.size() ; idx += 1)
++ name_idx;
if (name_idx->name != peek_head_name(member_path)) {
cerr << get_fileline() << ": internal error: "
<< "name_idx=" << name_idx->name
<< ", expecting member_name=" << peek_head_name(member_path)
<< endl;
des->errors += 1;
return false;
}
ivl_assert(*this, name_idx->name == peek_head_name(member_path));
++ name_idx;
const name_component_t&name_base = *name_idx;
// Shouldn't be seeing unpacked arrays of packed structs... // Shouldn't be seeing unpacked arrays of packed structs...
ivl_assert(*this, reg->unpacked_dimensions() == 0); ivl_assert(*this, reg->unpacked_dimensions() == 0);
// These make up the "part" select that is the equivilent of
// following the member path through the nested structs. To
// start with, the off[set] is zero, and use_width is the
// width of the entire variable. The first member_comp is at
// some offset within the variable, and will have a reduced
// width. As we step through the member_path the off
// increases, and use_width shrinks.
unsigned long off = 0;
unsigned long use_width = struct_type->packed_width();
pform_name_t completed_path;
do {
const name_component_t member_comp = member_path.front();
const perm_string&member_name = member_comp.name;
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Processing member_comp=" << member_comp
<< " (completed_path=" << completed_path << ")"
<< endl;
}
// This is a packed member, so the name is of the form // This is a packed member, so the name is of the form
// "a.b[...].c[...]" which means that the path_ must have at // "a.b[...].c[...]" which means that the path_ must have at
// least 2 components. We are processing "c[...]" at that // least 2 components. We are processing "c[...]" at that
// point (otherwise known as member_name) so we'll save a // point (otherwise known as member_name) and we have a
// reference to it in name_tail. We are also processing "b[]" // reference to it in member_comp.
// so save that as name_base.
ivl_assert(*this, path_.size() >= 2); // The member_path is the members we want to follow for the
// variable. For example, main[N].a.b may have main[N] as the
pform_name_t::const_reverse_iterator name_idx = path_.rbegin(); // base_name, and member_path=a.b. The member_name is the
ivl_assert(*this, name_idx->name == member_name); // start of the member_path, and is "a". The member_name
const name_component_t&name_tail = *name_idx; // should be a member of the struct_type type.
++ name_idx;
const name_component_t&name_base = *name_idx;
// Calculate the offset within the packed structure of the // Calculate the offset within the packed structure of the
// member, and any indices. We will add in the offset of the // member, and any indices. We will add in the offset of the
// struct into the packed array later. Note that this works // struct into the packed array later. Note that this works
// for packed unions as well (although the offset will be 0 // for packed unions as well (although the offset will be 0
// for union members). // for union members).
unsigned long off; unsigned long tmp_off;
const netstruct_t::member_t* member = struct_type->packed_member(member_name, off); const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off);
if (member == 0) { if (member == 0) {
cerr << get_fileline() << ": error: Member " << member_name cerr << get_fileline() << ": error: Member " << member_name
<< " is not a member of variable " << reg->name() << endl; << " is not a member of struct type of "
<< reg->name()
<< "." << completed_path << endl;
des->errors += 1; des->errors += 1;
return false; return false;
} }
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Member type: " << *(member->net_type)
<< endl;
}
unsigned long use_width = member->net_type->packed_width(); off += tmp_off;
ivl_assert(*this, use_width >= (unsigned long)member->net_type->packed_width());
use_width = member->net_type->packed_width();
// At this point, off and use_width are the part select
// expressed by the member_comp, which is a member of the
// struct. We can further refine the part select with any
// indices that might be present.
// Get the index component type. At this point, we only // Get the index component type. At this point, we only
// support bit select or none. // support bit select or none.
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
if (!name_tail.index.empty()) if (!member_comp.index.empty())
use_sel = name_tail.index.back().sel; use_sel = member_comp.index.back().sel;
if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) { if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) {
cerr << get_fileline() << ": sorry: Assignments to part selects of " cerr << get_fileline() << ": sorry: Assignments to part selects of "
@ -1201,7 +1254,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
return false; return false;
} }
if (! name_tail.index.empty()) { if (! member_comp.index.empty()) {
// If there are index expressions in this l-value // If there are index expressions in this l-value
// expression, then the implicit assumption is that the // expression, then the implicit assumption is that the
@ -1214,7 +1267,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
ivl_assert(*this, mem_vec); ivl_assert(*this, mem_vec);
const vector<netrange_t>&mem_packed_dims = mem_vec->packed_dims(); const vector<netrange_t>&mem_packed_dims = mem_vec->packed_dims();
if (name_tail.index.size() > mem_packed_dims.size()) { if (member_comp.index.size() > mem_packed_dims.size()) {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
<< "Too many index expressions for member." << endl; << "Too many index expressions for member." << endl;
des->errors += 1; des->errors += 1;
@ -1223,11 +1276,11 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
// Evaluate all but the last index expression, into prefix_indices. // Evaluate all but the last index expression, into prefix_indices.
list<long>prefix_indices; list<long>prefix_indices;
bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index); bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index);
ivl_assert(*this, rc); ivl_assert(*this, rc);
// Evaluate the last index expression into a constant long. // Evaluate the last index expression into a constant long.
NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true); NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true);
long tmp; long tmp;
if (texpr == 0 || !eval_as_long(tmp, texpr)) { if (texpr == 0 || !eval_as_long(tmp, texpr)) {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
@ -1248,6 +1301,24 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
use_width = lwid; use_width = lwid;
} }
// Complete this component of the path, mark it
// completed, and set up for the next component.
struct_type = dynamic_cast<const netstruct_t*> (member->net_type);
completed_path .push_back(member_comp);
member_path.pop_front();
} while (member_path.size() > 0 && struct_type != 0);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "After processing member_path, "
<< "off=" << off
<< ", use_width=" << use_width
<< ", completed_path=" << completed_path
<< ", member_path=" << member_path
<< endl;
}
// The dimensions in the expression must match the packed // The dimensions in the expression must match the packed
// dimensions that are declared for the variable. For example, // dimensions that are declared for the variable. For example,
// if foo is a packed array of struct, then this expression // if foo is a packed array of struct, then this expression