Skip to content

Commit 14bd9b3

Browse files
committed
Memoize level computation to avoid exponential behavior
Signed-off-by: Damien Doligez <damien.doligez@inria.fr>
1 parent 41d03d4 commit 14bd9b3

7 files changed

Lines changed: 66 additions & 15 deletions

File tree

src/expr.mli

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,15 +875,16 @@ module Levels: sig
875875
val kind_to_level:
876876
kind -> int
877877

878-
class virtual ['s] level_computation:
879-
['s] Visit.map
880878
class virtual ['s] _rm_expr_level:
881879
['s] Visit.map
882880

883881
val compute_level:
884882
ctx -> expr -> expr
885883
val rm_expr_level:
886884
ctx -> expr -> expr
885+
886+
val newcache:
887+
expr -> expr
887888
end
888889

889890

src/expr/e_anon.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ end
304304

305305
class anon = object (self : 'self)
306306
inherit anon_sg as super
307-
method expr scx e = Elab.desugar self#expr super#expr scx e
307+
method expr scx e =
308+
E_levels.newcache (Elab.desugar self#expr super#expr scx e)
308309
end
309310

310311

src/expr/e_levels.ml

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ type level_info = (* Stores information for computing the
5757

5858
let exprlevel : level_info pfuncs =
5959
Property.make "Expr.Levels.exprlevel"
60+
type elcache =
61+
| ELCache_full of hyp Deque.dq * expr_ * level_info
62+
| ELCache_empty
63+
let exprlevel_cache : elcache ref pfuncs =
64+
Property.make "Expr.Levels.exprlevel_cache"
65+
6066
let assert_is_level (level: int) =
6167
assert ((level >= 0) && (level <= 3))
6268
(* The value 4 signifies that the notion of level is undefined for
@@ -124,6 +130,23 @@ let kind_to_level (kind: kind): int =
124130
| Temporal -> 3
125131
| Unknown -> assert false
126132

133+
let has_cached_level cx e =
134+
match Property.get e exprlevel_cache with
135+
| exception Not_found -> false
136+
| {contents = ELCache_full (cx2, ecore, l)}
137+
when ecore == e.core && Deque.equal (==) cx2 cx -> true
138+
| _ -> false
139+
140+
let get_cached_level e =
141+
match Property.get e exprlevel_cache with
142+
| {contents = ELCache_full (_, _, l)} -> l
143+
| _ -> assert false
144+
| exception Not_found -> assert false
145+
146+
let set_cached_level e cx l =
147+
match Property.get e exprlevel_cache with
148+
| r -> r := ELCache_full (cx, e.core, l)
149+
| exception Not_found -> ()
127150

128151
(*
129152
The level property is stored in the syntax tree nodes.
@@ -142,8 +165,15 @@ class virtual ['s] level_computation = object (self : 'self)
142165
method expr ((_, cx) as scx) e =
143166
if (has_level e) then
144167
e
168+
else if has_cached_level cx e then
169+
assign e exprlevel (get_cached_level e)
145170
else
146171
begin
172+
let assign e tag l =
173+
assert (tag == exprlevel);
174+
set_cached_level e cx l;
175+
assign e tag l
176+
in
147177
let max_args_level scx args = (
148178
let es_ = List.map (self#expr scx) args in
149179
let es_levels = List.map get_level es_ in
@@ -402,7 +432,7 @@ class virtual ['s] level_computation = object (self : 'self)
402432
assign e exprlevel level_info
403433
| Bang (e, sels) -> assert false (* subexpression references are
404434
expanded before expanding `ENABLED` and `\cdot`. *)
405-
| Lambda (vs, e) ->
435+
| Lambda (vs, e2) ->
406436
(* Note: 2nd-order operators (signified via `shp` below) are
407437
handled by the declarations in `e_scx` below and the call to
408438
the method `self#hyp` in the pattern case `Apply (op, es)` below.
@@ -415,7 +445,7 @@ class virtual ['s] level_computation = object (self : 'self)
415445
@@ v)
416446
vs) in
417447
(* expression `e` *)
418-
let e_ = self#expr e_scx e in
448+
let e_ = self#expr e_scx e2 in
419449
let e_level = get_level e_ in
420450
(*
421451
if e_level > 2 then
@@ -962,7 +992,7 @@ class virtual ['s] level_computation = object (self : 'self)
962992
*)
963993

964994
method hyp scx h =
965-
let level_info = begin match h.core with
995+
let level_info h = begin match h.core with
966996
(* declared variable *)
967997
| Flex _ -> make_level_info 1
968998
(* declared operator of any arity *)
@@ -1003,18 +1033,14 @@ class virtual ['s] level_computation = object (self : 'self)
10031033
assert false (* not implemented *)
10041034
(* defined operator of any arity *)
10051035
| Defn (df, _, _, _) ->
1006-
let df_ = self#defn scx df in
1007-
get_level_info df_
1036+
get_level_info df
10081037
| Fact (e, _, _) ->
1009-
let e_ = self#expr scx e in
1010-
begin
1011-
if (has_level e_) then
1012-
get_level_info e_
1038+
if (has_level e) then
1039+
get_level_info e
10131040
else
10141041
(* For example, the notion of expression level is
10151042
undefined for theorems. *)
10161043
make_undefined_level_info
1017-
end
10181044
end in
10191045
(* Hidden definitions need to be visited to
10201046
have level information available for computing the
@@ -1041,11 +1067,12 @@ class virtual ['s] level_computation = object (self : 'self)
10411067
let h = Fact (self#expr scx e, vis, tm) @@ h in
10421068
(adj scx h, h)
10431069
in
1044-
let e_ = assign e_ exprlevel level_info in
1070+
let e_ = assign e_ exprlevel (level_info e_) in
10451071
(scx_, e_)
10461072

10471073
end
10481074

1075+
let newcache e = Property.assign e exprlevel_cache (ref ELCache_empty)
10491076

10501077
let compute_level cx expr =
10511078
let visitor = object

src/expr/e_levels.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ val assert_has_correct_level: expr -> unit
2222
val kind_to_level: kind -> int
2323

2424

25-
class virtual ['s] level_computation : ['s] E_visit.map
2625
class virtual ['s] _rm_expr_level : ['s] E_visit.map_visible_hyp
2726

2827
val compute_level: ctx -> expr -> expr
2928
val rm_expr_level: ctx -> expr -> expr
29+
30+
val newcache: expr -> expr

src/expr/e_tla_norm.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ open Property
1010
open E_t
1111
module B = Builtin
1212

13+
let ( @@ ) core e = E_levels.newcache (core @@ e)
14+
1315
let rec tuple_flatten e = match e.core with
1416
| Tuple es ->
1517
List.concat (List.map tuple_flatten es)

src/util/deque.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,20 @@ let alter ?(backwards=false) q n alt_fn =
206206
end
207207
in
208208
spin 0 q
209+
210+
(* Compare deques `q1` and `q2`, using `cmp` to compare their elements. *)
211+
let equal cmp q1 q2 =
212+
let rec diff l1 l2 =
213+
match l1, l2 with
214+
| x1 :: ll1, x2 :: ll2 when cmp x1 x2 -> diff ll1 ll2
215+
| [], _ -> Some l2
216+
| _, [] -> Some l1
217+
| _ -> None
218+
in
219+
q1.flen + q1.rlen = q2.flen + q2.rlen && begin
220+
let f = diff q1.front q2.front in
221+
let r = diff q1.rear q2.rear in
222+
match f, r with
223+
| Some lf, Some lr -> List.for_all2 cmp lf (List.rev lr)
224+
| _, _ -> false
225+
end

src/util/deque.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ val prepend_list : 'a list -> 'a dq -> 'a dq
3333

3434
val of_list : 'a list -> 'a dq
3535
val to_list : 'a dq -> 'a list
36+
37+
val equal : ('a -> 'a -> bool) -> 'a dq -> 'a dq -> bool

0 commit comments

Comments
 (0)