-
Notifications
You must be signed in to change notification settings - Fork 1
/
autocorres.ML
728 lines (627 loc) · 29.9 KB
/
autocorres.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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*)
(*
* The top-level "autocorres" command.
*)
structure AutoCorres =
struct
(*
* Option parsing for the autocorres command.
* These options are documented in the README.md.
*)
(*
* Most fields are wrapped in option so that the parser can work out
* whether they have been specified already.
*
* Additionally, everything is a reference as a hack around the fact
* that SML doesn't have field-updater syntax. There are other ways to
* work around this, but this is a light-weight solution.
*)
type autocorres_options = {
(* Do not lift heaps for these functions. *)
no_heap_abs : string list option ref,
(* Insist the the following functions should be lifted, even if our
* heuristics claim it won't succeed. *)
force_heap_abs : string list option ref,
(* Skip heap lifting for the whole program. *)
skip_heap_abs : bool option ref,
(* Enable unsigned word abstraction for these functions. *)
unsigned_word_abs : string list option ref,
(* Disable signed word abstraction for these functions. *)
no_signed_word_abs : string list option ref,
(* Skip word abstraction for the whole program. *)
skip_word_abs : bool option ref,
(* Only lift to these monads. *)
ts_rules : string list option ref,
(* Force functions to be lifted to certain monads.
The symtab is keyed on function name. *)
ts_force : string Symtab.table ref,
(* Create some funky syntax for heap operations. *)
heap_abs_syntax: bool option ref,
(* Only translate a subset of functions. *)
scope: string list option ref,
scope_depth: int option ref,
(* Do the translation in this locale (and use functions in this locale) *)
c_locale: string option ref,
(* Generate SIMPL wrappers that do not assert termination for the SIMPL.
* Also generates ac_corres proofs minus the termination flag.
* This option is for temporary CRefine compatibility. *)
no_c_termination: bool option ref,
(* Store detailed traces for conversions of the selected functions. *)
trace_heap_lift : string list option ref,
trace_word_abs : string list option ref,
(* Disable L1Peephole, L2Peephole and L2Opt rules. *)
no_opt : bool option ref,
(* Trace simplification rules. Note that some simplification is performed even with no_opt set. *)
trace_opt : bool option ref,
(* Define word{8,16,32,64} heaps even if the program does not use them. *)
gen_word_heaps : bool option ref,
keep_going : bool option ref,
(* Change generated names for lifted_globals fields *)
lifted_globals_field_prefix : string option ref,
lifted_globals_field_suffix : string option ref,
(* Change generated function names *)
function_name_prefix : string option ref,
function_name_suffix : string option ref
}
(* Get all that the given function depends on, up to "depth" functions deep. *)
fun get_function_deps get_callees roots depth =
let
fun get_calleess fns = Symset.union_sets (fns :: map get_callees (Symset.dest fns))
in
funpow depth get_calleess (Symset.make roots)
end
(* Convert the given reference from "NONE" to "SOME x", emitting an
* error if the value is already non-NONE. *)
fun none_to_some ref_field new_value error_msg opt =
case !(ref_field opt) of
NONE => ((ref_field opt) := SOME new_value; opt)
| SOME _ => error error_msg
(* Parsing expectations. *)
fun expect x y = !! (K (K ("autocorres: expected " ^ y ^ " after " ^ x)))
(* Generic parser for "NAME = THING" *)
fun named_option parser name elem_desc=
Parse.reserved name |--
expect (quote name) "\"=\"" (Parse.$$$ "=" |--
expect "\"=\"" elem_desc parser)
(* Generic parser for "NAME = STRING ..." *)
val named_opt = named_option (Scan.repeat Parse.text)
(* Generic parser for "NAME = <nat>" *)
val nat_opt = named_option Parse.nat
(* Valid options. *)
val no_heap_abs_parser =
named_opt "no_heap_abs" "function names" >>
(fn funcs => none_to_some (#no_heap_abs) funcs "autocorres: no_heap_abs option specified multiple times")
val force_heap_abs_parser =
named_opt "force_heap_abs" "function names" >>
(fn funcs => none_to_some (#force_heap_abs) funcs "autocorres: force_heap_abs option specified multiple times")
val skip_heap_abs_parser =
Parse.reserved "skip_heap_abs" >>
(fn _ => none_to_some (#skip_heap_abs) true "autocorres: skip_heap_abs option specified multiple times")
val ts_rules_parser =
named_opt "ts_rules" "rule names" >>
(fn rules => none_to_some (#ts_rules) rules "autocorres: ts_rules option specified multiple times")
val scope_parser =
named_opt "scope" "function names" >>
(fn funcs => none_to_some (#scope) funcs "autocorres: scope option specified multiple times")
val scope_depth_parser =
nat_opt "scope_depth" "integer" >>
(fn value => none_to_some (#scope_depth) value "autocorres: scope option specified multiple times")
val c_locale_parser =
named_option Parse.text "c_locale" "locale name" >>
(fn funcs => none_to_some (#c_locale) funcs
"autocorres: c_locale option specified multiple times")
val no_c_termination_parser =
Parse.reserved "no_c_termination" >>
(fn _ => none_to_some (#no_c_termination) true "autocorres: no_c_termination option specified multiple times")
val ts_force_parser =
((Parse.reserved "ts_force" |--
expect "\"ts_force\"" "rule name"
(Parse.text :-- (fn name => expect name "\"=\"" (Parse.$$$ "="))) --
Scan.repeat Parse.text)) >>
(fn ((rule, _), funcs) => fn opt =>
let
val _ =
(#ts_force opt) :=
(fold (fn func => (fn table =>
Symtab.update_new (func, rule) table
handle Symtab.DUP _ =>
error ("autocorres: function " ^ quote func
^ " is already being forced to a particular type.")
)) funcs (!(#ts_force opt)))
in
opt
end)
val unsigned_word_abs_parser =
named_opt "unsigned_word_abs" "function names" >>
(fn funcs => none_to_some (#unsigned_word_abs) funcs "autocorres: unsigned_word_abs option specified multiple times")
val no_signed_word_abs_parser =
named_opt "no_signed_word_abs" "function names" >>
(fn funcs => none_to_some (#no_signed_word_abs) funcs "autocorres: no_signed_word_abs option specified multiple times")
val skip_word_abs_parser =
Parse.reserved "skip_word_abs" >>
(fn _ => none_to_some (#skip_word_abs) true "autocorres: skip_word_abs option specified multiple times")
val heap_abs_syntax_parser =
Parse.reserved "heap_abs_syntax" >>
(fn _ => none_to_some (#heap_abs_syntax) true "autocorres: heap_abs_syntax option specified multiple times")
val trace_heap_lift_parser =
named_opt "trace_heap_lift" "function names" >>
(fn funcs => none_to_some (#trace_heap_lift) funcs "autocorres: trace_heap_lift option specified multiple times")
val trace_word_abs_parser =
named_opt "trace_word_abs" "function names" >>
(fn funcs => none_to_some (#trace_word_abs) funcs "autocorres: trace_word_abs option specified multiple times")
val no_opt_parser =
Parse.reserved "no_opt" >>
(fn _ => none_to_some (#no_opt) true "autocorres: no_opt option specified multiple times")
val trace_opt_parser =
Parse.reserved "trace_opt" >>
(fn _ => none_to_some (#trace_opt) true "autocorres: trace_opt option specified multiple times")
val gen_word_heaps_parser =
Parse.reserved "gen_word_heaps" >>
(fn _ => none_to_some (#gen_word_heaps) true "autocorres: gen_word_heaps option specified multiple times")
val keep_going_parser =
Parse.reserved "keep_going" >>
(fn _ => none_to_some (#keep_going) true "autocorres: keep_going option specified multiple times")
val lifted_globals_field_prefix_parser =
named_option Parse.text "lifted_globals_field_prefix" "string" >>
(fn funcs => none_to_some (#lifted_globals_field_prefix) funcs
"autocorres: lifted_globals_field_prefix option specified multiple times")
val lifted_globals_field_suffix_parser =
named_option Parse.text "lifted_globals_field_suffix" "string" >>
(fn funcs => none_to_some (#lifted_globals_field_suffix) funcs
"autocorres: lifted_globals_field_suffix option specified multiple times")
val function_name_prefix_parser =
named_option Parse.text "function_name_prefix" "string" >>
(fn funcs => none_to_some (#function_name_prefix) funcs
"autocorres: function_name_prefix option specified multiple times")
val function_name_suffix_parser =
named_option Parse.text "function_name_suffix" "string" >>
(fn funcs => none_to_some (#function_name_suffix) funcs
"autocorres: function_name_suffix option specified multiple times")
(*
* Blank set of options.
*
* Because we are using references, we need to construct a new set every
* time; hence the dummy parameter.
*)
fun default_opts _ = {
no_heap_abs = ref NONE,
force_heap_abs = ref NONE,
skip_heap_abs = ref NONE,
unsigned_word_abs = ref NONE,
no_signed_word_abs = ref NONE,
skip_word_abs = ref NONE,
ts_rules = ref NONE,
ts_force = ref Symtab.empty,
heap_abs_syntax = ref NONE,
scope = ref NONE,
scope_depth = ref NONE,
c_locale = ref NONE,
no_c_termination = ref NONE,
trace_heap_lift = ref NONE,
trace_word_abs = ref NONE,
no_opt = ref NONE,
trace_opt = ref NONE,
gen_word_heaps = ref NONE,
keep_going = ref NONE,
lifted_globals_field_prefix = ref NONE,
lifted_globals_field_suffix = ref NONE,
function_name_prefix = ref NONE,
function_name_suffix = ref NONE
} : autocorres_options
(* Combined parser. *)
val autocorres_parser : (autocorres_options * string) parser =
let
val option_parser =
(no_heap_abs_parser ||
force_heap_abs_parser ||
skip_heap_abs_parser ||
ts_rules_parser ||
ts_force_parser ||
unsigned_word_abs_parser ||
no_signed_word_abs_parser ||
skip_word_abs_parser ||
heap_abs_syntax_parser ||
scope_parser ||
scope_depth_parser ||
c_locale_parser ||
no_c_termination_parser ||
trace_heap_lift_parser ||
trace_word_abs_parser ||
no_opt_parser ||
trace_opt_parser ||
gen_word_heaps_parser ||
keep_going_parser ||
lifted_globals_field_prefix_parser ||
lifted_globals_field_suffix_parser ||
function_name_prefix_parser ||
function_name_suffix_parser)
|> !! (fn xs => K ("autocorres: unknown option " ^ quote (Parse.text (fst xs) |> #1)))
val options_parser = Parse.list option_parser >> (fn opt_fns => fold I opt_fns)
in
(* Options *)
(Scan.optional (Parse.$$$ "[" |-- options_parser --| Parse.$$$ "]") I
>> (fn f => f (default_opts ()))) --
(* Filename *)
Parse.text
end
(*
* Worker for the autocorres command.
*)
fun do_autocorres (opt : autocorres_options) filename thy =
let
(* Ensure that the filename has already been parsed by the C parser. *)
val csenv = case CalculateState.get_csenv thy filename of
NONE => error ("Filename '" ^ filename ^ "' has not been parsed by the C parser yet.")
| SOME x => x
(* Enter into the correct context. *)
val {base = locale_name,...} = OS.Path.splitBaseExt (OS.Path.file filename)
val locale_name = case !(#c_locale opt) of NONE => locale_name
| SOME l => l
val lthy = case try (Named_Target.begin (locale_name, Position.none)) thy of
SOME lthy => lthy
| NONE => error ("autocorres: no such locale: " ^ locale_name)
(* Fetch program information from the C-parser output. *)
val prog_info = ProgramInfo.get_prog_info lthy filename
val all_simpl_info = FunctionInfo.init_function_info lthy filename
val all_simpl_functions = Symset.make (Symtab.keys all_simpl_info)
(* Process autocorres options. *)
val keep_going = !(#keep_going opt) = SOME true
val _ = if not (!(#unsigned_word_abs opt) = NONE) andalso not (!(#skip_word_abs opt) = NONE) then
error "autocorres: unsigned_word_abs and skip_word_abs cannot be used together."
else if not (!(#no_signed_word_abs opt) = NONE) andalso not (!(#skip_word_abs opt) = NONE) then
error "autocorres: no_signed_word_abs and skip_word_abs cannot be used together."
else ()
val skip_word_abs = !(#skip_word_abs opt) = SOME true
val _ = if not (!(#force_heap_abs opt) = NONE) andalso not (!(#skip_heap_abs opt) = NONE) then
error "autocorres: force_heap_abs and skip_heap_abs cannot be used together."
else if not (!(#no_heap_abs opt) = NONE) andalso not (!(#skip_heap_abs opt) = NONE) then
error "autocorres: no_heap_abs and skip_heap_abs cannot be used together."
else ()
val no_heap_abs = these (!(#no_heap_abs opt))
(* Resolve rule names for ts_rules and ts_force. *)
val ts_force = Symtab.map (K (fn name => Monad_Types.get_monad_type name (Context.Proof lthy)
|> the handle Option => Monad_Types.error_no_such_mt name))
(!(#ts_force opt))
val ts_rules = Monad_Types.get_ordered_rules (these (!(#ts_rules opt))) (Context.Proof lthy)
(* heap_abs_syntax defaults to off. *)
val heap_abs_syntax = !(#heap_abs_syntax opt) = SOME true
(* Ensure that we are not both forcing and preventing a function from being heap lifted. *)
val conflicting_heap_lift_fns =
Symset.inter (Symset.make (these (!(#no_heap_abs opt)))) (Symset.make (these (!(#force_heap_abs opt))))
val _ = if not (Symset.is_empty conflicting_heap_lift_fns) then
error ("autocorres: Functions are declared as both 'no_heap_abs' and 'force_heap_abs': "
^ commas (Symset.dest conflicting_heap_lift_fns))
else
()
(* (Finished processing options.) *)
val existing_phases = Symtab.lookup (AutoCorresFunctionInfo.get thy) filename
val _ = if not (isSome existing_phases) then () else
tracing ("Attempting to restart from previous translation of " ^ filename)
(* Fetch any existing translations, when being run in incremental mode. *)
fun get_existing_optional_phase phase =
case existing_phases of
NONE => SOME Symtab.empty
| SOME phases => FunctionInfo.Phasetab.lookup phases phase
fun get_existing_phase phase =
Option.getOpt (get_existing_optional_phase phase, Symtab.empty)
val [existing_simpl_info,
existing_l1_info,
existing_l2_info,
existing_hl_info,
existing_wa_info,
existing_ts_info] =
map get_existing_phase
[FunctionInfo.CP,
FunctionInfo.L1,
FunctionInfo.L2,
FunctionInfo.HL,
FunctionInfo.WA,
FunctionInfo.TS]
(* HL and WA functions may be missing if skip_heap_abs and skip_word_abs
* are set. In that case, we carry forward the L2 or HL functions and
* cross our fingers. (Should work fine if skip_foo is used consistently
* for all functions, might not work if they are mixed.) *)
val existing_hl_info = Utils.symtab_merge_with snd (existing_l2_info, existing_hl_info);
val existing_wa_info = Utils.symtab_merge_with snd (existing_hl_info, existing_wa_info);
(* Skip functions that have already been translated. *)
val already_translated = Symset.make (Symtab.keys existing_ts_info)
(* Determine which functions should be translated.
* If "scope" is not specified, we translate all functions.
* Otherwise, we translate only "scope"d functions and their direct callees
* (which are translated using a trivial wrapper so that they can be called). *)
val (functions_to_translate, functions_to_wrap) =
case !(#scope opt) of
NONE => (all_simpl_functions, Symset.empty)
| SOME x =>
let
val scope_depth = the_default 2 (!(#scope_depth opt))
val get_deps = get_function_deps (fn f =>
FunctionInfo.all_callees (the (Symtab.lookup all_simpl_info f)))
val funcs = get_deps x scope_depth
val _ = tracing ("autocorres scope: selected " ^ Int.toString (Symset.card funcs) ^ " function(s):")
val _ = app (fn f => tracing (" " ^ f)) (Symset.dest funcs)
val funcs_callees =
Symset.subtract (Symset.union already_translated funcs) (get_deps (Symset.dest funcs) 1)
val _ = if Symset.is_empty funcs_callees then () else
(tracing ("autocorres scope: wrapping " ^
Int.toString (Symset.card funcs_callees) ^ " function(s):");
app (fn f => tracing (" " ^ f)) (Symset.dest funcs_callees))
in (funcs, funcs_callees) end
(* Functions that have already been translated cannot be translated again. *)
val already_translated = Symset.inter already_translated functions_to_translate
val _ = if Symset.is_empty already_translated then () else
error ("autocorres scope: these functions have already been translated: " ^
commas (Symset.dest already_translated))
(* If a function has no SIMPL body, we will not wrap its body;
* instead we create a dummy definition and translate it via the usual process. *)
val undefined_functions =
Symset.filter (fn f => #invented_body (the (Symtab.lookup all_simpl_info f))) functions_to_wrap
val functions_to_wrap = Symset.subtract undefined_functions functions_to_wrap
val functions_to_translate = Symset.union undefined_functions functions_to_translate
(* We will process these functions... *)
val functions_to_process = Symset.union functions_to_translate functions_to_wrap
(* ... and ignore these functions. *)
val functions_to_ignore = Symset.subtract functions_to_process all_simpl_functions
(* Disallow referring to functions that don't exist or are excluded from processing. *)
val funcs_in_options =
these (!(#no_heap_abs opt))
@ these (!(#force_heap_abs opt))
@ these (!(#unsigned_word_abs opt))
@ these (!(#no_signed_word_abs opt))
@ these (!(#scope opt))
@ Symtab.keys (!(#ts_force opt))
@ these (!(#trace_heap_lift opt))
@ these (!(#trace_word_abs opt))
|> Symset.make
val invalid_functions =
Symset.subtract all_simpl_functions funcs_in_options
val ignored_functions =
Symset.subtract (Symset.union invalid_functions functions_to_process) funcs_in_options
val _ =
if Symset.card invalid_functions > 0 then
error ("autocorres: no such function(s): " ^ commas (Symset.dest invalid_functions))
else if Symset.card ignored_functions > 0 then
error ("autocorres: cannot configure translation for excluded function(s): " ^
commas (Symset.dest ignored_functions))
else
()
(* Only translate "scope" functions and their direct callees. *)
val simpl_info =
Symtab.dest all_simpl_info
|> List.mapPartial (fn (name, info) =>
if Symset.contains functions_to_translate name then
SOME (name, FunctionInfo.function_info_upd_is_simpl_wrapper false info)
else if Symset.contains functions_to_wrap name then
SOME (name, FunctionInfo.function_info_upd_is_simpl_wrapper true info)
else
NONE)
|> Symtab.make
(* Recalculate callees after "scope" restriction. *)
val (simpl_call_graph, simpl_info) = FunctionInfo.calc_call_graph simpl_info
(* Check that recursive function groups are all lifted to the same monad. *)
val _ = #topo_sorted_functions simpl_call_graph
|> map (TypeStrengthen.compute_lift_rules ts_rules ts_force o Symset.dest)
(* Disable heap lifting for all un-translated functions. *)
val force_heap_abs = Symset.make (these (!(#force_heap_abs opt)))
val conflicting_heap_lift_fns = Symset.subtract functions_to_translate force_heap_abs
val _ = if not (Symset.is_empty conflicting_heap_lift_fns) then
error ("autocorres: Functions marked 'force_heap_abs' but excluded from 'scope': "
^ commas (Symset.dest conflicting_heap_lift_fns))
else
()
val no_heap_abs = Symset.union (Symset.make no_heap_abs) functions_to_wrap
(* Disable word abstraction for all un-translated functions. *)
val unsigned_word_abs = these (!(#unsigned_word_abs opt)) |> Symset.make
val no_signed_word_abs = these (!(#no_signed_word_abs opt)) |> Symset.make
val conflicting_unsigned_abs_fns =
Symset.subtract functions_to_translate unsigned_word_abs
val _ = if Symset.is_empty conflicting_unsigned_abs_fns then () else
error ("autocorres: Functions marked 'unsigned_word_abs' but excluded from 'scope': "
^ commas (Symset.dest conflicting_unsigned_abs_fns))
val no_signed_word_abs = Symset.union no_signed_word_abs functions_to_wrap
(*
* Sanity check the C parser's output.
*
* In the past, the C parser has defined terms that haven't type-checked due
* to sort constraints on constants. This doesn't violate the Isabelle
* kernel's soundness, but does wreck havoc on us.
*)
val sanity_errors =
Symtab.dest simpl_info
|> List.mapPartial (fn (fn_name, info) =>
case Symtab.lookup existing_l1_info fn_name of
SOME _ => NONE (* already translated; ignore *)
| _ => let
val def =
info
|> #definition
|> Thm.prop_of
|> Utils.rhs_of
in
(Syntax.check_term lthy def; NONE)
handle (ERROR str) => SOME (fn_name, str)
end)
val _ =
if length sanity_errors > 0 then
error ("C parser failed sanity checks. Erroneous functions: "
^ commas (map fst sanity_errors))
else
()
val do_opt = !(#no_opt opt) <> SOME true
val trace_opt = !(#trace_opt opt) = SOME true
val gen_word_heaps = !(#gen_word_heaps opt) = SOME true
(* Any function that was declared in the C file (but never defined) should
* stay in the nondet-monad unless explicitly instructed by the user to be
* something else. *)
val ts_force = let
val invented_functions =
functions_to_process
(* Select functions with an invented body. *)
|> Symset.filter (Symtab.lookup simpl_info #> the #> #invented_body)
(* Ignore functions which already have a "ts_force" rule applied to them. *)
|> Symset.subtract (Symset.make (Symtab.keys ts_force))
|> Symset.dest
in
(* Use the most general monadic type allowed by the user. *)
fold (fn n => Symtab.update_new (n, List.last ts_rules)) invented_functions ts_force
end
(* Prefixes/suffixes for generated names. *)
val make_lifted_globals_field_name = let
val prefix = case !(#lifted_globals_field_prefix opt) of
NONE => ""
| SOME p => p
val suffix = case !(#lifted_globals_field_suffix opt) of
NONE => "_''"
| SOME s => s
in fn f => prefix ^ f ^ suffix end
(* Prefixes/suffixes for generated names. *)
val make_function_name = let
val prefix = case !(#function_name_prefix opt) of
NONE => ""
| SOME p => p
val suffix = case !(#function_name_suffix opt) of
NONE => "'"
| SOME s => s
in fn f => prefix ^ f ^ suffix end
(* Initialise database for traces.
* Currently, this is implemented by mutating a global table.
* While slightly ugly, it does simplify adding more tracing to AutoCorres
* without cluttering the return types of existing code. *)
val trace_db: AutoCorresData.Trace Symtab.table (* type *) Symtab.table (* func *) Synchronized.var =
Synchronized.var "AutoCorres traces" Symtab.empty
fun add_trace f_name trace_name trace =
Synchronized.change trace_db
(Symtab.map_default (f_name, Symtab.empty) (Symtab.update_new (trace_name, trace)))
(* Do the translation. *)
val l1_results =
SimplConv.translate
filename prog_info simpl_info existing_simpl_info existing_l1_info
(!(#no_c_termination opt) <> SOME true)
do_opt trace_opt add_trace (prefix "l1_" o make_function_name) lthy
val l2_results =
LocalVarExtract.translate
filename prog_info l1_results existing_l1_info existing_l2_info
do_opt trace_opt add_trace (prefix "l2_" o make_function_name)
val skip_heap_abs = !(#skip_heap_abs opt) = SOME true
val (hl_results, maybe_heap_info) =
if skip_heap_abs
then (l2_results, NONE)
else let
val (l2_results', HL_setup) =
HeapLift.prepare_heap_lift
filename prog_info l2_results lthy all_simpl_info
make_lifted_globals_field_name gen_word_heaps heap_abs_syntax;
in (HeapLift.translate
filename prog_info l2_results' existing_l2_info existing_hl_info
HL_setup no_heap_abs force_heap_abs
heap_abs_syntax keep_going
(these (!(#trace_heap_lift opt))) do_opt trace_opt add_trace
(prefix "hl_" o make_function_name),
SOME (#heap_info HL_setup))
end
val wa_results =
if skip_word_abs
then hl_results
else WordAbstract.translate
filename prog_info maybe_heap_info
hl_results existing_hl_info existing_wa_info
unsigned_word_abs no_signed_word_abs
(these (!(#trace_word_abs opt))) do_opt trace_opt add_trace
(prefix "wa_" o make_function_name)
val ts_results =
TypeStrengthen.translate
ts_rules ts_force filename prog_info
wa_results existing_wa_info existing_ts_info
make_function_name keep_going do_opt add_trace
(* Collect final translation results. *)
val l1_info = FSeq.list_of l1_results |> map snd |> Utils.symtab_merge false;
val l2_info = FSeq.list_of l2_results |> map snd |> Utils.symtab_merge false;
val hl_info = if skip_heap_abs then Symtab.empty else
FSeq.list_of hl_results |> map snd |> Utils.symtab_merge false;
val wa_info = if skip_word_abs then Symtab.empty else
FSeq.list_of wa_results |> map snd |> Utils.symtab_merge false;
val ts_results' = FSeq.list_of ts_results;
val ts_info = ts_results' |> map snd |> Utils.symtab_merge false;
val lthy = if null ts_results' then lthy else fst (List.last ts_results');
(* Put together final ac_corres theorems.
* TODO: we should also store these as theory data. *)
fun prove_ac_corres fn_name =
let
val l1_thm = #corres_thm (the (Symtab.lookup l1_info fn_name))
handle Option => raise SimplConv.FunctionNotFound fn_name
val l2_thm = #corres_thm (the (Symtab.lookup l2_info fn_name))
handle Option => raise SimplConv.FunctionNotFound fn_name
val hl_thm = #corres_thm (the (Symtab.lookup hl_info fn_name))
(* If heap lifting was disabled, we use a placeholder *)
handle Option => @{thm L2Tcorres_trivial_from_local_var_extract} OF [l2_thm]
val wa_thm = #corres_thm (the (Symtab.lookup wa_info fn_name))
(* Placeholder for when word abstraction is disabled *)
handle Option => @{thm corresTA_trivial_from_heap_lift} OF [hl_thm]
val ts_thm = #corres_thm (the (Symtab.lookup ts_info fn_name))
handle Option => raise SimplConv.FunctionNotFound fn_name
in let
val final_thm = @{thm ac_corres_chain} OF [l1_thm, l2_thm, hl_thm, wa_thm, ts_thm]
(* Remove fluff, like "f o id", that gets introduced by the HL and WA placeholders *)
val final_thm' = Simplifier.simplify (put_simpset AUTOCORRES_SIMPSET lthy) final_thm
in SOME final_thm' end
handle THM _ =>
(Utils.THM_non_critical keep_going
("autocorres: failed to prove ac_corres theorem for " ^ fn_name)
0 [l1_thm, l2_thm, hl_thm, wa_thm, ts_thm];
NONE)
end
val ac_corres_thms =
Symtab.keys ts_info
|> Par_List.map (fn f => Option.map (pair f) (prove_ac_corres f))
|> List.mapPartial I
val lthy = fold (fn (f, thm) =>
Utils.define_lemma (make_function_name f ^ "_ac_corres") thm #> snd)
ac_corres_thms lthy
(* Name final mono theorems and add them to the simpset. *)
val lthy =
fold (fn (f, info) => fn lthy =>
case #mono_thm info of
NONE => lthy
| SOME mono_thm =>
Utils.define_lemma (make_function_name f ^ "_mono") mono_thm lthy |> snd
|> Utils.simp_add [mono_thm])
(Symtab.dest ts_info) lthy
(* Save function info for future reference. *)
val simpl_info' = Symtab.merge (K false) (existing_simpl_info, simpl_info);
val l1_info' = Symtab.merge (K false) (existing_l1_info, l1_info);
val l2_info' = Symtab.merge (K false) (existing_l2_info, l2_info);
val hl_info' = Symtab.merge (K false) (existing_hl_info, hl_info);
val wa_info' = Symtab.merge (K false) (existing_wa_info, wa_info);
val ts_info' = Symtab.merge (K false) (existing_ts_info, ts_info);
val new_results =
FunctionInfo.Phasetab.make
([(FunctionInfo.CP, simpl_info'),
(FunctionInfo.L1, l1_info'),
(FunctionInfo.L2, l2_info'),
(FunctionInfo.TS, ts_info')]
@ (if skip_heap_abs andalso Symtab.is_empty existing_hl_info
then [] else [(FunctionInfo.HL, hl_info')])
@ (if skip_word_abs andalso Symtab.is_empty existing_wa_info
then [] else [(FunctionInfo.WA, wa_info')])
)
val _ = tracing "Saving function info to AutoCorresFunctionInfo."
val lthy = Local_Theory.background_theory (
AutoCorresFunctionInfo.map (fn tbl =>
Symtab.update (filename, new_results) tbl)) lthy
(* All traces should be available at this point. Archive them. *)
val traces = Synchronized.value trace_db;
val _ = if Symtab.is_empty traces then () else
tracing "Saving translation traces to AutoCorresData.Traces."
val lthy = lthy |> Local_Theory.background_theory (
AutoCorresData.Traces.map
(Symtab.map_default (filename, Symtab.empty)
(fn old_traces => Utils.symtab_merge_with snd (old_traces, traces))));
in
(* Exit context. *)
Named_Target.exit lthy
end
end