diff --git a/PExpr.h b/PExpr.h index adf17a137..770c44969 100644 --- a/PExpr.h +++ b/PExpr.h @@ -433,10 +433,10 @@ class PEIdent : public PExpr { bool need_const_idx) const; NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*, NetNet*, - const perm_string&) const; + pform_name_t) const; bool elaborate_lval_net_packed_member_(Design*, NetScope*, NetAssign_*, - const name_component_t&) const; + pform_name_t member_path) const; bool elaborate_lval_darray_bit_(Design*, NetScope*, NetAssign_*) const; @@ -530,8 +530,7 @@ class PEIdent : public PExpr { NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, 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, long&midx, long&lidx) const; }; diff --git a/elab_lval.cc b/elab_lval.cc index 4696c9eaf..d8f3508c7 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -146,57 +146,6 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, 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 @@ -210,7 +159,6 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, NetNet* reg = 0; const NetExpr*par = 0; NetEvent* eve = 0; - perm_string method_name; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval: " @@ -231,32 +179,28 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, ivl_assert(*this, use_scope); } - symbol_search(this, des, use_scope, path_, reg, par, eve); - - /* If the signal is not found, check to see if this is a - member of a struct. Take the name of the form "a.b.member", - remove the member and store it into method_name, and retry - the search with "a.b". */ - if (reg == 0 && path_.size() >= 2) { - pform_name_t use_path = path_; - perm_string tmp_name = peek_tail_name(use_path); - use_path.pop_back(); - symbol_search(this, des, use_scope, use_path, reg, par, eve); - - if (reg && reg->struct_type()) { - method_name = tmp_name; - - } else if (reg && reg->class_type()) { - method_name = tmp_name; - - } else if (NetAssign_*subl = scan_lname_for_nested_members_(des, use_scope, path_)) { - return subl; - - } else { - reg = 0; - } + /* Try to find the base part of the path that names the + variable. The remainer is the member path. For example, if + the path is a.b.c.d, and a.b is the path to a variable, + then a.b becomes the base_path and c.d becomes the + member_path. If we cannot find the variable with any + prefix, then the base_path will be empty after this loop + and reg will remain nil. */ + pform_name_t base_path = path_; + pform_name_t member_path; + while (reg == 0 && base_path.size() > 0) { + symbol_search(this, des, use_scope, base_path, reg, par, eve); + // Found it! + if (reg != 0) break; + // Not found. Try to pop another name off the base_path + // and push it to the front of the member_path. + member_path.push_front( base_path.back() ); + base_path.pop_back(); } + + /* The l-value must be a variable. If not, then give up and + print a useful error message. */ if (reg == 0) { if (use_scope->type()==NetScope::FUNC && use_scope->func_def()->return_sig()==0 @@ -278,7 +222,10 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, if (debug_elaborate) { 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; } @@ -324,21 +271,27 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, 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); - name_component_t tmp_name (method_name); - elaborate_lval_net_packed_member_(des, use_scope, lv, tmp_name); + elaborate_lval_net_packed_member_(des, use_scope, lv, member_path); return lv; } - if (reg->class_type() && !method_name.nil() && gn_system_verilog()) { - NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, method_name); + // If the variable is a class object, then handle it with the + // 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; } + // Past this point, we should have taken care of the cases // 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); @@ -1063,89 +1016,137 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, 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, - NetNet*sig, const perm_string&method_name) const + NetNet*sig, pform_name_t member_path) const { if (debug_elaborate) { - cerr << get_fileline() << ": elaborate_lval_net_class_member_: " - << "l-value is property " << method_name + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: " + << "l-value is property " << member_path << " of " << sig->name() << "." << endl; } const netclass_t*class_type = sig->class_type(); ivl_assert(*this, class_type); - /* 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); - if (pidx < 0) { - cerr << get_fileline() << ": error: Class " << class_type->get_name() - << " does not have a property " << method_name << "." << endl; - des->errors += 1; - return 0; - } + // Iterate over the member_path. This handles nested class + // 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(); - property_qualifier_t qual = class_type->get_prop_qual(pidx); - if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { - cerr << get_fileline() << ": error: " - << "Local property " << class_type->get_prop_name(pidx) - << " is not accessible (l-value) in this context." - << " (scope=" << scope_path(scope) << ")" << endl; - des->errors += 1; - - } else if (qual.test_static()) { - - // Special case: this is a static property. Ignore the - // "this" sig and use the property itself, which is not - // part of the sig, as the l-value. - NetNet*psig = class_type->find_static_property(method_name); - ivl_assert(*this, psig); - - NetAssign_*lv = new NetAssign_(psig); - return lv; - - } else if (qual.test_const()) { - cerr << get_fileline() << ": error: " - << "Property " << class_type->get_prop_name(pidx) - << " is constant in this context." << endl; - des->errors += 1; - } - - NetAssign_*lv = new NetAssign_(sig); - lv->set_property(method_name); - - ivl_type_t ptype = class_type->get_prop_type(pidx); - const netdarray_t*mtype = dynamic_cast (ptype); - if (mtype) { - const name_component_t&name_tail = path_.back(); - if (! name_tail.index.empty()) { - cerr << get_fileline() << ": sorry: " - << "Array index of array properties not supported." + 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); + if (pidx < 0) { + cerr << get_fileline() << ": error: Class " << class_type->get_name() + << " does not have a property " << method_name << "." << endl; + des->errors += 1; + return 0; + } + + property_qualifier_t qual = class_type->get_prop_qual(pidx); + if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { + cerr << get_fileline() << ": error: " + << "Local property " << class_type->get_prop_name(pidx) + << " is not accessible (l-value) in this context." + << " (scope=" << scope_path(scope) << ")" << endl; + des->errors += 1; + + } else if (qual.test_static()) { + + // Special case: this is a static property. Ignore the + // "this" sig and use the property itself, which is not + // part of the sig, as the l-value. + NetNet*psig = class_type->find_static_property(method_name); + ivl_assert(*this, psig); + + lv = new NetAssign_(psig); + return lv; + + } else if (qual.test_const()) { + cerr << get_fileline() << ": error: " + << "Property " << class_type->get_prop_name(pidx) + << " is constant in this context." << endl; des->errors += 1; } - } + + lv = lv? new NetAssign_(lv) : new NetAssign_(sig); + lv->set_property(method_name); + + // Now get the type of the property. + ivl_type_t ptype = class_type->get_prop_type(pidx); + const netdarray_t*mtype = dynamic_cast (ptype); + if (mtype) { + if (! member_cur.index.empty()) { + cerr << get_fileline() << ": sorry: " + << "Array index of array properties not supported." + << endl; + des->errors += 1; + } + } + + // 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(ptype); + + } while (member_path.size() > 0); + 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, 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(); ivl_assert(*this, reg); const netstruct_t*struct_type = reg->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()) { cerr << get_fileline() << ": sorry: Only packed structures " << "are supported in l-value." << endl; @@ -1153,99 +1154,169 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, return false; } - // Shouldn't be seeing unpacked arrays of packed structs... - ivl_assert(*this, reg->unpacked_dimensions() == 0); - - // This is a packed member, so the name is of the form - // "a.b[...].c[...]" which means that the path_ must have at - // least 2 components. We are processing "c[...]" at that - // point (otherwise known as member_name) so we'll save a - // reference to it in name_tail. We are also processing "b[]" - // so save that as name_base. - - ivl_assert(*this, path_.size() >= 2); - + // 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(); - ivl_assert(*this, name_idx->name == member_name); - const name_component_t&name_tail = *name_idx; + 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; - // Calculate the offset within the packed structure of the - // member, and any indices. We will add in the offset of the - // struct into the packed array later. Note that this works - // for packed unions as well (although the offset will be 0 - // for union members). - unsigned long off; - const netstruct_t::member_t* member = struct_type->packed_member(member_name, off); + // Shouldn't be seeing unpacked arrays of packed structs... + ivl_assert(*this, reg->unpacked_dimensions() == 0); - if (member == 0) { - cerr << get_fileline() << ": error: Member " << member_name - << " is not a member of variable " << reg->name() << endl; - des->errors += 1; - return false; - } + // 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(); - unsigned long use_width = member->net_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; - // Get the index component type. At this point, we only - // support bit select or none. - index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; - if (!name_tail.index.empty()) - use_sel = name_tail.index.back().sel; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Processing member_comp=" << member_comp + << " (completed_path=" << completed_path << ")" + << endl; + } - if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) { - cerr << get_fileline() << ": sorry: Assignments to part selects of " - "a struct member are not yet supported." << endl; - des->errors += 1; - return false; - } + // This is a packed member, so the name is of the form + // "a.b[...].c[...]" which means that the path_ must have at + // least 2 components. We are processing "c[...]" at that + // point (otherwise known as member_name) and we have a + // reference to it in member_comp. - if (! name_tail.index.empty()) { + // 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 + // base_name, and member_path=a.b. The member_name is the + // start of the member_path, and is "a". The member_name + // should be a member of the struct_type type. - // If there are index expressions in this l-value - // expression, then the implicit assumption is that the - // member is a vector type with packed dimensions. For - // example, if the l-value expression is "foo.member[1][2]", - // then the member should be something like: - // ... logic [h:l][m:n] foo; - // Get the dimensions from the netvector_t that this implies. - const netvector_t*mem_vec = dynamic_cast(member->net_type); - ivl_assert(*this, mem_vec); - const vector&mem_packed_dims = mem_vec->packed_dims(); + // Calculate the offset within the packed structure of the + // member, and any indices. We will add in the offset of the + // struct into the packed array later. Note that this works + // for packed unions as well (although the offset will be 0 + // for union members). + unsigned long tmp_off; + const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off); - if (name_tail.index.size() > mem_packed_dims.size()) { - cerr << get_fileline() << ": error: " - << "Too many index expressions for member." << endl; + if (member == 0) { + cerr << get_fileline() << ": error: Member " << member_name + << " is not a member of struct type of " + << reg->name() + << "." << completed_path << endl; + des->errors += 1; + return false; + } + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Member type: " << *(member->net_type) + << endl; + } + + 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 + // support bit select or none. + index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; + if (!member_comp.index.empty()) + use_sel = member_comp.index.back().sel; + + if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) { + cerr << get_fileline() << ": sorry: Assignments to part selects of " + "a struct member are not yet supported." << endl; des->errors += 1; return false; } - // Evaluate all but the last index expression, into prefix_indices. - listprefix_indices; - bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index); - ivl_assert(*this, rc); + if (! member_comp.index.empty()) { - // Evaluate the last index expression into a constant long. - NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true); - long tmp; - if (texpr == 0 || !eval_as_long(tmp, texpr)) { - cerr << get_fileline() << ": error: " - "Array index expressions must be constant here." << endl; - des->errors += 1; - return false; + // If there are index expressions in this l-value + // expression, then the implicit assumption is that the + // member is a vector type with packed dimensions. For + // example, if the l-value expression is "foo.member[1][2]", + // then the member should be something like: + // ... logic [h:l][m:n] foo; + // Get the dimensions from the netvector_t that this implies. + const netvector_t*mem_vec = dynamic_cast(member->net_type); + ivl_assert(*this, mem_vec); + const vector&mem_packed_dims = mem_vec->packed_dims(); + + if (member_comp.index.size() > mem_packed_dims.size()) { + cerr << get_fileline() << ": error: " + << "Too many index expressions for member." << endl; + des->errors += 1; + return false; + } + + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index); + ivl_assert(*this, rc); + + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true); + long tmp; + if (texpr == 0 || !eval_as_long(tmp, texpr)) { + cerr << get_fileline() << ": error: " + "Array index expressions must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); + + off += loff; + use_width = lwid; } - delete texpr; + // Complete this component of the path, mark it + // completed, and set up for the next component. + struct_type = dynamic_cast (member->net_type); + completed_path .push_back(member_comp); + member_path.pop_front(); - // Now use the prefix_to_slice function to calculate the - // offset and width of the addressed slice of the member. - long loff; - unsigned long lwid; - prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); + } while (member_path.size() > 0 && struct_type != 0); - off += loff; - use_width = lwid; + 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