Optimize staticly known oversize shifts

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-19 09:02:17 +01:00
parent 749b93e405
commit 0cd1fdf47e
3 changed files with 45 additions and 28 deletions

View File

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

View File

@ -16,7 +16,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "-fno-dfg", "--stats", test.
test.execute() test.execute()
if test.vlt: 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.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes() test.passes()

View File

@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
test.execute() test.execute()
if test.vlt: 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.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes() test.passes()