diff --git a/compiler/lib/wasm/wa_asm_output.ml b/compiler/lib/wasm/wa_asm_output.ml index 76cb73ad8..42dfc32d1 100644 --- a/compiler/lib/wasm/wa_asm_output.ml +++ b/compiler/lib/wasm/wa_asm_output.ml @@ -301,6 +301,13 @@ module Output () = struct | MemoryGrow (mem, e) -> expression e ^^ line (string "memory.grow " ^^ integer mem) | Seq (l, e') -> concat_map instruction l ^^ expression e' | Pop _ -> empty + | IfExpr (ty, e, e1, e2) -> + expression e + ^^ line (string "if" ^^ block_type { params = []; result = [ ty ] }) + ^^ indent (expression e1) + ^^ line (string "else") + ^^ indent (expression e2) + ^^ line (string "end_if") | RefFunc _ | Call_ref _ | RefI31 _ diff --git a/compiler/lib/wasm/wa_ast.ml b/compiler/lib/wasm/wa_ast.ml index 193e74482..a5641e716 100644 --- a/compiler/lib/wasm/wa_ast.ml +++ b/compiler/lib/wasm/wa_ast.ml @@ -157,6 +157,7 @@ type expression = | ExternExternalize of expression | Br_on_cast of int * ref_type * ref_type * expression | Br_on_cast_fail of int * ref_type * ref_type * expression + | IfExpr of value_type * expression * expression * expression and instruction = | Drop of expression diff --git a/compiler/lib/wasm/wa_code_generation.ml b/compiler/lib/wasm/wa_code_generation.ml index 9fcdc5ec2..ab8a10849 100644 --- a/compiler/lib/wasm/wa_code_generation.ml +++ b/compiler/lib/wasm/wa_code_generation.ml @@ -347,11 +347,13 @@ let bin_op_is_smi (op : W.int_bin_op) = false | Eq | Ne | Lt _ | Gt _ | Le _ | Ge _ -> true -let is_smi e = +let rec is_smi e = match e with | W.Const (I32 i) -> Int32.equal (Int31.wrap i) i | UnOp ((I32 op | I64 op), _) -> un_op_is_smi op | BinOp ((I32 op | I64 op), _, _) -> bin_op_is_smi op + | I31Get (S, _) -> true + | I31Get (U, _) | Const (I64 _ | F32 _ | F64 _) | ConstSym _ | UnOp ((F32 _ | F64 _), _) @@ -373,7 +375,6 @@ let is_smi e = | RefFunc _ | Call_ref _ | RefI31 _ - | I31Get _ | ArrayNew _ | ArrayNewFixed _ | ArrayNewData _ @@ -388,6 +389,7 @@ let is_smi e = | Br_on_cast _ | Br_on_cast_fail _ -> false | BinOp ((F32 _ | F64 _), _, _) | RefTest _ | RefEq _ -> true + | IfExpr (_, _, ift, iff) -> is_smi ift && is_smi iff let get_i31_value x st = match st.instrs with diff --git a/compiler/lib/wasm/wa_gc_target.ml b/compiler/lib/wasm/wa_gc_target.ml index ec98462cc..60e07927b 100644 --- a/compiler/lib/wasm/wa_gc_target.ml +++ b/compiler/lib/wasm/wa_gc_target.ml @@ -435,15 +435,72 @@ module Value = struct let le = binop Arith.( <= ) - let eq i i' = + let ref_eq i i' = let* i = i in let* i' = i' in - val_int (return (W.RefEq (i, i'))) + return (W.RefEq (i, i')) - let neq i i' = - let* i = i in - let* i' = i' in - val_int (Arith.eqz (return (W.RefEq (i, i')))) + let ref ty = + { W.nullable = false; typ = Type ty } + + let ref_test typ e = + let* e = e in + return (W.RefTest (typ, e)) + + let caml_js_strict_equals x y = + let* x = x in + let* y = y in + let* f = + register_import + ~name:"caml_js_strict_equals" + ~import_module:"env" + (Fun { params = [ Type.value; Type.value ]; result = [ Type.value ] }) + in + return (W.Call (f, [ x; y ])) + + let if_expr ty cond ift iff = + let* cond = cond in + let* ift = ift in + let* iff = iff in + return (W.IfExpr (ty, cond, ift, iff)) + + let map f x = + let* x = x in + return (f x) + + let (>>|) x f = map f x + + let eq_gen ~negate x y = + let xv = Code.Var.fresh () in + let yv = Code.Var.fresh () in + let* js = Type.js_type in + let n = + if_expr + I32 + (* We mimic an "and" on the two conditions, but in a way that is nicer to the + binaryen optimizer. *) + (if_expr + I32 + (ref_test (ref js) (load xv)) + (ref_test (ref js) (load yv)) + (Arith.const 0l)) + (caml_js_strict_equals (load xv) (load yv) + >>| (fun e -> W.RefCast ({ nullable = false; typ = I31 }, e)) + >>| (fun e -> W.I31Get (S, e))) + (ref_eq (load xv) (load yv)) + in + seq + (let* () = store xv x in + let* () = store yv y in + return ()) + (val_int (if negate then Arith.eqz n else n)) + + + let eq x y = + eq_gen ~negate:false x y + + let neq x y = + eq_gen ~negate:true x y let ult = binop Arith.(ult) diff --git a/compiler/lib/wasm/wa_initialize_locals.ml b/compiler/lib/wasm/wa_initialize_locals.ml index 0f0931135..969a6fd23 100644 --- a/compiler/lib/wasm/wa_initialize_locals.ml +++ b/compiler/lib/wasm/wa_initialize_locals.ml @@ -51,6 +51,10 @@ let rec scan_expression ctx e = | Call (_, l) | ArrayNewFixed (_, l) | StructNew (_, l) -> scan_expressions ctx l | BlockExpr (_, l) -> scan_instructions ctx l | Seq (l, e') -> scan_instructions ctx (l @ [ Push e' ]) + | IfExpr (_, cond, e1, e2) -> + scan_expression ctx cond; + scan_expression (fork_context ctx) e1; + scan_expression (fork_context ctx) e2 and scan_expressions ctx l = List.iter ~f:(fun e -> scan_expression ctx e) l diff --git a/compiler/lib/wasm/wa_wat_output.ml b/compiler/lib/wasm/wa_wat_output.ml index 9c9e07579..7253974f1 100644 --- a/compiler/lib/wasm/wa_wat_output.ml +++ b/compiler/lib/wasm/wa_wat_output.ml @@ -331,6 +331,14 @@ let expression_or_instructions ctx in_function = ] | ExternInternalize e -> [ List (Atom "extern.internalize" :: expression e) ] | ExternExternalize e -> [ List (Atom "extern.externalize" :: expression e) ] + | IfExpr (ty, cond, ift, iff) -> + [ List + (Atom "if" + :: (block_type { params = []; result = [ ty ] }) + @ expression cond + @ [ List (Atom "then" :: expression ift) ] + @ [ List (Atom "else" :: expression iff) ]) + ] and instruction i = match i with | Drop e -> [ List (Atom "drop" :: expression e) ]