[wasm-gc] Implement isorecursive canonicalization
This implements isorecursive canonicalization for static types. Not implemented in this CL: - Runtime type canonicalization. - Cross-module signature canonicalization for purposes of call_indirect. Bug: v8:7748 Change-Id: I6214f947444eea8d7b15a29b35c94c3d07ddb525 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3541925 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#79665}
This commit is contained in:
parent
0ca7f58089
commit
e76ad5c6d9
@ -2434,6 +2434,8 @@ filegroup(
|
|||||||
"src/wasm/baseline/liftoff-compiler.h",
|
"src/wasm/baseline/liftoff-compiler.h",
|
||||||
"src/wasm/baseline/liftoff-register.h",
|
"src/wasm/baseline/liftoff-register.h",
|
||||||
"src/wasm/branch-hint-map.h",
|
"src/wasm/branch-hint-map.h",
|
||||||
|
"src/wasm/canonical-types.cc",
|
||||||
|
"src/wasm/canonical-types.h",
|
||||||
"src/wasm/code-space-access.cc",
|
"src/wasm/code-space-access.cc",
|
||||||
"src/wasm/code-space-access.h",
|
"src/wasm/code-space-access.h",
|
||||||
"src/wasm/compilation-environment.h",
|
"src/wasm/compilation-environment.h",
|
||||||
|
2
BUILD.gn
2
BUILD.gn
@ -3513,6 +3513,7 @@ v8_header_set("v8_internal_headers") {
|
|||||||
"src/wasm/baseline/liftoff-assembler.h",
|
"src/wasm/baseline/liftoff-assembler.h",
|
||||||
"src/wasm/baseline/liftoff-compiler.h",
|
"src/wasm/baseline/liftoff-compiler.h",
|
||||||
"src/wasm/baseline/liftoff-register.h",
|
"src/wasm/baseline/liftoff-register.h",
|
||||||
|
"src/wasm/canonical-types.h",
|
||||||
"src/wasm/code-space-access.h",
|
"src/wasm/code-space-access.h",
|
||||||
"src/wasm/compilation-environment.h",
|
"src/wasm/compilation-environment.h",
|
||||||
"src/wasm/decoder.h",
|
"src/wasm/decoder.h",
|
||||||
@ -4540,6 +4541,7 @@ v8_source_set("v8_base_without_compiler") {
|
|||||||
"src/trap-handler/handler-shared.cc",
|
"src/trap-handler/handler-shared.cc",
|
||||||
"src/wasm/baseline/liftoff-assembler.cc",
|
"src/wasm/baseline/liftoff-assembler.cc",
|
||||||
"src/wasm/baseline/liftoff-compiler.cc",
|
"src/wasm/baseline/liftoff-compiler.cc",
|
||||||
|
"src/wasm/canonical-types.cc",
|
||||||
"src/wasm/code-space-access.cc",
|
"src/wasm/code-space-access.cc",
|
||||||
"src/wasm/function-body-decoder.cc",
|
"src/wasm/function-body-decoder.cc",
|
||||||
"src/wasm/function-compiler.cc",
|
"src/wasm/function-compiler.cc",
|
||||||
|
@ -1095,10 +1095,14 @@ DEFINE_BOOL(wasm_speculative_inlining, false,
|
|||||||
DEFINE_BOOL(trace_wasm_inlining, false, "trace wasm inlining")
|
DEFINE_BOOL(trace_wasm_inlining, false, "trace wasm inlining")
|
||||||
DEFINE_BOOL(trace_wasm_speculative_inlining, false,
|
DEFINE_BOOL(trace_wasm_speculative_inlining, false,
|
||||||
"trace wasm speculative inlining")
|
"trace wasm speculative inlining")
|
||||||
|
DEFINE_BOOL(wasm_type_canonicalization, false,
|
||||||
|
"apply isorecursive canonicalization on wasm types")
|
||||||
DEFINE_IMPLICATION(wasm_speculative_inlining, experimental_wasm_typed_funcref)
|
DEFINE_IMPLICATION(wasm_speculative_inlining, experimental_wasm_typed_funcref)
|
||||||
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_dynamic_tiering)
|
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_dynamic_tiering)
|
||||||
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_inlining)
|
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_inlining)
|
||||||
DEFINE_WEAK_IMPLICATION(experimental_wasm_gc, wasm_speculative_inlining)
|
DEFINE_WEAK_IMPLICATION(experimental_wasm_gc, wasm_speculative_inlining)
|
||||||
|
DEFINE_WEAK_IMPLICATION(experimental_wasm_typed_funcref,
|
||||||
|
wasm_type_canonicalization)
|
||||||
// Speculative inlining needs type feedback from Liftoff and compilation in
|
// Speculative inlining needs type feedback from Liftoff and compilation in
|
||||||
// Turbofan.
|
// Turbofan.
|
||||||
DEFINE_NEG_NEG_IMPLICATION(liftoff, wasm_speculative_inlining)
|
DEFINE_NEG_NEG_IMPLICATION(liftoff, wasm_speculative_inlining)
|
||||||
|
149
src/wasm/canonical-types.cc
Normal file
149
src/wasm/canonical-types.cc
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "src/wasm/canonical-types.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
namespace wasm {
|
||||||
|
|
||||||
|
void TypeCanonicalizer::AddRecursiveGroup(WasmModule* module, uint32_t size) {
|
||||||
|
// Multiple threads could try to register recursive groups concurrently.
|
||||||
|
// TODO(manoskouk): Investigate if we can fine-grain the synchronization.
|
||||||
|
base::MutexGuard mutex_guard(&mutex_);
|
||||||
|
DCHECK_GE(module->types.size(), size);
|
||||||
|
uint32_t start_index = static_cast<uint32_t>(module->types.size()) - size;
|
||||||
|
CanonicalGroup group;
|
||||||
|
group.types.resize(size);
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
group.types[i] = CanonicalizeTypeDef(module, module->types[start_index + i],
|
||||||
|
start_index);
|
||||||
|
}
|
||||||
|
int canonical_index = FindCanonicalGroup(group);
|
||||||
|
if (canonical_index >= 0) {
|
||||||
|
// Identical group found. Map new types to the old types's canonical
|
||||||
|
// representatives.
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
module->isorecursive_canonical_type_ids[start_index + i] =
|
||||||
|
canonical_index + i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Identical group not found. Add new canonical representatives for the new
|
||||||
|
// types.
|
||||||
|
uint32_t first_canonical_index =
|
||||||
|
static_cast<uint32_t>(canonical_supertypes_.size());
|
||||||
|
canonical_supertypes_.resize(first_canonical_index + size);
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
CanonicalType& canonical_type = group.types[i];
|
||||||
|
// Compute the canonical index of the supertype: If it is relative, we
|
||||||
|
// need to add {first_canonical_index}.
|
||||||
|
canonical_supertypes_[first_canonical_index + i] =
|
||||||
|
canonical_type.is_relative_supertype
|
||||||
|
? canonical_type.type_def.supertype + first_canonical_index
|
||||||
|
: canonical_type.type_def.supertype;
|
||||||
|
module->isorecursive_canonical_type_ids[start_index + i] =
|
||||||
|
first_canonical_index + i;
|
||||||
|
}
|
||||||
|
canonical_groups_.emplace(group, first_canonical_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An index in a type gets mapped to a relative index if it is inside the new
|
||||||
|
// canonical group, or the canonical representative if it is not.
|
||||||
|
ValueType TypeCanonicalizer::CanonicalizeValueType(
|
||||||
|
const WasmModule* module, ValueType type,
|
||||||
|
uint32_t recursive_group_start) const {
|
||||||
|
if (!type.has_index()) return type;
|
||||||
|
return type.ref_index() >= recursive_group_start
|
||||||
|
? ValueType::CanonicalWithRelativeIndex(
|
||||||
|
type.kind(), type.ref_index() - recursive_group_start)
|
||||||
|
: ValueType::FromIndex(
|
||||||
|
type.kind(),
|
||||||
|
module->isorecursive_canonical_type_ids[type.ref_index()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TypeCanonicalizer::IsCanonicalSubtype(uint32_t sub_index,
|
||||||
|
uint32_t super_index,
|
||||||
|
const WasmModule* sub_module,
|
||||||
|
const WasmModule* super_module) {
|
||||||
|
// Multiple threads could try to register and access recursive groups
|
||||||
|
// concurrently.
|
||||||
|
// TODO(manoskouk): Investigate if we can improve this synchronization.
|
||||||
|
base::MutexGuard mutex_guard(&mutex_);
|
||||||
|
uint32_t canonical_super =
|
||||||
|
super_module->isorecursive_canonical_type_ids[super_index];
|
||||||
|
uint32_t canonical_sub =
|
||||||
|
sub_module->isorecursive_canonical_type_ids[sub_index];
|
||||||
|
while (canonical_sub != kNoSuperType) {
|
||||||
|
if (canonical_sub == canonical_super) return true;
|
||||||
|
canonical_sub = canonical_supertypes_[canonical_sub];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map all type indices (including supertype) inside {type} to indices relative
|
||||||
|
// to {recursive_group_start}.
|
||||||
|
TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
|
||||||
|
const WasmModule* module, TypeDefinition type,
|
||||||
|
uint32_t recursive_group_start) {
|
||||||
|
uint32_t canonical_supertype = kNoSuperType;
|
||||||
|
bool is_relative_supertype = false;
|
||||||
|
if (type.supertype < recursive_group_start) {
|
||||||
|
canonical_supertype =
|
||||||
|
module->isorecursive_canonical_type_ids[type.supertype];
|
||||||
|
} else if (type.supertype != kNoSuperType) {
|
||||||
|
canonical_supertype = type.supertype - recursive_group_start;
|
||||||
|
is_relative_supertype = true;
|
||||||
|
}
|
||||||
|
TypeDefinition result;
|
||||||
|
switch (type.kind) {
|
||||||
|
case TypeDefinition::kFunction: {
|
||||||
|
const FunctionSig* original_sig = type.function_sig;
|
||||||
|
FunctionSig::Builder builder(&zone_, original_sig->return_count(),
|
||||||
|
original_sig->parameter_count());
|
||||||
|
for (ValueType ret : original_sig->returns()) {
|
||||||
|
builder.AddReturn(
|
||||||
|
CanonicalizeValueType(module, ret, recursive_group_start));
|
||||||
|
}
|
||||||
|
for (ValueType param : original_sig->parameters()) {
|
||||||
|
builder.AddParam(
|
||||||
|
CanonicalizeValueType(module, param, recursive_group_start));
|
||||||
|
}
|
||||||
|
result = TypeDefinition(builder.Build(), canonical_supertype);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeDefinition::kStruct: {
|
||||||
|
const StructType* original_type = type.struct_type;
|
||||||
|
StructType::Builder builder(&zone_, original_type->field_count());
|
||||||
|
for (uint32_t i = 0; i < original_type->field_count(); i++) {
|
||||||
|
builder.AddField(CanonicalizeValueType(module, original_type->field(i),
|
||||||
|
recursive_group_start),
|
||||||
|
original_type->mutability(i));
|
||||||
|
}
|
||||||
|
result = TypeDefinition(builder.Build(), canonical_supertype);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeDefinition::kArray: {
|
||||||
|
ValueType element_type = CanonicalizeValueType(
|
||||||
|
module, type.array_type->element_type(), recursive_group_start);
|
||||||
|
result = TypeDefinition(
|
||||||
|
zone_.New<ArrayType>(element_type, type.array_type->mutability()),
|
||||||
|
canonical_supertype);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result, is_relative_supertype};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the index of the canonical representative of the first type in this
|
||||||
|
// group, or -1 if an identical group does not exist.
|
||||||
|
int TypeCanonicalizer::FindCanonicalGroup(CanonicalGroup& group) const {
|
||||||
|
auto element = canonical_groups_.find(group);
|
||||||
|
return element == canonical_groups_.end() ? -1 : element->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wasm
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
122
src/wasm/canonical-types.h
Normal file
122
src/wasm/canonical-types.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#if !V8_ENABLE_WEBASSEMBLY
|
||||||
|
#error This header should only be included if WebAssembly is enabled.
|
||||||
|
#endif // !V8_ENABLE_WEBASSEMBLY
|
||||||
|
|
||||||
|
#ifndef V8_WASM_CANONICAL_TYPES_H_
|
||||||
|
#define V8_WASM_CANONICAL_TYPES_H_
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "src/base/lazy-instance.h"
|
||||||
|
#include "src/wasm/wasm-module.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
namespace wasm {
|
||||||
|
|
||||||
|
// A singleton class, responsible for isorecursive canonicalization of wasm
|
||||||
|
// types.
|
||||||
|
// A recursive group is a subsequence of types explicitly marked in the type
|
||||||
|
// section of a wasm module. Identical recursive groups have to be canonicalized
|
||||||
|
// to a single canonical group and are are considered identical. Respective
|
||||||
|
// types in two identical groups are considered identical for all purposes.
|
||||||
|
// Two groups are considered identical if they have the same shape, and all
|
||||||
|
// type indices referenced in the same position in both groups reference:
|
||||||
|
// - identical types, if those do not belong to the rec. group,
|
||||||
|
// - types in the same relative position in the group, if those belong to the
|
||||||
|
// rec. group.
|
||||||
|
class TypeCanonicalizer {
|
||||||
|
public:
|
||||||
|
TypeCanonicalizer() = default;
|
||||||
|
|
||||||
|
static TypeCanonicalizer* instance() {
|
||||||
|
static base::LazyInstance<TypeCanonicalizer>::type instance_ =
|
||||||
|
LAZY_INSTANCE_INITIALIZER;
|
||||||
|
return instance_.Pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers the last {size} types of {module} as a recursive group, and
|
||||||
|
// possibly canonicalizes it if an identical one has been found.
|
||||||
|
// Modifies {module->isorecursive_canonical_type_ids}.
|
||||||
|
V8_EXPORT_PRIVATE void AddRecursiveGroup(WasmModule* module, uint32_t size);
|
||||||
|
|
||||||
|
// Returns if the type at {sub_index} in {sub_module} is a subtype of the
|
||||||
|
// type at {super_index} in {super_module} after canonicalization.
|
||||||
|
V8_EXPORT_PRIVATE bool IsCanonicalSubtype(uint32_t sub_index,
|
||||||
|
uint32_t super_index,
|
||||||
|
const WasmModule* sub_module,
|
||||||
|
const WasmModule* super_module);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using TypeInModule = std::pair<const WasmModule*, uint32_t>;
|
||||||
|
struct CanonicalType {
|
||||||
|
TypeDefinition type_def;
|
||||||
|
bool is_relative_supertype;
|
||||||
|
|
||||||
|
bool operator==(const CanonicalType& other) const {
|
||||||
|
return type_def == other.type_def &&
|
||||||
|
is_relative_supertype == other.is_relative_supertype;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const CanonicalType& other) const {
|
||||||
|
return type_def != other.type_def ||
|
||||||
|
is_relative_supertype != other.is_relative_supertype;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value() const {
|
||||||
|
return base::hash_combine(type_def.kind,
|
||||||
|
base::hash_value(is_relative_supertype));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct CanonicalGroup {
|
||||||
|
struct hash {
|
||||||
|
size_t operator()(const CanonicalGroup& group) const {
|
||||||
|
return group.hash_value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const CanonicalGroup& other) const {
|
||||||
|
return types == other.types;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const CanonicalGroup& other) const {
|
||||||
|
return types != other.types;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value() const {
|
||||||
|
size_t result = 0;
|
||||||
|
for (const CanonicalType& type : types) {
|
||||||
|
result = base::hash_combine(result, type.hash_value());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CanonicalType> types;
|
||||||
|
};
|
||||||
|
|
||||||
|
int FindCanonicalGroup(CanonicalGroup&) const;
|
||||||
|
|
||||||
|
CanonicalType CanonicalizeTypeDef(const WasmModule* module,
|
||||||
|
TypeDefinition type,
|
||||||
|
uint32_t recursive_group_start);
|
||||||
|
ValueType CanonicalizeValueType(const WasmModule* module, ValueType type,
|
||||||
|
uint32_t recursive_group_start) const;
|
||||||
|
|
||||||
|
std::vector<uint32_t> canonical_supertypes_;
|
||||||
|
// group -> canonical id of first type
|
||||||
|
std::unordered_map<CanonicalGroup, uint32_t, CanonicalGroup::hash>
|
||||||
|
canonical_groups_;
|
||||||
|
AccountingAllocator allocator_;
|
||||||
|
Zone zone_{&allocator_, "canonical type zone"};
|
||||||
|
base::Mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wasm
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // V8_WASM_CANONICAL_TYPES_H_
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/logging/metrics.h"
|
#include "src/logging/metrics.h"
|
||||||
#include "src/objects/objects-inl.h"
|
#include "src/objects/objects-inl.h"
|
||||||
#include "src/utils/ostreams.h"
|
#include "src/utils/ostreams.h"
|
||||||
|
#include "src/wasm/canonical-types.h"
|
||||||
#include "src/wasm/decoder.h"
|
#include "src/wasm/decoder.h"
|
||||||
#include "src/wasm/function-body-decoder-impl.h"
|
#include "src/wasm/function-body-decoder-impl.h"
|
||||||
#include "src/wasm/init-expr-interface.h"
|
#include "src/wasm/init-expr-interface.h"
|
||||||
@ -669,17 +670,21 @@ class ModuleDecoderImpl : public Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DecodeTypeSection() {
|
void DecodeTypeSection() {
|
||||||
|
TypeCanonicalizer* type_canon = TypeCanonicalizer::instance();
|
||||||
uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
|
uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
|
||||||
|
|
||||||
// Non wasm-gc type section decoding.
|
// Non wasm-gc type section decoding.
|
||||||
if (!enabled_features_.has_gc()) {
|
if (!enabled_features_.has_gc()) {
|
||||||
for (uint32_t i = 0; ok() && i < types_count; ++i) {
|
for (uint32_t i = 0; i < types_count; ++i) {
|
||||||
TRACE("DecodeSignature[%d] module+%d\n", i,
|
TRACE("DecodeSignature[%d] module+%d\n", i,
|
||||||
static_cast<int>(pc_ - start_));
|
static_cast<int>(pc_ - start_));
|
||||||
expect_u8("signature definition", kWasmFunctionTypeCode);
|
expect_u8("signature definition", kWasmFunctionTypeCode);
|
||||||
const FunctionSig* sig = consume_sig(module_->signature_zone.get());
|
const FunctionSig* sig = consume_sig(module_->signature_zone.get());
|
||||||
if (!ok()) break;
|
if (!ok()) break;
|
||||||
module_->add_signature(sig, kNoSuperType);
|
module_->add_signature(sig, kNoSuperType);
|
||||||
|
if (FLAG_wasm_type_canonicalization) {
|
||||||
|
type_canon->AddRecursiveGroup(module_.get(), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -700,6 +705,9 @@ class ModuleDecoderImpl : public Decoder {
|
|||||||
TypeDefinition type = consume_nominal_type_definition();
|
TypeDefinition type = consume_nominal_type_definition();
|
||||||
if (ok()) module_->add_type(type);
|
if (ok()) module_->add_type(type);
|
||||||
}
|
}
|
||||||
|
if (ok() && FLAG_wasm_type_canonicalization) {
|
||||||
|
type_canon->AddRecursiveGroup(module_.get(), types_count);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// wasm-gc isorecursive type section decoding.
|
// wasm-gc isorecursive type section decoding.
|
||||||
for (uint32_t i = 0; ok() && i < types_count; ++i) {
|
for (uint32_t i = 0; ok() && i < types_count; ++i) {
|
||||||
@ -722,9 +730,17 @@ class ModuleDecoderImpl : public Decoder {
|
|||||||
TypeDefinition type = consume_subtype_definition();
|
TypeDefinition type = consume_subtype_definition();
|
||||||
if (ok()) module_->add_type(type);
|
if (ok()) module_->add_type(type);
|
||||||
}
|
}
|
||||||
|
if (ok() && FLAG_wasm_type_canonicalization) {
|
||||||
|
type_canon->AddRecursiveGroup(module_.get(), group_size);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
TypeDefinition type = consume_subtype_definition();
|
TypeDefinition type = consume_subtype_definition();
|
||||||
if (ok()) module_->add_type(type);
|
if (ok()) {
|
||||||
|
module_->add_type(type);
|
||||||
|
if (FLAG_wasm_type_canonicalization) {
|
||||||
|
type_canon->AddRecursiveGroup(module_.get(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,8 +132,12 @@ class ArrayType : public ZoneObject {
|
|||||||
ValueType element_type() const { return rep_; }
|
ValueType element_type() const { return rep_; }
|
||||||
bool mutability() const { return mutability_; }
|
bool mutability() const { return mutability_; }
|
||||||
|
|
||||||
bool operator==(const ArrayType& other) const { return rep_ == other.rep_; }
|
bool operator==(const ArrayType& other) const {
|
||||||
bool operator!=(const ArrayType& other) const { return rep_ != other.rep_; }
|
return rep_ == other.rep_ && mutability_ == other.mutability_;
|
||||||
|
}
|
||||||
|
bool operator!=(const ArrayType& other) const {
|
||||||
|
return rep_ != other.rep_ || mutability_ != other.mutability_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ValueType rep_;
|
const ValueType rep_;
|
||||||
|
@ -279,12 +279,14 @@ constexpr bool is_defaultable(ValueKind kind) {
|
|||||||
return kind != kRef && !is_rtt(kind);
|
return kind != kRef && !is_rtt(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ValueType is encoded by three components: A ValueKind, a heap
|
// A ValueType is encoded by two components: a ValueKind and a heap
|
||||||
// representation (for reference types), and an inheritance depth (for rtts
|
// representation (for reference types/rtts). Those are encoded into 32 bits
|
||||||
// only). Those are encoded into 32 bits using base::BitField. The underlying
|
// using base::BitField. The underlying ValueKind enumeration includes four
|
||||||
// ValueKind enumeration includes four elements which do not strictly correspond
|
// elements which do not strictly correspond to value types: the two packed
|
||||||
// to value types: the two packed types i8 and i16, the void type (for control
|
// types i8 and i16, the void type (for control structures), and a bottom value
|
||||||
// structures), and a bottom value (for internal use).
|
// (for internal use).
|
||||||
|
// ValueType encoding includes an additional bit marking the index of a type as
|
||||||
|
// relative. This should only be used during type canonicalization.
|
||||||
class ValueType {
|
class ValueType {
|
||||||
public:
|
public:
|
||||||
/******************************* Constructors *******************************/
|
/******************************* Constructors *******************************/
|
||||||
@ -309,6 +311,11 @@ class ValueType {
|
|||||||
HeapTypeField::encode(type_index));
|
HeapTypeField::encode(type_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr ValueType FromIndex(ValueKind kind, uint32_t index) {
|
||||||
|
DCHECK(kind == kOptRef || kind == kRef || kind == kRtt);
|
||||||
|
return ValueType(KindField::encode(kind) | HeapTypeField::encode(index));
|
||||||
|
}
|
||||||
|
|
||||||
// Useful when deserializing a type stored in a runtime object.
|
// Useful when deserializing a type stored in a runtime object.
|
||||||
static constexpr ValueType FromRawBitField(uint32_t bit_field) {
|
static constexpr ValueType FromRawBitField(uint32_t bit_field) {
|
||||||
return ValueType(bit_field);
|
return ValueType(bit_field);
|
||||||
@ -491,8 +498,6 @@ class ValueType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int kLastUsedBit = 24;
|
|
||||||
|
|
||||||
/****************************** Pretty-printing *****************************/
|
/****************************** Pretty-printing *****************************/
|
||||||
constexpr char short_name() const { return wasm::short_name(kind()); }
|
constexpr char short_name() const { return wasm::short_name(kind()); }
|
||||||
|
|
||||||
@ -517,23 +522,40 @@ class ValueType {
|
|||||||
return buf.str();
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only use 31 bits so ValueType fits in a Smi. This can be changed if
|
/********************** Type canonicalization utilities *********************/
|
||||||
// needed.
|
static constexpr ValueType CanonicalWithRelativeIndex(ValueKind kind,
|
||||||
|
uint32_t index) {
|
||||||
|
return ValueType(KindField::encode(kind) | HeapTypeField::encode(index) |
|
||||||
|
CanonicalRelativeField::encode(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool is_canonical_relative() const {
|
||||||
|
return has_index() && CanonicalRelativeField::decode(bit_field_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************** Static constants ******************************/
|
||||||
|
static constexpr int kLastUsedBit = 25;
|
||||||
static constexpr int kKindBits = 5;
|
static constexpr int kKindBits = 5;
|
||||||
static constexpr int kHeapTypeBits = 20;
|
static constexpr int kHeapTypeBits = 20;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
STATIC_ASSERT(kV8MaxWasmTypes < (1u << kHeapTypeBits));
|
|
||||||
|
|
||||||
// {hash_value} directly reads {bit_field_}.
|
// {hash_value} directly reads {bit_field_}.
|
||||||
friend size_t hash_value(ValueType type);
|
friend size_t hash_value(ValueType type);
|
||||||
|
|
||||||
using KindField = base::BitField<ValueKind, 0, kKindBits>;
|
using KindField = base::BitField<ValueKind, 0, kKindBits>;
|
||||||
using HeapTypeField = KindField::Next<uint32_t, kHeapTypeBits>;
|
using HeapTypeField = KindField::Next<uint32_t, kHeapTypeBits>;
|
||||||
|
// Marks a type as a canonical type which uses an index relative to its
|
||||||
|
// recursive group start. Used only during type canonicalization.
|
||||||
|
using CanonicalRelativeField = HeapTypeField::Next<bool, 1>;
|
||||||
|
|
||||||
|
static_assert(kV8MaxWasmTypes < (1u << kHeapTypeBits),
|
||||||
|
"Type indices fit in kHeapTypeBits");
|
||||||
// This is implemented defensively against field order changes.
|
// This is implemented defensively against field order changes.
|
||||||
STATIC_ASSERT(kLastUsedBit ==
|
static_assert(kLastUsedBit ==
|
||||||
std::max(KindField::kLastUsedBit, HeapTypeField::kLastUsedBit));
|
std::max(KindField::kLastUsedBit,
|
||||||
|
std::max(HeapTypeField::kLastUsedBit,
|
||||||
|
CanonicalRelativeField::kLastUsedBit)),
|
||||||
|
"kLastUsedBit is consistent");
|
||||||
|
|
||||||
constexpr explicit ValueType(uint32_t bit_field) : bit_field_(bit_field) {}
|
constexpr explicit ValueType(uint32_t bit_field) : bit_field_(bit_field) {}
|
||||||
|
|
||||||
|
@ -274,6 +274,8 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
|
|||||||
globals_(zone),
|
globals_(zone),
|
||||||
exceptions_(zone),
|
exceptions_(zone),
|
||||||
signature_map_(zone),
|
signature_map_(zone),
|
||||||
|
current_recursive_group_start_(-1),
|
||||||
|
recursive_groups_(zone),
|
||||||
start_function_index_(-1),
|
start_function_index_(-1),
|
||||||
min_memory_size_(16),
|
min_memory_size_(16),
|
||||||
max_memory_size_(0),
|
max_memory_size_(0),
|
||||||
@ -593,10 +595,24 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
|
|||||||
// == Emit types =============================================================
|
// == Emit types =============================================================
|
||||||
if (types_.size() > 0) {
|
if (types_.size() > 0) {
|
||||||
size_t start = EmitSection(kTypeSectionCode, buffer);
|
size_t start = EmitSection(kTypeSectionCode, buffer);
|
||||||
buffer->write_size(types_.size());
|
size_t type_count = types_.size();
|
||||||
|
for (auto pair : recursive_groups_) {
|
||||||
|
// Every rec. group counts as one type entry.
|
||||||
|
type_count -= pair.second - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->write_size(type_count);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < types_.size(); i++) {
|
||||||
|
auto recursive_group = recursive_groups_.find(i);
|
||||||
|
|
||||||
|
if (recursive_group != recursive_groups_.end()) {
|
||||||
|
buffer->write_u8(kWasmRecursiveTypeGroupCode);
|
||||||
|
buffer->write_u32v(recursive_group->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeDefinition& type = types_[i];
|
||||||
|
|
||||||
// TODO(7748): Add support for recursive groups.
|
|
||||||
for (const TypeDefinition& type : types_) {
|
|
||||||
if (type.supertype != kNoSuperType) {
|
if (type.supertype != kNoSuperType) {
|
||||||
buffer->write_u8(kWasmSubtypeCode);
|
buffer->write_u8(kWasmSubtypeCode);
|
||||||
buffer->write_u8(1); // The supertype count is always 1.
|
buffer->write_u8(1); // The supertype count is always 1.
|
||||||
|
@ -359,6 +359,22 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
|
|||||||
void SetMaxMemorySize(uint32_t value);
|
void SetMaxMemorySize(uint32_t value);
|
||||||
void SetHasSharedMemory();
|
void SetHasSharedMemory();
|
||||||
|
|
||||||
|
void StartRecursiveTypeGroup() {
|
||||||
|
DCHECK_EQ(current_recursive_group_start_, -1);
|
||||||
|
current_recursive_group_start_ = static_cast<int>(types_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndRecursiveTypeGroup() {
|
||||||
|
// Make sure we are in a recursive group.
|
||||||
|
DCHECK_NE(current_recursive_group_start_, -1);
|
||||||
|
// Make sure the current recursive group has at least one element.
|
||||||
|
DCHECK_GT(static_cast<int>(types_.size()), current_recursive_group_start_);
|
||||||
|
recursive_groups_.emplace(
|
||||||
|
current_recursive_group_start_,
|
||||||
|
static_cast<uint32_t>(types_.size()) - current_recursive_group_start_);
|
||||||
|
current_recursive_group_start_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Writing methods.
|
// Writing methods.
|
||||||
void WriteTo(ZoneBuffer* buffer) const;
|
void WriteTo(ZoneBuffer* buffer) const;
|
||||||
void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const;
|
void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const;
|
||||||
@ -455,6 +471,9 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
|
|||||||
ZoneVector<WasmGlobal> globals_;
|
ZoneVector<WasmGlobal> globals_;
|
||||||
ZoneVector<int> exceptions_;
|
ZoneVector<int> exceptions_;
|
||||||
ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
|
ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
|
||||||
|
int current_recursive_group_start_;
|
||||||
|
// first index -> size
|
||||||
|
ZoneUnorderedMap<uint32_t, uint32_t> recursive_groups_;
|
||||||
int start_function_index_;
|
int start_function_index_;
|
||||||
uint32_t min_memory_size_;
|
uint32_t min_memory_size_;
|
||||||
uint32_t max_memory_size_;
|
uint32_t max_memory_size_;
|
||||||
|
@ -364,6 +364,25 @@ struct TypeDefinition {
|
|||||||
const StructType* struct_type;
|
const StructType* struct_type;
|
||||||
const ArrayType* array_type;
|
const ArrayType* array_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator==(const TypeDefinition& other) const {
|
||||||
|
if (supertype != other.supertype || kind != other.kind) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (kind) {
|
||||||
|
case kFunction:
|
||||||
|
return *function_sig == *other.function_sig;
|
||||||
|
case kStruct:
|
||||||
|
return *struct_type == *other.struct_type;
|
||||||
|
case kArray:
|
||||||
|
return *array_type == *other.array_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const TypeDefinition& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t supertype;
|
uint32_t supertype;
|
||||||
Kind kind;
|
Kind kind;
|
||||||
};
|
};
|
||||||
@ -424,15 +443,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
? signature_map.FindOrInsert(*type.function_sig)
|
? signature_map.FindOrInsert(*type.function_sig)
|
||||||
: 0;
|
: 0;
|
||||||
canonicalized_type_ids.push_back(canonical_id);
|
canonicalized_type_ids.push_back(canonical_id);
|
||||||
|
isorecursive_canonical_type_ids.push_back(-1); // Will be computed later.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_type(uint32_t index) const { return index < types.size(); }
|
bool has_type(uint32_t index) const { return index < types.size(); }
|
||||||
|
|
||||||
void add_signature(const FunctionSig* sig, uint32_t supertype) {
|
void add_signature(const FunctionSig* sig, uint32_t supertype) {
|
||||||
types.push_back(TypeDefinition(sig, supertype));
|
|
||||||
DCHECK_NOT_NULL(sig);
|
DCHECK_NOT_NULL(sig);
|
||||||
uint32_t canonical_id = signature_map.FindOrInsert(*sig);
|
add_type(TypeDefinition(sig, supertype));
|
||||||
canonicalized_type_ids.push_back(canonical_id);
|
|
||||||
}
|
}
|
||||||
bool has_signature(uint32_t index) const {
|
bool has_signature(uint32_t index) const {
|
||||||
return index < types.size() &&
|
return index < types.size() &&
|
||||||
@ -444,9 +462,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void add_struct_type(const StructType* type, uint32_t supertype) {
|
void add_struct_type(const StructType* type, uint32_t supertype) {
|
||||||
types.push_back(TypeDefinition(type, supertype));
|
DCHECK_NOT_NULL(type);
|
||||||
// No canonicalization for structs.
|
add_type(TypeDefinition(type, supertype));
|
||||||
canonicalized_type_ids.push_back(0);
|
|
||||||
}
|
}
|
||||||
bool has_struct(uint32_t index) const {
|
bool has_struct(uint32_t index) const {
|
||||||
return index < types.size() && types[index].kind == TypeDefinition::kStruct;
|
return index < types.size() && types[index].kind == TypeDefinition::kStruct;
|
||||||
@ -457,9 +474,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void add_array_type(const ArrayType* type, uint32_t supertype) {
|
void add_array_type(const ArrayType* type, uint32_t supertype) {
|
||||||
types.push_back(TypeDefinition(type, supertype));
|
DCHECK_NOT_NULL(type);
|
||||||
// No canonicalization for arrays.
|
add_type(TypeDefinition(type, supertype));
|
||||||
canonicalized_type_ids.push_back(0);
|
|
||||||
}
|
}
|
||||||
bool has_array(uint32_t index) const {
|
bool has_array(uint32_t index) const {
|
||||||
return index < types.size() && types[index].kind == TypeDefinition::kArray;
|
return index < types.size() && types[index].kind == TypeDefinition::kArray;
|
||||||
@ -478,11 +494,12 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeDefinition> types; // by type index
|
std::vector<TypeDefinition> types; // by type index
|
||||||
// Map from each type index to the index of its corresponding canonical index.
|
// TODO(7748): Unify the following two arrays.
|
||||||
// Canonical indices do not correspond to types.
|
// Maps each type index to a canonical index for purposes of call_indirect.
|
||||||
// Note: right now, only functions are canonicalized, and arrays and structs
|
|
||||||
// map to 0.
|
|
||||||
std::vector<uint32_t> canonicalized_type_ids;
|
std::vector<uint32_t> canonicalized_type_ids;
|
||||||
|
// Maps each type index to its global (cross-module) canonical index as per
|
||||||
|
// isorecursive type canonicalization.
|
||||||
|
std::vector<uint32_t> isorecursive_canonical_type_ids;
|
||||||
// Canonicalizing map for signature indexes.
|
// Canonicalizing map for signature indexes.
|
||||||
SignatureMap signature_map;
|
SignatureMap signature_map;
|
||||||
std::vector<WasmFunction> functions;
|
std::vector<WasmFunction> functions;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "src/wasm/wasm-subtyping.h"
|
#include "src/wasm/wasm-subtyping.h"
|
||||||
|
|
||||||
#include "src/base/platform/mutex.h"
|
#include "src/base/platform/mutex.h"
|
||||||
|
#include "src/wasm/canonical-types.h"
|
||||||
#include "src/wasm/wasm-module.h"
|
#include "src/wasm/wasm-module.h"
|
||||||
#include "src/zone/zone-containers.h"
|
#include "src/zone/zone-containers.h"
|
||||||
|
|
||||||
@ -18,17 +19,15 @@ V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
|
|||||||
const WasmModule* module1,
|
const WasmModule* module1,
|
||||||
const WasmModule* module2) {
|
const WasmModule* module2) {
|
||||||
DCHECK(index1 != index2 || module1 != module2);
|
DCHECK(index1 != index2 || module1 != module2);
|
||||||
// TODO(7748): Canonicalize types.
|
if (!FLAG_wasm_type_canonicalization) return false;
|
||||||
return false;
|
return module1->isorecursive_canonical_type_ids[index1] ==
|
||||||
|
module2->isorecursive_canonical_type_ids[index2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidStructSubtypeDefinition(uint32_t subtype_index,
|
bool ValidStructSubtypeDefinition(uint32_t subtype_index,
|
||||||
uint32_t supertype_index,
|
uint32_t supertype_index,
|
||||||
const WasmModule* sub_module,
|
const WasmModule* sub_module,
|
||||||
const WasmModule* super_module) {
|
const WasmModule* super_module) {
|
||||||
// TODO(7748): Figure out the cross-module story.
|
|
||||||
if (sub_module != super_module) return false;
|
|
||||||
|
|
||||||
const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
|
const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
|
||||||
const StructType* super_struct =
|
const StructType* super_struct =
|
||||||
super_module->types[supertype_index].struct_type;
|
super_module->types[supertype_index].struct_type;
|
||||||
@ -56,9 +55,6 @@ bool ValidArraySubtypeDefinition(uint32_t subtype_index,
|
|||||||
uint32_t supertype_index,
|
uint32_t supertype_index,
|
||||||
const WasmModule* sub_module,
|
const WasmModule* sub_module,
|
||||||
const WasmModule* super_module) {
|
const WasmModule* super_module) {
|
||||||
// TODO(7748): Figure out the cross-module story.
|
|
||||||
if (sub_module != super_module) return false;
|
|
||||||
|
|
||||||
const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
|
const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
|
||||||
const ArrayType* super_array =
|
const ArrayType* super_array =
|
||||||
super_module->types[supertype_index].array_type;
|
super_module->types[supertype_index].array_type;
|
||||||
@ -78,9 +74,6 @@ bool ValidFunctionSubtypeDefinition(uint32_t subtype_index,
|
|||||||
uint32_t supertype_index,
|
uint32_t supertype_index,
|
||||||
const WasmModule* sub_module,
|
const WasmModule* sub_module,
|
||||||
const WasmModule* super_module) {
|
const WasmModule* super_module) {
|
||||||
// TODO(7748): Figure out the cross-module story.
|
|
||||||
if (sub_module != super_module) return false;
|
|
||||||
|
|
||||||
const FunctionSig* sub_func = sub_module->types[subtype_index].function_sig;
|
const FunctionSig* sub_func = sub_module->types[subtype_index].function_sig;
|
||||||
const FunctionSig* super_func =
|
const FunctionSig* super_func =
|
||||||
super_module->types[supertype_index].function_sig;
|
super_module->types[supertype_index].function_sig;
|
||||||
@ -219,15 +212,17 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
|
|||||||
// equality; here we catch (ref $x) being a subtype of (ref null $x).
|
// equality; here we catch (ref $x) being a subtype of (ref null $x).
|
||||||
if (sub_module == super_module && sub_index == super_index) return true;
|
if (sub_module == super_module && sub_index == super_index) return true;
|
||||||
|
|
||||||
// TODO(7748): Figure out cross-module story.
|
if (FLAG_wasm_type_canonicalization) {
|
||||||
if (sub_module != super_module) return false;
|
return TypeCanonicalizer::instance()->IsCanonicalSubtype(
|
||||||
|
sub_index, super_index, sub_module, super_module);
|
||||||
uint32_t explicit_super = sub_module->supertype(sub_index);
|
} else {
|
||||||
while (true) {
|
uint32_t explicit_super = sub_module->supertype(sub_index);
|
||||||
if (explicit_super == super_index) return true;
|
while (true) {
|
||||||
// Reached the end of the explicitly defined inheritance chain.
|
if (explicit_super == super_index) return true;
|
||||||
if (explicit_super == kNoSuperType) return false;
|
// Reached the end of the explicitly defined inheritance chain.
|
||||||
explicit_super = sub_module->supertype(explicit_super);
|
if (explicit_super == kNoSuperType) return false;
|
||||||
|
explicit_super = sub_module->supertype(explicit_super);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,14 +27,16 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
|
|||||||
// - Two numeric types are equivalent iff they are equal.
|
// - Two numeric types are equivalent iff they are equal.
|
||||||
// - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
|
// - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
|
||||||
// Equivalence of heap types ht1 ~ ht2 is defined as follows:
|
// Equivalence of heap types ht1 ~ ht2 is defined as follows:
|
||||||
// - Two heap types are equivalent iff they are equal.
|
// - Two non-index heap types are equivalent iff they are equal.
|
||||||
// - TODO(7748): Implement iso-recursive canonicalization.
|
// - Two indexed heap types are equivalent iff they are iso-recursive
|
||||||
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
|
// equivalent.
|
||||||
const WasmModule* module1,
|
V8_NOINLINE V8_EXPORT_PRIVATE bool EquivalentTypes(ValueType type1,
|
||||||
const WasmModule* module2);
|
ValueType type2,
|
||||||
|
const WasmModule* module1,
|
||||||
|
const WasmModule* module2);
|
||||||
|
|
||||||
// Checks if subtype, defined in module1, is a subtype of supertype, defined in
|
// Checks if {subtype}, defined in {module1}, is a subtype of {supertype},
|
||||||
// module2.
|
// defined in {module2}.
|
||||||
// Subtyping between value types is described by the following rules
|
// Subtyping between value types is described by the following rules
|
||||||
// (structural subtyping):
|
// (structural subtyping):
|
||||||
// - numeric types are subtype-related iff they are equal.
|
// - numeric types are subtype-related iff they are equal.
|
||||||
@ -54,7 +56,7 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
|
|||||||
// - All structs are subtypes of data.
|
// - All structs are subtypes of data.
|
||||||
// - All arrays are subtypes of array.
|
// - All arrays are subtypes of array.
|
||||||
// - An indexed heap type h1 is a subtype of indexed heap type h2 if h2 is
|
// - An indexed heap type h1 is a subtype of indexed heap type h2 if h2 is
|
||||||
// transitively an explicit supertype of h1.
|
// transitively an explicit canonical supertype of h1.
|
||||||
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
|
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
|
||||||
const WasmModule* sub_module,
|
const WasmModule* sub_module,
|
||||||
const WasmModule* super_module) {
|
const WasmModule* super_module) {
|
||||||
@ -62,7 +64,7 @@ V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
|
|||||||
return IsSubtypeOfImpl(subtype, supertype, sub_module, super_module);
|
return IsSubtypeOfImpl(subtype, supertype, sub_module, super_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if 'subtype' is a subtype of 'supertype' (both defined in module).
|
// Checks if {subtype} is a subtype of {supertype} (both defined in {module}).
|
||||||
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
|
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
|
||||||
const WasmModule* module) {
|
const WasmModule* module) {
|
||||||
// If the types are trivially identical, exit early.
|
// If the types are trivially identical, exit early.
|
||||||
|
@ -1304,11 +1304,14 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
|
|||||||
tester.CheckResult(kZeroLength, 0); // Does not throw.
|
tester.CheckResult(kZeroLength, 0); // Does not throw.
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(7748): This test requires for recursive groups.
|
|
||||||
WASM_COMPILED_EXEC_TEST(NewDefault) {
|
WASM_COMPILED_EXEC_TEST(NewDefault) {
|
||||||
WasmGCTester tester(execution_tier);
|
WasmGCTester tester(execution_tier);
|
||||||
|
|
||||||
|
tester.builder()->StartRecursiveTypeGroup();
|
||||||
const byte struct_type = tester.DefineStruct(
|
const byte struct_type = tester.DefineStruct(
|
||||||
{F(wasm::kWasmI32, true), F(wasm::kWasmF64, true), F(optref(0), true)});
|
{F(wasm::kWasmI32, true), F(wasm::kWasmF64, true), F(optref(0), true)});
|
||||||
|
tester.builder()->EndRecursiveTypeGroup();
|
||||||
|
|
||||||
const byte array_type = tester.DefineArray(wasm::kWasmI32, true);
|
const byte array_type = tester.DefineArray(wasm::kWasmI32, true);
|
||||||
// Returns: struct[0] + f64_to_i32(struct[1]) + (struct[2].is_null ^ 1) == 0.
|
// Returns: struct[0] + f64_to_i32(struct[1]) + (struct[2].is_null ^ 1) == 0.
|
||||||
const byte allocate_struct = tester.DefineFunction(
|
const byte allocate_struct = tester.DefineFunction(
|
||||||
@ -1338,7 +1341,6 @@ WASM_COMPILED_EXEC_TEST(NewDefault) {
|
|||||||
tester.CheckResult(allocate_struct, 0);
|
tester.CheckResult(allocate_struct, 0);
|
||||||
tester.CheckResult(allocate_array, 0);
|
tester.CheckResult(allocate_array, 0);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
WASM_COMPILED_EXEC_TEST(BasicRtt) {
|
WASM_COMPILED_EXEC_TEST(BasicRtt) {
|
||||||
WasmGCTester tester(execution_tier);
|
WasmGCTester tester(execution_tier);
|
||||||
|
@ -96,11 +96,10 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
|
|
||||||
print("--imported function from another module--");
|
print("--imported function from another module--");
|
||||||
assertEquals(57, instance.exports.test_wasm_import());
|
assertEquals(57, instance.exports.test_wasm_import());
|
||||||
/* TODO(7748): Implement cross-module type canonicalization.
|
|
||||||
print("--not imported function defined in another module--");
|
print("--not imported function defined in another module--");
|
||||||
assertEquals(19, instance.exports.main(
|
assertEquals(19, instance.exports.main(
|
||||||
exporting_instance.exports.addition, 12, 7));
|
exporting_instance.exports.addition, 12, 7));
|
||||||
*/
|
|
||||||
print("--imported WebAssembly.Function--")
|
print("--imported WebAssembly.Function--")
|
||||||
assertEquals(21, instance.exports.test_js_api_import());
|
assertEquals(21, instance.exports.test_js_api_import());
|
||||||
print("--not imported WebAssembly.Function--")
|
print("--not imported WebAssembly.Function--")
|
||||||
|
@ -35,9 +35,8 @@ var importing_module = function(imported_function) {
|
|||||||
return builder.instantiate({other: {func: imported_function}});
|
return builder.instantiate({other: {func: imported_function}});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(7748): Implement cross-module subtyping.
|
|
||||||
// Same form/different index should be fine.
|
// Same form/different index should be fine.
|
||||||
// importing_module(exporting_module.exports.func2);
|
importing_module(exporting_module.exports.func2);
|
||||||
// Same index/different form should throw.
|
// Same index/different form should throw.
|
||||||
assertThrows(
|
assertThrows(
|
||||||
() => importing_module(exporting_module.exports.func1),
|
() => importing_module(exporting_module.exports.func1),
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||||
|
|
||||||
/* TODO(7748): Implement cross-module subtyping.
|
|
||||||
(function TestReferenceGlobals() {
|
(function TestReferenceGlobals() {
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
|
|
||||||
@ -106,7 +105,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
// The correct function reference has been passed.
|
// The correct function reference has been passed.
|
||||||
assertEquals(66, instance.exports.test_import(42, 24));
|
assertEquals(66, instance.exports.test_import(42, 24));
|
||||||
})();
|
})();
|
||||||
*/
|
|
||||||
|
|
||||||
(function TestStructInitExpr() {
|
(function TestStructInitExpr() {
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
// Flags: --experimental-wasm-gc
|
// Flags: --experimental-wasm-gc
|
||||||
|
|
||||||
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||||
/* TODO(7748): Implement cross-module subtyping.
|
|
||||||
(function TestTables() {
|
(function TestTables() {
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
var exporting_instance = (function() {
|
var exporting_instance = (function() {
|
||||||
@ -100,9 +99,8 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
|||||||
assertThrows(
|
assertThrows(
|
||||||
() => instance.exports.table.set(0, exporting_instance.exports.addition),
|
() => instance.exports.table.set(0, exporting_instance.exports.addition),
|
||||||
TypeError,
|
TypeError,
|
||||||
/Argument 1 must be null or a WebAssembly function of type compatible to/);
|
/Argument 1 is invalid for table of type \(ref null 0\)/);
|
||||||
})();
|
})();
|
||||||
*/
|
|
||||||
|
|
||||||
(function TestNonNullableTables() {
|
(function TestNonNullableTables() {
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
|
@ -155,7 +155,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
assertEquals(8, instance.exports.main(10, 0));
|
assertEquals(8, instance.exports.main(10, 0));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/* TODO(7748): Implement cross-module subtyping.
|
|
||||||
(function CallRefImportedFunction() {
|
(function CallRefImportedFunction() {
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
|
|
||||||
@ -196,7 +195,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
// The function f1 defined in another module should not be inlined.
|
// The function f1 defined in another module should not be inlined.
|
||||||
assertEquals(1, instance2.exports.main(0, instance1.exports.f1));
|
assertEquals(1, instance2.exports.main(0, instance1.exports.f1));
|
||||||
})();
|
})();
|
||||||
*/
|
|
||||||
|
|
||||||
// Check that we handle WasmJSFunctions properly and do not inline them, both
|
// Check that we handle WasmJSFunctions properly and do not inline them, both
|
||||||
// in the monomorphic and polymorphic case.
|
// in the monomorphic and polymorphic case.
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "src/objects/objects-inl.h"
|
#include "src/objects/objects-inl.h"
|
||||||
#include "src/objects/objects.h"
|
#include "src/objects/objects.h"
|
||||||
#include "src/utils/ostreams.h"
|
#include "src/utils/ostreams.h"
|
||||||
|
#include "src/wasm/canonical-types.h"
|
||||||
#include "src/wasm/function-body-decoder-impl.h"
|
#include "src/wasm/function-body-decoder-impl.h"
|
||||||
#include "src/wasm/leb-helper.h"
|
#include "src/wasm/leb-helper.h"
|
||||||
#include "src/wasm/local-decl-encoder.h"
|
#include "src/wasm/local-decl-encoder.h"
|
||||||
@ -89,6 +90,7 @@ class TestModuleBuilder {
|
|||||||
byte AddSignature(const FunctionSig* sig, uint32_t supertype = kNoSuperType) {
|
byte AddSignature(const FunctionSig* sig, uint32_t supertype = kNoSuperType) {
|
||||||
mod.add_signature(sig, supertype);
|
mod.add_signature(sig, supertype);
|
||||||
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
|
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module(), 1);
|
||||||
return static_cast<byte>(mod.types.size() - 1);
|
return static_cast<byte>(mod.types.size() - 1);
|
||||||
}
|
}
|
||||||
byte AddFunction(const FunctionSig* sig, bool declared = true) {
|
byte AddFunction(const FunctionSig* sig, bool declared = true) {
|
||||||
@ -131,12 +133,14 @@ class TestModuleBuilder {
|
|||||||
type_builder.AddField(field.first, field.second);
|
type_builder.AddField(field.first, field.second);
|
||||||
}
|
}
|
||||||
mod.add_struct_type(type_builder.Build(), supertype);
|
mod.add_struct_type(type_builder.Build(), supertype);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module(), 1);
|
||||||
return static_cast<byte>(mod.types.size() - 1);
|
return static_cast<byte>(mod.types.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte AddArray(ValueType type, bool mutability) {
|
byte AddArray(ValueType type, bool mutability) {
|
||||||
ArrayType* array = mod.signature_zone->New<ArrayType>(type, mutability);
|
ArrayType* array = mod.signature_zone->New<ArrayType>(type, mutability);
|
||||||
mod.add_array_type(array, kNoSuperType);
|
mod.add_array_type(array, kNoSuperType);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module(), 1);
|
||||||
return static_cast<byte>(mod.types.size() - 1);
|
return static_cast<byte>(mod.types.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3639,32 +3643,6 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefaultWithRtt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FunctionBodyDecoderTest, NominalStructSubtyping) {
|
|
||||||
WASM_FEATURE_SCOPE(typed_funcref);
|
|
||||||
WASM_FEATURE_SCOPE(gc);
|
|
||||||
byte structural_type = builder.AddStruct({F(kWasmI32, true)});
|
|
||||||
byte nominal_type = builder.AddStruct({F(kWasmI32, true)});
|
|
||||||
AddLocals(optref(structural_type), 1);
|
|
||||||
AddLocals(optref(nominal_type), 1);
|
|
||||||
// Try to assign a nominally-typed value to a structurally-typed local.
|
|
||||||
ExpectFailure(sigs.v_v(),
|
|
||||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(nominal_type))},
|
|
||||||
kAppendEnd, "expected type (ref null 0)");
|
|
||||||
// Try to assign a structurally-typed value to a nominally-typed local.
|
|
||||||
ExpectFailure(sigs.v_v(),
|
|
||||||
{WASM_LOCAL_SET(
|
|
||||||
1, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
|
||||||
structural_type, WASM_RTT_CANON(structural_type)))},
|
|
||||||
kAppendEnd, "expected type (ref null 1)");
|
|
||||||
// But assigning to the correctly typed local works.
|
|
||||||
ExpectValidates(sigs.v_v(),
|
|
||||||
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW_DEFAULT(nominal_type))});
|
|
||||||
ExpectValidates(sigs.v_v(),
|
|
||||||
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
|
|
||||||
structural_type,
|
|
||||||
WASM_RTT_CANON(structural_type)))});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
|
TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
|
||||||
WASM_FEATURE_SCOPE(typed_funcref);
|
WASM_FEATURE_SCOPE(typed_funcref);
|
||||||
AddLocals(kWasmAnyRef, 1);
|
AddLocals(kWasmAnyRef, 1);
|
||||||
|
@ -166,10 +166,12 @@ namespace module_decoder_unittest {
|
|||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define EXPECT_NOT_OK(result, msg) \
|
#define EXPECT_NOT_OK(result, msg) \
|
||||||
do { \
|
do { \
|
||||||
EXPECT_FALSE(result.ok()); \
|
EXPECT_FALSE(result.ok()); \
|
||||||
EXPECT_THAT(result.error().message(), HasSubstr(msg)); \
|
if (!result.ok()) { \
|
||||||
|
EXPECT_THAT(result.error().message(), HasSubstr(msg)); \
|
||||||
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
static size_t SizeOfVarInt(size_t value) {
|
static size_t SizeOfVarInt(size_t value) {
|
||||||
@ -803,7 +805,7 @@ TEST_F(WasmModuleVerifyTest, RttCanonGlobalTypeError) {
|
|||||||
static const byte data[] = {
|
static const byte data[] = {
|
||||||
SECTION(Type, ENTRY_COUNT(2),
|
SECTION(Type, ENTRY_COUNT(2),
|
||||||
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
|
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
|
||||||
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
|
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI64Code, true))),
|
||||||
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(0), 1, WASM_RTT_CANON(1),
|
SECTION(Global, ENTRY_COUNT(1), WASM_RTT(0), 1, WASM_RTT_CANON(1),
|
||||||
kExprEnd)};
|
kExprEnd)};
|
||||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||||
@ -1189,6 +1191,46 @@ TEST_F(WasmModuleVerifyTest, InvalidArrayTypeDef) {
|
|||||||
EXPECT_VERIFIES(immutable);
|
EXPECT_VERIFIES(immutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WasmModuleVerifyTest, TypeCanonicalization) {
|
||||||
|
WASM_FEATURE_SCOPE(typed_funcref);
|
||||||
|
WASM_FEATURE_SCOPE(gc);
|
||||||
|
FLAG_SCOPE(wasm_type_canonicalization);
|
||||||
|
static const byte identical_group[] = {
|
||||||
|
SECTION(Type, // --
|
||||||
|
ENTRY_COUNT(2), // two identical rec. groups
|
||||||
|
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(1), // --
|
||||||
|
kWasmArrayTypeCode, kI32Code, 0, // --
|
||||||
|
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(1), // --
|
||||||
|
kWasmArrayTypeCode, kI32Code, 0),
|
||||||
|
SECTION(Global, // --
|
||||||
|
ENTRY_COUNT(1), kRefCode, 0, 0, // Type, mutability
|
||||||
|
WASM_ARRAY_INIT_STATIC(1, 1, WASM_I32V(10)),
|
||||||
|
kExprEnd) // Init. expression
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global initializer should verify as identical type in other group
|
||||||
|
EXPECT_VERIFIES(identical_group);
|
||||||
|
|
||||||
|
static const byte non_identical_group[] = {
|
||||||
|
SECTION(Type, // --
|
||||||
|
ENTRY_COUNT(2), // two distrinct rec. groups
|
||||||
|
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(1), // --
|
||||||
|
kWasmArrayTypeCode, kI32Code, 0, // --
|
||||||
|
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(2), // --
|
||||||
|
kWasmArrayTypeCode, kI32Code, 0, // --
|
||||||
|
kWasmStructTypeCode, ENTRY_COUNT(0)),
|
||||||
|
SECTION(Global, // --
|
||||||
|
ENTRY_COUNT(1), kRefCode, 0, 0, // Type, mutability
|
||||||
|
WASM_ARRAY_INIT_STATIC(1, 1, WASM_I32V(10)),
|
||||||
|
kExprEnd) // Init. expression
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global initializer should not verify as type in distinct rec. group.
|
||||||
|
EXPECT_FAILURE_WITH_MSG(
|
||||||
|
non_identical_group,
|
||||||
|
"type error in init. expression[0] (expected (ref 0), got (ref 1))");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
|
TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
|
||||||
static const byte data[] = {SECTION(Tag, ENTRY_COUNT(0))};
|
static const byte data[] = {SECTION(Tag, ENTRY_COUNT(0))};
|
||||||
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
||||||
@ -3389,13 +3431,13 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
|
|||||||
EXPECT_NOT_OK(result, "data segments count 0 mismatch (1 expected)");
|
EXPECT_NOT_OK(result, "data segments count 0 mismatch (1 expected)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(7748): Add support for rec. groups.
|
|
||||||
TEST_F(WasmModuleVerifyTest, GcStructIdsPass) {
|
TEST_F(WasmModuleVerifyTest, GcStructIdsPass) {
|
||||||
WASM_FEATURE_SCOPE(gc);
|
WASM_FEATURE_SCOPE(gc);
|
||||||
WASM_FEATURE_SCOPE(typed_funcref);
|
WASM_FEATURE_SCOPE(typed_funcref);
|
||||||
|
|
||||||
static const byte data[] = {SECTION(
|
static const byte data[] = {SECTION(
|
||||||
Type, ENTRY_COUNT(3),
|
Type, ENTRY_COUNT(1), // One recursive group...
|
||||||
|
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(3), // with three entries.
|
||||||
WASM_STRUCT_DEF(FIELD_COUNT(3), STRUCT_FIELD(kI32Code, true),
|
WASM_STRUCT_DEF(FIELD_COUNT(3), STRUCT_FIELD(kI32Code, true),
|
||||||
STRUCT_FIELD(WASM_OPT_REF(0), true),
|
STRUCT_FIELD(WASM_OPT_REF(0), true),
|
||||||
STRUCT_FIELD(WASM_OPT_REF(1), true)),
|
STRUCT_FIELD(WASM_OPT_REF(1), true)),
|
||||||
@ -3404,7 +3446,7 @@ TEST_F(WasmModuleVerifyTest, GcStructIdsPass) {
|
|||||||
WASM_ARRAY_DEF(WASM_OPT_REF(0), true))};
|
WASM_ARRAY_DEF(WASM_OPT_REF(0), true))};
|
||||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||||
EXPECT_OK(result);
|
EXPECT_OK(result);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
TEST_F(WasmModuleVerifyTest, OutOfBoundsTypeInGlobal) {
|
TEST_F(WasmModuleVerifyTest, OutOfBoundsTypeInGlobal) {
|
||||||
WASM_FEATURE_SCOPE(typed_funcref);
|
WASM_FEATURE_SCOPE(typed_funcref);
|
||||||
@ -3424,7 +3466,6 @@ TEST_F(WasmModuleVerifyTest, OutOfBoundsTypeInType) {
|
|||||||
EXPECT_NOT_OK(result, "Type index 1 is out of bounds");
|
EXPECT_NOT_OK(result, "Type index 1 is out of bounds");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(7748): Add support for rec. groups.
|
|
||||||
TEST_F(WasmModuleVerifyTest, ForwardSupertype) {
|
TEST_F(WasmModuleVerifyTest, ForwardSupertype) {
|
||||||
WASM_FEATURE_SCOPE(typed_funcref);
|
WASM_FEATURE_SCOPE(typed_funcref);
|
||||||
WASM_FEATURE_SCOPE(gc);
|
WASM_FEATURE_SCOPE(gc);
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "src/wasm/canonical-types.h"
|
||||||
#include "src/wasm/wasm-subtyping.h"
|
#include "src/wasm/wasm-subtyping.h"
|
||||||
|
#include "test/common/flag-utils.h"
|
||||||
#include "test/common/wasm/flag-utils.h"
|
#include "test/common/wasm/flag-utils.h"
|
||||||
#include "test/unittests/test-utils.h"
|
#include "test/unittests/test-utils.h"
|
||||||
|
|
||||||
@ -25,29 +27,41 @@ FieldInit mut(ValueType type) { return FieldInit(type, true); }
|
|||||||
FieldInit immut(ValueType type) { return FieldInit(type, false); }
|
FieldInit immut(ValueType type) { return FieldInit(type, false); }
|
||||||
|
|
||||||
void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields,
|
void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields,
|
||||||
uint32_t supertype = kNoSuperType) {
|
uint32_t supertype = kNoSuperType,
|
||||||
|
bool in_singleton_rec_group = true) {
|
||||||
StructType::Builder builder(module->signature_zone.get(),
|
StructType::Builder builder(module->signature_zone.get(),
|
||||||
static_cast<uint32_t>(fields.size()));
|
static_cast<uint32_t>(fields.size()));
|
||||||
for (FieldInit field : fields) {
|
for (FieldInit field : fields) {
|
||||||
builder.AddField(field.first, field.second);
|
builder.AddField(field.first, field.second);
|
||||||
}
|
}
|
||||||
return module->add_struct_type(builder.Build(), supertype);
|
module->add_struct_type(builder.Build(), supertype);
|
||||||
|
if (in_singleton_rec_group) {
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefineArray(WasmModule* module, FieldInit element_type,
|
void DefineArray(WasmModule* module, FieldInit element_type,
|
||||||
uint32_t supertype = kNoSuperType) {
|
uint32_t supertype = kNoSuperType,
|
||||||
|
bool in_singleton_rec_group = true) {
|
||||||
module->add_array_type(module->signature_zone->New<ArrayType>(
|
module->add_array_type(module->signature_zone->New<ArrayType>(
|
||||||
element_type.first, element_type.second),
|
element_type.first, element_type.second),
|
||||||
supertype);
|
supertype);
|
||||||
|
if (in_singleton_rec_group) {
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefineSignature(WasmModule* module,
|
void DefineSignature(WasmModule* module,
|
||||||
std::initializer_list<ValueType> params,
|
std::initializer_list<ValueType> params,
|
||||||
std::initializer_list<ValueType> returns,
|
std::initializer_list<ValueType> returns,
|
||||||
uint32_t supertype = kNoSuperType) {
|
uint32_t supertype = kNoSuperType,
|
||||||
|
bool in_singleton_rec_group = true) {
|
||||||
module->add_signature(
|
module->add_signature(
|
||||||
FunctionSig::Build(module->signature_zone.get(), returns, params),
|
FunctionSig::Build(module->signature_zone.get(), returns, params),
|
||||||
supertype);
|
supertype);
|
||||||
|
if (in_singleton_rec_group) {
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WasmSubtypingTest, Subtyping) {
|
TEST_F(WasmSubtypingTest, Subtyping) {
|
||||||
@ -79,6 +93,37 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
|||||||
/* 14 */ DefineSignature(module, {ref(0)}, {kWasmI32}, 13);
|
/* 14 */ DefineSignature(module, {ref(0)}, {kWasmI32}, 13);
|
||||||
/* 15 */ DefineSignature(module, {ref(0)}, {ref(4)}, 16);
|
/* 15 */ DefineSignature(module, {ref(0)}, {ref(4)}, 16);
|
||||||
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
|
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
|
||||||
|
/* 17 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(17))});
|
||||||
|
|
||||||
|
// Rec. group.
|
||||||
|
/* 18 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(17))}, 17,
|
||||||
|
false);
|
||||||
|
/* 19 */ DefineArray(module, {mut(optRef(21))}, kNoSuperType, false);
|
||||||
|
/* 20 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
|
||||||
|
false);
|
||||||
|
/* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 4);
|
||||||
|
|
||||||
|
// Identical rec. group.
|
||||||
|
/* 22 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(17))}, 17,
|
||||||
|
false);
|
||||||
|
/* 23 */ DefineArray(module, {mut(optRef(25))}, kNoSuperType, false);
|
||||||
|
/* 24 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
|
||||||
|
false);
|
||||||
|
/* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 24, false);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 4);
|
||||||
|
|
||||||
|
// Nonidentical rec. group: the last function extends a type outside the
|
||||||
|
// recursive group.
|
||||||
|
/* 26 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(17))}, 17,
|
||||||
|
false);
|
||||||
|
/* 27 */ DefineArray(module, {mut(optRef(29))}, kNoSuperType, false);
|
||||||
|
/* 28 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
|
||||||
|
false);
|
||||||
|
/* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false);
|
||||||
|
TypeCanonicalizer::instance()->AddRecursiveGroup(module, 4);
|
||||||
|
|
||||||
|
/* 30 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(18))}, 18);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
|
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
|
||||||
@ -88,6 +133,7 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
|||||||
optRef(0), ref(0), optRef(2),
|
optRef(0), ref(0), optRef(2),
|
||||||
ref(2), optRef(11), ref(11)};
|
ref(2), optRef(11), ref(11)};
|
||||||
|
|
||||||
|
// Some macros to help managing types and modules.
|
||||||
#define SUBTYPE(type1, type2) \
|
#define SUBTYPE(type1, type2) \
|
||||||
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
|
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
|
||||||
#define SUBTYPE_IFF(type1, type2, condition) \
|
#define SUBTYPE_IFF(type1, type2, condition) \
|
||||||
@ -102,10 +148,20 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
|||||||
#define NOT_VALID_SUBTYPE(type1, type2) \
|
#define NOT_VALID_SUBTYPE(type1, type2) \
|
||||||
EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
|
EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
|
||||||
module1, module));
|
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));
|
||||||
|
|
||||||
|
for (WasmModule* module : {module1, module2}) {
|
||||||
|
// For cross module subtyping, we need to enable type canonicalization.
|
||||||
|
// Type judgements across modules should work the same as within one module.
|
||||||
|
FLAG_VALUE_SCOPE(wasm_type_canonicalization, module == module2);
|
||||||
|
|
||||||
// Type judgements across modules should work the same as within one module.
|
|
||||||
// TODO(7748): add module2 once we have a cross-module story.
|
|
||||||
for (WasmModule* module : {module1 /* , module2 */}) {
|
|
||||||
// Value types are unrelated, except if they are equal.
|
// Value types are unrelated, except if they are equal.
|
||||||
for (ValueType subtype : numeric_types) {
|
for (ValueType subtype : numeric_types) {
|
||||||
for (ValueType supertype : numeric_types) {
|
for (ValueType supertype : numeric_types) {
|
||||||
@ -183,9 +239,6 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
|||||||
SUBTYPE(ValueType::Rtt(5), ValueType::Rtt(5));
|
SUBTYPE(ValueType::Rtt(5), ValueType::Rtt(5));
|
||||||
// Rtts of unrelated types are unrelated.
|
// Rtts of unrelated types are unrelated.
|
||||||
NOT_SUBTYPE(ValueType::Rtt(1), ValueType::Rtt(2));
|
NOT_SUBTYPE(ValueType::Rtt(1), ValueType::Rtt(2));
|
||||||
// Rtts of identical types are subtype-related.
|
|
||||||
// TODO(7748): Implement type canonicalization.
|
|
||||||
// SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(9));
|
|
||||||
// Rtts of subtypes are not related.
|
// Rtts of subtypes are not related.
|
||||||
NOT_SUBTYPE(ValueType::Rtt(1), ValueType::Rtt(0));
|
NOT_SUBTYPE(ValueType::Rtt(1), ValueType::Rtt(0));
|
||||||
|
|
||||||
@ -201,10 +254,42 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
|||||||
// Identical types are subtype-related.
|
// Identical types are subtype-related.
|
||||||
VALID_SUBTYPE(ref(10), ref(10));
|
VALID_SUBTYPE(ref(10), ref(10));
|
||||||
VALID_SUBTYPE(ref(11), ref(11));
|
VALID_SUBTYPE(ref(11), ref(11));
|
||||||
|
|
||||||
|
{
|
||||||
|
// Canonicalization tests.
|
||||||
|
FLAG_SCOPE(wasm_type_canonicalization);
|
||||||
|
|
||||||
|
// Groups should only be canonicalized to identical groups.
|
||||||
|
IDENTICAL(18, 22);
|
||||||
|
IDENTICAL(19, 23);
|
||||||
|
IDENTICAL(20, 24);
|
||||||
|
IDENTICAL(21, 25);
|
||||||
|
|
||||||
|
DISTINCT(18, 26);
|
||||||
|
DISTINCT(19, 27);
|
||||||
|
DISTINCT(20, 28);
|
||||||
|
DISTINCT(21, 29);
|
||||||
|
|
||||||
|
// A type should not be canonicalized to an identical one with a different
|
||||||
|
// group structure.
|
||||||
|
DISTINCT(18, 17);
|
||||||
|
|
||||||
|
// A subtype should also be subtype of an equivalent type.
|
||||||
|
VALID_SUBTYPE(ref(30), ref(18));
|
||||||
|
VALID_SUBTYPE(ref(30), ref(22));
|
||||||
|
NOT_SUBTYPE(ref(30), ref(26));
|
||||||
|
|
||||||
|
// Rtts of identical types are subtype-related.
|
||||||
|
SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#undef SUBTYPE
|
#undef SUBTYPE
|
||||||
#undef NOT_SUBTYPE
|
#undef NOT_SUBTYPE
|
||||||
#undef SUBTYPE_IFF
|
#undef SUBTYPE_IFF
|
||||||
|
#undef VALID_SUBTYPE
|
||||||
|
#undef NOT_VALID_SUBTYPE
|
||||||
|
#undef IDENTICAL
|
||||||
|
#undef DISTINCT
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace subtyping_unittest
|
} // namespace subtyping_unittest
|
||||||
|
Loading…
Reference in New Issue
Block a user