Handle for loops with empty initialization statement

For loops may have empty initialization statements. In that case some things
can't be done, such as loop unrolling or synthesis, but otherwise it is a
valid thing to do. So generate the correct code in this case.
This commit is contained in:
Stephen Williams 2022-12-11 14:54:50 -08:00
parent 3509cc86f8
commit 78f37f7156
6 changed files with 127 additions and 57 deletions

View File

@ -1381,7 +1381,12 @@ void NetForever::dump(ostream&o, unsigned ind) const
void NetForLoop::dump(ostream&fd, unsigned ind) const
{
fd << setw(ind) << "" << "FOR LOOP index=" << index_->name() << endl;
fd << setw(ind) << "" << "FOR LOOP index=";
if (index_)
fd << index_->name();
else
fd << "<nil>";
fd << endl;
statement_->dump(fd, ind+4);
step_statement_->dump(fd, ind+4);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2021 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2022 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -5434,81 +5434,111 @@ NetProc* PForeach::elaborate_static_array_(Design*des, NetScope*scope,
}
/*
* elaborate the for loop as the equivalent while loop. This eases the
* task for the target code generator. The structure is:
* Elaborate the PForStatement as discovered by the parser into a
* NetForLoop object. The parser detects the:
*
* begin : top
* name1_ = expr1_;
* while (cond_) begin : body
* statement_;
* name2_ = expr2_;
* end
* end
* - index variable (name1_) (optional)
* - initial value (expr1_) (only if name1_ is present)
* - condition expression (cond_)
* - step statement (step_)
* - sub-statement (statement_)
*
* The rules that lead to the PForStatment look like:
*
* for ( <name1_> = <expr1_> ; <cond_> ; <step_> ) <statement_>
* for ( ; <cond_ ; <step_> ) <statement_>
*/
NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
{
NetExpr*initial_expr;
NetNet*sig;
bool error_flag = false;
assert(scope);
const PEIdent*id1 = dynamic_cast<const PEIdent*>(name1_);
assert(id1);
if (!name1_) {
// If there is no initial assignment expression, then mark that
// fact with null pointers.
ivl_assert(*this, !expr1_);
sig = nullptr;
initial_expr = nullptr;
/* make the expression, and later the initial assignment to
the condition variable. The statement in the for loop is
very specifically an assignment. */
NetNet*sig = des->find_signal(scope, id1->path());
if (sig == 0) {
cerr << id1->get_fileline() << ": register ``" << id1->path()
<< "'' unknown in " << scope_path(scope) << "." << endl;
} else if (const PEIdent*id1 = dynamic_cast<const PEIdent*>(name1_)) {
// If there is an initialization assignment, make the expression,
// and later the initial assignment to the condition variable. The
// statement in the for loop is very specifically an assignment.
sig = des->find_signal(scope, id1->path());
if (sig == 0) {
cerr << id1->get_fileline() << ": register ``" << id1->path()
<< "'' unknown in " << scope_path(scope) << "." << endl;
des->errors += 1;
return 0;
}
// Make the r-value of the initial assignment, and size it
// properly. Then use it to build the assignment statement.
initial_expr = elaborate_rval_expr(des, scope, sig->net_type(),
expr1_);
if (!initial_expr)
error_flag = true;
if (debug_elaborate && initial_expr) {
cerr << get_fileline() << ": debug: FOR initial assign: "
<< sig->name() << " = " << *initial_expr << endl;
}
} else {
cerr << get_fileline() << ": internal error: "
<< "Index name " << *name1_ << " is not a PEIdent." << endl;
des->errors += 1;
return 0;
}
assert(sig);
/* Make the r-value of the initial assignment, and size it
properly. Then use it to build the assignment statement. */
initial_expr = elaborate_rval_expr(des, scope, sig->net_type(),
expr1_);
if (debug_elaborate && initial_expr) {
cerr << get_fileline() << ": debug: FOR initial assign: "
<< sig->name() << " = " << *initial_expr << endl;
// Elaborate the statement that is contained in the for
// loop. If there is an error, this will return 0 and I should
// skip the append. No need to worry, the error has been
// reported so it's OK that the netlist is bogus.
NetProc*sub;
if (statement_) {
sub = statement_->elaborate(des, scope);
if (sub == 0)
error_flag = true;
} else {
sub = new NetBlock(NetBlock::SEQU, 0);
}
/* Elaborate the statement that is contained in the for
loop. If there is an error, this will return 0 and I should
skip the append. No need to worry, the error has been
reported so it's OK that the netlist is bogus. */
NetProc*sub;
if (statement_)
sub = statement_->elaborate(des, scope);
else
sub = new NetBlock(NetBlock::SEQU, 0);
/* Now elaborate the for_step statement. I really should do
some error checking here to make sure the step statement
really does step the variable. */
// Now elaborate the for_step statement. I really should do
// some error checking here to make sure the step statement
// really does step the variable.
NetProc*step = step_->elaborate(des, scope);
if (!step)
error_flag = true;
/* Elaborate the condition expression. Try to evaluate it too,
in case it is a constant. This is an interesting case
worthy of a warning. */
// Elaborate the condition expression. Try to evaluate it too,
// in case it is a constant. This is an interesting case
// worthy of a warning.
NetExpr*ce = elab_and_eval(des, scope, cond_, -1);
if (!ce)
error_flag = true;
if (dynamic_cast<NetEConst*>(ce)) {
cerr << get_fileline() << ": warning: condition expression "
"of for-loop is constant." << endl;
}
/* Error recovery - if we failed to elaborate any of the loop
expressions, give up now. */
if (initial_expr == 0 || ce == 0 || step == 0 || sub == 0) {
// Error recovery - if we failed to elaborate any of the loop
// expressions, give up now. Error counts where handled elsewhere.
if (error_flag) {
delete initial_expr;
delete ce;
delete step;
delete sub;
return 0;
}
/* All done, build up the loop. */
// All done, build up the loop. Note that sig and initial_expr may be
// nil. But if one is nil, then so is the other. The follow-on code
// can handle that case, but let's make sure with an assert that we
// have a consistent input.
ivl_assert(*this, sig || !initial_expr);
NetForLoop*loop = new NetForLoop(sig, initial_expr, ce, sub, step);
loop->set_line(*this);

View File

@ -207,12 +207,19 @@ NetForLoop::NetForLoop(NetNet*ind, NetExpr*iexpr, NetExpr*cond, NetProc*sub, Net
void NetForLoop::wrap_up()
{
NetBlock*top = new NetBlock(NetBlock::SEQU, 0);
top->set_line(*this);
NetAssign_*lv = new NetAssign_(index_);
NetAssign*set_stmt = new NetAssign(lv, init_expr_);
set_stmt->set_line(*init_expr_);
top->append(set_stmt);
// Handle the case that we are missing the initialization
// statement. This can happen for example with statments like this:
// for ( ; <condition> ; <step> ) <statement> ;
// If the index_ and init_expr_ are present, then generate the
// inital assignment and push it into the sequential block
if (index_ || init_expr_) {
top->set_line(*this);
NetAssign_*lv = new NetAssign_(index_);
NetAssign*set_stmt = new NetAssign(lv, init_expr_);
set_stmt->set_line(*init_expr_);
top->append(set_stmt);
}
NetBlock*internal_block = new NetBlock(NetBlock::SEQU, 0);
internal_block->set_line(*this);

10
parse.y
View File

@ -1,7 +1,7 @@
%{
/*
* Copyright (c) 1998-2021 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2022 Stephen Williams (steve@icarus.com)
* Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -1623,6 +1623,14 @@ loop_statement /* IEEE1800-2005: A.6.8 */
$$ = tmp;
}
// The initialization statement is optional.
| K_for '(' ';' expression ';' for_step ')'
statement_or_null
{ PForStatement*tmp = new PForStatement(nullptr, nullptr, $4, $6, $8);
FILE_NAME(tmp, @1);
$$ = tmp;
}
// Handle for_variable_declaration syntax by wrapping the for(...)
// statement in a synthetic named block. We can name the block
// after the variable that we are creating, that identifier is

View File

@ -1142,8 +1142,22 @@ void PForever::dump(ostream&out, unsigned ind) const
void PForStatement::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "" << "for (" << *name1_ << " = " << *expr1_
<< "; " << *cond_ << "; <for_step>)" << endl;
out << setw(ind) << "" << "for (";
if (name1_)
out << *name1_;
else
out << "<no-name1>";
out << " = ";
if (expr1_)
out << *expr1_;
else
out << "<no-expr1>";
out << "; ";
if (cond_)
out << *cond_;
else
out << "<no-cond>";
out << "; <for_step>)" << endl;
step_->dump(out, ind+6);
if (statement_)
statement_->dump(out, ind+3);

View File

@ -1453,6 +1453,12 @@ bool NetForLoop::synth_async(Design*des, NetScope*scope,
NexusSet&nex_map, NetBus&nex_out,
NetBus&enables, vector<mask_t>&bitmasks)
{
if (!index_) {
cerr << get_fileline() << ": sorry: Unable to synthesize for-loop without explicit index variable." << endl;
return false;
}
ivl_assert(*this, index_ && init_expr_);
if (debug_synth2) {
cerr << get_fileline() << ": NetForLoop::synth_async: "
<< "Index variable is " << index_->name() << endl;