Skip to content

Commit f602ae8

Browse files
committed
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.
1 parent 335db49 commit f602ae8

12 files changed

+184
-9
lines changed

Statement.cc

+10
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,16 @@ PForce::~PForce()
336336
delete expr_;
337337
}
338338

339+
PForeach::PForeach(perm_string av, perm_string ix, Statement*s)
340+
: array_var_(av), index_var_(ix), statement_(s)
341+
{
342+
}
343+
344+
PForeach::~PForeach()
345+
{
346+
delete statement_;
347+
}
348+
339349
PForever::PForever(Statement*s)
340350
: statement_(s)
341351
{

Statement.h

+16
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,22 @@ class PForce : public Statement {
443443
PExpr*expr_;
444444
};
445445

446+
class PForeach : public Statement {
447+
public:
448+
explicit PForeach(perm_string var, perm_string ix, Statement*stmt);
449+
~PForeach();
450+
451+
virtual NetProc* elaborate(Design*des, NetScope*scope) const;
452+
virtual void elaborate_scope(Design*des, NetScope*scope) const;
453+
virtual void elaborate_sig(Design*des, NetScope*scope) const;
454+
virtual void dump(ostream&out, unsigned ind) const;
455+
456+
private:
457+
perm_string array_var_;
458+
perm_string index_var_;
459+
Statement*statement_;
460+
};
461+
446462
class PForever : public Statement {
447463
public:
448464
explicit PForever(Statement*s);

elab_scope.cc

+12
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,18 @@ void PEventStatement::elaborate_scope(Design*des, NetScope*scope) const
18901890
statement_ -> elaborate_scope(des, scope);
18911891
}
18921892

1893+
/*
1894+
* The standard says that we create an implicit scope for foreach
1895+
* loops, but that is just to hold the index variables, and we'll
1896+
* handle them by creating unique names. So just jump into the
1897+
* contained statement for scope elaboration.
1898+
*/
1899+
void PForeach::elaborate_scope(Design*des, NetScope*scope) const
1900+
{
1901+
if (statement_)
1902+
statement_ -> elaborate_scope(des, scope);
1903+
}
1904+
18931905
/*
18941906
* Statements that contain a further statement but do not
18951907
* intrinsically add a scope need to elaborate_scope the contained

elab_sig.cc

+6
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,12 @@ void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const
806806
statement_->elaborate_sig(des, scope);
807807
}
808808

809+
void PForeach::elaborate_sig(Design*des, NetScope*scope) const
810+
{
811+
if (statement_)
812+
statement_->elaborate_sig(des, scope);
813+
}
814+
809815
void PForever::elaborate_sig(Design*des, NetScope*scope) const
810816
{
811817
if (statement_)

elaborate.cc

+59
Original file line numberDiff line numberDiff line change
@@ -4575,6 +4575,65 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const
45754575
return dev;
45764576
}
45774577

4578+
/*
4579+
* The foreach statement can be written as a for statement like so:
4580+
*
4581+
* for (<idx> = $low(<array>) ; <idx> <= $high(<array>) ; <idx> += 1)
4582+
* <statement_>
4583+
*
4584+
* The <idx> variable is already known to be in the containing named
4585+
* block scope, which was created by the parser.
4586+
*/
4587+
NetProc* PForeach::elaborate(Design*des, NetScope*scope) const
4588+
{
4589+
// Get the signal for the index variable.
4590+
pform_name_t index_name;
4591+
index_name.push_back(name_component_t(index_var_));
4592+
NetNet*idx_sig = des->find_signal(scope, index_name);
4593+
ivl_assert(*this, idx_sig);
4594+
4595+
NetESignal*idx_exp = new NetESignal(idx_sig);
4596+
idx_exp->set_line(*this);
4597+
4598+
// Get the signal for the array variable
4599+
pform_name_t array_name;
4600+
array_name.push_back(name_component_t(array_var_));
4601+
NetNet*array_sig = des->find_signal(scope, array_name);
4602+
ivl_assert(*this, array_sig);
4603+
4604+
NetESignal*array_exp = new NetESignal(array_sig);
4605+
array_exp->set_line(*this);
4606+
4607+
// Make an initialization expression for the index.
4608+
NetESFunc*init_expr = new NetESFunc("$low", IVL_VT_BOOL, 32, 1);
4609+
init_expr->set_line(*this);
4610+
init_expr->parm(0, array_exp);
4611+
4612+
// Make a condition expression: idx <= $high(array)
4613+
NetESFunc*high_exp = new NetESFunc("$high", IVL_VT_BOOL, 32, 1);
4614+
high_exp->set_line(*this);
4615+
high_exp->parm(0, array_exp);
4616+
4617+
NetEBComp*cond_expr = new NetEBComp('L', idx_exp, high_exp);
4618+
cond_expr->set_line(*this);
4619+
4620+
/* Elaborate the statement that is contained in the foreach
4621+
loop. */
4622+
NetProc*sub = statement_->elaborate(des, scope);
4623+
4624+
/* Make a step statement: idx += 1 */
4625+
NetAssign_*idx_lv = new NetAssign_(idx_sig);
4626+
NetEConst*step_val = make_const_val(1);
4627+
NetAssign*step = new NetAssign(idx_lv, '+', step_val);
4628+
step->set_line(*this);
4629+
4630+
NetForLoop*stmt = new NetForLoop(idx_sig, init_expr, cond_expr, sub, step);
4631+
stmt->set_line(*this);
4632+
stmt->wrap_up();
4633+
4634+
return stmt;
4635+
}
4636+
45784637
/*
45794638
* elaborate the for loop as the equivalent while loop. This eases the
45804639
* task for the target code generator. The structure is:

parse.y

+23-6
Original file line numberDiff line numberDiff line change
@@ -1376,12 +1376,29 @@ loop_statement /* IEEE1800-2005: A.6.8 */
13761376
$$ = tmp;
13771377
}
13781378

1379-
| K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' statement_or_null
1380-
{ yyerror(@1, "sorry: foreach loops not supported");
1381-
delete[]$3;
1382-
delete $5;
1383-
delete $8;
1384-
$$ = 0;
1379+
// When matching a foreach loop, implicitly create a named block
1380+
// to hold the definitions for the index variables.
1381+
| K_foreach '(' IDENTIFIER '[' loop_variables ']' ')'
1382+
{ static unsigned foreach_counter = 0;
1383+
char for_block_name[64];
1384+
snprintf(for_block_name, sizeof for_block_name, "$ivl_foreach%u", foreach_counter);
1385+
foreach_counter += 1;
1386+
1387+
PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ);
1388+
FILE_NAME(tmp, @1);
1389+
current_block_stack.push(tmp);
1390+
1391+
pform_make_foreach_declarations(@1, $5);
1392+
}
1393+
statement_or_null
1394+
{ PForeach*tmp_for = pform_make_foreach(@1, $3, $5, $9);
1395+
1396+
pform_pop_scope();
1397+
vector<Statement*>tmp_for_list(1);
1398+
tmp_for_list[0] = tmp_for;
1399+
PBlock*tmp_blk = current_block_stack.top();
1400+
tmp_blk->set_statement(tmp_for_list);
1401+
$$ = tmp_blk;
13851402
}
13861403

13871404
/* Error forms for loop statements. */

pform.cc

+36
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,42 @@ PCallTask* pform_make_call_task(const struct vlltype&loc,
704704
return tmp;
705705
}
706706

707+
void pform_make_foreach_declarations(const struct vlltype&loc,
708+
std::list<perm_string>*loop_vars)
709+
{
710+
static const struct str_pair_t str = { IVL_DR_STRONG, IVL_DR_STRONG };
711+
712+
list<decl_assignment_t*>assign_list;
713+
for (list<perm_string>::const_iterator cur = loop_vars->begin()
714+
; cur != loop_vars->end() ; ++ cur) {
715+
decl_assignment_t*tmp_assign = new decl_assignment_t;
716+
tmp_assign->name = lex_strings.make(*cur);
717+
assign_list.push_back(tmp_assign);
718+
}
719+
720+
pform_makewire(loc, 0, str, &assign_list, NetNet::REG, &size_type);
721+
}
722+
723+
PForeach* pform_make_foreach(const struct vlltype&loc,
724+
char*name,
725+
list<perm_string>*loop_vars,
726+
Statement*stmt)
727+
{
728+
perm_string use_name = lex_strings.make(name);
729+
delete[]name;
730+
731+
perm_string use_index = loop_vars->front();
732+
loop_vars->pop_front();
733+
734+
ivl_assert(loc, loop_vars->empty());
735+
delete loop_vars;
736+
737+
PForeach*fe = new PForeach(use_name, use_index, stmt);
738+
FILE_NAME(fe, loc);
739+
740+
return fe;
741+
}
742+
707743
static void pform_put_behavior_in_scope(PProcess*pp)
708744
{
709745
lexical_scope->behaviors.push_back(pp);

pform.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ extern PCallTask* pform_make_call_task(const struct vlltype&loc,
285285
const pform_name_t&name,
286286
const std::list<PExpr*>&parms);
287287

288+
extern void pform_make_foreach_declarations(const struct vlltype&loc,
289+
std::list<perm_string>*loop_vars);
290+
extern PForeach* pform_make_foreach(const struct vlltype&loc,
291+
char*ident,
292+
std::list<perm_string>*loop_vars,
293+
Statement*stmt);
288294

289295
/*
290296
* 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
349355

350356
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);
351357

352-
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);
358+
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);
353359

354360
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);
355361

pform_dump.cc

+9
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,15 @@ void PForce::dump(ostream&out, unsigned ind) const
967967
<< "; /* " << get_fileline() << " */" << endl;
968968
}
969969

970+
void PForeach::dump(ostream&fd, unsigned ind) const
971+
{
972+
fd << setw(ind) << "" << "foreach "
973+
<< "variable=" << array_var_
974+
<< ", index=" << index_var_
975+
<< " /* " << get_fileline() << " */" << endl;
976+
statement_->dump(fd, ind+3);
977+
}
978+
970979
void PForever::dump(ostream&out, unsigned ind) const
971980
{
972981
out << setw(ind) << "" << "forever /* " << get_fileline() << " */" << endl;

pform_string_type.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@
2121
# include "parse_misc.h"
2222
# include "ivl_assert.h"
2323

24-
static void pform_set_string_type(string_type_t*, perm_string name, NetNet::Type net_type, list<named_pexpr_t>*attr)
24+
static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list<named_pexpr_t>*attr)
2525
{
2626
PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING);
2727
pform_bind_attributes(net->attributes, attr, true);
2828
}
2929

30-
void pform_set_string_type(string_type_t*string_type, list<perm_string>*names, NetNet::Type net_type, list<named_pexpr_t>*attr)
30+
void pform_set_string_type(const string_type_t*string_type, list<perm_string>*names, NetNet::Type net_type, list<named_pexpr_t>*attr)
3131
{
3232
for (list<perm_string>::iterator cur = names->begin()
3333
; cur != names->end() ; ++ cur) {

pform_types.cc

+2
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ ivl_variable_type_t vector_type_t::figure_packed_base_type(void) const
4242
{
4343
return base_type;
4444
}
45+
46+
atom2_type_t size_type (32, true);

pform_types.h

+2
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ struct atom2_type_t : public data_type_t {
182182
ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const;
183183
};
184184

185+
extern atom2_type_t size_type;
186+
185187
/*
186188
* The vector_type_t class represents types in the old Verilog
187189
* way. Some typical examples:

0 commit comments

Comments
 (0)