From dc3f7f81c95c7c1c61a8eca4c2dfb35d4def1ba6 Mon Sep 17 00:00:00 2001 From: Manos Koukoutos Date: Fri, 4 Jun 2021 12:21:28 +0000 Subject: [PATCH] [wasm] Implement EvaluateInitExpression So far, initializer-expression evaluation was tied to setting global values. We now need it to operate independently of globals, so that we can implement new constant expressions like struct.new, which need their arguments to be computed before they can be initialized. Changes: - Move type computation of WasmInitExpr into WasmInitExpr::type. - Fix WasmInitExpr::type kRttSub case for rtts without depth. - Introduce InstanceBuilder::EvaluateInitExpression(). - Rename InstanceBuilder::GetRawGlobalPointer() -> GetRawUntaggedGlobalPointer(). - Simplify InstanceBuilder::InitGlobals using EvaluateInitExpression(). - Introduce ValueType::is_numeric. - Add Simd128(byte*) constructor. - Introduce WasmValue::CopyTo() for numeric types. Change-Id: Ic502b611f3998187abd9fc6ec377c2954c27abdc Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2939982 Commit-Queue: Manos Koukoutos Reviewed-by: Clemens Backes Cr-Commit-Position: refs/heads/master@{#74949} --- BUILD.gn | 1 + src/wasm/module-decoder.cc | 41 +------- src/wasm/module-instantiate.cc | 185 +++++++++++++++------------------ src/wasm/value-type.h | 14 +++ src/wasm/wasm-init-expr.cc | 58 +++++++++++ src/wasm/wasm-init-expr.h | 6 ++ src/wasm/wasm-value.h | 11 ++ 7 files changed, 173 insertions(+), 143 deletions(-) create mode 100644 src/wasm/wasm-init-expr.cc diff --git a/BUILD.gn b/BUILD.gn index da2e26a739..d54096f80e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -4102,6 +4102,7 @@ v8_source_set("v8_base_without_compiler") { "src/wasm/wasm-external-refs.cc", "src/wasm/wasm-features.cc", "src/wasm/wasm-import-wrapper-cache.cc", + "src/wasm/wasm-init-expr.cc", "src/wasm/wasm-js.cc", "src/wasm/wasm-module-builder.cc", "src/wasm/wasm-module-sourcemap.cc", diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index 4ef0875a00..74e1f3b3fe 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -1421,46 +1421,7 @@ class ModuleDecoderImpl : public Decoder { ModuleOrigin origin_; ValueType TypeOf(const WasmInitExpr& expr) { - switch (expr.kind()) { - case WasmInitExpr::kNone: - return kWasmVoid; - case WasmInitExpr::kGlobalGet: - return expr.immediate().index < module_->globals.size() - ? module_->globals[expr.immediate().index].type - : kWasmVoid; - case WasmInitExpr::kI32Const: - return kWasmI32; - case WasmInitExpr::kI64Const: - return kWasmI64; - case WasmInitExpr::kF32Const: - return kWasmF32; - case WasmInitExpr::kF64Const: - return kWasmF64; - case WasmInitExpr::kS128Const: - return kWasmS128; - case WasmInitExpr::kRefFuncConst: { - uint32_t heap_type = - enabled_features_.has_typed_funcref() - ? module_->functions[expr.immediate().index].sig_index - : HeapType::kFunc; - return ValueType::Ref(heap_type, kNonNullable); - } - case WasmInitExpr::kRefNullConst: - return ValueType::Ref(expr.immediate().heap_type, kNullable); - case WasmInitExpr::kRttCanon: { - return ValueType::Rtt(expr.immediate().heap_type, 0); - } - case WasmInitExpr::kRttSub: - case WasmInitExpr::kRttFreshSub: { - ValueType operand_type = TypeOf(*expr.operand()); - if (operand_type.is_rtt()) { - return ValueType::Rtt(expr.immediate().heap_type, - operand_type.depth() + 1); - } else { - return kWasmVoid; - } - } - } + return expr.type(module_.get(), enabled_features_); } bool has_seen_unordered_section(SectionCode section_code) { diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index c8f3843798..ef2bfb2401 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -426,13 +426,13 @@ class InstanceBuilder { int ProcessImports(Handle instance); template - T* GetRawGlobalPtr(const WasmGlobal& global); + T* GetRawUntaggedGlobalPtr(const WasmGlobal& global); // Process initialization of globals. void InitGlobals(Handle instance); - Handle RecursivelyEvaluateGlobalInitializer( - const WasmInitExpr& init, Handle instance); + WasmValue EvaluateInitExpression(const WasmInitExpr& init, + Handle instance); // Process the exports, creating wrappers for functions, tables, memories, // and globals. @@ -934,7 +934,7 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) { global.type.name().c_str()); switch (global.type.kind()) { case kI32: - WriteLittleEndianValue(GetRawGlobalPtr(global), + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), DoubleToInt32(num)); break; case kI64: @@ -943,11 +943,12 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) { // https://github.com/WebAssembly/JS-BigInt-integration/issues/12 UNREACHABLE(); case kF32: - WriteLittleEndianValue(GetRawGlobalPtr(global), + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), DoubleToFloat32(num)); break; case kF64: - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); break; default: UNREACHABLE(); @@ -959,7 +960,8 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) { raw_buffer_ptr(untagged_globals_, 0), global.offset, num, global.type.name().c_str()); DCHECK_EQ(kWasmI64, global.type); - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); } void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, @@ -969,25 +971,29 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, switch (global.type.kind()) { case kI32: { int32_t num = value->GetI32(); - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); TRACE("%d", num); break; } case kI64: { int64_t num = value->GetI64(); - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); TRACE("%" PRId64, num); break; } case kF32: { float num = value->GetF32(); - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); TRACE("%f", num); break; } case kF64: { double num = value->GetF64(); - WriteLittleEndianValue(GetRawGlobalPtr(global), num); + WriteLittleEndianValue(GetRawUntaggedGlobalPtr(global), + num); TRACE("%lf", num); break; } @@ -1603,45 +1609,78 @@ int InstanceBuilder::ProcessImports(Handle instance) { } template -T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) { +T* InstanceBuilder::GetRawUntaggedGlobalPtr(const WasmGlobal& global) { return reinterpret_cast(raw_buffer_ptr(untagged_globals_, global.offset)); } -Handle InstanceBuilder::RecursivelyEvaluateGlobalInitializer( +WasmValue InstanceBuilder::EvaluateInitExpression( const WasmInitExpr& init, Handle instance) { switch (init.kind()) { - case WasmInitExpr::kI32Const: - case WasmInitExpr::kI64Const: - case WasmInitExpr::kF32Const: - case WasmInitExpr::kF64Const: - case WasmInitExpr::kS128Const: - case WasmInitExpr::kRefNullConst: - case WasmInitExpr::kRefFuncConst: case WasmInitExpr::kNone: - // Handled directly by {InitGlobals()}, can't occur as recursive case. UNREACHABLE(); - break; + case WasmInitExpr::kI32Const: + return WasmValue(init.immediate().i32_const); + case WasmInitExpr::kI64Const: + return WasmValue(init.immediate().i64_const); + case WasmInitExpr::kF32Const: + return WasmValue(init.immediate().f32_const); + case WasmInitExpr::kF64Const: + return WasmValue(init.immediate().f64_const); + case WasmInitExpr::kS128Const: + return WasmValue(Simd128(init.immediate().s128_const.data())); + case WasmInitExpr::kRefNullConst: + return WasmValue(handle(ReadOnlyRoots(isolate_).null_value(), isolate_), + init.type(module_, enabled_)); + case WasmInitExpr::kRefFuncConst: { + auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction( + isolate_, instance, init.immediate().index); + return WasmValue(function, init.type(module_, enabled_)); + } 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(old_offset) < tagged_globals_->length()); - return handle(tagged_globals_->get(old_offset), isolate_); + const WasmGlobal& global = module_->globals[init.immediate().index]; + switch (global.type.kind()) { + case kI32: + return WasmValue(*GetRawUntaggedGlobalPtr(global)); + case kI64: + return WasmValue(*GetRawUntaggedGlobalPtr(global)); + case kF32: + return WasmValue(*GetRawUntaggedGlobalPtr(global)); + case kF64: + return WasmValue(*GetRawUntaggedGlobalPtr(global)); + case kS128: + return WasmValue(Simd128(GetRawUntaggedGlobalPtr(global))); + case kRef: + case kOptRef: + case kRtt: + case kRttWithDepth: { + DCHECK(static_cast(global.offset) < tagged_globals_->length()); + return WasmValue( + handle(tagged_globals_->get(global.offset), isolate_), + init.type(module_, enabled_)); + } + case kI8: + case kI16: + case kBottom: + case kVoid: + UNREACHABLE(); + } } case WasmInitExpr::kRttCanon: { int map_index = init.immediate().index; - return handle(instance->managed_object_maps().get(map_index), isolate_); + return WasmValue( + handle(instance->managed_object_maps().get(map_index), isolate_), + init.type(module_, enabled_)); } case WasmInitExpr::kRttSub: case WasmInitExpr::kRttFreshSub: { uint32_t type = init.immediate().index; - Handle parent = - RecursivelyEvaluateGlobalInitializer(*init.operand(), instance); - return AllocateSubRtt(isolate_, instance, type, Handle::cast(parent), - init.kind() == WasmInitExpr::kRttSub - ? WasmRttSubMode::kCanonicalize - : WasmRttSubMode::kFresh); + WasmValue parent = EvaluateInitExpression(*init.operand(), instance); + return WasmValue(AllocateSubRtt(isolate_, instance, type, + Handle::cast(parent.to_ref()), + init.kind() == WasmInitExpr::kRttSub + ? WasmRttSubMode::kCanonicalize + : WasmRttSubMode::kFresh), + init.type(module_, enabled_)); } } } @@ -1649,76 +1688,16 @@ Handle InstanceBuilder::RecursivelyEvaluateGlobalInitializer( // Process initialization of globals. void InstanceBuilder::InitGlobals(Handle instance) { for (const WasmGlobal& global : module_->globals) { - if (global.mutability && global.imported) { - continue; - } + if (global.mutability && global.imported) continue; + // Happens with imported globals. + if (global.init.kind() == WasmInitExpr::kNone) continue; - switch (global.init.kind()) { - case WasmInitExpr::kI32Const: - WriteLittleEndianValue(GetRawGlobalPtr(global), - global.init.immediate().i32_const); - break; - case WasmInitExpr::kI64Const: - WriteLittleEndianValue(GetRawGlobalPtr(global), - global.init.immediate().i64_const); - break; - case WasmInitExpr::kF32Const: - WriteLittleEndianValue(GetRawGlobalPtr(global), - global.init.immediate().f32_const); - break; - case WasmInitExpr::kF64Const: - WriteLittleEndianValue(GetRawGlobalPtr(global), - global.init.immediate().f64_const); - break; - case WasmInitExpr::kS128Const: - DCHECK(enabled_.has_simd()); - WriteLittleEndianValue>( - GetRawGlobalPtr>(global), - global.init.immediate().s128_const); - break; - case WasmInitExpr::kRefNullConst: - DCHECK(enabled_.has_reftypes() || enabled_.has_eh()); - if (global.imported) break; // We already initialized imported globals. + WasmValue value = EvaluateInitExpression(global.init, instance); - tagged_globals_->set(global.offset, - ReadOnlyRoots(isolate_).null_value(), - SKIP_WRITE_BARRIER); - break; - case WasmInitExpr::kRefFuncConst: { - DCHECK(enabled_.has_reftypes()); - auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction( - isolate_, instance, global.init.immediate().index); - tagged_globals_->set(global.offset, *function); - break; - } - case WasmInitExpr::kGlobalGet: { - // Initialize with another global. - uint32_t new_offset = global.offset; - uint32_t old_offset = - module_->globals[global.init.immediate().index].offset; - TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset); - if (global.type.is_reference()) { - DCHECK(enabled_.has_reftypes()); - tagged_globals_->set(new_offset, tagged_globals_->get(old_offset)); - } else { - size_t size = (global.type == kWasmI64 || global.type == kWasmF64) - ? sizeof(double) - : sizeof(int32_t); - base::Memcpy(raw_buffer_ptr(untagged_globals_, new_offset), - raw_buffer_ptr(untagged_globals_, old_offset), size); - } - break; - } - case WasmInitExpr::kRttCanon: - case WasmInitExpr::kRttSub: - case WasmInitExpr::kRttFreshSub: - tagged_globals_->set( - global.offset, - *RecursivelyEvaluateGlobalInitializer(global.init, instance)); - break; - case WasmInitExpr::kNone: - // Happens with imported globals. - break; + if (value.type().is_reference()) { + tagged_globals_->set(global.offset, *value.to_ref()); + } else { + value.CopyTo(GetRawUntaggedGlobalPtr(global)); } } } diff --git a/src/wasm/value-type.h b/src/wasm/value-type.h index 580311df22..c12496759f 100644 --- a/src/wasm/value-type.h +++ b/src/wasm/value-type.h @@ -186,6 +186,18 @@ enum ValueKind : uint8_t { #undef DEF_ENUM }; +constexpr bool is_numeric(ValueKind kind) { + switch (kind) { +#define NUMERIC_CASE(kind, ...) \ + case k##kind: \ + return true; + FOREACH_NUMERIC_VALUE_TYPE(NUMERIC_CASE) +#undef NUMERIC_CASE + default: + return false; + } +} + constexpr bool is_reference(ValueKind kind) { return kind == kRef || kind == kOptRef || kind == kRtt || kind == kRttWithDepth; @@ -312,6 +324,8 @@ class ValueType { } /******************************** Type checks *******************************/ + constexpr bool is_numeric() const { return wasm::is_numeric(kind()); } + constexpr bool is_reference() const { return wasm::is_reference(kind()); } constexpr bool is_object_reference() const { diff --git a/src/wasm/wasm-init-expr.cc b/src/wasm/wasm-init-expr.cc new file mode 100644 index 0000000000..79fe221741 --- /dev/null +++ b/src/wasm/wasm-init-expr.cc @@ -0,0 +1,58 @@ +// Copyright 2021 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/wasm-init-expr.h" + +#include "src/wasm/wasm-features.h" +#include "src/wasm/wasm-module.h" + +namespace v8 { +namespace internal { +namespace wasm { + +ValueType WasmInitExpr::type(const WasmModule* module, + const WasmFeatures& enabled_features) const { + switch (kind()) { + case kNone: + return kWasmBottom; + case kGlobalGet: + return immediate().index < module->globals.size() + ? module->globals[immediate().index].type + : kWasmBottom; + case kI32Const: + return kWasmI32; + case kI64Const: + return kWasmI64; + case kF32Const: + return kWasmF32; + case kF64Const: + return kWasmF64; + case kS128Const: + return kWasmS128; + case kRefFuncConst: { + uint32_t heap_type = enabled_features.has_typed_funcref() + ? module->functions[immediate().index].sig_index + : HeapType::kFunc; + return ValueType::Ref(heap_type, kNonNullable); + } + case kRefNullConst: + return ValueType::Ref(immediate().heap_type, kNullable); + case kRttCanon: + return ValueType::Rtt(immediate().heap_type, 0); + case kRttSub: + case kRttFreshSub: { + ValueType operand_type = operand()->type(module, enabled_features); + if (!operand_type.is_rtt()) return kWasmBottom; + if (operand_type.has_depth()) { + return ValueType::Rtt(immediate().heap_type, operand_type.depth() + 1); + } else { + return ValueType::Rtt(immediate().heap_type); + } + } + } +} + +} // namespace wasm +} // namespace internal +} // namespace v8 diff --git a/src/wasm/wasm-init-expr.h b/src/wasm/wasm-init-expr.h index 0be83ff72e..eaa3d734f4 100644 --- a/src/wasm/wasm-init-expr.h +++ b/src/wasm/wasm-init-expr.h @@ -17,6 +17,9 @@ namespace v8 { namespace internal { namespace wasm { +struct WasmModule; +class WasmFeatures; + // Representation of an initializer expression. class WasmInitExpr { public: @@ -144,6 +147,9 @@ class WasmInitExpr { return !(*this == other); } + ValueType type(const WasmModule* module, + const WasmFeatures& enabled_features) const; + private: Immediate immediate_; Operator kind_; diff --git a/src/wasm/wasm-value.h b/src/wasm/wasm-value.h index faaad18076..600504f0ef 100644 --- a/src/wasm/wasm-value.h +++ b/src/wasm/wasm-value.h @@ -48,6 +48,11 @@ class Simd128 { FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE_SPECIFIC_METHODS) #undef DEFINE_SIMD_TYPE_SPECIFIC_METHODS + explicit Simd128(byte* bytes) { + base::Memcpy(static_cast(val_), reinterpret_cast(bytes), + kSimd128Size); + } + const uint8_t* bytes() { return val_; } template @@ -128,6 +133,12 @@ class WasmValue { !memcmp(bit_pattern_, other.bit_pattern_, 16); } + void CopyTo(byte* to) { + DCHECK(type_.is_numeric()); + base::Memcpy(static_cast(to), static_cast(bit_pattern_), + type_.element_size_bytes()); + } + template inline T to() const;