Commit 5df74c35 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm] Properly implement parsing of s33 values

Motivation:
We used to approximate s33/i33 value parsing by first checking for
specific negative codes, and then parsing an u32 value if that failed.
This is not correct in all cases.

Changes:
- Implement i33 parsing in Decoder.
- Factor out parsing of heap types into read_heap_type.
- Introduce HeapType::kBottom.
- Introduce helper functions in WasmFeatures and value_type_reader.
- Remove macros from the parsing of value types.
- HeapType::code now returns an i32 for compatibility with the i33
  requirement.
- Introduce HeapType::Repr.
- Renamings: HeapType::type() -> representation(),
             ValueType::heap() -> heap_representation()

Bug: v8:7748
Change-Id: I04deabce8837a48af2226411cd706a397f9e5725
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2274118
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68633}
parent e76072bf
...@@ -5377,7 +5377,8 @@ Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) { ...@@ -5377,7 +5377,8 @@ Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) {
} }
return CALL_BUILTIN( return CALL_BUILTIN(
WasmAllocateRtt, WasmAllocateRtt,
graph()->NewNode(mcgraph()->common()->NumberConstant(type.type())), graph()->NewNode(
mcgraph()->common()->NumberConstant(type.representation())),
parent_rtt, parent_rtt,
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer())); LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
} }
...@@ -5780,7 +5781,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -5780,7 +5781,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
switch (type.kind()) { switch (type.kind()) {
case wasm::ValueType::kRef: case wasm::ValueType::kRef:
case wasm::ValueType::kOptRef: { case wasm::ValueType::kOptRef: {
switch (type.heap()) { switch (type.heap_representation()) {
case wasm::HeapType::kExtern: case wasm::HeapType::kExtern:
case wasm::HeapType::kExn: case wasm::HeapType::kExn:
return input; return input;
......
...@@ -73,7 +73,7 @@ ValKind V8ValueTypeToWasm(i::wasm::ValueType v8_valtype) { ...@@ -73,7 +73,7 @@ ValKind V8ValueTypeToWasm(i::wasm::ValueType v8_valtype) {
return F64; return F64;
case i::wasm::ValueType::kRef: case i::wasm::ValueType::kRef:
case i::wasm::ValueType::kOptRef: case i::wasm::ValueType::kOptRef:
switch (v8_valtype.heap()) { switch (v8_valtype.heap_representation()) {
case i::wasm::HeapType::kFunc: case i::wasm::HeapType::kFunc:
return FUNCREF; return FUNCREF;
case i::wasm::HeapType::kExtern: case i::wasm::HeapType::kExtern:
...@@ -1828,7 +1828,7 @@ auto Table::type() const -> own<TableType> { ...@@ -1828,7 +1828,7 @@ auto Table::type() const -> own<TableType> {
uint32_t max; uint32_t max;
if (!table->maximum_length().ToUint32(&max)) max = 0xFFFFFFFFu; if (!table->maximum_length().ToUint32(&max)) max = 0xFFFFFFFFu;
ValKind kind; ValKind kind;
switch (table->type().heap()) { switch (table->type().heap_representation()) {
case i::wasm::HeapType::kFunc: case i::wasm::HeapType::kFunc:
kind = FUNCREF; kind = FUNCREF;
break; break;
......
...@@ -127,6 +127,14 @@ class Decoder { ...@@ -127,6 +127,14 @@ class Decoder {
name); name);
} }
// Reads a variable-length 33-bit signed integer (little endian).
template <ValidateFlag validate>
int64_t read_i33v(const byte* pc, uint32_t* length,
const char* name = "signed LEB33") {
return read_leb<int64_t, validate, kNoAdvancePc, kNoTrace, 33>(pc, length,
name);
}
// Reads a prefixed-opcode, possibly with variable-length index. // Reads a prefixed-opcode, possibly with variable-length index.
// The length param is set to the number of bytes this index is encoded with. // The length param is set to the number of bytes this index is encoded with.
// For most cases (non variable-length), it will be 1. // For most cases (non variable-length), it will be 1.
...@@ -354,21 +362,23 @@ class Decoder { ...@@ -354,21 +362,23 @@ class Decoder {
} }
template <typename IntType, ValidateFlag validate, AdvancePCFlag advance_pc, template <typename IntType, ValidateFlag validate, AdvancePCFlag advance_pc,
TraceFlag trace> TraceFlag trace, size_t size_in_bits = 8 * sizeof(IntType)>
inline IntType read_leb(const byte* pc, uint32_t* length, inline IntType read_leb(const byte* pc, uint32_t* length,
const char* name = "varint") { const char* name = "varint") {
DCHECK_IMPLIES(advance_pc, pc == pc_); DCHECK_IMPLIES(advance_pc, pc == pc_);
static_assert(size_in_bits <= 8 * sizeof(IntType),
"leb does not fit in type");
TRACE_IF(trace, " +%u %-20s: ", pc_offset(), name); TRACE_IF(trace, " +%u %-20s: ", pc_offset(), name);
return read_leb_tail<IntType, validate, advance_pc, trace, 0>(pc, length, return read_leb_tail<IntType, validate, advance_pc, trace, size_in_bits, 0>(
name, 0); pc, length, name, 0);
} }
template <typename IntType, ValidateFlag validate, AdvancePCFlag advance_pc, template <typename IntType, ValidateFlag validate, AdvancePCFlag advance_pc,
TraceFlag trace, int byte_index> TraceFlag trace, size_t size_in_bits, int byte_index>
IntType read_leb_tail(const byte* pc, uint32_t* length, const char* name, IntType read_leb_tail(const byte* pc, uint32_t* length, const char* name,
IntType result) { IntType result) {
constexpr bool is_signed = std::is_signed<IntType>::value; constexpr bool is_signed = std::is_signed<IntType>::value;
constexpr int kMaxLength = (sizeof(IntType) * 8 + 6) / 7; constexpr int kMaxLength = (size_in_bits + 6) / 7;
static_assert(byte_index < kMaxLength, "invalid template instantiation"); static_assert(byte_index < kMaxLength, "invalid template instantiation");
constexpr int shift = byte_index * 7; constexpr int shift = byte_index * 7;
constexpr bool is_last_byte = byte_index == kMaxLength - 1; constexpr bool is_last_byte = byte_index == kMaxLength - 1;
...@@ -387,7 +397,7 @@ class Decoder { ...@@ -387,7 +397,7 @@ class Decoder {
// Compilers are not smart enough to figure out statically that the // Compilers are not smart enough to figure out statically that the
// following call is unreachable if is_last_byte is false. // following call is unreachable if is_last_byte is false.
constexpr int next_byte_index = byte_index + (is_last_byte ? 0 : 1); constexpr int next_byte_index = byte_index + (is_last_byte ? 0 : 1);
return read_leb_tail<IntType, validate, advance_pc, trace, return read_leb_tail<IntType, validate, advance_pc, trace, size_in_bits,
next_byte_index>(pc + 1, length, name, result); next_byte_index>(pc + 1, length, name, result);
} }
if (advance_pc) pc_ = pc + (at_end ? 0 : 1); if (advance_pc) pc_ = pc + (at_end ? 0 : 1);
...@@ -404,11 +414,11 @@ class Decoder { ...@@ -404,11 +414,11 @@ class Decoder {
// For unsigned values, the extra bits must be all zero. // For unsigned values, the extra bits must be all zero.
// For signed values, the extra bits *plus* the most significant bit must // For signed values, the extra bits *plus* the most significant bit must
// either be 0, or all ones. // either be 0, or all ones.
constexpr int kExtraBits = (sizeof(IntType) * 8) - ((kMaxLength - 1) * 7); constexpr int kExtraBits = size_in_bits - ((kMaxLength - 1) * 7);
constexpr int kSignExtBits = kExtraBits - (is_signed ? 1 : 0); constexpr int kSignExtBits = kExtraBits - (is_signed ? 1 : 0);
const byte checked_bits = b & (0xFF << kSignExtBits); const byte checked_bits = b & (0xFF << kSignExtBits);
constexpr byte kSignExtendedExtraBits = 0x7f & (0xFF << kSignExtBits); constexpr byte kSignExtendedExtraBits = 0x7f & (0xFF << kSignExtBits);
bool valid_extra_bits = const bool valid_extra_bits =
checked_bits == 0 || checked_bits == 0 ||
(is_signed && checked_bits == kSignExtendedExtraBits); (is_signed && checked_bits == kSignExtendedExtraBits);
if (!validate) { if (!validate) {
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
// Do only include this header for implementing new Interface of the // Do only include this header for implementing new Interface of the
// WasmFullDecoder. // WasmFullDecoder.
#include <inttypes.h>
#include "src/base/platform/elapsed-timer.h" #include "src/base/platform/elapsed-timer.h"
#include "src/base/small-vector.h" #include "src/base/small-vector.h"
#include "src/utils/bit-vector.h" #include "src/utils/bit-vector.h"
...@@ -127,6 +129,69 @@ struct WasmException; ...@@ -127,6 +129,69 @@ struct WasmException;
namespace value_type_reader { namespace value_type_reader {
V8_INLINE WasmFeature feature_for_heap_type(HeapType heap_type) {
switch (heap_type.representation()) {
case HeapType::kFunc:
case HeapType::kExtern:
return WasmFeature::kFeature_reftypes;
case HeapType::kExn:
return WasmFeature::kFeature_eh;
case HeapType::kEq:
case HeapType::kI31:
return WasmFeature::kFeature_gc;
default:
UNREACHABLE();
}
}
template <Decoder::ValidateFlag validate>
HeapType read_heap_type(Decoder* decoder, const byte* pc,
uint32_t* const length, const WasmFeatures& enabled) {
int64_t heap_index = decoder->read_i33v<validate>(pc, length, "heap type");
if (heap_index < 0) {
uint8_t uint_7_mask = 0x7F;
uint8_t code = static_cast<ValueTypeCode>(heap_index) & uint_7_mask;
switch (code) {
case kLocalFuncRef:
case kLocalExnRef:
case kLocalEqRef:
case kLocalExternRef:
case kLocalI31Ref: {
HeapType result = HeapType::from_code(code);
if (!VALIDATE(enabled.contains(feature_for_heap_type(result)))) {
decoder->errorf(
pc, "invalid heap type '%s', enable with --experimental-wasm-%s",
result.name().c_str(),
WasmFeatures::name_for_feature(feature_for_heap_type(result)));
return HeapType(HeapType::kBottom);
}
return result;
}
default:
if (validate)
decoder->errorf(pc, "Unknown heap type %" PRId64, heap_index);
return HeapType(HeapType::kBottom);
}
UNREACHABLE();
} else {
if (!VALIDATE(enabled.has_typed_funcref())) {
decoder->error(pc,
"Invalid indexed heap type, enable with "
"--experimental-wasm-typed-funcref");
return HeapType(HeapType::kBottom);
}
uint32_t type_index = static_cast<uint32_t>(heap_index);
if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
decoder->errorf(pc,
"Type index %u is greater than the maximum number %zu "
"of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
return HeapType(HeapType::kBottom);
}
return HeapType(type_index);
}
}
// Read a value type starting at address 'pc' in 'decoder'. // Read a value type starting at address 'pc' in 'decoder'.
// No bytes are consumed. The result is written into the 'result' parameter. // No bytes are consumed. The result is written into the 'result' parameter.
// Returns the amount of bytes read, or 0 if decoding failed. // Returns the amount of bytes read, or 0 if decoding failed.
...@@ -139,27 +204,24 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -139,27 +204,24 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
if (decoder->failed()) { if (decoder->failed()) {
return kWasmBottom; return kWasmBottom;
} }
ValueTypeCode code = static_cast<ValueTypeCode>(val); ValueTypeCode code = static_cast<ValueTypeCode>(val);
#define REF_TYPE_CASE(heap_type, nullable, feature) \
case kLocal##heap_type##Ref: { \
ValueType result = ValueType::Ref(HeapType::k##heap_type, nullable); \
if (!VALIDATE(enabled.has_##feature())) { \
decoder->errorf( \
pc, "invalid value type '%s', enable with --experimental-wasm-%s", \
result.type_name().c_str(), #feature); \
return kWasmBottom; \
} \
return result; \
}
switch (code) { switch (code) {
REF_TYPE_CASE(Func, kNullable, reftypes) case kLocalFuncRef:
REF_TYPE_CASE(Extern, kNullable, reftypes) case kLocalExnRef:
REF_TYPE_CASE(Eq, kNullable, gc) case kLocalEqRef:
REF_TYPE_CASE(Exn, kNullable, eh) case kLocalExternRef:
REF_TYPE_CASE(I31, kNonNullable, gc) case kLocalI31Ref: {
HeapType heap_type = HeapType::from_code(code);
ValueType result = ValueType::Ref(heap_type, kNullable);
if (!VALIDATE(enabled.contains(feature_for_heap_type(heap_type)))) {
decoder->errorf(
pc, "invalid value type '%s', enable with --experimental-wasm-%s",
result.type_name().c_str(),
WasmFeatures::name_for_feature(feature_for_heap_type(heap_type)));
return kWasmBottom;
}
return result;
}
case kLocalI32: case kLocalI32:
return kWasmI32; return kWasmI32;
case kLocalI64: case kLocalI64:
...@@ -170,40 +232,20 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -170,40 +232,20 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
return kWasmF64; return kWasmF64;
case kLocalRef: case kLocalRef:
case kLocalOptRef: { case kLocalOptRef: {
// Set length for the macro-defined cases:
*length += 1;
Nullability nullability = code == kLocalOptRef ? kNullable : kNonNullable; Nullability nullability = code == kLocalOptRef ? kNullable : kNonNullable;
uint8_t heap_index = decoder->read_u8<validate>(pc + 1, "heap type"); if (!VALIDATE(enabled.has_typed_funcref())) {
switch (static_cast<ValueTypeCode>(heap_index)) { decoder->errorf(pc,
REF_TYPE_CASE(Func, nullability, typed_funcref) "Invalid type 'ref%s', enable with "
REF_TYPE_CASE(Extern, nullability, typed_funcref) "--experimental-wasm-typed-funcref",
REF_TYPE_CASE(Eq, nullability, gc) nullability == kNullable ? " null" : "");
REF_TYPE_CASE(Exn, nullability, eh) return kWasmBottom;
REF_TYPE_CASE(I31, nullability, gc)
default:
uint32_t type_index =
decoder->read_u32v<validate>(pc + 1, length, "type index");
*length += 1;
if (!VALIDATE(enabled.has_gc())) {
decoder->error(
pc,
"invalid value type '(ref [null] (type $t))', enable with "
"--experimental-wasm-typed-gc");
return kWasmBottom;
}
if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
decoder->errorf(pc + 1,
"Type index %u is greater than the maximum "
"number %zu of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
return kWasmBottom;
}
return ValueType::Ref(type_index, nullability);
} }
UNREACHABLE(); HeapType heap_type =
read_heap_type<validate>(decoder, pc + 1, length, enabled);
*length += 1;
return heap_type.is_bottom() ? kWasmBottom
: ValueType::Ref(heap_type, nullability);
} }
#undef REF_TYPE_CASE
case kLocalRtt: { case kLocalRtt: {
if (!VALIDATE(enabled.has_gc())) { if (!VALIDATE(enabled.has_gc())) {
decoder->error( decoder->error(
...@@ -217,22 +259,21 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -217,22 +259,21 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
const uint32_t kMaxRttSubtypingDepth = 7; const uint32_t kMaxRttSubtypingDepth = 7;
if (!VALIDATE(depth <= kMaxRttSubtypingDepth)) { if (!VALIDATE(depth <= kMaxRttSubtypingDepth)) {
decoder->errorf(pc, decoder->errorf(pc,
"subtyping depth %u is greater than the maximum " "subtyping depth %u is greater than the maximum depth "
"depth %u supported by V8", "%u supported by V8",
depth, kMaxRttSubtypingDepth); depth, kMaxRttSubtypingDepth);
return kWasmBottom; return kWasmBottom;
} }
uint32_t type_index = decoder->read_u32v<validate>(pc + 1 + depth_length, HeapType heap_type = read_heap_type<validate>(
length, "type index"); decoder, pc + depth_length + 1, length, enabled);
if (!VALIDATE(type_index < kV8MaxWasmTypes)) { // TODO(7748): Support RTTs for generic types.
decoder->errorf(pc, if (heap_type.is_generic()) {
"Type index %u is greater than the maximum " decoder->error(pc, "UNIMPLEMENTED");
"number %zu of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
return kWasmBottom; return kWasmBottom;
} }
*length += 1 + depth_length; *length += depth_length + 1;
return ValueType::Rtt(type_index, static_cast<uint8_t>(depth)); return heap_type.is_bottom() ? kWasmBottom
: ValueType::Rtt(heap_type, depth);
} }
case kLocalS128: case kLocalS128:
if (!VALIDATE(enabled.has_simd())) { if (!VALIDATE(enabled.has_simd())) {
...@@ -364,30 +405,26 @@ struct BlockTypeImmediate { ...@@ -364,30 +405,26 @@ struct BlockTypeImmediate {
inline BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder, inline BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
const byte* pc) { const byte* pc) {
if (decoder->read_u8<validate>(pc, "block type") == kLocalVoid) { int64_t block_type =
// 1st case: void block. Struct fields stay at default values. decoder->read_i33v<validate>(pc, &length, "block type");
return; if (block_type < 0) {
} if ((static_cast<uint8_t>(block_type) & byte{0x7f}) == kLocalVoid) return;
type = value_type_reader::read_value_type<validate>(decoder, pc, &length, type = value_type_reader::read_value_type<validate>(decoder, pc, &length,
enabled); enabled);
if (type != kWasmBottom) { if (!VALIDATE(type != kWasmBottom)) {
// 2nd case: block with val type immediate. decoder->errorf(pc, "Invalid block type %" PRId64, block_type);
return; }
} } else {
// It has to be the 3rd case: multi-value block, if (!VALIDATE(enabled.has_mv())) {
// which is represented by a type index. decoder->errorf(pc,
if (!VALIDATE(enabled.has_mv())) { "invalid block type %" PRId64
decoder->error(pc, "invalid block type"); ", enable with --experimental-wasm-mv",
return; block_type);
} return;
if (!VALIDATE(decoder->ok())) return; }
int32_t index = type = kWasmBottom;
decoder->read_i32v<validate>(pc, &length, "block type index"); sig_index = static_cast<uint32_t>(block_type);
if (!VALIDATE(length > 0 && index >= 0)) {
decoder->error(pc, "invalid block type index");
return;
} }
sig_index = static_cast<uint32_t>(index);
} }
uint32_t in_arity() const { uint32_t in_arity() const {
...@@ -707,47 +744,11 @@ struct TableCopyImmediate { ...@@ -707,47 +744,11 @@ struct TableCopyImmediate {
template <Decoder::ValidateFlag validate> template <Decoder::ValidateFlag validate>
struct HeapTypeImmediate { struct HeapTypeImmediate {
uint32_t length = 1; uint32_t length = 1;
HeapType type = HeapType(HeapType::kFunc - 1); HeapType type = HeapType(HeapType::kBottom);
inline HeapTypeImmediate(const WasmFeatures& enabled, Decoder* decoder, inline HeapTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
const byte* pc) { const byte* pc) {
// Check for negative 1-byte value first, e.g. -0x10 == funcref. If there's type = value_type_reader::read_heap_type<validate>(decoder, pc, &length,
// no match, we'll read a uint32 from the same offset later. enabled);
uint8_t heap_index = decoder->read_u8<validate>(pc, "heap type immediate");
switch (static_cast<ValueTypeCode>(heap_index)) {
#define REF_TYPE_CASE(heap_type, feature) \
case kLocal##heap_type##Ref: { \
type = HeapType(HeapType::k##heap_type); \
if (!VALIDATE(enabled.has_##feature())) { \
decoder->errorf( \
pc, "invalid heap type '%s', enable with --experimental-wasm-%s", \
type.name().c_str(), #feature); \
} \
break; \
}
REF_TYPE_CASE(Func, reftypes)
REF_TYPE_CASE(Extern, reftypes)
REF_TYPE_CASE(Eq, gc)
REF_TYPE_CASE(Exn, eh)
#undef REF_TYPE_CASE
default:
uint32_t type_index =
decoder->read_u32v<validate>(pc, &length, "heap type immediate");
if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
decoder->errorf(pc,
"Type index %u is greater than the maximum number "
"%zu of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
break;
}
type = HeapType(type_index);
if (!VALIDATE(enabled.has_typed_funcref())) {
decoder->errorf(pc,
"invalid heap type %d, enable with "
"--experimental-wasm-typed-funcref",
type_index);
}
break;
}
} }
}; };
...@@ -3350,7 +3351,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3350,7 +3351,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
// TODO(7748): Drop this check if {imm} is dropped from the proposal // TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31. // à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.heap() == imm.index)) { if (!VALIDATE(rtt.type.heap_representation() == imm.index)) {
this->errorf(this->pc_ + len, this->errorf(this->pc_ + len,
"struct.new_with_rtt expected rtt for type %d, found " "struct.new_with_rtt expected rtt for type %d, found "
"rtt for type %s", "rtt for type %s",
......
...@@ -38,7 +38,7 @@ size_t LocalDeclEncoder::Emit(byte* buffer) const { ...@@ -38,7 +38,7 @@ size_t LocalDeclEncoder::Emit(byte* buffer) const {
++pos; ++pos;
} }
if (locals_type.encoding_needs_heap_type()) { if (locals_type.encoding_needs_heap_type()) {
LEBHelper::write_u32v(&pos, locals_type.heap_type().code()); LEBHelper::write_i32v(&pos, locals_type.heap_type().code());
} }
} }
DCHECK_EQ(Size(), pos - buffer); DCHECK_EQ(Size(), pos - buffer);
...@@ -66,7 +66,7 @@ size_t LocalDeclEncoder::Size() const { ...@@ -66,7 +66,7 @@ size_t LocalDeclEncoder::Size() const {
1 + // Opcode 1 + // Opcode
(p.second.has_depth() ? 1 : 0) + // Inheritance depth (p.second.has_depth() ? 1 : 0) + // Inheritance depth
(p.second.encoding_needs_heap_type() (p.second.encoding_needs_heap_type()
? LEBHelper::sizeof_u32v(p.second.heap_type().code()) ? LEBHelper::sizeof_i32v(p.second.heap_type().code())
: 0); // ref. index : 0); // ref. index
} }
return size; return size;
......
...@@ -45,39 +45,67 @@ class Simd128; ...@@ -45,39 +45,67 @@ class Simd128;
class HeapType { class HeapType {
public: public:
static const uint32_t kFunc = kV8MaxWasmTypes; // shorthand: c enum Representation : uint32_t {
static const uint32_t kExtern = kFunc + 1; // shorthand: e kFunc = kV8MaxWasmTypes, // shorthand: c
static const uint32_t kEq = kFunc + 2; // shorthand: q kExtern, // shorthand: e
static const uint32_t kExn = kFunc + 3; // shorthand: x kEq, // shorthand: q
static const uint32_t kI31 = kFunc + 4; // shorthand: j kExn, // shorthand: x
kI31, // shorthand: j
// This code is used to represent failures in the parsing of heap types and
// does not correspond to a wasm heap type.
kBottom
};
// Internal use only; defined in the public section to make it easy to // Internal use only; defined in the public section to make it easy to
// check that they are defined correctly: // check that they are defined correctly:
static const uint32_t kFirstSentinel = kFunc; static constexpr Representation kFirstSentinel = kFunc;
static const uint32_t kLastSentinel = kI31; static constexpr Representation kLastSentinel = kBottom;
static constexpr HeapType from_code(uint8_t code) {
switch (code) {
case ValueTypeCode::kLocalFuncRef:
return HeapType(kFunc);
case ValueTypeCode::kLocalExternRef:
return HeapType(kExtern);
case ValueTypeCode::kLocalEqRef:
return HeapType(kEq);
case ValueTypeCode::kLocalExnRef:
return HeapType(kExn);
case ValueTypeCode::kLocalI31Ref:
return HeapType(kI31);
default:
return HeapType(kBottom);
}
}
explicit constexpr HeapType(uint32_t type) : type_(type) { explicit constexpr HeapType(Representation repr) : representation_(repr) {
CONSTEXPR_DCHECK(is_valid()); CONSTEXPR_DCHECK(is_valid());
} }
explicit constexpr HeapType(uint32_t repr)
: HeapType(static_cast<Representation>(repr)) {}
constexpr bool operator==(HeapType other) const { constexpr bool operator==(HeapType other) const {
return type_ == other.type_; return representation_ == other.representation_;
} }
constexpr bool operator!=(HeapType other) const { constexpr bool operator!=(HeapType other) const {
return type_ != other.type_; return representation_ != other.representation_;
} }
constexpr uint32_t type() { return type_; } constexpr uint32_t representation() const { return representation_; }
constexpr uint32_t ref_index() { constexpr uint32_t ref_index() const {
CONSTEXPR_DCHECK(is_index()); CONSTEXPR_DCHECK(is_index());
return type_; return representation_;
}
constexpr bool is_generic() const {
return representation_ >= kFirstSentinel;
} }
constexpr bool is_generic() { return type_ >= kFirstSentinel; } constexpr bool is_index() const { return !is_generic(); }
constexpr bool is_index() { return !is_generic(); } constexpr bool is_bottom() const { return representation_ == kBottom; }
std::string name() { std::string name() const {
switch (type_) { switch (representation_) {
case kFunc: case kFunc:
return std::string("func"); return std::string("func");
case kExtern: case kExtern:
...@@ -89,31 +117,34 @@ class HeapType { ...@@ -89,31 +117,34 @@ class HeapType {
case kI31: case kI31:
return std::string("i31"); return std::string("i31");
default: default:
return std::to_string(type_); return std::to_string(representation_);
} }
} }
constexpr uint32_t code() { constexpr int32_t code() const {
switch (type_) { // kLocal* codes represent the first byte of the LEB128 encoding. To get the
// int32 represented by a code, we need to sign-extend it from 7 to 32 bits.
int32_t mask = 0xFFFFFF80;
switch (representation_) {
case kFunc: case kFunc:
return kLocalFuncRef; return mask | kLocalFuncRef;
case kExn: case kExn:
return kLocalExnRef; return mask | kLocalExnRef;
case kExtern: case kExtern:
return kLocalExternRef; return mask | kLocalExternRef;
case kEq: case kEq:
return kLocalEqRef; return mask | kLocalEqRef;
case kI31: case kI31:
return kLocalI31Ref; return mask | kLocalI31Ref;
default: default:
return type_; return static_cast<int32_t>(representation_);
} }
} }
private: private:
friend class ValueType; friend class ValueType;
uint32_t type_; Representation representation_;
constexpr bool is_valid() const { return type_ <= kLastSentinel; } constexpr bool is_valid() const { return representation_ <= kLastSentinel; }
}; };
enum Nullability : bool { kNonNullable, kNullable }; enum Nullability : bool { kNonNullable, kNullable };
...@@ -134,7 +165,8 @@ class ValueType { ...@@ -134,7 +165,8 @@ class ValueType {
constexpr bool is_nullable() const { return kind() == kOptRef; } constexpr bool is_nullable() const { return kind() == kOptRef; }
constexpr bool is_reference_to(uint32_t htype) const { constexpr bool is_reference_to(uint32_t htype) const {
return (kind() == kRef || kind() == kOptRef) && heap() == htype; return (kind() == kRef || kind() == kOptRef) &&
heap_representation() == htype;
} }
constexpr bool is_defaultable() const { constexpr bool is_defaultable() const {
...@@ -157,25 +189,27 @@ class ValueType { ...@@ -157,25 +189,27 @@ class ValueType {
return ValueType(KindField::encode(kind)); return ValueType(KindField::encode(kind));
} }
static constexpr ValueType Ref(uint32_t heap_type, Nullability nullability) { static constexpr ValueType Ref(uint32_t heap_type, Nullability nullability) {
CONSTEXPR_DCHECK(HeapType(heap_type).is_valid()); CONSTEXPR_DCHECK(heap_type != HeapType::kBottom &&
HeapType(heap_type).is_valid());
return ValueType( return ValueType(
KindField::encode(nullability == kNullable ? kOptRef : kRef) | KindField::encode(nullability == kNullable ? kOptRef : kRef) |
HeapTypeField::encode(heap_type)); HeapTypeField::encode(heap_type));
} }
static constexpr ValueType Ref(HeapType heap_type, Nullability nullability) { static constexpr ValueType Ref(HeapType heap_type, Nullability nullability) {
return Ref(heap_type.type(), nullability); return Ref(heap_type.representation(), nullability);
} }
static constexpr ValueType Rtt(uint32_t heap_type, static constexpr ValueType Rtt(uint32_t heap_type,
uint8_t inheritance_depth) { uint8_t inheritance_depth) {
CONSTEXPR_DCHECK(HeapType(heap_type).is_valid()); CONSTEXPR_DCHECK(heap_type != HeapType::kBottom &&
HeapType(heap_type).is_valid());
return ValueType(KindField::encode(kRtt) | return ValueType(KindField::encode(kRtt) |
HeapTypeField::encode(heap_type) | HeapTypeField::encode(heap_type) |
DepthField::encode(inheritance_depth)); DepthField::encode(inheritance_depth));
} }
static constexpr ValueType Rtt(HeapType heap_type, static constexpr ValueType Rtt(HeapType heap_type,
uint8_t inheritance_depth) { uint8_t inheritance_depth) {
return Rtt(heap_type.type(), inheritance_depth); return Rtt(heap_type.representation(), inheritance_depth);
} }
static constexpr ValueType FromRawBitField(uint32_t bit_field) { static constexpr ValueType FromRawBitField(uint32_t bit_field) {
...@@ -183,11 +217,13 @@ class ValueType { ...@@ -183,11 +217,13 @@ class ValueType {
} }
constexpr Kind kind() const { return KindField::decode(bit_field_); } constexpr Kind kind() const { return KindField::decode(bit_field_); }
constexpr uint32_t heap() const { constexpr uint32_t heap_representation() const {
CONSTEXPR_DCHECK(is_reference_type()); CONSTEXPR_DCHECK(is_reference_type());
return HeapTypeField::decode(bit_field_); return HeapTypeField::decode(bit_field_);
} }
constexpr HeapType heap_type() const { return HeapType(heap()); } constexpr HeapType heap_type() const {
return HeapType(heap_representation());
}
constexpr uint8_t depth() const { constexpr uint8_t depth() const {
CONSTEXPR_DCHECK(has_depth()); CONSTEXPR_DCHECK(has_depth());
return DepthField::decode(bit_field_); return DepthField::decode(bit_field_);
...@@ -252,7 +288,7 @@ class ValueType { ...@@ -252,7 +288,7 @@ class ValueType {
CONSTEXPR_DCHECK(kind() != kBottom); CONSTEXPR_DCHECK(kind() != kBottom);
switch (kind()) { switch (kind()) {
case kOptRef: case kOptRef:
switch (heap()) { switch (heap_representation()) {
case HeapType::kFunc: case HeapType::kFunc:
return kLocalFuncRef; return kLocalFuncRef;
case HeapType::kExtern: case HeapType::kExtern:
...@@ -265,7 +301,7 @@ class ValueType { ...@@ -265,7 +301,7 @@ class ValueType {
return kLocalOptRef; return kLocalOptRef;
} }
case kRef: case kRef:
if (heap() == HeapType::kI31) return kLocalI31Ref; if (heap_representation() == HeapType::kI31) return kLocalI31Ref;
return kLocalRef; return kLocalRef;
case kStmt: case kStmt:
return kLocalVoid; return kLocalVoid;
...@@ -283,9 +319,10 @@ class ValueType { ...@@ -283,9 +319,10 @@ class ValueType {
} }
constexpr bool encoding_needs_heap_type() const { constexpr bool encoding_needs_heap_type() const {
return (kind() == kRef && heap() != HeapType::kI31) || kind() == kRtt || return (kind() == kRef && heap_representation() != HeapType::kI31) ||
(kind() == kOptRef && kind() == kRtt ||
(!heap_type().is_generic() || heap() == HeapType::kI31)); (kind() == kOptRef && (!heap_type().is_generic() ||
heap_representation() == HeapType::kI31));
} }
static ValueType For(MachineType type) { static ValueType For(MachineType type) {
...@@ -323,14 +360,15 @@ class ValueType { ...@@ -323,14 +360,15 @@ class ValueType {
std::ostringstream buf; std::ostringstream buf;
switch (kind()) { switch (kind()) {
case kRef: case kRef:
if (heap() == HeapType::kI31) { if (heap_representation() == HeapType::kI31) {
buf << "i31ref"; buf << "i31ref";
} else { } else {
buf << "(ref " << heap_type().name() << ")"; buf << "(ref " << heap_type().name() << ")";
} }
break; break;
case kOptRef: case kOptRef:
if (heap_type().is_generic()) { if (heap_type().is_generic() &&
heap_representation() != HeapType::kI31) {
// We prefer the shorthand to be backwards-compatible with previous // We prefer the shorthand to be backwards-compatible with previous
// proposals. // proposals.
buf << heap_type().name() << "ref"; buf << heap_type().name() << "ref";
......
...@@ -39,6 +39,15 @@ class WasmFeatures : public base::EnumSet<WasmFeature> { ...@@ -39,6 +39,15 @@ class WasmFeatures : public base::EnumSet<WasmFeature> {
FOREACH_WASM_FEATURE(DECL_FEATURE_GETTER) FOREACH_WASM_FEATURE(DECL_FEATURE_GETTER)
#undef DECL_FEATURE_GETTER #undef DECL_FEATURE_GETTER
static constexpr const char* name_for_feature(WasmFeature feature) {
switch (feature) {
#define NAME(feat, ...) \
case WasmFeature::kFeature_##feat: \
return #feat;
FOREACH_WASM_FEATURE(NAME)
}
#undef NAME
}
static inline constexpr WasmFeatures All(); static inline constexpr WasmFeatures All();
static inline constexpr WasmFeatures None(); static inline constexpr WasmFeatures None();
static inline constexpr WasmFeatures ForAsmjs(); static inline constexpr WasmFeatures ForAsmjs();
......
...@@ -1337,7 +1337,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1337,7 +1337,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
case i::wasm::ValueType::kRef: case i::wasm::ValueType::kRef:
case i::wasm::ValueType::kOptRef: { case i::wasm::ValueType::kOptRef: {
switch (type.heap()) { switch (type.heap_representation()) {
case i::wasm::HeapType::kExtern: case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kExn: { case i::wasm::HeapType::kExn: {
if (args.Length() < 2) { if (args.Length() < 2) {
...@@ -1834,7 +1834,7 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -1834,7 +1834,7 @@ void WebAssemblyGlobalGetValueCommon(
break; break;
case i::wasm::ValueType::kRef: case i::wasm::ValueType::kRef:
case i::wasm::ValueType::kOptRef: case i::wasm::ValueType::kOptRef:
switch (receiver->type().heap()) { switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kExtern: case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kFunc: case i::wasm::HeapType::kFunc:
case i::wasm::HeapType::kExn: case i::wasm::HeapType::kExn:
...@@ -1923,7 +1923,7 @@ void WebAssemblyGlobalSetValue( ...@@ -1923,7 +1923,7 @@ void WebAssemblyGlobalSetValue(
break; break;
case i::wasm::ValueType::kRef: case i::wasm::ValueType::kRef:
case i::wasm::ValueType::kOptRef: case i::wasm::ValueType::kOptRef:
switch (receiver->type().heap()) { switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kExtern: case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kExn: case i::wasm::HeapType::kExn:
receiver->SetExternRef(Utils::OpenHandle(*args[0])); receiver->SetExternRef(Utils::OpenHandle(*args[0]));
......
...@@ -418,7 +418,7 @@ void WriteValueType(ZoneBuffer* buffer, const ValueType& type) { ...@@ -418,7 +418,7 @@ void WriteValueType(ZoneBuffer* buffer, const ValueType& type) {
buffer->write_u32v(type.depth()); buffer->write_u32v(type.depth());
} }
if (type.encoding_needs_heap_type()) { if (type.encoding_needs_heap_type()) {
buffer->write_u32v(type.heap_type().code()); buffer->write_i32v(type.heap_type().code());
} }
} }
...@@ -569,7 +569,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -569,7 +569,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
break; break;
case WasmInitExpr::kRefNullConst: case WasmInitExpr::kRefNullConst:
buffer->write_u8(kExprRefNull); buffer->write_u8(kExprRefNull);
buffer->write_u32v(global.type.heap_type().code()); buffer->write_i32v(global.type.heap_type().code());
break; break;
case WasmInitExpr::kRefFuncConst: case WasmInitExpr::kRefFuncConst:
UNIMPLEMENTED(); UNIMPLEMENTED();
......
...@@ -286,7 +286,7 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, ...@@ -286,7 +286,7 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate,
table_obj->set_entries(*backing_store); table_obj->set_entries(*backing_store);
table_obj->set_current_length(initial); table_obj->set_current_length(initial);
table_obj->set_maximum_length(*max); table_obj->set_maximum_length(*max);
table_obj->set_raw_type(static_cast<int>(type.heap())); table_obj->set_raw_type(static_cast<int>(type.heap_representation()));
table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array()); table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array());
if (entries != nullptr) { if (entries != nullptr) {
......
...@@ -145,15 +145,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(ValueType subtype, ...@@ -145,15 +145,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(ValueType subtype,
HeapType sub_heap = subtype.heap_type(); HeapType sub_heap = subtype.heap_type();
HeapType super_heap = supertype.heap_type(); HeapType super_heap = supertype.heap_type();
DCHECK(!module->has_signature(sub_heap.type()) && DCHECK(!module->has_signature(sub_heap.representation()) &&
!module->has_signature(super_heap.type())); !module->has_signature(super_heap.representation()));
if (sub_heap == super_heap) { if (sub_heap == super_heap) {
return true; return true;
} }
// eqref is a supertype of all reference types except funcref. // eqref is a supertype of all reference types except funcref.
if (super_heap.type() == HeapType::kEq) { if (super_heap.representation() == HeapType::kEq) {
return sub_heap.type() != HeapType::kFunc; return sub_heap.representation() != HeapType::kFunc;
} }
// At the moment, generic heap types are not subtyping-related otherwise. // At the moment, generic heap types are not subtyping-related otherwise.
if (sub_heap.is_generic() || super_heap.is_generic()) { if (sub_heap.is_generic() || super_heap.is_generic()) {
......
...@@ -945,6 +945,7 @@ TEST(MemoryWithOOBEmptyDataSegment) { ...@@ -945,6 +945,7 @@ TEST(MemoryWithOOBEmptyDataSegment) {
TEST(GcStructIdsPass) { TEST(GcStructIdsPass) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(reftypes); EXPERIMENTAL_FLAG_SCOPE(reftypes);
Isolate* isolate = CcTest::InitIsolateOnce(); Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -994,6 +995,7 @@ TEST(GcStructIdsPass) { ...@@ -994,6 +995,7 @@ TEST(GcStructIdsPass) {
TEST(GcTypeIdsUndefinedIndex) { TEST(GcTypeIdsUndefinedIndex) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(reftypes); EXPERIMENTAL_FLAG_SCOPE(reftypes);
Isolate* isolate = CcTest::InitIsolateOnce(); Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -1027,6 +1029,7 @@ TEST(GcTypeIdsUndefinedIndex) { ...@@ -1027,6 +1029,7 @@ TEST(GcTypeIdsUndefinedIndex) {
TEST(GcTypeIdsIllegalIndex) { TEST(GcTypeIdsIllegalIndex) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(reftypes); EXPERIMENTAL_FLAG_SCOPE(reftypes);
Isolate* isolate = CcTest::InitIsolateOnce(); Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -1064,6 +1067,7 @@ TEST(GcTypeIdsIllegalIndex) { ...@@ -1064,6 +1067,7 @@ TEST(GcTypeIdsIllegalIndex) {
TEST(GcTypeIdsFunSigIllegalIndex) { TEST(GcTypeIdsFunSigIllegalIndex) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(reftypes); EXPERIMENTAL_FLAG_SCOPE(reftypes);
Isolate* isolate = CcTest::InitIsolateOnce(); Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -1076,7 +1080,7 @@ TEST(GcTypeIdsFunSigIllegalIndex) { ...@@ -1076,7 +1080,7 @@ TEST(GcTypeIdsFunSigIllegalIndex) {
kTypeSectionCode, // -- kTypeSectionCode, // --
U32V_1(7), // Section size U32V_1(7), // Section size
U32V_1(1), // type count U32V_1(1), // type count
kWasmFunctionTypeCode, // index 0 = int32 -> int32 kWasmFunctionTypeCode, // index 0 = int32 -> (ref null 0)
U32V_1(1), // param count U32V_1(1), // param count
kLocalI32, // param 0 kLocalI32, // param 0
U32V_1(1), // returns count U32V_1(1), // returns count
......
...@@ -2792,7 +2792,7 @@ class WasmInterpreterInternals { ...@@ -2792,7 +2792,7 @@ class WasmInterpreterInternals {
} }
case ValueType::kRef: case ValueType::kRef:
case ValueType::kOptRef: { case ValueType::kOptRef: {
switch (sig->GetParam(i).heap()) { switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern: case HeapType::kExtern:
case HeapType::kExn: case HeapType::kExn:
case HeapType::kFunc: { case HeapType::kFunc: {
...@@ -2909,7 +2909,7 @@ class WasmInterpreterInternals { ...@@ -2909,7 +2909,7 @@ class WasmInterpreterInternals {
} }
case ValueType::kRef: case ValueType::kRef:
case ValueType::kOptRef: { case ValueType::kOptRef: {
switch (sig->GetParam(i).heap()) { switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern: case HeapType::kExtern:
case HeapType::kExn: case HeapType::kExn:
case HeapType::kFunc: { case HeapType::kFunc: {
......
...@@ -90,7 +90,7 @@ const char* ValueTypeToConstantName(ValueType type) { ...@@ -90,7 +90,7 @@ const char* ValueTypeToConstantName(ValueType type) {
case ValueType::kF64: case ValueType::kF64:
return "kWasmF64"; return "kWasmF64";
case ValueType::kOptRef: case ValueType::kOptRef:
switch (type.heap()) { switch (type.heap_representation()) {
case HeapType::kExtern: case HeapType::kExtern:
return "kWasmExternRef"; return "kWasmExternRef";
case HeapType::kFunc: case HeapType::kFunc:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment