2019-11-02 16:56:05 -05:00
|
|
|
open Mini_c
|
2019-12-10 12:00:21 -06:00
|
|
|
open Trace
|
2019-11-02 16:56:05 -05:00
|
|
|
|
2019-11-25 17:15:25 -06:00
|
|
|
(* TODO hack to specialize map_expression to identity monad *)
|
|
|
|
let map_expression :
|
|
|
|
(expression -> expression) -> (expression -> expression) =
|
|
|
|
fun f e ->
|
|
|
|
match Helpers.map_expression (fun e -> ok (f e)) e with
|
|
|
|
| Ok (e, _) -> e
|
|
|
|
| Error _ -> assert false (* impossible *)
|
|
|
|
|
|
|
|
|
|
|
|
(* Conservative purity test: ok to treat pure things as impure, must
|
|
|
|
not treat impure things as pure. *)
|
2019-11-13 13:43:03 -06:00
|
|
|
|
|
|
|
(* true if the name names a pure constant -- i.e. if uses will be pure
|
|
|
|
assuming arguments are pure *)
|
2019-12-04 11:40:58 +00:00
|
|
|
let is_pure_constant : constant -> bool =
|
2019-11-13 13:43:03 -06:00
|
|
|
function
|
2019-12-04 11:40:58 +00:00
|
|
|
| C_UNIT
|
|
|
|
| C_CAR | C_CDR | C_PAIR
|
|
|
|
| C_NIL | C_CONS
|
|
|
|
| C_NEG | C_OR | C_AND | C_XOR | C_NOT
|
|
|
|
| C_EQ | C_NEQ | C_LT | C_LE | C_GT | C_GE
|
|
|
|
| C_SOME
|
|
|
|
| C_UPDATE | C_MAP_GET | C_MAP_FIND_OPT | C_MAP_ADD | C_MAP_UPDATE
|
|
|
|
| C_INT | C_ABS | C_IS_NAT
|
|
|
|
| C_BALANCE | C_AMOUNT | C_ADDRESS | C_NOW | C_SOURCE | C_SENDER | C_CHAIN_ID
|
|
|
|
| C_SET_MEM | C_SET_ADD | C_SET_REMOVE | C_SLICE
|
|
|
|
| C_SHA256 | C_SHA512 | C_BLAKE2b | C_CHECK_SIGNATURE
|
|
|
|
| C_HASH_KEY | C_BYTES_PACK | C_CONCAT
|
2019-11-13 13:43:03 -06:00
|
|
|
-> true
|
2019-11-25 18:10:49 -06:00
|
|
|
(* unfortunately impure: *)
|
2019-12-04 11:40:58 +00:00
|
|
|
| C_ADD | C_SUB |C_MUL|C_DIV|C_MOD
|
2019-11-25 18:10:49 -06:00
|
|
|
(* impure: *)
|
2019-12-04 11:40:58 +00:00
|
|
|
| C_ASSERTION | C_ASSERT_INFERRED
|
|
|
|
| C_MAP_GET_FORCE | C_MAP_FIND
|
|
|
|
| C_FOLD_WHILE
|
|
|
|
| C_CALL
|
2019-11-13 13:43:03 -06:00
|
|
|
(* TODO... *)
|
2019-11-25 18:10:49 -06:00
|
|
|
| _
|
|
|
|
-> false
|
2019-11-02 16:56:05 -05:00
|
|
|
|
2019-11-13 13:43:03 -06:00
|
|
|
let rec is_pure : expression -> bool = fun e ->
|
|
|
|
match e.content with
|
|
|
|
| E_literal _
|
|
|
|
| E_closure _
|
|
|
|
| E_skip
|
|
|
|
| E_variable _
|
|
|
|
| E_make_empty_map _
|
2019-11-04 17:01:39 -06:00
|
|
|
| E_make_empty_big_map _
|
2019-11-13 13:43:03 -06:00
|
|
|
| E_make_empty_list _
|
|
|
|
| E_make_empty_set _
|
|
|
|
| E_make_none _
|
|
|
|
-> true
|
|
|
|
|
|
|
|
| E_if_bool (cond, bt, bf)
|
|
|
|
| E_if_none (cond, bt, (_, bf))
|
|
|
|
| E_if_cons (cond, bt, (_, bf))
|
|
|
|
| E_if_left (cond, (_, bt), (_, bf))
|
|
|
|
-> List.for_all is_pure [ cond ; bt ; bf ]
|
|
|
|
|
2020-01-16 19:36:04 +00:00
|
|
|
| E_let_in (_, _, e1, e2)
|
2019-11-13 13:43:03 -06:00
|
|
|
| E_sequence (e1, e2)
|
|
|
|
-> List.for_all is_pure [ e1 ; e2 ]
|
|
|
|
|
|
|
|
| E_constant (c, args)
|
|
|
|
-> is_pure_constant c && List.for_all is_pure args
|
2020-01-09 18:23:37 +01:00
|
|
|
| E_update (e, updates)
|
|
|
|
-> is_pure e && List.for_all (fun (_,e) -> is_pure e) updates
|
2019-11-13 13:43:03 -06:00
|
|
|
|
|
|
|
(* I'm not sure about these. Maybe can be tested better? *)
|
|
|
|
| E_application _
|
|
|
|
| E_iterator _
|
|
|
|
| E_fold _
|
|
|
|
-> false
|
|
|
|
|
|
|
|
(* Could be pure, but, divergence is an effect, so halting problem
|
|
|
|
is near... *)
|
|
|
|
| E_while _ -> false
|
|
|
|
|
|
|
|
(* definitely not pure *)
|
|
|
|
| E_assignment _ -> false
|
|
|
|
|
2019-12-04 11:40:58 +00:00
|
|
|
let occurs_in : expression_variable -> expression -> bool =
|
2019-11-25 17:15:25 -06:00
|
|
|
fun x e ->
|
|
|
|
let fvs = Free_variables.expression [] e in
|
|
|
|
Free_variables.mem x fvs
|
2019-11-13 13:43:03 -06:00
|
|
|
|
2019-12-04 11:40:58 +00:00
|
|
|
let occurs_count : expression_variable -> expression -> int =
|
2019-11-25 17:15:25 -06:00
|
|
|
fun x e ->
|
|
|
|
let fvs = Free_variables.expression [] e in
|
|
|
|
Free_variables.mem_count x fvs
|
|
|
|
|
|
|
|
(* If `ignore_lambdas` is true, ignore assignments which occur inside
|
|
|
|
lambdas, which have no effect on the value of the variable outside
|
|
|
|
of the lambda. *)
|
2019-12-04 11:40:58 +00:00
|
|
|
let rec is_assigned : ignore_lambdas:bool -> expression_variable -> expression -> bool =
|
2019-11-25 17:15:25 -06:00
|
|
|
fun ~ignore_lambdas x e ->
|
|
|
|
let self = is_assigned ~ignore_lambdas x in
|
|
|
|
let selfs = List.exists self in
|
|
|
|
let it = Var.equal x in
|
|
|
|
let self_binder binder body =
|
|
|
|
if it binder
|
|
|
|
then false
|
|
|
|
else self body in
|
|
|
|
let self_binder2 binder1 binder2 body =
|
|
|
|
if it binder1 || it binder2
|
|
|
|
then false
|
|
|
|
else self body in
|
|
|
|
match e.content with
|
|
|
|
| E_assignment (x, _, e) ->
|
|
|
|
it x || self e
|
2020-01-09 18:23:37 +01:00
|
|
|
| E_update (r, updates) ->
|
|
|
|
List.fold_left (fun prev (_,e) -> prev || self e) (self r) updates
|
2019-11-25 17:15:25 -06:00
|
|
|
| E_closure { binder; body } ->
|
|
|
|
if ignore_lambdas
|
|
|
|
then false
|
|
|
|
else self_binder binder body
|
|
|
|
| E_constant (_, args) ->
|
|
|
|
selfs args
|
|
|
|
| E_application (f, arg) ->
|
|
|
|
selfs [ f ; arg ]
|
|
|
|
| E_iterator (_, ((x, _), e1), e2) ->
|
|
|
|
self_binder x e1 || self e2
|
|
|
|
| E_fold (((x, _), e1), e2, e3) ->
|
|
|
|
self_binder x e1 || selfs [ e2 ; e3 ]
|
|
|
|
| E_if_bool (e1, e2, e3) ->
|
|
|
|
selfs [ e1 ; e2 ; e3 ]
|
|
|
|
| E_if_none (e1, e2, ((x, _), e3)) ->
|
|
|
|
selfs [ e1 ; e2 ] || self_binder x e3
|
|
|
|
| E_if_cons (e1, e2, (((hd, _), (tl, _)), e3)) ->
|
|
|
|
selfs [ e1 ; e2 ] || self_binder2 hd tl e3
|
|
|
|
| E_if_left (e1, ((l, _), e2), ((r, _), e3)) ->
|
|
|
|
self e1 || self_binder l e2 || self_binder r e3
|
2020-01-16 19:36:04 +00:00
|
|
|
| E_let_in ((x, _), _, e1, e2) ->
|
2019-11-25 17:15:25 -06:00
|
|
|
self e1 || self_binder x e2
|
|
|
|
| E_sequence (e1, e2) ->
|
|
|
|
selfs [ e1 ; e2 ]
|
|
|
|
| E_while (e1, e2) ->
|
|
|
|
selfs [ e1 ; e2 ]
|
|
|
|
| E_literal _
|
|
|
|
| E_skip
|
|
|
|
| E_variable _
|
|
|
|
| E_make_empty_map _
|
|
|
|
| E_make_empty_big_map _
|
|
|
|
| E_make_empty_list _
|
|
|
|
| E_make_empty_set _
|
|
|
|
| E_make_none _ ->
|
|
|
|
false
|
|
|
|
|
|
|
|
(* Let "inlining" mean transforming the code:
|
|
|
|
|
|
|
|
let x = e1 in e2
|
|
|
|
|
|
|
|
to:
|
|
|
|
|
|
|
|
e2[e1/x]
|
|
|
|
|
|
|
|
(where the latter signifies substituting e1 for x in e2.)
|
|
|
|
|
|
|
|
Things which can go wrong for inlining:
|
|
|
|
|
|
|
|
- If `e1` is not pure, inlining may fail to preserve semantics.
|
|
|
|
- If assignments to `x` occur in e2, inlining does not make sense.
|
|
|
|
- Free variables of `e1` may be assigned in e2, before usages of `x`.
|
|
|
|
- Free variables of `e1` may be shadowed in e2, at usages of `x`. This
|
|
|
|
is not a problem if the substitution is capture-avoiding.
|
|
|
|
- ?
|
|
|
|
*)
|
2019-11-02 16:56:05 -05:00
|
|
|
|
2019-12-04 11:40:58 +00:00
|
|
|
let can_inline : expression_variable -> expression -> expression -> bool =
|
2019-11-25 17:15:25 -06:00
|
|
|
fun x e1 e2 ->
|
|
|
|
is_pure e1 &&
|
|
|
|
(* if x does not occur in e2, there can be no other problems:
|
|
|
|
substitution will be a noop up to alpha-equivalence *)
|
|
|
|
(not (occurs_in x e2) ||
|
|
|
|
(* else, must worry about assignment *)
|
|
|
|
(not (is_assigned ~ignore_lambdas:false x e2) &&
|
|
|
|
List.for_all
|
|
|
|
(fun y -> not (is_assigned ~ignore_lambdas:true y e2))
|
|
|
|
(Free_variables.expression [] e2)))
|
|
|
|
|
2019-12-04 11:40:58 +00:00
|
|
|
let should_inline : expression_variable -> expression -> bool =
|
2019-11-25 17:15:25 -06:00
|
|
|
fun x e ->
|
|
|
|
occurs_count x e <= 1
|
|
|
|
|
|
|
|
let inline_let : bool ref -> expression -> expression =
|
|
|
|
fun changed e ->
|
|
|
|
match e.content with
|
2020-01-16 19:36:04 +00:00
|
|
|
| E_let_in ((x, _a), should_inline_here, e1, e2) ->
|
|
|
|
if can_inline x e1 e2 && (should_inline_here || should_inline x e2)
|
2019-11-25 17:15:25 -06:00
|
|
|
then
|
|
|
|
(* can raise Subst.Bad_argument, but should not happen, due to
|
|
|
|
can_inline *)
|
|
|
|
let e2' = Subst.subst_expression ~body:e2 ~x:x ~expr:e1 in
|
|
|
|
(changed := true ; e2')
|
|
|
|
else
|
|
|
|
e
|
|
|
|
| _ -> e
|
|
|
|
|
|
|
|
let inline_lets : bool ref -> expression -> expression =
|
|
|
|
fun changed ->
|
|
|
|
map_expression (inline_let changed)
|
|
|
|
|
|
|
|
|
|
|
|
(* Let "beta" mean transforming the code:
|
|
|
|
|
|
|
|
(\x. e1) e2
|
|
|
|
|
|
|
|
to:
|
|
|
|
|
|
|
|
let x = e2 in e1
|
|
|
|
|
|
|
|
Things which can go wrong for beta reduction:
|
|
|
|
|
|
|
|
- If e1 contains (meaningful) assignments to free variables, semantics
|
|
|
|
will not be preserved.
|
|
|
|
- ?
|
|
|
|
*)
|
|
|
|
|
|
|
|
let can_beta : anon_function -> bool =
|
|
|
|
fun lam ->
|
|
|
|
List.for_all
|
|
|
|
(fun x -> not (is_assigned ~ignore_lambdas:true x lam.body))
|
|
|
|
(Free_variables.lambda [] lam)
|
|
|
|
|
|
|
|
let beta : bool ref -> expression -> expression =
|
|
|
|
fun changed e ->
|
|
|
|
match e.content with
|
|
|
|
| E_application ({ content = E_closure { binder = x ; body = e1 } ; type_value = T_function (xtv, tv) }, e2) ->
|
|
|
|
if can_beta { binder = x ; body = e1 }
|
|
|
|
then
|
|
|
|
(changed := true ;
|
2020-01-16 19:36:04 +00:00
|
|
|
Expression.make (E_let_in ((x, xtv), false, e2, e1)) tv)
|
2019-11-25 17:15:25 -06:00
|
|
|
else e
|
|
|
|
|
|
|
|
(* also do CAR (PAIR x y) ↦ x, or CDR (PAIR x y) ↦ y, only if x and y are pure *)
|
2019-12-04 11:40:58 +00:00
|
|
|
| E_constant (C_CAR| C_CDR as const, [ { content = E_constant (C_PAIR, [ e1 ; e2 ]) ; type_value = _ } ]) ->
|
2019-11-25 17:15:25 -06:00
|
|
|
if is_pure e1 && is_pure e2
|
|
|
|
then (changed := true ;
|
|
|
|
match const with
|
2019-12-04 11:40:58 +00:00
|
|
|
| C_CAR -> e1
|
|
|
|
| C_CDR -> e2
|
2019-11-25 17:15:25 -06:00
|
|
|
| _ -> assert false)
|
|
|
|
else e
|
|
|
|
| _ -> e
|
|
|
|
|
|
|
|
let betas : bool ref -> expression -> expression =
|
|
|
|
fun changed ->
|
|
|
|
map_expression (beta changed)
|
|
|
|
|
|
|
|
let rec all_expression : expression -> expression =
|
|
|
|
fun e ->
|
|
|
|
let changed = ref false in
|
|
|
|
let e = inline_lets changed e in
|
|
|
|
let e = betas changed e in
|
|
|
|
if !changed
|
|
|
|
then all_expression e
|
|
|
|
else e
|