SMT2 front-end: fix bvsmod semantics (sign follows divisor)#9129
Open
tautschnig wants to merge 1 commit into
Open
SMT2 front-end: fix bvsmod semantics (sign follows divisor)#9129tautschnig wants to merge 1 commit into
tautschnig wants to merge 1 commit into
Conversation
The SMT2 parser registered bvsmod identically to bvsrem, mapping both to mod_exprt, i.e. truncated division where the remainder's sign follows the dividend. SMT-LIB defines bvsmod's result sign to follow the DIVISOR: e.g. (bvsmod #b1111 #b0111) over 4 bits is 6 (-1 smod 7), not -1. Consequence: wrong verdicts on benchmarks exercising bvsmod, e.g. QF_BV/log-slicing/bvsmod_13.smt2 (:status unsat) was answered sat. bv_mod gains a sign_follows_divisor flag: bvsmod is computed from the truncated remainder r as (r != 0 && sign(r) != sign(t)) ? r + t : r which matches the SMT-LIB definitional expansion case by case; the existing divisor-zero wrapper (bvsmod x 0 = x) is unchanged. The new regression test checks all 256 4-bit operand combinations against constants precomputed from the SMT-LIB definition. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
tautschnig
requested review from
TGWDB,
kroening,
martin-cs and
peterschrammel
as code owners
July 22, 2026 10:50
There was a problem hiding this comment.
Pull request overview
Fixes CBMC’s SMT2 front-end handling of bvsmod to match SMT-LIB semantics, where the signed modulo result’s sign follows the divisor (as opposed to the truncated remainder semantics used by bvsrem, where the sign follows the dividend). This directly addresses incorrect SAT/UNSAT outcomes on benchmarks that use bvsmod.
Changes:
- Extend
smt2_parsert::bv_modwith asign_follows_divisormode and implement SMT-LIBbvsmodas an adjustment of the truncated remainderr. - Wire
bvsmodto the newbv_mod(..., /*is_signed=*/true, /*sign_follows_divisor=*/true)behavior (keeping divisor-by-zero behavior unchanged). - Add a regression test enumerating all 4-bit operand combinations to validate
bvsmodsemantics.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/solvers/smt2/smt2_parser.h | Extends bv_mod API with a sign_follows_divisor flag (defaulting to false). |
| src/solvers/smt2/smt2_parser.cpp | Implements divisor-sign-following bvsmod semantics and updates the bvsmod expression registration. |
| regression/smt2_solver/bvsmod1/test.desc | Adds a regression test spec expecting unsat for the exhaustive 4-bit check. |
| regression/smt2_solver/bvsmod1/bvsmod1.smt2 | New SMT-LIB test that checks all 256 (4-bit) bvsmod pairs against precomputed expected results. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
1295
to
+1297
| // 2's complement signed remainder (sign follows divisor) | ||
| // We don't have that. | ||
| expressions["bvsmod"] = [this] { return bv_mod(operands(), true); }; | ||
| expressions["bvsmod"] = [this] { return bv_mod(operands(), true, true); }; |
Collaborator
|
Is this worth introducing say |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The SMT2 parser registered
bvsmodidentically tobvsrem, mapping both tomod_exprt— truncated division, where the remainder's sign follows the dividend. SMT-LIB definesbvsmod's result sign to follow the divisor: e.g.(bvsmod #b1111 #b0111)over 4 bits is6(−1 smod 7), not−1(the existing code even noted "bvsmod doesn't [match]" without acting on it).Consequence: wrong verdicts on benchmarks exercising
bvsmod— e.g. SMT-LIBQF_BV/log-slicing/bvsmod_13.smt2(:status unsat) was answeredsat. Found by cross-solver differential testing on a stratified SMT-LIB 2024 sample; it was the only verdict disagreement in 3,426 runs.bv_modgains asign_follows_divisorflag:bvsmodis computed from the truncated remainderras(r != 0 && sign(r) != sign(t)) ? r + t : r, which matches the SMT-LIB definitional expansion case by case. The divisor-zero wrapper (bvsmod x 0 = x) is unchanged, as arebvsrem/bvurem.Testing: new regression test
regression/smt2_solver/bvsmod1checks all 256 4-bit operand combinations against constants precomputed from the SMT-LIB definitional expansion; the definitional-expansion equivalence is also UNSAT at 13 bits; the fullregression/smt2_solversuite passes.