From f602ae84ab7caee0ec7bd075e2300ca6709b69b0 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Thu, 21 Aug 2014 15:47:46 -0700 Subject: [PATCH] Elaborate foreach loops as synthetic for loops. Create an implicit scope to hold the index variable, and generate a for loop to perform the functionality of the foreach. --- Statement.cc | 10 ++++++++ Statement.h | 16 ++++++++++++ elab_scope.cc | 12 +++++++++ elab_sig.cc | 6 +++++ elaborate.cc | 59 ++++++++++++++++++++++++++++++++++++++++++++ parse.y | 29 +++++++++++++++++----- pform.cc | 36 +++++++++++++++++++++++++++ pform.h | 8 +++++- pform_dump.cc | 9 +++++++ pform_string_type.cc | 4 +-- pform_types.cc | 2 ++ pform_types.h | 2 ++ 12 files changed, 184 insertions(+), 9 deletions(-) diff --git a/Statement.cc b/Statement.cc index f30b6f931..f0a395fd8 100644 --- a/Statement.cc +++ b/Statement.cc @@ -336,6 +336,16 @@ PForce::~PForce() delete expr_; } +PForeach::PForeach(perm_string av, perm_string ix, Statement*s) +: array_var_(av), index_var_(ix), statement_(s) +{ +} + +PForeach::~PForeach() +{ + delete statement_; +} + PForever::PForever(Statement*s) : statement_(s) { diff --git a/Statement.h b/Statement.h index b36ef239d..a3f17a1fa 100644 --- a/Statement.h +++ b/Statement.h @@ -443,6 +443,22 @@ class PForce : public Statement { PExpr*expr_; }; +class PForeach : public Statement { + public: + explicit PForeach(perm_string var, perm_string ix, Statement*stmt); + ~PForeach(); + + virtual NetProc* elaborate(Design*des, NetScope*scope) const; + virtual void elaborate_scope(Design*des, NetScope*scope) const; + virtual void elaborate_sig(Design*des, NetScope*scope) const; + virtual void dump(ostream&out, unsigned ind) const; + + private: + perm_string array_var_; + perm_string index_var_; + Statement*statement_; +}; + class PForever : public Statement { public: explicit PForever(Statement*s); diff --git a/elab_scope.cc b/elab_scope.cc index 2d70d938b..07bdd491b 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1890,6 +1890,18 @@ void PEventStatement::elaborate_scope(Design*des, NetScope*scope) const statement_ -> elaborate_scope(des, scope); } +/* + * The standard says that we create an implicit scope for foreach + * loops, but that is just to hold the index variables, and we'll + * handle them by creating unique names. So just jump into the + * contained statement for scope elaboration. + */ +void PForeach::elaborate_scope(Design*des, NetScope*scope) const +{ + if (statement_) + statement_ -> elaborate_scope(des, scope); +} + /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained diff --git a/elab_sig.cc b/elab_sig.cc index 862f94f4e..6c0318439 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -806,6 +806,12 @@ void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const statement_->elaborate_sig(des, scope); } +void PForeach::elaborate_sig(Design*des, NetScope*scope) const +{ + if (statement_) + statement_->elaborate_sig(des, scope); +} + void PForever::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) diff --git a/elaborate.cc b/elaborate.cc index 4cb9c41ec..a9743834b 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4575,6 +4575,65 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const return dev; } +/* + * The foreach statement can be written as a for statement like so: + * + * for ( = $low() ; <= $high() ; += 1) + * + * + * The variable is already known to be in the containing named + * block scope, which was created by the parser. + */ +NetProc* PForeach::elaborate(Design*des, NetScope*scope) const +{ + // Get the signal for the index variable. + pform_name_t index_name; + index_name.push_back(name_component_t(index_var_)); + NetNet*idx_sig = des->find_signal(scope, index_name); + ivl_assert(*this, idx_sig); + + NetESignal*idx_exp = new NetESignal(idx_sig); + idx_exp->set_line(*this); + + // Get the signal for the array variable + pform_name_t array_name; + array_name.push_back(name_component_t(array_var_)); + NetNet*array_sig = des->find_signal(scope, array_name); + ivl_assert(*this, array_sig); + + NetESignal*array_exp = new NetESignal(array_sig); + array_exp->set_line(*this); + + // Make an initialization expression for the index. + NetESFunc*init_expr = new NetESFunc("$low", IVL_VT_BOOL, 32, 1); + init_expr->set_line(*this); + init_expr->parm(0, array_exp); + + // Make a condition expression: idx <= $high(array) + NetESFunc*high_exp = new NetESFunc("$high", IVL_VT_BOOL, 32, 1); + high_exp->set_line(*this); + high_exp->parm(0, array_exp); + + NetEBComp*cond_expr = new NetEBComp('L', idx_exp, high_exp); + cond_expr->set_line(*this); + + /* Elaborate the statement that is contained in the foreach + loop. */ + NetProc*sub = statement_->elaborate(des, scope); + + /* Make a step statement: idx += 1 */ + NetAssign_*idx_lv = new NetAssign_(idx_sig); + NetEConst*step_val = make_const_val(1); + NetAssign*step = new NetAssign(idx_lv, '+', step_val); + step->set_line(*this); + + NetForLoop*stmt = new NetForLoop(idx_sig, init_expr, cond_expr, sub, step); + stmt->set_line(*this); + stmt->wrap_up(); + + return stmt; +} + /* * elaborate the for loop as the equivalent while loop. This eases the * task for the target code generator. The structure is: diff --git a/parse.y b/parse.y index 1f40e609c..42010d232 100644 --- a/parse.y +++ b/parse.y @@ -1376,12 +1376,29 @@ loop_statement /* IEEE1800-2005: A.6.8 */ $$ = tmp; } - | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' statement_or_null - { yyerror(@1, "sorry: foreach loops not supported"); - delete[]$3; - delete $5; - delete $8; - $$ = 0; + // When matching a foreach loop, implicitly create a named block + // to hold the definitions for the index variables. + | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' + { static unsigned foreach_counter = 0; + char for_block_name[64]; + snprintf(for_block_name, sizeof for_block_name, "$ivl_foreach%u", foreach_counter); + foreach_counter += 1; + + PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ); + FILE_NAME(tmp, @1); + current_block_stack.push(tmp); + + pform_make_foreach_declarations(@1, $5); + } + statement_or_null + { PForeach*tmp_for = pform_make_foreach(@1, $3, $5, $9); + + pform_pop_scope(); + vectortmp_for_list(1); + tmp_for_list[0] = tmp_for; + PBlock*tmp_blk = current_block_stack.top(); + tmp_blk->set_statement(tmp_for_list); + $$ = tmp_blk; } /* Error forms for loop statements. */ diff --git a/pform.cc b/pform.cc index 403612ff1..02214b64a 100644 --- a/pform.cc +++ b/pform.cc @@ -704,6 +704,42 @@ PCallTask* pform_make_call_task(const struct vlltype&loc, return tmp; } +void pform_make_foreach_declarations(const struct vlltype&loc, + std::list*loop_vars) +{ + static const struct str_pair_t str = { IVL_DR_STRONG, IVL_DR_STRONG }; + + listassign_list; + for (list::const_iterator cur = loop_vars->begin() + ; cur != loop_vars->end() ; ++ cur) { + decl_assignment_t*tmp_assign = new decl_assignment_t; + tmp_assign->name = lex_strings.make(*cur); + assign_list.push_back(tmp_assign); + } + + pform_makewire(loc, 0, str, &assign_list, NetNet::REG, &size_type); +} + +PForeach* pform_make_foreach(const struct vlltype&loc, + char*name, + list*loop_vars, + Statement*stmt) +{ + perm_string use_name = lex_strings.make(name); + delete[]name; + + perm_string use_index = loop_vars->front(); + loop_vars->pop_front(); + + ivl_assert(loc, loop_vars->empty()); + delete loop_vars; + + PForeach*fe = new PForeach(use_name, use_index, stmt); + FILE_NAME(fe, loc); + + return fe; +} + static void pform_put_behavior_in_scope(PProcess*pp) { lexical_scope->behaviors.push_back(pp); diff --git a/pform.h b/pform.h index 55035b1c6..b1b3039c7 100644 --- a/pform.h +++ b/pform.h @@ -285,6 +285,12 @@ extern PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const std::list&parms); +extern void pform_make_foreach_declarations(const struct vlltype&loc, + std::list*loop_vars); +extern PForeach* pform_make_foreach(const struct vlltype&loc, + char*ident, + std::list*loop_vars, + Statement*stmt); /* * The makewire functions announce to the pform code new wires. These @@ -349,7 +355,7 @@ extern void pform_set_data_type(const struct vlltype&li, data_type_t*, list*names, NetNet::Type net_type, std::list*attr); -extern void pform_set_string_type(string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); +extern void pform_set_string_type(const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); extern void pform_set_class_type(class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); diff --git a/pform_dump.cc b/pform_dump.cc index b01966e94..edadc055c 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -967,6 +967,15 @@ void PForce::dump(ostream&out, unsigned ind) const << "; /* " << get_fileline() << " */" << endl; } +void PForeach::dump(ostream&fd, unsigned ind) const +{ + fd << setw(ind) << "" << "foreach " + << "variable=" << array_var_ + << ", index=" << index_var_ + << " /* " << get_fileline() << " */" << endl; + statement_->dump(fd, ind+3); +} + void PForever::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "forever /* " << get_fileline() << " */" << endl; diff --git a/pform_string_type.cc b/pform_string_type.cc index 569056764..4d1f1d2dc 100644 --- a/pform_string_type.cc +++ b/pform_string_type.cc @@ -21,13 +21,13 @@ # include "parse_misc.h" # include "ivl_assert.h" -static void pform_set_string_type(string_type_t*, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) { PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); pform_bind_attributes(net->attributes, attr, true); } -void pform_set_string_type(string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_string_type(const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { diff --git a/pform_types.cc b/pform_types.cc index 5a7868bef..8d8d430d0 100644 --- a/pform_types.cc +++ b/pform_types.cc @@ -42,3 +42,5 @@ ivl_variable_type_t vector_type_t::figure_packed_base_type(void) const { return base_type; } + +atom2_type_t size_type (32, true); diff --git a/pform_types.h b/pform_types.h index 58b46f188..15d3a4853 100644 --- a/pform_types.h +++ b/pform_types.h @@ -182,6 +182,8 @@ struct atom2_type_t : public data_type_t { ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; }; +extern atom2_type_t size_type; + /* * The vector_type_t class represents types in the old Verilog * way. Some typical examples: