Internals: Add --debug-width option for developers to check width consistency (#4923)

This commit is contained in:
Yutetsu TAKATSUKASA 2024-03-02 22:57:26 +09:00 committed by GitHub
parent af51107587
commit da9521a351
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 88 additions and 8 deletions

View File

@ -1171,6 +1171,31 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
default: this->v3fatalSrc("Bad case"); break;
}
}
if (v3Global.opt.debugWidth() && v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH) {
if (const AstNodeExpr* const exprp = VN_CAST(this, NodeExpr)) {
const char* const whyp = exprp->widthMismatch();
if (whyp) {
auto dtypeStr = [](const AstNodeExpr* exprp) VL_MT_STABLE {
std::ostringstream ss;
exprp->dtypep()->dumpSmall(ss);
return ss.str();
};
if (const AstNodeUniop* const uniopp = VN_CAST(exprp, NodeUniop)) {
UASSERT_OBJ(!whyp, uniopp,
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(uniopp)
<< " LHS:" << dtypeStr(uniopp->lhsp()));
} else if (const AstNodeBiop* const biopp = VN_CAST(exprp, NodeBiop)) {
UASSERT_OBJ(!whyp, biopp,
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(biopp)
<< " LHS:" << dtypeStr(biopp->lhsp())
<< " RHS:" << dtypeStr(biopp->rhsp()));
} else {
UASSERT_OBJ(false, exprp,
"widthMismatch detected " << whyp << " in an unexpected type");
}
}
}
}
}
// cppcheck-suppress unusedFunction // Debug only

View File

@ -2002,7 +2002,7 @@ public:
// TODO stomp these width functions out, and call via dtypep() instead
inline int width() const VL_MT_STABLE;
inline int widthMin() const;
inline int widthMin() const VL_MT_STABLE;
int widthMinV() const {
return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width();
}

View File

@ -26,7 +26,7 @@
// Inline METHODS
int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; }
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
int AstNode::widthMin() const VL_MT_STABLE { return dtypep() ? dtypep()->widthMin() : 0; }
bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}

View File

@ -53,7 +53,7 @@ public:
// ACCESSORS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const;
virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE;
bool hasDType() const override { return true; }
/// Require VlUnpacked, instead of [] for POD elements.
/// A non-POD object is always compound, but some POD elements

View File

@ -65,6 +65,9 @@ public:
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
inline AstStmtExpr* makeStmt();
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
// broken()
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
};
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
// Binary expression
@ -3830,6 +3833,7 @@ public:
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstMul final : public AstNodeBiComAsv {
public:
@ -3924,6 +3928,7 @@ public:
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstXor final : public AstNodeBiComAsv {
public:
@ -3946,6 +3951,7 @@ public:
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
// === AstNodeDistBiop ===
@ -5082,6 +5088,7 @@ public:
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return true; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstNullCheck final : public AstNodeUniop {
// Return LHS after checking that LHS is non-null

View File

@ -1916,7 +1916,7 @@ void AstNodeDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, generic);
dumpJsonGen(str);
}
void AstNodeDType::dumpSmall(std::ostream& str) const {
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "")
<< (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : "");
if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width();
@ -2757,3 +2757,22 @@ void AstDelay::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isCycleDelay);
dumpJsonGen(str);
}
const char* AstAnd::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstOr::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstXor::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}

View File

@ -75,16 +75,17 @@ public:
VWidthMinUsage()
: m_e{LINT_WIDTH} {}
// cppcheck-suppress noExplicitConstructor
constexpr VWidthMinUsage(en _e)
: m_e{_e} {}
constexpr VWidthMinUsage(en _e) VL_PURE : m_e{_e} {}
constexpr VWidthMinUsage(const VWidthMinUsage& _e) VL_PURE = default;
explicit VWidthMinUsage(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
constexpr VWidthMinUsage& operator=(const VWidthMinUsage& _e) VL_PURE = default;
};
constexpr bool operator==(const VWidthMinUsage& lhs, const VWidthMinUsage& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VWidthMinUsage& lhs, VWidthMinUsage::en rhs) {
constexpr bool operator==(const VWidthMinUsage& lhs, VWidthMinUsage::en rhs) VL_PURE {
return lhs.m_e == rhs;
}
constexpr bool operator==(VWidthMinUsage::en lhs, const VWidthMinUsage& rhs) {
@ -140,7 +141,7 @@ public:
// ACCESSORS (general)
AstNetlist* rootp() const VL_MT_SAFE { return m_rootp; }
VWidthMinUsage widthMinUsage() const { return m_widthMinUsage; }
VWidthMinUsage widthMinUsage() const VL_PURE { return m_widthMinUsage; }
bool assertDTypesResolved() const { return m_assertDTypesResolved; }
bool assertScoped() const { return m_assertScoped; }

View File

@ -1197,6 +1197,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-debug-self-test", OnOff, &m_debugSelfTest).undocumented();
DECL_OPTION("-debug-sigsegv", CbCall, throwSigsegv).undocumented(); // See also --debug-abort
DECL_OPTION("-debug-stack-check", OnOff, &m_debugStackCheck).undocumented();
DECL_OPTION("-debug-width", OnOff, &m_debugWidth).undocumented();
DECL_OPTION("-decoration", CbCall, [this, fl]() { decorations(fl, "medium"); });
DECL_OPTION("-decorations", CbVal, [this, fl](const char* optp) { decorations(fl, optp); });
DECL_OPTION("-no-decoration", CbCall, [this, fl]() { decorations(fl, "none"); });

View File

@ -245,6 +245,7 @@ private:
bool m_debugProtect = false; // main switch: --debug-protect
bool m_debugSelfTest = false; // main switch: --debug-self-test
bool m_debugStackCheck = false; // main switch: --debug-stack-check
bool m_debugWidth = false; // main switch: --debug-width
bool m_decoration = true; // main switch: --decoration
bool m_decorationNodes = false; // main switch: --decoration=nodes
bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only
@ -483,6 +484,7 @@ public:
bool debugProtect() const VL_MT_SAFE { return m_debugProtect; }
bool debugSelfTest() const { return m_debugSelfTest; }
bool debugStackCheck() const { return m_debugStackCheck; }
bool debugWidth() const VL_PURE { return m_debugWidth; }
bool decoration() const VL_MT_SAFE { return m_decoration; }
bool decorationNodes() const VL_MT_SAFE { return m_decorationNodes; }
bool dpiHdrOnly() const { return m_dpiHdrOnly; }

View File

@ -0,0 +1,4 @@
%Error: Internal Error: t/t_const_opt.v:531:34: ../V3Ast.cpp:#: widthMismatch detected 'lhsp()->widthMin() != rhsp()->widthMin()' @ ../V3AstNodes.cpp:#OUT:(G/wu32/1) LHS:(G/w32) RHS:(G/wu32/1)
531 | always_ff @(posedge clkin_data[0], posedge myfirst, posedge mysecond)
| ^
... See the manual at https://verilator.org/verilator_doc.html for more assistance.

21
test_regress/t/t_debug_width.pl Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
top_filename("t/t_const_opt.v");
scenarios(simulator => 1);
lint(
verilator_flags2 => ["--lint-only", "--debug-width"],
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;