Skip to content

Commit c0eb89c

Browse files
committed
Test the counter restore and the fallback of deferred signature copies
The independent review found the env.mli comment for read_signature attached to the new stats function, and the comment and changelog overstating when copies are deferred: only after an earlier copy of the same interface in the process, never for the first one or a one-shot bsc. New unit tests check that Ident.with_current_time puts the counter back when its function raises, and that a deferred copy whose counts are wrong (a forged .cmi CRC) is made again with fresh stamps. Signed-off-by: Jono Prest <jjprest@gmail.com>
1 parent 5f84566 commit c0eb89c

4 files changed

Lines changed: 111 additions & 10 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
- Speed up type checking by copying a dependency's interface only when it is read, not when the compiler only checks whether it is a module alias.
1+
- Speed up type checking in builds: after the first copy of a dependency's interface, later requests only copy it when they read it, not when they only check whether it is a module alias.

‎compiler/ml/env.ml‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -558,14 +558,15 @@ let current_unit () = Domain.DLS.get current_unit_key
558558
(* The signature of a persistent structure is a copy of the one in its .cmi
559559
file whose bound identifiers are renamed ([Subst.signature Subst.identity]).
560560
The copy is often only asked for to learn that a compilation unit is not a
561-
module alias ([normalize_path]), so it is not made then: the stamps and type
562-
ids it would have taken are reserved, so every later stamp and id is the
563-
same as if it had been made, and the copy is made with exactly those stamps
564-
and ids when the signature is first read.
565-
566-
The number of stamps and ids a copy takes is learnt from the first copy of
567-
the same .cmi contents (keyed by its CRC) and kept for the whole process.
568-
It only depends on the signature graph, which the type checker does not
561+
module alias ([normalize_path]). When this process has already copied the
562+
same .cmi contents (same CRC), e.g. in an earlier request of the same
563+
build, the copy is not made then: the stamps and type ids it would take are
564+
reserved, so every later stamp and id is the same as if it had been made,
565+
and the copy is made with exactly those stamps and ids when the signature
566+
is first read. Otherwise (the first copy of an interface, or a one-shot
567+
bsc) it is made at once, as before, and its counts are kept.
568+
569+
The number of stamps and ids a copy takes only depends on the signature graph, which the type checker does not
569570
change before copying it: only copies of it are given to the rest of the
570571
type checker. If a reserved copy ever takes a different number of stamps or
571572
ids, it is thrown away and made again with fresh ones. *)

‎compiler/ml/env.mli‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,13 @@ val get_unit_name : unit -> string
178178
(* Read, save a signature to/from a file *)
179179

180180
val read_signature : string -> string -> signature
181+
(* Arguments: module name, file name. Results: signature. *)
181182

182183
val deferred_signature_copy_stats : unit -> int * int
183184
(* For tests: how many copies of compilation units' signatures were deferred,
184185
and how many of those did not take the stamps and type ids reserved for
185186
them and were made again with fresh ones (expected: none), in the process
186187
so far. *)
187-
(* Arguments: module name, file name. Results: signature. *)
188188

189189
val save_signature :
190190
?check_exists:unit ->

‎tests/ounit_tests/ounit_deferred_signature_tests.ml‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,109 @@ let unread_copy_tests _ =
101101
let eager = ask_only ~eager:true in
102102
assert_equal eager (ask_only ~eager:false)
103103

104+
(* [Ident.with_current_time] puts the counter back also when its function
105+
raises. *)
106+
let with_current_time_restores_on_exception _ =
107+
Ident.with_fresh (fun () ->
108+
let before = Ident.current_time () in
109+
let inside = ref 0 in
110+
(match
111+
Ident.with_current_time (before + 100) (fun () ->
112+
inside := Ident.binding_time (Ident.create "inside");
113+
failwith "raised inside")
114+
with
115+
| _ -> assert_failure "the function should have raised"
116+
| exception Failure _ -> ());
117+
assert_equal ~printer:string_of_int (before + 101) !inside;
118+
assert_equal ~printer:string_of_int ~msg:"counter restored after raise"
119+
before (Ident.current_time ());
120+
let (), final =
121+
Ident.with_current_time (before + 7) (fun () ->
122+
ignore (Ident.create "a");
123+
ignore (Ident.create "b"))
124+
in
125+
assert_equal ~printer:string_of_int (before + 9) final;
126+
assert_equal ~printer:string_of_int ~msg:"counter restored after return"
127+
before (Ident.current_time ()))
128+
129+
(* A compilation unit [Forged] whose .cmi claims the CRC of different contents:
130+
the counts learnt from the first contents are wrong for the second, so its
131+
deferred copy takes more stamps and ids than were reserved, and is made
132+
again with fresh ones. *)
133+
let value name =
134+
Types.Sig_value
135+
( Ident.create name,
136+
{
137+
val_type = Predef.type_int ();
138+
val_kind = Val_reg;
139+
val_loc = Location.none;
140+
val_attributes = [];
141+
} )
142+
143+
let forged_crc = Digest.string "ounit_deferred_signature_tests forged crc"
144+
145+
let with_forged_unit sign action =
146+
let cmi =
147+
{
148+
Cmi_format.cmi_name = "Forged";
149+
cmi_sign = sign;
150+
cmi_crcs = [("Forged", Some forged_crc)];
151+
cmi_flags = [];
152+
}
153+
in
154+
let saved_load = !Env.Persistent_signature.load in
155+
(Env.Persistent_signature.load :=
156+
fun ~unit_name ->
157+
if unit_name = "Forged" then
158+
Some {Env.Persistent_signature.filename = "Forged.cmi"; cmi}
159+
else saved_load ~unit_name);
160+
Fun.protect
161+
~finally:(fun () -> Env.Persistent_signature.load := saved_load)
162+
(fun () ->
163+
Compiler_request_state.with_fresh (fun () ->
164+
Request_local.with_fresh (fun () -> Ident.with_fresh action)))
165+
166+
let count_mismatch_fallback_tests _ =
167+
let path = unit_path "Forged" in
168+
(* Learn the counts of a one-value signature. *)
169+
with_forged_unit
170+
[value "x"]
171+
(fun () -> ignore (Env.find_module path Env.empty));
172+
let reserved_before, fallbacks_before =
173+
Env.deferred_signature_copy_stats ()
174+
in
175+
let stamps, probe_stamp =
176+
with_forged_unit
177+
[value "x"; value "y"]
178+
(fun () ->
179+
ignore (Env.normalize_path None Env.empty path);
180+
let probe_stamp = Ident.binding_time (Ident.create "probe") in
181+
match (Env.find_module path Env.empty).md_type with
182+
| Mty_signature items ->
183+
( List.map
184+
(function
185+
| Types.Sig_value (id, _) -> Ident.binding_time id
186+
| _ -> assert_failure "expected values")
187+
items,
188+
probe_stamp )
189+
| _ -> assert_failure "expected a signature")
190+
in
191+
let reserved_after, fallbacks_after = Env.deferred_signature_copy_stats () in
192+
assert_equal ~printer:string_of_int ~msg:"the copy was deferred"
193+
(reserved_before + 1) reserved_after;
194+
assert_equal ~printer:string_of_int ~msg:"the copy fell back"
195+
(fallbacks_before + 1) fallbacks_after;
196+
assert_bool "the fallback copy has both values with fresh stamps"
197+
(List.length stamps = 2
198+
&& List.for_all (fun stamp -> stamp > probe_stamp) stamps)
199+
104200
let suites =
105201
__FILE__
106202
>::: [
107203
"deferred_copy_matches_eager_copy" >:: deferred_copy_tests;
108204
"unread_copy_reserves_stamps_and_ids" >:: unread_copy_tests;
205+
"with_current_time_restores_on_exception"
206+
>:: with_current_time_restores_on_exception;
207+
"count_mismatch_falls_back_to_fresh_ids"
208+
>:: count_mismatch_fallback_tests;
109209
]

0 commit comments

Comments
 (0)