From 18e5c62e3f28c72adb37e16d528f99bb865c7fa9 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 22 Mar 2023 08:29:15 +0100 Subject: [PATCH 1/4] Add 2nd type annotations on casts --- interpreter/binary/decode.ml | 21 +-- interpreter/binary/encode.ml | 12 +- interpreter/exec/eval.ml | 12 +- interpreter/syntax/ast.ml | 4 +- interpreter/syntax/free.ml | 3 +- interpreter/syntax/operators.ml | 12 +- interpreter/text/arrange.ml | 17 +-- interpreter/text/lexer.mll | 14 +- interpreter/text/parser.mly | 24 ++-- interpreter/valid/valid.ml | 46 +++--- proposals/gc/MVP.md | 101 ++++++------- test/core/gc/br_on_cast.wast | 120 ++++++++-------- test/core/gc/br_on_cast_fail.wast | 166 +++++++++++----------- test/core/gc/ref_cast.wast | 100 ++++++------- test/core/gc/ref_test.wast | 228 +++++++++++++++--------------- test/core/gc/type-subtyping.wast | 22 +-- 16 files changed, 453 insertions(+), 449 deletions(-) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index 11687069b..c6d39fa54 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -62,6 +62,8 @@ let at f s = (* Generic values *) +let bit i n = n land (1 lsl i) <> 0 + let byte s = get s @@ -608,14 +610,17 @@ let rec instr s = | 0x21l -> i31_get_s | 0x22l -> i31_get_u - | 0x40l -> ref_test (heap_type s) - | 0x41l -> ref_cast (heap_type s) - | 0x42l -> let x = at var s in br_on_cast x (heap_type s) - | 0x43l -> let x = at var s in br_on_cast_fail x (heap_type s) - | 0x48l -> ref_test_null (heap_type s) - | 0x49l -> ref_cast_null (heap_type s) - | 0x4al -> let x = at var s in br_on_cast_null x (heap_type s) - | 0x4bl -> let x = at var s in br_on_cast_fail_null x (heap_type s) + | 0x40l -> ref_test (NoNull, heap_type s) + | 0x41l -> ref_cast (NoNull, heap_type s) + | 0x48l -> ref_test (Null, heap_type s) + | 0x49l -> ref_cast (Null, heap_type s) + | 0x4fl -> + let flags = byte s in + require (flags land 0xf8 = 0) s (pos + 2) "malformed br_on_cast flags"; + let x = at var s in + let rt1 = ((if bit 0 flags then Null else NoNull), heap_type s) in + let rt2 = ((if bit 1 flags then Null else NoNull), heap_type s) in + (if bit 2 flags then br_on_cast_fail else br_on_cast) x rt1 rt2 | 0x70l -> extern_internalize | 0x71l -> extern_externalize diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 93b9845dc..2026efc25 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -40,6 +40,8 @@ struct (* Generic values *) + let bit i b = (if b then 1 else 0) lsl i + let byte i = put s (Char.chr (i land 0xff)) let word16 i = byte (i land 0xff); byte (i lsr 8) let word32 i = @@ -241,10 +243,12 @@ struct | BrTable (xs, x) -> op 0x0e; vec var xs; var x | BrOnNull x -> op 0xd4; var x | BrOnNonNull x -> op 0xd6; var x - | BrOnCast (x, (NoNull, t)) -> op 0xfb; op 0x42; var x; heap_type t - | BrOnCast (x, (Null, t)) -> op 0xfb; op 0x4a; var x; heap_type t - | BrOnCastFail (x, (NoNull, t)) -> op 0xfb; op 0x43; var x; heap_type t - | BrOnCastFail (x, (Null, t)) -> op 0xfb; op 0x4b; var x; heap_type t + | BrOnCast (x, (nul1, t1), (nul2, t2)) -> + let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) + bit 2 false in + op 0xfb; op 0x4f; byte flags; var x; heap_type t1; heap_type t2 + | BrOnCastFail (x, (nul1, t1), (nul2, t2)) -> + let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) + bit 2 true in + op 0xfb; op 0x4f; byte flags; var x; heap_type t1; heap_type t2 | Return -> op 0x0f | Call x -> op 0x10; var x | CallRef x -> op 0x14; var x diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index 4e95592aa..f06c0efb9 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -215,16 +215,16 @@ let rec step (c : config) : config = | BrOnNonNull x, Ref r :: vs' -> Ref r :: vs', [Plain (Br x) @@ e.at] - | BrOnCast (x, rt), Ref r :: vs' -> - let rt' = dyn_ref_type c.frame.inst.types rt in - if Match.match_ref_type [] (type_of_ref r) rt' then + | BrOnCast (x, _rt1, rt2), Ref r :: vs' -> + let rt2' = dyn_ref_type c.frame.inst.types rt2 in + if Match.match_ref_type [] (type_of_ref r) rt2' then Ref r :: vs', [Plain (Br x) @@ e.at] else Ref r :: vs', [] - | BrOnCastFail (x, rt), Ref r :: vs' -> - let rt' = dyn_ref_type c.frame.inst.types rt in - if Match.match_ref_type [] (type_of_ref r) rt' then + | BrOnCastFail (x, _rt1, rt2), Ref r :: vs' -> + let rt2' = dyn_ref_type c.frame.inst.types rt2 in + if Match.match_ref_type [] (type_of_ref r) rt2' then Ref r :: vs', [] else Ref r :: vs', [Plain (Br x) @@ e.at] diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 77cb8f731..2d975cc41 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -154,8 +154,8 @@ and instr' = | BrTable of idx list * idx (* indexed break *) | BrOnNull of idx (* break on type *) | BrOnNonNull of idx (* break on type inverted *) - | BrOnCast of idx * ref_type (* break on type *) - | BrOnCastFail of idx * ref_type (* break on type inverted *) + | BrOnCast of idx * ref_type * ref_type (* break on type *) + | BrOnCastFail of idx * ref_type * ref_type (* break on type inverted *) | Return (* break from function body *) | Call of idx (* call function *) | CallRef of idx (* call function through reference *) diff --git a/interpreter/syntax/free.ml b/interpreter/syntax/free.ml index 5ee8e712c..39a417284 100644 --- a/interpreter/syntax/free.ml +++ b/interpreter/syntax/free.ml @@ -141,7 +141,8 @@ let rec instr (e : instr) = | Block (bt, es) | Loop (bt, es) -> block_type bt ++ block es | If (bt, es1, es2) -> block_type bt ++ block es1 ++ block es2 | Br x | BrIf x | BrOnNull x | BrOnNonNull x -> labels (idx x) - | BrOnCast (x, t) | BrOnCastFail (x, t) -> labels (idx x) ++ ref_type t + | BrOnCast (x, t1, t2) | BrOnCastFail (x, t1, t2) -> + labels (idx x) ++ ref_type t1 ++ ref_type t2 | BrTable (xs, x) -> list (fun x -> labels (idx x)) (x::xs) | Return -> empty | Call x -> funcs (idx x) diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 367389910..09fb16fd6 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -29,10 +29,8 @@ let br_if x = BrIf x let br_table xs x = BrTable (xs, x) let br_on_null x = BrOnNull x let br_on_non_null x = BrOnNonNull x -let br_on_cast x t = BrOnCast (x, (NoNull, t)) -let br_on_cast_null x t = BrOnCast (x, (Null, t)) -let br_on_cast_fail x t = BrOnCastFail (x, (NoNull, t)) -let br_on_cast_fail_null x t = BrOnCastFail (x, (Null, t)) +let br_on_cast x t1 t2 = BrOnCast (x, t1, t2) +let br_on_cast_fail x t1 t2 = BrOnCastFail (x, t1, t2) let return = Return let call x = Call x @@ -104,10 +102,8 @@ let data_drop x = DataDrop x let ref_is_null = RefIsNull let ref_as_non_null = RefAsNonNull -let ref_test t = RefTest (NoNull, t) -let ref_test_null t = RefTest (Null, t) -let ref_cast t = RefCast (NoNull, t) -let ref_cast_null t = RefCast (Null, t) +let ref_test t = RefTest t +let ref_cast t = RefCast t let ref_eq = RefEq let i31_new = I31New diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 980f90677..c300b2903 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -82,17 +82,10 @@ let var_type = function | DynX _ -> assert false | RecX x -> "rec." ^ nat32 x -let null = function - | NoNull -> "" - | Null -> "null " - let final = function | NoFinal -> "" | Final -> " final" -let ref_type_raw (nul, t) = - Atom (null nul ^ heap_type t) - let decls kind ts = tab kind (atom val_type) ts let field_type (FieldT (mut, t)) = @@ -512,8 +505,10 @@ let rec instr e = "br_table " ^ String.concat " " (list var (xs @ [x])), [] | BrOnNull x -> "br_on_null " ^ var x, [] | BrOnNonNull x -> "br_on_non_null " ^ var x, [] - | BrOnCast (x, t) -> "br_on_cast " ^ var x, [ref_type_raw t] - | BrOnCastFail (x, t) -> "br_on_cast_fail " ^ var x, [ref_type_raw t] + | BrOnCast (x, t1, t2) -> + "br_on_cast " ^ var x, [Atom (ref_type t1); Atom (ref_type t2)] + | BrOnCastFail (x, t1, t2) -> + "br_on_cast_fail " ^ var x, [Atom (ref_type t1); Atom (ref_type t2)] | Return -> "return", [] | Call x -> "call " ^ var x, [] | CallRef x -> "call_ref " ^ var x, [] @@ -549,8 +544,8 @@ let rec instr e = | RefFunc x -> "ref.func " ^ var x, [] | RefIsNull -> "ref.is_null", [] | RefAsNonNull -> "ref.as_non_null", [] - | RefTest t -> "ref.test", [ref_type_raw t] - | RefCast t -> "ref.cast", [ref_type_raw t] + | RefTest t -> "ref.test", [Atom (ref_type t)] + | RefCast t -> "ref.cast", [Atom (ref_type t)] | RefEq -> "ref.eq", [] | I31New -> "i31.new", [] | I31Get ext -> "i31.get" ^ extension ext, [] diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 8110c897e..6b81919f9 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -188,9 +188,9 @@ rule token = parse | "br_if" -> BR_IF | "br_table" -> BR_TABLE | "br_on_null" -> BR_ON_NULL br_on_null - | "br_on_non_null" -> BR_ON_NON_NULL br_on_non_null - | "br_on_cast" -> BR_ON_CAST (br_on_cast, br_on_cast_null) - | "br_on_cast_fail" -> BR_ON_CAST_FAIL (br_on_cast_fail, br_on_cast_fail_null) + | "br_on_non_null" -> BR_ON_NULL br_on_non_null + | "br_on_cast" -> BR_ON_CAST br_on_cast + | "br_on_cast_fail" -> BR_ON_CAST br_on_cast_fail | "return" -> RETURN | "if" -> IF | "then" -> THEN @@ -312,10 +312,10 @@ rule token = parse | "ref.extern" -> REF_EXTERN | "ref.host" -> REF_HOST - | "ref.is_null" -> REF_IS_NULL ref_is_null - | "ref.as_non_null" -> REF_AS_NON_NULL ref_as_non_null - | "ref.test" -> REF_TEST (ref_test, ref_test_null) - | "ref.cast" -> REF_CAST (ref_cast, ref_cast_null) + | "ref.is_null" -> REF_IS_NULL + | "ref.as_non_null" -> REF_AS_NON_NULL + | "ref.test" -> REF_TEST + | "ref.cast" -> REF_CAST | "ref.eq" -> REF_EQ | "i31.new" -> I31_NEW diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 27345e88c..0b04f334a 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -251,7 +251,7 @@ let inline_func_type_explicit (c : context) x ft at = %token MUT FIELD STRUCT ARRAY SUB FINAL REC %token UNREACHABLE NOP DROP SELECT %token BLOCK END IF THEN ELSE LOOP -%token BR BR_IF BR_TABLE BR_ON_NULL BR_ON_NON_NULL BR_ON_CAST BR_ON_CAST_FAIL +%token BR BR_IF BR_TABLE BR_ON_NULL BR_ON_NON_NULL BR_ON_CAST %token CALL CALL_REF CALL_INDIRECT RETURN RETURN_CALL_REF %token LOCAL_GET LOCAL_SET LOCAL_TEE GLOBAL_GET GLOBAL_SET %token TABLE_GET TABLE_SET @@ -288,10 +288,8 @@ let inline_func_type_explicit (c : context) x ft at = %token NUM_TYPE %token VEC_TYPE %token PACK_TYPE -%token REF_IS_NULL REF_AS_NON_NULL -%token<(Types.heap_type -> Ast.instr') * (Types.heap_type -> Ast.instr')> REF_TEST REF_CAST -%token Ast.instr'> BR_ON_NULL BR_ON_NON_NULL -%token<(Ast.idx -> Types.heap_type -> Ast.instr') * (Ast.idx -> Types.heap_type -> Ast.instr')> BR_ON_CAST BR_ON_CAST_FAIL +%token Ast.instr'> BR_ON_NULL +%token Types.ref_type -> Types.ref_type -> Ast.instr'> BR_ON_CAST %token I31_GET %token Ast.idx -> Ast.instr'> STRUCT_GET %token Ast.instr'> ARRAY_GET @@ -537,11 +535,7 @@ plain_instr : { fun c -> let xs, x = Lib.List.split_last ($2 c label :: $3 c label) in br_table xs x } | BR_ON_NULL var { fun c -> $1 ($2 c label) } - | BR_ON_NON_NULL var { fun c -> $1 ($2 c label) } - | BR_ON_CAST var heap_type { fun c -> fst $1 ($2 c label) ($3 c) } - | BR_ON_CAST var NULL heap_type { fun c -> snd $1 ($2 c label) ($4 c) } - | BR_ON_CAST_FAIL var heap_type { fun c -> fst $1 ($2 c label) ($3 c) } - | BR_ON_CAST_FAIL var NULL heap_type { fun c -> snd $1 ($2 c label) ($4 c) } + | BR_ON_CAST var ref_type ref_type { fun c -> $1 ($2 c label) ($3 c) ($4 c) } | RETURN { fun c -> return } | CALL var { fun c -> call ($2 c func) } | CALL_REF var { fun c -> call_ref ($2 c type_) } @@ -584,12 +578,10 @@ plain_instr : | DATA_DROP var { fun c -> data_drop ($2 c data) } | REF_NULL heap_type { fun c -> ref_null ($2 c) } | REF_FUNC var { fun c -> ref_func ($2 c func) } - | REF_IS_NULL { fun c -> $1 } - | REF_AS_NON_NULL { fun c -> $1 } - | REF_TEST heap_type { fun c -> fst $1 ($2 c) } - | REF_CAST heap_type { fun c -> fst $1 ($2 c) } - | REF_TEST NULL heap_type { fun c -> snd $1 ($3 c) } - | REF_CAST NULL heap_type { fun c -> snd $1 ($3 c) } + | REF_IS_NULL { fun c -> ref_is_null } + | REF_AS_NON_NULL { fun c -> ref_as_non_null } + | REF_TEST ref_type { fun c -> ref_test ($2 c) } + | REF_CAST ref_type { fun c -> ref_cast ($2 c) } | REF_EQ { fun c -> ref_eq } | I31_NEW { fun c -> i31_new } | I31_GET { fun c -> $1 } diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index f2d6cc579..5f8f8b8c3 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -444,41 +444,37 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in " but label has " ^ string_of_result_type (label c x)); (ts0 @ [RefT (Null, ht)]) --> ts0, [] - | BrOnCast (x, rt) -> - let (_nul, ht) = rt in - let rt' = peek_ref 0 s e.at in - let tht = top_of_heap_type c.types ht in + | BrOnCast (x, rt1, rt2) -> + check_ref_type c rt1 e.at; + check_ref_type c rt2 e.at; require - (match_ref_type c.types rt' (Null, tht)) e.at - ("type mismatch: instruction requires type " ^ - string_of_ref_type (Null, tht) ^ - " but stack has " ^ string_of_ref_type rt'); + (match_ref_type c.types rt2 rt1) e.at + ("type mismatch on cast: type " ^ string_of_ref_type rt2 ^ + " does not match " ^ string_of_ref_type rt1); require (label c x <> []) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt ^ + ("type mismatch: instruction requires type " ^ string_of_ref_type rt2 ^ " but label has " ^ string_of_result_type (label c x)); let ts0, t1 = Lib.List.split_last (label c x) in - require (match_val_type c.types (RefT rt) t1) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt ^ + require (match_val_type c.types (RefT rt2) t1) e.at + ("type mismatch: instruction requires type " ^ string_of_ref_type rt2 ^ " but label has " ^ string_of_result_type (label c x)); - (ts0 @ [RefT rt']) --> (ts0 @ [RefT rt']), [] + (ts0 @ [RefT rt1]) --> (ts0 @ [RefT rt1]), [] - | BrOnCastFail (x, rt) -> - let (_nul, ht) = rt in - let rt' = peek_ref 0 s e.at in - let tht = top_of_heap_type c.types ht in + | BrOnCastFail (x, rt1, rt2) -> + check_ref_type c rt1 e.at; + check_ref_type c rt2 e.at; require - (match_ref_type c.types rt' (Null, tht)) e.at - ("type mismatch: instruction requires type " ^ - string_of_ref_type (Null, tht) ^ - " but stack has " ^ string_of_ref_type rt'); + (match_ref_type c.types rt2 rt1) e.at + ("type mismatch on cast: type " ^ string_of_ref_type rt2 ^ + " does not match " ^ string_of_ref_type rt1); require (label c x <> []) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt' ^ + ("type mismatch: instruction requires type " ^ string_of_ref_type rt1 ^ " but label has " ^ string_of_result_type (label c x)); let ts0, t1 = Lib.List.split_last (label c x) in - require (match_val_type c.types (RefT rt') t1) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt' ^ + require (match_val_type c.types (RefT rt1) t1) e.at + ("type mismatch: instruction requires type " ^ string_of_ref_type rt1 ^ " but label has " ^ string_of_result_type (label c x)); - (ts0 @ [RefT rt']) --> (ts0 @ [RefT rt]), [] + (ts0 @ [RefT rt1]) --> (ts0 @ [RefT rt2]), [] | Return -> c.results -->... [], [] @@ -647,7 +643,7 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in | RefCast rt -> check_ref_type c rt e.at; let (nul, ht) = rt in - [RefT (Null, top_of_heap_type c.types ht)] --> [RefT (nul, ht)], [] + [RefT (Null, top_of_heap_type c.types ht)] --> [RefT rt], [] | RefEq -> [RefT (Null, EqHT); RefT (Null, EqHT)] --> [NumT I32T], [] diff --git a/proposals/gc/MVP.md b/proposals/gc/MVP.md index c7eeccb3b..2b350d8e5 100644 --- a/proposals/gc/MVP.md +++ b/proposals/gc/MVP.md @@ -429,7 +429,7 @@ Subtyping is not defined on type definitions. #### Runtime Types -* Runtime types (RTTs) are values representing concrete types at runtime. In the MVP, *canonical* RTTs are implicitly created by all instructions depending on runtime type information (recognisable by the suffix `_canon` in their mnemonics). In future versions, RTTs may become explicit values, and non-canonical versions of these instructions will be introduced. +* Runtime types (RTTs) are values representing concrete types at runtime. In the MVP, *canonical* RTTs are implicitly created by all instructions depending on runtime type information. In future versions, RTTs may become explicit values, and non-canonical versions of these instructions will be introduced. * An RTT value r1 is *equal* to another RTT value r2 iff they both represent the same static type. @@ -466,7 +466,7 @@ Then, `$rttA` would carry supertype vector `[$rttA]`, `$rttB` has `[$rttA, $rttB Now consider a function that casts a `$B` to a `$C`: ``` (func $castBtoC (param $x (ref $B)) (result (ref $C)) - (ref.cast_canon $C (local.get $x)) + (ref.cast $C (local.get $x)) ) ``` This can compile to machine code that (1) reads the RTT from `$x`, (2) checks that the length of its supertype table is >= 3, and (3) pointer-compares table[2] against `$rttC`. @@ -493,14 +493,14 @@ In particular, `ref.null` is typed as before, despite the introduction of `none` #### Structures -* `struct.new_canon ` allocates a structure with canonical [RTT](#values) and initialises its fields with given values - - `struct.new_canon $t : [t'*] -> [(ref $t)]` +* `struct.new ` allocates a structure with canonical [RTT](#values) and initialises its fields with given values + - `struct.new $t : [t'*] -> [(ref $t)]` - iff `expand($t) = struct (mut t'')*` - and `(t' = unpacked(t''))*` - this is a *constant instruction* -* `struct.new_canon_default ` allocates a structure of type `$t` with canonical [RTT](#values) and initialises its fields with default values - - `struct.new_canon_default $t : [] -> [(ref $t)]` +* `struct.new_default ` allocates a structure of type `$t` with canonical [RTT](#values) and initialises its fields with default values + - `struct.new_default $t : [] -> [(ref $t)]` - iff `expand($t) = struct (mut t')*` - and all `t'*` are defaultable - this is a *constant instruction* @@ -521,26 +521,26 @@ In particular, `ref.null` is typed as before, despite the introduction of `none` #### Arrays -* `array.new_canon ` allocates an array with canonical [RTT](#values) - - `array.new_canon $t : [t' i32] -> [(ref $t)]` +* `array.new ` allocates an array with canonical [RTT](#values) + - `array.new $t : [t' i32] -> [(ref $t)]` - iff `expand($t) = array (mut t'')` - and `t' = unpacked(t'')` - this is a *constant instruction* -* `array.new_canon_default ` allocates an array with canonical [RTT](#values) and initialises its fields with the default value - - `array.new_canon_default $t : [i32] -> [(ref $t)]` +* `array.new_default ` allocates an array with canonical [RTT](#values) and initialises its fields with the default value + - `array.new_default $t : [i32] -> [(ref $t)]` - iff `expand($t) = array (mut t')` - and `t'` is defaultable - this is a *constant instruction* -* `array.new_canon_fixed ` allocates an array with canonical [RTT](#values) of fixed size and initialises it from operands - - `array.new_canon_fixed $t N : [t^N] -> [(ref $t)]` +* `array.new_fixed ` allocates an array with canonical [RTT](#values) of fixed size and initialises it from operands + - `array.new_fixed $t N : [t^N] -> [(ref $t)]` - iff `expand($t) = array (mut t'')` - and `t' = unpacked(t'')` - this is a *constant instruction* -* `array.new_canon_data ` allocates an array with canonical [RTT](#values) and initialises it from a data segment - - `array.new_canon_data $t $d : [i32 i32] -> [(ref $t)]` +* `array.new_data ` allocates an array with canonical [RTT](#values) and initialises it from a data segment + - `array.new_data $t $d : [i32 i32] -> [(ref $t)]` - iff `expand($t) = array (mut t')` - and `t'` is numeric, vector, or packed - and `$d` is a defined data segment @@ -549,8 +549,8 @@ In particular, `ref.null` is typed as before, despite the introduction of `none` - traps if `offset + |t'|*size > len($d)` - note: for now, this is _not_ a constant instruction, in order to side-step issues of recursion between binary sections; this restriction will be lifted later -* `array.new_canon_elem ` allocates an array with canonical [RTT](#values) and initialises it from an element segment - - `array.new_canon_elem $t $e : [i32 i32] -> [(ref $t)]` +* `array.new_elem ` allocates an array with canonical [RTT](#values) and initialises it from an element segment + - `array.new_elem $t $e : [i32 i32] -> [(ref $t)]` - iff `expand($t) = array (mut t')` - and `$e : rt` - and `rt <: t'` @@ -609,31 +609,29 @@ Casts work for both abstract and concrete types. In the latter case, they test i * `ref.test ` tests whether a reference has a given type - `ref.test rt : [rt'] -> [i32]` - - iff `rt <: trt` and `rt' <: trt` for some `trt` + - iff `rt <: rt'` - if `rt` contains `null`, returns 1 for null, otherwise 0 * `ref.cast ` tries to convert a reference to a given type - `ref.cast rt : [rt'] -> [rt]` - - iff `rt <: trt` and `rt' <: trt` for some `trt` + - iff `rt <: rt'` - traps if reference is not of requested type - if `rt` contains `null`, a null operand is passed through, otherwise traps on null - equivalent to `(block $l (param trt) (result rt) (br_on_cast $l rt) (unreachable))` -* `br_on_cast ` branches if a reference has a given type - - `br_on_cast $l rt : [t0* rt'] -> [t0* rt']` - - iff `$l : [t0* t']` - - and `rt <: t'` - - and `rt <: trt` and `rt' <: trt` for some `trt` +* `br_on_cast ` branches if a reference has a given type + - `br_on_cast $l rt1 rt2 : [t0* rt1] -> [t0* rt1]` + - iff `$l : [t0* rt2]` + - and `rt2 <: rt1` - passes operand along with branch under target type, plus possible extra args - - if `rt` contains `null`, branches on null, otherwise does not + - if `rt2` contains `null`, branches on null, otherwise does not -* `br_on_cast_fail ` branches if a reference does not have a given type - - `br_on_cast_fail $l rt : [t0* rt'] -> [t0* rt]` - - iff `$l : [t0* t']` - - and `rt' <: t'` - - and `rt <: trt` and `rt' <: trt` for some `trt` +* `br_on_cast_fail ` branches if a reference does not have a given type + - `br_on_cast_fail $l rt1 rt2 : [t0* rt1] -> [t0* rt2]` + - iff `$l : [t0* rt1]` + - and `rt2 <: rt1` - passes operand along with branch, plus possible extra args - - if `rt` contains `null`, does not branch on null, otherwise does + - if `rt2` contains `null`, does not branch on null, otherwise does Note: Cast instructions do _not_ require the operand's source type to be a supertype of the target type. It can also be a "sibling" in the same hierarchy, i.e., they only need to have a common supertype (in practice, it is sufficient to test that both types share the same top heap type.). Allowing so is necessary to maintain subtype substitutability, i.e., the ability to maintain well-typedness when operands are replaced by subtypes. @@ -655,9 +653,9 @@ TODO: Should we remove the latter 3 from the typed function references proposal? In order to allow RTTs to be initialised as globals, the following extensions are made to the definition of *constant expressions*: * `i31.new` is a constant instruction -* `struct.new_canon` and `struct.new_canon_default` are constant instructions -* `array.new_canon`, `array.new_canon_default`, and `array.new_canon_fixed` are constant instructions - - Note: `array.new_canon_data` and `array.new_canon_elem` are not for the time being, see above +* `struct.new` and `struct.new_default` are constant instructions +* `array.new`, `array.new_default`, and `array.new_fixed` are constant instructions + - Note: `array.new_data` and `array.new_elem` are not for the time being, see above * `extern.internalize` and `extern.externalize` are constant instructions * `global.get` is a constant instruction and can access preceding (immutable) global definitions, not just imports as in the MVP @@ -749,36 +747,41 @@ The opcode for heap types is encoded as an `s33`. | ------ | --------------- | ---------- | | 0xd5 | `ref.eq` | | | 0xd6 | `br_on_non_null` | | -| 0xfb01 | `struct.new_canon $t` | `$t : typeidx` | -| 0xfb02 | `struct.new_canon_default $t` | `$t : typeidx` | +| 0xfb01 | `struct.new $t` | `$t : typeidx` | +| 0xfb02 | `struct.new_default $t` | `$t : typeidx` | | 0xfb03 | `struct.get $t i` | `$t : typeidx`, `i : fieldidx` | | 0xfb04 | `struct.get_s $t i` | `$t : typeidx`, `i : fieldidx` | | 0xfb05 | `struct.get_u $t i` | `$t : typeidx`, `i : fieldidx` | | 0xfb06 | `struct.set $t i` | `$t : typeidx`, `i : fieldidx` | -| 0xfb11 | `array.new_canon $t` | `$t : typeidx` | -| 0xfb12 | `array.new_canon_default $t` | `$t : typeidx` | +| 0xfb11 | `array.new $t` | `$t : typeidx` | +| 0xfb12 | `array.new_default $t` | `$t : typeidx` | | 0xfb13 | `array.get $t` | `$t : typeidx` | | 0xfb14 | `array.get_s $t` | `$t : typeidx` | | 0xfb15 | `array.get_u $t` | `$t : typeidx` | | 0xfb16 | `array.set $t` | `$t : typeidx` | | 0xfb17 | `array.len` | | -| 0xfb19 | `array.new_canon_fixed $t N` | `$t : typeidx`, `N : u32` | -| 0xfb1b | `array.new_canon_data $t $d` | `$t : typeidx`, `$d : dataidx` | -| 0xfb1c | `array.new_canon_elem $t $e` | `$t : typeidx`, `$e : elemidx` | +| 0xfb19 | `array.new_fixed $t N` | `$t : typeidx`, `N : u32` | +| 0xfb1b | `array.new_data $t $d` | `$t : typeidx`, `$d : dataidx` | +| 0xfb1c | `array.new_elem $t $e` | `$t : typeidx`, `$e : elemidx` | | 0xfb20 | `i31.new` | | | 0xfb21 | `i31.get_s` | | | 0xfb22 | `i31.get_u` | | -| 0xfb40 | `ref.test ht` | `ht : heaptype` | -| 0xfb41 | `ref.cast ht` | `ht : heaptype` | -| 0xfb42 | `br_on_cast $l ht` | `$l : labelidx`, `ht : heaptype` | -| 0xfb43 | `br_on_cast_fail $l ht` | `$l : labelidx`, `ht : heaptype` | -| 0xfb48 | `ref.test null ht` | `ht : heaptype` | -| 0xfb49 | `ref.cast null ht` | `ht : heaptype` | -| 0xfb4a | `br_on_cast $l null ht` | `$l : labelidx`, `ht : heaptype` | -| 0xfb4b | `br_on_cast_fail $l null ht` | `$l : labelidx`, `ht : heaptype` | +| 0xfb40 | `ref.test (ref ht)` | `ht : heaptype` | +| 0xfb41 | `ref.cast (ref ht)` | `ht : heaptype` | +| 0xfb48 | `ref.test (ref null ht)` | `ht : heaptype` | +| 0xfb49 | `ref.cast (ref null ht)` | `ht : heaptype` | +| 0xfb4f | `br_on_cast(_fail)? $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, $l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | | 0xfb70 | `extern.internalize` | | | 0xfb71 | `extern.externalize` | | +Flag byte encoding for `br_on_cast`: + +| Bit | Function | +| --- | ------------- | +| 0 | null1 present | +| 1 | null2 present | +| 2 | _fail present | + ## JS API @@ -795,7 +798,7 @@ See [GC JS API document](MVP-JS.md) . -## Appendix: Formal Rules +## Appendix: Formal Rules for Types ### Validity diff --git a/test/core/gc/br_on_cast.wast b/test/core/gc/br_on_cast.wast index c73d5419f..cb1c1756d 100644 --- a/test/core/gc/br_on_cast.wast +++ b/test/core/gc/br_on_cast.wast @@ -27,20 +27,20 @@ ) (func (export "br_on_i31") (param $i i32) (result i32) (block $l (result (ref i31)) - (br_on_cast $l i31 (table.get (local.get $i))) + (br_on_cast $l anyref (ref i31) (table.get (local.get $i))) (return (i32.const -1)) ) (i31.get_u) ) (func (export "br_on_struct") (param $i i32) (result i32) (block $l (result (ref struct)) - (br_on_cast $l struct (table.get (local.get $i))) + (br_on_cast $l anyref (ref struct) (table.get (local.get $i))) (return (i32.const -1)) ) (block $l2 (param structref) (result (ref $st)) (block $l3 (param structref) (result (ref $at)) - (br_on_cast $l2 $st) - (br_on_cast $l3 $at) + (br_on_cast $l2 structref (ref $st)) + (br_on_cast $l3 anyref (ref $at)) (return (i32.const -2)) ) (return (array.get_u $at (i32.const 0))) @@ -49,7 +49,7 @@ ) (func (export "br_on_array") (param $i i32) (result i32) (block $l (result (ref array)) - (br_on_cast $l array (table.get (local.get $i))) + (br_on_cast $l anyref (ref array) (table.get (local.get $i))) (return (i32.const -1)) ) (array.len) @@ -112,45 +112,45 @@ (call $init) (block $l (result structref) ;; must succeed - (drop (block (result structref) (br_on_cast 0 $t0 (ref.null struct)))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 3))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 4))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 4))))) - (drop (block (result structref) (br_on_cast 0 $t1 (ref.null struct)))) - (drop (block (result structref) (br_on_cast 0 $t1 (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast 0 $t1 (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t2 (ref.null struct)))) - (drop (block (result structref) (br_on_cast 0 $t2 (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t3 (ref.null struct)))) - (drop (block (result structref) (br_on_cast 0 $t3 (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t3) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t3) (table.get (i32.const 3))))) - (drop (block (result structref) (br_on_cast 0 $t4 (ref.null struct)))) - (drop (block (result structref) (br_on_cast 0 $t4 (table.get (i32.const 4))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t4) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t4) (table.get (i32.const 4))))) ;; must not succeed - (br_on_cast $l $t1 (table.get (i32.const 0))) - (br_on_cast $l $t1 (table.get (i32.const 3))) - (br_on_cast $l $t1 (table.get (i32.const 4))) + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 3))) + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 4))) - (br_on_cast $l $t2 (table.get (i32.const 0))) - (br_on_cast $l $t2 (table.get (i32.const 1))) - (br_on_cast $l $t2 (table.get (i32.const 3))) - (br_on_cast $l $t2 (table.get (i32.const 4))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 3))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 4))) - (br_on_cast $l $t3 (table.get (i32.const 0))) - (br_on_cast $l $t3 (table.get (i32.const 1))) - (br_on_cast $l $t3 (table.get (i32.const 2))) - (br_on_cast $l $t3 (table.get (i32.const 4))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 2))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 4))) - (br_on_cast $l $t4 (table.get (i32.const 0))) - (br_on_cast $l $t4 (table.get (i32.const 1))) - (br_on_cast $l $t4 (table.get (i32.const 2))) - (br_on_cast $l $t4 (table.get (i32.const 3))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 2))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 3))) (return) ) @@ -160,25 +160,25 @@ (func (export "test-canon") (call $init) (block $l - (drop (block (result structref) (br_on_cast 0 $t0' (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast 0 $t0' (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast 0 $t0' (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t0' (table.get (i32.const 3))))) - (drop (block (result structref) (br_on_cast 0 $t0' (table.get (i32.const 4))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 4))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 10))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 11))))) - (drop (block (result structref) (br_on_cast 0 $t0 (table.get (i32.const 12))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 10))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 11))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 12))))) - (drop (block (result structref) (br_on_cast 0 $t1' (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast 0 $t1' (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1') (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1') (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t1 (table.get (i32.const 11))))) - (drop (block (result structref) (br_on_cast 0 $t1 (table.get (i32.const 12))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 11))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 12))))) - (drop (block (result structref) (br_on_cast 0 $t2' (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t2') (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast 0 $t2 (table.get (i32.const 12))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (table.get (i32.const 12))))) (return) ) @@ -196,16 +196,13 @@ (type $t (struct)) (func (param (ref any)) (result (ref $t)) - (block (result (ref any)) (br_on_cast 1 $t (local.get 0))) (unreachable) + (block (result (ref any)) (br_on_cast 1 (ref any) (ref $t) (local.get 0))) (unreachable) ) (func (param (ref null any)) (result (ref $t)) - (block (result (ref null any)) (br_on_cast 1 $t (local.get 0))) (unreachable) - ) - (func (param (ref any)) (result (ref null $t)) - (block (result (ref any)) (br_on_cast 1 null $t (local.get 0))) (unreachable) + (block (result (ref null any)) (br_on_cast 1 (ref null any) (ref $t) (local.get 0))) (unreachable) ) (func (param (ref null any)) (result (ref null $t)) - (block (result (ref null any)) (br_on_cast 1 null $t (local.get 0))) (unreachable) + (block (result (ref null any)) (br_on_cast 1 (ref null any) (ref null $t) (local.get 0))) (unreachable) ) ) @@ -213,7 +210,16 @@ (module (type $t (struct)) (func (param (ref any)) (result (ref $t)) - (block (result (ref any)) (br_on_cast 1 null $t (local.get 0))) (unreachable) + (block (result (ref any)) (br_on_cast 1 (ref null any) (ref null $t) (local.get 0))) (unreachable) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref null $t)) + (block (result (ref any)) (br_on_cast 1 (ref any) (ref null $t) (local.get 0))) (unreachable) ) ) "type mismatch" @@ -222,7 +228,7 @@ (module (type $t (struct)) (func (param (ref null any)) (result (ref $t)) - (block (result (ref any)) (br_on_cast 1 $t (local.get 0))) (unreachable) + (block (result (ref any)) (br_on_cast 1 (ref null any) (ref $t) (local.get 0))) (unreachable) ) ) "type mismatch" diff --git a/test/core/gc/br_on_cast_fail.wast b/test/core/gc/br_on_cast_fail.wast index b85d34ca8..e8c8be681 100644 --- a/test/core/gc/br_on_cast_fail.wast +++ b/test/core/gc/br_on_cast_fail.wast @@ -27,18 +27,18 @@ ) (func (export "br_on_non_i31") (param $i i32) (result i32) (block $l (result anyref) - (br_on_cast_fail $l i31 (table.get (local.get $i))) + (br_on_cast_fail $l anyref (ref i31) (table.get (local.get $i))) (return (i31.get_u)) ) (return (i32.const -1)) ) (func (export "br_on_non_struct") (param $i i32) (result i32) (block $l (result anyref) - (br_on_cast_fail $l struct (table.get (local.get $i))) + (br_on_cast_fail $l anyref (ref struct) (table.get (local.get $i))) (block $l2 (param structref) (result (ref $st)) (block $l3 (param structref) (result (ref $at)) - (br_on_cast $l2 $st) - (br_on_cast $l3 $at) + (br_on_cast $l2 structref (ref $st)) + (br_on_cast $l3 anyref (ref $at)) (return (i32.const -2)) ) (return (array.get_u $at (i32.const 0))) @@ -49,7 +49,7 @@ ) (func (export "br_on_non_array") (param $i i32) (result i32) (block $l (result anyref) - (br_on_cast_fail $l array (table.get (local.get $i))) + (br_on_cast_fail $l anyref (ref array) (table.get (local.get $i))) (return (array.len)) ) (return (i32.const -1)) @@ -112,61 +112,61 @@ (call $init) (block $l (result structref) ;; must not succeed - (br_on_cast_fail $l null $t0 (ref.null struct)) - (br_on_cast_fail $l null $t0 (table.get (i32.const 0))) - (br_on_cast_fail $l null $t0 (table.get (i32.const 1))) - (br_on_cast_fail $l null $t0 (table.get (i32.const 2))) - (br_on_cast_fail $l null $t0 (table.get (i32.const 3))) - (br_on_cast_fail $l null $t0 (table.get (i32.const 4))) - (br_on_cast_fail $l $t0 (table.get (i32.const 0))) - (br_on_cast_fail $l $t0 (table.get (i32.const 1))) - (br_on_cast_fail $l $t0 (table.get (i32.const 2))) - (br_on_cast_fail $l $t0 (table.get (i32.const 3))) - (br_on_cast_fail $l $t0 (table.get (i32.const 4))) - - (br_on_cast_fail $l null $t1 (ref.null struct)) - (br_on_cast_fail $l null $t1 (table.get (i32.const 1))) - (br_on_cast_fail $l null $t1 (table.get (i32.const 2))) - (br_on_cast_fail $l $t1 (table.get (i32.const 1))) - (br_on_cast_fail $l $t1 (table.get (i32.const 2))) - - (br_on_cast_fail $l null $t2 (ref.null struct)) - (br_on_cast_fail $l null $t2 (table.get (i32.const 2))) - (br_on_cast_fail $l $t2 (table.get (i32.const 2))) - - (br_on_cast_fail $l null $t3 (ref.null struct)) - (br_on_cast_fail $l null $t3 (table.get (i32.const 3))) - (br_on_cast_fail $l $t3 (table.get (i32.const 3))) - - (br_on_cast_fail $l null $t4 (ref.null struct)) - (br_on_cast_fail $l null $t4 (table.get (i32.const 4))) - (br_on_cast_fail $l $t4 (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref null $t0) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 4))) + + (br_on_cast_fail $l structref (ref null $t1) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t1) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref null $t1) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref null $t2) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t2) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t2) (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref null $t3) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t3) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t3) (table.get (i32.const 3))) + + (br_on_cast_fail $l structref (ref null $t4) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t4) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t4) (table.get (i32.const 4))) ;; must succeed - (drop (block (result structref) (br_on_cast_fail 0 $t0 (ref.null struct)))) - - (drop (block (result structref) (br_on_cast_fail 0 $t1 (ref.null struct)))) - (drop (block (result structref) (br_on_cast_fail 0 $t1 (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast_fail 0 $t1 (table.get (i32.const 3))))) - (drop (block (result structref) (br_on_cast_fail 0 $t1 (table.get (i32.const 4))))) - - (drop (block (result structref) (br_on_cast_fail 0 $t2 (ref.null struct)))) - (drop (block (result structref) (br_on_cast_fail 0 $t2 (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast_fail 0 $t2 (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast_fail 0 $t2 (table.get (i32.const 3))))) - (drop (block (result structref) (br_on_cast_fail 0 $t2 (table.get (i32.const 4))))) - - (drop (block (result structref) (br_on_cast_fail 0 $t3 (ref.null struct)))) - (drop (block (result structref) (br_on_cast_fail 0 $t3 (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast_fail 0 $t3 (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast_fail 0 $t3 (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast_fail 0 $t3 (table.get (i32.const 4))))) - - (drop (block (result structref) (br_on_cast_fail 0 $t4 (ref.null struct)))) - (drop (block (result structref) (br_on_cast_fail 0 $t4 (table.get (i32.const 0))))) - (drop (block (result structref) (br_on_cast_fail 0 $t4 (table.get (i32.const 1))))) - (drop (block (result structref) (br_on_cast_fail 0 $t4 (table.get (i32.const 2))))) - (drop (block (result structref) (br_on_cast_fail 0 $t4 (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t0) (ref.null struct)))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 3))))) (return) ) @@ -176,24 +176,24 @@ (func (export "test-canon") (call $init) (block $l (result structref) - (br_on_cast_fail $l $t0 (table.get (i32.const 0))) - (br_on_cast_fail $l $t0 (table.get (i32.const 1))) - (br_on_cast_fail $l $t0 (table.get (i32.const 2))) - (br_on_cast_fail $l $t0 (table.get (i32.const 3))) - (br_on_cast_fail $l $t0 (table.get (i32.const 4))) - (br_on_cast_fail $l $t0 (table.get (i32.const 10))) - (br_on_cast_fail $l $t0 (table.get (i32.const 11))) - (br_on_cast_fail $l $t0 (table.get (i32.const 12))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 10))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 11))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 12))) - (br_on_cast_fail $l $t1' (table.get (i32.const 1))) - (br_on_cast_fail $l $t1' (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t1') (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t1') (table.get (i32.const 2))) - (br_on_cast_fail $l $t1 (table.get (i32.const 11))) - (br_on_cast_fail $l $t1 (table.get (i32.const 12))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 11))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 12))) - (br_on_cast_fail $l $t2' (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t2') (table.get (i32.const 2))) - (br_on_cast_fail $l $t2 (table.get (i32.const 12))) + (br_on_cast_fail $l structref (ref $t2) (table.get (i32.const 12))) (return) ) @@ -211,16 +211,13 @@ (type $t (struct)) (func (param (ref any)) (result (ref any)) - (block (result (ref $t)) (br_on_cast_fail 1 $t (local.get 0))) + (block (result (ref $t)) (br_on_cast_fail 1 (ref any) (ref $t) (local.get 0))) ) (func (param (ref null any)) (result (ref null any)) - (block (result (ref $t)) (br_on_cast_fail 1 $t (local.get 0))) - ) - (func (param (ref any)) (result (ref any)) - (block (result (ref null $t)) (br_on_cast_fail 1 null $t (local.get 0))) (ref.as_non_null) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref $t) (local.get 0))) ) (func (param (ref null any)) (result (ref null any)) - (block (result (ref null $t)) (br_on_cast_fail 1 null $t (local.get 0))) + (block (result (ref null $t)) (br_on_cast_fail 1 (ref null any) (ref null $t) (local.get 0))) ) ) @@ -228,7 +225,16 @@ (module (type $t (struct)) (func (param (ref any)) (result (ref any)) - (block (result (ref $t)) (br_on_cast_fail 1 null $t (local.get 0))) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref null $t) (local.get 0))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref any)) + (block (result (ref null $t)) (br_on_cast_fail 1 (ref any) (ref null $t) (local.get 0))) (ref.as_non_null) ) ) "type mismatch" @@ -237,7 +243,7 @@ (module (type $t (struct)) (func (param (ref null any)) (result (ref any)) - (block (result (ref $t)) (br_on_cast_fail 1 $t (local.get 0))) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref $t) (local.get 0))) ) ) "type mismatch" diff --git a/test/core/gc/ref_cast.wast b/test/core/gc/ref_cast.wast index b5bb163fd..edd95b859 100644 --- a/test/core/gc/ref_cast.wast +++ b/test/core/gc/ref_cast.wast @@ -23,26 +23,26 @@ (func (export "ref_cast_non_null") (param $i i32) (drop (ref.as_non_null (table.get (local.get $i)))) - (drop (ref.cast null any (table.get (local.get $i)))) + (drop (ref.cast (ref null any) (table.get (local.get $i)))) ) (func (export "ref_cast_null") (param $i i32) - (drop (ref.cast null any (table.get (local.get $i)))) - (drop (ref.cast null struct (table.get (local.get $i)))) - (drop (ref.cast null array (table.get (local.get $i)))) - (drop (ref.cast null i31 (table.get (local.get $i)))) - (drop (ref.cast null none (table.get (local.get $i)))) + (drop (ref.cast anyref (table.get (local.get $i)))) + (drop (ref.cast structref (table.get (local.get $i)))) + (drop (ref.cast arrayref (table.get (local.get $i)))) + (drop (ref.cast i31ref (table.get (local.get $i)))) + (drop (ref.cast nullref (table.get (local.get $i)))) ) (func (export "ref_cast_i31") (param $i i32) - (drop (ref.cast i31 (table.get (local.get $i)))) - (drop (ref.cast null i31 (table.get (local.get $i)))) + (drop (ref.cast (ref i31) (table.get (local.get $i)))) + (drop (ref.cast i31ref (table.get (local.get $i)))) ) (func (export "ref_cast_struct") (param $i i32) - (drop (ref.cast struct (table.get (local.get $i)))) - (drop (ref.cast null struct (table.get (local.get $i)))) + (drop (ref.cast (ref struct) (table.get (local.get $i)))) + (drop (ref.cast structref (table.get (local.get $i)))) ) (func (export "ref_cast_array") (param $i i32) - (drop (ref.cast array (table.get (local.get $i)))) - (drop (ref.cast null array (table.get (local.get $i)))) + (drop (ref.cast (ref array) (table.get (local.get $i)))) + (drop (ref.cast arrayref (table.get (local.get $i)))) ) ) @@ -122,63 +122,63 @@ (func (export "test-sub") (call $init) - (drop (ref.cast null $t0 (ref.null struct))) - (drop (ref.cast null $t0 (table.get (i32.const 0)))) - (drop (ref.cast null $t0 (table.get (i32.const 1)))) - (drop (ref.cast null $t0 (table.get (i32.const 2)))) - (drop (ref.cast null $t0 (table.get (i32.const 3)))) - (drop (ref.cast null $t0 (table.get (i32.const 4)))) + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 4)))) - (drop (ref.cast null $t0 (ref.null struct))) - (drop (ref.cast null $t1 (table.get (i32.const 1)))) - (drop (ref.cast null $t1 (table.get (i32.const 2)))) + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t1) (table.get (i32.const 1)))) + (drop (ref.cast (ref null $t1) (table.get (i32.const 2)))) - (drop (ref.cast null $t0 (ref.null struct))) - (drop (ref.cast null $t2 (table.get (i32.const 2)))) + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t2) (table.get (i32.const 2)))) - (drop (ref.cast null $t0 (ref.null struct))) - (drop (ref.cast null $t3 (table.get (i32.const 3)))) + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t3) (table.get (i32.const 3)))) - (drop (ref.cast null $t4 (table.get (i32.const 4)))) + (drop (ref.cast (ref null $t4) (table.get (i32.const 4)))) - (drop (ref.cast $t0 (table.get (i32.const 0)))) - (drop (ref.cast $t0 (table.get (i32.const 1)))) - (drop (ref.cast $t0 (table.get (i32.const 2)))) - (drop (ref.cast $t0 (table.get (i32.const 3)))) - (drop (ref.cast $t0 (table.get (i32.const 4)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 4)))) - (drop (ref.cast $t1 (table.get (i32.const 1)))) - (drop (ref.cast $t1 (table.get (i32.const 2)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 2)))) - (drop (ref.cast $t2 (table.get (i32.const 2)))) + (drop (ref.cast (ref $t2) (table.get (i32.const 2)))) - (drop (ref.cast $t3 (table.get (i32.const 3)))) + (drop (ref.cast (ref $t3) (table.get (i32.const 3)))) - (drop (ref.cast $t4 (table.get (i32.const 4)))) + (drop (ref.cast (ref $t4) (table.get (i32.const 4)))) ) (func (export "test-canon") (call $init) - (drop (ref.cast $t0 (table.get (i32.const 0)))) - (drop (ref.cast $t0 (table.get (i32.const 1)))) - (drop (ref.cast $t0 (table.get (i32.const 2)))) - (drop (ref.cast $t0 (table.get (i32.const 3)))) - (drop (ref.cast $t0 (table.get (i32.const 4)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 4)))) - (drop (ref.cast $t0 (table.get (i32.const 10)))) - (drop (ref.cast $t0 (table.get (i32.const 11)))) - (drop (ref.cast $t0 (table.get (i32.const 12)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 10)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 11)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 12)))) - (drop (ref.cast $t1' (table.get (i32.const 1)))) - (drop (ref.cast $t1' (table.get (i32.const 2)))) + (drop (ref.cast (ref $t1') (table.get (i32.const 1)))) + (drop (ref.cast (ref $t1') (table.get (i32.const 2)))) - (drop (ref.cast $t1 (table.get (i32.const 11)))) - (drop (ref.cast $t1 (table.get (i32.const 12)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 11)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 12)))) - (drop (ref.cast $t2' (table.get (i32.const 2)))) + (drop (ref.cast (ref $t2') (table.get (i32.const 2)))) - (drop (ref.cast $t2 (table.get (i32.const 12)))) + (drop (ref.cast (ref $t2) (table.get (i32.const 12)))) ) ) diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast index da769b35b..8d23023f3 100644 --- a/test/core/gc/ref_test.wast +++ b/test/core/gc/ref_test.wast @@ -37,63 +37,63 @@ (func (export "ref_test_null_data") (param $i i32) (result i32) (i32.add (ref.is_null (table.get $ta (local.get $i))) - (ref.test null none (table.get $ta (local.get $i))) + (ref.test nullref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_any") (param $i i32) (result i32) (i32.add - (ref.test any (table.get $ta (local.get $i))) - (ref.test null any (table.get $ta (local.get $i))) + (ref.test (ref any) (table.get $ta (local.get $i))) + (ref.test anyref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_eq") (param $i i32) (result i32) (i32.add - (ref.test eq (table.get $ta (local.get $i))) - (ref.test null eq (table.get $ta (local.get $i))) + (ref.test (ref eq) (table.get $ta (local.get $i))) + (ref.test eqref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_i31") (param $i i32) (result i32) (i32.add - (ref.test i31 (table.get $ta (local.get $i))) - (ref.test null i31 (table.get $ta (local.get $i))) + (ref.test (ref i31) (table.get $ta (local.get $i))) + (ref.test i31ref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_struct") (param $i i32) (result i32) (i32.add - (ref.test struct (table.get $ta (local.get $i))) - (ref.test null struct (table.get $ta (local.get $i))) + (ref.test (ref struct) (table.get $ta (local.get $i))) + (ref.test structref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_array") (param $i i32) (result i32) (i32.add - (ref.test array (table.get $ta (local.get $i))) - (ref.test null array (table.get $ta (local.get $i))) + (ref.test (ref array) (table.get $ta (local.get $i))) + (ref.test arrayref (table.get $ta (local.get $i))) ) ) (func (export "ref_test_null_func") (param $i i32) (result i32) (i32.add (ref.is_null (table.get $tf (local.get $i))) - (ref.test null nofunc (table.get $tf (local.get $i))) + (ref.test (ref null nofunc) (table.get $tf (local.get $i))) ) ) (func (export "ref_test_func") (param $i i32) (result i32) (i32.add - (ref.test func (table.get $tf (local.get $i))) - (ref.test null func (table.get $tf (local.get $i))) + (ref.test (ref func) (table.get $tf (local.get $i))) + (ref.test funcref (table.get $tf (local.get $i))) ) ) (func (export "ref_test_null_extern") (param $i i32) (result i32) (i32.add (ref.is_null (table.get $te (local.get $i))) - (ref.test null noextern (table.get $te (local.get $i))) + (ref.test (ref null noextern) (table.get $te (local.get $i))) ) ) (func (export "ref_test_extern") (param $i i32) (result i32) (i32.add - (ref.test extern (table.get $te (local.get $i))) - (ref.test null extern (table.get $te (local.get $i))) + (ref.test (ref extern) (table.get $te (local.get $i))) + (ref.test externref (table.get $te (local.get $i))) ) ) ) @@ -206,91 +206,91 @@ (call $init) (block $l ;; must hold - (br_if $l (i32.eqz (ref.test null $t0 (ref.null struct)))) - (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t0)))) - (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t1)))) - (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t2)))) - (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t3)))) - (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t4)))) - (br_if $l (i32.eqz (ref.test null $t0 (table.get (i32.const 0))))) - (br_if $l (i32.eqz (ref.test null $t0 (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test null $t0 (table.get (i32.const 2))))) - (br_if $l (i32.eqz (ref.test null $t0 (table.get (i32.const 3))))) - (br_if $l (i32.eqz (ref.test null $t0 (table.get (i32.const 4))))) - - (br_if $l (i32.eqz (ref.test null $t1 (ref.null struct)))) - (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t0)))) - (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t1)))) - (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t2)))) - (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t3)))) - (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t4)))) - (br_if $l (i32.eqz (ref.test null $t1 (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test null $t1 (table.get (i32.const 2))))) - - (br_if $l (i32.eqz (ref.test null $t2 (ref.null struct)))) - (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t0)))) - (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t1)))) - (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t2)))) - (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t3)))) - (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t4)))) - (br_if $l (i32.eqz (ref.test null $t2 (table.get (i32.const 2))))) - - (br_if $l (i32.eqz (ref.test null $t3 (ref.null struct)))) - (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t0)))) - (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t1)))) - (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t2)))) - (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t3)))) - (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t4)))) - (br_if $l (i32.eqz (ref.test null $t3 (table.get (i32.const 3))))) - - (br_if $l (i32.eqz (ref.test null $t4 (ref.null struct)))) - (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t0)))) - (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t1)))) - (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t2)))) - (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t3)))) - (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t4)))) - (br_if $l (i32.eqz (ref.test null $t4 (table.get (i32.const 4))))) - - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 0))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 2))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 3))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 4))))) - - (br_if $l (i32.eqz (ref.test $t1 (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test $t1 (table.get (i32.const 2))))) - - (br_if $l (i32.eqz (ref.test $t2 (table.get (i32.const 2))))) - - (br_if $l (i32.eqz (ref.test $t3 (table.get (i32.const 3))))) - - (br_if $l (i32.eqz (ref.test $t4 (table.get (i32.const 4))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref $t4) (table.get (i32.const 4))))) ;; must not hold - (br_if $l (ref.test $t0 (ref.null struct))) - (br_if $l (ref.test $t1 (ref.null struct))) - (br_if $l (ref.test $t2 (ref.null struct))) - (br_if $l (ref.test $t3 (ref.null struct))) - (br_if $l (ref.test $t4 (ref.null struct))) - - (br_if $l (ref.test $t1 (table.get (i32.const 0)))) - (br_if $l (ref.test $t1 (table.get (i32.const 3)))) - (br_if $l (ref.test $t1 (table.get (i32.const 4)))) - - (br_if $l (ref.test $t2 (table.get (i32.const 0)))) - (br_if $l (ref.test $t2 (table.get (i32.const 1)))) - (br_if $l (ref.test $t2 (table.get (i32.const 3)))) - (br_if $l (ref.test $t2 (table.get (i32.const 4)))) - - (br_if $l (ref.test $t3 (table.get (i32.const 0)))) - (br_if $l (ref.test $t3 (table.get (i32.const 1)))) - (br_if $l (ref.test $t3 (table.get (i32.const 2)))) - (br_if $l (ref.test $t3 (table.get (i32.const 4)))) - - (br_if $l (ref.test $t4 (table.get (i32.const 0)))) - (br_if $l (ref.test $t4 (table.get (i32.const 1)))) - (br_if $l (ref.test $t4 (table.get (i32.const 2)))) - (br_if $l (ref.test $t4 (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t0) (ref.null struct))) + (br_if $l (ref.test (ref $t1) (ref.null struct))) + (br_if $l (ref.test (ref $t2) (ref.null struct))) + (br_if $l (ref.test (ref $t3) (ref.null struct))) + (br_if $l (ref.test (ref $t4) (ref.null struct))) + + (br_if $l (ref.test (ref $t1) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t2) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t3) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t4) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 3)))) (return) ) @@ -300,25 +300,25 @@ (func (export "test-canon") (call $init) (block $l - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 0))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 2))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 3))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 4))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 10))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 11))))) - (br_if $l (i32.eqz (ref.test $t0 (table.get (i32.const 12))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 10))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12))))) - (br_if $l (i32.eqz (ref.test $t1' (table.get (i32.const 1))))) - (br_if $l (i32.eqz (ref.test $t1' (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2))))) - (br_if $l (i32.eqz (ref.test $t1 (table.get (i32.const 11))))) - (br_if $l (i32.eqz (ref.test $t1 (table.get (i32.const 12))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12))))) - (br_if $l (i32.eqz (ref.test $t2' (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2))))) - (br_if $l (i32.eqz (ref.test $t2 (table.get (i32.const 12))))) + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12))))) (return) ) diff --git a/test/core/gc/type-subtyping.wast b/test/core/gc/type-subtyping.wast index fc5d3d6b3..94197027a 100644 --- a/test/core/gc/type-subtyping.wast +++ b/test/core/gc/type-subtyping.wast @@ -133,12 +133,12 @@ (block (result (ref null $t1)) (call_indirect (type $t1) (i32.const 2))) (block (result (ref null $t2)) (call_indirect (type $t2) (i32.const 2))) - (block (result (ref null $t0)) (ref.cast $t0 (table.get (i32.const 0)))) - (block (result (ref null $t0)) (ref.cast $t0 (table.get (i32.const 1)))) - (block (result (ref null $t0)) (ref.cast $t0 (table.get (i32.const 2)))) - (block (result (ref null $t1)) (ref.cast $t1 (table.get (i32.const 1)))) - (block (result (ref null $t1)) (ref.cast $t1 (table.get (i32.const 2)))) - (block (result (ref null $t2)) (ref.cast $t2 (table.get (i32.const 2)))) + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 0)))) + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 1)))) + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 2)))) + (block (result (ref null $t1)) (ref.cast (ref $t1) (table.get (i32.const 1)))) + (block (result (ref null $t1)) (ref.cast (ref $t1) (table.get (i32.const 2)))) + (block (result (ref null $t2)) (ref.cast (ref $t2) (table.get (i32.const 2)))) (br 0) ) @@ -156,15 +156,15 @@ ) (func (export "fail4") - (ref.cast $t1 (table.get (i32.const 0))) + (ref.cast (ref $t1) (table.get (i32.const 0))) (br 0) ) (func (export "fail5") - (ref.cast $t2 (table.get (i32.const 0))) + (ref.cast (ref $t2) (table.get (i32.const 0))) (br 0) ) (func (export "fail6") - (ref.cast $t2 (table.get (i32.const 1))) + (ref.cast (ref $t2) (table.get (i32.const 1))) (br 0) ) ) @@ -192,11 +192,11 @@ ) (func (export "fail3") - (ref.cast $t1 (table.get (i32.const 1))) + (ref.cast (ref $t1) (table.get (i32.const 1))) (drop) ) (func (export "fail4") - (ref.cast $t2 (table.get (i32.const 0))) + (ref.cast (ref $t2) (table.get (i32.const 0))) (drop) ) ) From 0ab6b309fbaf89be281e92a9fc2343edb4390b16 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 22 Mar 2023 08:54:33 +0100 Subject: [PATCH 2/4] Remove canon --- interpreter/binary/decode.ml | 14 ++++++------- interpreter/syntax/operators.ml | 14 ++++++------- interpreter/text/arrange.ml | 10 ++++----- interpreter/text/lexer.mll | 14 ++++++------- interpreter/text/parser.mly | 6 +++--- test/core/gc/array.wast | 34 +++++++++++++++---------------- test/core/gc/br_on_cast.wast | 20 +++++++++--------- test/core/gc/br_on_cast_fail.wast | 20 +++++++++--------- test/core/gc/extern.wast | 4 ++-- test/core/gc/ref_cast.wast | 20 +++++++++--------- test/core/gc/ref_eq.wast | 8 ++++---- test/core/gc/ref_test.wast | 22 ++++++++++---------- test/core/gc/struct.wast | 12 +++++------ 13 files changed, 99 insertions(+), 99 deletions(-) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index c6d39fa54..bc66ecf30 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -587,24 +587,24 @@ let rec instr s = | 0xfb as b -> (match u32 s with - | 0x01l -> struct_new_canon (at var s) - | 0x02l -> struct_new_canon_default (at var s) + | 0x01l -> struct_new (at var s) + | 0x02l -> struct_new_default (at var s) | 0x03l -> let x = at var s in let y = at var s in struct_get x y | 0x04l -> let x = at var s in let y = at var s in struct_get_s x y | 0x05l -> let x = at var s in let y = at var s in struct_get_u x y | 0x06l -> let x = at var s in let y = at var s in struct_set x y - | 0x11l -> array_new_canon (at var s) - | 0x12l -> array_new_canon_default (at var s) + | 0x11l -> array_new (at var s) + | 0x12l -> array_new_default (at var s) | 0x13l -> array_get (at var s) | 0x14l -> array_get_s (at var s) | 0x15l -> array_get_u (at var s) | 0x16l -> array_set (at var s) | 0x17l -> array_len - | 0x19l -> let x = at var s in let n = u32 s in array_new_canon_fixed x n - | 0x1bl -> let x = at var s in let y = at var s in array_new_canon_data x y - | 0x1cl -> let x = at var s in let y = at var s in array_new_canon_elem x y + | 0x19l -> let x = at var s in let n = u32 s in array_new_fixed x n + | 0x1bl -> let x = at var s in let y = at var s in array_new_data x y + | 0x1cl -> let x = at var s in let y = at var s in array_new_elem x y | 0x20l -> i31_new | 0x21l -> i31_get_s diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 09fb16fd6..75eb8b127 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -109,17 +109,17 @@ let ref_eq = RefEq let i31_new = I31New let i31_get_u = I31Get ZX let i31_get_s = I31Get SX -let struct_new_canon x = StructNew (x, Explicit) -let struct_new_canon_default x = StructNew (x, Implicit) +let struct_new x = StructNew (x, Explicit) +let struct_new_default x = StructNew (x, Implicit) let struct_get x y = StructGet (x, y, None) let struct_get_u x y = StructGet (x, y, Some ZX) let struct_get_s x y = StructGet (x, y, Some SX) let struct_set x y = StructSet (x, y) -let array_new_canon x = ArrayNew (x, Explicit) -let array_new_canon_default x = ArrayNew (x, Implicit) -let array_new_canon_fixed x n = ArrayNewFixed (x, n) -let array_new_canon_elem x y = ArrayNewElem (x, y) -let array_new_canon_data x y = ArrayNewData (x, y) +let array_new x = ArrayNew (x, Explicit) +let array_new_default x = ArrayNew (x, Implicit) +let array_new_fixed x n = ArrayNewFixed (x, n) +let array_new_elem x y = ArrayNewElem (x, y) +let array_new_data x y = ArrayNewData (x, y) let array_get x = ArrayGet (x, None) let array_get_u x = ArrayGet (x, Some ZX) let array_get_s x = ArrayGet (x, Some SX) diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index c300b2903..efc1418fb 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -549,14 +549,14 @@ let rec instr e = | RefEq -> "ref.eq", [] | I31New -> "i31.new", [] | I31Get ext -> "i31.get" ^ extension ext, [] - | StructNew (x, op) -> "struct.new_canon" ^ initop op ^ " " ^ var x, [] + | StructNew (x, op) -> "struct.new" ^ initop op ^ " " ^ var x, [] | StructGet (x, y, exto) -> "struct.get" ^ opt_s extension exto ^ " " ^ var x ^ " " ^ var y, [] | StructSet (x, y) -> "struct.set " ^ var x ^ " " ^ var y, [] - | ArrayNew (x, op) -> "array.new_canon" ^ initop op ^ " " ^ var x, [] - | ArrayNewFixed (x, n) -> "array.new_canon_fixed " ^ var x ^ " " ^ nat32 n, [] - | ArrayNewElem (x, y) -> "array.new_canon_elem " ^ var x ^ " " ^ var y, [] - | ArrayNewData (x, y) -> "array.new_canon_data " ^ var x ^ " " ^ var y, [] + | ArrayNew (x, op) -> "array.new" ^ initop op ^ " " ^ var x, [] + | ArrayNewFixed (x, n) -> "array.new_fixed " ^ var x ^ " " ^ nat32 n, [] + | ArrayNewElem (x, y) -> "array.new_elem " ^ var x ^ " " ^ var y, [] + | ArrayNewData (x, y) -> "array.new_data " ^ var x ^ " " ^ var y, [] | ArrayGet (x, exto) -> "array.get" ^ opt_s extension exto ^ " " ^ var x, [] | ArraySet x -> "array.set " ^ var x, [] | ArrayLen -> "array.len", [] diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 6b81919f9..569d6f9c5 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -322,18 +322,18 @@ rule token = parse | "i31.get_u" -> I31_GET i31_get_u | "i31.get_s" -> I31_GET i31_get_s - | "struct.new_canon" -> STRUCT_NEW struct_new_canon - | "struct.new_canon_default" -> STRUCT_NEW struct_new_canon_default + | "struct.new" -> STRUCT_NEW struct_new + | "struct.new_default" -> STRUCT_NEW struct_new_default | "struct.get" -> STRUCT_GET struct_get | "struct.get_u" -> STRUCT_GET struct_get_u | "struct.get_s" -> STRUCT_GET struct_get_s | "struct.set" -> STRUCT_SET - | "array.new_canon" -> ARRAY_NEW array_new_canon - | "array.new_canon_default" -> ARRAY_NEW array_new_canon_default - | "array.new_canon_fixed" -> ARRAY_NEW_FIXED - | "array.new_canon_elem" -> ARRAY_NEW_ELEM - | "array.new_canon_data" -> ARRAY_NEW_DATA + | "array.new" -> ARRAY_NEW array_new + | "array.new_default" -> ARRAY_NEW array_new_default + | "array.new_fixed" -> ARRAY_NEW_FIXED + | "array.new_elem" -> ARRAY_NEW_ELEM + | "array.new_data" -> ARRAY_NEW_DATA | "array.get" -> ARRAY_GET array_get | "array.get_u" -> ARRAY_GET array_get_u | "array.get_s" -> ARRAY_GET array_get_s diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 0b04f334a..694a556b0 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -589,9 +589,9 @@ plain_instr : | STRUCT_GET var var { fun c -> $1 ($2 c type_) ($3 c field) } | STRUCT_SET var var { fun c -> struct_set ($2 c type_) ($3 c field) } | ARRAY_NEW var { fun c -> $1 ($2 c type_) } - | ARRAY_NEW_FIXED var nat32 { fun c -> array_new_canon_fixed ($2 c type_) $3 } - | ARRAY_NEW_ELEM var var { fun c -> array_new_canon_elem ($2 c type_) ($3 c elem) } - | ARRAY_NEW_DATA var var { fun c -> array_new_canon_data ($2 c type_) ($3 c data) } + | ARRAY_NEW_FIXED var nat32 { fun c -> array_new_fixed ($2 c type_) $3 } + | ARRAY_NEW_ELEM var var { fun c -> array_new_elem ($2 c type_) ($3 c elem) } + | ARRAY_NEW_DATA var var { fun c -> array_new_data ($2 c type_) ($3 c data) } | ARRAY_GET var { fun c -> $1 ($2 c type_) } | ARRAY_SET var { fun c -> array_set ($2 c type_) } | ARRAY_LEN { fun c -> array_len } diff --git a/test/core/gc/array.wast b/test/core/gc/array.wast index 7ee75b205..0da7e8432 100644 --- a/test/core/gc/array.wast +++ b/test/core/gc/array.wast @@ -61,11 +61,11 @@ (type $vec (array f32)) (type $mvec (array (mut f32))) - (global (ref $vec) (array.new_canon $vec (f32.const 1) (i32.const 3))) - (global (ref $vec) (array.new_canon_default $vec (i32.const 3))) + (global (ref $vec) (array.new $vec (f32.const 1) (i32.const 3))) + (global (ref $vec) (array.new_default $vec (i32.const 3))) (func $new (export "new") (result (ref $vec)) - (array.new_canon_default $vec (i32.const 3)) + (array.new_default $vec (i32.const 3)) ) (func $get (param $i i32) (param $v (ref $vec)) (result f32) @@ -81,7 +81,7 @@ ) (func (export "set_get") (param $i i32) (param $y f32) (result f32) (call $set_get (local.get $i) - (array.new_canon_default $mvec (i32.const 3)) + (array.new_default $mvec (i32.const 3)) (local.get $y) ) ) @@ -107,10 +107,10 @@ (type $vec (array f32)) (type $mvec (array (mut f32))) - (global (ref $vec) (array.new_canon_fixed $vec 2 (f32.const 1) (f32.const 2))) + (global (ref $vec) (array.new_fixed $vec 2 (f32.const 1) (f32.const 2))) (func $new (export "new") (result (ref $vec)) - (array.new_canon_fixed $vec 2 (f32.const 1) (f32.const 2)) + (array.new_fixed $vec 2 (f32.const 1) (f32.const 2)) ) (func $get (param $i i32) (param $v (ref $vec)) (result f32) @@ -126,7 +126,7 @@ ) (func (export "set_get") (param $i i32) (param $y f32) (result f32) (call $set_get (local.get $i) - (array.new_canon_fixed $mvec 3 (f32.const 1) (f32.const 2) (f32.const 3)) + (array.new_fixed $mvec 3 (f32.const 1) (f32.const 2) (f32.const 3)) (local.get $y) ) ) @@ -155,7 +155,7 @@ (data $d "\00\01\02\03\04") (func $new (export "new") (result (ref $vec)) - (array.new_canon_data $vec $d (i32.const 1) (i32.const 3)) + (array.new_data $vec $d (i32.const 1) (i32.const 3)) ) (func $get (param $i i32) (param $v (ref $vec)) (result i32) @@ -171,7 +171,7 @@ ) (func (export "set_get") (param $i i32) (param $y i32) (result i32) (call $set_get (local.get $i) - (array.new_canon_data $mvec $d (i32.const 1) (i32.const 3)) + (array.new_data $mvec $d (i32.const 1) (i32.const 3)) (local.get $y) ) ) @@ -201,19 +201,19 @@ (type $avec (array (mut anyref))) (elem $e (ref $bvec) - (array.new_canon $bvec (i32.const 7) (i32.const 3)) - (array.new_canon_fixed $bvec 2 (i32.const 1) (i32.const 2)) + (array.new $bvec (i32.const 7) (i32.const 3)) + (array.new_fixed $bvec 2 (i32.const 1) (i32.const 2)) ) (func $new (export "new") (result (ref $vec)) - (array.new_canon_elem $vec $e (i32.const 0) (i32.const 2)) + (array.new_elem $vec $e (i32.const 0) (i32.const 2)) ) (func $sub1 (result (ref $nvec)) - (array.new_canon_elem $nvec $e (i32.const 0) (i32.const 2)) + (array.new_elem $nvec $e (i32.const 0) (i32.const 2)) ) (func $sub2 (result (ref $avec)) - (array.new_canon_elem $avec $e (i32.const 0) (i32.const 2)) + (array.new_elem $avec $e (i32.const 0) (i32.const 2)) ) (func $get (param $i i32) (param $j i32) (param $v (ref $vec)) (result i32) @@ -229,7 +229,7 @@ ) (func (export "set_get") (param $i i32) (param $j i32) (param $y i32) (result i32) (call $set_get (local.get $i) (local.get $j) - (array.new_canon_elem $mvec $e (i32.const 0) (i32.const 2)) + (array.new_elem $mvec $e (i32.const 0) (i32.const 2)) (local.get $y) ) ) @@ -269,7 +269,7 @@ (data $d "\00\01\02\03\04") (global (ref $bvec) - (array.new_canon_data $bvec $d (i32.const 1) (i32.const 3)) + (array.new_data $bvec $d (i32.const 1) (i32.const 3)) ) ) "constant expression required" @@ -283,7 +283,7 @@ (elem $e (ref $bvec) (ref.null $bvec)) (global (ref $vvec) - (array.new_canon_elem $vvec $e (i32.const 0) (i32.const 1)) + (array.new_elem $vvec $e (i32.const 0) (i32.const 1)) ) ) "constant expression required" diff --git a/test/core/gc/br_on_cast.wast b/test/core/gc/br_on_cast.wast index cb1c1756d..06259900e 100644 --- a/test/core/gc/br_on_cast.wast +++ b/test/core/gc/br_on_cast.wast @@ -13,8 +13,8 @@ (func (export "init") (param $x externref) (table.set (i32.const 0) (ref.null any)) (table.set (i32.const 1) (i31.new (i32.const 7))) - (table.set (i32.const 2) (struct.new_canon $st (i32.const 6))) - (table.set (i32.const 3) (array.new_canon $at (i32.const 5) (i32.const 3))) + (table.set (i32.const 2) (struct.new $st (i32.const 6))) + (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) (table.set (i32.const 4) (extern.internalize (local.get $x))) ) @@ -98,14 +98,14 @@ (table 20 structref) (func $init - (table.set (i32.const 0) (struct.new_canon_default $t0)) - (table.set (i32.const 10) (struct.new_canon_default $t0')) - (table.set (i32.const 1) (struct.new_canon_default $t1)) - (table.set (i32.const 11) (struct.new_canon_default $t1')) - (table.set (i32.const 2) (struct.new_canon_default $t2)) - (table.set (i32.const 12) (struct.new_canon_default $t2')) - (table.set (i32.const 3) (struct.new_canon_default $t3)) - (table.set (i32.const 4) (struct.new_canon_default $t4)) + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0')) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) ) (func (export "test-sub") diff --git a/test/core/gc/br_on_cast_fail.wast b/test/core/gc/br_on_cast_fail.wast index e8c8be681..0b64f5909 100644 --- a/test/core/gc/br_on_cast_fail.wast +++ b/test/core/gc/br_on_cast_fail.wast @@ -13,8 +13,8 @@ (func (export "init") (param $x externref) (table.set (i32.const 0) (ref.null any)) (table.set (i32.const 1) (i31.new (i32.const 7))) - (table.set (i32.const 2) (struct.new_canon $st (i32.const 6))) - (table.set (i32.const 3) (array.new_canon $at (i32.const 5) (i32.const 3))) + (table.set (i32.const 2) (struct.new $st (i32.const 6))) + (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) (table.set (i32.const 4) (extern.internalize (local.get $x))) ) @@ -98,14 +98,14 @@ (table 20 structref) (func $init - (table.set (i32.const 0) (struct.new_canon_default $t0)) - (table.set (i32.const 10) (struct.new_canon_default $t0)) - (table.set (i32.const 1) (struct.new_canon_default $t1)) - (table.set (i32.const 11) (struct.new_canon_default $t1')) - (table.set (i32.const 2) (struct.new_canon_default $t2)) - (table.set (i32.const 12) (struct.new_canon_default $t2')) - (table.set (i32.const 3) (struct.new_canon_default $t3 )) - (table.set (i32.const 4) (struct.new_canon_default $t4)) + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3 )) + (table.set (i32.const 4) (struct.new_default $t4)) ) (func (export "test-sub") diff --git a/test/core/gc/extern.wast b/test/core/gc/extern.wast index 1f32a0abe..f9b01f11e 100644 --- a/test/core/gc/extern.wast +++ b/test/core/gc/extern.wast @@ -11,8 +11,8 @@ (func (export "init") (param $x externref) (table.set (i32.const 0) (ref.null any)) (table.set (i32.const 1) (i31.new (i32.const 7))) - (table.set (i32.const 2) (struct.new_canon_default $st)) - (table.set (i32.const 3) (array.new_canon_default $at (i32.const 0))) + (table.set (i32.const 2) (struct.new_default $st)) + (table.set (i32.const 3) (array.new_default $at (i32.const 0))) (table.set (i32.const 4) (extern.internalize (local.get $x))) ) diff --git a/test/core/gc/ref_cast.wast b/test/core/gc/ref_cast.wast index edd95b859..0a1a0edea 100644 --- a/test/core/gc/ref_cast.wast +++ b/test/core/gc/ref_cast.wast @@ -13,8 +13,8 @@ (func (export "init") (param $x externref) (table.set (i32.const 0) (ref.null any)) (table.set (i32.const 1) (i31.new (i32.const 7))) - (table.set (i32.const 2) (struct.new_canon_default $st)) - (table.set (i32.const 3) (array.new_canon_default $at (i32.const 0))) + (table.set (i32.const 2) (struct.new_default $st)) + (table.set (i32.const 3) (array.new_default $at (i32.const 0))) (table.set (i32.const 4) (extern.internalize (local.get $x))) (table.set (i32.const 5) (ref.null i31)) (table.set (i32.const 6) (ref.null struct)) @@ -109,14 +109,14 @@ (table 20 (ref null struct)) (func $init - (table.set (i32.const 0) (struct.new_canon_default $t0)) - (table.set (i32.const 10) (struct.new_canon_default $t0)) - (table.set (i32.const 1) (struct.new_canon_default $t1)) - (table.set (i32.const 11) (struct.new_canon_default $t1')) - (table.set (i32.const 2) (struct.new_canon_default $t2)) - (table.set (i32.const 12) (struct.new_canon_default $t2')) - (table.set (i32.const 3) (struct.new_canon_default $t3)) - (table.set (i32.const 4) (struct.new_canon_default $t4)) + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) ) (func (export "test-sub") diff --git a/test/core/gc/ref_eq.wast b/test/core/gc/ref_eq.wast index c8e090442..c30d7a5c1 100644 --- a/test/core/gc/ref_eq.wast +++ b/test/core/gc/ref_eq.wast @@ -15,10 +15,10 @@ (table.set (i32.const 2) (i31.new (i32.const 7))) (table.set (i32.const 3) (i31.new (i32.const 7))) (table.set (i32.const 4) (i31.new (i32.const 8))) - (table.set (i32.const 5) (struct.new_canon_default $st)) - (table.set (i32.const 6) (struct.new_canon_default $st)) - (table.set (i32.const 7) (array.new_canon_default $at (i32.const 0))) - (table.set (i32.const 8) (array.new_canon_default $at (i32.const 0))) + (table.set (i32.const 5) (struct.new_default $st)) + (table.set (i32.const 6) (struct.new_default $st)) + (table.set (i32.const 7) (array.new_default $at (i32.const 0))) + (table.set (i32.const 8) (array.new_default $at (i32.const 0))) ) (func (export "eq") (param $i i32) (param $j i32) (result i32) diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast index 8d23023f3..31cf62897 100644 --- a/test/core/gc/ref_test.wast +++ b/test/core/gc/ref_test.wast @@ -17,8 +17,8 @@ (table.set $ta (i32.const 1) (ref.null struct)) (table.set $ta (i32.const 2) (ref.null none)) (table.set $ta (i32.const 3) (i31.new (i32.const 7))) - (table.set $ta (i32.const 4) (struct.new_canon_default $st)) - (table.set $ta (i32.const 5) (array.new_canon_default $at (i32.const 0))) + (table.set $ta (i32.const 4) (struct.new_default $st)) + (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0))) (table.set $ta (i32.const 6) (extern.internalize (local.get $x))) (table.set $ta (i32.const 7) (extern.internalize (ref.null extern))) @@ -30,7 +30,7 @@ (table.set $te (i32.const 1) (ref.null extern)) (table.set $te (i32.const 2) (local.get $x)) (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8)))) - (table.set $te (i32.const 4) (extern.externalize (struct.new_canon_default $st))) + (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st))) (table.set $te (i32.const 5) (extern.externalize (ref.null any))) ) @@ -192,14 +192,14 @@ (table 20 (ref null struct)) (func $init - (table.set (i32.const 0) (struct.new_canon_default $t0)) - (table.set (i32.const 10) (struct.new_canon_default $t0)) - (table.set (i32.const 1) (struct.new_canon_default $t1)) - (table.set (i32.const 11) (struct.new_canon_default $t1')) - (table.set (i32.const 2) (struct.new_canon_default $t2)) - (table.set (i32.const 12) (struct.new_canon_default $t2')) - (table.set (i32.const 3) (struct.new_canon_default $t3)) - (table.set (i32.const 4) (struct.new_canon_default $t4)) + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) ) (func (export "test-sub") diff --git a/test/core/gc/struct.wast b/test/core/gc/struct.wast index bbd2c94a0..e06fb8a25 100644 --- a/test/core/gc/struct.wast +++ b/test/core/gc/struct.wast @@ -55,18 +55,18 @@ (module (type $vec (struct (field f32) (field $y (mut f32)) (field $z f32))) - (global (ref $vec) (struct.new_canon $vec (f32.const 1) (f32.const 2) (f32.const 3))) - (global (ref $vec) (struct.new_canon_default $vec)) + (global (ref $vec) (struct.new $vec (f32.const 1) (f32.const 2) (f32.const 3))) + (global (ref $vec) (struct.new_default $vec)) (func (export "new") (result anyref) - (struct.new_canon_default $vec) + (struct.new_default $vec) ) (func $get_0 (param $v (ref $vec)) (result f32) (struct.get $vec 0 (local.get $v)) ) (func (export "get_0") (result f32) - (call $get_0 (struct.new_canon_default $vec)) + (call $get_0 (struct.new_default $vec)) ) (func $set_get_y (param $v (ref $vec)) (param $y f32) (result f32) @@ -74,7 +74,7 @@ (struct.get $vec $y (local.get $v)) ) (func (export "set_get_y") (param $y f32) (result f32) - (call $set_get_y (struct.new_canon_default $vec) (local.get $y)) + (call $set_get_y (struct.new_default $vec) (local.get $y)) ) (func $set_get_1 (param $v (ref $vec)) (param $y f32) (result f32) @@ -82,7 +82,7 @@ (struct.get $vec $y (local.get $v)) ) (func (export "set_get_1") (param $y f32) (result f32) - (call $set_get_1 (struct.new_canon_default $vec) (local.get $y)) + (call $set_get_1 (struct.new_default $vec) (local.get $y)) ) ) From 782d554795580d96fbc063ff3253c419d54ff47c Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 22 Mar 2023 22:27:40 +0100 Subject: [PATCH 3/4] Perform type diff on failure path --- interpreter/valid/valid.ml | 14 ++++++++++---- proposals/gc/MVP.md | 8 ++++++-- test/core/gc/br_on_cast.wast | 16 ++++++++++++++++ test/core/gc/br_on_cast_fail.wast | 16 ++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index 5f8f8b8c3..d85837b0a 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -195,6 +195,11 @@ let check_type (c : context) (t : type_) : context = check_def_type c t.it t.at +let diff_ref_type (nul1, ht1) (nul2, ht2) = + match nul2 with + | Null -> (NoNull, ht1) + | NoNull -> (nul1, ht1) + (* Stack typing *) @@ -458,21 +463,22 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in require (match_val_type c.types (RefT rt2) t1) e.at ("type mismatch: instruction requires type " ^ string_of_ref_type rt2 ^ " but label has " ^ string_of_result_type (label c x)); - (ts0 @ [RefT rt1]) --> (ts0 @ [RefT rt1]), [] + (ts0 @ [RefT rt1]) --> (ts0 @ [RefT (diff_ref_type rt1 rt2)]), [] | BrOnCastFail (x, rt1, rt2) -> check_ref_type c rt1 e.at; check_ref_type c rt2 e.at; + let rt1' = diff_ref_type rt1 rt2 in require (match_ref_type c.types rt2 rt1) e.at ("type mismatch on cast: type " ^ string_of_ref_type rt2 ^ " does not match " ^ string_of_ref_type rt1); require (label c x <> []) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt1 ^ + ("type mismatch: instruction requires type " ^ string_of_ref_type rt1' ^ " but label has " ^ string_of_result_type (label c x)); let ts0, t1 = Lib.List.split_last (label c x) in - require (match_val_type c.types (RefT rt1) t1) e.at - ("type mismatch: instruction requires type " ^ string_of_ref_type rt1 ^ + require (match_val_type c.types (RefT rt1') t1) e.at + ("type mismatch: instruction requires type " ^ string_of_ref_type rt1' ^ " but label has " ^ string_of_result_type (label c x)); (ts0 @ [RefT rt1]) --> (ts0 @ [RefT rt2]), [] diff --git a/proposals/gc/MVP.md b/proposals/gc/MVP.md index 2b350d8e5..67072aed0 100644 --- a/proposals/gc/MVP.md +++ b/proposals/gc/MVP.md @@ -620,7 +620,7 @@ Casts work for both abstract and concrete types. In the latter case, they test i - equivalent to `(block $l (param trt) (result rt) (br_on_cast $l rt) (unreachable))` * `br_on_cast ` branches if a reference has a given type - - `br_on_cast $l rt1 rt2 : [t0* rt1] -> [t0* rt1]` + - `br_on_cast $l rt1 rt2 : [t0* rt1] -> [t0* rt1\rt2]` - iff `$l : [t0* rt2]` - and `rt2 <: rt1` - passes operand along with branch under target type, plus possible extra args @@ -628,11 +628,15 @@ Casts work for both abstract and concrete types. In the latter case, they test i * `br_on_cast_fail ` branches if a reference does not have a given type - `br_on_cast_fail $l rt1 rt2 : [t0* rt1] -> [t0* rt2]` - - iff `$l : [t0* rt1]` + - iff `$l : [t0* rt1\rt2]` - and `rt2 <: rt1` - passes operand along with branch, plus possible extra args - if `rt2` contains `null`, does not branch on null, otherwise does +where: + - `(ref null1? ht1)\(ref null ht2) = (ref ht1)` + - `(ref null1? ht1)\(ref ht2) = (ref null1? ht1)` + Note: Cast instructions do _not_ require the operand's source type to be a supertype of the target type. It can also be a "sibling" in the same hierarchy, i.e., they only need to have a common supertype (in practice, it is sufficient to test that both types share the same top heap type.). Allowing so is necessary to maintain subtype substitutability, i.e., the ability to maintain well-typedness when operands are replaced by subtypes. Note: The [reference types](https://github.com/WebAssembly/reference-types) and [typed function references](https://github.com/WebAssembly/function-references)already introduce similar `ref.is_null`, `br_on_null`, and `br_on_non_null` instructions. These can now be interpreted as syntactic sugar: diff --git a/test/core/gc/br_on_cast.wast b/test/core/gc/br_on_cast.wast index 06259900e..a7b35949a 100644 --- a/test/core/gc/br_on_cast.wast +++ b/test/core/gc/br_on_cast.wast @@ -54,6 +54,16 @@ ) (array.len) ) + + (func (export "null-diff") (param $i i32) (result i32) + (block $l (result (ref null struct)) + (block (result (ref any)) + (br_on_cast $l (ref null any) (ref null struct) (table.get (local.get $i))) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) ) (invoke "init" (ref.extern 0)) @@ -82,6 +92,12 @@ (assert_return (invoke "br_on_array" (i32.const 3)) (i32.const 3)) (assert_return (invoke "br_on_array" (i32.const 4)) (i32.const -1)) +(assert_return (invoke "null-diff" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 4)) (i32.const 0)) + ;; Concrete Types diff --git a/test/core/gc/br_on_cast_fail.wast b/test/core/gc/br_on_cast_fail.wast index 0b64f5909..601de88af 100644 --- a/test/core/gc/br_on_cast_fail.wast +++ b/test/core/gc/br_on_cast_fail.wast @@ -54,6 +54,16 @@ ) (return (i32.const -1)) ) + + (func (export "null-diff") (param $i i32) (result i32) + (block $l (result (ref any)) + (block (result (ref null struct)) + (br_on_cast_fail $l (ref null any) (ref null struct) (table.get (local.get $i))) + ) + (return (i32.const 1)) + ) + (return (i32.const 0)) + ) ) (invoke "init" (ref.extern 0)) @@ -82,6 +92,12 @@ (assert_return (invoke "br_on_non_array" (i32.const 3)) (i32.const 3)) (assert_return (invoke "br_on_non_array" (i32.const 4)) (i32.const -1)) +(assert_return (invoke "null-diff" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 4)) (i32.const 0)) + ;; Concrete Types From 745750eee0e189d8cd4021897d8934d5bd66aa22 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 3 May 2023 09:51:49 +0200 Subject: [PATCH 4/4] Separate opcodes for br_on_cast/_fail --- interpreter/binary/decode.ml | 6 +++--- interpreter/binary/encode.ml | 6 +++--- proposals/gc/MVP.md | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index bc66ecf30..7c45c6146 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -614,13 +614,13 @@ let rec instr s = | 0x41l -> ref_cast (NoNull, heap_type s) | 0x48l -> ref_test (Null, heap_type s) | 0x49l -> ref_cast (Null, heap_type s) - | 0x4fl -> + | 0x4el | 0x4fl as opcode -> let flags = byte s in - require (flags land 0xf8 = 0) s (pos + 2) "malformed br_on_cast flags"; + require (flags land 0xfc = 0) s (pos + 2) "malformed br_on_cast flags"; let x = at var s in let rt1 = ((if bit 0 flags then Null else NoNull), heap_type s) in let rt2 = ((if bit 1 flags then Null else NoNull), heap_type s) in - (if bit 2 flags then br_on_cast_fail else br_on_cast) x rt1 rt2 + (if opcode = 0x4el then br_on_cast else br_on_cast_fail) x rt1 rt2 | 0x70l -> extern_internalize | 0x71l -> extern_externalize diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 2026efc25..d71f8a95b 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -244,10 +244,10 @@ struct | BrOnNull x -> op 0xd4; var x | BrOnNonNull x -> op 0xd6; var x | BrOnCast (x, (nul1, t1), (nul2, t2)) -> - let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) + bit 2 false in - op 0xfb; op 0x4f; byte flags; var x; heap_type t1; heap_type t2 + let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) in + op 0xfb; op 0x4e; byte flags; var x; heap_type t1; heap_type t2 | BrOnCastFail (x, (nul1, t1), (nul2, t2)) -> - let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) + bit 2 true in + let flags = bit 0 (nul1 = Null) + bit 1 (nul2 = Null) in op 0xfb; op 0x4f; byte flags; var x; heap_type t1; heap_type t2 | Return -> op 0x0f | Call x -> op 0x10; var x diff --git a/proposals/gc/MVP.md b/proposals/gc/MVP.md index 67072aed0..0e1dbceb4 100644 --- a/proposals/gc/MVP.md +++ b/proposals/gc/MVP.md @@ -774,17 +774,17 @@ The opcode for heap types is encoded as an `s33`. | 0xfb41 | `ref.cast (ref ht)` | `ht : heaptype` | | 0xfb48 | `ref.test (ref null ht)` | `ht : heaptype` | | 0xfb49 | `ref.cast (ref null ht)` | `ht : heaptype` | -| 0xfb4f | `br_on_cast(_fail)? $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, $l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | +| 0xfb4e | `br_on_cast $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, $l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | +| 0xfb4f | `br_on_cast_fail $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, $l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | | 0xfb70 | `extern.internalize` | | | 0xfb71 | `extern.externalize` | | -Flag byte encoding for `br_on_cast`: +Flag byte encoding for `br_on_cast(_fail)?`: | Bit | Function | | --- | ------------- | | 0 | null1 present | | 1 | null2 present | -| 2 | _fail present | ## JS API