Skip to content

Commit 7628345

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 0f31605 commit 7628345

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
@@ -544,14 +544,15 @@ let current_unit () = Domain.DLS.get current_unit_key
544544
(* The signature of a persistent structure is a copy of the one in its .cmi
545545
file whose bound identifiers are renamed ([Subst.signature Subst.identity]).
546546
The copy is often only asked for to learn that a compilation unit is not a
547-
module alias ([normalize_path]), so it is not made then: the stamps and type
548-
ids it would have taken are reserved, so every later stamp and id is the
549-
same as if it had been made, and the copy is made with exactly those stamps
550-
and ids when the signature is first read.
551-
552-
The number of stamps and ids a copy takes is learnt from the first copy of
553-
the same .cmi contents (keyed by its CRC) and kept for the whole process.
554-
It only depends on the signature graph, which the type checker does not
547+
module alias ([normalize_path]). When this process has already copied the
548+
same .cmi contents (same CRC), e.g. in an earlier request of the same
549+
build, the copy is not made then: the stamps and type ids it would take are
550+
reserved, so every later stamp and id is the same as if it had been made,
551+
and the copy is made with exactly those stamps and ids when the signature
552+
is first read. Otherwise (the first copy of an interface, or a one-shot
553+
bsc) it is made at once, as before, and its counts are kept.
554+
555+
The number of stamps and ids a copy takes only depends on the signature graph, which the type checker does not
555556
change before copying it: only copies of it are given to the rest of the
556557
type checker. If a reserved copy ever takes a different number of stamps or
557558
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
@@ -166,13 +166,13 @@ val get_unit_name : unit -> string
166166
(* Read, save a signature to/from a file *)
167167

168168
val read_signature : string -> string -> signature
169+
(* Arguments: module name, file name. Results: signature. *)
169170

170171
val deferred_signature_copy_stats : unit -> int * int
171172
(* For tests: how many copies of compilation units' signatures were deferred,
172173
and how many of those did not take the stamps and type ids reserved for
173174
them and were made again with fresh ones (expected: none), in the process
174175
so far. *)
175-
(* Arguments: module name, file name. Results: signature. *)
176176

177177
val save_signature :
178178
?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)