Fix unintended side-effect insertion on associative array read (#8077)
This commit is contained in:
parent
0a3657517d
commit
649598c74c
|
|
@ -570,7 +570,8 @@ public:
|
|||
idxWidths.push_back(idx_width);
|
||||
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
|
||||
|
||||
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
|
||||
record_arr_table(var.atWrite(key), indexed_name, dimension - 1, indices,
|
||||
idxWidths);
|
||||
|
||||
// Cleanup indices and widths
|
||||
idxWidths.pop_back();
|
||||
|
|
@ -644,7 +645,7 @@ public:
|
|||
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices,
|
||||
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
|
||||
idxWidths);
|
||||
}
|
||||
}
|
||||
|
|
@ -772,7 +773,7 @@ public:
|
|||
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
|
||||
T_Key key;
|
||||
for (int exists = value.first(key); exists; exists = value.next(key)) {
|
||||
basicStdRandomization(value.at(key), width);
|
||||
basicStdRandomization(value.atWrite(key), width);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T
|
|||
T_Value value;
|
||||
os >> index;
|
||||
os >> value;
|
||||
rhs.at(index) = value;
|
||||
rhs.atWrite(index) = value;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1120,9 +1120,7 @@ public:
|
|||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
T_Value& atWrite(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
|
|
@ -1138,7 +1136,7 @@ public:
|
|||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
atWrite(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
|
|
@ -1395,7 +1393,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
|||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
rmem.setData(&(obj.atWrite(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4895,9 +4895,11 @@ public:
|
|||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; } // AssocSel creates on miss
|
||||
bool isGateOptimizable() const override {
|
||||
return !isLValue(); // AssocSel creates on miss
|
||||
}
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; } // AssocSel creates on miss
|
||||
bool isPure() override { return !isLValue(); } // AssocSel creates on miss
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2053,6 +2053,8 @@ bool AstNodeExpr::isLValue() const {
|
|||
return varrefp->access().isWriteOrRW();
|
||||
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
||||
return memberselp->access().isWriteOrRW();
|
||||
} else if (const AstStructSel* const structselp = VN_CAST(this, StructSel)) {
|
||||
return structselp->fromp()->isLValue();
|
||||
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
|
||||
return selp->fromp()->isLValue();
|
||||
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ void EmitCFunc::emitVarReset(const string& prefix, AstVar* varp, bool constructi
|
|||
const auto& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* const valuep = itr.second->valuep();
|
||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
||||
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} else if (VN_IS(dtypep, WildcardArrayDType)) {
|
||||
|
|
@ -496,7 +496,7 @@ void EmitCFunc::emitVarReset(const string& prefix, AstVar* varp, bool constructi
|
|||
const auto& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* const valuep = itr.second->valuep();
|
||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
||||
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
|
|
|
|||
|
|
@ -732,7 +732,8 @@ public:
|
|||
}
|
||||
void visit(AstAssocSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, ".at(");
|
||||
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||
putnbs(nodep, atFunc);
|
||||
AstAssocArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||
|
|
@ -741,7 +742,8 @@ public:
|
|||
}
|
||||
void visit(AstWildcardSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, ".at(");
|
||||
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||
putnbs(nodep, atFunc);
|
||||
AstWildcardArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/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()
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 Antmicro
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
|
||||
// dist
|
||||
class Cls;
|
||||
rand bit dict[int unsigned];
|
||||
function void call_rand;
|
||||
void'(randomize() with {
|
||||
dict[0] dist {
|
||||
1 :/ 1
|
||||
};
|
||||
});
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// foreach
|
||||
int dict1d[int];
|
||||
int dict2d[int][string];
|
||||
int dict3d[int][string][int];
|
||||
Cls cls;
|
||||
initial begin
|
||||
// 1D
|
||||
foreach (dict1d[b]) begin
|
||||
$error(b); // should never reach
|
||||
end
|
||||
`checkd(dict1d.size(), 0);
|
||||
|
||||
// 2D
|
||||
foreach (dict2d[0][b]) begin
|
||||
$error(b); // should never reach
|
||||
end
|
||||
`checkd(dict2d.size(), 0);
|
||||
|
||||
// 3D
|
||||
foreach (dict3d[0][i]) begin
|
||||
foreach (dict3d[0][i][j]) begin
|
||||
$error(i, j); // should never reach
|
||||
end
|
||||
end
|
||||
`checkd(dict3d.size(), 0);
|
||||
|
||||
cls = new;
|
||||
cls.call_rand();
|
||||
`checkd(cls.dict.size(), 0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/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()
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 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 Antmicro
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
int a;
|
||||
int dict1d[*];
|
||||
int dict2d[*][*];
|
||||
int dict3d[*][*][*];
|
||||
int dictmix[int][*];
|
||||
|
||||
`checkd(dict1d[0], 0);
|
||||
`checkd(dict1d.size(), 0);
|
||||
`checkd(dict2d[0][0], 0);
|
||||
`checkd(dict2d.size(), 0);
|
||||
`checkd(dict3d[0][0][0], 0);
|
||||
`checkd(dict3d.size(), 0);
|
||||
`checkd(dictmix[0][0], 0);
|
||||
`checkd(dictmix.size(), 0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue