We define the basic algebraic structure of bitvectors. We choose the Fin
representation over
others for its relative efficiency (Lean has special support for Nat
), and the fact that bitwise
operations on Fin
are already defined. Some other possible representations are List Bool
,
{ l : List Bool // l.length = w }
, Fin w → Bool
.
We define many of the bitvector operations from the
QF_BV
logic.
of SMT-LIB v2.
Equations
- BitVec.natCastInst = { natCast := BitVec.ofNat w }
Theorem for normalizing the bitvector literal representation.
All empty bitvectors are equal
Equations
- BitVec.instInhabited = { default := BitVec.zero n }
Returns a bitvector of size n
where all bits are 1
.
Equations
- BitVec.allOnes n = (2 ^ n - 1)#'⋯
Instances For
Converts an integer to its two's complement representation as a bitvector of the given width n
,
over- and underflowing as needed.
The underlying Nat
is (2^n + (i mod 2^n)) mod 2^n
. Converting the bitvector back to an Int
with BitVec.toInt
results in the value i.bmod (2^n)
.
Instances For
Equations
- BitVec.instIntCast = { intCast := BitVec.ofInt w }
Notation for bitvector literals. i#n
is a shorthand for BitVec.ofNat n i
.
Conventions for notations in identifiers:
The recommended spelling of
0#n
in identifiers iszero
(notofNat_zero
).The recommended spelling of
1#n
in identifiers isone
(notofNat_one
).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unexpander for bitvector literals.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Notation for bitvector literals without truncation. i#'lt
is a shorthand for BitVec.ofNatLT i lt
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unexpander for bitvector literals without truncation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Converts a bitvector into a fixed-width hexadecimal number with enough digits to represent it.
If n
is 0
, then one digit is returned. Otherwise, ⌊(n + 3) / 4⌋
digits are returned.
Equations
- x.toHex = (List.replicate ((n + 3) / 4 - (Nat.toDigits 16 x.toNat).asString.length) '0').asString ++ (Nat.toDigits 16 x.toNat).asString
Instances For
Equations
- BitVec.instRepr = { reprPrec := fun (a : BitVec n) (x : Nat) => Std.Format.text "0x" ++ Std.Format.text a.toHex ++ Std.Format.text "#" ++ repr n }
Equations
- BitVec.instToString = { toString := fun (a : BitVec n) => toString (repr a) }
Equations
- BitVec.instNeg = { neg := BitVec.neg }
Equations
- BitVec.instMul = { mul := BitVec.mul }
Equations
- BitVec.instDiv = { div := BitVec.udiv }
Equations
- BitVec.instMod = { mod := BitVec.umod }
Unsigned division of bitvectors using the
SMT-LIB convention,
where division by zero returns BitVector.allOnes n
.
SMT-LIB name: bvudiv
.
Instances For
Signed T-division (using the truncating rounding convention) for bitvectors. This function obeys the Lean convention that division by zero returns zero.
Examples:
Equations
Instances For
Signed division for bitvectors using the SMT-LIB using the
SMT-LIB convention,
where division by zero returns BitVector.allOnes n
.
Specifically, x.smtSDiv 0 = if x >= 0 then -1 else 1
SMT-LIB name: bvsdiv
.
Equations
Instances For
Remainder for signed division rounding to zero.
SMT-LIB name: bvsrem
.
Equations
Instances For
Remainder for signed division rounded to negative infinity.
SMT-LIB name: bvsmod
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Turns a Bool
into a bitvector of length 1
.
Equations
- BitVec.ofBool b = bif b then 1 else 0
Instances For
Fills a bitvector with w
copies of the bit b
.
Equations
- BitVec.fill w b = bif b then -1 else 0
Instances For
Signed less-than for bitvectors.
SMT-LIB name: bvslt
.
Examples:
BitVec.slt 6#4 7 = true
BitVec.slt 7#4 8 = false
Instances For
If two natural numbers n
and m
are equal, then a bitvector of width n
is also a bitvector of
width m
.
Using x.cast eq
should be preferred over eq ▸ x
because there are special-purpose simp
lemmas
that can more consistently simplify BitVec.cast
away.
Equations
- BitVec.cast eq x = x.toNat#'⋯
Instances For
Extracts the bits start
to start + len - 1
from a bitvector of size n
to yield a
new bitvector of size len
. If start + len > n
, then the bitvector is zero-extended.
Equations
- BitVec.extractLsb' start len x = BitVec.ofNat len (x.toNat >>> start)
Instances For
Extracts the bits from hi
down to lo
(both inclusive) from a bitvector, which is implicitly
zero-extended if necessary.
The resulting bitvector has size hi - lo + 1
.
SMT-LIB name: extract
.
Equations
- BitVec.extractLsb hi lo x = BitVec.extractLsb' lo (hi - lo + 1) x
Instances For
Increases the width of a bitvector to one that is at least as large by zero-extending it.
This is a constant-time operation because the underlying Nat
is unmodified; because the new width
is at least as large as the old one, no overflow is possible.
Equations
- BitVec.setWidth' le x = x.toNat#'⋯
Instances For
Increases the width of a bitvector to one that is at least as large by zero-extending it.
This is a constant-time operation because the underlying Nat
is unmodified; because the new width
is at least as large as the old one, no overflow is possible.
Equations
Instances For
Returns zeroExtend (w+n) x <<< n
without needing to compute x % 2^(2+n)
.
Equations
- msbs.shiftLeftZeroExtend m = (msbs.toNat <<< m)#'⋯
Instances For
Transforms a bitvector of length w
into a bitvector of length v
, padding with 0
as needed.
The specific behavior depends on the relationship between the starting width w
and the final width
v
:
- If
v > w
, it is zero-extended; the high bits are padded with zeroes until the bitvector hasv
bits. - If
v = w
, the bitvector is returned unchanged. - If
v < w
, the high bits are truncated.
BitVec.setWidth
, BitVec.zeroExtend
, and BitVec.truncate
are aliases for this operation.
SMT-LIB name: zero_extend
.
Equations
- BitVec.setWidth v x = if h : w ≤ v then BitVec.setWidth' h x else BitVec.ofNat v x.toNat
Instances For
Transforms a bitvector of length w
into a bitvector of length v
, padding with 0
as needed.
The specific behavior depends on the relationship between the starting width w
and the final width
v
:
- If
v > w
, it is zero-extended; the high bits are padded with zeroes until the bitvector hasv
bits. - If
v = w
, the bitvector is returned unchanged. - If
v < w
, the high bits are truncated.
BitVec.setWidth
, BitVec.zeroExtend
, and BitVec.truncate
are aliases for this operation.
SMT-LIB name: zero_extend
.
Equations
Instances For
Transforms a bitvector of length w
into a bitvector of length v
, padding with 0
as needed.
The specific behavior depends on the relationship between the starting width w
and the final width
v
:
- If
v > w
, it is zero-extended; the high bits are padded with zeroes until the bitvector hasv
bits. - If
v = w
, the bitvector is returned unchanged. - If
v < w
, the high bits are truncated.
BitVec.setWidth
, BitVec.zeroExtend
, and BitVec.truncate
are aliases for this operation.
SMT-LIB name: zero_extend
.
Equations
Instances For
Transforms a bitvector of length w
into a bitvector of length v
, padding as needed with the most
significant bit's value.
If x
is an empty bitvector, then the sign is treated as zero.
SMT-LIB name: sign_extend
.
Equations
- BitVec.signExtend v x = BitVec.ofInt v x.toInt
Instances For
Equations
- BitVec.instAndOp = { and := BitVec.and }
Equations
- BitVec.instOrOp = { or := BitVec.or }
Equations
- BitVec.instXor = { xor := BitVec.xor }
Bitwise complement for bitvectors. Usually accessed via the ~~~
prefix operator.
SMT-LIB name: bvnot
.
Example:
~~~(0b0101#4) == 0b1010
Equations
- x.not = BitVec.allOnes n ^^^ x
Instances For
Equations
- BitVec.instComplement = { complement := BitVec.not }
Equations
- BitVec.instHShiftLeftNat = { hShiftLeft := BitVec.shiftLeft }
Equations
- BitVec.instHShiftRightNat = { hShiftRight := BitVec.ushiftRight }
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with most significant bit's value.
As a numeric operation, this is equivalent to x.toInt >>> s
.
SMT-LIB name: bvashr
except this operator uses a Nat
shift value.
Equations
- x.sshiftRight s = BitVec.ofInt n (x.toInt >>> s)
Instances For
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with most significant bit's value.
As a numeric operation, this is equivalent to a.toInt >>> s.toNat
.
SMT-LIB name: bvashr
.
Equations
- a.sshiftRight' s = a.sshiftRight s.toNat
Instances For
Auxiliary function for rotateLeft
, which does not take into account the case where
the rotation amount is greater than the bitvector width.
Instances For
Rotates the bits in a bitvector to the left.
All the bits of x
are shifted to higher positions, with the top n
bits wrapping around to fill
the vacated low bits.
SMT-LIB name: rotate_left
, except this operator uses a Nat
shift amount.
Example:
(0b0011#4).rotateLeft 3 = 0b1001
Equations
- x.rotateLeft n = x.rotateLeftAux (n % w)
Instances For
Auxiliary function for rotateRight
, which does not take into account the case where
the rotation amount is greater than the bitvector width.
Instances For
Rotates the bits in a bitvector to the right.
All the bits of x
are shifted to lower positions, with the bottom n
bits wrapping around to fill
the vacated high bits.
SMT-LIB name: rotate_right
, except this operator uses a Nat
shift amount.
Example:
rotateRight 0b01001#5 1 = 0b10100
Equations
- x.rotateRight n = x.rotateRightAux (n % w)
Instances For
Concatenates two bitvectors using the “big-endian” convention that the more significant
input is on the left. Usually accessed via the ++
operator.
SMT-LIB name: concat
.
Example:
0xAB#8 ++ 0xCD#8 = 0xABCD#16
.
Equations
- msbs.append lsbs = msbs.shiftLeftZeroExtend m ||| BitVec.setWidth' ⋯ lsbs
Instances For
Equations
- BitVec.instHAppendHAddNat = { hAppend := BitVec.append }
Concatenates i
copies of x
into a new vector of length w * i
.
Equations
- BitVec.replicate 0 x✝ = 0#0
- BitVec.replicate n.succ x✝ = BitVec.cast ⋯ (x✝ ++ BitVec.replicate n x✝)
Instances For
Cons and Concat #
We give special names to the operations of adding a single bit to either end of a bitvector.
We follow the precedent of Vector.cons
/Vector.concat
both for the name, and for the decision
to have the resulting size be n + 1
for both operations (rather than 1 + n
, which would be the
result of appending a single bit to the front in the naive implementation).
Shifts all bits of x
to the left by 1
and sets the least significant bit to b
.
This is a non-dependent version of BitVec.concat
that does not change the total bitwidth.
Equations
- x.shiftConcat b = BitVec.truncate n (x.concat b)
Instances For
Prepends a single bit to the front of a bitvector, using big-endian order (see append
).
The new bit is the most significant bit.
Equations
- BitVec.cons msb lsbs = BitVec.cast ⋯ (BitVec.ofBool msb ++ lsbs)
Instances For
twoPow w i
is the bitvector 2^i
if i < w
, and 0
otherwise. In other words, it is 2 to the
power i
.
From the bitwise point of view, it has the i
th bit as 1
and all other bits as 0
.
Equations
- BitVec.twoPow w i = 1#w <<< i
Instances For
Equations
- BitVec.instHashable = { hash := BitVec.hash }
We add simp-lemmas that rewrite bitvector operations into the equivalent notation
Converts a list of Bool
s into a big-endian BitVec
.
Equations
- BitVec.ofBoolListBE [] = 0#0
- BitVec.ofBoolListBE (b :: bs) = BitVec.cons b (BitVec.ofBoolListBE bs)
Instances For
Converts a list of Bool
s into a little-endian BitVec
.
Equations
- BitVec.ofBoolListLE [] = 0#0
- BitVec.ofBoolListLE (b :: bs) = (BitVec.ofBoolListLE bs).concat b
Instances For
Overflow #
Checks whether addition of x
and y
results in signed overflow, treating x
and y
as 2's
complement signed bitvectors.
SMT-LIB name: bvsaddo
.
Equations
Instances For
Checks whether the subtraction of x
and y
results in signed overflow, treating x
and y
as
2's complement signed bitvectors.
SMT-Lib name: bvssubo
.