Commit dc3f7f81 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[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 <manoskouk@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74949}
parent f9db82ab
...@@ -4102,6 +4102,7 @@ v8_source_set("v8_base_without_compiler") { ...@@ -4102,6 +4102,7 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/wasm-external-refs.cc", "src/wasm/wasm-external-refs.cc",
"src/wasm/wasm-features.cc", "src/wasm/wasm-features.cc",
"src/wasm/wasm-import-wrapper-cache.cc", "src/wasm/wasm-import-wrapper-cache.cc",
"src/wasm/wasm-init-expr.cc",
"src/wasm/wasm-js.cc", "src/wasm/wasm-js.cc",
"src/wasm/wasm-module-builder.cc", "src/wasm/wasm-module-builder.cc",
"src/wasm/wasm-module-sourcemap.cc", "src/wasm/wasm-module-sourcemap.cc",
......
...@@ -1421,46 +1421,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1421,46 +1421,7 @@ class ModuleDecoderImpl : public Decoder {
ModuleOrigin origin_; ModuleOrigin origin_;
ValueType TypeOf(const WasmInitExpr& expr) { ValueType TypeOf(const WasmInitExpr& expr) {
switch (expr.kind()) { return expr.type(module_.get(), enabled_features_);
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;
}
}
}
} }
bool has_seen_unordered_section(SectionCode section_code) { bool has_seen_unordered_section(SectionCode section_code) {
......
...@@ -426,13 +426,13 @@ class InstanceBuilder { ...@@ -426,13 +426,13 @@ class InstanceBuilder {
int ProcessImports(Handle<WasmInstanceObject> instance); int ProcessImports(Handle<WasmInstanceObject> instance);
template <typename T> template <typename T>
T* GetRawGlobalPtr(const WasmGlobal& global); T* GetRawUntaggedGlobalPtr(const WasmGlobal& global);
// Process initialization of globals. // Process initialization of globals.
void InitGlobals(Handle<WasmInstanceObject> instance); void InitGlobals(Handle<WasmInstanceObject> instance);
Handle<Object> RecursivelyEvaluateGlobalInitializer( WasmValue EvaluateInitExpression(const WasmInitExpr& init,
const WasmInitExpr& init, Handle<WasmInstanceObject> instance); Handle<WasmInstanceObject> instance);
// Process the exports, creating wrappers for functions, tables, memories, // Process the exports, creating wrappers for functions, tables, memories,
// and globals. // and globals.
...@@ -934,7 +934,7 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) { ...@@ -934,7 +934,7 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
global.type.name().c_str()); global.type.name().c_str());
switch (global.type.kind()) { switch (global.type.kind()) {
case kI32: case kI32:
WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), WriteLittleEndianValue<int32_t>(GetRawUntaggedGlobalPtr<int32_t>(global),
DoubleToInt32(num)); DoubleToInt32(num));
break; break;
case kI64: case kI64:
...@@ -943,11 +943,12 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) { ...@@ -943,11 +943,12 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
// https://github.com/WebAssembly/JS-BigInt-integration/issues/12 // https://github.com/WebAssembly/JS-BigInt-integration/issues/12
UNREACHABLE(); UNREACHABLE();
case kF32: case kF32:
WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), WriteLittleEndianValue<float>(GetRawUntaggedGlobalPtr<float>(global),
DoubleToFloat32(num)); DoubleToFloat32(num));
break; break;
case kF64: case kF64:
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num); WriteLittleEndianValue<double>(GetRawUntaggedGlobalPtr<double>(global),
num);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -959,7 +960,8 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) { ...@@ -959,7 +960,8 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) {
raw_buffer_ptr(untagged_globals_, 0), global.offset, num, raw_buffer_ptr(untagged_globals_, 0), global.offset, num,
global.type.name().c_str()); global.type.name().c_str());
DCHECK_EQ(kWasmI64, global.type); DCHECK_EQ(kWasmI64, global.type);
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num); WriteLittleEndianValue<int64_t>(GetRawUntaggedGlobalPtr<int64_t>(global),
num);
} }
void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
...@@ -969,25 +971,29 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, ...@@ -969,25 +971,29 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
switch (global.type.kind()) { switch (global.type.kind()) {
case kI32: { case kI32: {
int32_t num = value->GetI32(); int32_t num = value->GetI32();
WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num); WriteLittleEndianValue<int32_t>(GetRawUntaggedGlobalPtr<int32_t>(global),
num);
TRACE("%d", num); TRACE("%d", num);
break; break;
} }
case kI64: { case kI64: {
int64_t num = value->GetI64(); int64_t num = value->GetI64();
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num); WriteLittleEndianValue<int64_t>(GetRawUntaggedGlobalPtr<int64_t>(global),
num);
TRACE("%" PRId64, num); TRACE("%" PRId64, num);
break; break;
} }
case kF32: { case kF32: {
float num = value->GetF32(); float num = value->GetF32();
WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num); WriteLittleEndianValue<float>(GetRawUntaggedGlobalPtr<float>(global),
num);
TRACE("%f", num); TRACE("%f", num);
break; break;
} }
case kF64: { case kF64: {
double num = value->GetF64(); double num = value->GetF64();
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num); WriteLittleEndianValue<double>(GetRawUntaggedGlobalPtr<double>(global),
num);
TRACE("%lf", num); TRACE("%lf", num);
break; break;
} }
...@@ -1603,45 +1609,78 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) { ...@@ -1603,45 +1609,78 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
} }
template <typename T> template <typename T>
T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) { T* InstanceBuilder::GetRawUntaggedGlobalPtr(const WasmGlobal& global) {
return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset)); return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
} }
Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer( WasmValue InstanceBuilder::EvaluateInitExpression(
const WasmInitExpr& init, Handle<WasmInstanceObject> instance) { const WasmInitExpr& init, Handle<WasmInstanceObject> instance) {
switch (init.kind()) { switch (init.kind()) {
case WasmInitExpr::kNone:
UNREACHABLE();
case WasmInitExpr::kI32Const: case WasmInitExpr::kI32Const:
return WasmValue(init.immediate().i32_const);
case WasmInitExpr::kI64Const: case WasmInitExpr::kI64Const:
return WasmValue(init.immediate().i64_const);
case WasmInitExpr::kF32Const: case WasmInitExpr::kF32Const:
return WasmValue(init.immediate().f32_const);
case WasmInitExpr::kF64Const: case WasmInitExpr::kF64Const:
return WasmValue(init.immediate().f64_const);
case WasmInitExpr::kS128Const: case WasmInitExpr::kS128Const:
return WasmValue(Simd128(init.immediate().s128_const.data()));
case WasmInitExpr::kRefNullConst: case WasmInitExpr::kRefNullConst:
case WasmInitExpr::kRefFuncConst: return WasmValue(handle(ReadOnlyRoots(isolate_).null_value(), isolate_),
case WasmInitExpr::kNone: init.type(module_, enabled_));
// Handled directly by {InitGlobals()}, can't occur as recursive case. case WasmInitExpr::kRefFuncConst: {
UNREACHABLE(); auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction(
break; isolate_, instance, init.immediate().index);
return WasmValue(function, init.type(module_, enabled_));
}
case WasmInitExpr::kGlobalGet: { case WasmInitExpr::kGlobalGet: {
// We can only get here for reference-type globals, but we don't have const WasmGlobal& global = module_->globals[init.immediate().index];
// enough information to DCHECK that directly. switch (global.type.kind()) {
DCHECK(enabled_.has_reftypes() || enabled_.has_eh()); case kI32:
uint32_t old_offset = module_->globals[init.immediate().index].offset; return WasmValue(*GetRawUntaggedGlobalPtr<int32_t>(global));
DCHECK(static_cast<int>(old_offset) < tagged_globals_->length()); case kI64:
return handle(tagged_globals_->get(old_offset), isolate_); return WasmValue(*GetRawUntaggedGlobalPtr<int64_t>(global));
case kF32:
return WasmValue(*GetRawUntaggedGlobalPtr<float>(global));
case kF64:
return WasmValue(*GetRawUntaggedGlobalPtr<double>(global));
case kS128:
return WasmValue(Simd128(GetRawUntaggedGlobalPtr<byte>(global)));
case kRef:
case kOptRef:
case kRtt:
case kRttWithDepth: {
DCHECK(static_cast<int>(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: { case WasmInitExpr::kRttCanon: {
int map_index = init.immediate().index; 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::kRttSub:
case WasmInitExpr::kRttFreshSub: { case WasmInitExpr::kRttFreshSub: {
uint32_t type = init.immediate().index; uint32_t type = init.immediate().index;
Handle<Object> parent = WasmValue parent = EvaluateInitExpression(*init.operand(), instance);
RecursivelyEvaluateGlobalInitializer(*init.operand(), instance); return WasmValue(AllocateSubRtt(isolate_, instance, type,
return AllocateSubRtt(isolate_, instance, type, Handle<Map>::cast(parent), Handle<Map>::cast(parent.to_ref()),
init.kind() == WasmInitExpr::kRttSub init.kind() == WasmInitExpr::kRttSub
? WasmRttSubMode::kCanonicalize ? WasmRttSubMode::kCanonicalize
: WasmRttSubMode::kFresh); : WasmRttSubMode::kFresh),
init.type(module_, enabled_));
} }
} }
} }
...@@ -1649,76 +1688,16 @@ Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer( ...@@ -1649,76 +1688,16 @@ Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
// Process initialization of globals. // Process initialization of globals.
void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) { void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
for (const WasmGlobal& global : module_->globals) { for (const WasmGlobal& global : module_->globals) {
if (global.mutability && global.imported) { if (global.mutability && global.imported) continue;
continue; // Happens with imported globals.
} if (global.init.kind() == WasmInitExpr::kNone) continue;
switch (global.init.kind()) { WasmValue value = EvaluateInitExpression(global.init, instance);
case WasmInitExpr::kI32Const:
WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
global.init.immediate().i32_const);
break;
case WasmInitExpr::kI64Const:
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
global.init.immediate().i64_const);
break;
case WasmInitExpr::kF32Const:
WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
global.init.immediate().f32_const);
break;
case WasmInitExpr::kF64Const:
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
global.init.immediate().f64_const);
break;
case WasmInitExpr::kS128Const:
DCHECK(enabled_.has_simd());
WriteLittleEndianValue<std::array<uint8_t, kSimd128Size>>(
GetRawGlobalPtr<std::array<uint8_t, kSimd128Size>>(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.
tagged_globals_->set(global.offset, if (value.type().is_reference()) {
ReadOnlyRoots(isolate_).null_value(), tagged_globals_->set(global.offset, *value.to_ref());
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 { } else {
size_t size = (global.type == kWasmI64 || global.type == kWasmF64) value.CopyTo(GetRawUntaggedGlobalPtr<byte>(global));
? 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;
} }
} }
} }
......
...@@ -186,6 +186,18 @@ enum ValueKind : uint8_t { ...@@ -186,6 +186,18 @@ enum ValueKind : uint8_t {
#undef DEF_ENUM #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) { constexpr bool is_reference(ValueKind kind) {
return kind == kRef || kind == kOptRef || kind == kRtt || return kind == kRef || kind == kOptRef || kind == kRtt ||
kind == kRttWithDepth; kind == kRttWithDepth;
...@@ -312,6 +324,8 @@ class ValueType { ...@@ -312,6 +324,8 @@ class ValueType {
} }
/******************************** Type checks *******************************/ /******************************** 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_reference() const { return wasm::is_reference(kind()); }
constexpr bool is_object_reference() const { constexpr bool is_object_reference() const {
......
// 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
...@@ -17,6 +17,9 @@ namespace v8 { ...@@ -17,6 +17,9 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
struct WasmModule;
class WasmFeatures;
// Representation of an initializer expression. // Representation of an initializer expression.
class WasmInitExpr { class WasmInitExpr {
public: public:
...@@ -144,6 +147,9 @@ class WasmInitExpr { ...@@ -144,6 +147,9 @@ class WasmInitExpr {
return !(*this == other); return !(*this == other);
} }
ValueType type(const WasmModule* module,
const WasmFeatures& enabled_features) const;
private: private:
Immediate immediate_; Immediate immediate_;
Operator kind_; Operator kind_;
......
...@@ -48,6 +48,11 @@ class Simd128 { ...@@ -48,6 +48,11 @@ class Simd128 {
FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE_SPECIFIC_METHODS) FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE_SPECIFIC_METHODS)
#undef DEFINE_SIMD_TYPE_SPECIFIC_METHODS #undef DEFINE_SIMD_TYPE_SPECIFIC_METHODS
explicit Simd128(byte* bytes) {
base::Memcpy(static_cast<void*>(val_), reinterpret_cast<void*>(bytes),
kSimd128Size);
}
const uint8_t* bytes() { return val_; } const uint8_t* bytes() { return val_; }
template <typename T> template <typename T>
...@@ -128,6 +133,12 @@ class WasmValue { ...@@ -128,6 +133,12 @@ class WasmValue {
!memcmp(bit_pattern_, other.bit_pattern_, 16); !memcmp(bit_pattern_, other.bit_pattern_, 16);
} }
void CopyTo(byte* to) {
DCHECK(type_.is_numeric());
base::Memcpy(static_cast<void*>(to), static_cast<void*>(bit_pattern_),
type_.element_size_bytes());
}
template <typename T> template <typename T>
inline T to() const; inline T to() const;
......
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