Skip to content

Commit 9c52520

Browse files
committed
feat: ushiftRight bitblasting
1 parent 90dab5e commit 9c52520

File tree

2 files changed

+83
-6
lines changed

2 files changed

+83
-6
lines changed

src/Init/Data/BitVec/Bitblast.lean

+65-6
Original file line numberDiff line numberDiff line change
@@ -403,12 +403,8 @@ theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
403403
induction n generalizing x y
404404
case zero =>
405405
ext i
406-
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, truncate_one]
407-
suffices (y &&& 1#w₂) = zeroExtend w₂ (ofBool (y.getLsb 0)) by simp [this]
408-
ext i
409-
by_cases h : (↑i : Nat) = 0
410-
· simp [h, Bool.and_comm]
411-
· simp [h]; omega
406+
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, truncate_one,
407+
and_one_eq_zeroExtend_ofBool_getLsb]
412408
case succ n ih =>
413409
simp only [shiftLeftRec_succ, and_twoPow]
414410
rw [ih]
@@ -431,4 +427,67 @@ theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
431427
· simp [of_length_zero]
432428
· simp [shiftLeftRec_eq]
433429

430+
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
431+
432+
/--
433+
`ushiftRightRec x y n` shifts `x` logically to the right by the first `n` bits of `y`.
434+
435+
The theorem `shiftRight_eq_ushiftRightRec` proves the equivalence
436+
of `(x >>> y)` and `ushiftRightRec`.
437+
438+
Together with equations `ushiftRightRec_zero`, `ushiftRightRec_succ`,
439+
this allows us to unfold `ushiftRight` into a circuit for bitblasting.
440+
-/
441+
def ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
442+
let shiftAmt := (y &&& (twoPow w₂ n))
443+
match n with
444+
| 0 => x >>> shiftAmt
445+
| n + 1 => (ushiftRightRec x y n) >>> shiftAmt
446+
447+
@[simp]
448+
theorem ushiftRightRec_zero (x : BitVec w₁) (y : BitVec w₂) :
449+
ushiftRightRec x y 0 = x >>> (y &&& twoPow w₂ 0) := by
450+
simp [ushiftRightRec]
451+
452+
@[simp]
453+
theorem ushiftRightRec_succ (x : BitVec w₁) (y : BitVec w₂) :
454+
ushiftRightRec x y (n + 1) = (ushiftRightRec x y n) >>> (y &&& twoPow w₂ (n + 1)) := by
455+
simp [ushiftRightRec]
456+
457+
/--
458+
If `y &&& z = 0`, `x >>> (y ||| z) = x >>> y >>> z`.
459+
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
460+
and thus `x >>> (y ||| z) = x >>> (y + z) = x >>> y >>> z`.
461+
-/
462+
theorem ushiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
463+
(h : y &&& z = 0#w₂) :
464+
x >>> (y ||| z) = x >>> y >>> z := by
465+
simp [← add_eq_or_of_and_eq_zero _ _ h, toNat_add_of_and_eq_zero h, shiftRight_add]
466+
467+
theorem ushiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
468+
ushiftRightRec x y n = x >>> (y.truncate (n + 1)).zeroExtend w₂ := by
469+
induction n generalizing x y
470+
case zero =>
471+
ext i
472+
simp only [ushiftRightRec_zero, twoPow_zero, Nat.reduceAdd,
473+
and_one_eq_zeroExtend_ofBool_getLsb, truncate_one]
474+
case succ n ih =>
475+
simp only [ushiftRightRec_succ, and_twoPow]
476+
rw [ih]
477+
by_cases h : y.getLsb (n + 1) <;> simp only [h, ↓reduceIte]
478+
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
479+
ushiftRight'_or_of_and_eq_zero]
480+
simp
481+
· simp [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1), h]
482+
483+
/--
484+
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
485+
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bitblasting.
486+
-/
487+
theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
488+
x >>> y = ushiftRightRec x y (w₂ - 1) := by
489+
rcases w₂ with rfl | w₂
490+
· simp [of_length_zero]
491+
· simp [ushiftRightRec_eq]
492+
434493
end BitVec

src/Init/Data/BitVec/Lemmas.lean

+18
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,16 @@ theorem getLsb_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
731731
getLsb (x >>> i) j = getLsb x (i+j) := by
732732
unfold getLsb ; simp
733733

734+
@[simp]
735+
theorem ushiftRight_zero_eq (x : BitVec w) : x >>> 0 = x := by
736+
simp [bv_toNat]
737+
738+
/-! ### ushiftRight reductions from BitVec to Nat -/
739+
740+
@[simp]
741+
theorem ushiftRight_eq' (x : BitVec w) (y : BitVec w₂) :
742+
x >>> y = x >>> y.toNat := by rfl
743+
734744
/-! ### sshiftRight -/
735745

736746
theorem sshiftRight_eq {x : BitVec n} {i : Nat} :
@@ -1549,4 +1559,12 @@ theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true
15491559
simp [hx]
15501560
· by_cases hik' : k < i + 1 <;> simp [hik, hik'] <;> omega
15511561

1562+
/-- Bitwise and of `(x : BitVec w)` with `1#w` equals zero extending `x.lsb` to `w`. -/
1563+
theorem and_one_eq_zeroExtend_ofBool_getLsb {x : BitVec w} :
1564+
(x &&& 1#w) = zeroExtend w (ofBool (x.getLsb 0)) := by
1565+
ext i
1566+
simp only [getLsb_and, getLsb_one, getLsb_zeroExtend, Fin.is_lt, decide_True, getLsb_ofBool,
1567+
Bool.true_and]
1568+
by_cases h : (0 = (i : Nat)) <;> simp [h] <;> omega
1569+
15521570
end BitVec

0 commit comments

Comments
 (0)