Skip to content

Commit 70260fc

Browse files
committed
feat: Confluent simp-set for left shift by BitVec.twoPow
This PR adds `@[simp]` theorems to confluently rewrite shifting left by `twoPow w i` (when `i` is constant).
1 parent 6278839 commit 70260fc

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

src/Init/Data/BitVec/Lemmas.lean

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,22 @@ theorem twoPow_and (x : BitVec w) (i : Nat) :
34013401
(twoPow w i) &&& x = if x.getLsbD i then twoPow w i else 0#w := by
34023402
rw [BitVec.and_comm, and_twoPow]
34033403

3404+
/-!
3405+
NOTE(confluence of multiplication by twoPow):
3406+
The three rewrite rules:
3407+
- mul_twoPow_eq_shiftLeft: x * twoPow w i = x << i
3408+
- twoPow_mul_eq_shiftLeft: twoPow w i * x = x << i
3409+
- twoPow_shiftLeft_eq_twoPow_add: `twoPow w i << j = twoPow w (i + j)`
3410+
3411+
ensure confluence in the case of `twoPow w i * twoPow w j`, which can get rewritten into:
3412+
- `twoPow w i * twoPow w j → twoPow w i << j → twoPow w (i + j)
3413+
- `twoPow w i * twoPow w j → twoPow w j << i → twoPow w (j + i)
3414+
- `twoPow w i * twoPow w j → twoPow w (i + j)
3415+
3416+
which produce the same right hand side, upto a commutation of `i + j`,
3417+
which is acceptable for constant folding.
3418+
-/
3419+
34043420
@[simp]
34053421
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
34063422
x * (twoPow w i) = x <<< i := by
@@ -3414,6 +3430,7 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
34143430
apply Nat.pow_dvd_pow 2 (by omega)
34153431
simp [Nat.mul_mod, hpow]
34163432

3433+
@[simp]
34173434
theorem twoPow_mul_eq_shiftLeft (x : BitVec w) (i : Nat) :
34183435
(twoPow w i) * x = x <<< i := by
34193436
rw [BitVec.mul_comm, mul_twoPow_eq_shiftLeft]
@@ -3429,11 +3446,15 @@ theorem shiftLeft_eq_mul_twoPow (x : BitVec w) (n : Nat) :
34293446
simp [getLsbD_shiftLeft, Fin.is_lt, decide_true, Bool.true_and, mul_twoPow_eq_shiftLeft]
34303447

34313448
/-- 2^i * 2^j = 2^(i + j) with bitvectors as well -/
3432-
theorem twoPow_mul_twoPow_eq {w : Nat} (i j : Nat) : twoPow w i * twoPow w j = twoPow w (i + j) := by
3449+
theorem twoPow_mul_twoPow_eq {w : Nat} (i j : Nat) : twoPow w i * twoPow w j = twoPow w (i + j) := by
34333450
apply BitVec.eq_of_toNat_eq
34343451
simp only [toNat_mul, toNat_twoPow]
34353452
rw [← Nat.mul_mod, Nat.pow_add]
34363453

3454+
@[simp]
3455+
theorem twoPow_shiftLeft_eq_twoPow_add {w i j : Nat} : twoPow w i <<< j = twoPow w (i + j) := by
3456+
rw [shiftLeft_eq_mul_twoPow, twoPow_mul_twoPow_eq]
3457+
34373458
/--
34383459
The unsigned division of `x` by `2^k` equals shifting `x` right by `k`,
34393460
when `k` is less than the bitwidth `w`.

tests/lean/run/bitvec_simproc.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,12 @@ example (h : False) : 1#2 = 2#2 := by
141141
simp; guard_target =ₛ False; exact h
142142
example : 1#22#2 := by
143143
simp
144+
/-! Check that multiplication by twoPow and <<< rewrites to the <<< normal form. -/
145+
example : twoPow 32 3 * twoPow 32 4 = twoPow 32 7 := by
146+
simp
147+
example (x : BitVec 32) : x * twoPow 32 4 = x <<< 4 := by
148+
simp
149+
example (x : BitVec 32) : twoPow 32 4 * x = x <<< 4 := by
150+
simp
151+
example (x : BitVec 32) : ((twoPow 32 4 * x) <<< 3) * twoPow 32 3 = x <<< 10 := by
152+
simp

0 commit comments

Comments
 (0)