Skip to content

Commit 6d7d3b8

Browse files
committed
Ensure that functions are evaluated after their arguments (flambda-backend #353)
1 parent 89bda6b commit 6d7d3b8

File tree

1 file changed

+60
-34
lines changed

1 file changed

+60
-34
lines changed

middle_end/closure/closure.ml

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ let is_pure_prim p =
224224
| Arbitrary_effects, _ -> false
225225

226226
(* Check if a clambda term is ``pure'',
227-
that is without side-effects *and* not containing function definitions *)
227+
that is without side-effects *and* not containing function definitions
228+
(Pure terms may still read mutable state) *)
228229

229230
let rec is_pure = function
230231
Uvar _ -> true
@@ -730,17 +731,19 @@ type env = {
730731
*)
731732

732733
(* Approximates "no effects and no coeffects" *)
733-
let is_substituable ~mutable_vars = function
734+
let rec is_substituable ~mutable_vars = function
734735
| Uvar v -> not (V.Set.mem v mutable_vars)
735736
| Uconst _ -> true
737+
| Uoffset(arg, _) -> is_substituable ~mutable_vars arg
736738
| _ -> false
737739

738740
(* Approximates "only generative effects" *)
739741
let is_erasable = function
740742
| Uclosure _ -> true
741743
| u -> is_pure u
742744

743-
let bind_params { backend; mutable_vars; _ } loc fpc params args body =
745+
let bind_params { backend; mutable_vars; _ } loc fdesc params args funct body =
746+
let fpc = fdesc.fun_float_const_prop in
744747
let rec aux subst pl al body =
745748
match (pl, al) with
746749
([], []) -> substitute (Debuginfo.from_location loc) (backend, fpc)
@@ -768,7 +771,16 @@ let bind_params { backend; mutable_vars; _ } loc fpc params args body =
768771
in
769772
(* Reverse parameters and arguments to preserve right-to-left
770773
evaluation order (PR#2910). *)
771-
aux V.Map.empty (List.rev params) (List.rev args) body
774+
let params, args = List.rev params, List.rev args in
775+
let params, args, body =
776+
(* Ensure funct is evaluated after args *)
777+
match params with
778+
| my_closure :: params when not fdesc.fun_closed ->
779+
(params @ [my_closure]), (args @ [funct]), body
780+
| _ ->
781+
params, args, (if is_pure funct then body else Usequence (funct, body))
782+
in
783+
aux V.Map.empty params args body
772784

773785
(* Check if a lambda term is ``pure'',
774786
that is without side-effects *and* not containing function definitions *)
@@ -787,36 +799,50 @@ let fail_if_probe ~probe msg =
787799
(* Generate a direct application *)
788800

789801
let direct_apply env fundesc ufunct uargs ~probe ~loc ~attribute =
790-
let app_args =
791-
if fundesc.fun_closed then uargs else uargs @ [ufunct] in
792-
let app =
793-
match fundesc.fun_inline, attribute with
794-
| _, Never_inlined | None, _ ->
795-
let dbg = Debuginfo.from_location loc in
796-
warning_if_forced_inlined ~loc ~attribute
797-
"Function information unavailable";
798-
if not fundesc.fun_closed then begin
799-
fail_if_probe ~probe "Not closed"
800-
end;
801-
begin match probe, attribute with
802-
| None, _ -> ()
803-
| Some _, Never_inlined -> ()
804-
| Some _, _ ->
805-
fail_if_probe ~probe "Erroneously marked to be inlined"
806-
end;
807-
Udirect_apply(fundesc.fun_label, app_args, probe, dbg)
808-
| Some(params, body), _ ->
809-
bind_params env loc fundesc.fun_float_const_prop params app_args
810-
body
811-
in
812-
(* If ufunct can contain side-effects or function definitions,
813-
we must make sure that it is evaluated exactly once.
814-
If the function is not closed, we evaluate ufunct as part of the
815-
arguments.
816-
If the function is closed, we force the evaluation of ufunct first. *)
817-
if not fundesc.fun_closed || is_pure ufunct
818-
then app
819-
else Usequence(ufunct, app)
802+
match fundesc.fun_inline, attribute with
803+
| _, Never_inlined
804+
| None, _ ->
805+
let dbg = Debuginfo.from_location loc in
806+
warning_if_forced_inlined ~loc ~attribute
807+
"Function information unavailable";
808+
if not fundesc.fun_closed then begin
809+
fail_if_probe ~probe "Not closed"
810+
end;
811+
begin match probe, attribute with
812+
| None, _ -> ()
813+
| Some _, Never_inlined -> ()
814+
| Some _, _ ->
815+
fail_if_probe ~probe "Erroneously marked to be inlined"
816+
end;
817+
if fundesc.fun_closed && is_pure ufunct then
818+
Udirect_apply(fundesc.fun_label, uargs, probe, dbg)
819+
else if not fundesc.fun_closed &&
820+
is_substituable ~mutable_vars:env.mutable_vars ufunct then
821+
Udirect_apply(fundesc.fun_label, uargs @ [ufunct], probe, dbg)
822+
else begin
823+
let args = List.map (fun arg ->
824+
if is_substituable ~mutable_vars:env.mutable_vars arg then
825+
None, arg
826+
else
827+
let id = V.create_local "arg" in
828+
Some (VP.create id, arg), Uvar id) uargs in
829+
let app_args = List.map snd args in
830+
List.fold_left (fun app (binding,_) ->
831+
match binding with
832+
| None -> app
833+
| Some (v, e) -> Ulet(Immutable, Pgenval, v, e, app))
834+
(if fundesc.fun_closed then
835+
Usequence (ufunct,
836+
Udirect_apply (fundesc.fun_label, app_args, probe, dbg))
837+
else
838+
let clos = V.create_local "clos" in
839+
Ulet(Immutable, Pgenval, VP.create clos, ufunct,
840+
Udirect_apply(fundesc.fun_label, app_args @ [Uvar clos],
841+
probe, dbg)))
842+
args
843+
end
844+
| Some(params, body), _ ->
845+
bind_params env loc fundesc params uargs ufunct body
820846

821847
(* Add [Value_integer] info to the approximation of an application *)
822848

0 commit comments

Comments
 (0)