lh-l4v/tools/autocorres/autocorres.ML

730 lines
30 KiB
Standard ML

(*
* Copyright 2014, NICTA
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(NICTA_BSD)
*)
(*
* 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 =
if skip_heap_abs
then l2_results
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)
end
val wa_results =
if skip_word_abs
then hl_results
else WordAbstract.translate
filename prog_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