Revert: 21020ea2: Support printing enum names for %p and %s (#5523 revert) (#7338 revert) (#7521 test)

This commit is contained in:
Wilson Snyder 2026-04-30 10:29:47 -04:00
parent 021f5f7d02
commit dd75c4cd1b
11 changed files with 31 additions and 356 deletions

View File

@ -944,20 +944,6 @@ std::string _vl_vsformat_time(std::string& tmp, T ld, int timeunit, bool left,
// Do a va_arg returning a quad, assuming input argument is anything less than wide
#define VL_VA_ARG_Q_(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData))
static void _vl_vsformat_read_qint(va_list app, int lbits, QData& ld, std::vector<EData>& strwide,
WDataInP& lwp, int& lsb) VL_MT_SAFE {
if (lbits <= VL_QUADSIZE) {
ld = VL_VA_ARG_Q_(app, lbits);
strwide.resize(2);
VL_SET_WQ(strwide, ld);
lwp = strwide.data();
} else {
lwp = va_arg(app, WDataInP);
ld = 0; // Consume the arg; enums > 64 bits wide are unsupported.
}
lsb = lbits - 1;
}
void _vl_vsformat(std::string& output, const std::string& format, int argc,
va_list ap) VL_MT_SAFE {
// Format a Verilog $write style format into the output list
@ -1092,7 +1078,6 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
// Similar code flow in V3Number::displayed
int lbits = 0;
void* thingp = nullptr;
const std::string* enump = nullptr;
QData ld = 0;
std::vector<EData> strwide;
WDataInP lwp = nullptr;
@ -1112,31 +1097,17 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
} else if (formatAttr == VL_VFORMATATTR_STRING) {
thingp = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM) {
lbits = va_arg(ap, int);
_vl_vsformat_read_qint(ap, lbits, ld, strwide, lwp, lsb);
++argn; // Internal ABI: runtime enum args are followed by generated name string
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
enump = va_arg(ap, std::string*);
if (enump && !enump->empty()) {
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
thingp = const_cast<std::string*>(enump);
} else if (fmt == 'p' && widthSet && width == 0) {
output += "'h";
fmt = 'h';
formatAttr = VL_VFORMATATTR_UNSIGNED;
} else {
if (fmt == 'p') width = 0;
widthSet = true;
fmt = 'd';
formatAttr = VL_VFORMATATTR_UNSIGNED;
}
if (widthSet && width == 0) {
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
}
} else { // Numeric
lbits = va_arg(ap, int);
_vl_vsformat_read_qint(ap, lbits, ld, strwide, lwp, lsb);
if (lbits <= VL_QUADSIZE) {
ld = VL_VA_ARG_Q_(ap, lbits);
strwide.resize(2);
VL_SET_WQ(strwide, ld);
lwp = strwide.data();
} else {
lwp = va_arg(ap, WDataInP);
ld = lwp[0];
}
if (fmt == 'p') {
if (widthSet && width == 0) { // For %0p, IEEE our choice, use 'h%0h
output += "'h";
@ -1147,6 +1118,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
fmt = 'd';
}
}
lsb = lbits - 1;
if (widthSet && width == 0) {
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
}

View File

@ -442,7 +442,6 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/WData/etc) Signed number; for %d showing sign
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
#define VL_VFORMATATTR_ENUM 'E' // (width, data..., std::string* name); enum with runtime %p/%s
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime

View File

@ -318,7 +318,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp
AstNode* const subargp = fargp ? fargp->exprp() : argp;
const VFormatAttr formatAttr = AstSFormatArg::formatAttrDefauled(fargp, subargp->dtypep());
puts(", '"s + formatAttr.ascii() + '\'');
if (formatAttr.isSigned() || formatAttr.isUnsigned() || formatAttr.isEnum())
if (formatAttr.isSigned() || formatAttr.isUnsigned())
puts("," + cvtToStr(subargp->widthMin()));
const bool addrof = isScan || formatAttr.isString() || formatAttr.isComplex();
puts(",");

View File

@ -46,7 +46,6 @@ public:
//
COMPLEX = VL_VFORMATATTR_COMPLEX,
DOUBLE = VL_VFORMATATTR_DOUBLE,
ENUM = VL_VFORMATATTR_ENUM,
SCOPE = VL_VFORMATATTR_SCOPE,
STRING = VL_VFORMATATTR_STRING,
TIMEUNIT = VL_VFORMATATTR_TIMEUNIT
@ -63,7 +62,6 @@ public:
char ascii() const { return m_e; }
bool isComplex() const { return m_e == COMPLEX; }
bool isDouble() const { return m_e == DOUBLE; }
bool isEnum() const { return m_e == ENUM; }
bool isSigned() const { return m_e == SIGNED; }
bool isString() const { return m_e == STRING; }
bool isUnsigned() const { return m_e == UNSIGNED; }

View File

@ -6364,17 +6364,7 @@ class WidthVisitor final : public VNVisitor {
AstNodeExpr* const newp = new AstToStringN{argp->fileline(), argp};
formatAttr = VFormatAttr::COMPLEX;
argp = newp;
} else if (nodep->exprFormat()) {
if (AstEnumDType* const enumDtp = formatEnumDType(argp)) {
nodep->addExprsp(new AstSFormatArg{argp->fileline(), VFormatAttr::ENUM, argp});
AstNodeExpr* const namep
= enumSelect(argp->cloneTreePure(false), enumDtp, VAttrType::ENUM_NAME);
nodep->addExprsp(
new AstSFormatArg{namep->fileline(), VFormatAttr::STRING, namep});
continue;
}
}
if (formatAttr.isUnsigned() && dtypep->isSigned()) {
} else if (dtypep->isSigned()) {
formatAttr = VFormatAttr::SIGNED;
}
if (VN_IS(argp, SFormatArg) // Already done
@ -8241,16 +8231,13 @@ class WidthVisitor final : public VNVisitor {
// For sformatf's with constant format, iterate/check arguments
UASSERT_OBJ(!nodep->exprFormat(), nodep, "Assumes constant format");
bool inPct = false;
string fmtMods;
AstNodeExpr* argp = nodep->exprsp();
string newFormat;
for (char ch : nodep->text()) {
if (!inPct && ch == '%') {
inPct = true;
fmtMods = "";
newFormat += ch;
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
fmtMods += ch;
newFormat += ch;
} else if (!inPct) { // Normal text
newFormat += ch;
@ -8258,7 +8245,7 @@ class WidthVisitor final : public VNVisitor {
inPct = false;
AstNodeExpr* const nextp = argp ? VN_AS(argp->nextp(), NodeExpr) : nullptr;
AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); // May not exist yet
AstNodeExpr* subargp = fargp ? fargp->exprp() : argp;
AstNodeExpr* const subargp = fargp ? fargp->exprp() : argp;
const AstNodeDType* const dtypep
= subargp ? subargp->dtypep()->skipRefp() : nullptr;
ch = std::tolower(ch);
@ -8315,35 +8302,7 @@ class WidthVisitor final : public VNVisitor {
}
break;
case 'p': // FALLTHRU
case 's':
// Keep enum `%p`/`%s` behavior aligned with enum.name():
// valid enum values print the mnemonic; invalid values print numeric fallback.
if (subargp) {
if (AstEnumDType* const enumDtp = formatEnumDType(subargp)) {
string fallbackFormat = "%0d";
if (ch == 'p') {
bool widthSet = false;
size_t width = 0;
for (const char mod : fmtMods) {
if (!std::isdigit(mod)) continue;
widthSet = true;
width = width * 10 + (mod - '0');
}
if (widthSet && width == 0) fallbackFormat = "'h%0h";
}
AstNodeExpr* const newp = new AstCond{
subargp->fileline(), enumTestValid(subargp, enumDtp),
enumSelect(subargp->cloneTreePure(false), enumDtp,
VAttrType::ENUM_NAME),
new AstSFormatF{subargp->fileline(), fallbackFormat, true,
subargp->cloneTreePure(false)}};
subargp->replaceWith(new AstSFormatArg{subargp->fileline(),
VFormatAttr::COMPLEX, newp});
VL_DO_DANGLING(pushDeletep(subargp), subargp);
}
}
argp = nextp;
break;
case 's': // FALLTHRU
default: // Most operators, just move to next argument
argp = nextp;
break;
@ -8354,16 +8313,6 @@ class WidthVisitor final : public VNVisitor {
nodep->text(newFormat);
}
static AstEnumDType* formatEnumDType(AstNodeExpr* subargp) {
AstEnumDType* enumDtp = VN_CAST(subargp->dtypep()->skipRefToEnump(), EnumDType);
if (!enumDtp) {
if (const AstVarRef* const varrefp = VN_CAST(subargp, VarRef)) {
enumDtp = VN_CAST(varrefp->varp()->dtypep()->skipRefToEnump(), EnumDType);
}
}
return enumDtp;
}
//----------------------------------------------------------------------
// LOWER LEVEL WIDTH METHODS (none iterate)

View File

@ -4,11 +4,6 @@
// SPDX-FileCopyrightText: 2020 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checks(gotv, expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
class Cls;
@ -17,32 +12,11 @@ module t;
B = 20,
C = 30
} en_t;
en_t en;
endclass
class WideCls;
typedef enum logic [95:0] {
A = 96'h1,
B = 96'h2
} en_t;
en_t en;
endclass
initial begin
Cls c;
WideCls w;
string s;
if (c.A != 10) $stop;
c = new;
c.en = c.B;
if (c.en != 20) $stop;
s = $sformatf("%p", c);
`checks(s, "'{en:'h14}");
w = new;
w.en = w.B;
s = $sformatf("%p", w);
`checks(s, "'{en:'h2}");
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -1,18 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -1,167 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// 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-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
module t (
input string empty_no_opt
);
typedef enum logic [1:0] {
E0 = 0,
E1 = 1,
E2 = 2
} my_e;
typedef enum logic [63:0] {
W64A = 64'h1,
W64B = 64'h0000_0001_0000_0001
} wide64_e;
my_e e;
wide64_e e64;
logic [63:0] n64;
`define check(got, exp) do if ((got) != (exp)) begin \
$write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__, `__LINE__, got, exp); \
$stop; \
end while(0)
initial begin
string fmt;
begin
my_e it;
string names_p;
string names_s;
string vals_d;
names_p = "";
names_s = "";
vals_d = "";
for (it = it.first; ; it = it.next) begin
if (names_p != "") begin
names_p = {names_p, ","};
names_s = {names_s, ","};
vals_d = {vals_d, ","};
end
names_p = {names_p, $sformatf("%p", it)};
names_s = {names_s, $sformatf("%s", it)};
vals_d = {vals_d, $sformatf("%0d", it)};
if (it == it.last) break;
end
`check(names_p, "E0,E1,E2");
`check(names_s, "E0,E1,E2");
`check(vals_d, "0,1,2");
end
// Valid enum values print mnemonic for %p/%s.
e = E0;
`check($sformatf("%p", e), "E0");
`check($sformatf("%s", e), "E0");
e = E1;
`check($sformatf("%p", e), "E1");
`check($sformatf("%P", e), "E1");
`check($sformatf("%0p", e), "E1");
`check($sformatf("%s", e), "E1");
`check($sformatf("%S", e), "E1");
`check($sformatf("%d", e), "1");
`check($sformatf("%0d", e), "1");
`check($sformatf("%h", e), "1");
`check($sformatf("%0h", e), "1");
`check($sformatf("%b", e), "01");
`check($sformatf("%0b", e), "1");
`check($sformatf("%o", e), "1");
`check($sformatf("%0o", e), "1");
`check($sformatf("%x", e), "1");
`check($sformatf("%0x", e), "1");
e = E2;
`check($sformatf("%p", e), "E2");
`check($sformatf("%s", e), "E2");
`check($sformatf("%s|%p", e, e), "E2|E2");
`check($sformatf("%4p", e), "E2");
`check($sformatf("%-4p", e), "E2");
`check($sformatf("%d", e), "2");
`check($sformatf("%h", e), "2");
`check($sformatf("%b", e), "10");
`check($sformatf("%0b", e), "10");
`check($sformatf("%o", e), "2");
`check($sformatf("%x", e), "2");
`check($sformatf("%4d", e), " 2");
`check($sformatf("%04d", e), "0002");
`check($sformatf("%4h", e), "0002");
`check($sformatf("%-4s", e), "E2 ");
`check($sformatf("%4s", e), " E2");
// `%p`/`%s` in non-terminal positions with mixed formatters.
`check($sformatf("%0d:%s:%0d", 9, e, 7), "9:E2:7");
`check($sformatf("%s %h %p", e, 4'hA, e), "E2 a E2");
`check($sformatf("pre %% %s post", e), "pre % E2 post");
// Complex enum expressions (non-var-ref) in format args.
`check($sformatf("%s", (1'b1 ? E2 : E0)), "E2");
// 64-bit enums should preserve bits above 32 in both named and numeric cases.
e64 = W64B;
`check($sformatf("%p", e64), "W64B");
`check($sformatf("%s", e64), "W64B");
e64 = wide64_e'(64'h0000_0002_0000_0001);
`check($sformatf("%p", e64), "8589934593");
`check($sformatf("%s", e64), "8589934593");
n64 = 64'h0000_0000_0000_0001;
`check($sformatf("%0p", n64), "'h1");
// Exercise display/write-family formatting path in addition to $sformatf checks.
$display("display-valid:%s:%0d:%p", e, 7, e);
$write("write-valid:%s:%0d:%p\n", e, 8, e);
// Invalid enum values fall back to numeric formatting for %p/%s.
e = my_e'(3);
`check($sformatf("%p", e), "3");
`check($sformatf("%P", e), "3");
`check($sformatf("%0p", e), "'h3");
`check($sformatf("%s", e), "3");
`check($sformatf("%S", e), "3");
`check($sformatf("%4p", e), "3");
`check($sformatf("%4s", e), " 3");
`check($sformatf("%d", e), "3");
`check($sformatf("%0d", e), "3");
`check($sformatf("%h", e), "3");
`check($sformatf("%0h", e), "3");
`check($sformatf("%b", e), "11");
`check($sformatf("%0b", e), "11");
`check($sformatf("%o", e), "3");
`check($sformatf("%x", e), "3");
// Non-terminal invalid-value fallback with mixed formatters.
`check($sformatf("%0d:%p:%0d", 9, e, 7), "9:3:7");
`check($sformatf("%s %h %p", e, 4'hA, e), "3 a 3");
`check($sformatf("pre %% %s post", e), "pre % 3 post");
`check($sformatf("%s|%p", e, e), "3|3");
`check($sformatf("%s", (1'b1 ? my_e'(3) : E0)), "3");
`check($sformatf("%p", (1'b0 ? E0 : my_e'(3))), "3");
$display("display-invalid:%s:%0d:%p", e, 7, e);
$write("write-invalid:%s:%0d:%p\n", e, 8, e);
// Runtime-computed $sformatf formats should preserve enum mnemonic/fallback behavior.
e = E2;
fmt = {"%", "s", empty_no_opt};
`check($sformatf(fmt, e), "E2");
fmt = {"%", "p", empty_no_opt};
`check($sformatf(fmt, e), "E2");
fmt = {"%0d:%", "s", ":%0d", empty_no_opt};
`check($sformatf(fmt, 9, e, 7), "9:E2:7");
fmt = {"%", "s", " %h %", "p", empty_no_opt};
`check($sformatf(fmt, e, 4'hA, e), "E2 a E2");
e = my_e'(3);
fmt = {"%", "s", empty_no_opt};
`check($sformatf(fmt, e), "3");
fmt = {"%", "p", empty_no_opt};
`check($sformatf(fmt, e), "3");
fmt = {"%0", "p", empty_no_opt};
`check($sformatf(fmt, e), "'h3");
fmt = {"%0d:%", "s", ":%0d", empty_no_opt};
`check($sformatf(fmt, 9, e, 7), "9:3:7");
fmt = {"%", "s", " %h %", "p", empty_no_opt};
`check($sformatf(fmt, e, 4'hA, e), "3 a 3");
fmt = {"%", "p", empty_no_opt};
`check($sformatf(fmt, e64), "8589934593");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -22,7 +22,7 @@ module t (
integer cyc = 0;
my_t e;
string s;
string all;
int i_cast;
// Check runtime
@ -45,10 +45,6 @@ module t (
`checkh(e.prev, E01);
`checkh(e.next(0), ELARGE);
`checkh(e.prev(0), ELARGE);
s = $sformatf("%p", e);
`checks(s, "ELARGE");
s = $sformatf("%s", e); // Non-standard but majority
`checks(s, "ELARGE");
e <= E01;
end
//
@ -72,8 +68,6 @@ module t (
end
else if (cyc == 21) begin
`checks(e.name, ""); // Unknown
s = $sformatf("%p", e);
`checks(s, "17");
end
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");

View File

@ -22,7 +22,7 @@ module t (
integer cyc = 0;
my_t e;
string s;
string all;
// Check runtime
always @(posedge clk) begin
@ -40,8 +40,6 @@ module t (
`checks(e.name, "ELARGE");
`checkh(e.next, E01);
`checkh(e.prev, E01);
s = $sformatf("%p", e);
`checks(s, "ELARGE");
e <= E01;
end
else if (cyc == 20) begin
@ -49,8 +47,6 @@ module t (
end
else if (cyc == 21) begin
`checks(e.name, ""); // Unknown
s = $sformatf("%p", e);
`checks(s, "17");
end
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");

View File

@ -11,8 +11,8 @@
// verilog_format: on
module t (
input clk
);
input clk
);
typedef enum [3:0] {
E01 = 1,
@ -20,18 +20,12 @@ module t (
E04 = 4
} my_t;
integer cyc = 0;
integer cyc = 0;
my_t e;
int arrayfits[e.num]; // Check can use as constant
int arrayfits [e.num]; // Check can use as constant
typedef struct {
my_t m_a;
my_t m_b;
} mystr_t;
mystr_t mystr;
string s;
string all;
// Check constification
initial begin
@ -54,38 +48,25 @@ module t (
`checkh(e.num, 3);
`checks(e.name, "E03");
//
s = "";
all = "";
for (my_t e = e.first; e != e.last; e = e.next) begin
s = {s, e.name};
all = {all, e.name};
end
e = e.last;
s = {s, e.name};
`checks(s, "E01E03E04");
//
e = E04;
s = $sformatf("%p", e);
`checks(s, "E04");
s = $sformatf("%p", E03);
`checks(s, "E03");
s = $sformatf("%s", E03); // Non-standard but majority
`checks(s, "E03");
//
mystr.m_a = E03;
mystr.m_b = E04;
s = $sformatf("%p", mystr);
`checks(s, "'{m_a:'h3, m_b:'h4}");
all = {all, e.name};
`checks(all, "E01E03E04");
end
localparam THREE = 3;
// Check runtime
always @(posedge clk) begin
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
if (cyc==0) begin
// Setup
e <= E01;
end
else if (cyc == 1) begin
else if (cyc==1) begin
`checks(e.name, "E01");
`checkh(e.next, E03);
`checkh(e.next(1), E03);
@ -95,7 +76,7 @@ module t (
`checkh(e.prev(2), E03);
e <= E03;
end
else if (cyc == 2) begin
else if (cyc==2) begin
`checks(e.name, "E03");
`checkh(e.next, E04);
`checkh(e.next(1), E04);
@ -105,7 +86,7 @@ module t (
`checkh(e.prev(2), E04);
e <= E04;
end
else if (cyc == 3) begin
else if (cyc==3) begin
`checks(e.name, "E04");
`checkh(e.next, E01);
`checkh(e.next(1), E01);
@ -114,11 +95,8 @@ module t (
`checkh(e.prev(1), E03);
`checkh(e.prev(2), E01);
e <= E01;
//
s = $sformatf("%p", e);
`checks(s, "E04");
end
else if (cyc == 99) begin
else if (cyc==99) begin
$write("*-* All Finished *-*\n");
$finish;
end