[wasm-gc] Implement runtime-type canonicalization
Add an array of canonical rtts on the isolate. Each wasm instance copies its rtts from there, based on the type index -> canonical index mapping in the module. Bug: v8:7748 Change-Id: I0958686c51ecab15a3215a0da3bee1ad6d543cb3 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3548821 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#79712}
This commit is contained in:
parent
d36f596e8a
commit
c3ed607d63
@ -2183,6 +2183,17 @@ void Heap::CheckCollectionRequested() {
|
||||
current_gc_callback_flags_);
|
||||
}
|
||||
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
void Heap::EnsureWasmCanonicalRttsSize(int length) {
|
||||
Handle<WeakArrayList> current_rtts = handle(wasm_canonical_rtts(), isolate_);
|
||||
if (length <= current_rtts->length()) return;
|
||||
Handle<WeakArrayList> result = WeakArrayList::EnsureSpace(
|
||||
isolate(), current_rtts, length, AllocationType::kOld);
|
||||
result->set_length(length);
|
||||
set_wasm_canonical_rtts(*result);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Heap::UpdateSurvivalStatistics(int start_new_space_size) {
|
||||
if (start_new_space_size == 0) return;
|
||||
|
||||
|
@ -783,6 +783,12 @@ class Heap {
|
||||
std::min(max_old_generation_size(), std::max(heap_limit, min_limit)));
|
||||
}
|
||||
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
// TODO(manoskouk): Inline this if STRONG_MUTABLE_MOVABLE_ROOT_LIST setters
|
||||
// become public.
|
||||
void EnsureWasmCanonicalRttsSize(int length);
|
||||
#endif
|
||||
|
||||
// ===========================================================================
|
||||
// Initialization. ===========================================================
|
||||
// ===========================================================================
|
||||
|
@ -829,6 +829,7 @@ void Heap::CreateInitialObjects() {
|
||||
#ifdef V8_ENABLE_WEBASSEMBLY
|
||||
set_active_continuation(roots.undefined_value());
|
||||
set_active_suspender(roots.undefined_value());
|
||||
set_wasm_canonical_rtts(roots.empty_weak_array_list());
|
||||
#endif // V8_ENABLE_WEBASSEMBLY
|
||||
|
||||
set_script_list(roots.empty_weak_array_list());
|
||||
|
@ -323,7 +323,8 @@ class Symbol;
|
||||
V(ArrayList, basic_block_profiling_data, BasicBlockProfilingData) \
|
||||
V(WeakArrayList, shared_wasm_memories, SharedWasmMemories) \
|
||||
IF_WASM(V, HeapObject, active_continuation, ActiveContinuation) \
|
||||
IF_WASM(V, HeapObject, active_suspender, ActiveSuspender)
|
||||
IF_WASM(V, HeapObject, active_suspender, ActiveSuspender) \
|
||||
IF_WASM(V, WeakArrayList, wasm_canonical_rtts, WasmCanonicalRtts)
|
||||
|
||||
// Entries in this list are limited to Smis and are not visited during GC.
|
||||
#define SMI_ROOT_LIST(V) \
|
||||
|
@ -139,9 +139,6 @@ Handle<DescriptorArray> CreateArrayDescriptorArray(
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(jkummerow): Move these elsewhere.
|
||||
Handle<Map> CreateStructMap(Isolate* isolate, const WasmModule* module,
|
||||
int struct_index, Handle<Map> opt_rtt_parent,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
@ -218,6 +215,24 @@ void CreateMapForType(Isolate* isolate, const WasmModule* module,
|
||||
Handle<FixedArray> maps) {
|
||||
// Recursive calls for supertypes may already have created this map.
|
||||
if (maps->get(type_index).IsMap()) return;
|
||||
|
||||
Handle<WeakArrayList> canonical_rtts;
|
||||
uint32_t canonical_type_index =
|
||||
module->isorecursive_canonical_type_ids[type_index];
|
||||
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
// Try to find the canonical map for this type in the isolate store.
|
||||
canonical_rtts = handle(isolate->heap()->wasm_canonical_rtts(), isolate);
|
||||
DCHECK_GT(static_cast<uint32_t>(canonical_rtts->length()),
|
||||
canonical_type_index);
|
||||
MaybeObject maybe_canonical_map = canonical_rtts->Get(canonical_type_index);
|
||||
if (maybe_canonical_map.IsStrongOrWeak() &&
|
||||
maybe_canonical_map.GetHeapObject().IsMap()) {
|
||||
maps->set(type_index, maybe_canonical_map.GetHeapObject());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Handle<Map> rtt_parent;
|
||||
// If the type with {type_index} has an explicit supertype, make sure the
|
||||
// map for that supertype is created first, so that the supertypes list
|
||||
@ -239,13 +254,17 @@ void CreateMapForType(Isolate* isolate, const WasmModule* module,
|
||||
break;
|
||||
case TypeDefinition::kFunction:
|
||||
// TODO(7748): Create funcref RTTs lazily?
|
||||
// TODO(7748): Canonicalize function maps (cross-module)?
|
||||
map = CreateFuncRefMap(isolate, module, rtt_parent, instance);
|
||||
break;
|
||||
}
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
canonical_rtts->Set(canonical_type_index, HeapObjectReference::Weak(*map));
|
||||
}
|
||||
maps->set(type_index, *map);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// A helper class to simplify instantiating a module from a module object.
|
||||
// It closes over the {Isolate}, the {ErrorThrower}, etc.
|
||||
class InstanceBuilder {
|
||||
@ -643,6 +662,15 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
// list.
|
||||
//--------------------------------------------------------------------------
|
||||
if (enabled_.has_gc()) {
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
uint32_t maximum_canonical_type_index =
|
||||
*std::max_element(module_->isorecursive_canonical_type_ids.begin(),
|
||||
module_->isorecursive_canonical_type_ids.end());
|
||||
// Make sure all canonical indices have been set.
|
||||
DCHECK_NE(maximum_canonical_type_index, kNoSuperType);
|
||||
isolate_->heap()->EnsureWasmCanonicalRttsSize(
|
||||
maximum_canonical_type_index + 1);
|
||||
}
|
||||
Handle<FixedArray> maps = isolate_->factory()->NewFixedArray(
|
||||
static_cast<int>(module_->types.size()));
|
||||
for (uint32_t index = 0; index < module_->types.size(); index++) {
|
||||
|
@ -443,7 +443,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
||||
? signature_map.FindOrInsert(*type.function_sig)
|
||||
: 0;
|
||||
canonicalized_type_ids.push_back(canonical_id);
|
||||
isorecursive_canonical_type_ids.push_back(-1); // Will be computed later.
|
||||
// Canonical type will be computed later.
|
||||
isorecursive_canonical_type_ids.push_back(kNoSuperType);
|
||||
}
|
||||
|
||||
bool has_type(uint32_t index) const { return index < types.size(); }
|
||||
|
@ -1036,14 +1036,6 @@ class WasmSuspenderObject
|
||||
#undef DECL_OPTIONAL_ACCESSORS
|
||||
|
||||
namespace wasm {
|
||||
|
||||
Handle<Map> CreateStructMap(Isolate* isolate, const WasmModule* module,
|
||||
int struct_index, MaybeHandle<Map> rtt_parent,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
|
||||
int array_index, MaybeHandle<Map> rtt_parent,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
Handle<Object> value, ValueType expected,
|
||||
const char** error_message);
|
||||
|
@ -56,6 +56,7 @@ bool IsInitiallyMutable(Factory* factory, Address object_address) {
|
||||
V(retaining_path_targets) \
|
||||
V(serialized_global_proxy_sizes) \
|
||||
V(serialized_objects) \
|
||||
IF_WASM(V, wasm_canonical_rtts) \
|
||||
V(weak_refs_keep_during_job)
|
||||
|
||||
#define TEST_CAN_BE_READ_ONLY(name) \
|
||||
|
@ -39,6 +39,9 @@ class WasmGCTester {
|
||||
execution_tier == TestExecutionTier::kLiftoff),
|
||||
flag_liftoff_only(&v8::internal::FLAG_liftoff_only,
|
||||
execution_tier == TestExecutionTier::kLiftoff),
|
||||
// Test both setups with canonicalization and without.
|
||||
flag_canonicalization(&v8::internal::FLAG_wasm_type_canonicalization,
|
||||
execution_tier == TestExecutionTier::kTurbofan),
|
||||
flag_tierup(&v8::internal::FLAG_wasm_tier_up, false),
|
||||
zone_(&allocator, ZONE_NAME),
|
||||
builder_(&zone_),
|
||||
@ -181,6 +184,7 @@ class WasmGCTester {
|
||||
const FlagScope<bool> flag_typedfuns;
|
||||
const FlagScope<bool> flag_liftoff;
|
||||
const FlagScope<bool> flag_liftoff_only;
|
||||
const FlagScope<bool> flag_canonicalization;
|
||||
const FlagScope<bool> flag_tierup;
|
||||
|
||||
byte DefineFunctionImpl(WasmFunctionBuilder* fun,
|
||||
|
67
test/mjsunit/wasm/runtime-type-canonicalization.js
Normal file
67
test/mjsunit/wasm/runtime-type-canonicalization.js
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
|
||||
// Flags: --expose-gc --experimental-wasm-gc
|
||||
|
||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
let struct_index = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
let identical_struct_index = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
let distinct_struct_index = builder.addStruct([makeField(kWasmI64, true)]);
|
||||
|
||||
let struct_init = builder.addFunction("struct_init",
|
||||
makeSig([], [kWasmDataRef]))
|
||||
.addBody([kGCPrefix, kExprStructNewDefault, struct_index])
|
||||
.exportFunc();
|
||||
let test_pass = builder.addFunction("test_pass",
|
||||
makeSig([kWasmDataRef], [kWasmI32]))
|
||||
.addBody([kExprLocalGet, 0,
|
||||
kGCPrefix, kExprRefTestStatic, identical_struct_index])
|
||||
.exportFunc();
|
||||
let test_fail = builder.addFunction("test_fail",
|
||||
makeSig([kWasmDataRef], [kWasmI32]))
|
||||
.addBody([kExprLocalGet, 0,
|
||||
kGCPrefix, kExprRefTestStatic, distinct_struct_index])
|
||||
.exportFunc();
|
||||
|
||||
(function TestCanonicalizationSameInstance() {
|
||||
print(arguments.callee.name);
|
||||
let instance = builder.instantiate({});
|
||||
assertEquals(1, instance.exports.test_pass(instance.exports.struct_init()));
|
||||
assertEquals(0, instance.exports.test_fail(instance.exports.struct_init()));
|
||||
})();
|
||||
|
||||
(function TestCanonicalizationSameModuleDifferentInstances() {
|
||||
print(arguments.callee.name);
|
||||
let module = builder.toModule();
|
||||
let instance1 = new WebAssembly.Instance(module, {});
|
||||
let instance2 = new WebAssembly.Instance(module, {});
|
||||
assertEquals(1, instance2.exports.test_pass(instance1.exports.struct_init()));
|
||||
assertEquals(0, instance2.exports.test_fail(instance1.exports.struct_init()));
|
||||
})();
|
||||
|
||||
// GC between tests so that the type registry is cleared.
|
||||
gc();
|
||||
|
||||
(function TestCanonicalizationDifferentModules() {
|
||||
print(arguments.callee.name);
|
||||
let instance1 = builder.instantiate({});
|
||||
let instance2 = builder.instantiate({});
|
||||
assertEquals(1, instance2.exports.test_pass(instance1.exports.struct_init()));
|
||||
assertEquals(0, instance2.exports.test_fail(instance1.exports.struct_init()));
|
||||
})();
|
||||
|
||||
(function TestCanonicalizationDifferentModulesAfterGC() {
|
||||
print(arguments.callee.name);
|
||||
let struct = (function make_struct() {
|
||||
return builder.instantiate({}).exports.struct_init();
|
||||
})();
|
||||
// A the live {struct} object keeps the instance alive.
|
||||
gc();
|
||||
let instance = builder.instantiate({});
|
||||
assertEquals(1, instance.exports.test_pass(struct));
|
||||
assertEquals(0, instance.exports.test_fail(struct));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user