[wasm-gc] Implement optional-depth rtts

As per the latest wasm-gc spec, rtts now have optional depth, with
(rtt n type) <: (rtt type) for every depth n. Liftoff compilation for
type checks without depth are not supported yet.

Bug: v8:7748
Change-Id: I4971875e6a42db6d333b61ca5e2996e875f39f60
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2649043
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72395}
This commit is contained in:
Manos Koukoutos 2021-01-27 15:13:43 +00:00 committed by Commit Bot
parent 7cc78c535d
commit 3a2ae154f9
29 changed files with 359 additions and 125 deletions

View File

@ -2321,6 +2321,7 @@ Node* WasmGraphBuilder::Throw(uint32_t exception_index,
++index;
break;
case wasm::ValueType::kRtt: // TODO(7748): Implement.
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:
case wasm::ValueType::kStmt:
@ -2469,6 +2470,7 @@ Node* WasmGraphBuilder::GetExceptionValues(Node* except_obj,
++index;
break;
case wasm::ValueType::kRtt: // TODO(7748): Implement.
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:
case wasm::ValueType::kStmt:
@ -5837,12 +5839,16 @@ void WasmGraphBuilder::TypeCheck(
}
Node* type_info = gasm_->LoadWasmTypeInfo(map);
Node* supertypes = gasm_->LoadSupertypes(type_info);
Node* length =
Node* supertypes_length =
BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(supertypes));
callbacks.fail_if_not(
gasm_->Uint32LessThan(gasm_->Int32Constant(config.rtt_depth), length));
Node* rtt_depth =
config.rtt_depth >= 0
? gasm_->Int32Constant(config.rtt_depth)
: BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(
gasm_->LoadSupertypes(gasm_->LoadWasmTypeInfo(rtt))));
callbacks.fail_if_not(gasm_->Uint32LessThan(rtt_depth, supertypes_length));
Node* maybe_match = gasm_->LoadFixedArrayElement(
supertypes, config.rtt_depth, MachineType::TaggedPointer());
supertypes, rtt_depth, MachineType::TaggedPointer());
callbacks.fail_if_not(gasm_->TaggedEqual(maybe_match, rtt));
}
@ -6261,6 +6267,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
UNREACHABLE();
}
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
// TODO(7748): Figure out what to do for RTTs.
UNIMPLEMENTED();
case wasm::ValueType::kI8:
@ -6414,6 +6421,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
return BuildChangeBigIntToInt64(input, js_context);
case wasm::ValueType::kRtt: // TODO(7748): Implement.
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kS128:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:
@ -6470,6 +6478,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
case wasm::ValueType::kOptRef:
case wasm::ValueType::kI64:
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kS128:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:
@ -6609,6 +6618,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
case wasm::ValueType::kOptRef:
case wasm::ValueType::kI64:
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kS128:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:
@ -6659,6 +6669,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
case wasm::ValueType::kOptRef:
case wasm::ValueType::kI64:
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kS128:
case wasm::ValueType::kI8:
case wasm::ValueType::kI16:

View File

@ -169,7 +169,7 @@ class WasmGraphBuilder {
bool object_can_be_null;
bool object_must_be_data_ref;
bool object_can_be_i31;
uint8_t rtt_depth;
int8_t rtt_depth;
};
enum EnforceBoundsCheck : bool { // --
kNeedsBoundsCheck = true,

View File

@ -1750,6 +1750,7 @@ void WasmStruct::WasmStructPrint(std::ostream& os) { // NOLINT
case wasm::ValueType::kRef:
case wasm::ValueType::kOptRef:
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kBottom:
case wasm::ValueType::kStmt:
os << "UNIMPLEMENTED"; // TODO(7748): Implement.
@ -1789,6 +1790,7 @@ void WasmArray::WasmArrayPrint(std::ostream& os) { // NOLINT
case wasm::ValueType::kRef:
case wasm::ValueType::kOptRef:
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kBottom:
case wasm::ValueType::kStmt:
os << "\n Printing elements of this type is unimplemented, sorry";

View File

@ -304,6 +304,7 @@ inline void Store(LiftoffAssembler* assm, LiftoffRegister src, MemOperand dst,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->str(src.gp(), dst);
break;
case ValueType::kI64:
@ -338,6 +339,7 @@ inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, MemOperand src,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->ldr(dst.gp(), src);
break;
case ValueType::kI64:

View File

@ -80,6 +80,7 @@ inline CPURegister GetRegFromType(const LiftoffRegister& reg, ValueType type) {
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
return reg.gp().X();
case ValueType::kF32:
return reg.fp().S();
@ -1503,6 +1504,7 @@ void LiftoffAssembler::emit_cond_jump(LiftoffCondition liftoff_cond,
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
DCHECK(rhs.is_valid());
DCHECK(liftoff_cond == kEqual || liftoff_cond == kUnequal);
V8_FALLTHROUGH;

View File

@ -72,6 +72,7 @@ inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Register base,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->mov(dst.gp(), src);
break;
case ValueType::kI64:
@ -1130,6 +1131,7 @@ void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) {
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
mov(dst, reg.gp());
break;
case ValueType::kI64:
@ -2394,6 +2396,7 @@ void LiftoffAssembler::emit_cond_jump(LiftoffCondition liftoff_cond,
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
DCHECK(liftoff_cond == kEqual || liftoff_cond == kUnequal);
V8_FALLTHROUGH;
case ValueType::kI32:

View File

@ -494,6 +494,7 @@ class LiftoffCompiler {
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
if (FLAG_experimental_liftoff_extern_ref) return true;
@ -4452,8 +4453,13 @@ class LiftoffCompiler {
// Step 3: check the list's length.
LiftoffRegister list_length = tmp2;
__ LoadFixedArrayLengthAsInt32(list_length, tmp1.gp(), pinned);
__ emit_i32_cond_jumpi(kUnsignedLessEqual, no_match, list_length.gp(),
rtt.type.depth());
if (rtt.type.has_depth()) {
__ emit_i32_cond_jumpi(kUnsignedLessEqual, no_match, list_length.gp(),
rtt.type.depth());
} else {
unsupported(decoder, kGC, "rtt without depth");
}
// Step 4: load the candidate list slot into {tmp1}, and compare it.
__ LoadTaggedPointer(
tmp1.gp(), tmp1.gp(), no_reg,
@ -5011,6 +5017,7 @@ class LiftoffCompiler {
case ValueType::kOptRef:
return LoadNullValue(reg.gp(), pinned);
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kStmt:
case ValueType::kBottom:
case ValueType::kRef:

View File

@ -69,6 +69,7 @@ static inline constexpr RegClass reg_class_for(ValueType::Kind kind) {
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
return kGpReg;
default:
return kNoReg; // unsupported type

View File

@ -91,6 +91,7 @@ inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Register base,
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->lw(dst.gp(), src);
break;
case ValueType::kI64:
@ -118,6 +119,7 @@ inline void Store(LiftoffAssembler* assm, Register base, int32_t offset,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->Usw(src.gp(), dst);
break;
case ValueType::kI64:
@ -722,6 +724,7 @@ void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) {
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
sw(reg.gp(), dst);
break;
case ValueType::kI64:

View File

@ -102,6 +102,7 @@ inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, MemOperand src,
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->Ld(dst.gp(), src);
break;
case ValueType::kF32:
@ -129,6 +130,7 @@ inline void Store(LiftoffAssembler* assm, Register base, int32_t offset,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->Usd(src.gp(), dst);
break;
case ValueType::kF32:
@ -661,6 +663,7 @@ void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) {
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
Sd(reg.gp(), dst);
break;
case ValueType::kF32:

View File

@ -93,6 +93,7 @@ inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Operand src,
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
assm->movq(dst.gp(), src);
break;
case ValueType::kF32:
@ -828,6 +829,7 @@ void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) {
case ValueType::kOptRef:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
movq(dst, reg.gp());
break;
case ValueType::kF32:
@ -2043,6 +2045,7 @@ void LiftoffAssembler::emit_cond_jump(LiftoffCondition liftoff_cond,
case ValueType::kRef:
case ValueType::kOptRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
DCHECK(liftoff_cond == kEqual || liftoff_cond == kUnequal);
V8_FALLTHROUGH;
case ValueType::kI64:

View File

@ -1420,6 +1420,7 @@ void PushArgs(const i::wasm::FunctionSig* sig, const Val args[],
packer->Push(WasmRefToV8(store->i_isolate(), args[i].ref())->ptr());
break;
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
case i::wasm::ValueType::kS128:
// TODO(7748): Implement.
UNIMPLEMENTED();
@ -1460,6 +1461,7 @@ void PopArgs(const i::wasm::FunctionSig* sig, Val results[],
break;
}
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
case i::wasm::ValueType::kS128:
// TODO(7748): Implement.
UNIMPLEMENTED();
@ -1724,6 +1726,7 @@ auto Global::get() const -> Val {
return Val(V8RefValueToWasm(store, v8_global->GetRef()));
}
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
case i::wasm::ValueType::kS128:
// TODO(7748): Implement these.
UNIMPLEMENTED();

View File

@ -317,7 +317,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
return heap_type.is_bottom() ? kWasmBottom
: ValueType::Ref(heap_type, nullability);
}
case kRttCode: {
case kRttWithDepthCode: {
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
decoder, pc,
@ -355,6 +355,32 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
}
return ValueType::Rtt(type_index, depth);
}
case kRttCode: {
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
decoder, pc,
"invalid value type 'rtt', enable with --experimental-wasm-gc");
return kWasmBottom;
}
uint32_t type_index = decoder->read_u32v<validate>(pc + 1, length);
*length += 1;
if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
DecodeError<validate>(
decoder, pc,
"Type index %u is greater than the maximum number %zu "
"of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
return kWasmBottom;
}
// We use capacity over size so this works mid-DecodeTypeSection.
if (!VALIDATE(module == nullptr ||
type_index < module->types.capacity())) {
DecodeError<validate>(decoder, pc, "Type index %u is out of bounds",
type_index);
return kWasmBottom;
}
return ValueType::Rtt(type_index);
}
case kS128Code: {
if (!VALIDATE(enabled.has_simd())) {
DecodeError<validate>(
@ -3797,8 +3823,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
rtt.type.ref_index() == imm.index)) {
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(imm.struct_type->field_count(), rtt,
"rtt for type " + std::to_string(imm.index));
return 0;
@ -3831,8 +3858,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
rtt.type.ref_index() == imm.index)) {
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(0, rtt, "rtt for type " + std::to_string(imm.index));
return 0;
}
@ -3904,8 +3932,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
rtt.type.ref_index() == imm.index)) {
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(2, rtt, "rtt for type " + std::to_string(imm.index));
return 0;
}
@ -3933,8 +3962,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
rtt.type.ref_index() == imm.index)) {
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(1, rtt, "rtt for type " + std::to_string(imm.index));
return 0;
}

View File

@ -990,7 +990,7 @@ class WasmGraphBuildingInterface {
DCHECK(object_type.is_object_reference_type()); // Checked by validation.
result.object_must_be_data_ref = is_data_ref_type(object_type, module);
result.object_can_be_i31 = IsSubtypeOf(kWasmI31Ref, object_type, module);
result.rtt_depth = rtt_type.depth();
result.rtt_depth = rtt_type.has_depth() ? rtt_type.depth() : -1;
return result;
}
@ -1142,6 +1142,7 @@ class WasmGraphBuildingInterface {
case ValueType::kOptRef:
return builder_->RefNull();
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kStmt:
case ValueType::kBottom:
case ValueType::kRef:

View File

@ -41,6 +41,9 @@ size_t LocalDeclEncoder::Emit(byte* buffer) const {
*pos = locals_type.depth();
++pos;
}
if (locals_type.is_rtt()) {
LEBHelper::write_u32v(&pos, locals_type.ref_index());
}
if (locals_type.encoding_needs_heap_type()) {
LEBHelper::write_i32v(&pos, locals_type.heap_type().code());
}
@ -66,12 +69,14 @@ uint32_t LocalDeclEncoder::AddLocals(uint32_t count, ValueType type) {
size_t LocalDeclEncoder::Size() const {
size_t size = LEBHelper::sizeof_u32v(local_decls.size());
for (auto p : local_decls) {
size += LEBHelper::sizeof_u32v(p.first) + // number of locals
1 + // Opcode
(p.second.has_depth() ? 1 : 0) + // Inheritance depth
(p.second.encoding_needs_heap_type()
? LEBHelper::sizeof_i32v(p.second.heap_type().code())
: 0); // ref. index
size +=
LEBHelper::sizeof_u32v(p.first) + // number of locals
1 + // Opcode
(p.second.has_depth() ? 1 : 0) + // Inheritance depth
(p.second.encoding_needs_heap_type()
? LEBHelper::sizeof_i32v(p.second.heap_type().code())
: 0) +
(p.second.is_rtt() ? LEBHelper::sizeof_u32v(p.second.ref_index()) : 0);
}
return size;
}

View File

@ -912,6 +912,7 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
break;
}
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kRef:
case ValueType::kOptRef: {
tagged_globals_->set(global.offset, *value->GetRef());

View File

@ -35,12 +35,13 @@ class Simd128;
V(I8, 0, I8, Int8, 'b', "i8") \
V(I16, 1, I16, Int16, 'h', "i16")
#define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \
FOREACH_NUMERIC_VALUE_TYPE(V) \
V(Rtt, kTaggedSizeLog2, Rtt, TaggedPointer, 't', "rtt") \
V(Ref, kTaggedSizeLog2, Ref, AnyTagged, 'r', "ref") \
V(OptRef, kTaggedSizeLog2, OptRef, AnyTagged, 'n', "ref null") \
#define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \
FOREACH_NUMERIC_VALUE_TYPE(V) \
V(Rtt, kTaggedSizeLog2, Rtt, TaggedPointer, 't', "rtt") \
V(RttWithDepth, kTaggedSizeLog2, RttWithDepth, TaggedPointer, 'k', "rtt") \
V(Ref, kTaggedSizeLog2, Ref, AnyTagged, 'r', "ref") \
V(OptRef, kTaggedSizeLog2, OptRef, AnyTagged, 'n', "ref null") \
V(Bottom, -1, Void, None, '*', "<bot>")
// Represents a WebAssembly heap type, as per the typed-funcref and gc
@ -194,10 +195,16 @@ class ValueType {
return Ref(heap_type.representation(), nullability);
}
static constexpr ValueType Rtt(uint32_t type_index) {
CONSTEXPR_DCHECK(HeapType(type_index).is_index());
return ValueType(KindField::encode(kRtt) |
HeapTypeField::encode(type_index));
}
static constexpr ValueType Rtt(uint32_t type_index,
uint8_t inheritance_depth) {
CONSTEXPR_DCHECK(HeapType(type_index).is_index());
return ValueType(KindField::encode(kRtt) |
return ValueType(KindField::encode(kRttWithDepth) |
HeapTypeField::encode(type_index) |
DepthField::encode(inheritance_depth));
}
@ -209,7 +216,8 @@ class ValueType {
/******************************** Type checks *******************************/
constexpr bool is_reference_type() const {
return kind() == kRef || kind() == kOptRef || kind() == kRtt;
return kind() == kRef || kind() == kOptRef || kind() == kRtt ||
kind() == kRttWithDepth;
}
constexpr bool is_object_reference_type() const {
@ -223,8 +231,10 @@ class ValueType {
heap_representation() == htype;
}
constexpr bool is_rtt() const { return kind() == kRtt; }
constexpr bool has_depth() const { return is_rtt(); }
constexpr bool is_rtt() const {
return kind() == kRtt || kind() == kRttWithDepth;
}
constexpr bool has_depth() const { return kind() == kRttWithDepth; }
constexpr bool has_index() const {
return is_rtt() || (is_object_reference_type() && heap_type().is_index());
@ -232,7 +242,7 @@ class ValueType {
constexpr bool is_defaultable() const {
CONSTEXPR_DCHECK(kind() != kBottom && kind() != kStmt);
return kind() != kRef && kind() != kRtt;
return kind() != kRef && !is_rtt();
}
constexpr bool is_bottom() const { return kind() == kBottom; }
@ -377,6 +387,8 @@ class ValueType {
return kVoidCode;
case kRtt:
return kRttCode;
case kRttWithDepth:
return kRttWithDepthCode;
#define NUMERIC_TYPE_CASE(kind, ...) \
case k##kind: \
return k##kind##Code;
@ -392,7 +404,6 @@ class ValueType {
// binary format, taking into account available type shorthands.
constexpr bool encoding_needs_heap_type() const {
return (kind() == kRef && heap_representation() != HeapType::kI31) ||
kind() == kRtt ||
(kind() == kOptRef && (!heap_type().is_generic() ||
heap_representation() == HeapType::kI31));
}
@ -429,10 +440,13 @@ class ValueType {
buf << "(ref null " << heap_type().name() << ")";
}
break;
case kRtt:
case kRttWithDepth:
buf << "(rtt " << static_cast<uint32_t>(depth()) << " " << ref_index()
<< ")";
break;
case kRtt:
buf << "(rtt " << ref_index() << ")";
break;
default:
buf << kind_name();
}

View File

@ -38,7 +38,8 @@ enum ValueTypeCode : uint8_t {
kOptRefCode = 0x6c,
kRefCode = 0x6b,
kI31RefCode = 0x6a,
kRttCode = 0x69,
kRttWithDepthCode = 0x69,
kRttCode = 0x68,
};
// Binary encoding of other types.
constexpr uint8_t kWasmFunctionTypeCode = 0x60;

View File

@ -1376,6 +1376,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
break;
}
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
// TODO(7748): Implement.
UNIMPLEMENTED();
case i::wasm::ValueType::kI8:
@ -1847,6 +1848,7 @@ void WebAssemblyGlobalGetValueCommon(
}
break;
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
UNIMPLEMENTED(); // TODO(7748): Implement.
break;
case i::wasm::ValueType::kI8:
@ -1939,6 +1941,7 @@ void WebAssemblyGlobalSetValue(
}
break;
case i::wasm::ValueType::kRtt:
case i::wasm::ValueType::kRttWithDepth:
// TODO(7748): Implement.
UNIMPLEMENTED();
break;

View File

@ -414,11 +414,11 @@ void WasmModuleBuilder::SetHasSharedMemory() { has_shared_memory_ = true; }
namespace {
void WriteValueType(ZoneBuffer* buffer, const ValueType& type) {
buffer->write_u8(type.value_type_code());
if (type.is_object_reference_type() && type.encoding_needs_heap_type()) {
if (type.encoding_needs_heap_type()) {
buffer->write_i32v(type.heap_type().code());
}
if (type.is_rtt()) {
buffer->write_u32v(type.depth());
if (type.has_depth()) buffer->write_u32v(type.depth());
buffer->write_u32v(type.ref_index());
}
}
@ -490,6 +490,7 @@ void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init,
case ValueType::kBottom:
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
UNREACHABLE();
}
break;

View File

@ -1745,6 +1745,7 @@ uint32_t WasmExceptionPackage::GetEncodedSize(
encoded_size += 1;
break;
case wasm::ValueType::kRtt:
case wasm::ValueType::kRttWithDepth:
case wasm::ValueType::kStmt:
case wasm::ValueType::kBottom:
case wasm::ValueType::kI8:

View File

@ -274,12 +274,34 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
const WasmModule* super_module) {
DCHECK(subtype != supertype || sub_module != super_module);
if (!subtype.is_reference_type()) return subtype == supertype;
if (subtype.is_rtt()) {
return supertype.is_rtt() && subtype.depth() == supertype.depth() &&
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module);
switch (subtype.kind()) {
case ValueType::kI32:
case ValueType::kI64:
case ValueType::kF32:
case ValueType::kF64:
case ValueType::kS128:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
case ValueType::kBottom:
return subtype == supertype;
case ValueType::kRtt:
return supertype.kind() == ValueType::kRtt &&
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module);
case ValueType::kRttWithDepth:
return (supertype.kind() == ValueType::kRtt &&
((sub_module == super_module &&
subtype.ref_index() == supertype.ref_index()) ||
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module))) ||
(supertype.kind() == ValueType::kRttWithDepth &&
supertype.depth() == subtype.depth() &&
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module));
case ValueType::kRef:
case ValueType::kOptRef:
break;
}
DCHECK(subtype.is_object_reference_type());

View File

@ -798,11 +798,13 @@ WASM_COMPILED_EXEC_TEST(NewDefault) {
tester.CheckResult(allocate_array, 0);
}
TEST(BasicRTT) {
TEST(BasicRtt) {
WasmGCTester tester;
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
const byte subtype_index =
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
ValueType kRttTypes[] = {ValueType::Rtt(type_index, 0)};
FunctionSig sig_t_v(1, 0, kRttTypes);
ValueType kRttSubtypes[] = {ValueType::Rtt(subtype_index, 1)};
@ -822,6 +824,7 @@ TEST(BasicRTT) {
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
const int kFieldIndex = 1;
const int kStructIndexCode = 1; // Shifted in 'let' block.
const int kRttIndexCode = 0; // Let-bound, hence first local.
@ -836,20 +839,20 @@ TEST(BasicRTT) {
const byte kRefCast = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LET_1_I(
WASM_RTT(1, subtype_index),
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)),
WASM_LOCAL_SET(kStructIndexCode,
WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_LOCAL_GET(kRttIndexCode))),
WASM_I32_ADD(
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(kStructIndexCode),
WASM_LOCAL_GET(kRttIndexCode)),
WASM_STRUCT_GET(
subtype_index, kFieldIndex,
WASM_REF_CAST(subtype_index, WASM_LOCAL_GET(kStructIndexCode),
WASM_LOCAL_GET(kRttIndexCode)))),
kExprEnd)});
WASM_RTT_WITH_DEPTH(1, subtype_index),
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)),
WASM_LOCAL_SET(kStructIndexCode,
WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_LOCAL_GET(kRttIndexCode))),
WASM_I32_ADD(
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(kStructIndexCode),
WASM_LOCAL_GET(kRttIndexCode)),
WASM_STRUCT_GET(subtype_index, kFieldIndex,
WASM_REF_CAST(subtype_index,
WASM_LOCAL_GET(kStructIndexCode),
WASM_LOCAL_GET(kRttIndexCode))))),
kExprEnd});
tester.CompileModule();
@ -881,6 +884,89 @@ TEST(BasicRTT) {
tester.CheckResult(kRefCast, 43);
}
TEST(NoDepthRtt) {
WasmGCTester tester;
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
const byte subtype_index =
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
const byte empty_struct_index = tester.DefineStruct({});
ValueType kRttSubtypeNoDepth = ValueType::Rtt(subtype_index);
FunctionSig sig_t2_v_nd(1, 0, &kRttSubtypeNoDepth);
const byte kRttSubtypeCanon = tester.DefineFunction(
&sig_t2_v_nd, {}, {WASM_RTT_CANON(subtype_index), kExprEnd});
const byte kRttSubtypeSub = tester.DefineFunction(
&sig_t2_v_nd, {},
{WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)), kExprEnd});
const byte kTestCanon = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_RTT_CANON(subtype_index))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
kExprEnd});
const byte kTestSub = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeSub)),
kExprEnd});
const byte kTestSubVsEmpty = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_RTT_SUB(subtype_index,
WASM_RTT_CANON(empty_struct_index)))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeSub)),
kExprEnd});
const byte kTestSubVsCanon = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_RTT_CANON(subtype_index))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeSub)),
kExprEnd});
const byte kTestCanonVsSub = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW_WITH_RTT(
subtype_index, WASM_I32V(11), WASM_I32V(42),
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
kExprEnd});
const byte kTestSuperVsSub = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index))),
WASM_REF_TEST(subtype_index, WASM_LOCAL_GET(0),
WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kTestCanon, 1);
tester.CheckResult(kTestSub, 1);
tester.CheckResult(kTestSubVsEmpty, 0);
tester.CheckResult(kTestSubVsCanon, 0);
tester.CheckResult(kTestCanonVsSub, 0);
tester.CheckResult(kTestSuperVsSub, 0);
}
WASM_COMPILED_EXEC_TEST(ArrayNewMap) {
WasmGCTester tester(execution_tier);
FLAG_experimental_liftoff_extern_ref = true;

View File

@ -1256,8 +1256,9 @@ class WasmInterpreterInternals {
val = WasmValue(isolate_->factory()->null_value());
break;
}
case ValueType::kRef:
case ValueType::kRtt: // TODO(7748): Implement.
case ValueType::kRef: // TODO(7748): Implement.
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kStmt:
case ValueType::kBottom:
case ValueType::kI8:
@ -3034,6 +3035,7 @@ class WasmInterpreterInternals {
break;
}
case ValueType::kRtt: // TODO(7748): Implement.
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
@ -3151,6 +3153,7 @@ class WasmInterpreterInternals {
break;
}
case ValueType::kRtt: // TODO(7748): Implement.
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
@ -3522,6 +3525,7 @@ class WasmInterpreterInternals {
break;
}
case ValueType::kRtt: // TODO(7748): Implement.
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
@ -3933,6 +3937,7 @@ class WasmInterpreterInternals {
break;
}
case ValueType::kRtt:
case ValueType::kRttWithDepth:
// TODO(7748): Implement properly.
PrintF("rtt");
break;

View File

@ -526,7 +526,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_LEN(typeidx, array) \
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx)
#define WASM_RTT(depth, typeidx) kRttCode, U32V_1(depth), U32V_1(typeidx)
#define WASM_RTT_WITH_DEPTH(depth, typeidx) \
kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx)
#define WASM_RTT(typeidx) kRttCode, U32V_1(typeidx)
#define WASM_RTT_CANON(typeidx) \
WASM_GC_OP(kExprRttCanon), static_cast<byte>(typeidx)
#define WASM_RTT_SUB(typeidx, supertype) \

View File

@ -70,6 +70,7 @@ OwnedVector<WasmValue> MakeDefaultInterpreterArguments(Isolate* isolate,
break;
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
@ -104,6 +105,7 @@ OwnedVector<Handle<Object>> MakeDefaultArguments(Isolate* isolate,
break;
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kRttWithDepth:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:

View File

@ -4230,55 +4230,61 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)}));
// Passing/failing tests due to static subtyping.
std::pair<HeapType::Representation, HeapType::Representation> valid_pairs[] =
{{HeapType::kAny, array_heap},
{HeapType::kAny, super_struct_heap},
{HeapType::kEq, array_heap},
{HeapType::kEq, super_struct_heap},
{super_struct_heap, sub_struct_heap}};
std::tuple<HeapType::Representation, HeapType::Representation, bool> tests[] =
{{HeapType::kAny, array_heap, true},
{HeapType::kAny, super_struct_heap, true},
{HeapType::kEq, array_heap, true},
{HeapType::kEq, super_struct_heap, true},
{super_struct_heap, sub_struct_heap, true},
{sub_struct_heap, super_struct_heap, false},
{sub_struct_heap, array_heap, false},
{HeapType::kFunc, array_heap, false}};
for (auto test : tests) {
HeapType from_heap = HeapType(std::get<0>(test));
HeapType to_heap = HeapType(std::get<1>(test));
bool should_pass = std::get<2>(test);
for (auto pair : valid_pairs) {
HeapType from_heap = HeapType(pair.first);
HeapType to_heap = HeapType(pair.second);
ValueType test_reps[] = {kWasmI32, ValueType::Ref(from_heap, kNullable)};
FunctionSig test_sig(1, 1, test_reps);
ValueType cast_reps[] = {ValueType::Ref(to_heap, kNonNullable),
ValueType::Ref(from_heap, kNonNullable)};
FunctionSig cast_sig(1, 1, cast_reps);
ExpectValidates(&test_sig,
{WASM_REF_TEST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))});
ExpectValidates(&cast_sig,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))});
}
std::pair<HeapType::Representation, HeapType::Representation>
invalid_pairs[] = {{sub_struct_heap, super_struct_heap},
{sub_struct_heap, array_heap},
{HeapType::kFunc, array_heap}};
ValueType cast_reps_with_depth[] = {ValueType::Ref(to_heap, kNullable),
ValueType::Ref(from_heap, kNullable)};
FunctionSig cast_sig_with_depth(1, 1, cast_reps_with_depth);
for (auto pair : invalid_pairs) {
HeapType from_heap = HeapType(pair.first);
HeapType to_heap = HeapType(pair.second);
ValueType test_reps[] = {kWasmI32, ValueType::Ref(from_heap, kNullable)};
FunctionSig test_sig(1, 1, test_reps);
ValueType cast_reps[] = {ValueType::Ref(to_heap, kNullable),
ValueType::Ref(from_heap, kNullable)};
FunctionSig cast_sig(1, 1, cast_reps);
ValueType::Ref(from_heap, kNullable),
ValueType::Rtt(to_heap.ref_index())};
FunctionSig cast_sig(1, 2, cast_reps);
std::string error_message = "[0] expected supertype of type " +
std::to_string(to_heap.ref_index()) +
", found local.get of type " +
test_reps[1].name();
ExpectFailure(&test_sig,
{WASM_REF_TEST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))},
kAppendEnd, ("ref.test" + error_message).c_str());
ExpectFailure(&cast_sig,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))},
kAppendEnd, ("ref.cast" + error_message).c_str());
if (should_pass) {
ExpectValidates(&test_sig,
{WASM_REF_TEST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))});
ExpectValidates(&cast_sig_with_depth,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))});
ExpectValidates(&cast_sig,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_LOCAL_GET(1))});
} else {
std::string error_message = "[0] expected supertype of type " +
std::to_string(to_heap.ref_index()) +
", found local.get of type " +
test_reps[1].name();
ExpectFailure(&test_sig,
{WASM_REF_TEST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))},
kAppendEnd, ("ref.test" + error_message).c_str());
ExpectFailure(&cast_sig_with_depth,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))},
kAppendEnd, ("ref.cast" + error_message).c_str());
ExpectFailure(&cast_sig,
{WASM_REF_CAST(WASM_HEAP_TYPE(to_heap), WASM_LOCAL_GET(0),
WASM_LOCAL_GET(1))},
kAppendEnd, ("ref.cast" + error_message).c_str());
}
}
// Trivial type error.

View File

@ -569,12 +569,12 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) {
static const byte referencing_undefined_global_nested[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI32Code, true)),
SECTION(Global, ENTRY_COUNT(2), // --
WASM_RTT(1, 0), // type
WASM_RTT_WITH_DEPTH(1, 0), // type
0, // mutable
WASM_RTT_SUB(0, // init value
WASM_GLOBAL_GET(1)), // --
kExprEnd, // --
WASM_RTT(0, 0), // type
WASM_RTT_WITH_DEPTH(0, 0), // type
0, // mutable
WASM_RTT_CANON(0), kExprEnd) // init value
};
@ -796,8 +796,8 @@ TEST_F(WasmModuleVerifyTest, RttCanonGlobalStruct) {
static const byte data[] = {
SECTION(Type, ENTRY_COUNT(1),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(0, 0), 0, WASM_RTT_CANON(0),
kExprEnd)};
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(0, 0), 0,
WASM_RTT_CANON(0), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
}
@ -809,8 +809,8 @@ TEST_F(WasmModuleVerifyTest, RttCanonGlobalTypeError) {
static const byte data[] = {
SECTION(Type, ENTRY_COUNT(1),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(1, 0), 1, WASM_RTT_CANON(0),
kExprEnd)};
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 0), 1,
WASM_RTT_CANON(0), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result,
"type error in init expression, expected (rtt 1 0), got "
@ -826,7 +826,7 @@ TEST_F(WasmModuleVerifyTest, GlobalRttSubOfCanon) {
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(1, 1), 1,
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
WASM_RTT_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
WasmInitExpr expected = WasmInitExpr::RttSub(1, WasmInitExpr::RttCanon(0));
@ -843,7 +843,7 @@ TEST_F(WasmModuleVerifyTest, GlobalRttSubOfSubOfCanon) {
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(2, 1), 1,
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(2, 1), 1,
WASM_RTT_SUB(1, WASM_RTT_SUB(1, WASM_RTT_CANON(0))), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
WasmInitExpr expected = WasmInitExpr::RttSub(
@ -861,14 +861,14 @@ TEST_F(WasmModuleVerifyTest, GlobalRttSubOfGlobal) {
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
STRUCT_FIELD(kI32Code, true))),
SECTION(Import, // section header
ENTRY_COUNT(1), // number of imports
ADD_COUNT('m'), // module name
ADD_COUNT('f'), // global name
kExternalGlobal, // import kind
WASM_RTT(0, 0), // type
0), // mutability
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(1, 1), 1,
SECTION(Import, // section header
ENTRY_COUNT(1), // number of imports
ADD_COUNT('m'), // module name
ADD_COUNT('f'), // global name
kExternalGlobal, // import kind
WASM_RTT_WITH_DEPTH(0, 0), // type
0), // mutability
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
WASM_RTT_SUB(1, WASM_GLOBAL_GET(0)), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
WasmInitExpr expected = WasmInitExpr::RttSub(1, WasmInitExpr::GlobalGet(0));
@ -890,7 +890,7 @@ TEST_F(WasmModuleVerifyTest, GlobalRttSubOfGlobalTypeError) {
kExternalGlobal, // import kind
kI32Code, // type
0), // mutability
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(1, 0), 1,
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 0), 1,
WASM_RTT_SUB(0, WASM_GLOBAL_GET(0)), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "rtt.sub requires a supertype rtt on stack");
@ -904,7 +904,7 @@ TEST_F(WasmModuleVerifyTest, GlobalRttSubIllegalParent) {
SECTION(Type, ENTRY_COUNT(2),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kF32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(1, 1), 1,
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
WASM_RTT_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "rtt.sub requires a supertype rtt on stack");
@ -917,7 +917,7 @@ TEST_F(WasmModuleVerifyTest, RttSubGlobalTypeError) {
static const byte data[] = {
SECTION(Type, ENTRY_COUNT(1),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(0, 0), 1,
SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(0, 0), 1,
WASM_RTT_SUB(0, WASM_RTT_CANON(0)), kExprEnd)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result,
@ -1982,7 +1982,7 @@ TEST_F(WasmModuleVerifyTest, IllegalTableTypes) {
{kOptRefCode, 1},
{kOptRefCode, kI31RefCode},
{kI31RefCode},
{kRttCode, 2, 0}};
{kRttWithDepthCode, 2, 0}};
for (Vec type : table_types) {
Vec data = {

View File

@ -131,6 +131,13 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Identical rtts are subtypes of each other.
CHECK(IsSubtypeOf(ValueType::Rtt(5, 3), ValueType::Rtt(5, 3), module1,
module2));
CHECK(IsSubtypeOf(ValueType::Rtt(5), ValueType::Rtt(5), module1, module2));
// Rtts of unrelated types are unrelated.
CHECK(!IsSubtypeOf(ValueType::Rtt(1, 1), ValueType::Rtt(2, 1), module1,
module2));
CHECK(!IsSubtypeOf(ValueType::Rtt(1), ValueType::Rtt(2), module1, module2));
CHECK(!IsSubtypeOf(ValueType::Rtt(1, 0), ValueType::Rtt(2), module1,
module2));
// Rtts of different depth are unrelated.
CHECK(!IsSubtypeOf(ValueType::Rtt(5, 1), ValueType::Rtt(5, 3), module1,
module2));
@ -139,9 +146,16 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Rtts of identical types are subtype-related.
CHECK(IsSubtypeOf(ValueType::Rtt(8, 1), ValueType::Rtt(9, 1), module1,
module));
CHECK(IsSubtypeOf(ValueType::Rtt(8), ValueType::Rtt(9), module1, module));
// Rtts of subtypes are not related.
CHECK(!IsSubtypeOf(ValueType::Rtt(1, 1), ValueType::Rtt(0, 1), module1,
module));
CHECK(!IsSubtypeOf(ValueType::Rtt(1), ValueType::Rtt(0), module1, module));
// rtt(t, d) <: rtt(t)
for (uint8_t depth : {0, 1, 5}) {
CHECK(IsSubtypeOf(ValueType::Rtt(1, depth), ValueType::Rtt(1), module1,
module));
}
}
}