@@ -368,6 +368,7 @@ impl GcOps {
368368 let Some ( op) = op. fixup ( & self . limits , num_types) else {
369369 continue ;
370370 } ;
371+ let op = StackType :: fixup_cast ( op, & self . types , & encoding_order) ;
371372
372373 debug_assert ! ( operand_types. is_empty( ) ) ;
373374 op. operand_types ( & mut operand_types) ;
@@ -584,6 +585,22 @@ macro_rules! for_each_gc_op {
584585 type_index = type_index. checked_rem( num_types) ?;
585586 } ) ]
586587 NullTypedStruct { type_index: u32 } ,
588+
589+ #[ operands( [ Some ( Struct ( Some ( sub_type_index) ) ) ] ) ]
590+ #[ results( [ Struct ( Some ( super_type_index) ) ] ) ]
591+ #[ fixup( |_limits, num_types| {
592+ sub_type_index = sub_type_index. checked_rem( num_types) ?;
593+ super_type_index = super_type_index. checked_rem( num_types) ?;
594+ } ) ]
595+ RefCastUpward { sub_type_index: u32 , super_type_index: u32 } ,
596+
597+ #[ operands( [ Some ( Struct ( Some ( super_type_index) ) ) ] ) ]
598+ #[ results( [ Struct ( Some ( sub_type_index) ) ] ) ]
599+ #[ fixup( |_limits, num_types| {
600+ sub_type_index = sub_type_index. checked_rem( num_types) ?;
601+ super_type_index = super_type_index. checked_rem( num_types) ?;
602+ } ) ]
603+ RefCastDownward { sub_type_index: u32 , super_type_index: u32 } ,
587604 }
588605 } ;
589606}
@@ -890,6 +907,66 @@ impl GcOp {
890907 encoding_bases. typed_table_base + type_index,
891908 ) ) ;
892909 }
910+ Self :: RefCastUpward {
911+ sub_type_index : _,
912+ super_type_index,
913+ } => {
914+ // The value on the stack is already the subtype, so this
915+ // cast always succeeds.
916+ let heap_type = wasm_encoder:: HeapType :: Concrete (
917+ encoding_bases. struct_type_base + super_type_index,
918+ ) ;
919+ func. instruction ( & Instruction :: RefCastNullable ( heap_type) ) ;
920+ }
921+ Self :: RefCastDownward {
922+ sub_type_index,
923+ super_type_index,
924+ } => {
925+ // Fallible downcast that never traps:
926+ //
927+ // local.tee $my_temp
928+ // ;; Test if the downcast will succeed.
929+ // ref.test ...
930+ // if (result (ref null $my_sub))
931+ // ;; The downcast will succeed, do a downcast-or-trap
932+ // ;; operation which we know will not trap.
933+ // local.get $my_temp
934+ // ref.cast ...
935+ // else
936+ // ;; The downcast would fail, so just create a null
937+ // ;; reference instead.
938+ // ref.null ...
939+ // end
940+ let sub_wasm_type = encoding_bases. struct_type_base + sub_type_index;
941+ let sub_heap_type = wasm_encoder:: HeapType :: Concrete ( sub_wasm_type) ;
942+ let temp_local = encoding_bases. typed_local_base + super_type_index;
943+
944+ // Tee the supertype value into a temp local (saves and
945+ // leaves the value on the stack for ref.test).
946+ func. instruction ( & Instruction :: LocalTee ( temp_local) ) ;
947+
948+ // Test if the downcast will succeed.
949+ func. instruction ( & Instruction :: RefTestNullable ( sub_heap_type) ) ;
950+
951+ // if (result (ref null $sub_type))
952+ func. instruction ( & Instruction :: If ( wasm_encoder:: BlockType :: Result (
953+ ValType :: Ref ( RefType {
954+ nullable : true ,
955+ heap_type : sub_heap_type,
956+ } ) ,
957+ ) ) ) ;
958+
959+ // The downcast will succeed; do the cast.
960+ func. instruction ( & Instruction :: LocalGet ( temp_local) ) ;
961+ func. instruction ( & Instruction :: RefCastNullable ( sub_heap_type) ) ;
962+
963+ func. instruction ( & Instruction :: Else ) ;
964+
965+ // The downcast would fail; produce null instead.
966+ func. instruction ( & Instruction :: RefNull ( sub_heap_type) ) ;
967+
968+ func. instruction ( & Instruction :: End ) ;
969+ }
893970 }
894971 }
895972}
0 commit comments