[wasm-gc] Add new br_on_cast_fail variant taking a heap type immediate
Adds new `br_on_cast_fail <branch depth> <heap type>` instruction with opcode 0xfb43. The instruction branches if the cast fails. `null` is treated as a cast failure (meaning the branch is taken). The heap type may be any concreate heap type index or an abstract type like `(ref null eq)`. Bug: v8:7748 Change-Id: I97a78d6d0872703ab825016cab4e737f8f79995f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4084981 Reviewed-by: Manos Koukoutos <manoskouk@chromium.org> Commit-Queue: Matthias Liedtke <mliedtke@chromium.org> Cr-Commit-Position: refs/heads/main@{#84733}
This commit is contained in:
parent
bf17a2c78f
commit
c2a1261355
@ -6159,6 +6159,31 @@ class LiftoffCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
void BrOnCastFailAbstract(FullDecoder* decoder, const Value& obj,
|
||||
HeapType type, Value* result_on_fallthrough,
|
||||
uint32_t depth) {
|
||||
switch (type.representation()) {
|
||||
case HeapType::kEq:
|
||||
return BrOnNonEq(decoder, obj, result_on_fallthrough, depth);
|
||||
case HeapType::kI31:
|
||||
return BrOnNonI31(decoder, obj, result_on_fallthrough, depth);
|
||||
case HeapType::kStruct:
|
||||
return BrOnNonStruct(decoder, obj, result_on_fallthrough, depth);
|
||||
case HeapType::kArray:
|
||||
return BrOnNonArray(decoder, obj, result_on_fallthrough, depth);
|
||||
case HeapType::kNone:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
// TODO(mliedtke): This becomes reachable for `br_on_cast_fail null`.
|
||||
UNREACHABLE();
|
||||
case HeapType::kAny:
|
||||
// Any may never need a cast as it is either implicitly convertible or
|
||||
// never convertible for any given type.
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeCheck {
|
||||
Register obj_reg = no_reg;
|
||||
ValueType obj_type;
|
||||
@ -6435,6 +6460,11 @@ class LiftoffCompiler {
|
||||
br_depth);
|
||||
}
|
||||
|
||||
void BrOnNonEq(FullDecoder* decoder, const Value& object,
|
||||
Value* /* value_on_branch */, uint32_t br_depth) {
|
||||
BrOnNonAbstractType<&LiftoffCompiler::EqCheck>(object, decoder, br_depth);
|
||||
}
|
||||
|
||||
void StringNewWtf8(FullDecoder* decoder, const MemoryIndexImmediate& imm,
|
||||
const unibrow::Utf8Variant variant, const Value& offset,
|
||||
const Value& size, Value* result) {
|
||||
|
@ -1101,6 +1101,8 @@ struct ControlBase : public PcForErrors<ValidationTag::full_validation> {
|
||||
Value* result_on_fallthrough, uint32_t depth) \
|
||||
F(BrOnCastAbstract, const Value& obj, HeapType type, \
|
||||
Value* result_on_branch, uint32_t depth, bool null_succeeds) \
|
||||
F(BrOnCastFailAbstract, const Value& obj, HeapType type, \
|
||||
Value* result_on_fallthrough, uint32_t depth) \
|
||||
F(RefIsStruct, const Value& object, Value* result) \
|
||||
F(RefIsEq, const Value& object, Value* result) \
|
||||
F(RefIsI31, const Value& object, Value* result) \
|
||||
@ -2207,6 +2209,7 @@ class WasmDecoder : public Decoder {
|
||||
(ios.HeapType(imm), ...);
|
||||
return length + branch.length + imm.length;
|
||||
}
|
||||
case kExprBrOnCastFailDeprecated:
|
||||
case kExprBrOnCastDeprecated: {
|
||||
BranchDepthImmediate branch(decoder, pc + length, validate);
|
||||
IndexImmediate index(decoder, pc + length + branch.length,
|
||||
@ -2437,6 +2440,7 @@ class WasmDecoder : public Decoder {
|
||||
case kExprBrOnCast:
|
||||
case kExprBrOnCastNull:
|
||||
case kExprBrOnCastFail:
|
||||
case kExprBrOnCastFailDeprecated:
|
||||
case kExprBrOnCastDeprecated:
|
||||
return {1, 1};
|
||||
case kExprStructSet:
|
||||
@ -5321,6 +5325,98 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
return pc_offset;
|
||||
}
|
||||
case kExprBrOnCastFail: {
|
||||
NON_CONST_ONLY
|
||||
BranchDepthImmediate branch_depth(this, this->pc_ + opcode_length,
|
||||
validate);
|
||||
if (!this->Validate(this->pc_ + opcode_length, branch_depth,
|
||||
control_.size())) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t pc_offset = opcode_length + branch_depth.length;
|
||||
HeapTypeImmediate imm(this->enabled_, this, this->pc_ + pc_offset,
|
||||
validate);
|
||||
this->Validate(this->pc_ + opcode_length, imm);
|
||||
if (!VALIDATE(this->ok())) return 0;
|
||||
pc_offset += 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());
|
||||
// Differently to other instructions we don't push the RTT yet.
|
||||
}
|
||||
|
||||
Value obj = Peek(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 br_on_cast_fail: %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;
|
||||
}
|
||||
|
||||
Control* c = control_at(branch_depth.depth);
|
||||
if (c->br_merge()->arity == 0) {
|
||||
this->DecodeError(
|
||||
"br_on_cast_fail must target a branch of arity at least 1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
|
||||
bool null_succeeds = false;
|
||||
Value result_on_fallthrough = CreateValue(ValueType::Ref(target_type));
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
// This logic ensures that code generation can assume that functions
|
||||
// can only be cast between compatible types.
|
||||
if (V8_UNLIKELY(
|
||||
TypeCheckAlwaysFails(obj, target_type, null_succeeds))) {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(Drop); // rtt
|
||||
}
|
||||
// The types are incompatible (i.e. neither of the two types is a
|
||||
// subtype of the other). Always branch.
|
||||
CALL_INTERFACE(BrOrRet, branch_depth.depth, 0);
|
||||
// We know that the following code is not reachable, but according
|
||||
// to the spec it technically is. Set it to spec-only reachable.
|
||||
SetSucceedingCodeDynamicallyUnreachable();
|
||||
c->br_merge()->reached = true;
|
||||
} else if (V8_UNLIKELY(TypeCheckAlwaysSucceeds(obj, target_type))) {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(Drop); // rtt
|
||||
}
|
||||
// The branch can still be taken on null.
|
||||
if (obj.type.is_nullable()) {
|
||||
CALL_INTERFACE(BrOnNull, obj, branch_depth.depth, true,
|
||||
&result_on_fallthrough);
|
||||
c->br_merge()->reached = true;
|
||||
}
|
||||
// Otherwise, the type check always succeeds. Do not branch. Also,
|
||||
// the object is already on the stack; do not manipulate the stack.
|
||||
} else {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(BrOnCastFail, obj, rtt.value(),
|
||||
&result_on_fallthrough, branch_depth.depth);
|
||||
} else {
|
||||
CALL_INTERFACE(BrOnCastFailAbstract, obj, target_type,
|
||||
&result_on_fallthrough, branch_depth.depth);
|
||||
}
|
||||
c->br_merge()->reached = true;
|
||||
}
|
||||
}
|
||||
// Make sure the correct value is on the stack state on fallthrough.
|
||||
Drop(obj);
|
||||
Push(result_on_fallthrough);
|
||||
return pc_offset;
|
||||
}
|
||||
case kExprBrOnCastFailDeprecated: {
|
||||
NON_CONST_ONLY
|
||||
BranchDepthImmediate branch_depth(this, this->pc_ + opcode_length,
|
||||
validate);
|
||||
@ -5350,12 +5446,7 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
"br_on_cast_fail must target a branch of arity at least 1");
|
||||
return 0;
|
||||
}
|
||||
// Attention: contrary to most other instructions, we modify the stack
|
||||
// before calling the interface function. This makes it significantly
|
||||
// more convenient to pass around the values that will be on the stack
|
||||
// when the branch is taken. In this case, we leave {obj} on the stack
|
||||
// to type check the branch.
|
||||
// TODO(jkummerow): Reconsider this choice.
|
||||
|
||||
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
|
||||
Value result_on_fallthrough = CreateValue(ValueType::Ref(imm.index));
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
|
@ -1336,6 +1336,30 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
}
|
||||
|
||||
void BrOnCastFailAbstract(FullDecoder* decoder, const Value& object,
|
||||
HeapType type, Value* value_on_branch,
|
||||
uint32_t br_depth) {
|
||||
switch (type.representation()) {
|
||||
case HeapType::kEq:
|
||||
return BrOnNonEq(decoder, object, value_on_branch, br_depth);
|
||||
case HeapType::kI31:
|
||||
return BrOnNonI31(decoder, object, value_on_branch, br_depth);
|
||||
case HeapType::kStruct:
|
||||
return BrOnNonStruct(decoder, object, value_on_branch, br_depth);
|
||||
case HeapType::kArray:
|
||||
return BrOnNonArray(decoder, object, value_on_branch, br_depth);
|
||||
case HeapType::kNone:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
// TODO(mliedtke): Implement for br_on_cast_fail null.
|
||||
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 RefIsEq(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
bool null_succeeds = false;
|
||||
SetAndTypeNode(result,
|
||||
@ -1350,6 +1374,17 @@ class WasmGraphBuildingInterface {
|
||||
true, null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnNonEq(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_fallthrough, uint32_t br_depth) {
|
||||
bool null_succeeds = false;
|
||||
// TODO(7748): Merge BrOn* and BrOnNon* instructions as their only
|
||||
// difference is a boolean flag passed to BrOnCastAbs. This could also be
|
||||
// leveraged to merge BrOnCastFailAbstract and BrOnCastAbstract.
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnEq>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_fallthrough,
|
||||
br_depth, false, null_succeeds);
|
||||
}
|
||||
|
||||
void RefIsStruct(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
bool null_succeeds = false;
|
||||
SetAndTypeNode(result,
|
||||
|
@ -716,7 +716,8 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
|
||||
V(BrOnCast, 0xfb42, _, "br_on_cast") \
|
||||
V(BrOnCastNull, 0xfb4a, _, "br_on_cast null") \
|
||||
V(BrOnCastDeprecated, 0xfb46, _, "br_on_cast") \
|
||||
V(BrOnCastFail, 0xfb47, _, "br_on_cast_fail") \
|
||||
V(BrOnCastFail, 0xfb43, _, "br_on_cast_fail") \
|
||||
V(BrOnCastFailDeprecated, 0xfb47, _, "br_on_cast_fail") \
|
||||
V(RefCastNop, 0xfb4c, _, "ref.cast_nop") \
|
||||
V(RefIsStruct, 0xfb51, _, "ref.is_struct") \
|
||||
V(RefIsI31, 0xfb52, _, "ref.is_i31") \
|
||||
|
@ -1373,14 +1373,14 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
|
||||
tester.sigs.i_v(), {refNull(subtype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
|
||||
WASM_BLOCK_R(refNull(subtype_index), WASM_LOCAL_GET(0),
|
||||
WASM_BR_ON_CAST_FAIL(0, sig_index), WASM_DROP,
|
||||
WASM_BR_ON_CAST_FAIL_DEPRECATED(0, sig_index), WASM_DROP,
|
||||
WASM_RETURN(WASM_I32V(0))),
|
||||
WASM_DROP, WASM_I32V(1), WASM_END});
|
||||
|
||||
const byte kBrOnCastFailUnrelatedNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_BLOCK_R(refNull(subtype_index), WASM_REF_NULL(subtype_index),
|
||||
WASM_BR_ON_CAST_FAIL(0, sig_index), WASM_DROP,
|
||||
WASM_BR_ON_CAST_FAIL_DEPRECATED(0, sig_index), WASM_DROP,
|
||||
WASM_RETURN(WASM_I32V(0))),
|
||||
WASM_DROP, WASM_I32V(1), WASM_END});
|
||||
|
||||
@ -1388,7 +1388,7 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_BLOCK_R(refNull(subtype_index),
|
||||
WASM_STRUCT_NEW_DEFAULT(subtype_index),
|
||||
WASM_BR_ON_CAST_FAIL(0, sig_index), WASM_DROP,
|
||||
WASM_BR_ON_CAST_FAIL_DEPRECATED(0, sig_index), WASM_DROP,
|
||||
WASM_RETURN(WASM_I32V(0))),
|
||||
WASM_DROP, WASM_I32V(1), WASM_END});
|
||||
|
||||
|
@ -547,6 +547,9 @@ inline uint16_t ExtractPrefixedOpcodeBytes(WasmOpcode opcode) {
|
||||
#define WASM_BR_ON_CAST_FAIL(depth, typeidx) \
|
||||
WASM_GC_OP(kExprBrOnCastFail), static_cast<byte>(depth), \
|
||||
static_cast<byte>(typeidx)
|
||||
#define WASM_BR_ON_CAST_FAIL_DEPRECATED(depth, typeidx) \
|
||||
WASM_GC_OP(kExprBrOnCastFailDeprecated), static_cast<byte>(depth), \
|
||||
static_cast<byte>(typeidx)
|
||||
|
||||
#define WASM_GC_INTERNALIZE(extern) extern, WASM_GC_OP(kExprExternInternalize)
|
||||
#define WASM_GC_EXTERNALIZE(ref) ref, WASM_GC_OP(kExprExternExternalize)
|
||||
|
@ -108,6 +108,20 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
builder.addFunction(`brOnCastFail${typeName}`,
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kAnyRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprExternInternalize,
|
||||
kGCPrefix, kExprBrOnCastFail, 0, typeCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
});
|
||||
|
||||
var instance = builder.instantiate();
|
||||
@ -527,4 +541,86 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(0, wasm.brOnCastNullNone(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullNone(1));
|
||||
assertEquals(0, wasm.brOnCastNullNone(jsObj));
|
||||
|
||||
// br_on_cast_fail
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(null));
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(undefined));
|
||||
assertEquals(0, wasm.brOnCastFailStructSuper(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastFailStructSuper(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(1));
|
||||
assertEquals(1, wasm.brOnCastFailStructSuper(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(null));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(undefined));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastFailStructSub(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(1));
|
||||
assertEquals(1, wasm.brOnCastFailStructSub(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailArray(null));
|
||||
assertEquals(1, wasm.brOnCastFailArray(undefined));
|
||||
assertEquals(1, wasm.brOnCastFailArray(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastFailArray(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastFailArray(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailArray(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailArray(1));
|
||||
assertEquals(1, wasm.brOnCastFailArray(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailI31(null));
|
||||
assertEquals(1, wasm.brOnCastFailI31(undefined));
|
||||
assertEquals(1, wasm.brOnCastFailI31(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastFailI31(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastFailI31(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailI31(funcObj));
|
||||
assertEquals(0, wasm.brOnCastFailI31(1));
|
||||
assertEquals(1, wasm.brOnCastFailI31(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(null));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(undefined));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastFailAnyArray(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(1));
|
||||
assertEquals(1, wasm.brOnCastFailAnyArray(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailStruct(null));
|
||||
assertEquals(1, wasm.brOnCastFailStruct(undefined));
|
||||
assertEquals(0, wasm.brOnCastFailStruct(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastFailStruct(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastFailStruct(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailStruct(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailStruct(1));
|
||||
assertEquals(1, wasm.brOnCastFailStruct(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailEq(null));
|
||||
assertEquals(1, wasm.brOnCastFailEq(undefined));
|
||||
assertEquals(0, wasm.brOnCastFailEq(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastFailEq(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastFailEq(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailEq(funcObj));
|
||||
assertEquals(0, wasm.brOnCastFailEq(1));
|
||||
assertEquals(1, wasm.brOnCastFailEq(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailAny(null));
|
||||
assertEquals(0, wasm.brOnCastFailAny(undefined));
|
||||
assertEquals(0, wasm.brOnCastFailAny(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastFailAny(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastFailAny(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastFailAny(funcObj));
|
||||
assertEquals(0, wasm.brOnCastFailAny(1));
|
||||
assertEquals(0, wasm.brOnCastFailAny(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFailNone(null));
|
||||
assertEquals(1, wasm.brOnCastFailNone(undefined));
|
||||
assertEquals(1, wasm.brOnCastFailNone(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastFailNone(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastFailNone(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastFailNone(funcObj));
|
||||
assertEquals(1, wasm.brOnCastFailNone(1));
|
||||
assertEquals(1, wasm.brOnCastFailNone(jsObj));
|
||||
})();
|
||||
|
@ -245,6 +245,20 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
|
||||
builder.addFunction(`brOnCastFail_${name}`,
|
||||
makeSig([kWasmFuncRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kFuncRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastFail, 0, typeCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
}
|
||||
|
||||
let instance = builder.instantiate();
|
||||
@ -291,6 +305,26 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(0, wasm.brOnCastNull_nullfuncref(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCastNull_super(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCastNull_sub(wasm.fctSub));
|
||||
|
||||
assertEquals(1, wasm.brOnCastFail_funcref(null));
|
||||
assertEquals(1, wasm.brOnCastFail_nullfuncref(null));
|
||||
assertEquals(1, wasm.brOnCastFail_super(null));
|
||||
assertEquals(1, wasm.brOnCastFail_sub(null));
|
||||
|
||||
assertEquals(0, wasm.brOnCastFail_funcref(jsFct));
|
||||
assertEquals(1, wasm.brOnCastFail_nullfuncref(jsFct));
|
||||
assertEquals(1, wasm.brOnCastFail_super(jsFct));
|
||||
assertEquals(1, wasm.brOnCastFail_sub(jsFct));
|
||||
|
||||
assertEquals(0, wasm.brOnCastFail_funcref(wasm.fctSuper));
|
||||
assertEquals(1, wasm.brOnCastFail_nullfuncref(wasm.fctSuper));
|
||||
assertEquals(0, wasm.brOnCastFail_super(wasm.fctSuper));
|
||||
assertEquals(1, wasm.brOnCastFail_sub(wasm.fctSuper));
|
||||
|
||||
assertEquals(0, wasm.brOnCastFail_funcref(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCastFail_nullfuncref(wasm.fctSub));
|
||||
assertEquals(0, wasm.brOnCastFail_super(wasm.fctSub));
|
||||
assertEquals(0, wasm.brOnCastFail_sub(wasm.fctSub));
|
||||
})();
|
||||
|
||||
(function RefTestExternRef() {
|
||||
@ -441,6 +475,35 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('castFailToExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, kExternRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastFail, 0, kExternRefCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
builder.addFunction('castFailToNullExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, kExternRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastFail, 0, kNullExternRefCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let wasm = instance.exports;
|
||||
let obj = {};
|
||||
@ -468,6 +531,18 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(0, wasm.castNullToNullExternRef(1));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(obj));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(wasm.castToExternRef));
|
||||
|
||||
assertEquals(1, wasm.castFailToExternRef(null));
|
||||
assertEquals(0, wasm.castFailToExternRef(undefined));
|
||||
assertEquals(0, wasm.castFailToExternRef(1));
|
||||
assertEquals(0, wasm.castFailToExternRef(obj));
|
||||
assertEquals(0, wasm.castFailToExternRef(wasm.castToExternRef));
|
||||
|
||||
assertEquals(1, wasm.castFailToNullExternRef(null));
|
||||
assertEquals(1, wasm.castFailToNullExternRef(undefined));
|
||||
assertEquals(1, wasm.castFailToNullExternRef(1));
|
||||
assertEquals(1, wasm.castFailToNullExternRef(obj));
|
||||
assertEquals(1, wasm.castFailToNullExternRef(wasm.castToExternRef));
|
||||
})();
|
||||
|
||||
|
||||
@ -653,6 +728,23 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
let sourceHeapType = sourceType.heap_type ?? (sourceType & kLeb128Mask);
|
||||
builder.addFunction(`brOnCastFail_${test.source}_to_${target}`,
|
||||
makeSig([wasmRefType(creatorType)], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, sourceHeapType,
|
||||
kExprLocalGet, 0,
|
||||
kExprCallRef, ...wasmUnsignedLeb(creatorType),
|
||||
kGCPrefix, kExprBrOnCastFail, 0, heapType,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,6 +785,10 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
res = wasm[`brOnCastNull_${test.source}_to_${target}`](create_value);
|
||||
assertEquals(
|
||||
validValues.includes(value) || value == "nullref" ? 1 : 0, res);
|
||||
|
||||
print(`Test br_on_cast_fail: ${test.source}(${value}) -> ${target}`);
|
||||
res = wasm[`brOnCastFail_${test.source}_to_${target}`](create_value);
|
||||
assertEquals(!validValues.includes(value) || value == "nullref" ? 1 : 0, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -518,7 +518,7 @@ let kExprRefCastDeprecated = 0x45;
|
||||
let kExprBrOnCast = 0x42;
|
||||
let kExprBrOnCastNull = 0x4a;
|
||||
let kExprBrOnCastDeprecated = 0x46;
|
||||
let kExprBrOnCastFail = 0x47;
|
||||
let kExprBrOnCastFail = 0x43;
|
||||
let kExprRefCastNop = 0x4c;
|
||||
let kExprRefIsData = 0x51;
|
||||
let kExprRefIsI31 = 0x52;
|
||||
|
@ -4474,8 +4474,8 @@ TEST_F(FunctionBodyDecoderTest, BrOnCastOrCastFail) {
|
||||
ExpectFailure(
|
||||
FunctionSig::Build(this->zone(), {supertype}, {kWasmExternRef}),
|
||||
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST_FAIL(0, sub_struct)}, kAppendEnd,
|
||||
"br_on_cast_fail[0] expected subtype of (ref null func), (ref null "
|
||||
"struct) or (ref null array), found local.get of type externref");
|
||||
"Invalid types for br_on_cast_fail: local.get of type externref has to "
|
||||
"be in the same reference type hierarchy as (ref 1)");
|
||||
|
||||
// Cast between types of different type hierarchies is invalid.
|
||||
ExpectFailure(
|
||||
|
Loading…
Reference in New Issue
Block a user