package coq-lsp

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file memo.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
module CS = Stats

module Stats = struct
  type 'a t =
    { res : 'a
    ; cache_hit : bool
    ; memory : int
    ; time : float
    }

  let make ?(cache_hit = false) ~time res =
    (* This is quite slow! *)
    (* let memory = Obj.magic res |> Obj.reachable_words in *)
    let memory = 0 in
    { res; cache_hit; memory; time }
end

module CacheStats = struct
  let nhit, ntotal = (ref 0, ref 0)

  let reset () =
    nhit := 0;
    ntotal := 0

  let hit () =
    incr nhit;
    incr ntotal

  let miss () = incr ntotal

  let stats () =
    if !ntotal = 0 then "no stats"
    else
      let hit_rate =
        Stdlib.Float.of_int !nhit /. Stdlib.Float.of_int !ntotal *. 100.0
      in
      Format.asprintf "cache hit rate: %3.2f" hit_rate
end

module Interp = struct
  (* Loc-independent command evalution and caching. *)
  module VernacInput = struct
    type t = Coq.State.t * Coq.Ast.t

    (* This crutially relies on our ppx to ignore the CAst location *)
    let equal (st1, v1) (st2, v2) =
      if Coq.Ast.compare v1 v2 = 0 then
        if Coq.State.compare st1 st2 = 0 then true else false
      else false

    let hash (st, v) = Hashtbl.hash (Coq.Ast.hash v, st)
  end

  type t = VernacInput.t

  let input_info (st, v) =
    Format.asprintf "stm: %d | st %d" (Coq.Ast.hash v) (Hashtbl.hash st)

  module HC = Hashtbl.Make (VernacInput)

  module Result = struct
    (* We store the location as to compute an offset for cached results *)
    type t = Loc.t * Coq.State.t Coq.Interp.interp_result
  end

  type cache = Result.t HC.t

  let cache : cache ref = ref (HC.create 1000)

  (* This is very expensive *)
  let size () = Obj.reachable_words (Obj.magic cache)

  let in_cache st stm =
    let kind = CS.Kind.Hashing in
    CS.record ~kind ~f:(HC.find_opt !cache) (st, stm)

  (* XXX: Move elsewhere *)
  let loc_offset (l1 : Loc.t) (l2 : Loc.t) =
    let line_offset = l2.line_nb - l1.line_nb in
    let bol_offset = l2.bol_pos - l1.bol_pos in
    let line_last_offset = l2.line_nb_last - l1.line_nb_last in
    let bol_last_offset = l2.bol_pos_last - l1.bol_pos_last in
    let bp_offset = l2.bp - l1.bp in
    let ep_offset = l2.ep - l1.ep in
    ( line_offset
    , bol_offset
    , line_last_offset
    , bol_last_offset
    , bp_offset
    , ep_offset )

  let loc_apply_offset
      ( line_offset
      , bol_offset
      , line_last_offset
      , bol_last_offset
      , bp_offset
      , ep_offset ) (loc : Loc.t) =
    { loc with
      line_nb = loc.line_nb + line_offset
    ; bol_pos = loc.bol_pos + bol_offset
    ; line_nb_last = loc.line_nb_last + line_last_offset
    ; bol_pos_last = loc.bol_pos_last + bol_last_offset
    ; bp = loc.bp + bp_offset
    ; ep = loc.ep + ep_offset
    }

  let adjust_offset ~stm_loc ~cached_loc res =
    let offset = loc_offset cached_loc stm_loc in
    let f = loc_apply_offset offset in
    Coq.Protect.E.map_loc ~f res

  let eval (st, stm) : _ Stats.t =
    let stm_loc = Coq.Ast.loc stm |> Option.get in
    match in_cache st stm with
    | Some (cached_loc, res), time ->
      if Debug.cache then Io.Log.trace "memo" "cache hit";
      CacheStats.hit ();
      let res = adjust_offset ~stm_loc ~cached_loc res in
      Stats.make ~cache_hit:true ~time res
    | None, time_hash -> (
      if Debug.cache then Io.Log.trace "memo" "cache miss";
      CacheStats.miss ();
      let kind = CS.Kind.Exec in
      let res, time_interp = CS.record ~kind ~f:(Coq.Interp.interp ~st) stm in
      let time = time_hash +. time_interp in
      match res.r with
      | Coq.Protect.R.Interrupted ->
        (* Don't cache interruptions *)
        Stats.make ~time res
      | Coq.Protect.R.Completed _ ->
        let () = HC.add !cache (st, stm) (stm_loc, res) in
        let time = time_hash +. time_interp in
        Stats.make ~time res)
end

module Admit = struct
  type t = Coq.State.t

  module C = Hashtbl.Make (Coq.State)

  let cache = C.create 1000

  let eval v =
    match C.find_opt cache v with
    | None ->
      let admitted_st = Coq.State.admit ~st:v in
      C.add cache v admitted_st;
      admitted_st
    | Some admitted_st -> admitted_st
end

module Init = struct
  module S = struct
    type t = Coq.State.t * Coq.Workspace.t * Lang.LUri.File.t

    let equal (s1, w1, u1) (s2, w2, u2) : bool =
      if Lang.LUri.File.compare u1 u2 = 0 then
        if Coq.Workspace.compare w1 w2 = 0 then
          if Coq.State.compare s1 s2 = 0 then true else false
        else false
      else false

    let hash (st, w, uri) =
      Hashtbl.hash
        (Coq.State.hash st, Coq.Workspace.hash w, Lang.LUri.File.hash uri)
  end

  type t = S.t

  module C = Hashtbl.Make (S)

  let cache = C.create 1000

  let eval v =
    match C.find_opt cache v with
    | None ->
      let root_state, workspace, uri = v in
      let admitted_st = Coq.Init.doc_init ~root_state ~workspace ~uri in
      C.add cache v admitted_st;
      admitted_st
    | Some res -> res
end
OCaml

Innovation. Community. Security.