Commit 52ce0774 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Fix and complete non-trivial Global initializers

In addition to decoding them, we also have to evaluate the initializer
instructions when instantiating a module.
Drive-by fix: use "big-endian" encoding (prefix comes first) when
emitting initializers in the module builder.

Bug: v8:7748
Change-Id: Idfa0f5db298a8f6c6100fc09e1984e4a2e170e4a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2298004
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68866}
parent 40e10b34
......@@ -5272,16 +5272,7 @@ Node* ArrayLength(GraphAssembler* gasm, Node* array) {
Node* WasmGraphBuilder::StructNew(uint32_t struct_index,
const wasm::StructType* type,
Vector<Node*> fields) {
// This logic is duplicated from module-instantiate.cc.
// TODO(jkummerow): Find a nicer solution.
int map_index = 0;
const std::vector<uint8_t>& type_kinds = env_->module->type_kinds;
for (uint32_t i = 0; i < struct_index; i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
map_index++;
}
}
int map_index = wasm::GetCanonicalRttIndex(env_->module, struct_index);
Node* s = CALL_BUILTIN(
WasmAllocateStruct,
graph()->NewNode(mcgraph()->common()->NumberConstant(map_index)),
......@@ -5307,17 +5298,7 @@ Node* WasmGraphBuilder::StructNewWithRtt(uint32_t struct_index,
Node* WasmGraphBuilder::ArrayNew(uint32_t array_index,
const wasm::ArrayType* type, Node* length,
Node* initial_value) {
// This logic is duplicated from module-instantiate.cc.
// TODO(jkummerow): Find a nicer solution.
int map_index = 0;
const std::vector<uint8_t>& type_kinds = env_->module->type_kinds;
for (uint32_t i = 0; i < array_index; i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
map_index++;
}
}
int map_index = GetCanonicalRttIndex(env_->module, array_index);
wasm::ValueType element_type = type->element_type();
Node* a = CALL_BUILTIN(
WasmAllocateArray,
......@@ -5377,19 +5358,10 @@ Node* WasmGraphBuilder::RttCanon(wasm::HeapType type) {
UNREACHABLE();
}
}
// This logic is duplicated from module-instantiate.cc.
// TODO(jkummerow): Find a nicer solution.
int map_index = 0;
const std::vector<uint8_t>& type_kinds = env_->module->type_kinds;
for (uint32_t i = 0; i < type.ref_index(); i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
map_index++;
}
}
int map_index = wasm::GetCanonicalRttIndex(env_->module, type.ref_index());
Node* maps_list =
LOAD_INSTANCE_FIELD(ManagedObjectMaps, MachineType::TaggedPointer());
return LOAD_FIXED_ARRAY_SLOT_PTR(maps_list, type.ref_index());
return LOAD_FIXED_ARRAY_SLOT_PTR(maps_list, map_index);
}
Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) {
......
......@@ -493,65 +493,15 @@ RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
return ReadOnlyRoots(isolate).undefined_value();
}
namespace {
// TODO(7748): Consider storing this array in Maps'
// "transitions_or_prototype_info" slot.
// Also consider being more memory-efficient, e.g. use inline storage for
// single entries, and/or adapt the growth strategy.
class RttSubtypes : public ArrayList {
public:
static Handle<ArrayList> Insert(Isolate* isolate, Handle<ArrayList> array,
uint32_t type_index, Handle<Map> sub_rtt) {
Handle<Smi> key = handle(Smi::FromInt(type_index), isolate);
return Add(isolate, array, key, sub_rtt);
}
static Map SearchSubtype(Handle<ArrayList> array, uint32_t type_index) {
// Linear search for now.
// TODO(7748): Consider keeping the array sorted and using binary search
// here, if empirical data indicates that that would be worthwhile.
int count = array->Length();
for (int i = 0; i < count; i += 2) {
if (Smi::cast(array->Get(i)).value() == static_cast<int>(type_index)) {
return Map::cast(array->Get(i + 1));
}
}
return {};
}
};
} // namespace
RUNTIME_FUNCTION(Runtime_WasmAllocateRtt) {
ClearThreadInWasmScope flag_scope;
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_UINT32_ARG_CHECKED(type_index, 0);
CONVERT_ARG_HANDLE_CHECKED(Map, parent, 1);
// Check for an existing RTT first.
DCHECK(parent->IsWasmStructMap() || parent->IsWasmArrayMap());
Handle<ArrayList> cache(parent->wasm_type_info().subtypes(), isolate);
Map maybe_cached = RttSubtypes::SearchSubtype(cache, type_index);
if (!maybe_cached.is_null()) return maybe_cached;
// Allocate a fresh RTT otherwise.
Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
isolate);
const wasm::WasmModule* module = instance->module();
Handle<Map> rtt;
if (wasm::HeapType(type_index).is_generic()) {
rtt = wasm::CreateGenericRtt(isolate, module, parent);
} else if (module->has_struct(type_index)) {
rtt = wasm::CreateStructMap(isolate, module, type_index, parent);
} else if (module->has_array(type_index)) {
rtt = wasm::CreateArrayMap(isolate, module, type_index, parent);
} else {
UNREACHABLE();
}
cache = RttSubtypes::Insert(isolate, cache, type_index, rtt);
parent->wasm_type_info().set_subtypes(*cache);
return *rtt;
return *wasm::AllocateSubRtt(isolate, instance, type_index, parent);
}
} // namespace internal
......
......@@ -136,6 +136,62 @@ Handle<Map> CreateGenericRtt(Isolate* isolate, const WasmModule* module,
return map;
}
namespace {
// TODO(7748): Consider storing this array in Maps'
// "transitions_or_prototype_info" slot.
// Also consider being more memory-efficient, e.g. use inline storage for
// single entries, and/or adapt the growth strategy.
class RttSubtypes : public ArrayList {
public:
static Handle<ArrayList> Insert(Isolate* isolate, Handle<ArrayList> array,
uint32_t type_index, Handle<Map> sub_rtt) {
Handle<Smi> key = handle(Smi::FromInt(type_index), isolate);
return Add(isolate, array, key, sub_rtt);
}
static Map SearchSubtype(Handle<ArrayList> array, uint32_t type_index) {
// Linear search for now.
// TODO(7748): Consider keeping the array sorted and using binary search
// here, if empirical data indicates that that would be worthwhile.
int count = array->Length();
for (int i = 0; i < count; i += 2) {
if (Smi::cast(array->Get(i)).value() == static_cast<int>(type_index)) {
return Map::cast(array->Get(i + 1));
}
}
return {};
}
};
} // namespace
Handle<Map> AllocateSubRtt(Isolate* isolate,
Handle<WasmInstanceObject> instance, uint32_t type,
Handle<Map> parent) {
// Check for an existing RTT first.
DCHECK(parent->IsWasmStructMap() || parent->IsWasmArrayMap());
Handle<ArrayList> cache(parent->wasm_type_info().subtypes(), isolate);
Map maybe_cached = RttSubtypes::SearchSubtype(cache, type);
if (!maybe_cached.is_null()) return handle(maybe_cached, isolate);
// Allocate a fresh RTT otherwise.
const wasm::WasmModule* module = instance->module();
Handle<Map> rtt;
if (wasm::HeapType(type).is_generic()) {
rtt = wasm::CreateGenericRtt(isolate, module, parent);
} else if (module->has_struct(type)) {
rtt = wasm::CreateStructMap(isolate, module, type, parent);
} else if (module->has_array(type)) {
rtt = wasm::CreateArrayMap(isolate, module, type, parent);
} else {
UNREACHABLE();
}
cache = RttSubtypes::Insert(isolate, cache, type, rtt);
parent->wasm_type_info().set_subtypes(*cache);
return rtt;
}
// A helper class to simplify instantiating a module from a module object.
// It closes over the {Isolate}, the {ErrorThrower}, etc.
class InstanceBuilder {
......@@ -274,6 +330,9 @@ class InstanceBuilder {
// Process initialization of globals.
void InitGlobals(Handle<WasmInstanceObject> instance);
Handle<Object> RecursivelyEvaluateGlobalInitializer(
const WasmInitExpr& init, Handle<WasmInstanceObject> instance);
// Process the exports, creating wrappers for functions, tables, memories,
// and globals.
void ProcessExports(Handle<WasmInstanceObject> instance);
......@@ -503,6 +562,35 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
int num_imported_functions = ProcessImports(instance);
if (num_imported_functions < 0) return {};
//--------------------------------------------------------------------------
// Create maps for managed objects (GC proposal).
// Must happen before {InitGlobals} because globals can refer to these maps.
//--------------------------------------------------------------------------
if (enabled_.has_gc()) {
int count = GetCanonicalRttIndex(
module_, static_cast<uint32_t>(module_->type_kinds.size()));
Handle<FixedArray> maps =
isolate_->factory()->NewUninitializedFixedArray(count);
// TODO(7748): Do we want a different sentinel here?
Handle<Map> anyref_sentinel_map = isolate_->factory()->null_map();
int map_index = 0;
for (int i = 0; i < static_cast<int>(module_->type_kinds.size()); i++) {
if (module_->type_kinds[i] == kWasmStructTypeCode) {
Handle<Map> map =
CreateStructMap(isolate_, module_, i, anyref_sentinel_map);
maps->set(map_index++, *map);
}
if (module_->type_kinds[i] == kWasmArrayTypeCode) {
Handle<Map> map =
CreateArrayMap(isolate_, module_, i, anyref_sentinel_map);
maps->set(map_index++, *map);
}
}
// {GetCanonicalRttIndex} must be in sync with the for-loop above.
DCHECK_EQ(count, map_index);
instance->set_managed_object_maps(*maps);
}
//--------------------------------------------------------------------------
// Process the initialization for the module's globals.
//--------------------------------------------------------------------------
......@@ -583,36 +671,6 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (thrower_->error()) return {};
}
//--------------------------------------------------------------------------
// Create maps for managed objects (GC proposal).
//--------------------------------------------------------------------------
if (enabled_.has_gc()) {
int count = 0;
for (uint8_t type_kind : module_->type_kinds) {
if (type_kind == kWasmStructTypeCode || type_kind == kWasmArrayTypeCode) {
count++;
}
}
Handle<FixedArray> maps =
isolate_->factory()->NewUninitializedFixedArray(count);
// TODO(7748): Do we want a different sentinel here?
Handle<Map> anyref_sentinel_map = isolate_->factory()->null_map();
int map_index = 0;
for (int i = 0; i < static_cast<int>(module_->type_kinds.size()); i++) {
if (module_->type_kinds[i] == kWasmStructTypeCode) {
Handle<Map> map =
CreateStructMap(isolate_, module_, i, anyref_sentinel_map);
maps->set(map_index++, *map);
}
if (module_->type_kinds[i] == kWasmArrayTypeCode) {
Handle<Map> map =
CreateArrayMap(isolate_, module_, i, anyref_sentinel_map);
maps->set(map_index++, *map);
}
}
instance->set_managed_object_maps(*maps);
}
//--------------------------------------------------------------------------
// Create a wrapper for the start function.
//--------------------------------------------------------------------------
......@@ -1402,6 +1460,57 @@ T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
}
Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
const WasmInitExpr& init, Handle<WasmInstanceObject> instance) {
switch (init.kind()) {
case WasmInitExpr::kI32Const:
case WasmInitExpr::kI64Const:
case WasmInitExpr::kF32Const:
case WasmInitExpr::kF64Const:
case WasmInitExpr::kRefNullConst:
case WasmInitExpr::kRefFuncConst:
case WasmInitExpr::kNone:
// Handled directly by {InitGlobals()}, can't occur as recursive case.
UNREACHABLE();
break;
case WasmInitExpr::kGlobalGet: {
// We can only get here for reference-type globals, but we don't have
// enough information to DCHECK that directly.
DCHECK(enabled_.has_reftypes() || enabled_.has_eh());
uint32_t old_offset = module_->globals[init.immediate().index].offset;
DCHECK(static_cast<int>(old_offset) < tagged_globals_->length());
return handle(tagged_globals_->get(old_offset), isolate_);
}
case WasmInitExpr::kRttCanon: {
switch (init.immediate().heap_type) {
case wasm::HeapType::kEq:
return isolate_->root_handle(RootIndex::kWasmRttEqrefMap);
case wasm::HeapType::kExtern:
return isolate_->root_handle(RootIndex::kWasmRttExternrefMap);
case wasm::HeapType::kFunc:
return isolate_->root_handle(RootIndex::kWasmRttFuncrefMap);
case wasm::HeapType::kI31:
return isolate_->root_handle(RootIndex::kWasmRttI31refMap);
case wasm::HeapType::kExn:
UNIMPLEMENTED(); // TODO(jkummerow): This is going away?
case wasm::HeapType::kBottom:
UNREACHABLE();
}
// Non-generic types fall through.
int map_index = GetCanonicalRttIndex(
module_, static_cast<uint32_t>(init.immediate().heap_type));
return handle(instance->managed_object_maps().get(map_index), isolate_);
}
case WasmInitExpr::kRttSub: {
uint32_t type = static_cast<uint32_t>(init.immediate().heap_type);
Handle<Object> parent =
RecursivelyEvaluateGlobalInitializer(*init.operand(), instance);
return AllocateSubRtt(isolate_, instance, type,
Handle<Map>::cast(parent));
}
}
}
// Process initialization of globals.
void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
for (const WasmGlobal& global : module_->globals) {
......@@ -1459,12 +1568,15 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
}
break;
}
case WasmInitExpr::kRttCanon:
case WasmInitExpr::kRttSub:
tagged_globals_->set(
global.offset,
*RecursivelyEvaluateGlobalInitializer(global.init, instance));
break;
case WasmInitExpr::kNone:
// Happens with imported globals.
break;
default:
UNREACHABLE();
break;
}
}
}
......
......@@ -489,14 +489,18 @@ void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init,
break;
}
case WasmInitExpr::kRttCanon:
buffer->write_u16(kExprRttCanon);
buffer->write_u32v(init.immediate().heap_type);
STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprRttCanon));
buffer->write_i32v(HeapType(init.immediate().heap_type).code());
break;
case WasmInitExpr::kRttSub:
// TODO(7748): If immediates for rtts remain in the standard, adapt this
// to emit them.
buffer->write_u16(kExprRttSub);
buffer->write_u32v(init.immediate().heap_type);
STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprRttSub));
buffer->write_i32v(HeapType(init.immediate().heap_type).code());
WriteGlobalInitializer(buffer, *init.operand(), kWasmBottom);
break;
}
......
......@@ -83,6 +83,21 @@ int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
return result;
}
// static
int GetCanonicalRttIndex(const WasmModule* module, uint32_t type_index) {
int rtt_index = 0;
const std::vector<uint8_t>& type_kinds = module->type_kinds;
// This logic must be in sync with the code in module-instantiate.cc that
// initializes the "managed_object_maps" list on the instance.
for (uint32_t i = 0; i < type_index; i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
rtt_index++;
}
}
return rtt_index;
}
// static
int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index) {
const std::vector<WasmFunction>& functions = module->functions;
......
......@@ -403,6 +403,10 @@ V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
bool is_import);
// Returns the index of the canonical RTT of the given struct/array type
// in the instance's list of canonical RTTs.
int GetCanonicalRttIndex(const WasmModule* module, uint32_t type_index);
// Return the byte offset of the function identified by the given index.
// The offset will be relative to the start of the module bytes.
// Returns -1 if the function index is invalid.
......
......@@ -948,6 +948,9 @@ Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
int array_index, Handle<Map> rtt_parent);
Handle<Map> CreateGenericRtt(Isolate* isolate, const WasmModule* module,
Handle<Map> rtt_parent);
Handle<Map> AllocateSubRtt(Isolate* isolate,
Handle<WasmInstanceObject> instance, uint32_t type,
Handle<Map> parent);
} // namespace wasm
......
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