Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bitVec shiftLeft recurrences for bitblasting #4571

Merged
merged 51 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2fde296
stash
bollu Jul 2, 2024
b60eb5f
chore: rebase
bollu Jul 3, 2024
92fefe2
merge
bollu Jul 3, 2024
640df34
chore: fix warnings
bollu Jul 3, 2024
f1a3738
chore: minimize delta in basic, lemmas
bollu Jul 3, 2024
d87babd
chore: minimize delta in bitblast
bollu Jul 3, 2024
8ddb247
chore: cleanup, move lemmas to lemmas
bollu Jul 3, 2024
c775f5f
chore: move lemmas into lemmas
bollu Jul 3, 2024
18af03d
chore: move lemmas
bollu Jul 3, 2024
f9275e0
Update src/Init/Data/BitVec/Bitblast.lean
bollu Jul 3, 2024
b8f8a6e
Update src/Init/Data/BitVec/Bitblast.lean
bollu Jul 3, 2024
7d1918b
chore: add NOTE about extra hypothesis
bollu Jul 10, 2024
c22f3a6
chore: fix simp
bollu Jul 10, 2024
c4939d9
chore: fixup simp
bollu Jul 10, 2024
51e0c09
chore: cleanup
bollu Jul 10, 2024
f5450ae
chore: cleanup proof
bollu Jul 10, 2024
fdeecb0
chore: fixup simp
bollu Jul 22, 2024
abb6c99
chore: fuse rw
bollu Jul 22, 2024
0a6ed69
chore: fuse rw
bollu Jul 22, 2024
0da724d
chore: squeeze proof
bollu Jul 22, 2024
12b4082
chore: squeeze
bollu Jul 22, 2024
0b5b6db
chore: mark unused hypothesis as inaccessible
bollu Jul 22, 2024
cb2b544
Update src/Init/Data/BitVec/Bitblast.lean
bollu Jul 22, 2024
cce9e8f
Update src/Init/Data/BitVec/Lemmas.lean
bollu Jul 23, 2024
8009f75
chore: clean up proof of getLsb_ofNat_one to reuse getLsb_twoPow
bollu Jul 23, 2024
cebfe1b
chore: mark shiftLeft_eq' as a simp lemma
bollu Jul 23, 2024
9a73edf
chore: delete duplicate lemmas
bollu Jul 23, 2024
b5f7a9b
chore: and_twoPow_eq -> and_twoPow. Also add twoPow_and
bollu Jul 23, 2024
86ff901
chore: rephrase docstring, unsure if I got it right
bollu Jul 23, 2024
38cd01c
Update src/Init/Data/BitVec/Bitblast.lean
bollu Jul 23, 2024
5df7d21
chore: massively cleanup proof via Kim's strategy
bollu Jul 23, 2024
0393592
Update src/Init/Data/BitVec/Lemmas.lean
bollu Jul 24, 2024
6060574
chore: more cleanup
bollu Jul 24, 2024
5651e3b
chore: move lemma from bitblast into lemma, and write docstrings
bollu Jul 24, 2024
5981e7d
getLsb_ofNat_one -> getLsb_one
bollu Jul 24, 2024
805d815
chore: add docstring for shiftLeft_eq_shiftLeftRec
bollu Jul 24, 2024
f70acdc
chore: replace have heq : ...; simp [heq] anti-pattern with suffices
bollu Jul 24, 2024
c4b9473
chore: don't pass explicit params where un-necessary
bollu Jul 24, 2024
5ead5b4
chore: explain carry_of_and_eq_zero
bollu Jul 24, 2024
568864f
chore: move addition lemma next to carry
bollu Jul 24, 2024
f76acf9
chore: un-simp twoPow_zero
bollu Jul 24, 2024
83d367c
chore: final refactor
bollu Jul 24, 2024
c575ce8
chore: delete type ascription
bollu Jul 25, 2024
50f34f9
chore: change w -> w₁
bollu Jul 25, 2024
e5f9a2d
chore: delete repeated the
bollu Jul 25, 2024
d980687
chore: update comments
bollu Jul 25, 2024
47d51f9
chore: fix typo: w -> w₁
bollu Jul 25, 2024
3af6a9d
Update src/Init/Data/BitVec/Lemmas.lean
tobiasgrosser Jul 26, 2024
1517414
Update src/Init/Data/BitVec/Bitblast.lean
tobiasgrosser Jul 26, 2024
67e676c
Update src/Init/Data/BitVec/Bitblast.lean
tobiasgrosser Jul 26, 2024
e0a827f
Finish renames
tobiasgrosser Jul 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 109 additions & 1 deletion src/Init/Data/BitVec/Bitblast.lean
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w
simp [hik', hik'']
· ext k
simp
omega
by_cases hi : x.getLsb i <;> simp [hi] <;> omega
bollu marked this conversation as resolved.
Show resolved Hide resolved

/--
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
Expand Down Expand Up @@ -326,4 +326,112 @@ theorem getLsb_mul (x y : BitVec w) (i : Nat) :
· simp
· omega

/-## shiftLeft recurrence for bitblasting -/
bollu marked this conversation as resolved.
Show resolved Hide resolved

/--
A recurrence that describes shifting by an arbitrary bitvector
as shifting by a constant amount.
bollu marked this conversation as resolved.
Show resolved Hide resolved
-/
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x <<< shiftAmt
| n + 1 => (shiftLeftRec x y n) <<< shiftAmt
bollu marked this conversation as resolved.
Show resolved Hide resolved

@[simp]
theorem shiftLeftRec_zero {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y 0 = x <<< (y &&& twoPow w₂ 0) := by
simp [shiftLeftRec]

@[simp]
theorem shiftLeftRec_succ {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y (n + 1) =
(shiftLeftRec x y n) <<< (y &&& twoPow w₂ (n + 1)) := by
simp [shiftLeftRec]

@[simp]
theorem getLsb_ofNat_one {w i : Nat} :
(1#w).getLsb i = (decide (i = 0) && decide (i < w)) := by
bollu marked this conversation as resolved.
Show resolved Hide resolved
rcases w with rfl | w
· simp
· simp only [getLsb, toNat_ofNat, testBit_mod_two_pow]
by_cases hi : i = 0
· simp [hi]
· simp only [hi, decide_False, Bool.false_and,
and_eq_false_imp, decide_eq_true_eq]
intros _; simp [testBit, shiftRight_eq_div_pow];
bollu marked this conversation as resolved.
Show resolved Hide resolved
suffices 1 / 2^i = 0 by simp [this]
apply Nat.div_eq_of_lt (Nat.one_lt_two_pow_iff.mpr hi)

/--
If `(y &&& z = 0)`, then shifting by `y ||| z` is the same as shifting by `y` and then by `z`.
Note that the hypothesis `h'` is implied by `h : y &&& z = 0#w`,
but we choose to take the additional hypothesis and leave proving it
as an implication of `h` for a follow up PR.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be too bad. Just prove inductively that carry i x y false = false given x &&& y = 0.

e.g.

theorem carry_of_and_eq_zero (h : x &&& y = 0) : carry i x y false = false := by
  induction i with
  | zero => simp
  | succ i ih => 
    replace h := congrArg (·.getLsb i) h
    simp_all [carry_succ]

comes right after carry_succ.

Seems easier that the proof in the special case you have to give for this hypothesis later.

theorem carry_width {x y : BitVec w} : carry w x y c = decide (x.toNat + y.toNat + c.toNat ≥ 2^w) := sorry

should make your life easy, to turn the goal about x.toNat + y.toNat into something about carry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is incredibly clean. I've implemented this now. The hint about using carry to prove this escaped me!

-/
theorem shiftLeft_or_eq_shiftLeft_shiftLeft_of_and_eq_zero {x : BitVec w} {y z : BitVec w₂}
bollu marked this conversation as resolved.
Show resolved Hide resolved
(h : y &&& z = 0#w₂) (h' : y.toNat + z.toNat < 2^w₂) :
x <<< (y ||| z) = x <<< y <<< z := by
simp [← add_eq_or_of_and_eq_zero _ _ h, shiftLeft_eq', shiftLeft_add,
toNat_add, Nat.mod_eq_of_lt h']

theorem getLsb_shiftLeft' {x : BitVec w} {y : BitVec w₂} {i : Nat} :
(x <<< y).getLsb i = (decide (i < w) && !decide (i < y.toNat) && x.getLsb (i - y.toNat)) := by
simp [shiftLeft_eq', getLsb_shiftLeft]

/--
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
-/
theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} (hn : n + 1 ≤ w₂) :
bollu marked this conversation as resolved.
Show resolved Hide resolved
shiftLeftRec x y n = x <<< (y.truncate (n + 1)).zeroExtend w₂ := by
induction n generalizing x y
case zero =>
ext i
simp only [shiftLeftRec_zero, twoPow_zero_eq_one, Nat.reduceAdd, truncate_one_eq_ofBool_getLsb]
have heq : (y &&& 1#w₂) = zeroExtend w₂ (ofBool (y.getLsb 0)) := by
ext i
by_cases h : (↑i : Nat) = 0 <;> simp [h, Bool.and_comm]
simp [heq]
case succ n ih =>
simp only [shiftLeftRec_succ, and_twoPow_eq_getLsb]
by_cases h : y.getLsb (n + 1)
· simp only [h, ↓reduceIte]
rw [ih (hn := by omega)]
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h]
rw [shiftLeft_or_eq_shiftLeft_shiftLeft_of_and_eq_zero]
· simp
· simp only [toNat_truncate, toNat_twoPow]
have hpow : 2 ^ (n + 1) < 2 ^ w₂ := by
apply Nat.pow_lt_pow_of_lt (by decide) (by omega)
have h₂ : 2 ^ (n + 1) % 2 ^ w₂ = 2 ^ (n + 1) := Nat.mod_eq_of_lt (by omega)
have h₁ : y.toNat % 2 ^ (n + 1) % 2 ^ w₂ = y.toNat % 2 ^ (n + 1) := by
apply Nat.mod_eq_of_lt
apply Nat.lt_of_lt_of_le (m := 2 ^ (n + 1))
apply Nat.mod_lt
apply Nat.pow_pos (by decide); omega
obtain h₁ : y.toNat % 2 ^ (n + 1) % 2 ^ w₂ = y.toNat % 2 ^ (n + 1) := by
apply Nat.mod_eq_of_lt
apply Nat.lt_of_lt_of_le (m := 2 ^ (n + 1)) <;> omega
rw [h₁, h₂]
rcases w₂ with rfl | w₂
· omega
· apply Nat.add_lt_add_of_lt_of_le
· simp only [pow_eq, Nat.mul_eq, Nat.mul_one]
apply Nat.lt_of_lt_of_le (m := 2 ^ (n + 1))
· apply Nat.mod_lt
· apply Nat.pow_pos (by decide)
· apply Nat.pow_le_pow_of_le_right (by decide) (by omega)
· simp only [pow_eq]
apply Nat.pow_le_pow_of_le_right (by decide) (by omega)
· simp only [h, false_eq_true, ↓reduceIte, shiftLeft_zero']
rw [ih (hn := by omega)]
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1)]
simp [h]

theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
x <<< y = shiftLeftRec x y (w₂ - 1) := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [shiftLeftRec_eq (x := x) (y := y) (n := w₂) (by omega)]

end BitVec
45 changes: 45 additions & 0 deletions src/Init/Data/BitVec/Lemmas.lean
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,12 @@ theorem zeroExtend_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
omega

/-- Truncating to width 1 produces a bitvector equal to the the LSB. -/
theorem truncate_one_eq_ofBool_getLsb {x : BitVec w} :
tobiasgrosser marked this conversation as resolved.
Show resolved Hide resolved
x.truncate 1 = ofBool (x.getLsb 0) := by
ext i
simp [show i = 0 by omega]

/-! ## extractLsb -/

@[simp]
Expand Down Expand Up @@ -626,6 +632,10 @@ theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
apply eq_of_toNat_eq
simp

@[simp]
theorem zero_shiftLeft_eq (n : Nat) : (0#w) <<< n = 0 := by
bollu marked this conversation as resolved.
Show resolved Hide resolved
simp [bv_toNat]

@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
rw [← testBit_toNat, getLsb]
Expand Down Expand Up @@ -691,6 +701,20 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
(x <<< n) <<< m = x <<< (n + m) := by
rw [shiftLeft_add]

/-! ### shiftLeft reductions from BitVec to Nat -/

theorem shiftLeft_eq' {x : BitVec w} {y : BitVec w₂} :
x <<< y = x <<< y.toNat := by rfl
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be a simp lemma, and then we never prove anything about shifting by a BitVec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I wasn't sure if it should be or not, so I left it as-is.


@[simp]
theorem shiftLeft_zero' {x : BitVec w} :
x <<< (0#w₂) = x := by
simp [shiftLeft_eq']

theorem shiftLeft_shiftLeft' {x y z : BitVec w} :
x <<< y <<< z = x <<< (y.toNat + z.toNat) := by
simp [shiftLeft_eq', shiftLeft_add]

/-! ### ushiftRight -/

@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
Expand Down Expand Up @@ -1471,6 +1495,27 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
apply Nat.pow_dvd_pow 2 (by omega)
simp [Nat.mul_mod, hpow]

@[simp]
theorem and_twoPow_eq_getLsb {x : BitVec w} {i : Nat} :
bollu marked this conversation as resolved.
Show resolved Hide resolved
x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w := by
ext j
simp only [getLsb_and, getLsb_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsb i <;> simp_all

theorem BitVec.toNat_twoPow {w : Nat} {i : Nat} : (twoPow w i).toNat = 2^i % 2^w := by
bollu marked this conversation as resolved.
Show resolved Hide resolved
rcases w with rfl | w
· simp [Nat.mod_one]
· simp [twoPow, toNat_shiftLeft]
have hone : 1 < 2 ^ (w + 1) := by
rw [show 1 = 2^0 by simp[Nat.pow_zero]]
exact Nat.pow_lt_pow_of_lt (by omega) (by omega)
simp [Nat.mod_eq_of_lt hone, Nat.shiftLeft_eq]

@[simp]
theorem twoPow_zero_eq_one {w : Nat} : twoPow w 0 = 1#w := by
apply eq_of_toNat_eq
simp

/- ### zeroExtend, truncate, and bitwise operations -/

/--
Expand Down
Loading