Skip to content

Commit c523cbf

Browse files
committed
Implement exceptions by returning null
1 parent b607422 commit c523cbf

File tree

7 files changed

+179
-71
lines changed

7 files changed

+179
-71
lines changed

compiler/lib-wasm/curry.ml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,19 +228,24 @@ module Make (Target : Target_sig.S) = struct
228228
match l with
229229
| [] ->
230230
let* y = y in
231-
instr (Push y)
231+
instr (Return (Some y))
232232
| x :: rem ->
233233
let* x = load x in
234-
build_applies (call ~cps:false ~arity:1 y [ x ]) rem
234+
let* c = call ~cps:false ~arity:1 y [ x ] in
235+
build_applies (return (W.Br_on_null (0, c))) rem
235236
in
236237
build_applies (load f) l)
237238
in
239+
let body =
240+
let* () = block { params = []; result = [] } body in
241+
instr (Return (Some (RefNull Any)))
242+
in
238243
let param_names = l @ [ f ] in
239244
let locals, body = function_body ~context ~param_names ~body in
240245
W.Function
241246
{ name
242247
; exported_name = None
243-
; typ = Type.primitive_type (arity + 1)
248+
; typ = Type.func_type arity
244249
; param_names
245250
; locals
246251
; body

compiler/lib-wasm/gc_target.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ let include_closure_arity = false
2727
module Type = struct
2828
let value = W.Ref { nullable = false; typ = Eq }
2929

30+
let value_or_exn = W.Ref { nullable = true; typ = Eq }
31+
3032
let block_type =
3133
register_type "block" (fun () ->
3234
return
@@ -205,7 +207,8 @@ module Type = struct
205207
let primitive_type n =
206208
{ W.params = List.init ~len:n ~f:(fun _ -> value); result = [ value ] }
207209

208-
let func_type n = primitive_type (n + 1)
210+
let func_type n =
211+
{ W.params = List.init ~len:(n + 1) ~f:(fun _ -> value); result = [ value_or_exn ] }
209212

210213
let function_type ~cps n =
211214
let n = if cps then n + 1 else n in

compiler/lib-wasm/generate.ml

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ module Generate (Target : Target_sig.S) = struct
183183

184184
let zero_divide_pc = -2
185185

186+
let exception_handler_pc = -3
187+
186188
let rec translate_expr ctx context x e =
187189
match e with
188190
| Apply { f; args; exact }
@@ -200,17 +202,21 @@ module Generate (Target : Target_sig.S) = struct
200202
(load funct)
201203
in
202204
let* b = is_closure f in
205+
let label = label_index context exception_handler_pc in
203206
if b
204-
then return (W.Call (f, List.rev (closure :: acc)))
207+
then return (W.Br_on_null (label, W.Call (f, List.rev (closure :: acc))))
205208
else
206209
match funct with
207210
| W.RefFunc g ->
208211
(* Functions with constant closures ignore their
209212
environment. In case of partial application, we
210213
still need the closure. *)
211214
let* cl = if exact then Value.unit else return closure in
212-
return (W.Call (g, List.rev (cl :: acc)))
213-
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
215+
return (W.Br_on_null (label, W.Call (g, List.rev (cl :: acc))))
216+
| _ ->
217+
return
218+
(W.Br_on_null
219+
(label, W.Call_ref (ty, funct, List.rev (closure :: acc)))))
214220
| x :: r ->
215221
let* x = load x in
216222
loop (x :: acc) r
@@ -222,7 +228,9 @@ module Generate (Target : Target_sig.S) = struct
222228
in
223229
let* args = expression_list load args in
224230
let* closure = load f in
225-
return (W.Call (apply, args @ [ closure ]))
231+
return
232+
(W.Br_on_null
233+
(label_index context exception_handler_pc, W.Call (apply, args @ [ closure ])))
226234
| Block (tag, a, _, _) ->
227235
Memory.allocate
228236
~deadcode_sentinal:ctx.deadcode_sentinal
@@ -824,32 +832,55 @@ module Generate (Target : Target_sig.S) = struct
824832
{ params = []; result = [] }
825833
(body ~result_typ:[] ~fall_through:(`Block pc) ~context:(`Block pc :: context))
826834
in
827-
if List.is_empty result_typ
835+
if true && List.is_empty result_typ
828836
then handler
829837
else
830838
let* () = handler in
831-
instr (W.Return (Some (RefI31 (Const (I32 0l)))))
839+
let* u = Value.unit in
840+
instr (W.Return (Some u))
832841
else body ~result_typ ~fall_through ~context
833842

834-
let wrap_with_handlers p pc ~result_typ ~fall_through ~context body =
843+
let wrap_with_handlers ~location p pc ~result_typ ~fall_through ~context body =
835844
let need_zero_divide_handler, need_bound_error_handler = needed_handlers p pc in
836845
wrap_with_handler
837-
need_bound_error_handler
838-
bound_error_pc
839-
(let* f =
840-
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
841-
in
842-
instr (CallInstr (f, [])))
846+
true
847+
exception_handler_pc
848+
(match location with
849+
| `Toplevel ->
850+
let* exn =
851+
register_import
852+
~import_module:"env"
853+
~name:"caml_exception"
854+
(Global { mut = true; typ = Type.value })
855+
in
856+
let* tag = register_import ~name:exception_name (Tag Type.value) in
857+
instr (Throw (tag, GlobalGet exn))
858+
| `Exception_handler ->
859+
let* exn =
860+
register_import
861+
~import_module:"env"
862+
~name:"caml_exception"
863+
(Global { mut = true; typ = Type.value })
864+
in
865+
instr (Br (2, Some (GlobalGet exn)))
866+
| `Function -> instr (Return (Some (RefNull Any))))
843867
(wrap_with_handler
844-
need_zero_divide_handler
845-
zero_divide_pc
868+
need_bound_error_handler
869+
bound_error_pc
846870
(let* f =
847-
register_import
848-
~name:"caml_raise_zero_divide"
849-
(Fun { params = []; result = [] })
871+
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
850872
in
851873
instr (CallInstr (f, [])))
852-
body)
874+
(wrap_with_handler
875+
need_zero_divide_handler
876+
zero_divide_pc
877+
(let* f =
878+
register_import
879+
~name:"caml_raise_zero_divide"
880+
(Fun { params = []; result = [] })
881+
in
882+
instr (CallInstr (f, [])))
883+
body))
853884
~result_typ
854885
~fall_through
855886
~context
@@ -950,19 +981,34 @@ module Generate (Target : Target_sig.S) = struct
950981
instr (Br_table (e, List.map ~f:dest l, dest a.(len - 1)))
951982
| Raise (x, _) -> (
952983
let* e = load x in
953-
let* tag = register_import ~name:exception_name (Tag Type.value) in
954984
match fall_through with
955985
| `Catch -> instr (Push e)
956986
| `Block _ | `Return | `Skip -> (
957987
match catch_index context with
958988
| Some i -> instr (Br (i, Some e))
959-
| None -> instr (Throw (tag, e))))
989+
| None ->
990+
if Option.is_some name_opt
991+
then
992+
let* exn =
993+
register_import
994+
~import_module:"env"
995+
~name:"caml_exception"
996+
(Global { mut = true; typ = Type.value })
997+
in
998+
let* () = instr (GlobalSet (exn, e)) in
999+
instr (Return (Some (RefNull Any)))
1000+
else
1001+
let* tag =
1002+
register_import ~name:exception_name (Tag Type.value)
1003+
in
1004+
instr (Throw (tag, e))))
9601005
| Pushtrap (cont, x, cont') ->
9611006
handle_exceptions
9621007
~result_typ
9631008
~fall_through
9641009
~context:(extend_context fall_through context)
9651010
(wrap_with_handlers
1011+
~location:`Exception_handler
9661012
p
9671013
(fst cont)
9681014
(fun ~result_typ ~fall_through ~context ->
@@ -1033,6 +1079,10 @@ module Generate (Target : Target_sig.S) = struct
10331079
let* () = build_initial_env in
10341080
let* () =
10351081
wrap_with_handlers
1082+
~location:
1083+
(match name_opt with
1084+
| None -> `Toplevel
1085+
| Some _ -> `Function)
10361086
p
10371087
pc
10381088
~result_typ:[ Type.value ]
@@ -1081,7 +1131,9 @@ module Generate (Target : Target_sig.S) = struct
10811131
in
10821132
let* () = instr (Drop (Call (f, []))) in
10831133
cont)
1084-
~init:(instr (Push (RefI31 (Const (I32 0l)))))
1134+
~init:
1135+
(let* u = Value.unit in
1136+
instr (Push u))
10851137
to_link)
10861138
in
10871139
context.other_fields <-

compiler/lib-wasm/tail_call.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ let rewrite_tail_call ~y i =
3030
Some (Wasm_ast.Return_call (symb, l))
3131
| LocalSet (x, Call_ref (ty, e, l)) when Code.Var.equal x y ->
3232
Some (Return_call_ref (ty, e, l))
33+
| LocalSet (x, Br_on_null (_, Call (symb, l))) when Code.Var.equal x y ->
34+
Some (Wasm_ast.Return_call (symb, l))
35+
| LocalSet (x, Br_on_null (_, Call_ref (ty, e, l))) when Code.Var.equal x y ->
36+
Some (Return_call_ref (ty, e, l))
3337
| _ -> None
3438

3539
let rec instruction ~tail i =
@@ -42,6 +46,11 @@ let rec instruction ~tail i =
4246
| Push (Call (symb, l)) when tail -> Return_call (symb, l)
4347
| Push (Call_ref (ty, e, l)) when tail -> Return_call_ref (ty, e, l)
4448
| Push (Call_ref _) -> i
49+
| Return (Some (Br_on_null (_, Call (symb, l)))) -> Return_call (symb, l)
50+
| Return (Some (Br_on_null (_, Call_ref (ty, e, l)))) -> Return_call_ref (ty, e, l)
51+
| Push (Br_on_null (_, Call (symb, l))) when tail -> Return_call (symb, l)
52+
| Push (Br_on_null (_, Call_ref (ty, e, l))) when tail -> Return_call_ref (ty, e, l)
53+
| Push (Br_on_null (_, Call_ref _)) -> i
4554
| Drop (BlockExpr (typ, l)) -> Drop (BlockExpr (typ, instructions ~tail:false l))
4655
| Drop _
4756
| LocalSet _

0 commit comments

Comments
 (0)