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:
parent
335db49282
commit
f602ae84ab
10
Statement.cc
10
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)
|
||||
{
|
||||
|
|
|
|||
16
Statement.h
16
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_)
|
||||
|
|
|
|||
59
elaborate.cc
59
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 (<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
29
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();
|
||||
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. */
|
||||
|
|
|
|||
36
pform.cc
36
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<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);
|
||||
|
|
|
|||
8
pform.h
8
pform.h
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue