Merge pull request #807 from steveicarus/steveicarus/issue801-empty-for-init

Handle empty for-loop init statement
This commit is contained in:
Stephen Williams 2022-12-11 16:07:24 -08:00 committed by GitHub
commit bb779112c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 144 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
@ -5451,81 +5451,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

@ -0,0 +1,16 @@
module main;
initial begin
int idx;
idx = 1;
for ( ; idx < 5 ; idx += 1) begin
$display("... %02d", idx);
end
if (idx !== 5) begin
$display("FAILED -- idx=%0d", idx);
$finish;
end
$display("PASSED");
$finish;
end
endmodule // main

View File

@ -221,6 +221,7 @@ br_gh672 normal,-g2009 ivltests
br_gh699 CE,-g2009 ivltests
br_gh756 normal,-g2009 ivltests
br_gh782a normal,-g2009 ivltests gold=br_gh782a.gold
br_gh801 normal,-g2009 ivltests
br_ml20171017 normal,-g2009 ivltests
br_ml20180227 CE,-g2009 ivltests
br_ml20180309a normal,-g2009 ivltests

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;