Remove AstJumpLabel (#6221)
Remove AstJumpLabel AstJumpGo now references one if its enclosing AstJumpBlocks, and branches straight after the referenced block. That is: ``` JumpBlock a { ... JumpGo(a); ... } // <--- the JumpGo(a) goes here ``` This is sufficient for all use cases and makes control flow much easier to reason about. As a result, V3Const can optimize a bit more aggressively. Second half of, and fixes #6216
This commit is contained in:
parent
763183f067
commit
7c5d462564
|
@ -2194,8 +2194,6 @@ public:
|
|||
// Used by AstNode::broken()
|
||||
bool brokeExists() const { return V3Broken::isLinkable(this); }
|
||||
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
|
||||
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
|
||||
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
|
||||
|
||||
// CONSTRUCTORS
|
||||
virtual ~AstNode() = default;
|
||||
|
|
|
@ -3279,14 +3279,13 @@ public:
|
|||
bool isDelayed() const { return m_delayed; }
|
||||
};
|
||||
class AstJumpBlock final : public AstNodeStmt {
|
||||
// Block of code including a single JumpLabel, and 0+ JumpGo's to that label
|
||||
// Block of code that might contain AstJumpGo statements as children,
|
||||
// which when exectued branch to right after the referenced AstJumpBlock.
|
||||
// AstJumpBlocks can nest, and an AstJumpGo can reference any of the
|
||||
// enclosing AstJumpBlocks (can break out of mulitple levels).
|
||||
// Parents: {statement list}
|
||||
// Children: {statement list, with JumpGo and JumpLabel below}
|
||||
// Children: {statement list, with JumpGo below}
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := endStmtsp : List[AstNode]
|
||||
//
|
||||
// @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration
|
||||
int m_labelNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment
|
||||
VIsCached m_purity; // Pure state
|
||||
public:
|
||||
// After construction must call ->labelp to associate with appropriate label
|
||||
|
@ -3295,66 +3294,35 @@ public:
|
|||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstJumpBlock;
|
||||
const char* broken() const override;
|
||||
int instrCount() const override { return 0; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int labelNum() const { return m_labelNum; }
|
||||
void labelNum(int flag) { m_labelNum = flag; }
|
||||
AstJumpLabel* labelp() const { return m_labelp; }
|
||||
void labelp(AstJumpLabel* labelp) { m_labelp = labelp; }
|
||||
bool isPure() override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const;
|
||||
};
|
||||
class AstJumpGo final : public AstNodeStmt {
|
||||
// Jump point; branch down to a JumpLabel
|
||||
// No support for backward jumps at present
|
||||
// Parents: {statement list with JumpBlock above}
|
||||
// Branch to right after the referenced encloding AstJumpBlock
|
||||
// Parents: statement, including the referenced AstJumpBlock
|
||||
// Children: none
|
||||
//
|
||||
// @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration
|
||||
// @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after
|
||||
public:
|
||||
AstJumpGo(FileLine* fl, AstJumpLabel* labelp)
|
||||
AstJumpGo(FileLine* fl, AstJumpBlock* blockp)
|
||||
: ASTGEN_SUPER_JumpGo(fl)
|
||||
, m_labelp{labelp} {}
|
||||
, m_blockp{blockp} {}
|
||||
ASTGEN_MEMBERS_AstJumpGo;
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return labelp() == VN_DBG_AS(samep, JumpGo)->labelp();
|
||||
return blockp() == VN_DBG_AS(samep, JumpGo)->blockp();
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isBrancher() const override {
|
||||
return true; // SPECIAL: We don't process code after breaks
|
||||
}
|
||||
AstJumpLabel* labelp() const { return m_labelp; }
|
||||
};
|
||||
class AstJumpLabel final : public AstNodeStmt {
|
||||
// Jump point declaration
|
||||
// Parents: {statement list with JumpBlock above}
|
||||
// Children: none
|
||||
// @astgen ptr := m_blockp : AstJumpBlock // [After V3Jump] Pointer to declaration
|
||||
public:
|
||||
AstJumpLabel(FileLine* fl, AstJumpBlock* blockp)
|
||||
: ASTGEN_SUPER_JumpLabel(fl)
|
||||
, m_blockp{blockp} {}
|
||||
ASTGEN_MEMBERS_AstJumpLabel;
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!blockp()->brokeExistsAbove());
|
||||
BROKEN_RTN(blockp()->labelp() != this);
|
||||
return nullptr;
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
int instrCount() const override { return 0; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return blockp() == VN_DBG_AS(samep, JumpLabel)->blockp();
|
||||
}
|
||||
AstJumpBlock* blockp() const { return m_blockp; }
|
||||
};
|
||||
class AstMonitorOff final : public AstNodeStmt {
|
||||
|
|
|
@ -1139,10 +1139,6 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
|||
return nodep;
|
||||
}
|
||||
|
||||
const char* AstJumpBlock::broken() const {
|
||||
BROKEN_RTN(!labelp()->brokeExistsBelow());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstJumpBlock::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
|
@ -1977,21 +1973,6 @@ AstNodeExpr* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
|
|||
}
|
||||
|
||||
void AstJumpGo::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
if (labelp()) {
|
||||
labelp()->dump(str);
|
||||
} else {
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
const char* AstJumpGo::broken() const {
|
||||
BROKEN_RTN(!labelp()->brokeExistsBelow());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AstJumpLabel::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
if (blockp()) {
|
||||
|
@ -2000,7 +1981,11 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
|||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
const char* AstJumpGo::broken() const {
|
||||
BROKEN_RTN(!blockp()->brokeExistsAbove());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AstMemberDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
@ -912,7 +913,6 @@ class ConstVisitor final : public VNVisitor {
|
|||
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
|
||||
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
||||
// AstVar::user4p -> Used by variable marking/finding
|
||||
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
|
||||
// AstEnum::user4 -> bool. Recursing.
|
||||
|
||||
// STATE
|
||||
|
@ -938,6 +938,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass
|
||||
V3UniqueNames m_concswapNames; // For generating unique temporary variable names
|
||||
std::map<const AstNode*, bool> m_containsMemberAccess; // Caches results of matchBiopToBitwise
|
||||
std::unordered_set<AstJumpBlock*> m_usedJumpBlocks; // JumpBlocks used by some JumpGo
|
||||
|
||||
// METHODS
|
||||
|
||||
|
@ -2303,19 +2304,6 @@ class ConstVisitor final : public VNVisitor {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) {
|
||||
// If JumpGo has an upper JumpBlock that is to same label, then
|
||||
// code will by normal sequential operation do the JUMPGO and it
|
||||
// can be removed.
|
||||
if (nodep->nextp()) return false; // Label jumps other statements
|
||||
AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock);
|
||||
if (!aboveBlockp) return false;
|
||||
if (aboveBlockp != nodep->labelp()->blockp()) return false;
|
||||
if (aboveBlockp->endStmtsp() != nodep->labelp()) return false;
|
||||
UINFO(4, "JUMPGO => last remove " << nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Boolean replacements
|
||||
bool operandBoolShift(const AstNode* nodep) {
|
||||
|
@ -3402,52 +3390,39 @@ class ConstVisitor final : public VNVisitor {
|
|||
// Jump elimination
|
||||
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Jump to label where label immediately follows this JumpGo is not useful
|
||||
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
// Keep the label, might be other jumps pointing to it, gets cleaned later
|
||||
return;
|
||||
}
|
||||
if (m_doExpensive) {
|
||||
// Any non-label statements (at this statement level) can never execute
|
||||
while (nodep->nextp() && !VN_IS(nodep->nextp(), JumpLabel)) {
|
||||
pushDeletep(nodep->nextp()->unlinkFrBack());
|
||||
// Any statements following the JumpGo (at this statement level) never execute, delete
|
||||
if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext());
|
||||
|
||||
// JumpGo as last statement in target JumpBlock (including last in a last sub-list),
|
||||
// is a no-op, remove it.
|
||||
for (AstNode* abovep = nodep->abovep(); abovep; abovep = abovep->abovep()) {
|
||||
if (abovep == nodep->blockp()) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
// If last statement in a jump label we have JumpLabel(...., JumpGo)
|
||||
// Often caused by "return" in a Verilog function. The Go is pointless, remove.
|
||||
if (replaceJumpGoNext(nodep, nodep->abovep())) return;
|
||||
// Also optimize If with a then or else's final statement being this JumpGo
|
||||
// We only do single ifs... Ideally we'd look at control flow and delete any
|
||||
// Jumps where any following control flow point is the label
|
||||
if (!nodep->nextp()) {
|
||||
if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) {
|
||||
if (!aboveIfp->nextp()) {
|
||||
if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
nodep->labelp()->blockp()->user4(true);
|
||||
// Stop if not doing expensive, or if the above node is not the last in its list,
|
||||
// ... or if it's not an 'if' TODO: it would be enough if it was not a branch.
|
||||
if (!m_doExpensive || abovep->nextp() || !VN_IS(abovep, If)) break;
|
||||
}
|
||||
// Mark JumpBlock as used
|
||||
m_usedJumpBlocks.emplace(nodep->blockp());
|
||||
m_hasJumpDelay = true;
|
||||
}
|
||||
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
// Because JumpLabels disable many optimizations,
|
||||
// remove JumpLabels that are not pointed to by any AstJumpGos
|
||||
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
|
||||
iterateChildren(nodep);
|
||||
// AstJumpGo's below here that point to this node will set user4
|
||||
if (m_doExpensive && !nodep->user4()) {
|
||||
|
||||
// Remove if empty
|
||||
if (!nodep->stmtsp()) {
|
||||
UINFO(4, "JUMPLABEL => empty " << nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no JumpGo points to this node, replace it with its body
|
||||
if (!m_usedJumpBlocks.count(nodep)) {
|
||||
UINFO(4, "JUMPLABEL => unused " << nodep);
|
||||
AstNode* underp = nullptr;
|
||||
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (underp) {
|
||||
nodep->replaceWith(underp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
pushDeletep(nodep->labelp()->unlinkFrBack());
|
||||
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
|
@ -117,7 +118,7 @@ public:
|
|||
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||
VMemberMap m_memberMap;
|
||||
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
||||
int m_labelNum = 0; // Next label number
|
||||
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for JumpBlocks
|
||||
bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr
|
||||
bool m_emitConstInit = false; // Emitting constant initializer
|
||||
bool m_createdScopeHash = false; // Already created a scope hash
|
||||
|
@ -325,6 +326,7 @@ public:
|
|||
VL_RESTORER(m_createdScopeHash);
|
||||
m_cfuncp = nodep;
|
||||
m_instantiatesOwnProcess = false;
|
||||
m_labelNumbers.clear(); // No need to save/restore, all Jumps must be within the function
|
||||
|
||||
splitSizeInc(nodep);
|
||||
|
||||
|
@ -993,25 +995,29 @@ public:
|
|||
puts(";\n");
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
nodep->labelNum(++m_labelNum);
|
||||
// Allocate label number
|
||||
const size_t n = m_labelNumbers.size();
|
||||
const bool newEntry = m_labelNumbers.emplace(nodep, n).second;
|
||||
UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide");
|
||||
// Emit
|
||||
putns(nodep, "{\n"); // Make it visually obvious label jumps outside these
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
iterateAndNextConstNull(nodep->endStmtsp());
|
||||
puts("__Vlabel" + std::to_string(n) + ": ;\n");
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
// Retrieve target label number - must already exist (from enclosing AstJumpBlock)
|
||||
const size_t n = m_labelNumbers.at(nodep->blockp());
|
||||
// Emit
|
||||
putns(nodep, "goto __Vlabel" + std::to_string(n) + ";\n");
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
putns(nodep, "{\n");
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
putns(nodep, "goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
|
||||
}
|
||||
void visit(AstJumpLabel* nodep) override {
|
||||
putns(nodep, "__Vlabel" + cvtToStr(nodep->blockp()->labelNum()) + ": ;\n");
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
putns(nodep, "while (");
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
@ -37,6 +38,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
|||
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
|
||||
bool m_arrayPost = false; // Print array information that goes after identifier (vs after)
|
||||
std::deque<AstNodeArrayDType*> m_packedps; // Packed arrays to print with BasicDType
|
||||
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for JumpBlocks
|
||||
|
||||
// METHODS
|
||||
virtual void puts(const string& str) = 0;
|
||||
|
@ -308,14 +310,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
|||
puts(");\n");
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
putbs("begin : label" + cvtToStr(nodep->labelNum()) + "\n");
|
||||
if (nodep->stmtsp()) iterateAndNextConstNull(nodep->stmtsp());
|
||||
// Allocate label number
|
||||
const size_t n = m_labelNumbers.size();
|
||||
const bool newEntry = m_labelNumbers.emplace(nodep, n).second;
|
||||
UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide");
|
||||
// Emit
|
||||
putbs("begin : label" + std::to_string(n) + "\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("end\n");
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
putbs("disable label" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
|
||||
// Retrieve target label number - Sometimes EmitV is used by debug code,
|
||||
// so allow printing with an unknown target
|
||||
const auto it = m_labelNumbers.find(nodep->blockp());
|
||||
const std::string label
|
||||
= it != m_labelNumbers.end() ? "label" + std::to_string(it->second) : "<UNKNOWN>";
|
||||
putbs("disable " + label + ";\n");
|
||||
}
|
||||
void visit(AstJumpLabel* nodep) override { putbs("// " + cvtToStr(nodep->blockp()) + ":\n"); }
|
||||
void visit(AstNodeReadWriteMem* nodep) override {
|
||||
putfs(nodep, nodep->verilogKwd());
|
||||
putbs("(");
|
||||
|
|
|
@ -320,9 +320,7 @@ class HasherVisitor final : public VNVisitorConst {
|
|||
});
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
|
||||
iterateConstNull(nodep->labelp());
|
||||
});
|
||||
m_hash += hashNodeAndIterate(nodep, false, false, []() {});
|
||||
}
|
||||
void visit(AstTraceInc* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
|
||||
|
|
|
@ -45,8 +45,8 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
|
||||
class LinkJumpVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstNode::user1() -> AstJumpLabel*, for this block if endOfIter
|
||||
// AstNode::user2() -> AstJumpLabel*, for this block if !endOfIter
|
||||
// AstNode::user1() -> AstJumpBlock*, for body of this loop
|
||||
// AstNode::user2() -> AstJumpBlock*, for this block
|
||||
// AstNodeBlock::user3() -> bool, true if contains a fork
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
|
@ -65,36 +65,34 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
"__VprocessQueue"}; // Names for queues needed for 'disable' handling
|
||||
|
||||
// METHODS
|
||||
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
|
||||
// Put label under given node, and if WHILE optionally at end of iteration
|
||||
UINFO(4, "Create label for " << nodep);
|
||||
if (VN_IS(nodep, JumpLabel)) return VN_AS(nodep, JumpLabel); // Done
|
||||
// Get (and create if necessary) the JumpBlock for this statement
|
||||
AstJumpBlock* getJumpBlock(AstNode* nodep, bool endOfIter) {
|
||||
// Wrap 'nodep' in JumpBlock. If loop, wrap the body instead if endOfIter is true
|
||||
UINFO(4, "Create JumpBlock for " << nodep);
|
||||
|
||||
// Made it previously? We always jump to the end, so this works out
|
||||
if (endOfIter) {
|
||||
if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpLabel);
|
||||
if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpBlock);
|
||||
} else {
|
||||
if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpLabel);
|
||||
if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpBlock);
|
||||
}
|
||||
|
||||
AstNode* underp = nullptr;
|
||||
bool under_and_next = true;
|
||||
if (VN_IS(nodep, NodeBlock)) {
|
||||
underp = VN_AS(nodep, NodeBlock)->stmtsp();
|
||||
} else if (VN_IS(nodep, NodeFTask)) {
|
||||
underp = VN_AS(nodep, NodeFTask)->stmtsp();
|
||||
} else if (VN_IS(nodep, Foreach)) {
|
||||
if (AstNodeBlock* const blockp = VN_CAST(nodep, NodeBlock)) {
|
||||
underp = blockp->stmtsp();
|
||||
} else if (AstNodeFTask* const fTaskp = VN_CAST(nodep, NodeFTask)) {
|
||||
underp = fTaskp->stmtsp();
|
||||
} else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) {
|
||||
if (endOfIter) {
|
||||
underp = VN_AS(nodep, Foreach)->stmtsp();
|
||||
underp = foreachp->stmtsp();
|
||||
} else {
|
||||
underp = nodep;
|
||||
under_and_next = false; // IE we skip the entire foreach
|
||||
}
|
||||
} else if (VN_IS(nodep, While)) {
|
||||
} else if (AstWhile* const whilep = VN_CAST(nodep, While)) {
|
||||
if (endOfIter) {
|
||||
// Note we jump to end of bodysp; a FOR loop has its
|
||||
// increment under incsp() which we don't skip
|
||||
underp = VN_AS(nodep, While)->stmtsp();
|
||||
underp = whilep->stmtsp();
|
||||
} else {
|
||||
underp = nodep;
|
||||
under_and_next = false; // IE we skip the entire while
|
||||
|
@ -118,36 +116,32 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(underp, nodep, "Break/disable/continue not under expected statement");
|
||||
UINFO(5, " Underpoint is " << underp);
|
||||
|
||||
if (VN_IS(underp, JumpLabel)) {
|
||||
return VN_AS(underp, JumpLabel);
|
||||
} else { // Move underp stuff to be under a new label
|
||||
AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), nullptr};
|
||||
AstJumpLabel* const labelp = new AstJumpLabel{nodep->fileline(), blockp};
|
||||
blockp->labelp(labelp);
|
||||
|
||||
VNRelinker repHandle;
|
||||
if (under_and_next) {
|
||||
underp->unlinkFrBackWithNext(&repHandle);
|
||||
} else {
|
||||
underp->unlinkFrBack(&repHandle);
|
||||
}
|
||||
repHandle.relink(blockp);
|
||||
|
||||
blockp->addStmtsp(underp);
|
||||
// Keep any AstVars under the function not under the new JumpLabel
|
||||
for (AstNode *nextp, *varp = underp; varp; varp = nextp) {
|
||||
nextp = varp->nextp();
|
||||
if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack());
|
||||
}
|
||||
// Label goes last
|
||||
blockp->addEndStmtsp(labelp);
|
||||
if (endOfIter) {
|
||||
nodep->user1p(labelp);
|
||||
} else {
|
||||
nodep->user2p(labelp);
|
||||
}
|
||||
return labelp;
|
||||
// If already wrapped, we are done ...
|
||||
if (!underp->nextp() || !under_and_next) {
|
||||
if (AstJumpBlock* const blockp = VN_CAST(underp, JumpBlock)) return blockp;
|
||||
}
|
||||
|
||||
// Move underp stuff to be under a new AstJumpBlock
|
||||
VNRelinker repHandle;
|
||||
if (under_and_next) {
|
||||
underp->unlinkFrBackWithNext(&repHandle);
|
||||
} else {
|
||||
underp->unlinkFrBack(&repHandle);
|
||||
}
|
||||
AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), underp};
|
||||
if (endOfIter) {
|
||||
nodep->user1p(blockp);
|
||||
} else {
|
||||
nodep->user2p(blockp);
|
||||
}
|
||||
repHandle.relink(blockp);
|
||||
|
||||
// Keep any AstVars under the function not under the new JumpLabel
|
||||
for (AstNode *nextp, *varp = underp; varp; varp = nextp) {
|
||||
nextp = varp->nextp();
|
||||
if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack());
|
||||
}
|
||||
return blockp;
|
||||
}
|
||||
void addPrefixToBlocksRecurse(const std::string& prefix, AstNode* const nodep) {
|
||||
// Add a prefix to blocks
|
||||
|
@ -379,8 +373,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
nodep->lhsp()->unlinkFrBackWithNext()});
|
||||
}
|
||||
// Jump to the end of the function call
|
||||
AstJumpLabel* const labelp = findAddLabel(m_ftaskp, false);
|
||||
nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), labelp});
|
||||
AstJumpBlock* const blockp = getJumpBlock(m_ftaskp, false);
|
||||
nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), blockp});
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
@ -391,8 +385,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
nodep->v3error("break isn't underneath a loop");
|
||||
} else {
|
||||
// Jump to the end of the loop
|
||||
AstJumpLabel* const labelp = findAddLabel(m_loopp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
AstJumpBlock* const blockp = getJumpBlock(m_loopp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp});
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
@ -404,8 +398,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
} else {
|
||||
// Jump to the end of this iteration
|
||||
// If a "for" loop then need to still do the post-loop increment
|
||||
AstJumpLabel* const labelp = findAddLabel(m_loopp, true);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
AstJumpBlock* const blockp = getJumpBlock(m_loopp, true);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp});
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
@ -444,8 +438,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
"Unsupported: disabling block that contains a fork");
|
||||
} else {
|
||||
// Jump to the end of the named block
|
||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
AstJumpBlock* const blockp = getJumpBlock(beginp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp});
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '"
|
||||
|
|
|
@ -397,10 +397,9 @@ private:
|
|||
UASSERT_OBJ(vscp, nodep, "Not linked");
|
||||
return vscp;
|
||||
}
|
||||
bool jumpingOver(const AstNode* nodep) const {
|
||||
// True to jump over this node - all visitors must call this up front
|
||||
return (m_jumpp && m_jumpp->labelp() != nodep);
|
||||
}
|
||||
|
||||
// True if current node might be jumped over - all visitors must call this up front
|
||||
bool jumpingOver() const { return m_jumpp; }
|
||||
void assignOutValue(AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) {
|
||||
if (VN_IS(nodep, AssignDly)) {
|
||||
// Don't do setValue, as value isn't yet visible to following statements
|
||||
|
@ -413,7 +412,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
void visit(AstAlways* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
@ -421,7 +420,7 @@ private:
|
|||
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
UASSERT_OBJ(nodep->varp(), nodep, "Unlinked");
|
||||
iterateChildrenConst(nodep->varp());
|
||||
|
@ -490,7 +489,7 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (m_scoped) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
|
@ -500,7 +499,7 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
|
@ -520,7 +519,7 @@ private:
|
|||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
|
@ -529,7 +528,7 @@ private:
|
|||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
UINFO(5, " IF " << nodep);
|
||||
checkNodeInfo(nodep);
|
||||
if (m_checkOnly) {
|
||||
|
@ -856,7 +855,7 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
|
||||
|
@ -900,7 +899,7 @@ private:
|
|||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstNodeCase* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
UINFO(5, " CASE " << nodep);
|
||||
checkNodeInfo(nodep);
|
||||
if (m_checkOnly) {
|
||||
|
@ -939,7 +938,7 @@ private:
|
|||
|
||||
void visit(AstCaseItem* nodep) override {
|
||||
// Real handling is in AstNodeCase
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
@ -947,12 +946,12 @@ private:
|
|||
void visit(AstComment*) override {}
|
||||
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
if (!optimizable()) return;
|
||||
|
@ -962,30 +961,24 @@ private:
|
|||
}
|
||||
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_jumpp && m_jumpp->blockp() == nodep) {
|
||||
UINFO(5, " JUMP DONE " << nodep);
|
||||
m_jumpp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
if (!m_checkOnly) {
|
||||
UINFO(5, " JUMP GO " << nodep);
|
||||
m_jumpp = nodep;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpLabel* nodep) override {
|
||||
// This only supports forward jumps. That's all we make at present,
|
||||
// AstJumpGo::broken uses brokeExistsBelow() to check this.
|
||||
if (jumpingOver(nodep)) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_jumpp && m_jumpp->labelp() == nodep) {
|
||||
UINFO(5, " JUMP DONE " << nodep);
|
||||
m_jumpp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstStop* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
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(
|
||||
|
@ -1030,7 +1023,7 @@ private:
|
|||
|
||||
void visit(AstWhile* nodep) override {
|
||||
// Doing lots of Whiles is slow, so only for parameters
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
UINFO(5, " WHILE " << nodep);
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
|
@ -1044,15 +1037,15 @@ private:
|
|||
while (true) {
|
||||
UINFO(5, " WHILE-ITER " << nodep);
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
if (jumpingOver(nodep)) break;
|
||||
if (jumpingOver()) break;
|
||||
if (!optimizable()) break;
|
||||
if (!fetchConst(nodep->condp())->num().isNeqZero()) { //
|
||||
break;
|
||||
}
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
if (jumpingOver(nodep)) break;
|
||||
if (jumpingOver()) break;
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
if (jumpingOver(nodep)) break;
|
||||
if (jumpingOver()) break;
|
||||
|
||||
// Prep for next loop
|
||||
if (loops++
|
||||
|
@ -1068,7 +1061,7 @@ private:
|
|||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
UINFO(5, " FUNCREF " << nodep);
|
||||
checkNodeInfo(nodep);
|
||||
|
@ -1140,7 +1133,7 @@ private:
|
|||
}
|
||||
|
||||
void visit(AstVar* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
|
@ -1148,12 +1141,12 @@ private:
|
|||
}
|
||||
|
||||
void visit(AstScopeName* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
// Ignore
|
||||
}
|
||||
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
|
@ -1214,7 +1207,7 @@ private:
|
|||
}
|
||||
|
||||
void visit(AstDisplay* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
// We ignore isPredictOptimizable as $display is often in constant
|
||||
// functions and we want them to work if used with parameters
|
||||
|
@ -1242,11 +1235,11 @@ private:
|
|||
// Some CMethods such as size() on queues could be supported, but
|
||||
// instead we should change those methods to new Ast types so we can
|
||||
// properly dispatch them
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
knownBadNodeType(nodep);
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
knownBadNodeType(nodep);
|
||||
}
|
||||
// ====
|
||||
|
@ -1255,7 +1248,7 @@ private:
|
|||
// AstCoverInc, AstFinish,
|
||||
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
|
||||
void visit(AstNode* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (jumpingOver()) return;
|
||||
badNodeType(nodep);
|
||||
}
|
||||
|
||||
|
|
|
@ -116,10 +116,10 @@ module Vt_debug_emitv_t;
|
|||
endtask
|
||||
function f;
|
||||
input int signed v;
|
||||
begin : label0
|
||||
begin : label1
|
||||
$display("stmt");
|
||||
f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1));
|
||||
disable label0;
|
||||
disable label1;
|
||||
end
|
||||
endfunction
|
||||
initial begin
|
||||
|
@ -203,12 +203,12 @@ module Vt_debug_emitv_t;
|
|||
begin : unnamedblk3
|
||||
int signed i;
|
||||
i = 'sh0;
|
||||
begin : label0
|
||||
begin : label2
|
||||
while ((i < cyc)) begin
|
||||
begin
|
||||
sum = (sum + i);
|
||||
if ((sum > 'sha)) begin
|
||||
disable label0;
|
||||
disable label2;
|
||||
end
|
||||
else begin
|
||||
sum = (sum + 'sh1);
|
||||
|
@ -390,13 +390,13 @@ module Vt_debug_emitv_sub;
|
|||
endtask
|
||||
function f;
|
||||
input int signed v;
|
||||
begin : label0
|
||||
begin : label3
|
||||
if ((v == 'sh0)) begin
|
||||
f = 'sh21;
|
||||
disable label0;
|
||||
disable label3;
|
||||
end
|
||||
f = ({32'h1{{31'h0, v[2]}}} + 32'h1);
|
||||
disable label0;
|
||||
disable label3;
|
||||
end
|
||||
endfunction
|
||||
real r;
|
||||
|
|
Loading…
Reference in New Issue