Optimize staticly known oversize shifts (#7806)

The non *Ovr flavours of AstShift* have better downstream constant
folding, so keep using those if proven safe. Fold overshifts explicitly
instead of introducing *Ovr shifts.
This commit is contained in:
Geza Lore 2026-06-22 10:06:26 +01:00 committed by GitHub
parent fed922a538
commit b37c84109d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 28 deletions

View File

@ -139,34 +139,52 @@ class PremitVisitor final : public VNVisitor {
}
void visitShift(AstNodeBiop* nodep) {
// Shifts of > 32/64 bits in C++ will wrap-around and generate non-0s
UINFO(4, " ShiftFix " << nodep);
const AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const);
if (shiftp && shiftp->num().mostSetBitP1() > 32) {
shiftp->v3warn(
E_UNSUPPORTED,
"Unsupported: Shifting of by over 32-bit number isn't supported."
<< " (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n");
}
if (nodep->widthMin() <= 64 // Else we'll use large operators which work right
// C operator's width must be < maximum shift which is
// based on Verilog width
&& nodep->width() < (1LL << nodep->rhsp()->widthMin())) {
AstNode* newp;
if (VN_IS(nodep, ShiftL)) {
newp = new AstShiftLOvr{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()};
} else if (VN_IS(nodep, ShiftR)) {
newp = new AstShiftROvr{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()};
} else {
UASSERT_OBJ(VN_IS(nodep, ShiftRS), nodep, "Bad case");
newp = new AstShiftRSOvr{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()};
UASSERT_OBJ(VN_IS(nodep, ShiftL) || VN_IS(nodep, ShiftR) || VN_IS(nodep, ShiftRS), nodep,
"Bad case");
// Shift larger than the width of the type (overshift) is undefined behavour in C++
// (in practice will shift by the wrapped shift amount). These are requierd to go to
// zero/msbs, so replacing them here.
FileLine* const flp = nodep->fileline();
if (const AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const)) {
// Shift amount known to be constant. If oversized shift, replace with zero/msbs.
// Otherwise we can leave the original shifts which have better constant folding
// than the *Ovr versions.
const bool isOversized = shiftp->num().mostSetBitP1() > 32 //
|| (shiftp->num().toSQuad() >= nodep->width());
if (isOversized) {
AstNodeExpr* newp = nullptr;
if (VN_IS(nodep, ShiftRS)) {
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const msbp = new AstSel{flp, lhsp, nodep->width() - 1, 1};
newp = new AstExtendS{flp, msbp, nodep->width()};
} else {
newp = new AstConst{flp, AstConst::DTyped{}, nodep->dtypep()};
}
nodep->replaceWithKeepDType(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
} else {
// Shift amount not known at compile time. Convert to *Ovr version. Don't need to do
// if it would use a wide operation which works correctly at runtime, of if the max
// value of the shift amount is less than the with of the shifted value.
if (nodep->widthMin() <= VL_QUADSIZE
&& (nodep->width() < (1LL << nodep->rhsp()->widthMin()))) {
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
AstNodeExpr* newp = nullptr;
if (VN_IS(nodep, ShiftL)) {
newp = new AstShiftLOvr{flp, lhsp, rhsp};
} else if (VN_IS(nodep, ShiftR)) {
newp = new AstShiftROvr{flp, lhsp, rhsp};
} else {
newp = new AstShiftRSOvr{flp, lhsp, rhsp};
}
nodep->replaceWithKeepDType(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
nodep->replaceWithKeepDType(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
iterateChildren(nodep);
checkNode(nodep);

View File

@ -16,7 +16,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "-fno-dfg", "--stats", test.
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 48)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 50)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()

View File

@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 38)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 40)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()