Fix constant functions with and begin/end

This commit is contained in:
Wilson Snyder 2009-07-21 14:31:16 -04:00
parent 584cfa4d4a
commit 15b3c9797b
7 changed files with 144 additions and 124 deletions

View File

@ -1410,8 +1410,7 @@ struct AstRepeat : public AstNodeStmt {
ASTNODE_NODE_FUNCS(Repeat, REPEAT)
AstNode* countp() const { return op2p()->castNode(); } // op2= condition to continue
AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop
virtual bool isGateOptimizable() const { return false; }
virtual bool isPredictOptimizable() const { return false; }
virtual bool isGateOptimizable() const { return false; } // Not releavant - converted to FOR
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }

View File

@ -830,10 +830,8 @@ private:
void replaceWithSimulation(AstNode* nodep) {
SimulateVisitor simvis;
simvis.mainParamCheck(nodep);
if (simvis.optimizable()) { // Run it - may also be unoptimizable due to large for loop
simvis.mainParamEmulate(nodep);
}
// Run it - may be unoptimizable due to large for loop, etc
simvis.mainParamEmulate(nodep);
if (!simvis.optimizable()) {
AstNode* errorp = simvis.whyNotNodep(); if (!errorp) errorp = nodep;
nodep->v3error("Expecting expression to be constant, but can't determine constant for "

View File

@ -74,7 +74,7 @@ private:
// STATE
// Major mode
bool m_checking; ///< Checking vs. simulation mode
bool m_checkOnly; ///< Checking only (no simulation) mode
bool m_scoped; ///< Running with AstVarScopes instead of AstVars
bool m_params; ///< Doing parameter propagation
// Checking:
@ -102,6 +102,8 @@ public:
virtual void varRefCb(AstVarRef* nodep) {}
void clearOptimizable(AstNode* nodep/*null ok*/, const string& why) {
// Something bad found. optimizable() will return false,
// and fetchNumber should not be called or it may assert.
if (!m_whyNotNodep) {
m_whyNotNodep = nodep;
if (debug()>=5) {
@ -112,7 +114,7 @@ public:
m_whyNotOptimizable = why;
}
}
bool optimizable() const { return m_whyNotNodep==NULL; }
inline bool optimizable() const { return m_whyNotNodep==NULL; }
string whyNotMessage() const { return m_whyNotOptimizable; }
AstNode* whyNotNodep() const { return m_whyNotNodep; }
@ -189,8 +191,10 @@ private:
}
void checkNodeInfo(AstNode* nodep) {
m_instrCount += nodep->instrCount();
m_dataCount += nodep->width();
if (m_checkOnly) {
m_instrCount += nodep->instrCount();
m_dataCount += nodep->width();
}
if (!nodep->isPredictOptimizable()) {
//UINFO(9," !predictopt "<<nodep<<endl);
clearOptimizable(nodep,"Isn't predictable");
@ -199,18 +203,14 @@ private:
void badNodeType(AstNode* nodep) {
// Call for default node types, or other node types we don't know how to handle
if (m_checking) {
checkNodeInfo(nodep);
if (optimizable()) {
// Hmm, what is this then?
// In production code, we'll just not optimize. It should be fixed though.
clearOptimizable(nodep, "Unknown node type, perhaps missing visitor in SimulateVisitor");
checkNodeInfo(nodep);
if (optimizable()) {
// Hmm, what is this then?
// In production code, we'll just not optimize. It should be fixed though.
clearOptimizable(nodep, "Unknown node type, perhaps missing visitor in SimulateVisitor");
#ifdef VL_DEBUG
UINFO(0,"Unknown node type in SimulateVisitor: "<<nodep->prettyTypeName()<<endl);
UINFO(0,"Unknown node type in SimulateVisitor: "<<nodep->prettyTypeName()<<endl);
#endif
}
} else { // simulating
nodep->v3fatalSrc("Optimizable should have been cleared in check step, and never reach simulation.");
}
}
@ -229,42 +229,41 @@ private:
// VISITORS
virtual void visit(AstAlways* nodep, AstNUser*) {
if (m_checking) checkNodeInfo(nodep);
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
}
virtual void visit(AstSenTree* nodep, AstNUser*) {
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (!optimizable()) return; // Accelerate
AstNode* vscp = varOrScope(nodep);
if (m_checking) {
if (m_checking && !optimizable()) return; // Accelerate
// We can't have non-delayed assignments with same value on LHS and RHS
// as we don't figure out variable ordering.
// Delayed is OK though, as we'll decode the next state separately.
if (nodep->varp()->arraysp()) clearOptimizable(nodep,"Array references");
if (nodep->lvalue()) {
if (m_inDlyAssign) {
if (!(vscp->user1() & VU_LVDLY)) {
vscp->user1( vscp->user1() | VU_LVDLY);
varRefCb (nodep);
}
} else { // nondly asn
if (!(vscp->user1() & VU_LV)) {
if (!m_params && (vscp->user1() & VU_RV)) clearOptimizable(nodep,"Var read & write");
vscp->user1( vscp->user1() | VU_LV);
varRefCb (nodep);
}
// We can't have non-delayed assignments with same value on LHS and RHS
// as we don't figure out variable ordering.
// Delayed is OK though, as we'll decode the next state separately.
if (nodep->varp()->arraysp()) clearOptimizable(nodep,"Array references");
if (nodep->lvalue()) {
if (m_inDlyAssign) {
if (!(vscp->user1() & VU_LVDLY)) {
vscp->user1( vscp->user1() | VU_LVDLY);
if (m_checkOnly) varRefCb (nodep);
}
} else {
if (!(vscp->user1() & VU_RV)) {
if (!m_params && (vscp->user1() & VU_LV)) clearOptimizable(nodep,"Var write & read");
vscp->user1( vscp->user1() | VU_RV);
varRefCb (nodep);
} else { // nondly asn
if (!(vscp->user1() & VU_LV)) {
if (!m_params && (vscp->user1() & VU_RV)) clearOptimizable(nodep,"Var read & write");
vscp->user1( vscp->user1() | VU_LV);
if (m_checkOnly) varRefCb (nodep);
}
}
} else {
if (!(vscp->user1() & VU_RV)) {
if (!m_params && (vscp->user1() & VU_LV)) clearOptimizable(nodep,"Var write & read");
vscp->user1( vscp->user1() | VU_RV);
if (m_checkOnly) varRefCb (nodep);
}
}
else { // simulating
if (!m_checkOnly && optimizable()) { // simulating
if (nodep->lvalue()) {
nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor.");
} else {
@ -293,52 +292,47 @@ private:
}
virtual void visit(AstNodeIf* nodep, AstNUser*) {
UINFO(5," IF "<<nodep<<endl);
if (m_checking) {
checkNodeInfo(nodep);
checkNodeInfo(nodep);
if (m_checkOnly) {
nodep->iterateChildren(*this);
} else {
nodep->condp()->iterateAndNext(*this);
if (fetchNumber(nodep->condp())->isNeqZero()) {
nodep->ifsp()->iterateAndNext(*this);
} else {
nodep->elsesp()->iterateAndNext(*this);
if (optimizable()) {
if (fetchNumber(nodep->condp())->isNeqZero()) {
nodep->ifsp()->iterateAndNext(*this);
} else {
nodep->elsesp()->iterateAndNext(*this);
}
}
}
}
virtual void visit(AstConst* nodep, AstNUser*) {
if (m_checking) {
checkNodeInfo(nodep);
} else {
checkNodeInfo(nodep);
if (!m_checkOnly && optimizable()) {
setNumber(nodep, &(nodep->num()));
}
}
virtual void visit(AstNodeUniop* nodep, AstNUser*) {
if (m_checking) {
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
} else {
nodep->iterateChildren(*this);
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
if (!m_checkOnly && optimizable()) {
nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()));
}
}
virtual void visit(AstNodeBiop* nodep, AstNUser*) {
if (m_checking) {
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
} else {
nodep->iterateChildren(*this);
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
if (!m_checkOnly && optimizable()) {
nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp()));
}
}
virtual void visit(AstNodeTriop* nodep, AstNUser*) {
if (m_checking) {
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
} else {
nodep->iterateChildren(*this);
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
if (!m_checkOnly && optimizable()) {
nodep->numberOperate(*newNumber(nodep),
*fetchNumber(nodep->lhsp()),
*fetchNumber(nodep->rhsp()),
@ -348,57 +342,65 @@ private:
virtual void visit(AstNodeCond* nodep, AstNUser*) {
// We could use above visit(AstNodeTriop), but it's slower even O(n^2) to evaluate
// both sides when we really only need to evaluate one side.
if (m_checking) {
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
if (m_checkOnly) {
nodep->iterateChildren(*this);
} else {
nodep->condp()->accept(*this);
if (fetchNumber(nodep->condp())->isNeqZero()) {
nodep->expr1p()->accept(*this);
newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p()));
} else {
nodep->expr2p()->accept(*this);
newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p()));
if (optimizable()) {
if (fetchNumber(nodep->condp())->isNeqZero()) {
nodep->expr1p()->accept(*this);
newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p()));
} else {
nodep->expr2p()->accept(*this);
newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p()));
}
}
}
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
if (m_checking) {
if (!optimizable()) return; // Accelerate
if (nodep->castAssignDly()) {
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
m_anyAssignDly = true;
m_inDlyAssign = true;
} else {
if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non dly assigns");
m_anyAssignComb = true;
}
nodep->iterateChildren(*this);
if (!optimizable()) return; // Accelerate
if (nodep->castAssignDly()) {
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
m_anyAssignDly = true;
m_inDlyAssign = true;
} else {
if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non dly assigns");
m_anyAssignComb = true;
}
if (!nodep->lhsp()->castVarRef()) {
clearOptimizable(nodep, "LHS isn't simple variable");
}
else if (!m_checking) {
else if (m_checkOnly) {
nodep->iterateChildren(*this);
}
else if (optimizable()) {
nodep->rhsp()->iterateAndNext(*this);
AstNode* vscp = varOrScope(nodep->lhsp()->castVarRef());
// Copy by value, not reference, as we don't want a=a+1 to get right results
if (nodep->castAssignDly()) {
// Don't do setNumber, as value isn't yet visible to following statements
newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
} else {
newNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
if (optimizable()) {
AstNode* vscp = varOrScope(nodep->lhsp()->castVarRef());
// Copy by value, not reference, as we don't want a=a+1 to get right results
if (nodep->castAssignDly()) {
// Don't do setNumber, as value isn't yet visible to following statements
newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
} else {
newNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp()));
}
}
}
m_inDlyAssign = false;
}
virtual void visit(AstBegin* nodep, AstNUser*) {
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeCase* nodep, AstNUser*) {
UINFO(5," CASE "<<nodep<<endl);
if (m_checking) {
checkNodeInfo(nodep);
checkNodeInfo(nodep);
if (m_checkOnly) {
nodep->iterateChildren(*this);
} else {
} else if (optimizable()) {
nodep->exprp()->iterateAndNext(*this);
bool hit = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
@ -406,11 +408,13 @@ private:
for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) {
if (hit) break;
ep->iterateAndNext(*this);
V3Number match (nodep->fileline(), 1);
match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep));
if (match.isNeqZero()) {
itemp->bodysp()->iterateAndNext(*this);
hit = true;
if (optimizable()) {
V3Number match (nodep->fileline(), 1);
match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep));
if (match.isNeqZero()) {
itemp->bodysp()->iterateAndNext(*this);
hit = true;
}
}
}
}
@ -434,19 +438,28 @@ private:
virtual void visit(AstComment*, AstNUser*) {}
virtual void visit(AstStop* nodep, AstNUser*) {
if (m_params) { // This message seems better than an obscure $stop
// The spec says $stop is just ignored, it seems evil to ignore assertions
clearOptimizable(nodep,"$stop executed during function constification; maybe indicates assertion firing");
}
checkNodeInfo(nodep);
}
virtual void visit(AstNodeFor* nodep, AstNUser*) {
// Doing lots of Whiles is slow, so only for parameters
UINFO(5," FOR "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
if (m_checking) {
checkNodeInfo(nodep);
checkNodeInfo(nodep);
if (m_checkOnly) {
nodep->iterateChildren(*this);
} else {
} else if (optimizable()) {
int loops = 0;
nodep->initsp()->iterateAndNext(*this);
while (1) {
UINFO(5," FOR-ITER "<<nodep<<endl);
nodep->condp()->iterateAndNext(*this);
if (!optimizable()) break;
if (!fetchNumber(nodep->condp())->isNeqZero()) {
break;
}
@ -464,15 +477,16 @@ private:
// Doing lots of Whiles is slow, so only for parameters
UINFO(5," WHILE "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
if (m_checking) {
checkNodeInfo(nodep);
checkNodeInfo(nodep);
if (m_checkOnly) {
nodep->iterateChildren(*this);
} else {
} else if (optimizable()) {
int loops = 0;
while (1) {
UINFO(5," WHILE-ITER "<<nodep<<endl);
nodep->precondsp()->iterateAndNext(*this);
nodep->condp()->iterateAndNext(*this);
if (!optimizable()) break;
if (!fetchNumber(nodep->condp())->isNeqZero()) {
break;
}
@ -507,7 +521,7 @@ private:
// Evaluate pin value
pinp->accept(*this);
// Apply value to the function
if (!m_checking) {
if (!m_checkOnly && optimizable()) {
newNumber(stmtp)->opAssign(*fetchNumber(pinp));
}
}
@ -516,7 +530,7 @@ private:
}
// Evaluate the function
funcp->accept(*this);
if (!m_checking) {
if (!m_checkOnly && optimizable()) {
// Grab return value from output variable
newNumber(nodep)->opAssign(*fetchNumber(funcp->fvarp()));
}
@ -540,8 +554,8 @@ public:
setMode(false,false,false);
clear(); // We reuse this structure in the main loop, so put initializers inside clear()
}
void setMode(bool scoped, bool checking, bool params) {
m_checking = checking;
void setMode(bool scoped, bool checkOnly, bool params) {
m_checkOnly = checkOnly;
m_scoped = scoped;
m_params = params;
}
@ -569,10 +583,6 @@ public:
setMode(true/*scoped*/,false/*checking*/, false/*params*/);
nodep->accept(*this);
}
void mainParamCheck (AstNode* nodep) {
setMode(false/*scoped*/,true/*checking*/, true/*params*/);
nodep->accept(*this);
}
void mainParamEmulate (AstNode* nodep) {
setMode(false/*scoped*/,false/*checking*/, true/*params*/);
nodep->accept(*this);

View File

@ -295,6 +295,7 @@ private:
// Simulate
simvis.mainTableEmulate(nodep);
if (!simvis.optimizable()) simvis.whyNotNodep()->v3fatalSrc("Optimizable cleared, even though earlier test run said not: "<<simvis.whyNotMessage());
// If a output changed, add it to table
int outnum = 0;

View File

@ -50,7 +50,9 @@ module t;
integer i;
begin
i=0;
f_while = 1;
begin : named
f_while = 1;
end : named
while (i<=a) begin
if (i[0]) f_while = f_while + 1;
i = i + 1;
@ -63,6 +65,7 @@ module t;
case(a)
32'd1: f_case = 1;
32'd0, 32'd4: f_case = 18;
32'd1234: begin $display("never get here"); $stop; end
default: f_case = 99;
endcase
endfunction

View File

@ -19,6 +19,8 @@ q{%Error: t/t_func_const_bad.v:\d+: Expecting expression to be constant, but can
%Error: t/t_func_const_bad.v:\d+: ... Location of non-constant VARREF 'modvar': Language violation: reference to non-function-local variable
%Error: t/t_func_const_bad.v:\d+: Expecting expression to be constant, but can't determine constant for FUNCREF 'f_bad_infinite'
%Error: t/t_func_const_bad.v:\d+: ... Location of non-constant WHILE: Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above 1024
%Error: t/t_func_const_bad.v:\d+: Expecting expression to be constant, but can't determine constant for FUNCREF 'f_bad_stop'
%Error: t/t_func_const_bad.v:\d+: ... Location of non-constant STOP: .stop executed during function constification; maybe indicates assertion firing
%Error: Exiting due to.*},
);

View File

@ -38,4 +38,11 @@ module t;
f_bad_infinite = 0;
end
endfunction
// Our own - stop
localparam BSTOP = f_bad_stop(3);
function integer f_bad_stop(input [31:0] a);
$stop;
endfunction
endmodule