Closed brianhuffman closed 4 years ago
Ok, it looks like rotate_left
and rotate_right
with an argument that's not a numeric literal is not part of the smtlib standard. (The smtlib standard does include left- and right-shift operators with symbolic shift amounts, however, but we're not using them either.)
Another encoding of symbolic rotates that z3 handles much better than lookup tables is to use a barrel-shifter-style construction, where we branch on the bits of the rotation amount:
(set-option :smtlib2_compliant true)
(set-option :diagnostic-output-channel "stdout")
(set-option :produce-models true)
(set-logic QF_UFBV)
; --- skolem constants ---
(declare-fun s0 () (_ BitVec 5))
(declare-fun s1 () (_ BitVec 32))
; --- formula ---
(define-fun x0 () Bool (distinct #b0 ((_ extract 0 0) s0)))
(define-fun x1 () Bool (distinct #b0 ((_ extract 1 1) s0)))
(define-fun x2 () Bool (distinct #b0 ((_ extract 2 2) s0)))
(define-fun x3 () Bool (distinct #b0 ((_ extract 3 3) s0)))
(define-fun x4 () Bool (distinct #b0 ((_ extract 4 4) s0)))
(define-fun s2 () (_ BitVec 32) (ite x0 ((_ rotate_left 1) s1) s1))
(define-fun s3 () (_ BitVec 32) (ite x1 ((_ rotate_left 2) s2) s2))
(define-fun s4 () (_ BitVec 32) (ite x2 ((_ rotate_left 4) s3) s3))
(define-fun s5 () (_ BitVec 32) (ite x3 ((_ rotate_left 8) s4) s4))
(define-fun s6 () (_ BitVec 32) (ite x4 ((_ rotate_left 16) s5) s5))
(define-fun s7 () (_ BitVec 32) (ite x0 ((_ rotate_right 1) s6) s6))
(define-fun s8 () (_ BitVec 32) (ite x1 ((_ rotate_right 2) s7) s7))
(define-fun s9 () (_ BitVec 32) (ite x2 ((_ rotate_right 4) s8) s8))
(define-fun s10 () (_ BitVec 32) (ite x3 ((_ rotate_right 8) s9) s9))
(define-fun s11 () (_ BitVec 32) (ite x4 ((_ rotate_right 16) s10) s10))
(define-fun s12 () Bool (= s11 s1))
(assert (not s12))
(check-sat)
z3
can handle this in just a few hundredths of a second.
@brianhuffman
I'm not sure why you say: _The smtlib standard does include left- and right-shift operators with symbolic shift amounts, however, but we're not using them either._:
Prelude Data.SBV> proveWith z3{verbose=True} $ \x -> (x::SWord8) `sShiftLeft` (2::SWord8) .== x*4
...
[GOOD] (set-logic QF_BV)
...
[GOOD] (define-fun s1 () (_ BitVec 8) #x02)
[GOOD] (define-fun s3 () (_ BitVec 8) #x04)
[GOOD] (declare-fun s0 () (_ BitVec 8))
...
[GOOD] (define-fun s2 () (_ BitVec 8) (bvshl s0 s1))
[GOOD] (define-fun s4 () (_ BitVec 8) (bvmul s0 s3))
[GOOD] (define-fun s5 () Bool (= s2 s4))
[GOOD] (assert (not s5))
[SEND] (check-sat)
[RECV] unsat
*** Solver : Z3
*** Exit code: ExitSuccess
Q.E.D.
So, SBV does call the variable versions for sShiftLeft
and sShiftRight
; is that not what Cryptol is using? If not, we should fix that!
Regarding the rotate: That is a shame indeed; I wish SMTLib allowed for variable rotations. But looks like z3 supports them, so we can:
Add a SolverCapability
to indicate variable rotates are supported, and have SBV spit these out without creating the tables. We already do this for a number of things, so it would be quite within the SBV philosophy. See here: https://github.com/LeventErkok/sbv/blob/master/Data/SBV/Core/Symbolic.hs#L1586-L1601
Implement the barrel-shifter-style construction as you mentioned; and have all solvers take advantage of this.
I'd go for option 1, since it is probably simpler to do and honestly z3
is used almost always as the backend; though I do know you also care about ABC.
Pull requests most welcome!
Oh, my mistake about cryptol/sbv not using the symbolic shift amounts; at some point in the past this was true but I guess not anymore.
Yes, it used to be that way back in the day when they didn't have symbolic shift amounts. I wonder why they didn't add it to the rotates.
I'm working on a patch to implement the first idea above, i.e., do it via SolverCapability
. If you want to also have the barrel-shifter, I'll leave that to you: You can add that orthogonally later on. (Or, I suppose you can also do that directly inside Cryptol, no SBV support should be necessary for that.)
@brianhuffman
I've played around with this a bit, but ran into z3 issues; filed here: https://github.com/Z3Prover/z3/issues/2142
Depending on their response, my inclination is to support such symbolic rotate cases only if z3 ends up properly supporting them. I've a feeling they don't really support it, but some variants seem to go through the parser.
Ah, as I suspected; z3 doesn't really support symbolic rotates. The fact that what you tried passed was a fluke apparently. They just put in a patch to reject it at parse-time now.
I'll see if I can get your barrel-shift based idea working. I like it better this way as all solvers (potentially) can benefit from it.
@brianhuffman
I've checked in an implementation of "barrel" rotates: https://github.com/LeventErkok/sbv/commit/10e58be41e11a13500838137d07b52f654617edd
Instead of modifying the existing rotate code, I opted to add these as separate functions. The semantics of rotates is already rather complicated, and I think it's worth having the regular version in there in case there's something wrong with the barrel versions. They require bit-vector arguments and also insist that the rotate amount is unsigned. I think that should take care of the Cryptol use case, by generating simpler code.
I tried with z3, and it does seem to be performing well with them. Give it a shot and let me know what you find out. (There'll be an SBV release soon, and then I expect a rather lengthy period of inactivity, so it's best to flesh out any issues quickly.)
I tested this just now... it seems like we are still using SBV in a way that generates tables for the rotates... but Z3 now seems to solve this problem quickly (0.05s with Z3 4.8.7)? I'm not sure what changed.
Do we still want to swap over to the barrel-shifter?
This proof is now done essentially instantly by all the solvers I have access to (except ABC, which crashes shrug), I think we can close this. For posterity, my Z3 at the moment is 4.8.7
Rotating right by
r
is the inverse of rotating left byr
:It takes a really long time for z3 to figure this out! (Yices and CVC4 are pretty fast, admittedly.)
However, z3 could be fast on this problem! The issue is that the rotations are encoded in smtlib using tables:
Checking this with
z3
takes more than 30 seconds. But checking this equivalent variant takes only a few milliseconds:We should be using the
rotate_left
androtate_right
functions with symbolic rotation amounts instead of generating lookup tables for rotations.