Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make resuming a continuation more efficient #1765

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ jobs:
skip-effects: false
skip-test: false
skip-doc: false
- os: ubuntu-latest
ocaml-compiler: "5.1"
skip-effects: false
skip-test: false
skip-doc: true
# Note this OCaml compiler is bytecode only
- os: ubuntu-latest
ocaml-compiler: "ocaml-variants.5.2.0+options,ocaml-option-32bit"
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Merged Wasm_of_ocaml (#1724)
* Lib: removed no longer relevant Js.optdef type annotations (#1769)
* Misc: drop support for IE
* Compiler/Runtime: Make resuming a continuation more efficient (#1765)

## Bug fixes
* Fix small bug in global data flow analysis (#1768)
Expand Down
39 changes: 31 additions & 8 deletions compiler/lib/effects.ml
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,34 @@ let cps_block ~st ~k pc block =
in

let rewrite_instr x e =
let perform_effect ~effect_ ~continuation =
let perform_effect ~effect_ continuation_and_tail =
Some
(fun ~k ->
let e =
Prim (Extern "caml_perform_effect", [ Pv effect_; continuation; Pv k ])
match Config.target () with
| `JavaScript -> (
match continuation_and_tail with
| None -> Prim (Extern "caml_perform_effect", [ Pv effect_; Pv k ])
| Some (continuation, tail) ->
Prim
( Extern "caml_reperform_effect"
, [ Pv effect_; continuation; tail; Pv k ] ))
| `Wasm -> (
(* temporary until we finish the change to the wasmoo
runtime *)
match continuation_and_tail with
| None ->
Prim
( Extern "caml_perform_effect"
, [ Pv effect_
; Pc (Int Targetint.zero)
; Pc (Int Targetint.zero)
; Pv k
] )
| Some (continuation, tail) ->
Prim
( Extern "caml_perform_effect"
, [ Pv effect_; continuation; tail; Pv k ] ))
in
let x = Var.fresh () in
[ Let (x, e) ], Return x)
Expand All @@ -560,22 +583,22 @@ let cps_block ~st ~k pc block =
exact || Global_flow.exact_call st.flow_info f (List.length args)
in
tail_call ~st ~exact ~in_cps:true ~check:true ~f (args @ [ k ]))
| Prim (Extern "%resume", [ Pv stack; Pv f; Pv arg ]) ->
| Prim (Extern "%resume", [ Pv stack; Pv f; Pv arg; tail ]) ->
Some
(fun ~k ->
let k' = Var.fresh_n "cont" in
tail_call
~st
~instrs:[ Let (k', Prim (Extern "caml_resume_stack", [ Pv stack; Pv k ])) ]
~instrs:
[ Let (k', Prim (Extern "caml_resume_stack", [ Pv stack; tail; Pv k ])) ]
~exact:(Global_flow.exact_call st.flow_info f 1)
~in_cps:true
~check:true
~f
[ arg; k' ])
| Prim (Extern "%perform", [ Pv effect_ ]) ->
perform_effect ~effect_ ~continuation:(Pc (Int Targetint.zero))
| Prim (Extern "%reperform", [ Pv effect_; continuation ]) ->
perform_effect ~effect_ ~continuation
| Prim (Extern "%perform", [ Pv effect_ ]) -> perform_effect ~effect_ None
| Prim (Extern "%reperform", [ Pv effect_; continuation; tail ]) ->
perform_effect ~effect_ (Some (continuation, tail))
| _ -> None
in

Expand Down
26 changes: 16 additions & 10 deletions compiler/lib/parse_bytecode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2368,7 +2368,6 @@ and compile infos pc state (instrs : instr list) =
let func = State.peek 0 state in
let arg = State.peek 1 state in
let x, state = State.fresh_var state in

if debug_parser ()
then
Format.printf
Expand All @@ -2381,23 +2380,30 @@ and compile infos pc state (instrs : instr list) =
func
Var.print
arg;
let state =
let state, tail =
match Ocaml_version.compare Ocaml_version.current [ 5; 2 ] < 0 with
| true -> State.pop 2 state
| false -> State.pop 3 state
| true -> State.pop 2 state, Pc (Int (Targetint.of_int_exn 0))
| false ->
let tail = State.peek 2 state in
State.pop 3 state, Pv tail
in

compile
infos
(pc + 1)
state
(Let (x, Prim (Extern "%resume", [ Pv stack; Pv func; Pv arg ])) :: instrs)
(Let (x, Prim (Extern "%resume", [ Pv stack; Pv func; Pv arg; tail ])) :: instrs)
| RESUMETERM ->
let stack = State.accu state in
let func = State.peek 0 state in
let arg = State.peek 1 state in
let x, state = State.fresh_var state in

let tail =
match Ocaml_version.compare Ocaml_version.current [ 5; 2 ] < 0 with
| true -> Pc (Int (Targetint.of_int_exn 0))
| false ->
let tail = State.peek 2 state in
Pv tail
in
if debug_parser ()
then
Format.printf
Expand All @@ -2408,7 +2414,7 @@ and compile infos pc state (instrs : instr list) =
func
Var.print
arg;
( Let (x, Prim (Extern "%resume", [ Pv stack; Pv func; Pv arg ])) :: instrs
( Let (x, Prim (Extern "%resume", [ Pv stack; Pv func; Pv arg; tail ])) :: instrs
, Return x
, state )
| PERFORM ->
Expand All @@ -2425,13 +2431,13 @@ and compile infos pc state (instrs : instr list) =
| REPERFORMTERM ->
let eff = State.accu state in
let stack = State.peek 0 state in
(* We don't need [State.peek 1 state] *)
let tail = State.peek 1 state in
let state = State.pop 2 state in
let x, state = State.fresh_var state in

if debug_parser ()
then Format.printf "return reperform(%a, %a)@." Var.print eff Var.print stack;
( Let (x, Prim (Extern "%reperform", [ Pv eff; Pv stack ])) :: instrs
( Let (x, Prim (Extern "%reperform", [ Pv eff; Pv stack; Pv tail ])) :: instrs
, Return x
, state )
| EVENT | BREAK | FIRST_UNIMPLEMENTED_OP -> assert false)
Expand Down
3 changes: 0 additions & 3 deletions compiler/tests-check-prim/main.4.14.output
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ caml_bigstring_blit_string_to_ba
caml_bigstring_memcmp
caml_hash_mix_bigstring

From +effect.js:
jsoo_effect_not_supported

From +fs.js:
caml_ba_map_file
caml_ba_map_file_bytecode
Expand Down
3 changes: 0 additions & 3 deletions compiler/tests-check-prim/unix-Unix.4.14.output
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ caml_bigstring_blit_string_to_ba
caml_bigstring_memcmp
caml_hash_mix_bigstring

From +effect.js:
jsoo_effect_not_supported

From +fs.js:
caml_ba_map_file
caml_ba_map_file_bytecode
Expand Down
3 changes: 0 additions & 3 deletions compiler/tests-check-prim/unix-Win32.4.14.output
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ caml_bigstring_blit_string_to_ba
caml_bigstring_memcmp
caml_hash_mix_bigstring

From +effect.js:
jsoo_effect_not_supported

From +fs.js:
caml_ba_map_file
caml_ba_map_file_bytecode
Expand Down
35 changes: 22 additions & 13 deletions compiler/tests-full/stdlib.cma.expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -36136,8 +36136,8 @@
/*<<effect.ml:28:11>>*/ return 0;
var
x = /*<<effect.ml:22:16>>*/ param[2],
_i_ = /*<<effect.ml:25:12>>*/ caml_call1(Stdlib_Printexc[26], x),
msg = /*<<effect.ml:24:18>>*/ caml_call2(Stdlib_Printf[4], _a_, _i_);
_o_ = /*<<effect.ml:25:12>>*/ caml_call1(Stdlib_Printexc[26], x),
msg = /*<<effect.ml:24:18>>*/ caml_call2(Stdlib_Printf[4], _a_, _o_);
/*<<effect.ml:27:8>>*/ return [0, msg];
/*<<effect.ml:28:15>>*/ }
/*<<effect.ml:30:2>>*/ caml_call1(Stdlib_Printexc[9], printer);
Expand All @@ -36155,22 +36155,28 @@
"Effect.Continuation_already_resumed",
Continuation_already_resumed);
function continue$0(k, v){
var _h_ = /*<<effect.ml:62:11>>*/ caml_continuation_use_noexc(k);
function _g_(x){
var
_l_ = /*<<effect.ml:62:11>>*/ k[2],
_n_ = caml_continuation_use_noexc(k);
function _m_(x){
/*<<effect.ml:62:41>>*/ return x;
/*<<effect.ml:62:42>>*/ }
/*<<effect.ml:62:30>>*/ return jsoo_effect_not_supported() /*<<effect.ml:62:65>>*/ ;
}
function discontinue(k, e){
var _f_ = /*<<effect.ml:65:11>>*/ caml_continuation_use_noexc(k);
function _e_(e){
var
_i_ = /*<<effect.ml:65:11>>*/ k[2],
_k_ = caml_continuation_use_noexc(k);
function _j_(e){
/*<<effect.ml:65:41>>*/ throw caml_maybe_attach_backtrace(e, 1);
/*<<effect.ml:65:48>>*/ }
/*<<effect.ml:65:30>>*/ return jsoo_effect_not_supported() /*<<effect.ml:65:71>>*/ ;
}
function discontinue_with_backtrace(k, e, bt){
var _d_ = /*<<effect.ml:68:11>>*/ caml_continuation_use_noexc(k);
function _c_(e){
var
_f_ = /*<<effect.ml:68:11>>*/ k[2],
_h_ = caml_continuation_use_noexc(k);
function _g_(e){
/*<<effect.ml:68:41>>*/ caml_restore_raw_backtrace(e, bt);
throw caml_maybe_attach_backtrace(e, 0);
/*<<effect.ml:68:75>>*/ }
Expand All @@ -36187,8 +36193,9 @@
}
var
s =
/*<<effect.ml:87:12>>*/ caml_alloc_stack(handler[1], handler[2], effc);
/*<<effect.ml:88:4>>*/ return jsoo_effect_not_supported() /*<<effect.ml:88:23>>*/ ;
/*<<effect.ml:87:12>>*/ caml_alloc_stack(handler[1], handler[2], effc),
_e_ = /*<<effect.ml:88:4>>*/ 0;
return jsoo_effect_not_supported() /*<<effect.ml:88:23>>*/ ;
}
function try_with(comp, arg, handler){
function effc(eff, k, last_fiber){
Expand All @@ -36208,8 +36215,9 @@
function(e){
/*<<effect.ml:101:47>>*/ throw caml_maybe_attach_backtrace(e, 1);
/*<<effect.ml:101:54>>*/ },
effc);
/*<<effect.ml:102:4>>*/ return jsoo_effect_not_supported() /*<<effect.ml:102:23>>*/ ;
effc),
_d_ = /*<<effect.ml:102:4>>*/ 0;
return jsoo_effect_not_supported() /*<<effect.ml:102:23>>*/ ;
}
var
Deep =
Expand Down Expand Up @@ -36243,7 +36251,7 @@
var s = /*<<effect.ml:135:12>>*/ caml_alloc_stack(error, error, effc);
/*<<effect.ml:136:4>>*/ try{
/*<<effect.ml:136:10>>*/ jsoo_effect_not_supported();
var _b_ = /*<<effect.ml:136:26>>*/ 0;
var _b_ = /*<<effect.ml:136:26>>*/ 0, _c_ = 0;
}
catch(exn$0){
var exn = /*<<?>>*/ caml_wrap_exception(exn$0);
Expand All @@ -36263,6 +36271,7 @@
/*<<effect.ml:160:10>>*/ return caml_call1(f, k) /*<<effect.ml:161:42>>*/ ;
}
var
last_fiber = /*<<effect.ml:163:4>>*/ k[2],
stack =
/*<<effect.ml:164:16>>*/ runtime.caml_continuation_use_and_update_handler_noexc
(k, handler[1], handler[2], effc);
Expand Down
73 changes: 73 additions & 0 deletions compiler/tests-jsoo/lib-effects/deep_state.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
(* deep_state.ml *)

open Effect
open Effect.Shallow

module type State = sig
type a

type _ Effect.t += Get : a Effect.t

type _ Effect.t += Set : a -> unit Effect.t
end

module Make (S : State) = struct
let rec loop : type x y. S.a -> (x, y) continuation -> x -> y =
fun s k x ->
continue_with
k
x
{ retc = (fun y -> y)
; exnc = raise
; effc =
(fun (type b) (e : b Effect.t) ->
match e with
| S.Get -> Some (fun (k : (b, _) continuation) -> loop s k s)
| S.Set s -> Some (fun (k : (b, _) continuation) -> loop s k ())
| _ -> None)
}

let handle (s : S.a) (f : unit -> 'a) : 'a = loop s (fiber f) ()

let get () = perform S.Get

let set v = perform (S.Set v)
end

module IntState = struct
type a = int

type _ Effect.t += Get : int Effect.t

type _ Effect.t += Set : int -> unit Effect.t
end

module StringState = struct
type a = string

type _ Effect.t += Get : string Effect.t

type _ Effect.t += Set : string -> unit Effect.t
end

let main () =
let depth = int_of_string Sys.argv.(1) in
let ops = int_of_string Sys.argv.(2) in
Printf.printf "Running deepstate: depth=%d ops=%d\n" depth ops;
let module SS = Make (StringState) in
let rec setup_deep_state n () =
if n = 0
then
for _ = 1 to ops do
(* SS.set (SS.get () ^ "_" ^ (string_of_int i)) *)
SS.set (SS.get ())
done
(* print_endline @@ SS.get() *)
else
let module IS = Make (IntState) in
IS.handle 0 @@ setup_deep_state (n - 1)
in

SS.handle "Hello, world!" @@ setup_deep_state depth

let _ = main ()
9 changes: 9 additions & 0 deletions compiler/tests-jsoo/lib-effects/dune
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@

(library
(name jsoo_testsuite_effect)
(modules
(:standard \ deep_state))
(enabled_if
(>= %{ocaml_version} 5))
(inline_tests
(modes js wasm best))
(preprocess
(pps ppx_expect)))

(executable
(name deep_state)
(enabled_if
(>= %{ocaml_version} 5))
(modules deep_state)
(modes js))
Loading
Loading