[wasm-gc] Add new ref.cast taking any reference
The new ref.cast (opcode 0xfb41) takes any reference and expects a Heaptype immediate. the HeapType can be a concrete or an abstract type. Differently to the old ref.cast instruction, it traps on null. A variant which doesn't trap on null (ref.cast null) will be added in a future CL. Bug: v8:7748 Change-Id: Id5764a7553a57c5cb838682c9ec331d15d7d25c0 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3948663 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Matthias Liedtke <mliedtke@chromium.org> Cr-Commit-Position: refs/heads/main@{#83724}
This commit is contained in:
parent
2ea6a9e223
commit
7ff8d6eb9e
@ -5548,6 +5548,27 @@ Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
|
||||
return gasm_->WasmTypeCast(object, rtt, config);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::RefCastAbstract(Node* object, wasm::HeapType type,
|
||||
wasm::WasmCodePosition position) {
|
||||
bool is_nullable =
|
||||
compiler::NodeProperties::GetType(object).AsWasm().type.is_nullable();
|
||||
switch (type.representation()) {
|
||||
case wasm::HeapType::kEq:
|
||||
return RefAsEq(object, is_nullable, position);
|
||||
case wasm::HeapType::kI31:
|
||||
return RefAsI31(object, position);
|
||||
case wasm::HeapType::kStruct:
|
||||
return RefAsStruct(object, is_nullable, position);
|
||||
case wasm::HeapType::kArray:
|
||||
return RefAsArray(object, is_nullable, position);
|
||||
case wasm::HeapType::kAny:
|
||||
// Any may never need a cast as it is either implicitly convertible or
|
||||
// never convertible for any given type.
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
|
||||
WasmTypeCheckConfig config,
|
||||
Node** match_control, Node** match_effect,
|
||||
@ -5572,6 +5593,17 @@ Node* WasmGraphBuilder::RefIsEq(Node* object, bool object_can_be_null,
|
||||
return done.PhiAt(0);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::RefAsEq(Node* object, bool object_can_be_null,
|
||||
wasm::WasmCodePosition position) {
|
||||
bool null_succeeds = false;
|
||||
auto done = gasm_->MakeLabel();
|
||||
EqCheck(object, object_can_be_null, CastCallbacks(&done, position),
|
||||
null_succeeds);
|
||||
gasm_->Goto(&done);
|
||||
gasm_->Bind(&done);
|
||||
return object;
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::RefIsStruct(Node* object, bool object_can_be_null,
|
||||
bool null_succeeds) {
|
||||
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
|
||||
|
@ -497,10 +497,14 @@ class WasmGraphBuilder {
|
||||
Node* RefTestAbstract(Node* object, wasm::HeapType type, bool null_succeeds);
|
||||
Node* RefCast(Node* object, Node* rtt, WasmTypeCheckConfig config,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* RefCastAbstract(Node* object, wasm::HeapType type,
|
||||
wasm::WasmCodePosition position);
|
||||
void BrOnCast(Node* object, Node* rtt, WasmTypeCheckConfig config,
|
||||
Node** match_control, Node** match_effect,
|
||||
Node** no_match_control, Node** no_match_effect);
|
||||
Node* RefIsEq(Node* object, bool object_can_be_null, bool null_succeeds);
|
||||
Node* RefAsEq(Node* object, bool object_can_be_null,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* RefIsStruct(Node* object, bool object_can_be_null, bool null_succeeds);
|
||||
Node* RefAsStruct(Node* object, bool object_can_be_null,
|
||||
wasm::WasmCodePosition position);
|
||||
|
@ -154,10 +154,18 @@ Reduction WasmGCLowering::ReduceWasmTypeCast(Node* node) {
|
||||
auto end_label = gasm_.MakeLabel();
|
||||
|
||||
if (object_can_be_null) {
|
||||
gasm_.GotoIf(gasm_.TaggedEqual(object, Null()), &end_label,
|
||||
BranchHint::kFalse);
|
||||
Node* is_null = gasm_.TaggedEqual(object, Null());
|
||||
if (config.null_succeeds) {
|
||||
gasm_.GotoIf(is_null, &end_label, BranchHint::kFalse);
|
||||
} else {
|
||||
gasm_.TrapIf(is_null, TrapId::kTrapIllegalCast);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(7748): Only perform SMI check if source type may contain i31, any or
|
||||
// extern.
|
||||
gasm_.TrapIf(gasm_.IsI31(object), TrapId::kTrapIllegalCast);
|
||||
|
||||
Node* map = gasm_.LoadMap(object);
|
||||
|
||||
// First, check if types happen to be equal. This has been shown to give large
|
||||
|
@ -2679,7 +2679,8 @@ class LiftoffCompiler {
|
||||
__ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
|
||||
}
|
||||
|
||||
void AssertNull(FullDecoder* decoder, const Value& arg, Value* result) {
|
||||
void AssertNullImpl(FullDecoder* decoder, const Value& arg, Value* result,
|
||||
LiftoffCondition cond) {
|
||||
LiftoffRegList pinned;
|
||||
LiftoffRegister obj = pinned.set(__ PopToRegister(pinned));
|
||||
Label* trap_label =
|
||||
@ -2688,12 +2689,20 @@ class LiftoffCompiler {
|
||||
LoadNullValue(null.gp(), pinned);
|
||||
{
|
||||
FREEZE_STATE(trapping);
|
||||
__ emit_cond_jump(kUnequal, trap_label, kRefNull, obj.gp(), null.gp(),
|
||||
__ emit_cond_jump(cond, trap_label, kRefNull, obj.gp(), null.gp(),
|
||||
trapping);
|
||||
}
|
||||
__ PushRegister(kRefNull, obj);
|
||||
}
|
||||
|
||||
void AssertNull(FullDecoder* decoder, const Value& arg, Value* result) {
|
||||
AssertNullImpl(decoder, arg, result, kUnequal);
|
||||
}
|
||||
|
||||
void AssertNotNull(FullDecoder* decoder, const Value& arg, Value* result) {
|
||||
AssertNullImpl(decoder, arg, result, kEqual);
|
||||
}
|
||||
|
||||
void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) {
|
||||
unsupported(decoder, kOtherReason, "testing opcode");
|
||||
}
|
||||
@ -6010,7 +6019,7 @@ class LiftoffCompiler {
|
||||
}
|
||||
|
||||
void RefCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
|
||||
Value* result) {
|
||||
Value* result, bool null_succeeds) {
|
||||
if (v8_flags.experimental_wasm_assume_ref_cast_succeeds) {
|
||||
// Just drop the rtt.
|
||||
__ DropValues(1);
|
||||
@ -6028,13 +6037,33 @@ class LiftoffCompiler {
|
||||
|
||||
{
|
||||
FREEZE_STATE(frozen);
|
||||
NullSucceeds on_null = null_succeeds ? kNullSucceeds : kNullFails;
|
||||
SubtypeCheck(decoder->module_, obj_reg.gp(), obj.type, rtt_reg.gp(),
|
||||
rtt.type, scratch_null, scratch2, trap_label, kNullSucceeds,
|
||||
rtt.type, scratch_null, scratch2, trap_label, on_null,
|
||||
frozen);
|
||||
}
|
||||
__ PushRegister(obj.type.kind(), obj_reg);
|
||||
}
|
||||
|
||||
void RefCastAbstract(FullDecoder* decoder, const Value& obj, HeapType type,
|
||||
Value* result_val) {
|
||||
switch (type.representation()) {
|
||||
case HeapType::kEq:
|
||||
return RefAsEq(decoder, obj, result_val);
|
||||
case HeapType::kI31:
|
||||
return RefAsI31(decoder, obj, result_val);
|
||||
case HeapType::kStruct:
|
||||
return RefAsStruct(decoder, obj, result_val);
|
||||
case HeapType::kArray:
|
||||
return RefAsArray(decoder, obj, result_val);
|
||||
case HeapType::kAny:
|
||||
// Any may never need a cast as it is either implicitly convertible or
|
||||
// never convertible for any given type.
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void BrOnCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
|
||||
Value* /* result_on_branch */, uint32_t depth) {
|
||||
// Avoid having sequences of branches do duplicate work.
|
||||
@ -6242,6 +6271,10 @@ class LiftoffCompiler {
|
||||
(this->*type_checker)(check, frozen);
|
||||
}
|
||||
|
||||
void RefAsEq(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
AbstractTypeCast<&LiftoffCompiler::EqCheck>(object, decoder, kRef);
|
||||
}
|
||||
|
||||
void RefAsStruct(FullDecoder* decoder, const Value& object,
|
||||
Value* /* result */) {
|
||||
AbstractTypeCast<&LiftoffCompiler::StructCheck>(object, decoder, kRef);
|
||||
|
@ -1070,8 +1070,11 @@ struct ControlBase : public PcForErrors<validate> {
|
||||
bool null_succeeds) \
|
||||
F(RefTestAbstract, const Value& obj, HeapType type, Value* result, \
|
||||
bool null_succeeds) \
|
||||
F(RefCast, const Value& obj, const Value& rtt, Value* result) \
|
||||
F(RefCast, const Value& obj, const Value& rtt, Value* result, \
|
||||
bool null_succeeds) \
|
||||
F(RefCastAbstract, const Value& obj, HeapType type, Value* result) \
|
||||
F(AssertNull, const Value& obj, Value* result) \
|
||||
F(AssertNotNull, const Value& obj, Value* result) \
|
||||
F(BrOnCast, const Value& obj, const Value& rtt, Value* result_on_branch, \
|
||||
uint32_t depth) \
|
||||
F(BrOnCastFail, const Value& obj, const Value& rtt, \
|
||||
@ -2119,6 +2122,7 @@ class WasmDecoder : public Decoder {
|
||||
if (io) io->BranchDepth(imm);
|
||||
return length + imm.length;
|
||||
}
|
||||
case kExprRefCast:
|
||||
case kExprRefTest:
|
||||
case kExprRefTestNull: {
|
||||
HeapTypeImmediate<validate> imm(WasmFeatures::All(), decoder,
|
||||
@ -2127,7 +2131,7 @@ class WasmDecoder : public Decoder {
|
||||
return length + imm.length;
|
||||
}
|
||||
case kExprRefTestDeprecated:
|
||||
case kExprRefCast:
|
||||
case kExprRefCastDeprecated:
|
||||
case kExprRefCastNop: {
|
||||
IndexImmediate<validate> imm(decoder, pc + length, "type index");
|
||||
if (io) io->TypeIndex(imm);
|
||||
@ -2358,6 +2362,7 @@ class WasmDecoder : public Decoder {
|
||||
case kExprRefTestNull:
|
||||
case kExprRefTestDeprecated:
|
||||
case kExprRefCast:
|
||||
case kExprRefCastDeprecated:
|
||||
case kExprRefCastNop:
|
||||
case kExprBrOnCast:
|
||||
case kExprBrOnCastFail:
|
||||
@ -4794,6 +4799,84 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Push(value);
|
||||
return opcode_length;
|
||||
}
|
||||
case kExprRefCast: {
|
||||
NON_CONST_ONLY
|
||||
HeapTypeImmediate<validate> imm(
|
||||
this->enabled_, this, this->pc_ + opcode_length, this->module_);
|
||||
if (!VALIDATE(this->ok())) return 0;
|
||||
opcode_length += imm.length;
|
||||
|
||||
std::optional<Value> rtt;
|
||||
HeapType target_type = imm.type;
|
||||
if (imm.type.is_index()) {
|
||||
rtt = CreateValue(ValueType::Rtt(imm.type.ref_index()));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.type.ref_index(),
|
||||
&rtt.value());
|
||||
Push(rtt.value());
|
||||
}
|
||||
|
||||
Value obj = Peek(rtt.has_value() ? 1 : 0);
|
||||
if (!VALIDATE((obj.type.is_object_reference() &&
|
||||
IsSameTypeHierarchy(obj.type.heap_type(), target_type,
|
||||
this->module_)) ||
|
||||
obj.type.is_bottom())) {
|
||||
this->DecodeError(
|
||||
obj.pc(),
|
||||
"Invalid types for ref.cast: %s of type %s has to "
|
||||
"be in the same reference type hierarchy as (ref %s)",
|
||||
SafeOpcodeNameAt(obj.pc()), obj.type.name().c_str(),
|
||||
target_type.name().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(mliedtke): Add support for ref.cast null.
|
||||
bool null_succeeds = false;
|
||||
Value value = CreateValue(ValueType::RefMaybeNull(
|
||||
imm.type, (obj.type.is_bottom() || !null_succeeds)
|
||||
? kNonNullable
|
||||
: obj.type.nullability()));
|
||||
if (current_code_reachable_and_ok_) {
|
||||
// This logic ensures that code generation can assume that functions
|
||||
// can only be cast to function types, and data objects to data types.
|
||||
if (V8_UNLIKELY(TypeCheckAlwaysSucceeds(obj, target_type))) {
|
||||
// Drop the rtt from the stack, then forward the object value to the
|
||||
// result.
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(Drop);
|
||||
}
|
||||
if (obj.type.is_nullable() && !null_succeeds) {
|
||||
CALL_INTERFACE(AssertNotNull, obj, &value);
|
||||
} else {
|
||||
CALL_INTERFACE(Forward, obj, &value);
|
||||
}
|
||||
} else if (V8_UNLIKELY(TypeCheckAlwaysFails(obj, target_type,
|
||||
null_succeeds))) {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(Drop);
|
||||
}
|
||||
// Unrelated types. The only way this will not trap is if the object
|
||||
// is null.
|
||||
if (obj.type.is_nullable() && null_succeeds) {
|
||||
// Drop rtt from the stack, then assert that obj is null.
|
||||
CALL_INTERFACE(AssertNull, obj, &value);
|
||||
} else {
|
||||
CALL_INTERFACE(Trap, TrapReason::kTrapIllegalCast);
|
||||
// We know that the following code is not reachable, but according
|
||||
// to the spec it technically is. Set it to spec-only reachable.
|
||||
SetSucceedingCodeDynamicallyUnreachable();
|
||||
}
|
||||
} else {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(RefCast, obj, rtt.value(), &value, null_succeeds);
|
||||
} else {
|
||||
CALL_INTERFACE(RefCastAbstract, obj, target_type, &value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Drop(1 + rtt.has_value());
|
||||
Push(value);
|
||||
return opcode_length;
|
||||
}
|
||||
case kExprRefTestNull:
|
||||
case kExprRefTest: {
|
||||
NON_CONST_ONLY
|
||||
@ -4944,7 +5027,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Push(value);
|
||||
return opcode_length;
|
||||
}
|
||||
case kExprRefCast: {
|
||||
case kExprRefCastDeprecated: {
|
||||
NON_CONST_ONLY
|
||||
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
|
||||
"type index");
|
||||
@ -4990,7 +5073,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
SetSucceedingCodeDynamicallyUnreachable();
|
||||
}
|
||||
} else {
|
||||
CALL_INTERFACE(RefCast, obj, rtt, &value);
|
||||
bool null_succeeds = true;
|
||||
CALL_INTERFACE(RefCast, obj, rtt, &value, null_succeeds);
|
||||
}
|
||||
}
|
||||
Drop(2);
|
||||
|
@ -477,6 +477,12 @@ class WasmGraphBuildingInterface {
|
||||
Forward(decoder, obj, result);
|
||||
}
|
||||
|
||||
void AssertNotNull(FullDecoder* decoder, const Value& obj, Value* result) {
|
||||
builder_->TrapIfTrue(wasm::TrapReason::kTrapIllegalCast,
|
||||
builder_->IsNull(obj.node), decoder->position());
|
||||
Forward(decoder, obj, result);
|
||||
}
|
||||
|
||||
void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) {}
|
||||
|
||||
void Select(FullDecoder* decoder, const Value& cond, const Value& fval,
|
||||
@ -1254,9 +1260,7 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void RefCast(FullDecoder* decoder, const Value& object, const Value& rtt,
|
||||
Value* result) {
|
||||
// TODO(mliedtke): Should be a parameter for generic ref.cast instructions.
|
||||
const bool null_succeeds = false;
|
||||
Value* result, bool null_succeeds) {
|
||||
WasmTypeCheckConfig config = ComputeWasmTypeCheckConfig(
|
||||
object.type, rtt.type, decoder->module_, null_succeeds);
|
||||
TFNode* cast_node = v8_flags.experimental_wasm_assume_ref_cast_succeeds
|
||||
@ -1266,6 +1270,15 @@ class WasmGraphBuildingInterface {
|
||||
SetAndTypeNode(result, cast_node);
|
||||
}
|
||||
|
||||
void RefCastAbstract(FullDecoder* decoder, const Value& object,
|
||||
wasm::HeapType type, Value* result) {
|
||||
TFNode* node = object.node;
|
||||
if (!v8_flags.experimental_wasm_assume_ref_cast_succeeds) {
|
||||
node = builder_->RefCastAbstract(object.node, type, decoder->position());
|
||||
}
|
||||
SetAndTypeNode(result, builder_->TypeGuard(node, result->type));
|
||||
}
|
||||
|
||||
template <void (compiler::WasmGraphBuilder::*branch_function)(
|
||||
TFNode*, TFNode*, WasmTypeCheckConfig, TFNode**, TFNode**, TFNode**,
|
||||
TFNode**)>
|
||||
|
@ -710,7 +710,8 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
|
||||
V(RefTest, 0xfb40, _, "ref.test") \
|
||||
V(RefTestNull, 0xfb48, _, "ref.test null") \
|
||||
V(RefTestDeprecated, 0xfb44, _, "ref.test") \
|
||||
V(RefCast, 0xfb45, _, "ref.cast") \
|
||||
V(RefCast, 0xfb41, _, "ref.cast") \
|
||||
V(RefCastDeprecated, 0xfb45, _, "ref.cast") \
|
||||
V(BrOnCast, 0xfb46, _, "br_on_cast") \
|
||||
V(BrOnCastFail, 0xfb47, _, "br_on_cast_fail") \
|
||||
V(RefCastNop, 0xfb4c, _, "ref.cast_nop") \
|
||||
|
@ -592,15 +592,26 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
WASM_GC_OP(kExprStructGet), type_index, 0, WASM_LOCAL_GET(0),
|
||||
kExprI32Add, kExprEnd});
|
||||
|
||||
const byte kTestNullDeprecated = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kWasmStructRef},
|
||||
{WASM_BLOCK_R(ValueType::RefNull(type_index),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
|
||||
// Not taken for nullref.
|
||||
WASM_BR_ON_CAST(0, type_index),
|
||||
WASM_GC_OP(kExprRefCastDeprecated), type_index,
|
||||
|
||||
WASM_LOCAL_SET(0, WASM_I32V(222))), // Final result.
|
||||
WASM_DROP, WASM_LOCAL_GET(0), kExprEnd});
|
||||
|
||||
const byte kTestNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kWasmStructRef},
|
||||
{WASM_BLOCK_R(
|
||||
ValueType::RefNull(type_index), WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
|
||||
// Not taken for nullref.
|
||||
WASM_BR_ON_CAST(0, type_index), WASM_GC_OP(kExprRefCast), type_index,
|
||||
|
||||
WASM_LOCAL_SET(0, WASM_I32V(222))), // Final result.
|
||||
{WASM_BLOCK_R(ValueType::RefNull(type_index),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
|
||||
// Not taken for nullref.
|
||||
WASM_BR_ON_CAST(0, type_index), WASM_GC_OP(kExprRefCast),
|
||||
type_index), // Traps
|
||||
WASM_DROP, WASM_LOCAL_GET(0), kExprEnd});
|
||||
|
||||
const byte kTypedAfterBranch = tester.DefineFunction(
|
||||
@ -620,7 +631,8 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
|
||||
tester.CompileModule();
|
||||
tester.CheckResult(kTestStructStatic, 222);
|
||||
tester.CheckResult(kTestNull, 222);
|
||||
tester.CheckResult(kTestNullDeprecated, 222);
|
||||
tester.CheckHasThrown(kTestNull);
|
||||
tester.CheckResult(kTypedAfterBranch, 42);
|
||||
}
|
||||
|
||||
@ -1213,6 +1225,11 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
|
||||
{WASM_REF_TEST_DEPRECATED(WASM_STRUCT_NEW_DEFAULT(type_index), sig_index),
|
||||
kExprEnd});
|
||||
|
||||
const byte kRefCastNullDeprecated =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST_DEPRECATED(
|
||||
WASM_REF_NULL(type_index), subtype_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastNull =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST(
|
||||
@ -1223,24 +1240,32 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
|
||||
{WASM_REF_IS_NULL(
|
||||
WASM_REF_CAST(WASM_STRUCT_NEW_DEFAULT(subtype_index), type_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastUpcastNullDeprecated =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST_DEPRECATED(
|
||||
WASM_REF_NULL(subtype_index), type_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastUpcastNull =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST(
|
||||
WASM_REF_NULL(subtype_index), type_index)),
|
||||
kExprEnd});
|
||||
// Note: Casting of types from different type hierarchies is only valid for
|
||||
// the deprecated cast instruction.
|
||||
const byte kRefCastUnrelatedNullable = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {refNull(subtype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
|
||||
WASM_REF_IS_NULL(WASM_REF_CAST(WASM_LOCAL_GET(0), sig_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastUnrelatedNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(subtype_index), sig_index)),
|
||||
WASM_REF_IS_NULL(WASM_REF_CAST_DEPRECATED(WASM_LOCAL_GET(0), sig_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastUnrelatedNull =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST_DEPRECATED(
|
||||
WASM_REF_NULL(subtype_index), sig_index)),
|
||||
kExprEnd});
|
||||
const byte kRefCastUnrelatedNonNullable = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_NULL(
|
||||
WASM_REF_CAST(WASM_STRUCT_NEW_DEFAULT(type_index), sig_index)),
|
||||
{WASM_REF_IS_NULL(WASM_REF_CAST_DEPRECATED(
|
||||
WASM_STRUCT_NEW_DEFAULT(type_index), sig_index)),
|
||||
kExprEnd});
|
||||
|
||||
const byte kBrOnCastNull = tester.DefineFunction(
|
||||
@ -1340,9 +1365,11 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
|
||||
tester.CheckResult(kRefTestUnrelatedNullDeprecated, 0);
|
||||
tester.CheckResult(kRefTestUnrelatedNonNullableDeprecated, 0);
|
||||
|
||||
tester.CheckResult(kRefCastNull, 1);
|
||||
tester.CheckResult(kRefCastNullDeprecated, 1);
|
||||
tester.CheckHasThrown(kRefCastNull);
|
||||
tester.CheckResult(kRefCastUpcast, 0);
|
||||
tester.CheckResult(kRefCastUpcastNull, 1);
|
||||
tester.CheckResult(kRefCastUpcastNullDeprecated, 1);
|
||||
tester.CheckHasThrown(kRefCastUpcastNull);
|
||||
tester.CheckHasThrown(kRefCastUnrelatedNullable);
|
||||
tester.CheckResult(kRefCastUnrelatedNull, 1);
|
||||
tester.CheckHasThrown(kRefCastUnrelatedNonNullable);
|
||||
@ -1621,8 +1648,8 @@ WASM_COMPILED_EXEC_TEST(CastNullRef) {
|
||||
tester.CheckHasThrown(to_array);
|
||||
tester.CheckHasThrown(to_struct);
|
||||
tester.CheckHasThrown(to_i31);
|
||||
// Static ref.cast succeeds.
|
||||
tester.CheckResult(to_struct_idx, 1);
|
||||
// ref.cast traps on null.
|
||||
tester.CheckHasThrown(to_struct_idx);
|
||||
}
|
||||
|
||||
WASM_COMPILED_EXEC_TEST(CallReftypeParameters) {
|
||||
|
@ -525,6 +525,8 @@ inline uint16_t ExtractPrefixedOpcodeBytes(WasmOpcode opcode) {
|
||||
ref, WASM_GC_OP(kExprRefTestDeprecated), static_cast<byte>(typeidx)
|
||||
#define WASM_REF_TEST(ref, typeidx) \
|
||||
ref, WASM_GC_OP(kExprRefTest), static_cast<byte>(typeidx)
|
||||
#define WASM_REF_CAST_DEPRECATED(ref, typeidx) \
|
||||
ref, WASM_GC_OP(kExprRefCastDeprecated), static_cast<byte>(typeidx)
|
||||
#define WASM_REF_CAST(ref, typeidx) \
|
||||
ref, WASM_GC_OP(kExprRefCast), static_cast<byte>(typeidx)
|
||||
// Takes a reference value from the value stack to allow sequences of
|
||||
|
@ -58,6 +58,15 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kGCPrefix, kExprExternInternalize,
|
||||
kGCPrefix, kExprRefTestNull, typeCode,
|
||||
]).exportFunc();
|
||||
|
||||
builder.addFunction(`refCast${typeName}`,
|
||||
makeSig([kWasmExternRef], [kWasmExternRef]))
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprExternInternalize,
|
||||
kGCPrefix, kExprRefCast, typeCode,
|
||||
kGCPrefix, kExprExternExternalize,
|
||||
]).exportFunc();
|
||||
});
|
||||
|
||||
var instance = builder.instantiate();
|
||||
@ -134,4 +143,84 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals([1, 1], wasm.refTestAny(wasm.createFuncRef()));
|
||||
assertEquals([1, 1], wasm.refTestAny(1)); // ref.i31
|
||||
assertEquals([1, 1], wasm.refTestAny({'JavaScript': 'Object'}));
|
||||
|
||||
// ref.cast
|
||||
let structSuperObj = wasm.createStructSuper();
|
||||
let structSubObj = wasm.createStructSub();
|
||||
let arrayObj = wasm.createArray();
|
||||
let jsObj = {'JavaScript': 'Object'};
|
||||
let funcObj = wasm.createFuncRef();
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(undefined));
|
||||
assertSame(structSuperObj, wasm.refCastStructSuper(structSuperObj));
|
||||
assertSame(structSubObj, wasm.refCastStructSuper(structSubObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(funcObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSuper(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(undefined));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(structSuperObj));
|
||||
assertSame(structSubObj, wasm.refCastStructSub(structSubObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(funcObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStructSub(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(undefined));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(structSuperObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(structSubObj));
|
||||
assertSame(arrayObj, wasm.refCastArray(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(funcObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastArray(jsObj));
|
||||
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(undefined));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(structSuperObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(structSubObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(funcObj));
|
||||
assertEquals(1, wasm.refCastI31(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastI31(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(undefined));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(structSuperObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(structSubObj));
|
||||
assertSame(arrayObj, wasm.refCastAnyArray(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(funcObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAnyArray(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(undefined));
|
||||
assertSame(structSuperObj, wasm.refCastStruct(structSuperObj));
|
||||
assertSame(structSubObj, wasm.refCastStruct(structSubObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(funcObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastStruct(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastEq(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastEq(undefined));
|
||||
assertSame(structSuperObj, wasm.refCastEq(structSuperObj));
|
||||
assertSame(structSubObj, wasm.refCastEq(structSubObj));
|
||||
assertSame(arrayObj, wasm.refCastEq(arrayObj));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastEq(funcObj));
|
||||
assertEquals(1, wasm.refCastEq(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastEq(jsObj));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastAny(null));
|
||||
assertSame(undefined, wasm.refCastAny(undefined));
|
||||
assertSame(structSuperObj, wasm.refCastAny(structSuperObj));
|
||||
assertSame(structSubObj, wasm.refCastAny(structSubObj));
|
||||
assertSame(arrayObj, wasm.refCastAny(arrayObj));
|
||||
assertSame(funcObj, wasm.refCastAny(funcObj));
|
||||
assertEquals(1, wasm.refCastAny(1));
|
||||
assertSame(jsObj, wasm.refCastAny(jsObj));
|
||||
})();
|
||||
|
@ -25,6 +25,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
let casts = [
|
||||
kExprRefTest,
|
||||
kExprRefTestNull,
|
||||
kExprRefCast,
|
||||
];
|
||||
|
||||
for (let [source_type, target_type_imm] of types) {
|
||||
@ -33,10 +34,11 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(struct, builder.addStruct([makeField(kWasmI32, true)]));
|
||||
assertEquals(array, builder.addArray(kWasmI32));
|
||||
assertEquals(sig, builder.addType(makeSig([kWasmI32], [])));
|
||||
builder.addFunction('refTest', makeSig([kWasmI32], [source_type]))
|
||||
builder.addFunction('refTest', makeSig([source_type], []))
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, cast, target_type_imm,
|
||||
kExprDrop,
|
||||
]);
|
||||
|
||||
assertThrows(() => builder.instantiate(),
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
// Test casting null from one type to another using ref.test.
|
||||
(function RefTestNull() {
|
||||
// Test casting null from one type to another using ref.test & ref.cast.
|
||||
(function RefCastFromNull() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
let structSuper = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
@ -36,13 +36,19 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
];
|
||||
|
||||
for (let [sourceType, targetType, testName] of tests) {
|
||||
builder.addFunction('testNull' + testName,
|
||||
makeSig([], [kWasmI32]))
|
||||
builder.addFunction('testNull' + testName, makeSig([], [kWasmI32]))
|
||||
.addLocals(wasmRefNullType(sourceType), 1)
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprRefTest, targetType & kLeb128Mask,
|
||||
]).exportFunc();
|
||||
builder.addFunction('castNull' + testName, makeSig([], []))
|
||||
.addLocals(wasmRefNullType(sourceType), 1)
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprRefCast, targetType & kLeb128Mask,
|
||||
kExprDrop,
|
||||
]).exportFunc();
|
||||
}
|
||||
|
||||
let instance = builder.instantiate();
|
||||
@ -50,6 +56,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
for (let [sourceType, targetType, testName] of tests) {
|
||||
assertEquals(0, wasm['testNull' + testName]());
|
||||
assertTraps(kTrapIllegalCast, wasm['castNull' + testName]);
|
||||
}
|
||||
})();
|
||||
|
||||
@ -80,12 +87,56 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals([1, 0, 1, 1], wasm.testFromFuncRef(wasm.fctSub));
|
||||
})();
|
||||
|
||||
(function RefCastFuncRef() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
let sigSuper = builder.addType(makeSig([kWasmI32], []));
|
||||
let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper);
|
||||
|
||||
builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc();
|
||||
builder.addFunction('fctSub', sigSub).addBody([]).exportFunc();
|
||||
builder.addFunction('castToFuncRef', makeSig([kWasmFuncRef], [kWasmFuncRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, kFuncRefCode])
|
||||
.exportFunc();
|
||||
builder.addFunction('castToNullFuncRef',
|
||||
makeSig([kWasmFuncRef], [kWasmFuncRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, kNullFuncRefCode])
|
||||
.exportFunc();
|
||||
builder.addFunction('castToSuper', makeSig([kWasmFuncRef], [kWasmFuncRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, sigSuper])
|
||||
.exportFunc();
|
||||
builder.addFunction('castToSub', makeSig([kWasmFuncRef], [kWasmFuncRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, sigSub])
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let wasm = instance.exports;
|
||||
let jsFct = new WebAssembly.Function(
|
||||
{parameters:['i32', 'i32'], results: ['i32']},
|
||||
function mul(a, b) { return a * b; });
|
||||
assertSame(jsFct, wasm.castToFuncRef(jsFct));
|
||||
assertSame(wasm.fctSuper, wasm.castToFuncRef(wasm.fctSuper));
|
||||
assertSame(wasm.fctSub, wasm.castToFuncRef(wasm.fctSub));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullFuncRef(jsFct));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullFuncRef(wasm.fctSuper));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullFuncRef(wasm.fctSub));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToSuper(jsFct));
|
||||
assertSame(wasm.fctSuper, wasm.castToSuper(wasm.fctSuper));
|
||||
assertSame(wasm.fctSub, wasm.castToSuper(wasm.fctSub));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToSub(jsFct));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToSub(wasm.fctSuper));
|
||||
assertSame(wasm.fctSub, wasm.castToSub(wasm.fctSub));
|
||||
})();
|
||||
|
||||
(function RefTestExternRef() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction('testExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmI32, kWasmI32,]))
|
||||
makeSig([kWasmExternRef], [kWasmI32, kWasmI32]))
|
||||
.addBody([
|
||||
kExprLocalGet, 0, kGCPrefix, kExprRefTest, kExternRefCode,
|
||||
kExprLocalGet, 0, kGCPrefix, kExprRefTest, kNullExternRefCode,
|
||||
@ -100,6 +151,36 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals([1, 0], wasm.testExternRef(wasm.testExternRef));
|
||||
})();
|
||||
|
||||
(function RefCastExternRef() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction('castToExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmExternRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, kExternRefCode])
|
||||
.exportFunc();
|
||||
builder.addFunction('castToNullExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmExternRef]))
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprRefCast, kNullExternRefCode])
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let wasm = instance.exports;
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToExternRef(null));
|
||||
assertEquals(undefined, wasm.castToExternRef(undefined));
|
||||
assertEquals(1, wasm.castToExternRef(1));
|
||||
let obj = {};
|
||||
assertSame(obj, wasm.castToExternRef(obj));
|
||||
assertSame(wasm.castToExternRef, wasm.castToExternRef(wasm.castToExternRef));
|
||||
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullExternRef(null));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullExternRef(undefined));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullExternRef(1));
|
||||
assertTraps(kTrapIllegalCast, () => wasm.castToNullExternRef(obj));
|
||||
assertTraps(kTrapIllegalCast,
|
||||
() => wasm.castToNullExternRef(wasm.castToExternRef));
|
||||
})();
|
||||
|
||||
(function RefTestAnyRefHierarchy() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
@ -136,6 +217,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
source: 'any',
|
||||
values: ['nullref', 'i31ref', 'structSuper', 'structSub', 'array'],
|
||||
targets: {
|
||||
any: ['i31ref', 'structSuper', 'structSub', 'array'],
|
||||
eq: ['i31ref', 'structSuper', 'structSub', 'array'],
|
||||
struct: ['structSuper', 'structSub'],
|
||||
anyArray: ['array'],
|
||||
@ -225,6 +307,15 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprCallRef, creatorType,
|
||||
kGCPrefix, kExprRefTestNull, heapType,
|
||||
]).exportFunc();
|
||||
|
||||
builder.addFunction(`cast_${test.source}_to_${target}`,
|
||||
makeSig([wasmRefType(creatorType)], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kExprCallRef, creatorType,
|
||||
kGCPrefix, kExprRefCast, heapType,
|
||||
kExprRefIsNull, // We can't expose the cast object to JS in most cases.
|
||||
]).exportFunc();
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,6 +333,14 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
res = wasm[`test_null_${test.source}_to_${target}`](create_value);
|
||||
assertEquals(
|
||||
(validValues.includes(value) || value == "nullref") ? 1 : 0, res);
|
||||
|
||||
print(`Test ref.cast: ${test.source}(${value}) -> ${target}`);
|
||||
let cast = wasm[`cast_${test.source}_to_${target}`];
|
||||
if (validValues.includes(value)) {
|
||||
assertEquals(0, cast(create_value));
|
||||
} else {
|
||||
assertTraps(kTrapIllegalCast, () => cast(create_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -468,8 +468,9 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.addBody([
|
||||
// Cast from struct_a to struct_b via common base type struct_super.
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprRefCast, struct_super,
|
||||
kGCPrefix, kExprRefCast, struct_b, // annotated as 'ref null none'
|
||||
// TODO(7748): Replace cast op with "ref.cast null".
|
||||
kGCPrefix, kExprRefCastDeprecated, struct_super,
|
||||
kGCPrefix, kExprRefCastDeprecated, struct_b, // annotated as 'ref null none'
|
||||
kExprRefIsNull,
|
||||
]);
|
||||
|
||||
@ -514,7 +515,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
// local.get 0 is known to be null until end of block.
|
||||
kExprLocalGet, 0,
|
||||
// This cast is a no-op and shold be optimized away.
|
||||
kGCPrefix, kExprRefCast, struct_b,
|
||||
// TODO(7748): Replace with "ref.cast null".
|
||||
kGCPrefix, kExprRefCastDeprecated, struct_b,
|
||||
kExprEnd,
|
||||
kExprRefIsNull,
|
||||
]);
|
||||
|
@ -510,7 +510,8 @@ let kExprI31GetU = 0x22;
|
||||
let kExprRefTest = 0x40;
|
||||
let kExprRefTestNull = 0x48;
|
||||
let kExprRefTestDeprecated = 0x44;
|
||||
let kExprRefCast = 0x45;
|
||||
let kExprRefCast = 0x41;
|
||||
let kExprRefCastDeprecated = 0x45;
|
||||
let kExprBrOnCast = 0x46;
|
||||
let kExprBrOnCastFail = 0x47;
|
||||
let kExprRefCastNop = 0x4c;
|
||||
|
@ -1160,8 +1160,9 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
|
||||
ExpectValidates(sigs.i_v(),
|
||||
{WASM_UNREACHABLE, WASM_GC_OP(kExprRefTest), kEqRefCode});
|
||||
|
||||
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefCast),
|
||||
struct_index, kExprDrop});
|
||||
ExpectValidates(sigs.v_v(),
|
||||
{WASM_UNREACHABLE, WASM_GC_OP(kExprRefCastDeprecated),
|
||||
struct_index, kExprDrop});
|
||||
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefCast),
|
||||
struct_index, kExprDrop});
|
||||
|
||||
@ -1170,6 +1171,9 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
|
||||
ExpectValidates(&sig_v_s, {WASM_UNREACHABLE, WASM_LOCAL_GET(0), kExprBrOnNull,
|
||||
0, kExprCallFunction, struct_consumer});
|
||||
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(zone(), {struct_type}, {}),
|
||||
{WASM_UNREACHABLE, WASM_GC_OP(kExprRefCastDeprecated), struct_index});
|
||||
ExpectValidates(FunctionSig::Build(zone(), {struct_type}, {}),
|
||||
{WASM_UNREACHABLE, WASM_GC_OP(kExprRefCast), struct_index});
|
||||
|
||||
@ -4331,7 +4335,7 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
|
||||
HeapType from_heap = HeapType(std::get<0>(test));
|
||||
HeapType to_heap = HeapType(std::get<1>(test));
|
||||
bool should_pass = std::get<2>(test);
|
||||
bool should_pass_ref_test = std::get<3>(test);
|
||||
bool should_pass_new_ops = std::get<3>(test);
|
||||
SCOPED_TRACE("from_heap = " + from_heap.name() +
|
||||
", to_heap = " + to_heap.name());
|
||||
|
||||
@ -4346,7 +4350,8 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
|
||||
ExpectValidates(&test_sig,
|
||||
{WASM_REF_TEST_DEPRECATED(WASM_LOCAL_GET(0),
|
||||
WASM_HEAP_TYPE(to_heap))});
|
||||
ExpectValidates(&cast_sig, {WASM_REF_CAST(WASM_LOCAL_GET(0),
|
||||
ExpectValidates(&cast_sig,
|
||||
{WASM_REF_CAST_DEPRECATED(WASM_LOCAL_GET(0),
|
||||
WASM_HEAP_TYPE(to_heap))});
|
||||
} else {
|
||||
std::string error_message =
|
||||
@ -4358,22 +4363,29 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
|
||||
WASM_HEAP_TYPE(to_heap))},
|
||||
kAppendEnd, ("ref.test" + error_message).c_str());
|
||||
ExpectFailure(&cast_sig,
|
||||
{WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_HEAP_TYPE(to_heap))},
|
||||
{WASM_REF_CAST_DEPRECATED(WASM_LOCAL_GET(0),
|
||||
WASM_HEAP_TYPE(to_heap))},
|
||||
kAppendEnd, ("ref.cast" + error_message).c_str());
|
||||
}
|
||||
|
||||
if (should_pass_ref_test) {
|
||||
if (should_pass_new_ops) {
|
||||
ExpectValidates(&test_sig, {WASM_REF_TEST(WASM_LOCAL_GET(0),
|
||||
WASM_HEAP_TYPE(to_heap))});
|
||||
ExpectValidates(&cast_sig, {WASM_REF_CAST(WASM_LOCAL_GET(0),
|
||||
WASM_HEAP_TYPE(to_heap))});
|
||||
} else {
|
||||
std::string error_message =
|
||||
"Invalid types for ref.test: local.get of type " +
|
||||
cast_reps[1].name() +
|
||||
"local.get of type " + cast_reps[1].name() +
|
||||
" has to be in the same reference type hierarchy as (ref " +
|
||||
to_heap.name() + ")";
|
||||
ExpectFailure(&test_sig,
|
||||
{WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_HEAP_TYPE(to_heap))},
|
||||
kAppendEnd, error_message.c_str());
|
||||
kAppendEnd,
|
||||
("Invalid types for ref.test: " + error_message).c_str());
|
||||
ExpectFailure(&cast_sig,
|
||||
{WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_HEAP_TYPE(to_heap))},
|
||||
kAppendEnd,
|
||||
("Invalid types for ref.cast: " + error_message).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4389,10 +4401,15 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
|
||||
"Invalid types for ref.test: i32.const of type i32 has to be "
|
||||
"in the same reference type hierarchy as (ref 0)");
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_REF_CAST(WASM_I32V(1), array_heap), kExprDrop},
|
||||
{WASM_REF_CAST_DEPRECATED(WASM_I32V(1), array_heap), kExprDrop},
|
||||
kAppendEnd,
|
||||
"ref.cast[0] expected subtype of (ref null func), (ref null "
|
||||
"struct) or (ref null array), found i32.const of type i32");
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_REF_CAST(WASM_I32V(1), array_heap), kExprDrop},
|
||||
kAppendEnd,
|
||||
"Invalid types for ref.cast: i32.const of type i32 has to be "
|
||||
"in the same reference type hierarchy as (ref 0)");
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, BrOnCastOrCastFail) {
|
||||
@ -4407,6 +4424,10 @@ TEST_F(FunctionBodyDecoderTest, BrOnCastOrCastFail) {
|
||||
ValueType supertype = ValueType::RefNull(super_struct);
|
||||
ValueType subtype = ValueType::RefNull(sub_struct);
|
||||
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(this->zone(), {kWasmI32, subtype}, {supertype}),
|
||||
{WASM_I32V(42), WASM_LOCAL_GET(0), WASM_BR_ON_CAST(0, sub_struct),
|
||||
WASM_GC_OP(kExprRefCast), sub_struct});
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(this->zone(), {kWasmI32, subtype}, {supertype}),
|
||||
{WASM_I32V(42), WASM_LOCAL_GET(0), WASM_BR_ON_CAST(0, sub_struct),
|
||||
|
Loading…
Reference in New Issue
Block a user