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.
This commit is contained in:
Stephen Williams 2014-08-21 15:47:46 -07:00
parent 335db49282
commit f602ae84ab
12 changed files with 184 additions and 9 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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_)

View File

@ -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 (<idx> = $low(<array>) ; <idx> <= $high(<array>) ; <idx> += 1)
* <statement_>
*
* The <idx> 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:

29
parse.y
View File

@ -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();
vector<Statement*>tmp_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. */

View File

@ -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<perm_string>*loop_vars)
{
static const struct str_pair_t str = { IVL_DR_STRONG, IVL_DR_STRONG };
list<decl_assignment_t*>assign_list;
for (list<perm_string>::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<perm_string>*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);

View File

@ -285,6 +285,12 @@ extern PCallTask* pform_make_call_task(const struct vlltype&loc,
const pform_name_t&name,
const std::list<PExpr*>&parms);
extern void pform_make_foreach_declarations(const struct vlltype&loc,
std::list<perm_string>*loop_vars);
extern PForeach* pform_make_foreach(const struct vlltype&loc,
char*ident,
std::list<perm_string>*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<perm
extern void pform_set_struct_type(struct_type_t*struct_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*attr);
extern void pform_set_string_type(string_type_t*string_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*attr);
extern void pform_set_string_type(const string_type_t*string_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*attr);
extern void pform_set_class_type(class_type_t*class_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*addr);

View File

@ -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;

View File

@ -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<named_pexpr_t>*attr)
static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list<named_pexpr_t>*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<perm_string>*names, NetNet::Type net_type, list<named_pexpr_t>*attr)
void pform_set_string_type(const string_type_t*string_type, list<perm_string>*names, NetNet::Type net_type, list<named_pexpr_t>*attr)
{
for (list<perm_string>::iterator cur = names->begin()
; cur != names->end() ; ++ cur) {

View File

@ -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);

View File

@ -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: