Skip to content

Commit 2aff70f

Browse files
committed
Implement exceptions by returning null
1 parent c2414b3 commit 2aff70f

File tree

7 files changed

+176
-69
lines changed

7 files changed

+176
-69
lines changed

compiler/lib-wasm/curry.ml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,20 +256,25 @@ module Make (Target : Target_sig.S) = struct
256256
match l with
257257
| [] ->
258258
let* y = y in
259-
instr (Push y)
259+
instr (Return (Some y))
260260
| x :: rem ->
261261
let* x = load x in
262-
build_applies (call ~cps:false ~arity:1 y [ x ]) rem
262+
let* c = call ~cps:false ~arity:1 y [ x ] in
263+
build_applies (return (W.Br_on_null (0, c))) rem
263264
in
264265
build_applies (load f) l)
265266
in
267+
let body =
268+
let* () = block { params = []; result = [] } body in
269+
instr (Return (Some (RefNull Any)))
270+
in
266271
let param_names = l @ [ f ] in
267272
let locals, body = function_body ~context ~param_names ~body in
268273
W.Function
269274
{ name
270275
; exported_name = None
271276
; typ = None
272-
; signature = Type.primitive_type (arity + 1)
277+
; signature = Type.func_type arity
273278
; param_names
274279
; locals
275280
; 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
@@ -604,6 +604,8 @@ module Generate (Target : Target_sig.S) = struct
604604
in
605605
Memory.allocate ~tag:0 ~deadcode_sentinal:ctx.deadcode_sentinal l)
606606

607+
let exception_handler_pc = -3
608+
607609
let rec translate_expr ctx context x e =
608610
match e with
609611
| Apply { f; args; exact }
@@ -621,17 +623,21 @@ module Generate (Target : Target_sig.S) = struct
621623
(load funct)
622624
in
623625
let* b = is_closure f in
626+
let label = label_index context exception_handler_pc in
624627
if b
625-
then return (W.Call (f, List.rev (closure :: acc)))
628+
then return (W.Br_on_null (label, W.Call (f, List.rev (closure :: acc))))
626629
else
627630
match funct with
628631
| W.RefFunc g ->
629632
(* Functions with constant closures ignore their
630633
environment. In case of partial application, we
631634
still need the closure. *)
632635
let* cl = if exact then Value.unit else return closure in
633-
return (W.Call (g, List.rev (cl :: acc)))
634-
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
636+
return (W.Br_on_null (label, W.Call (g, List.rev (cl :: acc))))
637+
| _ ->
638+
return
639+
(W.Br_on_null
640+
(label, W.Call_ref (ty, funct, List.rev (closure :: acc)))))
635641
| x :: r ->
636642
let* x = load x in
637643
loop (x :: acc) r
@@ -643,7 +649,9 @@ module Generate (Target : Target_sig.S) = struct
643649
in
644650
let* args = expression_list load args in
645651
let* closure = load f in
646-
return (W.Call (apply, args @ [ closure ]))
652+
return
653+
(W.Br_on_null
654+
(label_index context exception_handler_pc, W.Call (apply, args @ [ closure ])))
647655
| Block (tag, a, _, _) ->
648656
Memory.allocate
649657
~deadcode_sentinal:ctx.deadcode_sentinal
@@ -869,32 +877,55 @@ module Generate (Target : Target_sig.S) = struct
869877
{ params = []; result = [] }
870878
(body ~result_typ:[] ~fall_through:(`Block pc) ~context:(`Block pc :: context))
871879
in
872-
if List.is_empty result_typ
880+
if true && List.is_empty result_typ
873881
then handler
874882
else
875883
let* () = handler in
876-
instr (W.Return (Some (RefI31 (Const (I32 0l)))))
884+
let* u = Value.unit in
885+
instr (W.Return (Some u))
877886
else body ~result_typ ~fall_through ~context
878887

879-
let wrap_with_handlers p pc ~result_typ ~fall_through ~context body =
888+
let wrap_with_handlers ~location p pc ~result_typ ~fall_through ~context body =
880889
let need_zero_divide_handler, need_bound_error_handler = needed_handlers p pc in
881890
wrap_with_handler
882-
need_bound_error_handler
883-
bound_error_pc
884-
(let* f =
885-
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
886-
in
887-
instr (CallInstr (f, [])))
891+
true
892+
exception_handler_pc
893+
(match location with
894+
| `Toplevel ->
895+
let* exn =
896+
register_import
897+
~import_module:"env"
898+
~name:"caml_exception"
899+
(Global { mut = true; typ = Type.value })
900+
in
901+
let* tag = register_import ~name:exception_name (Tag Type.value) in
902+
instr (Throw (tag, GlobalGet exn))
903+
| `Exception_handler ->
904+
let* exn =
905+
register_import
906+
~import_module:"env"
907+
~name:"caml_exception"
908+
(Global { mut = true; typ = Type.value })
909+
in
910+
instr (Br (2, Some (GlobalGet exn)))
911+
| `Function -> instr (Return (Some (RefNull Any))))
888912
(wrap_with_handler
889-
need_zero_divide_handler
890-
zero_divide_pc
913+
need_bound_error_handler
914+
bound_error_pc
891915
(let* f =
892-
register_import
893-
~name:"caml_raise_zero_divide"
894-
(Fun { params = []; result = [] })
916+
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
895917
in
896918
instr (CallInstr (f, [])))
897-
body)
919+
(wrap_with_handler
920+
need_zero_divide_handler
921+
zero_divide_pc
922+
(let* f =
923+
register_import
924+
~name:"caml_raise_zero_divide"
925+
(Fun { params = []; result = [] })
926+
in
927+
instr (CallInstr (f, [])))
928+
body))
898929
~result_typ
899930
~fall_through
900931
~context
@@ -996,19 +1027,34 @@ module Generate (Target : Target_sig.S) = struct
9961027
instr (Br_table (e, List.map ~f:dest l, dest a.(len - 1)))
9971028
| Raise (x, _) -> (
9981029
let* e = load x in
999-
let* tag = register_import ~name:exception_name (Tag Type.value) in
10001030
match fall_through with
10011031
| `Catch -> instr (Push e)
10021032
| `Block _ | `Return | `Skip -> (
10031033
match catch_index context with
10041034
| Some i -> instr (Br (i, Some e))
1005-
| None -> instr (Throw (tag, e))))
1035+
| None ->
1036+
if Option.is_some name_opt
1037+
then
1038+
let* exn =
1039+
register_import
1040+
~import_module:"env"
1041+
~name:"caml_exception"
1042+
(Global { mut = true; typ = Type.value })
1043+
in
1044+
let* () = instr (GlobalSet (exn, e)) in
1045+
instr (Return (Some (RefNull Any)))
1046+
else
1047+
let* tag =
1048+
register_import ~name:exception_name (Tag Type.value)
1049+
in
1050+
instr (Throw (tag, e))))
10061051
| Pushtrap (cont, x, cont') ->
10071052
handle_exceptions
10081053
~result_typ
10091054
~fall_through
10101055
~context:(extend_context fall_through context)
10111056
(wrap_with_handlers
1057+
~location:`Exception_handler
10121058
p
10131059
(fst cont)
10141060
(fun ~result_typ ~fall_through ~context ->
@@ -1079,6 +1125,10 @@ module Generate (Target : Target_sig.S) = struct
10791125
let* () = build_initial_env in
10801126
let* () =
10811127
wrap_with_handlers
1128+
~location:
1129+
(match name_opt with
1130+
| None -> `Toplevel
1131+
| Some _ -> `Function)
10821132
p
10831133
pc
10841134
~result_typ:[ Type.value ]
@@ -1130,7 +1180,9 @@ module Generate (Target : Target_sig.S) = struct
11301180
in
11311181
let* () = instr (Drop (Call (f, []))) in
11321182
cont)
1133-
~init:(instr (Push (RefI31 (Const (I32 0l)))))
1183+
~init:
1184+
(let* u = Value.unit in
1185+
instr (Push u))
11341186
to_link)
11351187
in
11361188
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)