[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: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74949}
This commit is contained in:
Manos Koukoutos 2021-06-04 12:21:28 +00:00 committed by V8 LUCI CQ
parent f9db82ab33
commit dc3f7f81c9
7 changed files with 173 additions and 143 deletions

View File

@ -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",

View File

@ -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) {

View File

@ -426,13 +426,13 @@ class InstanceBuilder {
int ProcessImports(Handle<WasmInstanceObject> instance);
template <typename T>
T* GetRawGlobalPtr(const WasmGlobal& global);
T* GetRawUntaggedGlobalPtr(const WasmGlobal& global);
// Process initialization of globals.
void InitGlobals(Handle<WasmInstanceObject> instance);
Handle<Object> RecursivelyEvaluateGlobalInitializer(
const WasmInitExpr& init, Handle<WasmInstanceObject> instance);
WasmValue EvaluateInitExpression(const WasmInitExpr& init,
Handle<WasmInstanceObject> 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<int32_t>(GetRawGlobalPtr<int32_t>(global),
WriteLittleEndianValue<int32_t>(GetRawUntaggedGlobalPtr<int32_t>(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<float>(GetRawGlobalPtr<float>(global),
WriteLittleEndianValue<float>(GetRawUntaggedGlobalPtr<float>(global),
DoubleToFloat32(num));
break;
case kF64:
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
WriteLittleEndianValue<double>(GetRawUntaggedGlobalPtr<double>(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<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
WriteLittleEndianValue<int64_t>(GetRawUntaggedGlobalPtr<int64_t>(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<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
WriteLittleEndianValue<int32_t>(GetRawUntaggedGlobalPtr<int32_t>(global),
num);
TRACE("%d", num);
break;
}
case kI64: {
int64_t num = value->GetI64();
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
WriteLittleEndianValue<int64_t>(GetRawUntaggedGlobalPtr<int64_t>(global),
num);
TRACE("%" PRId64, num);
break;
}
case kF32: {
float num = value->GetF32();
WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
WriteLittleEndianValue<float>(GetRawUntaggedGlobalPtr<float>(global),
num);
TRACE("%f", num);
break;
}
case kF64: {
double num = value->GetF64();
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
WriteLittleEndianValue<double>(GetRawUntaggedGlobalPtr<double>(global),
num);
TRACE("%lf", num);
break;
}
@ -1603,45 +1609,78 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
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));
}
Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
WasmValue InstanceBuilder::EvaluateInitExpression(
const WasmInitExpr& init, Handle<WasmInstanceObject> 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<int>(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<int32_t>(global));
case kI64:
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: {
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<Object> parent =
RecursivelyEvaluateGlobalInitializer(*init.operand(), instance);
return AllocateSubRtt(isolate_, instance, type, Handle<Map>::cast(parent),
init.kind() == WasmInitExpr::kRttSub
? WasmRttSubMode::kCanonicalize
: WasmRttSubMode::kFresh);
WasmValue parent = EvaluateInitExpression(*init.operand(), instance);
return WasmValue(AllocateSubRtt(isolate_, instance, type,
Handle<Map>::cast(parent.to_ref()),
init.kind() == WasmInitExpr::kRttSub
? WasmRttSubMode::kCanonicalize
: WasmRttSubMode::kFresh),
init.type(module_, enabled_));
}
}
}
@ -1649,76 +1688,16 @@ Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
// Process initialization of globals.
void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> 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<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.
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<byte>(global));
}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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_;

View File

@ -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<void*>(val_), reinterpret_cast<void*>(bytes),
kSimd128Size);
}
const uint8_t* bytes() { return val_; }
template <typename T>
@ -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<void*>(to), static_cast<void*>(bit_pattern_),
type_.element_size_bytes());
}
template <typename T>
inline T to() const;