[wasm-gc] Add new br_on_cast null variant taking a heap type immediate
Adds new `br_on_cast null <branch depth> <heap type>` instruction with opcode 0xfb4a. The instruction branches on null. The heap type may be any concreate heap type index or an abstract type like `(ref null eq)`. Bug: v8:7748 Change-Id: I0f1debacc80a304f7cfc262fd2cde7f43fc804d3 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4075086 Commit-Queue: Matthias Liedtke <mliedtke@chromium.org> Reviewed-by: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#84703}
This commit is contained in:
parent
90722a662c
commit
ac4c5c468d
@ -5703,7 +5703,13 @@ void WasmGraphBuilder::BrOnEq(Node* object, Node* /*rtt*/,
|
||||
BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
|
||||
[=](Callbacks callbacks) -> void {
|
||||
if (config.from.is_nullable()) {
|
||||
callbacks.fail_if(gasm_->IsNull(object), BranchHint::kFalse);
|
||||
if (config.to.is_nullable()) {
|
||||
callbacks.succeed_if(gasm_->IsNull(object),
|
||||
BranchHint::kFalse);
|
||||
} else {
|
||||
callbacks.fail_if(gasm_->IsNull(object),
|
||||
BranchHint::kFalse);
|
||||
}
|
||||
}
|
||||
callbacks.succeed_if(gasm_->IsI31(object), BranchHint::kFalse);
|
||||
Node* map = gasm_->LoadMap(object);
|
||||
@ -5747,7 +5753,7 @@ void WasmGraphBuilder::BrOnStruct(Node* object, Node* /*rtt*/,
|
||||
Node** match_control, Node** match_effect,
|
||||
Node** no_match_control,
|
||||
Node** no_match_effect) {
|
||||
bool null_succeeds = false;
|
||||
bool null_succeeds = config.to.is_nullable();
|
||||
BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
|
||||
[=](Callbacks callbacks) -> void {
|
||||
if (!v8_flags.wasm_gc_structref_as_dataref) {
|
||||
@ -5787,7 +5793,7 @@ void WasmGraphBuilder::BrOnArray(Node* object, Node* /*rtt*/,
|
||||
Node** match_control, Node** match_effect,
|
||||
Node** no_match_control,
|
||||
Node** no_match_effect) {
|
||||
bool null_succeeds = false;
|
||||
bool null_succeeds = config.to.is_nullable();
|
||||
BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
|
||||
[=](Callbacks callbacks) -> void {
|
||||
return ManagedObjectInstanceCheck(
|
||||
@ -5823,15 +5829,21 @@ Node* WasmGraphBuilder::RefAsI31(Node* object, wasm::WasmCodePosition position,
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::BrOnI31(Node* object, Node* /* rtt */,
|
||||
WasmTypeCheckConfig /* config */,
|
||||
Node** match_control, Node** match_effect,
|
||||
Node** no_match_control,
|
||||
WasmTypeCheckConfig config, Node** match_control,
|
||||
Node** match_effect, Node** no_match_control,
|
||||
Node** no_match_effect) {
|
||||
gasm_->Branch(gasm_->IsI31(object), match_control, no_match_control,
|
||||
BranchHint::kTrue);
|
||||
SetControl(*no_match_control);
|
||||
*match_effect = effect();
|
||||
*no_match_effect = effect();
|
||||
BrOnCastAbs(
|
||||
match_control, match_effect, no_match_control, no_match_effect,
|
||||
[=](Callbacks callbacks) -> void {
|
||||
if (config.from.is_nullable()) {
|
||||
if (config.to.is_nullable()) {
|
||||
callbacks.succeed_if(gasm_->IsNull(object), BranchHint::kFalse);
|
||||
} else {
|
||||
callbacks.fail_if(gasm_->IsNull(object), BranchHint::kFalse);
|
||||
}
|
||||
}
|
||||
callbacks.fail_if_not(gasm_->IsI31(object), BranchHint::kTrue);
|
||||
});
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::TypeGuard(Node* value, wasm::ValueType type) {
|
||||
|
@ -6079,7 +6079,8 @@ class LiftoffCompiler {
|
||||
}
|
||||
|
||||
void BrOnCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
|
||||
Value* /* result_on_branch */, uint32_t depth) {
|
||||
Value* /* result_on_branch */, uint32_t depth,
|
||||
bool null_succeeds) {
|
||||
// Avoid having sequences of branches do duplicate work.
|
||||
if (depth != decoder->control_depth() - 1) {
|
||||
__ PrepareForBranch(decoder->control_at(depth)->br_merge()->arity, {});
|
||||
@ -6095,8 +6096,9 @@ class LiftoffCompiler {
|
||||
if (obj.type.is_nullable()) LoadNullValue(scratch_null, pinned);
|
||||
FREEZE_STATE(frozen);
|
||||
|
||||
NullSucceeds null_handling = null_succeeds ? kNullSucceeds : kNullFails;
|
||||
SubtypeCheck(decoder->module_, obj_reg.gp(), obj.type, rtt_reg.gp(),
|
||||
rtt.type, scratch_null, scratch2, &cont_false, kNullFails,
|
||||
rtt.type, scratch_null, scratch2, &cont_false, null_handling,
|
||||
frozen);
|
||||
|
||||
BrOrRetImpl(decoder, depth, scratch_null, scratch2);
|
||||
@ -6133,21 +6135,22 @@ class LiftoffCompiler {
|
||||
}
|
||||
|
||||
void BrOnCastAbstract(FullDecoder* decoder, const Value& obj, HeapType type,
|
||||
Value* result_on_branch, uint32_t depth) {
|
||||
Value* result_on_branch, uint32_t depth,
|
||||
bool null_succeeds) {
|
||||
switch (type.representation()) {
|
||||
case HeapType::kEq:
|
||||
return BrOnEq(decoder, obj, result_on_branch, depth);
|
||||
return BrOnEq(decoder, obj, result_on_branch, depth, null_succeeds);
|
||||
case HeapType::kI31:
|
||||
return BrOnI31(decoder, obj, result_on_branch, depth);
|
||||
return BrOnI31(decoder, obj, result_on_branch, depth, null_succeeds);
|
||||
case HeapType::kStruct:
|
||||
return BrOnStruct(decoder, obj, result_on_branch, depth);
|
||||
return BrOnStruct(decoder, obj, result_on_branch, depth, null_succeeds);
|
||||
case HeapType::kArray:
|
||||
return BrOnArray(decoder, obj, result_on_branch, depth);
|
||||
return BrOnArray(decoder, obj, result_on_branch, depth, null_succeeds);
|
||||
case HeapType::kNone:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
// TODO(mliedtke): This becomes reachable for `br_on_cast null`.
|
||||
UNREACHABLE();
|
||||
DCHECK(null_succeeds);
|
||||
return BrOnNull(decoder, obj, depth, true, nullptr);
|
||||
case HeapType::kAny:
|
||||
// Any may never need a cast as it is either implicitly convertible or
|
||||
// never convertible for any given type.
|
||||
@ -6341,19 +6344,24 @@ class LiftoffCompiler {
|
||||
|
||||
template <TypeChecker type_checker>
|
||||
void BrOnAbstractType(const Value& object, FullDecoder* decoder,
|
||||
uint32_t br_depth) {
|
||||
bool null_succeeds = false; // TODO(mliedtke): Use parameter.
|
||||
uint32_t br_depth, bool null_succeeds) {
|
||||
// Avoid having sequences of branches do duplicate work.
|
||||
if (br_depth != decoder->control_depth() - 1) {
|
||||
__ PrepareForBranch(decoder->control_at(br_depth)->br_merge()->arity, {});
|
||||
}
|
||||
|
||||
Label no_match;
|
||||
Label no_match, match;
|
||||
TypeCheck check(object.type, &no_match, null_succeeds);
|
||||
Initialize(check, kPeek);
|
||||
FREEZE_STATE(frozen);
|
||||
|
||||
if (null_succeeds && check.obj_type.is_nullable()) {
|
||||
__ emit_cond_jump(kEqual, &match, kRefNull, check.obj_reg,
|
||||
check.null_reg(), frozen);
|
||||
}
|
||||
|
||||
(this->*type_checker)(check, frozen);
|
||||
__ bind(&match);
|
||||
BrOrRetImpl(decoder, br_depth, check.tmp1, check.tmp2);
|
||||
|
||||
__ bind(&no_match);
|
||||
@ -6383,23 +6391,31 @@ class LiftoffCompiler {
|
||||
}
|
||||
|
||||
void BrOnEq(FullDecoder* decoder, const Value& object,
|
||||
Value* /* value_on_branch */, uint32_t br_depth) {
|
||||
BrOnAbstractType<&LiftoffCompiler::EqCheck>(object, decoder, br_depth);
|
||||
Value* /* value_on_branch */, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnAbstractType<&LiftoffCompiler::EqCheck>(object, decoder, br_depth,
|
||||
null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnStruct(FullDecoder* decoder, const Value& object,
|
||||
Value* /* value_on_branch */, uint32_t br_depth) {
|
||||
BrOnAbstractType<&LiftoffCompiler::StructCheck>(object, decoder, br_depth);
|
||||
Value* /* value_on_branch */, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnAbstractType<&LiftoffCompiler::StructCheck>(object, decoder, br_depth,
|
||||
null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnI31(FullDecoder* decoder, const Value& object,
|
||||
Value* /* value_on_branch */, uint32_t br_depth) {
|
||||
BrOnAbstractType<&LiftoffCompiler::I31Check>(object, decoder, br_depth);
|
||||
Value* /* value_on_branch */, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnAbstractType<&LiftoffCompiler::I31Check>(object, decoder, br_depth,
|
||||
null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnArray(FullDecoder* decoder, const Value& object,
|
||||
Value* /* value_on_branch */, uint32_t br_depth) {
|
||||
BrOnAbstractType<&LiftoffCompiler::ArrayCheck>(object, decoder, br_depth);
|
||||
Value* /* value_on_branch */, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnAbstractType<&LiftoffCompiler::ArrayCheck>(object, decoder, br_depth,
|
||||
null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnNonStruct(FullDecoder* decoder, const Value& object,
|
||||
|
@ -1096,11 +1096,11 @@ struct ControlBase : public PcForErrors<ValidationTag::full_validation> {
|
||||
F(AssertNullTypecheck, const Value& obj, Value* result) \
|
||||
F(AssertNotNullTypecheck, const Value& obj, Value* result) \
|
||||
F(BrOnCast, const Value& obj, const Value& rtt, Value* result_on_branch, \
|
||||
uint32_t depth) \
|
||||
uint32_t depth, bool null_succeeds) \
|
||||
F(BrOnCastFail, const Value& obj, const Value& rtt, \
|
||||
Value* result_on_fallthrough, uint32_t depth) \
|
||||
F(BrOnCastAbstract, const Value& obj, HeapType type, \
|
||||
Value* result_on_branch, uint32_t depth) \
|
||||
Value* result_on_branch, uint32_t depth, bool null_succeeds) \
|
||||
F(RefIsStruct, const Value& object, Value* result) \
|
||||
F(RefIsEq, const Value& object, Value* result) \
|
||||
F(RefIsI31, const Value& object, Value* result) \
|
||||
@ -1109,9 +1109,11 @@ struct ControlBase : public PcForErrors<ValidationTag::full_validation> {
|
||||
F(RefAsI31, const Value& object, Value* result) \
|
||||
F(RefAsArray, const Value& object, Value* result) \
|
||||
F(BrOnStruct, const Value& object, Value* value_on_branch, \
|
||||
uint32_t br_depth) \
|
||||
F(BrOnI31, const Value& object, Value* value_on_branch, uint32_t br_depth) \
|
||||
F(BrOnArray, const Value& object, Value* value_on_branch, uint32_t br_depth) \
|
||||
uint32_t br_depth, bool null_succeeds) \
|
||||
F(BrOnI31, const Value& object, Value* value_on_branch, uint32_t br_depth, \
|
||||
bool null_succeeds) \
|
||||
F(BrOnArray, const Value& object, Value* value_on_branch, uint32_t br_depth, \
|
||||
bool null_succeeds) \
|
||||
F(BrOnNonStruct, const Value& object, Value* value_on_fallthrough, \
|
||||
uint32_t br_depth) \
|
||||
F(BrOnNonI31, const Value& object, Value* value_on_fallthrough, \
|
||||
@ -2196,6 +2198,7 @@ class WasmDecoder : public Decoder {
|
||||
return length + imm.length;
|
||||
}
|
||||
case kExprBrOnCast:
|
||||
case kExprBrOnCastNull:
|
||||
case kExprBrOnCastFail: {
|
||||
BranchDepthImmediate branch(decoder, pc + length, validate);
|
||||
HeapTypeImmediate imm(WasmFeatures::All(), decoder,
|
||||
@ -2432,6 +2435,7 @@ class WasmDecoder : public Decoder {
|
||||
case kExprRefCastDeprecated:
|
||||
case kExprRefCastNop:
|
||||
case kExprBrOnCast:
|
||||
case kExprBrOnCastNull:
|
||||
case kExprBrOnCastFail:
|
||||
case kExprBrOnCastDeprecated:
|
||||
return {1, 1};
|
||||
@ -5142,7 +5146,8 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
Push(value);
|
||||
return opcode_length;
|
||||
}
|
||||
case kExprBrOnCast: {
|
||||
case kExprBrOnCast:
|
||||
case kExprBrOnCastNull: {
|
||||
NON_CONST_ONLY
|
||||
BranchDepthImmediate branch_depth(this, this->pc_ + opcode_length,
|
||||
validate);
|
||||
@ -5194,9 +5199,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
// will be on the stack when the branch is taken.
|
||||
// TODO(jkummerow): Reconsider this choice.
|
||||
Drop(obj);
|
||||
// TODO(mliedtke): Use RefNull for br_on_cast_null.
|
||||
bool null_succeeds = false;
|
||||
Push(CreateValue(ValueType::Ref(target_type)));
|
||||
bool null_succeeds = opcode == kExprBrOnCastNull;
|
||||
Push(CreateValue(ValueType::RefMaybeNull(
|
||||
imm.type, (obj.type.is_bottom() || !null_succeeds)
|
||||
? kNonNullable
|
||||
: obj.type.nullability())));
|
||||
// The {value_on_branch} parameter we pass to the interface must
|
||||
// be pointer-identical to the object on the stack.
|
||||
Value* value_on_branch = stack_value(1);
|
||||
@ -5208,8 +5215,9 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(Drop); // rtt
|
||||
}
|
||||
// The branch will still not be taken on null.
|
||||
if (obj.type.is_nullable()) {
|
||||
// The branch will still not be taken on null if not
|
||||
// {null_succeeds}.
|
||||
if (obj.type.is_nullable() && !null_succeeds) {
|
||||
CALL_INTERFACE(BrOnNonNull, obj, value_on_branch,
|
||||
branch_depth.depth, false);
|
||||
} else {
|
||||
@ -5224,10 +5232,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
null_succeeds))) {
|
||||
if (rtt.has_value()) {
|
||||
CALL_INTERFACE(BrOnCast, obj, rtt.value(), value_on_branch,
|
||||
branch_depth.depth);
|
||||
branch_depth.depth, null_succeeds);
|
||||
} else {
|
||||
CALL_INTERFACE(BrOnCastAbstract, obj, target_type,
|
||||
value_on_branch, branch_depth.depth);
|
||||
value_on_branch, branch_depth.depth,
|
||||
null_succeeds);
|
||||
}
|
||||
c->br_merge()->reached = true;
|
||||
}
|
||||
@ -5299,8 +5308,9 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
}
|
||||
c->br_merge()->reached = true;
|
||||
} else if (V8_LIKELY(!TypeCheckAlwaysFails(obj, rtt))) {
|
||||
bool null_succeeds = false;
|
||||
CALL_INTERFACE(BrOnCast, obj, rtt, value_on_branch,
|
||||
branch_depth.depth);
|
||||
branch_depth.depth, null_succeeds);
|
||||
c->br_merge()->reached = true;
|
||||
}
|
||||
// Otherwise the types are unrelated. Do not branch.
|
||||
@ -5487,13 +5497,16 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
// {result_on_branch} which was passed-by-value to {Push}.
|
||||
Value* value_on_branch = stack_value(1);
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
bool null_succeeds = false;
|
||||
if (opcode == kExprBrOnStruct) {
|
||||
CALL_INTERFACE(BrOnStruct, obj, value_on_branch,
|
||||
branch_depth.depth);
|
||||
CALL_INTERFACE(BrOnStruct, obj, value_on_branch, branch_depth.depth,
|
||||
null_succeeds);
|
||||
} else if (opcode == kExprBrOnArray) {
|
||||
CALL_INTERFACE(BrOnArray, obj, value_on_branch, branch_depth.depth);
|
||||
CALL_INTERFACE(BrOnArray, obj, value_on_branch, branch_depth.depth,
|
||||
null_succeeds);
|
||||
} else {
|
||||
CALL_INTERFACE(BrOnI31, obj, value_on_branch, branch_depth.depth);
|
||||
CALL_INTERFACE(BrOnI31, obj, value_on_branch, branch_depth.depth,
|
||||
null_succeeds);
|
||||
}
|
||||
c->br_merge()->reached = true;
|
||||
}
|
||||
|
@ -1261,12 +1261,14 @@ class WasmGraphBuildingInterface {
|
||||
TFNode**)>
|
||||
void BrOnCastAbs(FullDecoder* decoder, const Value& object, const Value& rtt,
|
||||
Value* forwarding_value, uint32_t br_depth,
|
||||
bool branch_on_match) {
|
||||
// TODO(mliedtke): Add generic br_on_cast instructions where null succeeds.
|
||||
WasmTypeCheckConfig config = {object.type,
|
||||
!rtt.type.is_bottom()
|
||||
? ValueType::Ref(rtt.type.ref_index())
|
||||
: kWasmBottom};
|
||||
bool branch_on_match, bool null_succeeds) {
|
||||
// If the type is bottom (used for abstract types), set HeapType to None.
|
||||
// The heap type is not read but the null information is needed for the
|
||||
// cast.
|
||||
ValueType to_type = ValueType::RefMaybeNull(
|
||||
!rtt.type.is_bottom() ? rtt.type.ref_index() : HeapType::kNone,
|
||||
null_succeeds ? kNullable : kNonNullable);
|
||||
WasmTypeCheckConfig config = {object.type, to_type};
|
||||
SsaEnv* branch_env = Split(decoder->zone(), ssa_env_);
|
||||
SsaEnv* no_branch_env = Steal(decoder->zone(), ssa_env_);
|
||||
no_branch_env->SetNotMerged();
|
||||
@ -1286,34 +1288,46 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void BrOnCast(FullDecoder* decoder, const Value& object, const Value& rtt,
|
||||
Value* value_on_branch, uint32_t br_depth) {
|
||||
Value* value_on_branch, uint32_t br_depth, bool null_succeeds) {
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>(
|
||||
decoder, object, rtt, value_on_branch, br_depth, true);
|
||||
decoder, object, rtt, value_on_branch, br_depth, true, null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnCastFail(FullDecoder* decoder, const Value& object, const Value& rtt,
|
||||
Value* value_on_fallthrough, uint32_t br_depth) {
|
||||
bool null_succeeds = false;
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>(
|
||||
decoder, object, rtt, value_on_fallthrough, br_depth, false);
|
||||
decoder, object, rtt, value_on_fallthrough, br_depth, false,
|
||||
null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnCastAbstract(FullDecoder* decoder, const Value& object,
|
||||
HeapType type, Value* value_on_branch,
|
||||
uint32_t br_depth) {
|
||||
uint32_t br_depth, bool null_succeeds) {
|
||||
switch (type.representation()) {
|
||||
case HeapType::kEq:
|
||||
return BrOnEq(decoder, object, value_on_branch, br_depth);
|
||||
return BrOnEq(decoder, object, value_on_branch, br_depth,
|
||||
null_succeeds);
|
||||
case HeapType::kI31:
|
||||
return BrOnI31(decoder, object, value_on_branch, br_depth);
|
||||
return BrOnI31(decoder, object, value_on_branch, br_depth,
|
||||
null_succeeds);
|
||||
case HeapType::kStruct:
|
||||
return BrOnStruct(decoder, object, value_on_branch, br_depth);
|
||||
return BrOnStruct(decoder, object, value_on_branch, br_depth,
|
||||
null_succeeds);
|
||||
case HeapType::kArray:
|
||||
return BrOnArray(decoder, object, value_on_branch, br_depth);
|
||||
return BrOnArray(decoder, object, value_on_branch, br_depth,
|
||||
null_succeeds);
|
||||
case HeapType::kNone:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
// TODO(mliedtke): This becomes reachable for `br_on_cast null`.
|
||||
UNREACHABLE();
|
||||
DCHECK(null_succeeds);
|
||||
// This is needed for BrOnNull. {value_on_branch} is on the value stack
|
||||
// and BrOnNull interacts with the values on the stack.
|
||||
// TODO(7748): The compiler shouldn't have to access the stack used by
|
||||
// the decoder ideally.
|
||||
SetAndTypeNode(value_on_branch,
|
||||
builder_->TypeGuard(object.node, value_on_branch->type));
|
||||
return BrOnNull(decoder, object, br_depth, true, value_on_branch);
|
||||
case HeapType::kAny:
|
||||
// Any may never need a cast as it is either implicitly convertible or
|
||||
// never convertible for any given type.
|
||||
@ -1330,10 +1344,10 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void BrOnEq(FullDecoder* decoder, const Value& object, Value* value_on_branch,
|
||||
uint32_t br_depth) {
|
||||
uint32_t br_depth, bool null_succeeds) {
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnEq>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth,
|
||||
true);
|
||||
true, null_succeeds);
|
||||
}
|
||||
|
||||
void RefIsStruct(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
@ -1353,17 +1367,19 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void BrOnStruct(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_branch, uint32_t br_depth) {
|
||||
Value* value_on_branch, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnStruct>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth,
|
||||
true);
|
||||
true, null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnNonStruct(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_fallthrough, uint32_t br_depth) {
|
||||
bool null_succeeds = false;
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnStruct>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_fallthrough,
|
||||
br_depth, false);
|
||||
br_depth, false, null_succeeds);
|
||||
}
|
||||
|
||||
void RefIsArray(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
@ -1383,17 +1399,19 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void BrOnArray(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_branch, uint32_t br_depth) {
|
||||
Value* value_on_branch, uint32_t br_depth,
|
||||
bool null_succeeds) {
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnArray>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth,
|
||||
true);
|
||||
true, null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnNonArray(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_fallthrough, uint32_t br_depth) {
|
||||
bool null_succeeds = false;
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnArray>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_fallthrough,
|
||||
br_depth, false);
|
||||
br_depth, false, null_succeeds);
|
||||
}
|
||||
|
||||
void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) {
|
||||
@ -1410,17 +1428,18 @@ class WasmGraphBuildingInterface {
|
||||
}
|
||||
|
||||
void BrOnI31(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_branch, uint32_t br_depth) {
|
||||
Value* value_on_branch, uint32_t br_depth, bool null_succeeds) {
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnI31>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth,
|
||||
true);
|
||||
true, null_succeeds);
|
||||
}
|
||||
|
||||
void BrOnNonI31(FullDecoder* decoder, const Value& object,
|
||||
Value* value_on_fallthrough, uint32_t br_depth) {
|
||||
bool null_succeeds = false;
|
||||
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnI31>(
|
||||
decoder, object, Value{nullptr, kWasmBottom}, value_on_fallthrough,
|
||||
br_depth, false);
|
||||
br_depth, false, null_succeeds);
|
||||
}
|
||||
|
||||
void StringNewWtf8(FullDecoder* decoder, const MemoryIndexImmediate& memory,
|
||||
|
@ -714,6 +714,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
|
||||
V(RefCastNull, 0xfb49, _, "ref.cast null") \
|
||||
V(RefCastDeprecated, 0xfb45, _, "ref.cast") \
|
||||
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(RefCastNop, 0xfb4c, _, "ref.cast_nop") \
|
||||
|
@ -592,6 +592,29 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
WASM_GC_OP(kExprStructGet), type_index, 0, WASM_LOCAL_GET(0),
|
||||
kExprI32Add, kExprEnd});
|
||||
|
||||
const byte kTestStructStaticNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kWasmStructRef},
|
||||
{WASM_BLOCK_R(
|
||||
ValueType::RefNull(type_index), WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
// Pipe a struct through a local so it's statically typed as
|
||||
// structref.
|
||||
WASM_LOCAL_SET(1, WASM_STRUCT_NEW(other_type_index, WASM_F32(1.0))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// The type check fails, so this branch isn't taken.
|
||||
WASM_BR_ON_CAST(0, type_index), WASM_DROP,
|
||||
|
||||
WASM_LOCAL_SET(0, WASM_I32V(221)), // (Final result) - 1
|
||||
WASM_LOCAL_SET(1, WASM_STRUCT_NEW(type_index, WASM_I32V(1))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// This branch is taken.
|
||||
WASM_BR_ON_CAST_NULL(0, type_index), WASM_GC_OP(kExprRefCast),
|
||||
type_index,
|
||||
|
||||
// Not executed due to the branch.
|
||||
WASM_LOCAL_SET(0, WASM_I32V(333))),
|
||||
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),
|
||||
@ -614,6 +637,17 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
type_index), // Traps
|
||||
WASM_DROP, WASM_LOCAL_GET(0), kExprEnd});
|
||||
|
||||
// "br_on_cast null" also branches on null, treating it as a successful cast.
|
||||
const byte kTestNullNull = 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.
|
||||
// Taken for nullref with br_on_cast null.
|
||||
WASM_BR_ON_CAST_NULL(0, type_index),
|
||||
WASM_GC_OP(kExprRefCast), type_index),
|
||||
WASM_DROP, WASM_LOCAL_GET(0), kExprEnd});
|
||||
|
||||
const byte kTypedAfterBranch = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kWasmStructRef},
|
||||
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW(type_index, WASM_I32V(42))),
|
||||
@ -631,8 +665,10 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
|
||||
tester.CompileModule();
|
||||
tester.CheckResult(kTestStructStatic, 222);
|
||||
tester.CheckResult(kTestStructStaticNull, 222);
|
||||
tester.CheckResult(kTestNullDeprecated, 222);
|
||||
tester.CheckHasThrown(kTestNull);
|
||||
tester.CheckResult(kTestNullNull, 111);
|
||||
tester.CheckResult(kTypedAfterBranch, 42);
|
||||
}
|
||||
|
||||
|
@ -534,6 +534,9 @@ inline uint16_t ExtractPrefixedOpcodeBytes(WasmOpcode opcode) {
|
||||
#define WASM_BR_ON_CAST(depth, typeidx) \
|
||||
WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth), \
|
||||
static_cast<byte>(typeidx)
|
||||
#define WASM_BR_ON_CAST_NULL(depth, typeidx) \
|
||||
WASM_GC_OP(kExprBrOnCastNull), static_cast<byte>(depth), \
|
||||
static_cast<byte>(typeidx)
|
||||
#define WASM_BR_ON_CAST_DEPRECATED(depth, typeidx) \
|
||||
WASM_GC_OP(kExprBrOnCastDeprecated), static_cast<byte>(depth), \
|
||||
static_cast<byte>(typeidx)
|
||||
|
@ -80,7 +80,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kGCPrefix, kExprExternExternalize,
|
||||
]).exportFunc();
|
||||
|
||||
builder.addFunction(`brOn${typeName}`,
|
||||
builder.addFunction(`brOnCast${typeName}`,
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRef, typeCode,
|
||||
@ -94,6 +94,20 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
builder.addFunction(`brOnCastNull${typeName}`,
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, typeCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprExternInternalize,
|
||||
kGCPrefix, kExprBrOnCastNull, 0, typeCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
});
|
||||
|
||||
var instance = builder.instantiate();
|
||||
@ -351,84 +365,166 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertTraps(kTrapIllegalCast, () => wasm.refCastNullNone(jsObj));
|
||||
|
||||
// br_on_cast
|
||||
assertEquals(0, wasm.brOnStructSuper(null));
|
||||
assertEquals(0, wasm.brOnStructSuper(undefined));
|
||||
assertEquals(1, wasm.brOnStructSuper(structSuperObj));
|
||||
assertEquals(1, wasm.brOnStructSuper(structSubObj));
|
||||
assertEquals(0, wasm.brOnStructSuper(arrayObj));
|
||||
assertEquals(0, wasm.brOnStructSuper(funcObj));
|
||||
assertEquals(0, wasm.brOnStructSuper(1));
|
||||
assertEquals(0, wasm.brOnStructSuper(jsObj));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(null));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(undefined));
|
||||
assertEquals(1, wasm.brOnCastStructSuper(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastStructSuper(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(funcObj));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(1));
|
||||
assertEquals(0, wasm.brOnCastStructSuper(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnStructSub(null));
|
||||
assertEquals(0, wasm.brOnStructSub(undefined));
|
||||
assertEquals(0, wasm.brOnStructSub(structSuperObj));
|
||||
assertEquals(1, wasm.brOnStructSub(structSubObj));
|
||||
assertEquals(0, wasm.brOnStructSub(arrayObj));
|
||||
assertEquals(0, wasm.brOnStructSub(funcObj));
|
||||
assertEquals(0, wasm.brOnStructSub(1));
|
||||
assertEquals(0, wasm.brOnStructSub(jsObj));
|
||||
assertEquals(0, wasm.brOnCastStructSub(null));
|
||||
assertEquals(0, wasm.brOnCastStructSub(undefined));
|
||||
assertEquals(0, wasm.brOnCastStructSub(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastStructSub(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastStructSub(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastStructSub(funcObj));
|
||||
assertEquals(0, wasm.brOnCastStructSub(1));
|
||||
assertEquals(0, wasm.brOnCastStructSub(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnArray(null));
|
||||
assertEquals(0, wasm.brOnArray(undefined));
|
||||
assertEquals(0, wasm.brOnArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnArray(funcObj));
|
||||
assertEquals(0, wasm.brOnArray(1));
|
||||
assertEquals(0, wasm.brOnArray(jsObj));
|
||||
assertEquals(0, wasm.brOnCastArray(null));
|
||||
assertEquals(0, wasm.brOnCastArray(undefined));
|
||||
assertEquals(0, wasm.brOnCastArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastArray(funcObj));
|
||||
assertEquals(0, wasm.brOnCastArray(1));
|
||||
assertEquals(0, wasm.brOnCastArray(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnI31(null));
|
||||
assertEquals(0, wasm.brOnI31(undefined));
|
||||
assertEquals(0, wasm.brOnI31(structSuperObj));
|
||||
assertEquals(0, wasm.brOnI31(structSubObj));
|
||||
assertEquals(0, wasm.brOnI31(arrayObj));
|
||||
assertEquals(0, wasm.brOnI31(funcObj));
|
||||
assertEquals(1, wasm.brOnI31(1));
|
||||
assertEquals(0, wasm.brOnI31(jsObj));
|
||||
assertEquals(0, wasm.brOnCastI31(null));
|
||||
assertEquals(0, wasm.brOnCastI31(undefined));
|
||||
assertEquals(0, wasm.brOnCastI31(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastI31(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastI31(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastI31(funcObj));
|
||||
assertEquals(1, wasm.brOnCastI31(1));
|
||||
assertEquals(0, wasm.brOnCastI31(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnAnyArray(null));
|
||||
assertEquals(0, wasm.brOnAnyArray(undefined));
|
||||
assertEquals(0, wasm.brOnAnyArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnAnyArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnAnyArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnAnyArray(funcObj));
|
||||
assertEquals(0, wasm.brOnAnyArray(1));
|
||||
assertEquals(0, wasm.brOnAnyArray(jsObj));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(null));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(undefined));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastAnyArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(funcObj));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(1));
|
||||
assertEquals(0, wasm.brOnCastAnyArray(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnStruct(null));
|
||||
assertEquals(0, wasm.brOnStruct(undefined));
|
||||
assertEquals(1, wasm.brOnStruct(structSuperObj));
|
||||
assertEquals(1, wasm.brOnStruct(structSubObj));
|
||||
assertEquals(0, wasm.brOnStruct(arrayObj));
|
||||
assertEquals(0, wasm.brOnStruct(funcObj));
|
||||
assertEquals(0, wasm.brOnStruct(1));
|
||||
assertEquals(0, wasm.brOnStruct(jsObj));
|
||||
assertEquals(0, wasm.brOnCastStruct(null));
|
||||
assertEquals(0, wasm.brOnCastStruct(undefined));
|
||||
assertEquals(1, wasm.brOnCastStruct(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastStruct(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastStruct(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastStruct(funcObj));
|
||||
assertEquals(0, wasm.brOnCastStruct(1));
|
||||
assertEquals(0, wasm.brOnCastStruct(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnEq(null));
|
||||
assertEquals(0, wasm.brOnEq(undefined));
|
||||
assertEquals(1, wasm.brOnEq(structSuperObj));
|
||||
assertEquals(1, wasm.brOnEq(structSubObj));
|
||||
assertEquals(1, wasm.brOnEq(arrayObj));
|
||||
assertEquals(0, wasm.brOnEq(funcObj));
|
||||
assertEquals(1, wasm.brOnEq(1));
|
||||
assertEquals(0, wasm.brOnEq(jsObj));
|
||||
assertEquals(0, wasm.brOnCastEq(null));
|
||||
assertEquals(0, wasm.brOnCastEq(undefined));
|
||||
assertEquals(1, wasm.brOnCastEq(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastEq(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastEq(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastEq(funcObj));
|
||||
assertEquals(1, wasm.brOnCastEq(1));
|
||||
assertEquals(0, wasm.brOnCastEq(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnAny(null));
|
||||
assertEquals(1, wasm.brOnAny(undefined));
|
||||
assertEquals(1, wasm.brOnAny(structSuperObj));
|
||||
assertEquals(1, wasm.brOnAny(structSubObj));
|
||||
assertEquals(1, wasm.brOnAny(arrayObj));
|
||||
assertEquals(1, wasm.brOnAny(funcObj));
|
||||
assertEquals(1, wasm.brOnAny(1));
|
||||
assertEquals(1, wasm.brOnAny(jsObj));
|
||||
assertEquals(0, wasm.brOnCastAny(null));
|
||||
assertEquals(1, wasm.brOnCastAny(undefined));
|
||||
assertEquals(1, wasm.brOnCastAny(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastAny(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastAny(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastAny(funcObj));
|
||||
assertEquals(1, wasm.brOnCastAny(1));
|
||||
assertEquals(1, wasm.brOnCastAny(jsObj));
|
||||
|
||||
assertEquals(0, wasm.brOnNone(null));
|
||||
assertEquals(0, wasm.brOnNone(undefined));
|
||||
assertEquals(0, wasm.brOnNone(structSuperObj));
|
||||
assertEquals(0, wasm.brOnNone(structSubObj));
|
||||
assertEquals(0, wasm.brOnNone(arrayObj));
|
||||
assertEquals(0, wasm.brOnNone(funcObj));
|
||||
assertEquals(0, wasm.brOnNone(1));
|
||||
assertEquals(0, wasm.brOnNone(jsObj));
|
||||
assertEquals(0, wasm.brOnCastNone(null));
|
||||
assertEquals(0, wasm.brOnCastNone(undefined));
|
||||
assertEquals(0, wasm.brOnCastNone(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastNone(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNone(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNone(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNone(1));
|
||||
assertEquals(0, wasm.brOnCastNone(jsObj));
|
||||
|
||||
// br_on_cast null
|
||||
assertEquals(1, wasm.brOnCastNullStructSuper(null));
|
||||
assertEquals(0, wasm.brOnCastNullStructSuper(undefined));
|
||||
assertEquals(1, wasm.brOnCastNullStructSuper(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastNullStructSuper(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSuper(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSuper(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSuper(1));
|
||||
assertEquals(0, wasm.brOnCastNullStructSuper(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullStructSub(null));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(undefined));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastNullStructSub(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(1));
|
||||
assertEquals(0, wasm.brOnCastNullStructSub(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullArray(null));
|
||||
assertEquals(0, wasm.brOnCastNullArray(undefined));
|
||||
assertEquals(0, wasm.brOnCastNullArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastNullArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastNullArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullArray(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullArray(1));
|
||||
assertEquals(0, wasm.brOnCastNullArray(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullI31(null));
|
||||
assertEquals(0, wasm.brOnCastNullI31(undefined));
|
||||
assertEquals(0, wasm.brOnCastNullI31(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastNullI31(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNullI31(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullI31(funcObj));
|
||||
assertEquals(1, wasm.brOnCastNullI31(1));
|
||||
assertEquals(0, wasm.brOnCastNullI31(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullAnyArray(null));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(undefined));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastNullAnyArray(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(1));
|
||||
assertEquals(0, wasm.brOnCastNullAnyArray(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullStruct(null));
|
||||
assertEquals(0, wasm.brOnCastNullStruct(undefined));
|
||||
assertEquals(1, wasm.brOnCastNullStruct(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastNullStruct(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNullStruct(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullStruct(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullStruct(1));
|
||||
assertEquals(0, wasm.brOnCastNullStruct(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullEq(null));
|
||||
assertEquals(0, wasm.brOnCastNullEq(undefined));
|
||||
assertEquals(1, wasm.brOnCastNullEq(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastNullEq(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastNullEq(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullEq(funcObj));
|
||||
assertEquals(1, wasm.brOnCastNullEq(1));
|
||||
assertEquals(0, wasm.brOnCastNullEq(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullAny(null));
|
||||
assertEquals(1, wasm.brOnCastNullAny(undefined));
|
||||
assertEquals(1, wasm.brOnCastNullAny(structSuperObj));
|
||||
assertEquals(1, wasm.brOnCastNullAny(structSubObj));
|
||||
assertEquals(1, wasm.brOnCastNullAny(arrayObj));
|
||||
assertEquals(1, wasm.brOnCastNullAny(funcObj));
|
||||
assertEquals(1, wasm.brOnCastNullAny(1));
|
||||
assertEquals(1, wasm.brOnCastNullAny(jsObj));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNullNone(null));
|
||||
assertEquals(0, wasm.brOnCastNullNone(undefined));
|
||||
assertEquals(0, wasm.brOnCastNullNone(structSuperObj));
|
||||
assertEquals(0, wasm.brOnCastNullNone(structSubObj));
|
||||
assertEquals(0, wasm.brOnCastNullNone(arrayObj));
|
||||
assertEquals(0, wasm.brOnCastNullNone(funcObj));
|
||||
assertEquals(0, wasm.brOnCastNullNone(1));
|
||||
assertEquals(0, wasm.brOnCastNullNone(jsObj));
|
||||
})();
|
||||
|
@ -231,6 +231,20 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
|
||||
builder.addFunction(`brOnCastNull_${name}`,
|
||||
makeSig([kWasmFuncRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, typeCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastNull, 0, typeCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
]).exportFunc();
|
||||
}
|
||||
|
||||
let instance = builder.instantiate();
|
||||
@ -257,6 +271,26 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(0, wasm.brOnCast_nullfuncref(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCast_super(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCast_sub(wasm.fctSub));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNull_funcref(null));
|
||||
assertEquals(1, wasm.brOnCastNull_nullfuncref(null));
|
||||
assertEquals(1, wasm.brOnCastNull_super(null));
|
||||
assertEquals(1, wasm.brOnCastNull_sub(null));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNull_funcref(jsFct));
|
||||
assertEquals(0, wasm.brOnCastNull_nullfuncref(jsFct));
|
||||
assertEquals(0, wasm.brOnCastNull_super(jsFct));
|
||||
assertEquals(0, wasm.brOnCastNull_sub(jsFct));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNull_funcref(wasm.fctSuper));
|
||||
assertEquals(0, wasm.brOnCastNull_nullfuncref(wasm.fctSuper));
|
||||
assertEquals(1, wasm.brOnCastNull_super(wasm.fctSuper));
|
||||
assertEquals(0, wasm.brOnCastNull_sub(wasm.fctSuper));
|
||||
|
||||
assertEquals(1, wasm.brOnCastNull_funcref(wasm.fctSub));
|
||||
assertEquals(0, wasm.brOnCastNull_nullfuncref(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCastNull_super(wasm.fctSub));
|
||||
assertEquals(1, wasm.brOnCastNull_sub(wasm.fctSub));
|
||||
})();
|
||||
|
||||
(function RefTestExternRef() {
|
||||
@ -378,6 +412,35 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('castNullToExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, kExternRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastNull, 0, kExternRefCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
builder.addFunction('castNullToNullExternRef',
|
||||
makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, kNullExternRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprBrOnCastNull, 0, kNullExternRefCode,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let wasm = instance.exports;
|
||||
let obj = {};
|
||||
@ -394,6 +457,17 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(0, wasm.castToNullExternRef(obj));
|
||||
assertEquals(0, wasm.castToNullExternRef(wasm.castToExternRef));
|
||||
|
||||
assertEquals(1, wasm.castNullToExternRef(null));
|
||||
assertEquals(1, wasm.castNullToExternRef(undefined));
|
||||
assertEquals(1, wasm.castNullToExternRef(1));
|
||||
assertEquals(1, wasm.castNullToExternRef(obj));
|
||||
assertEquals(1, wasm.castNullToExternRef(wasm.castToExternRef));
|
||||
|
||||
assertEquals(1, wasm.castNullToNullExternRef(null));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(undefined));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(1));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(obj));
|
||||
assertEquals(0, wasm.castNullToNullExternRef(wasm.castToExternRef));
|
||||
})();
|
||||
|
||||
|
||||
@ -563,6 +637,22 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction(`brOnCastNull_${test.source}_to_${target}`,
|
||||
makeSig([wasmRefType(creatorType)], [kWasmI32]))
|
||||
.addBody([
|
||||
kExprBlock, kWasmRefNull, heapType,
|
||||
kExprLocalGet, 0,
|
||||
kExprCallRef, ...wasmUnsignedLeb(creatorType),
|
||||
kGCPrefix, kExprBrOnCastNull, 0, heapType,
|
||||
kExprI32Const, 0,
|
||||
kExprReturn,
|
||||
kExprEnd,
|
||||
kExprDrop,
|
||||
kExprI32Const, 1,
|
||||
kExprReturn,
|
||||
])
|
||||
.exportFunc();
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,6 +689,10 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
print(`Test br_on_cast: ${test.source}(${value}) -> ${target}`);
|
||||
res = wasm[`brOnCast_${test.source}_to_${target}`](create_value);
|
||||
assertEquals(validValues.includes(value) ? 1 : 0, res);
|
||||
print(`Test br_on_cast null: ${test.source}(${value}) -> ${target}`);
|
||||
res = wasm[`brOnCastNull_${test.source}_to_${target}`](create_value);
|
||||
assertEquals(
|
||||
validValues.includes(value) || value == "nullref" ? 1 : 0, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -516,6 +516,7 @@ let kExprRefCast = 0x41;
|
||||
let kExprRefCastNull = 0x49;
|
||||
let kExprRefCastDeprecated = 0x45;
|
||||
let kExprBrOnCast = 0x42;
|
||||
let kExprBrOnCastNull = 0x4a;
|
||||
let kExprBrOnCastDeprecated = 0x46;
|
||||
let kExprBrOnCastFail = 0x47;
|
||||
let kExprRefCastNop = 0x4c;
|
||||
|
Loading…
Reference in New Issue
Block a user