3

I've been trying to show that two bitvectors are equivalent when all its individual bits are equivalent. In other words, the following statement where a and b are bv64 and BitIsSet is a function extracting the ith bit from the bitvector, and WORD_SIZE is 64.

(a == b) == (forall i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) == BitIsSet(b, i)))

The first case, where a == b seems trivial for Dafny. So what I'm left to show is that

if a != b {
    assert !(forall i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) == BitIsSet(b, i)));
}

which basically is showing that there exists an i such that the two bits are different:

    assert exists i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) != BitIsSet(b, i)) 

Any hints on how to best go about this?

For completeness, BitIsSet basically masks the bit at ith position and compares it against 0.

predicate method {:opaque} BitIsSet(x:bv64, i: nat)
    requires i < WORD_SIZE
{
   (x & ((1 as bv64) << (i as bv7))) != 0
}

The same thing by using sequences of booleans seems to be pretty much trivial, I suspect that there is some kind of axiom being used?

lemma SeqBoolEquiv(a: seq<bool>, b: seq<bool>)
    requires |a| == WORD_SIZE && |b| == WORD_SIZE
    ensures (a == b) <==> (forall i | 0 <= i < WORD_SIZE :: a[i] == b[i])
{
}

Edit: Trying to understand what's going on here: Would the inability to show equivalence for bitvectors (as opposed to sequences) likely be due to a missing axiom at Dafny level, or some possible limitations at Boogie or Z3 showing equivalence of bitvectors there?

1 Answers1

3

I've managed to do the proof, roughly following the proof of a colleague in a different Language. So, Dafny is happy to show that

(a & 0xffff_ffff_ffff_ffff) == (b & 0xffff_ffff_ffff_ffff) <==> a == b

We can use this and do induction over the bitmask, converting our initial proof into:

((a & Bitmask(N)) == (b & Bitmask(N)) == (forall i | 0 <= i < N :: (BitIsSet(a, i) == BitIsSet(b, i)))

then do induction over N with base case N == 0 where Bitmask(0) == 0, and show the step case reasoning about the additional bit.

Finally, we can use the induction proof to get the result for N == WORD_SIZE which gives us the original lemma as (a & Bitmask(WORD_SIZE)) == a.

EDIT: The BitMask definition. Does rely on overflow to get BitMask(64) working, i.e., it shifts the 1 out of the bv64, then 0 - 1 == 0xff..ff. Dafny seems happy with it, and I can show that BitMask(WORD_SIZE) == 0xff..ff

    function method {:opaque} BitMask(i: uint64) : bv64
        requires i <= WORD_SIZE
    {
        ((1 as bv64) << (i as bv7)) - 1
    }