We define bitvectors. We choose the Fin
representation over others for its relative efficiency
(Lean has special support for Nat
), alignment with UIntXY
types which are also represented
with Fin
, 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-LIBv2.
A bitvector of the specified width.
This is represented as the underlying Nat
number in both the runtime
and the kernel, inheriting all the special support for Nat
.
- ofFin :: (
Interpret a bitvector as a number less than
2^w
. O(1), because we useFin
as the internal representation of a bitvector.- )
Instances For
Equations
Instances For
Equations
- instDecidableEqBitVec = BitVec.decEq
The BitVec
with value i mod 2^n
.
Equations
- i#n = { toFin := Fin.ofNat' i ⋯ }
Instances For
Equations
- BitVec.natCastInst = { natCast := BitVec.ofNat w }
Given a bitvector a
, return the underlying Nat
. This is O(1) because BitVec
is a
(zero-cost) wrapper around a Nat
.
Equations
- BitVec.toNat a = ↑a.toFin
Instances For
Return the bound in terms of toNat.
Theorem for normalizing the bit vector literal representation.
All empty bitvectors are equal
Every bitvector of length 0 is equal to nil
, i.e., there is only one empty bitvector
Return a bitvector 0
of size n
. This is the bitvector with all zero bits.
Equations
- BitVec.zero n = 0#'⋯
Instances For
Equations
- BitVec.instInhabitedBitVec = { default := BitVec.zero n }
Return the i
-th least significant bit or false
if i ≥ w
.
Equations
- BitVec.getLsb x i = Nat.testBit (BitVec.toNat x) i
Instances For
Return the i
-th most significant bit or false
if i ≥ w
.
Equations
- BitVec.getMsb x i = (decide (i < w) && BitVec.getLsb x (w - 1 - i))
Instances For
Return most-significant bit in bitvector.
Equations
- BitVec.msb a = BitVec.getMsb a 0
Instances For
Interpret the bitvector as an integer stored in two's complement form.
Equations
- BitVec.toInt a = if 2 * BitVec.toNat a < 2 ^ n then ↑(BitVec.toNat a) else ↑(BitVec.toNat a) - ↑(2 ^ n)
Instances For
Equations
- BitVec.instIntCastBitVec = { intCast := BitVec.ofInt w }
Notation for bit vector literals. i#n
is a shorthand for BitVec.ofNat n i
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unexpander for bit vector literals.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Notation for bit vector 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 bit vector literals without truncation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Convert bitvector into a fixed-width hex number.
Equations
- BitVec.toHex x = let s := List.asString (Nat.toDigits 16 (BitVec.toNat x)); let t := List.asString (List.replicate ((n + 3) / 4 - String.length s) '0'); t ++ s
Instances For
Equations
- BitVec.instReprBitVec = { reprPrec := fun (a : BitVec n) (x : Nat) => Std.Format.text "0x" ++ Std.Format.text (BitVec.toHex a) ++ Std.Format.text "#" ++ repr n }
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
modulo 2^n
.
SMT-Lib name: bvadd
.
Equations
- BitVec.add x y = (BitVec.toNat x + BitVec.toNat y)#n
Instances For
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo 2^n
.
Equations
- BitVec.sub x y = (BitVec.toNat x + (2 ^ n - BitVec.toNat y))#n
Instances For
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
modulo 2^n
.
SMT-Lib name: bvneg
.
Equations
- BitVec.neg x = (2 ^ n - BitVec.toNat x)#n
Instances For
Return the absolute value of a signed bitvector.
Equations
- BitVec.abs s = if BitVec.msb s = true then BitVec.neg s else s
Instances For
Multiplication for bit vectors. This can be interpreted as either signed or unsigned negation
modulo 2^n
.
SMT-Lib name: bvmul
.
Equations
- BitVec.mul x y = (BitVec.toNat x * BitVec.toNat y)#n
Instances For
Unsigned division for bit vectors using the Lean convention where division by zero returns zero.
Equations
- BitVec.udiv x y = (BitVec.toNat x / BitVec.toNat y)#'⋯
Instances For
Unsigned modulo for bit vectors.
SMT-Lib name: bvurem
.
Equations
- BitVec.umod x y = (BitVec.toNat x % BitVec.toNat y)#'⋯
Instances For
Unsigned division for bit vectors using the
SMT-Lib convention
where division by zero returns the allOnes
bitvector.
SMT-Lib name: bvudiv
.
Equations
- BitVec.smtUDiv x y = if y = 0 then BitVec.allOnes n else BitVec.udiv x y
Instances For
Signed division for bit vectors using SMTLIB rules for division by zero.
Specifically, smtSDiv x 0 = if x >= 0 then -1 else 1
SMT-Lib name: bvsdiv
.
Equations
- One or more equations did not get rendered due to their size.
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
Unsigned less-than for bit vectors.
SMT-Lib name: bvult
.
Equations
- BitVec.ult x y = decide (BitVec.toNat x < BitVec.toNat y)
Instances For
Equations
- BitVec.instLTBitVec = { lt := fun (x x_1 : BitVec n) => BitVec.toNat x < BitVec.toNat x_1 }
Equations
Unsigned less-than-or-equal-to for bit vectors.
SMT-Lib name: bvule
.
Equations
- BitVec.ule x y = decide (BitVec.toNat x ≤ BitVec.toNat y)
Instances For
Equations
- BitVec.instLEBitVec = { le := fun (x x_1 : BitVec n) => BitVec.toNat x ≤ BitVec.toNat x_1 }
Equations
Signed less-than for bit vectors.
BitVec.slt 6#4 7 = true
BitVec.slt 7#4 8 = false
SMT-Lib name: bvslt
.
Equations
- BitVec.slt x y = decide (BitVec.toInt x < BitVec.toInt y)
Instances For
Signed less-than-or-equal-to for bit vectors.
SMT-Lib name: bvsle
.
Equations
- BitVec.sle x y = decide (BitVec.toInt x ≤ BitVec.toInt y)
Instances For
cast eq i
embeds i
into an equal BitVec
type.
Equations
- BitVec.cast eq i = (BitVec.toNat i)#'⋯
Instances For
Extraction of bits start
to start + len - 1
from a bit vector of size n
to yield a
new bitvector of size len
. If start + len > n
, then the vector will be zero-padded in the
high bits.
Equations
- BitVec.extractLsb' start len a = (BitVec.toNat a >>> start)#len
Instances For
Extraction of bits hi
(inclusive) down to lo
(inclusive) from a bit vector of size n
to
yield a new bitvector of size hi - lo + 1
.
SMT-Lib name: extract
.
Equations
- BitVec.extractLsb hi lo a = BitVec.extractLsb' lo (hi - lo + 1) a
Instances For
A version of zeroExtend
that requires a proof, but is a noop.
Equations
- BitVec.zeroExtend' le x = (BitVec.toNat x)#'⋯
Instances For
shiftLeftZeroExtend x n
returns zeroExtend (w+n) x <<< n
without
needing to compute x % 2^(2+n)
.
Equations
- BitVec.shiftLeftZeroExtend msbs m = let shiftLeftLt := ⋯; (BitVec.toNat msbs <<< m)#'⋯
Instances For
Zero extend vector x
of length w
by adding zeros in the high bits until it has length v
.
If v < w
then it truncates the high bits instead.
SMT-Lib name: zero_extend
.
Equations
- BitVec.zeroExtend v x = if h : w ≤ v then BitVec.zeroExtend' h x else (BitVec.toNat x)#v
Instances For
Truncate the high bits of bitvector x
of length w
, resulting in a vector of length v
.
If v > w
then it zero-extends the vector instead.
Equations
Instances For
Sign extend a vector of length w
, extending with i
additional copies of the most significant
bit in x
. If x
is an empty vector, then the sign is treated as zero.
SMT-Lib name: sign_extend
.
Equations
- BitVec.signExtend v x = BitVec.ofInt v (BitVec.toInt x)
Instances For
Bitwise AND for bit vectors.
0b1010#4 &&& 0b0110#4 = 0b0010#4
SMT-Lib name: bvand
.
Equations
- BitVec.and x y = (BitVec.toNat x &&& BitVec.toNat y)#'⋯
Instances For
Bitwise XOR for bit vectors.
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
SMT-Lib name: bvxor
.
Equations
- BitVec.xor x y = (BitVec.toNat x ^^^ BitVec.toNat y)#'⋯
Instances For
Bitwise NOT for bit vectors.
~~~(0b0101#4) == 0b1010
SMT-Lib name: bvnot
.
Equations
- BitVec.not x = BitVec.allOnes n ^^^ x
Instances For
Equations
- BitVec.instComplementBitVec = { complement := BitVec.not }
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
equivalent to a * 2^s
, modulo 2^n
.
SMT-Lib name: bvshl
except this operator uses a Nat
shift value.
Equations
- BitVec.shiftLeft a s = (BitVec.toNat a <<< s)#n
Instances For
Equations
- BitVec.instHShiftLeftBitVecNat = { hShiftLeft := BitVec.shiftLeft }
(Logical) right shift for bit vectors. The high bits are filled with zeros.
As a numeric operation, this is equivalent to a / 2^s
, rounding down.
SMT-Lib name: bvlshr
except this operator uses a Nat
shift value.
Equations
- BitVec.ushiftRight a s = (BitVec.toNat a >>> s)#'⋯
Instances For
Equations
- BitVec.instHShiftRightBitVecNat = { hShiftRight := BitVec.ushiftRight }
Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit.
As a numeric operation, this is equivalent to a.toInt >>> s
.
SMT-Lib name: bvashr
except this operator uses a Nat
shift value.
Equations
- BitVec.sshiftRight a s = BitVec.ofInt n (BitVec.toInt a >>> s)
Instances For
Equations
- BitVec.instHShiftLeftBitVec = { hShiftLeft := fun (x : BitVec m) (y : BitVec n) => x <<< BitVec.toNat y }
Equations
- BitVec.instHShiftRightBitVec = { hShiftRight := fun (x : BitVec m) (y : BitVec n) => x >>> BitVec.toNat y }
Rotate left for bit vectors. All the bits of x
are shifted to higher positions, with the top n
bits wrapping around to fill the low bits.
rotateLeft 0b0011#4 3 = 0b1001
SMT-Lib name: rotate_left
except this operator uses a Nat
shift amount.
Instances For
Rotate right for bit vectors. All the bits of x
are shifted to lower positions, with the
bottom n
bits wrapping around to fill the high bits.
rotateRight 0b01001#5 1 = 0b10100
SMT-Lib name: rotate_right
except this operator uses a Nat
shift amount.
Instances For
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
input is on the left, so 0xAB#8 ++ 0xCD#8 = 0xABCD#16
.
SMT-Lib name: concat
.
Equations
- BitVec.append msbs lsbs = BitVec.shiftLeftZeroExtend msbs m ||| BitVec.zeroExtend' ⋯ lsbs
Instances For
replicate i x
concatenates i
copies of x
into a new vector of length w*i
.
Equations
- BitVec.replicate 0 x = 0
- BitVec.replicate (Nat.succ n) x = let_fun hEq := ⋯; hEq ▸ (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).
Append a single bit to the end of a bitvector, using big endian order (see append
).
That is, the new bit is the least significant bit.
Equations
- BitVec.concat msbs lsb = msbs ++ BitVec.ofBool lsb
Instances For
Prepend a single bit to the front of a bitvector, using big endian order (see append
).
That is, the new bit is the most significant bit.
Equations
- BitVec.cons msb lsbs = BitVec.cast ⋯ (BitVec.ofBool msb ++ lsbs)
Instances For
We add simp-lemmas that rewrite bitvector operations into the equivalent notation