This PR improves bv_decide's performance in the presence of large
literals.
The core change of this PR is the reformulation of the reflection code
for literals to:
```diff
def eval (assign : Assignment) : BVExpr w → BitVec w
| .var idx =>
- let ⟨bv⟩ := assign.get idx
- bv.truncate w
+ let packedBv := assign.get idx
+ /-
+ This formulation improves performance, as in a well formed expression the condition always holds
+ so there is no need for the more involved `BitVec.truncate` logic.
+ -/
+ if h : packedBv.w = w then
+ h ▸ packedBv.bv
+ else
+ packedBv.bv.truncate w
```
The remainder is merely further simplifications that make the terms
smaller and easier to deal with in general. This change is motivated by
applying the following diff to the kernel:
```diff
diff --git a/src/kernel/type_checker.cpp b/src/kernel/type_checker.cpp
index b0e6844dca..f13bb96bd4 100644
--- a/src/kernel/type_checker.cpp
+++ b/src/kernel/type_checker.cpp
@@ -518,6 +518,7 @@ optional<constant_info> type_checker::is_delta(expr const & e) const {
optional<expr> type_checker::unfold_definition_core(expr const & e) {
if (is_constant(e)) {
if (auto d = is_delta(e)) {
+// std::cout << "Working on unfolding: " << d->get_name() << std::endl;
if (length(const_levels(e)) == d->get_num_lparams()) {
if (m_diag) {
m_diag->record_unfold(d->get_name());
```
and observing that in the test case from #6043 we see a long series of
```
Working on unfolding: Bool.decEq
Working on unfolding: Bool.decEq.match_1
Working on unfolding: Bool.casesOn
Working on unfolding: Nat.ble
Working on unfolding: Nat.brecOn
Working on unfolding: Nat.beq.match_1
Working on unfolding: Nat.casesOn
Working on unfolding: Nat.casesOn
Working on unfolding: Nat.beq.match_1
Working on unfolding: Nat.casesOn
Working on unfolding: Nat.casesOn
```
the chain begins with `BitVec.truncate`, works through a few
abstractions and then continues like above forever, so I avoid the call
to truncate like this. It is not quite clear to me why removing `ofBool`
helps so much here, maybe some other kernel heuristic kicks in to rescue
us.
Either way this diff is a general improvement for reflection of `BitVec`
constants as we should never have to run `BitVec.truncate` again!
Fixes: #6043
30 lines
1 KiB
Text
30 lines
1 KiB
Text
import Std.Tactic.BVDecide
|
||
|
||
open BitVec
|
||
|
||
theorem substructure_unit_1 (x y z : BitVec 8) : ((x = y) ∧ (y = z)) ↔ ¬(¬(x =y) ∨ (¬(y = z))) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_1' (x y z : BitVec 8) : ((x = y) && (y = z)) ↔ ¬(¬(x = y) || (!(y = z))) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_1'' (x y z : BitVec 8) : (Bool.and (x = y) (y = z)) ↔ ¬(Bool.or (!(x = y)) (Bool.not (y = z))) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_2 (x y : BitVec 8) : x = y → y = x := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_3 (x y : BitVec 8) : (x = y) ^^ (y ≠ x) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_3' (x y : BitVec 8) : Bool.xor (x = y) (y ≠ x) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_4 (a b : Bool) : (a && b) = (b && a) := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_5 (x : BitVec 32) : (if x.getLsbD 0 then !x.getLsbD 0 else x.getLsbD 0) = false := by
|
||
bv_decide
|
||
|
||
theorem substructure_unit_6 (x y : BitVec 32) (h : x.getLsbD 0 = false) : (if x.getLsbD 0 then x else y) = y := by
|
||
bv_decide
|