Skip to content

Commit 6846b08

Browse files
committed
Implement exceptions by returning null
1 parent 824efea commit 6846b08

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
@@ -782,6 +782,8 @@ module Generate (Target : Target_sig.S) = struct
782782
in
783783
Memory.allocate ~tag:0 ~deadcode_sentinal:ctx.deadcode_sentinal ~load l)
784784

785+
let exception_handler_pc = -3
786+
785787
let rec translate_expr ctx context x e =
786788
match e with
787789
| Apply { f; args; exact }
@@ -799,17 +801,21 @@ module Generate (Target : Target_sig.S) = struct
799801
(load funct)
800802
in
801803
let* b = is_closure f in
804+
let label = label_index context exception_handler_pc in
802805
if b
803-
then return (W.Call (f, List.rev (closure :: acc)))
806+
then return (W.Br_on_null (label, W.Call (f, List.rev (closure :: acc))))
804807
else
805808
match funct with
806809
| W.RefFunc g ->
807810
(* Functions with constant closures ignore their
808811
environment. In case of partial application, we
809812
still need the closure. *)
810813
let* cl = if exact then Value.unit else return closure in
811-
return (W.Call (g, List.rev (cl :: acc)))
812-
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
814+
return (W.Br_on_null (label, W.Call (g, List.rev (cl :: acc))))
815+
| _ ->
816+
return
817+
(W.Br_on_null
818+
(label, W.Call_ref (ty, funct, List.rev (closure :: acc)))))
813819
| x :: r ->
814820
let* x = load_and_box ctx x in
815821
loop (x :: acc) r
@@ -821,7 +827,9 @@ module Generate (Target : Target_sig.S) = struct
821827
in
822828
let* args = expression_list (fun x -> load_and_box ctx x) args in
823829
let* closure = load f in
824-
return (W.Call (apply, args @ [ closure ]))
830+
return
831+
(W.Br_on_null
832+
(label_index context exception_handler_pc, W.Call (apply, args @ [ closure ])))
825833
| Block (tag, a, _, _) ->
826834
Memory.allocate
827835
~deadcode_sentinal:ctx.deadcode_sentinal
@@ -1075,32 +1083,55 @@ module Generate (Target : Target_sig.S) = struct
10751083
{ params = []; result = [] }
10761084
(body ~result_typ:[] ~fall_through:(`Block pc) ~context:(`Block pc :: context))
10771085
in
1078-
if List.is_empty result_typ
1086+
if true && List.is_empty result_typ
10791087
then handler
10801088
else
10811089
let* () = handler in
1082-
instr (W.Return (Some (RefI31 (Const (I32 0l)))))
1090+
let* u = Value.unit in
1091+
instr (W.Return (Some u))
10831092
else body ~result_typ ~fall_through ~context
10841093

1085-
let wrap_with_handlers p pc ~result_typ ~fall_through ~context body =
1094+
let wrap_with_handlers ~location p pc ~result_typ ~fall_through ~context body =
10861095
let need_zero_divide_handler, need_bound_error_handler = needed_handlers p pc in
10871096
wrap_with_handler
1088-
need_bound_error_handler
1089-
bound_error_pc
1090-
(let* f =
1091-
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
1092-
in
1093-
instr (CallInstr (f, [])))
1097+
true
1098+
exception_handler_pc
1099+
(match location with
1100+
| `Toplevel ->
1101+
let* exn =
1102+
register_import
1103+
~import_module:"env"
1104+
~name:"caml_exception"
1105+
(Global { mut = true; typ = Type.value })
1106+
in
1107+
let* tag = register_import ~name:exception_name (Tag Type.value) in
1108+
instr (Throw (tag, GlobalGet exn))
1109+
| `Exception_handler ->
1110+
let* exn =
1111+
register_import
1112+
~import_module:"env"
1113+
~name:"caml_exception"
1114+
(Global { mut = true; typ = Type.value })
1115+
in
1116+
instr (Br (2, Some (GlobalGet exn)))
1117+
| `Function -> instr (Return (Some (RefNull Any))))
10941118
(wrap_with_handler
1095-
need_zero_divide_handler
1096-
zero_divide_pc
1119+
need_bound_error_handler
1120+
bound_error_pc
10971121
(let* f =
1098-
register_import
1099-
~name:"caml_raise_zero_divide"
1100-
(Fun { params = []; result = [] })
1122+
register_import ~name:"caml_bound_error" (Fun { params = []; result = [] })
11011123
in
11021124
instr (CallInstr (f, [])))
1103-
body)
1125+
(wrap_with_handler
1126+
need_zero_divide_handler
1127+
zero_divide_pc
1128+
(let* f =
1129+
register_import
1130+
~name:"caml_raise_zero_divide"
1131+
(Fun { params = []; result = [] })
1132+
in
1133+
instr (CallInstr (f, [])))
1134+
body))
11041135
~result_typ
11051136
~fall_through
11061137
~context
@@ -1208,19 +1239,34 @@ module Generate (Target : Target_sig.S) = struct
12081239
instr (Br_table (e, List.map ~f:dest l, dest a.(len - 1)))
12091240
| Raise (x, _) -> (
12101241
let* e = load x in
1211-
let* tag = register_import ~name:exception_name (Tag Type.value) in
12121242
match fall_through with
12131243
| `Catch -> instr (Push e)
12141244
| `Block _ | `Return | `Skip -> (
12151245
match catch_index context with
12161246
| Some i -> instr (Br (i, Some e))
1217-
| None -> instr (Throw (tag, e))))
1247+
| None ->
1248+
if Option.is_some name_opt
1249+
then
1250+
let* exn =
1251+
register_import
1252+
~import_module:"env"
1253+
~name:"caml_exception"
1254+
(Global { mut = true; typ = Type.value })
1255+
in
1256+
let* () = instr (GlobalSet (exn, e)) in
1257+
instr (Return (Some (RefNull Any)))
1258+
else
1259+
let* tag =
1260+
register_import ~name:exception_name (Tag Type.value)
1261+
in
1262+
instr (Throw (tag, e))))
12181263
| Pushtrap (cont, x, cont') ->
12191264
handle_exceptions
12201265
~result_typ
12211266
~fall_through
12221267
~context:(extend_context fall_through context)
12231268
(wrap_with_handlers
1269+
~location:`Exception_handler
12241270
p
12251271
(fst cont)
12261272
(fun ~result_typ ~fall_through ~context ->
@@ -1291,6 +1337,10 @@ module Generate (Target : Target_sig.S) = struct
12911337
let* () = build_initial_env in
12921338
let* () =
12931339
wrap_with_handlers
1340+
~location:
1341+
(match name_opt with
1342+
| None -> `Toplevel
1343+
| Some _ -> `Function)
12941344
p
12951345
pc
12961346
~result_typ:[ Type.value ]
@@ -1342,7 +1392,9 @@ module Generate (Target : Target_sig.S) = struct
13421392
in
13431393
let* () = instr (Drop (Call (f, []))) in
13441394
cont)
1345-
~init:(instr (Push (RefI31 (Const (I32 0l)))))
1395+
~init:
1396+
(let* u = Value.unit in
1397+
instr (Push u))
13461398
to_link)
13471399
in
13481400
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)