[wasm-gc] Introduce separate constructors for ref and (ref null)
Most often, the {ValueType::Ref} constructor was called with a constant nullability. To make things more convenient, this CL renames {Ref} to {RefMaybeNull}, and introduces {Ref} and {RefNull} constructors with fixed nullability. Bug: v8:7748 Change-Id: I664ff184ca936cc752e152c3c67546d79aa24390 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3732936 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#81494}
This commit is contained in:
parent
f6bf7cdb0f
commit
bcd8bf90ff
@ -128,9 +128,8 @@ Reduction WasmGCOperatorReducer::ReduceIf(Node* node, bool condition) {
|
||||
if (object_type.type.is_bottom()) return NoChange();
|
||||
|
||||
Node* rtt = NodeProperties::GetValueInput(condition_node, 1);
|
||||
wasm::ValueType rtt_type = wasm::ValueType::Ref(
|
||||
NodeProperties::GetType(rtt).AsWasm().type.ref_index(),
|
||||
wasm::kNullable);
|
||||
wasm::ValueType rtt_type = wasm::ValueType::RefNull(
|
||||
NodeProperties::GetType(rtt).AsWasm().type.ref_index());
|
||||
|
||||
// TODO(manoskouk): Think about {module_} below if we have cross-module
|
||||
// inlining.
|
||||
@ -271,8 +270,7 @@ Reduction WasmGCOperatorReducer::ReduceWasmTypeCast(Node* node) {
|
||||
TrapId::kTrapIllegalCast);
|
||||
// TODO(manoskouk): Improve the type when we have nullref.
|
||||
Node* null_node = SetType(
|
||||
gasm_.Null(),
|
||||
wasm::ValueType::Ref(rtt_type.type.ref_index(), wasm::kNullable));
|
||||
gasm_.Null(), wasm::ValueType::RefNull(rtt_type.type.ref_index()));
|
||||
ReplaceWithValue(node, null_node, gasm_.effect(), gasm_.control());
|
||||
node->Kill();
|
||||
return Replace(null_node);
|
||||
@ -290,8 +288,7 @@ Reduction WasmGCOperatorReducer::ReduceWasmTypeCast(Node* node) {
|
||||
// inlining.
|
||||
wasm::TypeInModule new_type = wasm::Intersection(
|
||||
object_type,
|
||||
{wasm::ValueType::Ref(rtt_type.type.ref_index(), wasm::kNullable),
|
||||
module_});
|
||||
{wasm::ValueType::RefNull(rtt_type.type.ref_index()), module_});
|
||||
|
||||
return UpdateNodeAndAliasesTypes(node, GetState(control), node, new_type,
|
||||
false);
|
||||
|
@ -91,7 +91,7 @@ Reduction WasmTyper::Reduce(Node* node) {
|
||||
NodeProperties::GetType(NodeProperties::GetValueInput(node, 1))
|
||||
.AsWasm();
|
||||
wasm::ValueType to_type =
|
||||
wasm::ValueType::Ref(rtt_type.type.ref_index(), wasm::kNullable);
|
||||
wasm::ValueType::RefNull(rtt_type.type.ref_index());
|
||||
computed_type = wasm::Intersection(object_type.type, to_type,
|
||||
object_type.module, rtt_type.module);
|
||||
if (object_type.type.is_nullable() && computed_type.type.is_bottom()) {
|
||||
|
@ -3394,8 +3394,8 @@ class LiftoffCompiler {
|
||||
ValueType type =
|
||||
index < static_cast<int>(__ num_locals())
|
||||
? decoder->local_type(index)
|
||||
: exception ? ValueType::Ref(HeapType::kAny, kNonNullable)
|
||||
: decoder->stack_value(decoder_stack_index--)->type;
|
||||
: exception ? ValueType::Ref(HeapType::kAny)
|
||||
: decoder->stack_value(decoder_stack_index--)->type;
|
||||
DCHECK(CheckCompatibleStackSlotTypes(slot.kind(), type.kind()));
|
||||
value.type = type;
|
||||
switch (slot.loc()) {
|
||||
|
@ -92,8 +92,7 @@ void ConstantExpressionInterface::RefFunc(FullDecoder* decoder,
|
||||
return;
|
||||
}
|
||||
if (!generate_value()) return;
|
||||
ValueType type = ValueType::Ref(module_->functions[function_index].sig_index,
|
||||
kNonNullable);
|
||||
ValueType type = ValueType::Ref(module_->functions[function_index].sig_index);
|
||||
Handle<WasmInternalFunction> internal =
|
||||
WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate_, instance_,
|
||||
function_index);
|
||||
@ -131,7 +130,7 @@ void ConstantExpressionInterface::StructNewWithRtt(
|
||||
WasmValue(isolate_->factory()->NewWasmStruct(
|
||||
imm.struct_type, field_values.data(),
|
||||
Handle<Map>::cast(rtt.runtime_value.to_ref())),
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
ValueType::Ref(HeapType(imm.index)));
|
||||
}
|
||||
|
||||
void ConstantExpressionInterface::StringConst(
|
||||
@ -192,7 +191,7 @@ void ConstantExpressionInterface::StructNewDefault(
|
||||
WasmValue(isolate_->factory()->NewWasmStruct(
|
||||
imm.struct_type, field_values.data(),
|
||||
Handle<Map>::cast(rtt.runtime_value.to_ref())),
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
ValueType::Ref(HeapType(imm.index)));
|
||||
}
|
||||
|
||||
void ConstantExpressionInterface::ArrayNewFixed(
|
||||
@ -205,7 +204,7 @@ void ConstantExpressionInterface::ArrayNewFixed(
|
||||
WasmValue(isolate_->factory()->NewWasmArrayFromElements(
|
||||
imm.array_type, element_values,
|
||||
Handle<Map>::cast(rtt.runtime_value.to_ref())),
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
ValueType::Ref(HeapType(imm.index)));
|
||||
}
|
||||
|
||||
void ConstantExpressionInterface::ArrayNewSegment(
|
||||
@ -222,8 +221,7 @@ void ConstantExpressionInterface::ArrayNewSegment(
|
||||
return;
|
||||
}
|
||||
ValueType element_type = array_imm.array_type->element_type();
|
||||
ValueType result_type =
|
||||
ValueType::Ref(HeapType(array_imm.index), kNonNullable);
|
||||
ValueType result_type = ValueType::Ref(HeapType(array_imm.index));
|
||||
if (element_type.is_numeric()) {
|
||||
const WasmDataSegment& data_segment =
|
||||
module_->data_segments[segment_imm.index];
|
||||
|
@ -36,7 +36,7 @@ ValueOrError EvaluateConstantExpression(Zone* zone, ConstantExpression expr,
|
||||
return WasmValue(expr.i32_value());
|
||||
case ConstantExpression::kRefNull:
|
||||
return WasmValue(isolate->factory()->null_value(),
|
||||
ValueType::Ref(expr.repr(), kNullable));
|
||||
ValueType::RefNull(expr.repr()));
|
||||
case ConstantExpression::kRefFunc: {
|
||||
uint32_t index = expr.index();
|
||||
Handle<Object> value =
|
||||
|
@ -299,7 +299,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
code == kI31RefCode || code == kDataRefCode || code == kArrayRefCode
|
||||
? kNonNullable
|
||||
: kNullable;
|
||||
return ValueType::Ref(heap_type, nullability);
|
||||
return ValueType::RefMaybeNull(heap_type, nullability);
|
||||
}
|
||||
case kStringRefCode:
|
||||
case kStringViewWtf8Code:
|
||||
@ -312,7 +312,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
HeapType::from_code(code).name().c_str());
|
||||
return kWasmBottom;
|
||||
}
|
||||
return ValueType::Ref(HeapType::from_code(code), kNullable);
|
||||
return ValueType::RefNull(HeapType::from_code(code));
|
||||
}
|
||||
case kI32Code:
|
||||
return kWasmI32;
|
||||
@ -335,8 +335,9 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
HeapType heap_type =
|
||||
read_heap_type<validate>(decoder, pc + 1, length, module, enabled);
|
||||
*length += 1;
|
||||
return heap_type.is_bottom() ? kWasmBottom
|
||||
: ValueType::Ref(heap_type, nullability);
|
||||
return heap_type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::RefMaybeNull(heap_type, nullability);
|
||||
}
|
||||
// TODO(7748): This is here only for backwards compatibility, and the parsed
|
||||
// depth is ignored.
|
||||
@ -1450,7 +1451,7 @@ class WasmDecoder : public Decoder {
|
||||
|
||||
// Check that the dynamic signature for this call is a subtype of the static
|
||||
// type of the table the function is defined in.
|
||||
ValueType immediate_type = ValueType::Ref(imm.sig_imm.index, kNonNullable);
|
||||
ValueType immediate_type = ValueType::Ref(imm.sig_imm.index);
|
||||
if (!VALIDATE(IsSubtypeOf(immediate_type, table_type, module_))) {
|
||||
DecodeError(pc,
|
||||
"call_indirect: Immediate signature #%u is not a subtype of "
|
||||
@ -2940,8 +2941,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// the stack as it is.
|
||||
break;
|
||||
case kRefNull: {
|
||||
Value result = CreateValue(
|
||||
ValueType::Ref(ref_object.type.heap_type(), kNonNullable));
|
||||
Value result = CreateValue(ValueType::Ref(ref_object.type.heap_type()));
|
||||
// The result of br_on_null has the same value as the argument (but a
|
||||
// non-nullable type).
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
@ -3298,7 +3298,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_ + 1,
|
||||
this->module_);
|
||||
if (!VALIDATE(this->ok())) return 0;
|
||||
ValueType type = ValueType::Ref(imm.type, kNullable);
|
||||
ValueType type = ValueType::RefNull(imm.type);
|
||||
Value value = CreateValue(type);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefNull, type, &value);
|
||||
Push(value);
|
||||
@ -3341,7 +3341,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
HeapType heap_type(this->enabled_.has_typed_funcref()
|
||||
? this->module_->functions[imm.index].sig_index
|
||||
: HeapType::kFunc);
|
||||
Value value = CreateValue(ValueType::Ref(heap_type, kNonNullable));
|
||||
Value value = CreateValue(ValueType::Ref(heap_type));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefFunc, imm.index, &value);
|
||||
Push(value);
|
||||
return 1 + imm.length;
|
||||
@ -3357,8 +3357,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// A non-nullable value can remain as-is.
|
||||
return 1;
|
||||
case kRefNull: {
|
||||
Value result =
|
||||
CreateValue(ValueType::Ref(value.type.heap_type(), kNonNullable));
|
||||
Value result = CreateValue(ValueType::Ref(value.type.heap_type()));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefAsNonNull, value, &result);
|
||||
Drop(value);
|
||||
Push(result);
|
||||
@ -4246,18 +4245,16 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// Checks if types are unrelated, thus type checking will always fail. Does
|
||||
// not account for nullability.
|
||||
bool TypeCheckAlwaysFails(Value obj, Value rtt) {
|
||||
return !IsSubtypeOf(ValueType::Ref(rtt.type.ref_index(), kNonNullable),
|
||||
obj.type, this->module_) &&
|
||||
!IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(rtt.type.ref_index(), kNullable),
|
||||
return !IsSubtypeOf(ValueType::Ref(rtt.type.ref_index()), obj.type,
|
||||
this->module_) &&
|
||||
!IsSubtypeOf(obj.type, ValueType::RefNull(rtt.type.ref_index()),
|
||||
this->module_);
|
||||
}
|
||||
|
||||
// Checks it {obj} is a subtype of {rtt}'s type, thus checking will always
|
||||
// succeed. Does not account for nullability.
|
||||
bool TypeCheckAlwaysSucceeds(Value obj, Value rtt) {
|
||||
return IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(rtt.type.ref_index(), kNullable),
|
||||
return IsSubtypeOf(obj.type, ValueType::RefNull(rtt.type.ref_index()),
|
||||
this->module_);
|
||||
}
|
||||
|
||||
@ -4283,7 +4280,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Push(rtt);
|
||||
}
|
||||
ArgVector args = PeekArgs(imm.struct_type, 1);
|
||||
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
|
||||
Value value = CreateValue(ValueType::Ref(imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructNewWithRtt, imm, rtt,
|
||||
args.begin(), &value);
|
||||
Drop(rtt);
|
||||
@ -4314,7 +4311,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
|
||||
Push(rtt);
|
||||
}
|
||||
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
|
||||
Value value = CreateValue(ValueType::Ref(imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructNewDefault, imm, rtt, &value);
|
||||
Drop(rtt);
|
||||
Push(value);
|
||||
@ -4335,7 +4332,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
return 0;
|
||||
}
|
||||
Value struct_obj =
|
||||
Peek(0, 0, ValueType::Ref(field.struct_imm.index, kNullable));
|
||||
Peek(0, 0, ValueType::RefNull(field.struct_imm.index));
|
||||
Value value = CreateValue(field_type);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructGet, struct_obj, field, true,
|
||||
&value);
|
||||
@ -4359,7 +4356,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
return 0;
|
||||
}
|
||||
Value struct_obj =
|
||||
Peek(0, 0, ValueType::Ref(field.struct_imm.index, kNullable));
|
||||
Peek(0, 0, ValueType::RefNull(field.struct_imm.index));
|
||||
Value value = CreateValue(field_type.Unpacked());
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructGet, struct_obj, field,
|
||||
opcode == kExprStructGetS, &value);
|
||||
@ -4380,7 +4377,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Value field_value =
|
||||
Peek(0, 1, struct_type->field(field.field_imm.index).Unpacked());
|
||||
Value struct_obj =
|
||||
Peek(1, 0, ValueType::Ref(field.struct_imm.index, kNullable));
|
||||
Peek(1, 0, ValueType::RefNull(field.struct_imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructSet, struct_obj, field,
|
||||
field_value);
|
||||
Drop(2);
|
||||
@ -4401,7 +4398,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Value length = Peek(1, 1, kWasmI32);
|
||||
Value initial_value =
|
||||
Peek(2, 0, imm.array_type->element_type().Unpacked());
|
||||
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
|
||||
Value value = CreateValue(ValueType::Ref(imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayNewWithRtt, imm, length,
|
||||
initial_value, rtt, &value);
|
||||
Drop(3); // rtt, length, initial_value.
|
||||
@ -4428,7 +4425,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Push(rtt);
|
||||
}
|
||||
Value length = Peek(1, 0, kWasmI32);
|
||||
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
|
||||
Value value = CreateValue(ValueType::Ref(imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayNewDefault, imm, length, rtt,
|
||||
&value);
|
||||
Drop(2); // rtt, length
|
||||
@ -4473,8 +4470,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Value length = Peek(1, 1, kWasmI32);
|
||||
Value offset = Peek(2, 0, kWasmI32);
|
||||
|
||||
Value array =
|
||||
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
|
||||
Value array = CreateValue(ValueType::Ref(array_imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayNewSegment, array_imm,
|
||||
data_segment, offset, length, rtt,
|
||||
&array);
|
||||
@ -4506,8 +4502,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Value rtt = CreateValue(rtt_type);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, array_imm.index, &rtt);
|
||||
Push(rtt);
|
||||
Value array =
|
||||
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
|
||||
Value array = CreateValue(ValueType::Ref(array_imm.index));
|
||||
ValueType elem_segment_type =
|
||||
this->module_->elem_segments[elem_segment.index].type;
|
||||
if (V8_UNLIKELY(
|
||||
@ -4543,7 +4538,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
return 0;
|
||||
}
|
||||
Value index = Peek(0, 1, kWasmI32);
|
||||
Value array_obj = Peek(1, 0, ValueType::Ref(imm.index, kNullable));
|
||||
Value array_obj = Peek(1, 0, ValueType::RefNull(imm.index));
|
||||
Value value = CreateValue(imm.array_type->element_type().Unpacked());
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayGet, array_obj, imm, index,
|
||||
opcode == kExprArrayGetS, &value);
|
||||
@ -4563,7 +4558,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
return 0;
|
||||
}
|
||||
Value index = Peek(0, 1, kWasmI32);
|
||||
Value array_obj = Peek(1, 0, ValueType::Ref(imm.index, kNullable));
|
||||
Value array_obj = Peek(1, 0, ValueType::RefNull(imm.index));
|
||||
Value value = CreateValue(imm.array_type->element_type());
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayGet, array_obj, imm, index,
|
||||
true, &value);
|
||||
@ -4582,7 +4577,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
Value value = Peek(0, 2, imm.array_type->element_type().Unpacked());
|
||||
Value index = Peek(1, 1, kWasmI32);
|
||||
Value array_obj = Peek(2, 0, ValueType::Ref(imm.index, kNullable));
|
||||
Value array_obj = Peek(2, 0, ValueType::RefNull(imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArraySet, array_obj, imm, index,
|
||||
value);
|
||||
Drop(3);
|
||||
@ -4593,8 +4588,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// Read but ignore an immediate array type index.
|
||||
// TODO(7748): Remove this once we are ready to make breaking changes.
|
||||
ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
|
||||
Value array_obj =
|
||||
Peek(0, 0, ValueType::Ref(HeapType::kArray, kNullable));
|
||||
Value array_obj = Peek(0, 0, ValueType::RefNull(HeapType::kArray));
|
||||
Value value = CreateValue(kWasmI32);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value);
|
||||
Drop(array_obj);
|
||||
@ -4626,9 +4620,9 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
return 0;
|
||||
}
|
||||
// [dst, dst_index, src, src_index, length]
|
||||
Value dst = Peek(4, 0, ValueType::Ref(dst_imm.index, kNullable));
|
||||
Value dst = Peek(4, 0, ValueType::RefNull(dst_imm.index));
|
||||
Value dst_index = Peek(3, 1, kWasmI32);
|
||||
Value src = Peek(2, 2, ValueType::Ref(src_imm.index, kNullable));
|
||||
Value src = Peek(2, 2, ValueType::RefNull(src_imm.index));
|
||||
Value src_index = Peek(1, 3, kWasmI32);
|
||||
Value length = Peek(0, 4, kWasmI32);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayCopy, dst, dst_index, src,
|
||||
@ -4664,8 +4658,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
element_type.Unpacked());
|
||||
FunctionSig element_sig(0, elem_count, element_types.data());
|
||||
ArgVector elements = PeekArgs(&element_sig, 1);
|
||||
Value result =
|
||||
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
|
||||
Value result = CreateValue(ValueType::Ref(array_imm.index));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayNewFixed, array_imm, elements,
|
||||
rtt, &result);
|
||||
Drop(elem_count + 1);
|
||||
@ -4730,8 +4723,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Value obj = Peek(1);
|
||||
Value value = CreateValue(kWasmI32);
|
||||
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
|
||||
IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(HeapType::kData, kNullable),
|
||||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
|
||||
this->module_) ||
|
||||
obj.type.is_bottom())) {
|
||||
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
|
||||
@ -4778,14 +4770,13 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
opcode_length += imm.length;
|
||||
Value obj = Peek(0);
|
||||
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
|
||||
IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(HeapType::kData, kNullable),
|
||||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
|
||||
this->module_) ||
|
||||
obj.type.is_bottom())) {
|
||||
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
|
||||
return 0;
|
||||
}
|
||||
Value value = CreateValue(ValueType::Ref(
|
||||
Value value = CreateValue(ValueType::RefMaybeNull(
|
||||
imm.index,
|
||||
obj.type.is_bottom() ? kNonNullable : obj.type.nullability()));
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(Forward, obj, &value);
|
||||
@ -4814,21 +4805,20 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
Value obj = Peek(1);
|
||||
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
|
||||
IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(HeapType::kData, kNullable),
|
||||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
|
||||
this->module_) ||
|
||||
obj.type.is_bottom())) {
|
||||
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
|
||||
return 0;
|
||||
}
|
||||
// If either value is bottom, we emit the most specific type possible.
|
||||
Value value =
|
||||
CreateValue(rtt.type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::Ref(rtt.type.ref_index(),
|
||||
obj.type.is_bottom()
|
||||
? kNonNullable
|
||||
: obj.type.nullability()));
|
||||
Value value = CreateValue(
|
||||
rtt.type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::RefMaybeNull(rtt.type.ref_index(),
|
||||
obj.type.is_bottom()
|
||||
? kNonNullable
|
||||
: obj.type.nullability()));
|
||||
if (current_code_reachable_and_ok_) {
|
||||
// This logic ensures that code generation can assume that functions
|
||||
// can only be cast to function types, and data objects to data types.
|
||||
@ -4886,8 +4876,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
Value obj = Peek(1);
|
||||
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
|
||||
IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(HeapType::kData, kNullable),
|
||||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
|
||||
this->module_) ||
|
||||
obj.type.is_bottom())) {
|
||||
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
|
||||
@ -4905,10 +4894,9 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// will be on the stack when the branch is taken.
|
||||
// TODO(jkummerow): Reconsider this choice.
|
||||
Drop(2); // {obj} and {rtt}.
|
||||
Push(CreateValue(
|
||||
rtt.type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::Ref(rtt.type.ref_index(), kNonNullable)));
|
||||
Push(CreateValue(rtt.type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::Ref(rtt.type.ref_index())));
|
||||
// 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);
|
||||
@ -4970,8 +4958,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
Value obj = Peek(1);
|
||||
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
|
||||
IsSubtypeOf(obj.type,
|
||||
ValueType::Ref(HeapType::kData, kNullable),
|
||||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
|
||||
this->module_) ||
|
||||
obj.type.is_bottom())) {
|
||||
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
|
||||
@ -4992,9 +4979,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
Drop(rtt);
|
||||
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
|
||||
Value result_on_fallthrough = CreateValue(
|
||||
rtt.type.is_bottom()
|
||||
? kWasmBottom
|
||||
: ValueType::Ref(rtt.type.ref_index(), kNonNullable));
|
||||
rtt.type.is_bottom() ? kWasmBottom
|
||||
: ValueType::Ref(rtt.type.ref_index()));
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
// This logic ensures that code generation can assume that functions
|
||||
// can only be cast to function types, and data objects to data types.
|
||||
@ -5069,7 +5055,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
NON_CONST_ONLY \
|
||||
Value arg = Peek(0, 0, kWasmAnyRef); \
|
||||
ValueType non_nullable_abstract_type = \
|
||||
ValueType::Ref(HeapType::k##h_type, kNonNullable); \
|
||||
ValueType::Ref(HeapType::k##h_type); \
|
||||
Value result = CreateValue(non_nullable_abstract_type); \
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) { \
|
||||
if (IsHeapSubtypeOf(arg.type.heap_type(), HeapType(HeapType::k##h_type), \
|
||||
@ -5132,8 +5118,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
? HeapType::kData
|
||||
: opcode == kExprBrOnArray ? HeapType::kArray
|
||||
: HeapType::kI31;
|
||||
Value result_on_branch =
|
||||
CreateValue(ValueType::Ref(heap_type, kNonNullable));
|
||||
Value result_on_branch = CreateValue(ValueType::Ref(heap_type));
|
||||
Push(result_on_branch);
|
||||
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
|
||||
// The {value_on_branch} parameter we pass to the interface must be
|
||||
@ -5184,8 +5169,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
? HeapType::kData
|
||||
: opcode == kExprBrOnNonArray ? HeapType::kArray
|
||||
: HeapType::kI31;
|
||||
Value value_on_fallthrough =
|
||||
CreateValue(ValueType::Ref(heap_type, kNonNullable));
|
||||
Value value_on_fallthrough = CreateValue(ValueType::Ref(heap_type));
|
||||
|
||||
if (V8_LIKELY(current_code_reachable_and_ok_)) {
|
||||
if (opcode == kExprBrOnNonFunc) {
|
||||
|
@ -1969,8 +1969,7 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
ValueType type =
|
||||
enabled_features_.has_typed_funcref()
|
||||
? ValueType::Ref(module_->functions[index].sig_index,
|
||||
kNonNullable)
|
||||
? ValueType::Ref(module_->functions[index].sig_index)
|
||||
: kWasmFuncRef;
|
||||
TYPE_CHECK(type)
|
||||
module_->functions[index].declared = true;
|
||||
@ -1984,7 +1983,7 @@ class ModuleDecoderImpl : public Decoder {
|
||||
this, pc() + 1, &length, module_.get(), enabled_features_);
|
||||
if (V8_UNLIKELY(failed())) return {};
|
||||
if (V8_LIKELY(lookahead(1 + length, kExprEnd))) {
|
||||
TYPE_CHECK(ValueType::Ref(type, kNullable))
|
||||
TYPE_CHECK(ValueType::RefNull(type))
|
||||
consume_bytes(length + 2);
|
||||
return ConstantExpression::RefNull(type.representation());
|
||||
}
|
||||
@ -2276,7 +2275,7 @@ class ModuleDecoderImpl : public Decoder {
|
||||
if (failed()) return index;
|
||||
DCHECK_NOT_NULL(func);
|
||||
DCHECK_EQ(index, func->func_index);
|
||||
ValueType entry_type = ValueType::Ref(func->sig_index, kNonNullable);
|
||||
ValueType entry_type = ValueType::Ref(func->sig_index);
|
||||
if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))) {
|
||||
errorf(initial_pc,
|
||||
"Invalid type in element entry: expected %s, got %s instead.",
|
||||
|
@ -330,14 +330,32 @@ class ValueType {
|
||||
DCHECK(kind == kBottom || kind <= kI16);
|
||||
return ValueType(KindField::encode(kind));
|
||||
}
|
||||
static constexpr ValueType Ref(uint32_t heap_type, Nullability nullability) {
|
||||
static constexpr ValueType Ref(uint32_t heap_type) {
|
||||
DCHECK(HeapType(heap_type).is_valid());
|
||||
return ValueType(KindField::encode(kRef) |
|
||||
HeapTypeField::encode(heap_type));
|
||||
}
|
||||
static constexpr ValueType Ref(HeapType heap_type) {
|
||||
return Ref(heap_type.representation());
|
||||
}
|
||||
static constexpr ValueType RefNull(uint32_t heap_type) {
|
||||
DCHECK(HeapType(heap_type).is_valid());
|
||||
return ValueType(KindField::encode(kRefNull) |
|
||||
HeapTypeField::encode(heap_type));
|
||||
}
|
||||
static constexpr ValueType RefNull(HeapType heap_type) {
|
||||
return RefNull(heap_type.representation());
|
||||
}
|
||||
static constexpr ValueType RefMaybeNull(uint32_t heap_type,
|
||||
Nullability nullability) {
|
||||
DCHECK(HeapType(heap_type).is_valid());
|
||||
return ValueType(
|
||||
KindField::encode(nullability == kNullable ? kRefNull : kRef) |
|
||||
HeapTypeField::encode(heap_type));
|
||||
}
|
||||
static constexpr ValueType Ref(HeapType heap_type, Nullability nullability) {
|
||||
return Ref(heap_type.representation(), nullability);
|
||||
static constexpr ValueType RefMaybeNull(HeapType heap_type,
|
||||
Nullability nullability) {
|
||||
return RefMaybeNull(heap_type.representation(), nullability);
|
||||
}
|
||||
|
||||
static constexpr ValueType Rtt(uint32_t type_index) {
|
||||
@ -391,12 +409,12 @@ class ValueType {
|
||||
|
||||
// If {this} is (ref null $t), returns (ref $t). Otherwise, returns {this}.
|
||||
constexpr ValueType AsNonNull() const {
|
||||
return is_nullable() ? Ref(heap_type(), kNonNullable) : *this;
|
||||
return is_nullable() ? Ref(heap_type()) : *this;
|
||||
}
|
||||
|
||||
// If {this} is (ref $t), returns (ref null $t). Otherwise, returns {this}.
|
||||
constexpr ValueType AsNullable() const {
|
||||
return is_non_nullable() ? Ref(heap_type(), kNullable) : *this;
|
||||
return is_non_nullable() ? RefNull(heap_type()) : *this;
|
||||
}
|
||||
|
||||
/***************************** Field Accessors ******************************/
|
||||
@ -468,7 +486,7 @@ class ValueType {
|
||||
case MachineRepresentation::kFloat64:
|
||||
return Primitive(kF64);
|
||||
case MachineRepresentation::kTaggedPointer:
|
||||
return Ref(HeapType::kAny, kNullable);
|
||||
return RefNull(HeapType::kAny);
|
||||
case MachineRepresentation::kSimd128:
|
||||
return Primitive(kS128);
|
||||
default:
|
||||
@ -650,29 +668,25 @@ constexpr ValueType kWasmI16 = ValueType::Primitive(kI16);
|
||||
constexpr ValueType kWasmVoid = ValueType::Primitive(kVoid);
|
||||
constexpr ValueType kWasmBottom = ValueType::Primitive(kBottom);
|
||||
// Established reference-type and wasm-gc proposal shorthands.
|
||||
constexpr ValueType kWasmFuncRef = ValueType::Ref(HeapType::kFunc, kNullable);
|
||||
constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable);
|
||||
constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
|
||||
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
|
||||
constexpr ValueType kWasmDataRef =
|
||||
ValueType::Ref(HeapType::kData, kNonNullable);
|
||||
constexpr ValueType kWasmArrayRef =
|
||||
ValueType::Ref(HeapType::kArray, kNonNullable);
|
||||
constexpr ValueType kWasmStringRef =
|
||||
ValueType::Ref(HeapType::kString, kNullable);
|
||||
constexpr ValueType kWasmFuncRef = ValueType::RefNull(HeapType::kFunc);
|
||||
constexpr ValueType kWasmAnyRef = ValueType::RefNull(HeapType::kAny);
|
||||
constexpr ValueType kWasmEqRef = ValueType::RefNull(HeapType::kEq);
|
||||
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31);
|
||||
constexpr ValueType kWasmDataRef = ValueType::Ref(HeapType::kData);
|
||||
constexpr ValueType kWasmArrayRef = ValueType::Ref(HeapType::kArray);
|
||||
constexpr ValueType kWasmStringRef = ValueType::RefNull(HeapType::kString);
|
||||
constexpr ValueType kWasmStringViewWtf8 =
|
||||
ValueType::Ref(HeapType::kStringViewWtf8, kNullable);
|
||||
ValueType::RefNull(HeapType::kStringViewWtf8);
|
||||
constexpr ValueType kWasmStringViewWtf16 =
|
||||
ValueType::Ref(HeapType::kStringViewWtf16, kNullable);
|
||||
ValueType::RefNull(HeapType::kStringViewWtf16);
|
||||
constexpr ValueType kWasmStringViewIter =
|
||||
ValueType::Ref(HeapType::kStringViewIter, kNullable);
|
||||
ValueType::RefNull(HeapType::kStringViewIter);
|
||||
|
||||
// Constants used by the generic js-to-wasm wrapper.
|
||||
constexpr int kWasmValueKindBitsMask = (1u << ValueType::kKindBits) - 1;
|
||||
|
||||
// This is used in wasm.tq.
|
||||
constexpr ValueType kWasmAnyNonNullableRef =
|
||||
ValueType::Ref(HeapType::kAny, kNonNullable);
|
||||
constexpr ValueType kWasmAnyNonNullableRef = ValueType::Ref(HeapType::kAny);
|
||||
|
||||
#define FOREACH_WASMVALUE_CTYPES(V) \
|
||||
V(kI32, int32_t) \
|
||||
|
@ -34,23 +34,23 @@ ValueType WasmInitExpr::type(const WasmModule* module,
|
||||
uint32_t heap_type = enabled_features.has_typed_funcref()
|
||||
? module->functions[immediate().index].sig_index
|
||||
: HeapType::kFunc;
|
||||
return ValueType::Ref(heap_type, kNonNullable);
|
||||
return ValueType::Ref(heap_type);
|
||||
}
|
||||
case kRefNullConst:
|
||||
return ValueType::Ref(immediate().heap_type, kNullable);
|
||||
return ValueType::RefNull(immediate().heap_type);
|
||||
case kStructNewWithRtt:
|
||||
case kStructNew:
|
||||
case kStructNewDefaultWithRtt:
|
||||
case kStructNewDefault:
|
||||
case kArrayNewFixed:
|
||||
case kArrayNewFixedStatic:
|
||||
return ValueType::Ref(immediate().index, kNonNullable);
|
||||
return ValueType::Ref(immediate().index);
|
||||
case kI31New:
|
||||
return kWasmI31Ref.AsNonNull();
|
||||
case kRttCanon:
|
||||
return ValueType::Rtt(immediate().heap_type);
|
||||
case kStringConst:
|
||||
return ValueType::Ref(HeapType::kString, kNonNullable);
|
||||
return ValueType::Ref(HeapType::kString);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2366,8 +2366,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
const WasmModule* exporting_module = function.instance().module();
|
||||
ValueType real_type = ValueType::Ref(
|
||||
exporting_module->functions[function.function_index()]
|
||||
.sig_index,
|
||||
kNonNullable);
|
||||
.sig_index);
|
||||
if (!IsSubtypeOf(real_type, expected, exporting_module, module)) {
|
||||
*error_message =
|
||||
"assigned exported function has to be a subtype of the "
|
||||
|
@ -394,20 +394,21 @@ V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1, ValueType type2,
|
||||
HeapType heap1 = type1.heap_type();
|
||||
HeapType heap2 = type2.heap_type();
|
||||
if (heap1 == heap2 && module1 == module2) {
|
||||
return {ValueType::Ref(heap1, nullability), module1};
|
||||
return {ValueType::RefMaybeNull(heap1, nullability), module1};
|
||||
}
|
||||
if (heap1.is_generic()) {
|
||||
return {ValueType::Ref(CommonAncestorWithGeneric(heap1, heap2, module2),
|
||||
nullability),
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestorWithGeneric(heap1, heap2, module2), nullability),
|
||||
module1};
|
||||
} else if (heap2.is_generic()) {
|
||||
return {ValueType::Ref(CommonAncestorWithGeneric(heap2, heap1, module1),
|
||||
nullability),
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestorWithGeneric(heap2, heap1, module1), nullability),
|
||||
module1};
|
||||
} else {
|
||||
return {ValueType::Ref(CommonAncestor(heap1.ref_index(), heap2.ref_index(),
|
||||
module1, module2),
|
||||
nullability),
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestor(heap1.ref_index(), heap2.ref_index(), module1,
|
||||
module2),
|
||||
nullability),
|
||||
module1};
|
||||
}
|
||||
}
|
||||
@ -423,11 +424,13 @@ TypeInModule Intersection(ValueType type1, ValueType type2,
|
||||
Nullability nullability =
|
||||
type1.is_nullable() && type2.is_nullable() ? kNullable : kNonNullable;
|
||||
return IsHeapSubtypeOf(type1.heap_type(), type2.heap_type(), module1, module2)
|
||||
? TypeInModule{ValueType::Ref(type1.heap_type(), nullability),
|
||||
? TypeInModule{ValueType::RefMaybeNull(type1.heap_type(),
|
||||
nullability),
|
||||
module1}
|
||||
: IsHeapSubtypeOf(type2.heap_type(), type1.heap_type(), module2,
|
||||
module1)
|
||||
? TypeInModule{ValueType::Ref(type2.heap_type(), nullability),
|
||||
? TypeInModule{ValueType::RefMaybeNull(type2.heap_type(),
|
||||
nullability),
|
||||
module2}
|
||||
: TypeInModule{kWasmBottom, module1};
|
||||
}
|
||||
|
@ -264,11 +264,9 @@ class WasmGCTester {
|
||||
ErrorThrower thrower;
|
||||
};
|
||||
|
||||
ValueType ref(uint32_t type_index) {
|
||||
return ValueType::Ref(type_index, kNonNullable);
|
||||
}
|
||||
ValueType ref(uint32_t type_index) { return ValueType::Ref(type_index); }
|
||||
ValueType refNull(uint32_t type_index) {
|
||||
return ValueType::Ref(type_index, kNullable);
|
||||
return ValueType::RefNull(type_index);
|
||||
}
|
||||
|
||||
WASM_COMPILED_EXEC_TEST(WasmBasicStruct) {
|
||||
@ -495,15 +493,15 @@ WASM_COMPILED_EXEC_TEST(RefCast) {
|
||||
{F(kWasmI32, true), F(kWasmF32, false)}, supertype_index);
|
||||
const byte subtype2_index = tester.DefineStruct(
|
||||
{F(kWasmI32, true), F(kWasmI64, false)}, supertype_index);
|
||||
auto super_sig = FixedSizeSignature<ValueType>::Params(
|
||||
ValueType::Ref(subtype1_index, kNullable))
|
||||
.Returns(ValueType::Ref(supertype_index, kNullable));
|
||||
auto sub_sig1 = FixedSizeSignature<ValueType>::Params(
|
||||
ValueType::Ref(supertype_index, kNullable))
|
||||
.Returns(ValueType::Ref(subtype1_index, kNullable));
|
||||
auto sub_sig2 = FixedSizeSignature<ValueType>::Params(
|
||||
ValueType::Ref(supertype_index, kNullable))
|
||||
.Returns(ValueType::Ref(subtype2_index, kNullable));
|
||||
auto super_sig =
|
||||
FixedSizeSignature<ValueType>::Params(ValueType::RefNull(subtype1_index))
|
||||
.Returns(ValueType::RefNull(supertype_index));
|
||||
auto sub_sig1 =
|
||||
FixedSizeSignature<ValueType>::Params(ValueType::RefNull(supertype_index))
|
||||
.Returns(ValueType::RefNull(subtype1_index));
|
||||
auto sub_sig2 =
|
||||
FixedSizeSignature<ValueType>::Params(ValueType::RefNull(supertype_index))
|
||||
.Returns(ValueType::RefNull(subtype2_index));
|
||||
const byte function_type_index = tester.DefineSignature(&super_sig);
|
||||
const byte function_subtype1_index =
|
||||
tester.DefineSignature(&sub_sig1, function_type_index);
|
||||
@ -515,11 +513,11 @@ WASM_COMPILED_EXEC_TEST(RefCast) {
|
||||
WASM_RTT_CANON(subtype1_index)),
|
||||
WASM_END});
|
||||
// Just so this function counts as "declared".
|
||||
tester.AddGlobal(ValueType::Ref(function_type_index, kNullable), false,
|
||||
tester.AddGlobal(ValueType::RefNull(function_type_index), false,
|
||||
WasmInitExpr::RefFuncConst(function_index));
|
||||
|
||||
const byte kTestSuccessful = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
subtype1_index, WASM_RTT_CANON(subtype1_index))),
|
||||
WASM_STRUCT_GET(
|
||||
@ -528,7 +526,7 @@ WASM_COMPILED_EXEC_TEST(RefCast) {
|
||||
WASM_END});
|
||||
|
||||
const byte kTestFailed = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
subtype1_index, WASM_RTT_CANON(subtype1_index))),
|
||||
WASM_STRUCT_GET(
|
||||
@ -537,20 +535,20 @@ WASM_COMPILED_EXEC_TEST(RefCast) {
|
||||
WASM_END});
|
||||
|
||||
const byte kFuncTestSuccessfulSuper = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(function_type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(function_type_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_REF_FUNC(function_index)),
|
||||
WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_RTT_CANON(function_type_index)),
|
||||
WASM_DROP, WASM_I32V(0), WASM_END});
|
||||
|
||||
const byte kFuncTestSuccessfulSub = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(function_type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(function_type_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_REF_FUNC(function_index)),
|
||||
WASM_REF_CAST(WASM_LOCAL_GET(0),
|
||||
WASM_RTT_CANON(function_subtype1_index)),
|
||||
WASM_DROP, WASM_I32V(0), WASM_END});
|
||||
|
||||
const byte kFuncTestFailed = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(function_type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(function_type_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_REF_FUNC(function_index)),
|
||||
WASM_REF_CAST(WASM_LOCAL_GET(0),
|
||||
WASM_RTT_CANON(function_subtype2_index)),
|
||||
@ -574,14 +572,14 @@ WASM_COMPILED_EXEC_TEST(RefCastStatic) {
|
||||
{F(kWasmI32, true), F(kWasmI64, false)}, supertype_index);
|
||||
|
||||
const byte kTestSuccessful = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype1_index)),
|
||||
WASM_STRUCT_GET(subtype1_index, 0,
|
||||
WASM_REF_CAST_STATIC(WASM_LOCAL_GET(0), subtype1_index)),
|
||||
WASM_END});
|
||||
|
||||
const byte kTestFailed = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype1_index)),
|
||||
WASM_STRUCT_GET(subtype2_index, 0,
|
||||
WASM_REF_CAST_STATIC(WASM_LOCAL_GET(0), subtype2_index)),
|
||||
@ -603,14 +601,14 @@ WASM_COMPILED_EXEC_TEST(RefCastStaticNoChecks) {
|
||||
{F(kWasmI32, true), F(kWasmI64, false)}, supertype_index);
|
||||
|
||||
const byte kTestSuccessful = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype1_index)),
|
||||
WASM_STRUCT_GET(subtype1_index, 0,
|
||||
WASM_REF_CAST_STATIC(WASM_LOCAL_GET(0), subtype1_index)),
|
||||
WASM_END});
|
||||
|
||||
const byte kTestFailed = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(supertype_index)},
|
||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype1_index)),
|
||||
WASM_STRUCT_GET(subtype2_index, 0,
|
||||
WASM_REF_CAST_STATIC(WASM_LOCAL_GET(0), subtype2_index)),
|
||||
@ -623,7 +621,7 @@ WASM_COMPILED_EXEC_TEST(RefCastStaticNoChecks) {
|
||||
|
||||
WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
WasmGCTester tester(execution_tier);
|
||||
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
|
||||
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
|
||||
const byte type_index = tester.DefineStruct({F(kWasmI32, true)});
|
||||
const byte other_type_index = tester.DefineStruct({F(kWasmF32, true)});
|
||||
const byte rtt_index =
|
||||
@ -632,36 +630,35 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
static_cast<HeapType::Representation>(type_index)));
|
||||
const byte kTestStruct = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
|
||||
{WASM_BLOCK_R(ValueType::Ref(type_index, kNullable),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
// Pipe a struct through a local so it's statically typed
|
||||
// as dataref.
|
||||
WASM_LOCAL_SET(1, WASM_STRUCT_NEW_WITH_RTT(
|
||||
other_type_index, WASM_F32(1.0),
|
||||
WASM_RTT_CANON(other_type_index))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// The type check fails, so this branch isn't taken.
|
||||
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)), WASM_DROP,
|
||||
{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 dataref.
|
||||
WASM_LOCAL_SET(
|
||||
1, WASM_STRUCT_NEW_WITH_RTT(other_type_index, WASM_F32(1.0),
|
||||
WASM_RTT_CANON(other_type_index))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// The type check fails, so this branch isn't taken.
|
||||
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)), WASM_DROP,
|
||||
|
||||
WASM_LOCAL_SET(0, WASM_I32V(221)), // (Final result) - 1
|
||||
WASM_LOCAL_SET(1, WASM_STRUCT_NEW_WITH_RTT(
|
||||
type_index, WASM_I32V(1),
|
||||
WASM_GLOBAL_GET(rtt_index))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// This branch is taken.
|
||||
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)),
|
||||
WASM_GLOBAL_GET(rtt_index), WASM_GC_OP(kExprRefCast),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(221)), // (Final result) - 1
|
||||
WASM_LOCAL_SET(1,
|
||||
WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(1),
|
||||
WASM_GLOBAL_GET(rtt_index))),
|
||||
WASM_LOCAL_GET(1),
|
||||
// This branch is taken.
|
||||
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)),
|
||||
WASM_GLOBAL_GET(rtt_index), WASM_GC_OP(kExprRefCast),
|
||||
|
||||
// Not executed due to the branch.
|
||||
WASM_LOCAL_SET(0, WASM_I32V(333))),
|
||||
// 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 kTestStructStatic = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
|
||||
{WASM_BLOCK_R(
|
||||
ValueType::Ref(type_index, kNullable),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
ValueType::RefNull(type_index), WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
// Pipe a struct through a local so it's statically typed
|
||||
// as dataref.
|
||||
WASM_LOCAL_SET(1, WASM_STRUCT_NEW(other_type_index, WASM_F32(1.0))),
|
||||
@ -683,7 +680,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
|
||||
const byte kTestNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
|
||||
{WASM_BLOCK_R(ValueType::Ref(type_index, kNullable),
|
||||
{WASM_BLOCK_R(ValueType::RefNull(type_index),
|
||||
WASM_LOCAL_SET(0, WASM_I32V(111)),
|
||||
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
|
||||
// Not taken for nullref.
|
||||
@ -700,8 +697,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
WASM_BLOCK_I(
|
||||
// The inner block should take the early branch with a struct
|
||||
// on the stack.
|
||||
WASM_BLOCK_R(ValueType::Ref(type_index, kNonNullable),
|
||||
WASM_LOCAL_GET(1),
|
||||
WASM_BLOCK_R(ValueType::Ref(type_index), WASM_LOCAL_GET(1),
|
||||
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)),
|
||||
// Returning 123 is the unreachable failure case.
|
||||
WASM_I32V(123), WASM_BR(1)),
|
||||
@ -719,7 +715,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
|
||||
|
||||
WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
|
||||
WasmGCTester tester(execution_tier);
|
||||
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
|
||||
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
|
||||
const byte type0 = tester.DefineStruct({F(kWasmI32, true)});
|
||||
const byte type1 =
|
||||
tester.DefineStruct({F(kWasmI64, true), F(kWasmI32, true)});
|
||||
@ -760,11 +756,11 @@ WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
|
||||
const byte kNull = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {kDataRefNull}, {FUNCTION_BODY(WASM_REF_NULL(type0))});
|
||||
|
||||
const byte kUnrelatedTypes = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(type1, kNullable)},
|
||||
{FUNCTION_BODY(WASM_STRUCT_NEW_WITH_RTT(type1, WASM_I64V(10),
|
||||
WASM_I32V(field1_value),
|
||||
WASM_RTT_CANON(type1)))});
|
||||
const byte kUnrelatedTypes =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {ValueType::RefNull(type1)},
|
||||
{FUNCTION_BODY(WASM_STRUCT_NEW_WITH_RTT(
|
||||
type1, WASM_I64V(10), WASM_I32V(field1_value),
|
||||
WASM_RTT_CANON(type1)))});
|
||||
#undef FUNCTION_BODY
|
||||
|
||||
const byte kBranchTakenStatic = tester.DefineFunction(
|
||||
@ -1787,7 +1783,7 @@ WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
|
||||
type_index, WASM_I32V(10), WASM_RTT_CANON(type_index))),
|
||||
kExprEnd});
|
||||
const byte kIsArrayUpcastNullable = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(type_index)},
|
||||
{WASM_LOCAL_SET(
|
||||
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(10),
|
||||
WASM_RTT_CANON(type_index))),
|
||||
@ -1796,7 +1792,7 @@ WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_REF_IS_ARRAY(WASM_REF_NULL(type_index)), kExprEnd});
|
||||
const byte kIsArrayUnrelated = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(struct_type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(struct_type_index)},
|
||||
{WASM_LOCAL_SET(
|
||||
0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
struct_type_index, WASM_RTT_CANON(struct_type_index))),
|
||||
@ -1818,7 +1814,7 @@ WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
|
||||
type_index, WASM_I32V(10), WASM_RTT_CANON(type_index)))),
|
||||
kExprEnd});
|
||||
const byte kAsArrayUpcastNullable = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(type_index)},
|
||||
{WASM_LOCAL_SET(
|
||||
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(10),
|
||||
WASM_RTT_CANON(type_index))),
|
||||
@ -1828,7 +1824,7 @@ WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
|
||||
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_REF_NULL(type_index))),
|
||||
kExprEnd});
|
||||
const byte kAsArrayUnrelated = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {ValueType::Ref(struct_type_index, kNullable)},
|
||||
tester.sigs.i_v(), {ValueType::RefNull(struct_type_index)},
|
||||
{WASM_LOCAL_SET(
|
||||
0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
struct_type_index, WASM_RTT_CANON(struct_type_index))),
|
||||
@ -1894,7 +1890,7 @@ WASM_COMPILED_EXEC_TEST(ArrayNewMap) {
|
||||
|
||||
const byte type_index = tester.DefineArray(kWasmI32, true);
|
||||
|
||||
ValueType array_type = ValueType::Ref(type_index, kNonNullable);
|
||||
ValueType array_type = ValueType::Ref(type_index);
|
||||
FunctionSig sig(1, 0, &array_type);
|
||||
const byte array_new_with_rtt = tester.DefineFunction(
|
||||
&sig, {},
|
||||
@ -1933,10 +1929,10 @@ WASM_COMPILED_EXEC_TEST(FunctionRefs) {
|
||||
const byte other_sig_index = tester.DefineSignature(tester.sigs.d_d());
|
||||
|
||||
// This is just so func_index counts as "declared".
|
||||
tester.AddGlobal(ValueType::Ref(sig_index, kNullable), false,
|
||||
tester.AddGlobal(ValueType::RefNull(sig_index), false,
|
||||
WasmInitExpr::RefFuncConst(func_index));
|
||||
|
||||
ValueType func_type = ValueType::Ref(sig_index, kNullable);
|
||||
ValueType func_type = ValueType::RefNull(sig_index);
|
||||
FunctionSig sig_func(1, 0, &func_type);
|
||||
|
||||
ValueType rtt0 = ValueType::Rtt(sig_index);
|
||||
@ -2002,7 +1998,7 @@ WASM_COMPILED_EXEC_TEST(CallRef) {
|
||||
kExprEnd});
|
||||
|
||||
// This is just so func_index counts as "declared".
|
||||
tester.AddGlobal(ValueType::Ref(0, kNullable), false,
|
||||
tester.AddGlobal(ValueType::RefNull(0), false,
|
||||
WasmInitExpr::RefFuncConst(callee));
|
||||
|
||||
tester.CompileModule();
|
||||
@ -2062,7 +2058,7 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
|
||||
byte sig_index = 2;
|
||||
|
||||
// This is just so func_index counts as "declared".
|
||||
tester.AddGlobal(ValueType::Ref(sig_index, kNullable), false,
|
||||
tester.AddGlobal(ValueType::RefNull(sig_index), false,
|
||||
WasmInitExpr::RefFuncConst(function_index));
|
||||
|
||||
byte kDataCheckNull = tester.DefineFunction(
|
||||
@ -2284,11 +2280,11 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
|
||||
const byte SubType = tester.DefineStruct(
|
||||
{F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}, SuperType);
|
||||
|
||||
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
|
||||
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
|
||||
const byte ListType = tester.DefineArray(kDataRefNull, true);
|
||||
|
||||
const byte List =
|
||||
tester.AddGlobal(ValueType::Ref(ListType, kNullable), true,
|
||||
tester.AddGlobal(ValueType::RefNull(ListType), true,
|
||||
WasmInitExpr::RefNullConst(
|
||||
static_cast<HeapType::Representation>(ListType)));
|
||||
const byte RttSuper = tester.AddGlobal(
|
||||
@ -2339,7 +2335,7 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
|
||||
{
|
||||
wasm::kWasmI32,
|
||||
wasm::kWasmI32,
|
||||
ValueType::Ref(ListType, kNullable),
|
||||
ValueType::RefNull(ListType),
|
||||
},
|
||||
{WASM_LOCAL_SET(list, WASM_GLOBAL_GET(List)),
|
||||
// sum = 0;
|
||||
|
@ -437,7 +437,7 @@ TEST(Liftoff_debug_side_table_catch_all) {
|
||||
LiftoffCompileEnvironment env;
|
||||
TestSignatures sigs;
|
||||
int ex = env.builder()->AddException(sigs.v_v());
|
||||
ValueType exception_type = ValueType::Ref(HeapType::kAny, kNonNullable);
|
||||
ValueType exception_type = ValueType::Ref(HeapType::kAny);
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{}, {kWasmI32},
|
||||
{WASM_TRY_CATCH_ALL_T(kWasmI32, WASM_STMTS(WASM_I32V(0), WASM_THROW(ex)),
|
||||
@ -462,7 +462,7 @@ TEST(Liftoff_debug_side_table_catch_all) {
|
||||
TEST(Regress1199526) {
|
||||
EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
LiftoffCompileEnvironment env;
|
||||
ValueType exception_type = ValueType::Ref(HeapType::kAny, kNonNullable);
|
||||
ValueType exception_type = ValueType::Ref(HeapType::kAny);
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{}, {},
|
||||
{kExprTry, kVoidCode, kExprCallFunction, 0, kExprCatchAll, kExprLoop,
|
||||
|
@ -3673,7 +3673,7 @@ WASM_EXEC_TEST(IndirectNullTyped) {
|
||||
FunctionSig sig(1, 0, &kWasmI32);
|
||||
byte sig_index = r.builder().AddSignature(&sig);
|
||||
r.builder().AddIndirectFunctionTable(nullptr, 1,
|
||||
ValueType::Ref(sig_index, kNullable));
|
||||
ValueType::RefNull(sig_index));
|
||||
|
||||
BUILD(r, WASM_CALL_INDIRECT(sig_index, WASM_I32V(0)));
|
||||
|
||||
|
@ -3520,7 +3520,7 @@ class WasmInterpreterInternals {
|
||||
WasmFeatures::All(), &decoder, code->at(pc + 1), module());
|
||||
len = 1 + imm.length;
|
||||
Push(WasmValue(isolate_->factory()->null_value(),
|
||||
ValueType::Ref(imm.type, kNullable)));
|
||||
ValueType::RefNull(imm.type)));
|
||||
break;
|
||||
}
|
||||
case kExprRefFunc: {
|
||||
|
@ -144,15 +144,17 @@ ValueType GetValueTypeHelper(DataRange* data, bool liftoff_as_reference,
|
||||
// Conceptually, user-defined types are added to the end of the list. Pick a
|
||||
// random one among them.
|
||||
uint32_t id = data->get<uint8_t>() % (types.size() + num_user_defined_types);
|
||||
|
||||
Nullability nullability = nullable ? kNullable : kNonNullable;
|
||||
|
||||
if (id >= types.size()) {
|
||||
// Return user-defined type.
|
||||
return ValueType::Ref(id - static_cast<uint32_t>(types.size()),
|
||||
nullable ? kNullable : kNonNullable);
|
||||
return ValueType::RefMaybeNull(id - static_cast<uint32_t>(types.size()),
|
||||
nullability);
|
||||
}
|
||||
// If returning a reference type, fix its nullability according to {nullable}.
|
||||
if (types[id].is_reference()) {
|
||||
return ValueType::Ref(types[id].heap_type(),
|
||||
nullable ? kNullable : kNonNullable);
|
||||
return ValueType::RefMaybeNull(types[id].heap_type(), nullability);
|
||||
}
|
||||
// Otherwise, just return the picked type.
|
||||
return types[id];
|
||||
@ -934,7 +936,7 @@ class WasmGenerator {
|
||||
}
|
||||
|
||||
bool table_get(HeapType type, DataRange* data, Nullability nullable) {
|
||||
ValueType needed_type = ValueType::Ref(type, nullable);
|
||||
ValueType needed_type = ValueType::RefMaybeNull(type, nullable);
|
||||
int table_count = builder_->builder()->NumTables();
|
||||
ZoneVector<uint32_t> table(builder_->builder()->zone());
|
||||
for (int i = 0; i < table_count; i++) {
|
||||
@ -1024,7 +1026,7 @@ class WasmGenerator {
|
||||
}
|
||||
}
|
||||
bool array_get_ref(HeapType type, DataRange* data, Nullability nullable) {
|
||||
ValueType needed_type = ValueType::Ref(type, nullable);
|
||||
ValueType needed_type = ValueType::RefMaybeNull(type, nullable);
|
||||
return array_get_helper(needed_type, data);
|
||||
}
|
||||
|
||||
@ -1120,7 +1122,7 @@ class WasmGenerator {
|
||||
}
|
||||
|
||||
bool struct_get_ref(HeapType type, DataRange* data, Nullability nullable) {
|
||||
ValueType needed_type = ValueType::Ref(type, nullable);
|
||||
ValueType needed_type = ValueType::RefMaybeNull(type, nullable);
|
||||
return struct_get_helper(needed_type, data);
|
||||
}
|
||||
|
||||
|
@ -1117,8 +1117,8 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
|
||||
byte struct_index = builder.AddStruct({F(kWasmI32, true), F(kWasmI64, true)});
|
||||
byte array_index = builder.AddArray(kWasmI32, true);
|
||||
|
||||
ValueType struct_type = ValueType::Ref(struct_index, kNonNullable);
|
||||
ValueType struct_type_null = ValueType::Ref(struct_index, kNullable);
|
||||
ValueType struct_type = ValueType::Ref(struct_index);
|
||||
ValueType struct_type_null = ValueType::RefNull(struct_index);
|
||||
FunctionSig sig_v_s(0, 1, &struct_type);
|
||||
byte struct_consumer = builder.AddFunction(&sig_v_s);
|
||||
byte struct_consumer2 = builder.AddFunction(
|
||||
@ -1936,7 +1936,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectCallsWithMismatchedSigs2) {
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
byte table_type_index = builder.AddSignature(sigs.i_i());
|
||||
byte table_index =
|
||||
builder.InitializeTable(ValueType::Ref(table_type_index, kNullable));
|
||||
builder.InitializeTable(ValueType::RefNull(table_type_index));
|
||||
|
||||
ExpectValidates(sigs.i_v(),
|
||||
{WASM_CALL_INDIRECT_TABLE(table_index, table_type_index,
|
||||
@ -1970,25 +1970,25 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) {
|
||||
builder.AddStruct({F(kWasmI32, false), F(kWasmF64, false)}, super_struct);
|
||||
|
||||
byte table_supertype = builder.AddSignature(
|
||||
FunctionSig::Build(zone(), {ValueType::Ref(empty_struct, kNullable)},
|
||||
{ValueType::Ref(sub_struct, kNullable)}));
|
||||
FunctionSig::Build(zone(), {ValueType::RefNull(empty_struct)},
|
||||
{ValueType::RefNull(sub_struct)}));
|
||||
byte table_type = builder.AddSignature(
|
||||
FunctionSig::Build(zone(), {ValueType::Ref(super_struct, kNullable)},
|
||||
{ValueType::Ref(sub_struct, kNullable)}),
|
||||
FunctionSig::Build(zone(), {ValueType::RefNull(super_struct)},
|
||||
{ValueType::RefNull(sub_struct)}),
|
||||
table_supertype);
|
||||
auto function_sig =
|
||||
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)},
|
||||
{ValueType::Ref(super_struct, kNullable)});
|
||||
FunctionSig::Build(zone(), {ValueType::RefNull(sub_struct)},
|
||||
{ValueType::RefNull(super_struct)});
|
||||
byte function_type = builder.AddSignature(function_sig, table_type);
|
||||
|
||||
byte function = builder.AddFunction(function_type);
|
||||
|
||||
byte table = builder.InitializeTable(ValueType::Ref(table_type, kNullable));
|
||||
byte table = builder.InitializeTable(ValueType::RefNull(table_type));
|
||||
|
||||
// We can call-indirect from a typed function table with an immediate type
|
||||
// that is a subtype of the table type.
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)}, {}),
|
||||
FunctionSig::Build(zone(), {ValueType::RefNull(sub_struct)}, {}),
|
||||
{WASM_CALL_INDIRECT_TABLE(table, function_type,
|
||||
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
super_struct, WASM_RTT_CANON(super_struct)),
|
||||
@ -1999,7 +1999,7 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) {
|
||||
WASM_REF_FUNC(function))});
|
||||
// table.get's subtyping works as expected.
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(zone(), {ValueType::Ref(table_supertype, kNullable)},
|
||||
FunctionSig::Build(zone(), {ValueType::RefNull(table_supertype)},
|
||||
{kWasmI32}),
|
||||
{WASM_TABLE_GET(0, WASM_LOCAL_GET(0))});
|
||||
}
|
||||
@ -2189,10 +2189,10 @@ TEST_F(FunctionBodyDecoderTest, TableSet) {
|
||||
byte tab_func2 = builder.AddTable(kWasmFuncRef, 10, false, 20);
|
||||
byte tab_ref2 = builder.AddTable(kWasmAnyRef, 10, false, 20);
|
||||
byte tab_typed_func =
|
||||
builder.AddTable(ValueType::Ref(tab_type, kNullable), 10, false, 20);
|
||||
builder.AddTable(ValueType::RefNull(tab_type), 10, false, 20);
|
||||
|
||||
ValueType sig_types[]{kWasmAnyRef, kWasmFuncRef, kWasmI32,
|
||||
ValueType::Ref(tab_type, kNonNullable)};
|
||||
ValueType::Ref(tab_type)};
|
||||
FunctionSig sig(0, 4, sig_types);
|
||||
byte local_ref = 0;
|
||||
byte local_func = 1;
|
||||
@ -2245,10 +2245,10 @@ TEST_F(FunctionBodyDecoderTest, TableGet) {
|
||||
byte tab_func2 = builder.AddTable(kWasmFuncRef, 10, false, 20);
|
||||
byte tab_ref2 = builder.AddTable(kWasmAnyRef, 10, false, 20);
|
||||
byte tab_typed_func =
|
||||
builder.AddTable(ValueType::Ref(tab_type, kNullable), 10, false, 20);
|
||||
builder.AddTable(ValueType::RefNull(tab_type), 10, false, 20);
|
||||
|
||||
ValueType sig_types[]{kWasmAnyRef, kWasmFuncRef, kWasmI32,
|
||||
ValueType::Ref(tab_type, kNullable)};
|
||||
ValueType::RefNull(tab_type)};
|
||||
FunctionSig sig(0, 4, sig_types);
|
||||
byte local_ref = 0;
|
||||
byte local_func = 1;
|
||||
@ -2693,8 +2693,8 @@ TEST_F(FunctionBodyDecoderTest, BrTableSubtyping) {
|
||||
{F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)}, supertype2);
|
||||
ExpectValidates(
|
||||
sigs.v_v(),
|
||||
{WASM_BLOCK_R(wasm::ValueType::Ref(supertype1, kNonNullable),
|
||||
WASM_BLOCK_R(wasm::ValueType::Ref(supertype2, kNonNullable),
|
||||
{WASM_BLOCK_R(wasm::ValueType::Ref(supertype1),
|
||||
WASM_BLOCK_R(wasm::ValueType::Ref(supertype2),
|
||||
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
||||
subtype, WASM_RTT_CANON(subtype)),
|
||||
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0),
|
||||
@ -3604,12 +3604,8 @@ TEST_F(FunctionBodyDecoderTest, UnpackPackedTypes) {
|
||||
}
|
||||
}
|
||||
|
||||
ValueType ref(byte type_index) {
|
||||
return ValueType::Ref(type_index, kNonNullable);
|
||||
}
|
||||
ValueType refNull(byte type_index) {
|
||||
return ValueType::Ref(type_index, kNullable);
|
||||
}
|
||||
ValueType ref(byte type_index) { return ValueType::Ref(type_index); }
|
||||
ValueType refNull(byte type_index) { return ValueType::RefNull(type_index); }
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, StructNewDefaultWithRtt) {
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
@ -3651,7 +3647,7 @@ TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, NonDefaultableLocal) {
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
AddLocals(ValueType::Ref(HeapType::kAny, kNonNullable), 1);
|
||||
AddLocals(ValueType::Ref(HeapType::kAny), 1);
|
||||
ExpectFailure(sigs.v_v(), {}, kAppendEnd,
|
||||
"Cannot define function-level local of non-defaultable type");
|
||||
}
|
||||
@ -3730,20 +3726,19 @@ TEST_F(FunctionBodyDecoderTest, RefEq) {
|
||||
byte struct_type_index = builder.AddStruct({F(kWasmI32, true)});
|
||||
ValueType eqref_subtypes[] = {kWasmEqRef,
|
||||
kWasmI31Ref,
|
||||
ValueType::Ref(HeapType::kEq, kNonNullable),
|
||||
ValueType::Ref(HeapType::kI31, kNullable),
|
||||
ValueType::Ref(HeapType::kEq),
|
||||
ValueType::RefNull(HeapType::kI31),
|
||||
ref(struct_type_index),
|
||||
refNull(struct_type_index)};
|
||||
ValueType non_eqref_subtypes[] = {
|
||||
kWasmI32,
|
||||
kWasmI64,
|
||||
kWasmF32,
|
||||
kWasmF64,
|
||||
kWasmS128,
|
||||
kWasmFuncRef,
|
||||
kWasmAnyRef,
|
||||
ValueType::Ref(HeapType::kAny, kNonNullable),
|
||||
ValueType::Ref(HeapType::kFunc, kNonNullable)};
|
||||
ValueType non_eqref_subtypes[] = {kWasmI32,
|
||||
kWasmI64,
|
||||
kWasmF32,
|
||||
kWasmF64,
|
||||
kWasmS128,
|
||||
kWasmFuncRef,
|
||||
kWasmAnyRef,
|
||||
ValueType::Ref(HeapType::kAny),
|
||||
ValueType::Ref(HeapType::kFunc)};
|
||||
|
||||
for (ValueType type1 : eqref_subtypes) {
|
||||
for (ValueType type2 : eqref_subtypes) {
|
||||
@ -3782,16 +3777,15 @@ TEST_F(FunctionBodyDecoderTest, RefAsNonNull) {
|
||||
|
||||
// It works with nullable types.
|
||||
for (uint32_t heap_type : heap_types) {
|
||||
ValueType reprs[] = {ValueType::Ref(heap_type, kNonNullable),
|
||||
ValueType::Ref(heap_type, kNullable)};
|
||||
ValueType reprs[] = {ValueType::Ref(heap_type),
|
||||
ValueType::RefNull(heap_type)};
|
||||
FunctionSig sig(1, 1, reprs);
|
||||
ExpectValidates(&sig, {WASM_REF_AS_NON_NULL(WASM_LOCAL_GET(0))});
|
||||
}
|
||||
|
||||
// It works with non-nullable types.
|
||||
for (uint32_t heap_type : heap_types) {
|
||||
ValueType reprs[] = {ValueType::Ref(heap_type, kNonNullable),
|
||||
ValueType::Ref(heap_type, kNonNullable)};
|
||||
ValueType reprs[] = {ValueType::Ref(heap_type), ValueType::Ref(heap_type)};
|
||||
FunctionSig sig(1, 1, reprs);
|
||||
ExpectValidates(&sig, {WASM_REF_AS_NON_NULL(WASM_LOCAL_GET(0))});
|
||||
}
|
||||
@ -3816,7 +3810,7 @@ TEST_F(FunctionBodyDecoderTest, RefNull) {
|
||||
HeapType::kEq, HeapType::kAny, HeapType::kI31};
|
||||
// It works with heap types.
|
||||
for (uint32_t type_repr : type_reprs) {
|
||||
const ValueType type = ValueType::Ref(type_repr, kNullable);
|
||||
const ValueType type = ValueType::RefNull(type_repr);
|
||||
const FunctionSig sig(1, 0, &type);
|
||||
ExpectValidates(&sig, {WASM_REF_NULL(WASM_HEAP_TYPE(HeapType(type_repr)))});
|
||||
}
|
||||
@ -3841,7 +3835,7 @@ TEST_F(FunctionBodyDecoderTest, RefIsNull) {
|
||||
HeapType::kEq, HeapType::kAny, HeapType::kI31};
|
||||
|
||||
for (uint32_t heap_type : heap_types) {
|
||||
const ValueType types[] = {kWasmI32, ValueType::Ref(heap_type, kNullable)};
|
||||
const ValueType types[] = {kWasmI32, ValueType::RefNull(heap_type)};
|
||||
const FunctionSig sig(1, 1, types);
|
||||
// It works for nullable references.
|
||||
ExpectValidates(&sig, {WASM_REF_IS_NULL(WASM_LOCAL_GET(0))});
|
||||
@ -3860,8 +3854,8 @@ TEST_F(FunctionBodyDecoderTest, BrOnNull) {
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
|
||||
const ValueType reps[] = {ValueType::Ref(HeapType::kFunc, kNonNullable),
|
||||
ValueType::Ref(HeapType::kFunc, kNullable)};
|
||||
const ValueType reps[] = {ValueType::Ref(HeapType::kFunc),
|
||||
ValueType::RefNull(HeapType::kFunc)};
|
||||
const FunctionSig sig(1, 1, reps);
|
||||
ExpectValidates(
|
||||
&sig, {WASM_BLOCK_R(reps[0], WASM_REF_AS_NON_NULL(WASM_LOCAL_GET(0)),
|
||||
@ -3882,8 +3876,8 @@ TEST_F(FunctionBodyDecoderTest, BrOnNonNull) {
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
FLAG_SCOPE(experimental_wasm_gc);
|
||||
|
||||
const ValueType reps[] = {ValueType::Ref(HeapType::kFunc, kNonNullable),
|
||||
ValueType::Ref(HeapType::kFunc, kNullable)};
|
||||
const ValueType reps[] = {ValueType::Ref(HeapType::kFunc),
|
||||
ValueType::RefNull(HeapType::kFunc)};
|
||||
const FunctionSig sig(1, 1, reps);
|
||||
ExpectValidates(
|
||||
&sig,
|
||||
@ -3911,7 +3905,7 @@ TEST_F(FunctionBodyDecoderTest, GCStruct) {
|
||||
byte immutable_struct_type_index = builder.AddStruct({F(kWasmI32, false)});
|
||||
byte field_index = 0;
|
||||
|
||||
ValueType struct_type = ValueType::Ref(struct_type_index, kNonNullable);
|
||||
ValueType struct_type = ValueType::Ref(struct_type_index);
|
||||
ValueType reps_i_r[] = {kWasmI32, struct_type};
|
||||
ValueType reps_f_r[] = {kWasmF32, struct_type};
|
||||
const FunctionSig sig_i_r(1, 1, reps_i_r);
|
||||
@ -4042,15 +4036,13 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
|
||||
byte struct_type_index = builder.AddStruct({F(kWasmI32, false)});
|
||||
byte immutable_array_type_index = builder.AddArray(kWasmI32, false);
|
||||
|
||||
ValueType array_type = ValueType::Ref(array_type_index, kNonNullable);
|
||||
ValueType immutable_array_type =
|
||||
ValueType::Ref(immutable_array_type_index, kNonNullable);
|
||||
ValueType array_type = ValueType::Ref(array_type_index);
|
||||
ValueType immutable_array_type = ValueType::Ref(immutable_array_type_index);
|
||||
ValueType reps_c_r[] = {kWasmFuncRef, array_type};
|
||||
ValueType reps_f_r[] = {kWasmF32, array_type};
|
||||
ValueType reps_i_r[] = {kWasmI32, array_type};
|
||||
ValueType reps_i_a[] = {kWasmI32, kWasmArrayRef};
|
||||
ValueType reps_i_s[] = {kWasmI32,
|
||||
ValueType::Ref(struct_type_index, kNonNullable)};
|
||||
ValueType reps_i_s[] = {kWasmI32, ValueType::Ref(struct_type_index)};
|
||||
const FunctionSig sig_c_r(1, 1, reps_c_r);
|
||||
const FunctionSig sig_v_r(0, 1, &array_type);
|
||||
const FunctionSig sig_v_r2(0, 1, &immutable_array_type);
|
||||
@ -4339,15 +4331,15 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
|
||||
HeapType to_heap = HeapType(std::get<1>(test));
|
||||
bool should_pass = std::get<2>(test);
|
||||
|
||||
ValueType test_reps[] = {kWasmI32, ValueType::Ref(from_heap, kNullable)};
|
||||
ValueType test_reps[] = {kWasmI32, ValueType::RefNull(from_heap)};
|
||||
FunctionSig test_sig(1, 1, test_reps);
|
||||
|
||||
ValueType cast_reps_with_depth[] = {ValueType::Ref(to_heap, kNullable),
|
||||
ValueType::Ref(from_heap, kNullable)};
|
||||
ValueType cast_reps_with_depth[] = {ValueType::RefNull(to_heap),
|
||||
ValueType::RefNull(from_heap)};
|
||||
FunctionSig cast_sig_with_depth(1, 1, cast_reps_with_depth);
|
||||
|
||||
ValueType cast_reps[] = {ValueType::Ref(to_heap, kNullable),
|
||||
ValueType::Ref(from_heap, kNullable),
|
||||
ValueType cast_reps[] = {ValueType::RefNull(to_heap),
|
||||
ValueType::RefNull(from_heap),
|
||||
ValueType::Rtt(to_heap.ref_index())};
|
||||
FunctionSig cast_sig(1, 2, cast_reps);
|
||||
|
||||
@ -4403,8 +4395,8 @@ TEST_F(FunctionBodyDecoderTest, BrOnCastOrCastFail) {
|
||||
byte sub_struct =
|
||||
builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)}, super_struct);
|
||||
|
||||
ValueType supertype = ValueType::Ref(super_struct, kNullable);
|
||||
ValueType subtype = ValueType::Ref(sub_struct, kNullable);
|
||||
ValueType supertype = ValueType::RefNull(super_struct);
|
||||
ValueType subtype = ValueType::RefNull(sub_struct);
|
||||
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(this->zone(), {kWasmI32, subtype}, {supertype}),
|
||||
@ -4468,7 +4460,7 @@ TEST_F(FunctionBodyDecoderTest, BrOnAbstractType) {
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
FLAG_SCOPE(experimental_wasm_gc);
|
||||
|
||||
ValueType kNonNullableFunc = ValueType::Ref(HeapType::kFunc, kNonNullable);
|
||||
ValueType kNonNullableFunc = ValueType::Ref(HeapType::kFunc);
|
||||
|
||||
ExpectValidates(
|
||||
FunctionSig::Build(this->zone(), {kNonNullableFunc}, {kWasmAnyRef}),
|
||||
@ -4526,10 +4518,10 @@ TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
|
||||
|
||||
byte array_type = builder.AddArray(kWasmI8, true);
|
||||
|
||||
ValueType types[] = {ValueType::Ref(array_type, kNonNullable)};
|
||||
ValueType types[] = {ValueType::Ref(array_type)};
|
||||
FunctionSig sig(1, 0, types);
|
||||
|
||||
AddLocals(ValueType::Ref(array_type, kNullable), 1);
|
||||
AddLocals(ValueType::RefNull(array_type), 1);
|
||||
|
||||
ExpectFailure(&sig,
|
||||
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
|
||||
@ -5107,7 +5099,7 @@ TEST_F(LocalDeclDecoderTest, InvalidTypeIndex) {
|
||||
const byte* end = nullptr;
|
||||
LocalDeclEncoder local_decls(zone());
|
||||
|
||||
local_decls.AddLocals(1, ValueType::Ref(0, kNullable));
|
||||
local_decls.AddLocals(1, ValueType::RefNull(0));
|
||||
BodyLocalDecls decls(zone());
|
||||
bool result = DecodeLocalDecls(&decls, data, end);
|
||||
EXPECT_FALSE(result);
|
||||
|
@ -2210,7 +2210,7 @@ TEST_F(WasmModuleVerifyTest, TypedFunctionTable) {
|
||||
|
||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||
EXPECT_OK(result);
|
||||
EXPECT_EQ(ValueType::Ref(0, kNullable), result.value()->tables[0].type);
|
||||
EXPECT_EQ(ValueType::RefNull(0), result.value()->tables[0].type);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, NullableTableIllegalInitializer) {
|
||||
@ -2277,7 +2277,7 @@ TEST_F(WasmModuleVerifyTest, NonNullableTable) {
|
||||
SECTION(Code, ENTRY_COUNT(1), NOP_BODY)};
|
||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||
EXPECT_OK(result);
|
||||
EXPECT_EQ(ValueType::Ref(0, kNonNullable), result.value()->tables[0].type);
|
||||
EXPECT_EQ(ValueType::Ref(0), result.value()->tables[0].type);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, NonNullableTableNoInitializer) {
|
||||
|
@ -16,11 +16,9 @@ namespace subtyping_unittest {
|
||||
class WasmSubtypingTest : public TestWithPlatform {};
|
||||
using FieldInit = std::pair<ValueType, bool>;
|
||||
|
||||
constexpr ValueType ref(uint32_t index) {
|
||||
return ValueType::Ref(index, kNonNullable);
|
||||
}
|
||||
constexpr ValueType ref(uint32_t index) { return ValueType::Ref(index); }
|
||||
constexpr ValueType refNull(uint32_t index) {
|
||||
return ValueType::Ref(index, kNullable);
|
||||
return ValueType::RefNull(index);
|
||||
}
|
||||
|
||||
FieldInit mut(ValueType type) { return FieldInit(type, true); }
|
||||
@ -153,14 +151,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
#define NOT_VALID_SUBTYPE(type1, type2) \
|
||||
EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
|
||||
module1, module));
|
||||
#define IDENTICAL(index1, index2) \
|
||||
EXPECT_TRUE(EquivalentTypes(ValueType::Ref(index1, kNullable), \
|
||||
ValueType::Ref(index2, kNullable), module1, \
|
||||
module));
|
||||
#define DISTINCT(index1, index2) \
|
||||
EXPECT_FALSE(EquivalentTypes(ValueType::Ref(index1, kNullable), \
|
||||
ValueType::Ref(index2, kNullable), module1, \
|
||||
module));
|
||||
#define IDENTICAL(index1, index2) \
|
||||
EXPECT_TRUE(EquivalentTypes(ValueType::RefNull(index1), \
|
||||
ValueType::RefNull(index2), module1, module));
|
||||
#define DISTINCT(index1, index2) \
|
||||
EXPECT_FALSE(EquivalentTypes(ValueType::RefNull(index1), \
|
||||
ValueType::RefNull(index2), module1, module));
|
||||
// Union always expresses the result in terms of module1.
|
||||
#define UNION(type1, type2, type_result) \
|
||||
EXPECT_EQ(Union(type1, type2, module1, module), \
|
||||
|
Loading…
Reference in New Issue
Block a user