[wasm][refactor] Clean up constant expressions
Changes: - Rename InitExpression -> ConstantExpression in places which reference the ConstantExpression type. - Move ConstantExpression to its own file, along with ValueOrError and EvaluateConstantExpression. Change-Id: Ife572d783531216b6ea3d2626e4fbf4048463253 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3702798 Reviewed-by: Clemens Backes <clemensb@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#81204}
This commit is contained in:
parent
dc670a3c08
commit
e4a7ef2b3b
@ -2480,6 +2480,10 @@ filegroup(
|
||||
"src/wasm/code-space-access.cc",
|
||||
"src/wasm/code-space-access.h",
|
||||
"src/wasm/compilation-environment.h",
|
||||
"src/wasm/constant-expression.cc",
|
||||
"src/wasm/constant-expression.h",
|
||||
"src/wasm/constant-expression-interface.cc",
|
||||
"src/wasm/constant-expression-interface.h",
|
||||
"src/wasm/decoder.h",
|
||||
"src/wasm/function-body-decoder.cc",
|
||||
"src/wasm/function-body-decoder.h",
|
||||
@ -2488,8 +2492,6 @@ filegroup(
|
||||
"src/wasm/function-compiler.h",
|
||||
"src/wasm/graph-builder-interface.cc",
|
||||
"src/wasm/graph-builder-interface.h",
|
||||
"src/wasm/init-expr-interface.cc",
|
||||
"src/wasm/init-expr-interface.h",
|
||||
"src/wasm/jump-table-assembler.cc",
|
||||
"src/wasm/jump-table-assembler.h",
|
||||
"src/wasm/leb-helper.h",
|
||||
|
6
BUILD.gn
6
BUILD.gn
@ -3579,12 +3579,13 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/wasm/canonical-types.h",
|
||||
"src/wasm/code-space-access.h",
|
||||
"src/wasm/compilation-environment.h",
|
||||
"src/wasm/constant-expression-interface.h",
|
||||
"src/wasm/constant-expression.h",
|
||||
"src/wasm/decoder.h",
|
||||
"src/wasm/function-body-decoder-impl.h",
|
||||
"src/wasm/function-body-decoder.h",
|
||||
"src/wasm/function-compiler.h",
|
||||
"src/wasm/graph-builder-interface.h",
|
||||
"src/wasm/init-expr-interface.h",
|
||||
"src/wasm/jump-table-assembler.h",
|
||||
"src/wasm/leb-helper.h",
|
||||
"src/wasm/local-decl-encoder.h",
|
||||
@ -4661,10 +4662,11 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/wasm/baseline/liftoff-compiler.cc",
|
||||
"src/wasm/canonical-types.cc",
|
||||
"src/wasm/code-space-access.cc",
|
||||
"src/wasm/constant-expression-interface.cc",
|
||||
"src/wasm/constant-expression.cc",
|
||||
"src/wasm/function-body-decoder.cc",
|
||||
"src/wasm/function-compiler.cc",
|
||||
"src/wasm/graph-builder-interface.cc",
|
||||
"src/wasm/init-expr-interface.cc",
|
||||
"src/wasm/jump-table-assembler.cc",
|
||||
"src/wasm/local-decl-encoder.cc",
|
||||
"src/wasm/memory-protection-key.cc",
|
||||
|
@ -1775,9 +1775,9 @@ Handle<Object> Factory::NewWasmArrayFromElementSegment(
|
||||
AccountingAllocator allocator;
|
||||
Zone zone(&allocator, ZONE_NAME);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
wasm::ValueOrError maybe_element =
|
||||
wasm::EvaluateInitExpression(&zone, segment->entries[start_offset + i],
|
||||
element_type, isolate(), instance);
|
||||
wasm::ValueOrError maybe_element = wasm::EvaluateConstantExpression(
|
||||
&zone, segment->entries[start_offset + i], element_type, isolate(),
|
||||
instance);
|
||||
if (wasm::is_error(maybe_element)) {
|
||||
return handle(Smi::FromEnum(wasm::to_error(maybe_element)), isolate());
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/wasm/init-expr-interface.h"
|
||||
#include "src/wasm/constant-expression-interface.h"
|
||||
|
||||
#include "src/execution/isolate.h"
|
||||
#include "src/handles/handles-inl.h"
|
||||
@ -17,36 +17,36 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
void InitExprInterface::I32Const(FullDecoder* decoder, Value* result,
|
||||
int32_t value) {
|
||||
void ConstantExpressionInterface::I32Const(FullDecoder* decoder, Value* result,
|
||||
int32_t value) {
|
||||
if (generate_value()) result->runtime_value = WasmValue(value);
|
||||
}
|
||||
|
||||
void InitExprInterface::I64Const(FullDecoder* decoder, Value* result,
|
||||
int64_t value) {
|
||||
void ConstantExpressionInterface::I64Const(FullDecoder* decoder, Value* result,
|
||||
int64_t value) {
|
||||
if (generate_value()) result->runtime_value = WasmValue(value);
|
||||
}
|
||||
|
||||
void InitExprInterface::F32Const(FullDecoder* decoder, Value* result,
|
||||
float value) {
|
||||
void ConstantExpressionInterface::F32Const(FullDecoder* decoder, Value* result,
|
||||
float value) {
|
||||
if (generate_value()) result->runtime_value = WasmValue(value);
|
||||
}
|
||||
|
||||
void InitExprInterface::F64Const(FullDecoder* decoder, Value* result,
|
||||
double value) {
|
||||
void ConstantExpressionInterface::F64Const(FullDecoder* decoder, Value* result,
|
||||
double value) {
|
||||
if (generate_value()) result->runtime_value = WasmValue(value);
|
||||
}
|
||||
|
||||
void InitExprInterface::S128Const(FullDecoder* decoder,
|
||||
Simd128Immediate<validate>& imm,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::S128Const(FullDecoder* decoder,
|
||||
Simd128Immediate<validate>& imm,
|
||||
Value* result) {
|
||||
if (!generate_value()) return;
|
||||
result->runtime_value = WasmValue(imm.value, kWasmS128);
|
||||
}
|
||||
|
||||
void InitExprInterface::BinOp(FullDecoder* decoder, WasmOpcode opcode,
|
||||
const Value& lhs, const Value& rhs,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::BinOp(FullDecoder* decoder, WasmOpcode opcode,
|
||||
const Value& lhs, const Value& rhs,
|
||||
Value* result) {
|
||||
if (!generate_value()) return;
|
||||
switch (opcode) {
|
||||
case kExprI32Add:
|
||||
@ -78,14 +78,15 @@ void InitExprInterface::BinOp(FullDecoder* decoder, WasmOpcode opcode,
|
||||
}
|
||||
}
|
||||
|
||||
void InitExprInterface::RefNull(FullDecoder* decoder, ValueType type,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::RefNull(FullDecoder* decoder, ValueType type,
|
||||
Value* result) {
|
||||
if (!generate_value()) return;
|
||||
result->runtime_value = WasmValue(isolate_->factory()->null_value(), type);
|
||||
}
|
||||
|
||||
void InitExprInterface::RefFunc(FullDecoder* decoder, uint32_t function_index,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::RefFunc(FullDecoder* decoder,
|
||||
uint32_t function_index,
|
||||
Value* result) {
|
||||
if (isolate_ == nullptr) {
|
||||
outer_module_->functions[function_index].declared = true;
|
||||
return;
|
||||
@ -99,8 +100,9 @@ void InitExprInterface::RefFunc(FullDecoder* decoder, uint32_t function_index,
|
||||
result->runtime_value = WasmValue(internal, type);
|
||||
}
|
||||
|
||||
void InitExprInterface::GlobalGet(FullDecoder* decoder, Value* result,
|
||||
const GlobalIndexImmediate<validate>& imm) {
|
||||
void ConstantExpressionInterface::GlobalGet(
|
||||
FullDecoder* decoder, Value* result,
|
||||
const GlobalIndexImmediate<validate>& imm) {
|
||||
if (!generate_value()) return;
|
||||
const WasmGlobal& global = module_->globals[imm.index];
|
||||
DCHECK(!global.mutability);
|
||||
@ -117,7 +119,7 @@ void InitExprInterface::GlobalGet(FullDecoder* decoder, Value* result,
|
||||
global.type);
|
||||
}
|
||||
|
||||
void InitExprInterface::StructNewWithRtt(
|
||||
void ConstantExpressionInterface::StructNewWithRtt(
|
||||
FullDecoder* decoder, const StructIndexImmediate<validate>& imm,
|
||||
const Value& rtt, const Value args[], Value* result) {
|
||||
if (!generate_value()) return;
|
||||
@ -132,9 +134,9 @@ void InitExprInterface::StructNewWithRtt(
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
}
|
||||
|
||||
void InitExprInterface::StringConst(FullDecoder* decoder,
|
||||
const StringConstImmediate<validate>& imm,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::StringConst(
|
||||
FullDecoder* decoder, const StringConstImmediate<validate>& imm,
|
||||
Value* result) {
|
||||
if (!generate_value()) return;
|
||||
static_assert(base::IsInRange(kV8MaxWasmStringLiterals, 0, Smi::kMaxValue));
|
||||
|
||||
@ -178,7 +180,7 @@ WasmValue DefaultValueForType(ValueType type, Isolate* isolate) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void InitExprInterface::StructNewDefault(
|
||||
void ConstantExpressionInterface::StructNewDefault(
|
||||
FullDecoder* decoder, const StructIndexImmediate<validate>& imm,
|
||||
const Value& rtt, Value* result) {
|
||||
if (!generate_value()) return;
|
||||
@ -193,10 +195,9 @@ void InitExprInterface::StructNewDefault(
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
}
|
||||
|
||||
void InitExprInterface::ArrayInit(FullDecoder* decoder,
|
||||
const ArrayIndexImmediate<validate>& imm,
|
||||
const base::Vector<Value>& elements,
|
||||
const Value& rtt, Value* result) {
|
||||
void ConstantExpressionInterface::ArrayInit(
|
||||
FullDecoder* decoder, const ArrayIndexImmediate<validate>& imm,
|
||||
const base::Vector<Value>& elements, const Value& rtt, Value* result) {
|
||||
if (!generate_value()) return;
|
||||
std::vector<WasmValue> element_values;
|
||||
for (Value elem : elements) element_values.push_back(elem.runtime_value);
|
||||
@ -207,7 +208,7 @@ void InitExprInterface::ArrayInit(FullDecoder* decoder,
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
}
|
||||
|
||||
void InitExprInterface::ArrayInitFromSegment(
|
||||
void ConstantExpressionInterface::ArrayInitFromSegment(
|
||||
FullDecoder* decoder, const ArrayIndexImmediate<validate>& array_imm,
|
||||
const IndexImmediate<validate>& segment_imm, const Value& offset_value,
|
||||
const Value& length_value, const Value& rtt, Value* result) {
|
||||
@ -267,16 +268,16 @@ void InitExprInterface::ArrayInitFromSegment(
|
||||
}
|
||||
}
|
||||
|
||||
void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,
|
||||
Value* result) {
|
||||
void ConstantExpressionInterface::RttCanon(FullDecoder* decoder,
|
||||
uint32_t type_index, Value* result) {
|
||||
if (!generate_value()) return;
|
||||
result->runtime_value = WasmValue(
|
||||
handle(instance_->managed_object_maps().get(type_index), isolate_),
|
||||
ValueType::Rtt(type_index));
|
||||
}
|
||||
|
||||
void InitExprInterface::DoReturn(FullDecoder* decoder,
|
||||
uint32_t /*drop_values*/) {
|
||||
void ConstantExpressionInterface::DoReturn(FullDecoder* decoder,
|
||||
uint32_t /*drop_values*/) {
|
||||
end_found_ = true;
|
||||
// End decoding on "end".
|
||||
decoder->set_end(decoder->pc() + 1);
|
@ -6,8 +6,8 @@
|
||||
#error This header should only be included if WebAssembly is enabled.
|
||||
#endif // !V8_ENABLE_WEBASSEMBLY
|
||||
|
||||
#ifndef V8_WASM_INIT_EXPR_INTERFACE_H_
|
||||
#define V8_WASM_INIT_EXPR_INTERFACE_H_
|
||||
#ifndef V8_WASM_CONSTANT_EXPRESSION_INTERFACE_H_
|
||||
#define V8_WASM_CONSTANT_EXPRESSION_INTERFACE_H_
|
||||
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/function-body-decoder-impl.h"
|
||||
@ -21,16 +21,16 @@ class JSArrayBuffer;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
// An interface for WasmFullDecoder used to decode initializer expressions. This
|
||||
// An interface for WasmFullDecoder used to decode constant expressions. This
|
||||
// interface has two modes: only validation (when {isolate_ == nullptr}), which
|
||||
// is used in module-decoder, and code-generation (when {isolate_ != nullptr}),
|
||||
// which is used in module-instantiate. We merge two distinct functionalities
|
||||
// in one class to reduce the number of WasmFullDecoder instantiations, and thus
|
||||
// V8 binary code size.
|
||||
class InitExprInterface {
|
||||
class ConstantExpressionInterface {
|
||||
public:
|
||||
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
|
||||
static constexpr DecodingMode decoding_mode = kInitExpression;
|
||||
static constexpr DecodingMode decoding_mode = kConstantExpression;
|
||||
|
||||
struct Value : public ValueBase<validate> {
|
||||
WasmValue runtime_value;
|
||||
@ -42,10 +42,10 @@ class InitExprInterface {
|
||||
|
||||
using Control = ControlBase<Value, validate>;
|
||||
using FullDecoder =
|
||||
WasmFullDecoder<validate, InitExprInterface, decoding_mode>;
|
||||
WasmFullDecoder<validate, ConstantExpressionInterface, decoding_mode>;
|
||||
|
||||
InitExprInterface(const WasmModule* module, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance)
|
||||
ConstantExpressionInterface(const WasmModule* module, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance)
|
||||
: module_(module),
|
||||
outer_module_(nullptr),
|
||||
isolate_(isolate),
|
||||
@ -53,7 +53,7 @@ class InitExprInterface {
|
||||
DCHECK_NOT_NULL(isolate);
|
||||
}
|
||||
|
||||
explicit InitExprInterface(WasmModule* outer_module)
|
||||
explicit ConstantExpressionInterface(WasmModule* outer_module)
|
||||
: module_(nullptr), outer_module_(outer_module), isolate_(nullptr) {}
|
||||
|
||||
#define EMPTY_INTERFACE_FUNCTION(name, ...) \
|
||||
@ -100,4 +100,4 @@ class InitExprInterface {
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_WASM_INIT_EXPR_INTERFACE_H_
|
||||
#endif // V8_WASM_CONSTANT_EXPRESSION_INTERFACE_H_
|
78
src/wasm/constant-expression.cc
Normal file
78
src/wasm/constant-expression.cc
Normal file
@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
#include "src/wasm/constant-expression.h"
|
||||
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/heap/factory-inl.h"
|
||||
#include "src/heap/factory.h"
|
||||
#include "src/objects/oddball.h"
|
||||
#include "src/roots/roots.h"
|
||||
#include "src/wasm/constant-expression-interface.h"
|
||||
#include "src/wasm/function-body-decoder-impl.h"
|
||||
#include "src/wasm/wasm-code-manager.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/wasm/wasm-opcodes-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
WireBytesRef ConstantExpression::wire_bytes_ref() const {
|
||||
DCHECK_EQ(kind(), kWireBytesRef);
|
||||
return WireBytesRef(OffsetField::decode(bit_field_),
|
||||
LengthField::decode(bit_field_));
|
||||
}
|
||||
|
||||
ValueOrError EvaluateConstantExpression(Zone* zone, ConstantExpression expr,
|
||||
ValueType expected, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
switch (expr.kind()) {
|
||||
case ConstantExpression::kEmpty:
|
||||
UNREACHABLE();
|
||||
case ConstantExpression::kI32Const:
|
||||
return WasmValue(expr.i32_value());
|
||||
case ConstantExpression::kRefNull:
|
||||
return WasmValue(isolate->factory()->null_value(),
|
||||
ValueType::Ref(expr.repr(), kNullable));
|
||||
case ConstantExpression::kRefFunc: {
|
||||
uint32_t index = expr.index();
|
||||
Handle<Object> value =
|
||||
WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance,
|
||||
index);
|
||||
return WasmValue(value, expected);
|
||||
}
|
||||
case ConstantExpression::kWireBytesRef: {
|
||||
WireBytesRef ref = expr.wire_bytes_ref();
|
||||
|
||||
base::Vector<const byte> module_bytes =
|
||||
instance->module_object().native_module()->wire_bytes();
|
||||
|
||||
const byte* start = module_bytes.begin() + ref.offset();
|
||||
const byte* end = module_bytes.begin() + ref.end_offset();
|
||||
|
||||
auto sig = FixedSizeSignature<ValueType>::Returns(expected);
|
||||
FunctionBody body(&sig, ref.offset(), start, end);
|
||||
WasmFeatures detected;
|
||||
// We use kFullValidation so we do not have to create another template
|
||||
// instance of WasmFullDecoder, which would cost us >50Kb binary code
|
||||
// size.
|
||||
WasmFullDecoder<Decoder::kFullValidation, ConstantExpressionInterface,
|
||||
kConstantExpression>
|
||||
decoder(zone, instance->module(), WasmFeatures::All(), &detected,
|
||||
body, instance->module(), isolate, instance);
|
||||
|
||||
decoder.DecodeFunctionBody();
|
||||
|
||||
return decoder.interface().has_error()
|
||||
? ValueOrError(decoder.interface().error())
|
||||
: ValueOrError(decoder.interface().computed_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
142
src/wasm/constant-expression.h
Normal file
142
src/wasm/constant-expression.h
Normal file
@ -0,0 +1,142 @@
|
||||
// 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.
|
||||
|
||||
#if !V8_ENABLE_WEBASSEMBLY
|
||||
#error This header should only be included if WebAssembly is enabled.
|
||||
#endif // !V8_ENABLE_WEBASSEMBLY
|
||||
|
||||
#ifndef V8_WASM_CONSTANT_EXPRESSION_H_
|
||||
#define V8_WASM_CONSTANT_EXPRESSION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "src/base/bit-field.h"
|
||||
#include "src/wasm/value-type.h"
|
||||
#include "src/wasm/wasm-value.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum class MessageTemplate;
|
||||
class WasmInstanceObject;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class WireBytesRef;
|
||||
|
||||
// A representation of a constant expression. The most common expression types
|
||||
// are hard-coded, while the rest are represented as a {WireBytesRef}.
|
||||
class ConstantExpression {
|
||||
public:
|
||||
enum Kind {
|
||||
kEmpty,
|
||||
kI32Const,
|
||||
kRefNull,
|
||||
kRefFunc,
|
||||
kWireBytesRef,
|
||||
kLastKind = kWireBytesRef
|
||||
};
|
||||
|
||||
union Value {
|
||||
int32_t i32_value;
|
||||
uint32_t index_or_offset;
|
||||
HeapType::Representation repr;
|
||||
};
|
||||
|
||||
ConstantExpression() : bit_field_(KindField::encode(kEmpty)) {}
|
||||
|
||||
static ConstantExpression I32Const(int32_t value) {
|
||||
return ConstantExpression(ValueField::encode(value) |
|
||||
KindField::encode(kI32Const));
|
||||
}
|
||||
static ConstantExpression RefFunc(uint32_t index) {
|
||||
return ConstantExpression(ValueField::encode(index) |
|
||||
KindField::encode(kRefFunc));
|
||||
}
|
||||
static ConstantExpression RefNull(HeapType::Representation repr) {
|
||||
return ConstantExpression(ValueField::encode(repr) |
|
||||
KindField::encode(kRefNull));
|
||||
}
|
||||
static ConstantExpression WireBytes(uint32_t offset, uint32_t length) {
|
||||
return ConstantExpression(OffsetField::encode(offset) |
|
||||
LengthField::encode(length) |
|
||||
KindField::encode(kWireBytesRef));
|
||||
}
|
||||
|
||||
Kind kind() const { return KindField::decode(bit_field_); }
|
||||
|
||||
bool is_set() const { return kind() != kEmpty; }
|
||||
|
||||
uint32_t index() const {
|
||||
DCHECK_EQ(kind(), kRefFunc);
|
||||
return ValueField::decode(bit_field_);
|
||||
}
|
||||
|
||||
HeapType::Representation repr() const {
|
||||
DCHECK_EQ(kind(), kRefNull);
|
||||
return static_cast<HeapType::Representation>(
|
||||
ValueField::decode(bit_field_));
|
||||
}
|
||||
|
||||
int32_t i32_value() const {
|
||||
DCHECK_EQ(kind(), kI32Const);
|
||||
return ValueField::decode(bit_field_);
|
||||
}
|
||||
|
||||
V8_EXPORT_PRIVATE WireBytesRef wire_bytes_ref() const;
|
||||
|
||||
private:
|
||||
static constexpr int kValueBits = 32;
|
||||
static constexpr int kLengthBits = 30;
|
||||
static constexpr int kOffsetBits = 30;
|
||||
static constexpr int kKindBits = 3;
|
||||
|
||||
// There are two possible combinations of fields: offset + length + kind if
|
||||
// kind = kWireBytesRef, or value + kind for anything else.
|
||||
using ValueField = base::BitField<uint32_t, 0, kValueBits, uint64_t>;
|
||||
using OffsetField = base::BitField<uint32_t, 0, kOffsetBits, uint64_t>;
|
||||
using LengthField = OffsetField::Next<uint32_t, kLengthBits>;
|
||||
using KindField = LengthField::Next<Kind, kKindBits>;
|
||||
|
||||
// Make sure we reserve enough bits for a {WireBytesRef}'s length and offset.
|
||||
static_assert(kV8MaxWasmModuleSize <= LengthField::kMax + 1);
|
||||
static_assert(kV8MaxWasmModuleSize <= OffsetField::kMax + 1);
|
||||
// Make sure kind fits in kKindBits.
|
||||
static_assert(kLastKind <= KindField::kMax + 1);
|
||||
|
||||
explicit ConstantExpression(uint64_t bit_field) : bit_field_(bit_field) {}
|
||||
|
||||
uint64_t bit_field_;
|
||||
};
|
||||
|
||||
// We want to keep {ConstantExpression} small to reduce memory usage during
|
||||
// compilation/instantiation.
|
||||
static_assert(sizeof(ConstantExpression) <= 8);
|
||||
|
||||
using ValueOrError = std::variant<WasmValue, MessageTemplate>;
|
||||
|
||||
V8_INLINE bool is_error(ValueOrError result) {
|
||||
return std::holds_alternative<MessageTemplate>(result);
|
||||
}
|
||||
V8_INLINE MessageTemplate to_error(ValueOrError result) {
|
||||
return std::get<MessageTemplate>(result);
|
||||
}
|
||||
V8_INLINE WasmValue to_value(ValueOrError result) {
|
||||
return std::get<WasmValue>(result);
|
||||
}
|
||||
|
||||
// Evaluates a constant expression.
|
||||
// Returns a {WasmValue} if the evaluation succeeds, or an error as a
|
||||
// {MessageTemplate} if it fails.
|
||||
ValueOrError EvaluateConstantExpression(Zone* zone, ConstantExpression expr,
|
||||
ValueType expected, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_WASM_CONSTANT_EXPRESSION_H_
|
@ -413,7 +413,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
}
|
||||
} // namespace value_type_reader
|
||||
|
||||
enum DecodingMode { kFunctionBody, kInitExpression };
|
||||
enum DecodingMode { kFunctionBody, kConstantExpression };
|
||||
|
||||
// Helpers for decoding different kinds of immediates which follow bytecodes.
|
||||
template <Decoder::ValidateFlag validate>
|
||||
@ -1362,7 +1362,7 @@ class WasmDecoder : public Decoder {
|
||||
}
|
||||
imm.global = &module_->globals[imm.index];
|
||||
|
||||
if (decoding_mode == kInitExpression) {
|
||||
if (decoding_mode == kConstantExpression) {
|
||||
if (!VALIDATE(!imm.global->mutability)) {
|
||||
this->DecodeError(pc,
|
||||
"mutable globals cannot be used in initializer "
|
||||
@ -2750,7 +2750,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
|
||||
#define BUILD_SIMPLE_OPCODE(op, _, sig) \
|
||||
DECODE(op) { \
|
||||
if (decoding_mode == kInitExpression) { \
|
||||
if (decoding_mode == kConstantExpression) { \
|
||||
if (!VALIDATE(this->enabled_.has_extended_const())) { \
|
||||
NonConstError(this, kExpr##op); \
|
||||
return 0; \
|
||||
@ -3690,13 +3690,13 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
// Hence just list all implementations explicitly here, which also gives more
|
||||
// freedom to use the same implementation for different opcodes.
|
||||
#define DECODE_IMPL(opcode) DECODE_IMPL2(kExpr##opcode, opcode)
|
||||
#define DECODE_IMPL2(opcode, name) \
|
||||
if (idx == opcode) { \
|
||||
if (decoding_mode == kInitExpression) { \
|
||||
return &WasmFullDecoder::NonConstError; \
|
||||
} else { \
|
||||
return &WasmFullDecoder::Decode##name; \
|
||||
} \
|
||||
#define DECODE_IMPL2(opcode, name) \
|
||||
if (idx == opcode) { \
|
||||
if (decoding_mode == kConstantExpression) { \
|
||||
return &WasmFullDecoder::NonConstError; \
|
||||
} else { \
|
||||
return &WasmFullDecoder::Decode##name; \
|
||||
} \
|
||||
}
|
||||
#define DECODE_IMPL_CONST(opcode) DECODE_IMPL_CONST2(kExpr##opcode, opcode)
|
||||
#define DECODE_IMPL_CONST2(opcode, name) \
|
||||
@ -4078,7 +4078,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
|
||||
uint32_t DecodeSimdOpcode(WasmOpcode opcode, uint32_t opcode_length) {
|
||||
if (decoding_mode == kInitExpression) {
|
||||
if (decoding_mode == kConstantExpression) {
|
||||
// Currently, only s128.const is allowed in initializer expressions.
|
||||
if (opcode != kExprS128Const) {
|
||||
this->DecodeError("opcode %s is not allowed in init. expressions",
|
||||
@ -4232,7 +4232,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
|
||||
#define NON_CONST_ONLY \
|
||||
if (decoding_mode == kInitExpression) { \
|
||||
if (decoding_mode == kConstantExpression) { \
|
||||
this->DecodeError("opcode %s is not allowed in init. expressions", \
|
||||
this->SafeOpcodeNameAt(this->pc())); \
|
||||
return 0; \
|
||||
@ -5684,7 +5684,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
|
||||
void PushMergeValues(Control* c, Merge<Value>* merge) {
|
||||
if (decoding_mode == kInitExpression) return;
|
||||
if (decoding_mode == kConstantExpression) return;
|
||||
DCHECK_EQ(c, &control_.back());
|
||||
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
|
||||
DCHECK_LE(stack_ + c->stack_depth, stack_end_);
|
||||
@ -5830,7 +5830,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
uint32_t actual = stack_size() - control_.back().stack_depth;
|
||||
// Here we have to check for !unreachable(), because we need to typecheck as
|
||||
// if the current code is reachable even if it is spec-only reachable.
|
||||
if (V8_LIKELY(decoding_mode == kInitExpression ||
|
||||
if (V8_LIKELY(decoding_mode == kConstantExpression ||
|
||||
!control_.back().unreachable())) {
|
||||
if (V8_UNLIKELY(strict_count ? actual != drop_values + arity
|
||||
: actual < drop_values + arity)) {
|
||||
|
@ -14,9 +14,10 @@
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/utils/ostreams.h"
|
||||
#include "src/wasm/canonical-types.h"
|
||||
#include "src/wasm/constant-expression-interface.h"
|
||||
#include "src/wasm/constant-expression.h"
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/function-body-decoder-impl.h"
|
||||
#include "src/wasm/init-expr-interface.h"
|
||||
#include "src/wasm/struct-types.h"
|
||||
#include "src/wasm/wasm-constants.h"
|
||||
#include "src/wasm/wasm-engine.h"
|
||||
@ -2009,8 +2010,8 @@ class ModuleDecoderImpl : public Decoder {
|
||||
auto sig = FixedSizeSignature<ValueType>::Returns(expected);
|
||||
FunctionBody body(&sig, buffer_offset_, pc_, end_);
|
||||
WasmFeatures detected;
|
||||
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface,
|
||||
kInitExpression>
|
||||
WasmFullDecoder<Decoder::kFullValidation, ConstantExpressionInterface,
|
||||
kConstantExpression>
|
||||
decoder(&init_expr_zone_, module, enabled_features_, &detected, body,
|
||||
module);
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "src/tracing/trace-event.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/wasm/code-space-access.h"
|
||||
#include "src/wasm/init-expr-interface.h"
|
||||
#include "src/wasm/constant-expression-interface.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-constants.h"
|
||||
#include "src/wasm/wasm-engine.h"
|
||||
@ -301,7 +301,7 @@ class InstanceBuilder {
|
||||
Handle<WasmExportedFunction> start_function_;
|
||||
std::vector<SanitizedImport> sanitized_imports_;
|
||||
// We pass this {Zone} to the temporary {WasmFullDecoder} we allocate during
|
||||
// each call to {EvaluateInitExpression}. This has been found to improve
|
||||
// each call to {EvaluateConstantExpression}. This has been found to improve
|
||||
// performance a bit over allocating a new {Zone} each time.
|
||||
Zone init_expr_zone_;
|
||||
|
||||
@ -906,53 +906,6 @@ bool MaybeMarkError(ValueOrError value, ErrorThrower* thrower) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ValueOrError EvaluateInitExpression(Zone* zone, ConstantExpression expr,
|
||||
ValueType expected, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
switch (expr.kind()) {
|
||||
case ConstantExpression::kEmpty:
|
||||
UNREACHABLE();
|
||||
case ConstantExpression::kI32Const:
|
||||
return WasmValue(expr.i32_value());
|
||||
case ConstantExpression::kRefNull:
|
||||
return WasmValue(isolate->factory()->null_value(),
|
||||
ValueType::Ref(expr.repr(), kNullable));
|
||||
case ConstantExpression::kRefFunc: {
|
||||
uint32_t index = expr.index();
|
||||
Handle<Object> value =
|
||||
WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance,
|
||||
index);
|
||||
return WasmValue(value, expected);
|
||||
}
|
||||
case ConstantExpression::kWireBytesRef: {
|
||||
WireBytesRef ref = expr.wire_bytes_ref();
|
||||
|
||||
base::Vector<const byte> module_bytes =
|
||||
instance->module_object().native_module()->wire_bytes();
|
||||
|
||||
const byte* start = module_bytes.begin() + ref.offset();
|
||||
const byte* end = module_bytes.begin() + ref.end_offset();
|
||||
|
||||
auto sig = FixedSizeSignature<ValueType>::Returns(expected);
|
||||
FunctionBody body(&sig, ref.offset(), start, end);
|
||||
WasmFeatures detected;
|
||||
// We use kFullValidation so we do not have to create another template
|
||||
// instance of WasmFullDecoder, which would cost us >50Kb binary code
|
||||
// size.
|
||||
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface,
|
||||
kInitExpression>
|
||||
decoder(zone, instance->module(), WasmFeatures::All(), &detected,
|
||||
body, instance->module(), isolate, instance);
|
||||
|
||||
decoder.DecodeFunctionBody();
|
||||
|
||||
return decoder.interface().has_error()
|
||||
? ValueOrError(decoder.interface().error())
|
||||
: ValueOrError(decoder.interface().computed_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up an import value in the {ffi_} object specifically for linking an
|
||||
// asm.js module. This only performs non-observable lookups, which allows
|
||||
// falling back to JavaScript proper (and hence re-executing all lookups) if
|
||||
@ -1011,7 +964,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
|
||||
|
||||
size_t dest_offset;
|
||||
if (module_->is_memory64) {
|
||||
ValueOrError result = EvaluateInitExpression(
|
||||
ValueOrError result = EvaluateConstantExpression(
|
||||
&init_expr_zone_, segment.dest_addr, kWasmI64, isolate_, instance);
|
||||
if (MaybeMarkError(result, thrower_)) return;
|
||||
uint64_t dest_offset_64 = to_value(result).to_u64();
|
||||
@ -1022,7 +975,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
|
||||
dest_offset = static_cast<size_t>(std::min(
|
||||
dest_offset_64, uint64_t{std::numeric_limits<size_t>::max()}));
|
||||
} else {
|
||||
ValueOrError result = EvaluateInitExpression(
|
||||
ValueOrError result = EvaluateConstantExpression(
|
||||
&init_expr_zone_, segment.dest_addr, kWasmI32, isolate_, instance);
|
||||
if (MaybeMarkError(result, thrower_)) return;
|
||||
dest_offset = to_value(result).to_u32();
|
||||
@ -1722,7 +1675,7 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
|
||||
// Happens with imported globals.
|
||||
if (!global.init.is_set()) continue;
|
||||
|
||||
ValueOrError result = EvaluateInitExpression(
|
||||
ValueOrError result = EvaluateConstantExpression(
|
||||
&init_expr_zone_, global.init, global.type, isolate_, instance);
|
||||
if (MaybeMarkError(result, thrower_)) return;
|
||||
|
||||
@ -1989,8 +1942,8 @@ void InstanceBuilder::InitializeNonDefaultableTables(
|
||||
}
|
||||
} else {
|
||||
ValueOrError result =
|
||||
EvaluateInitExpression(&init_expr_zone_, table.initial_value,
|
||||
table.type, isolate_, instance);
|
||||
EvaluateConstantExpression(&init_expr_zone_, table.initial_value,
|
||||
table.type, isolate_, instance);
|
||||
if (MaybeMarkError(result, thrower_)) return;
|
||||
for (uint32_t entry_index = 0; entry_index < table.initial_size;
|
||||
entry_index++) {
|
||||
@ -2040,7 +1993,7 @@ base::Optional<MessageTemplate> LoadElemSegmentImpl(
|
||||
entry.kind() == ConstantExpression::kRefNull) {
|
||||
SetFunctionTableNullEntry(isolate, table_object, entry_index);
|
||||
} else {
|
||||
ValueOrError result = EvaluateInitExpression(
|
||||
ValueOrError result = EvaluateConstantExpression(
|
||||
zone, entry, elem_segment.type, isolate, instance);
|
||||
if (is_error(result)) return to_error(result);
|
||||
WasmTableObject::Set(isolate, table_object, entry_index,
|
||||
@ -2059,7 +2012,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
|
||||
if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
|
||||
|
||||
uint32_t table_index = elem_segment.table_index;
|
||||
ValueOrError value = EvaluateInitExpression(
|
||||
ValueOrError value = EvaluateConstantExpression(
|
||||
&init_expr_zone_, elem_segment.offset, kWasmI32, isolate_, instance);
|
||||
if (MaybeMarkError(value, thrower_)) return;
|
||||
uint32_t dst = std::get<WasmValue>(value).to_u32();
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "include/v8config.h"
|
||||
#include "src/base/optional.h"
|
||||
#include "src/common/message-template.h"
|
||||
@ -50,22 +48,6 @@ base::Optional<MessageTemplate> LoadElemSegment(
|
||||
uint32_t segment_index, uint32_t dst, uint32_t src,
|
||||
uint32_t count) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
using ValueOrError = std::variant<WasmValue, MessageTemplate>;
|
||||
|
||||
V8_INLINE bool is_error(ValueOrError result) {
|
||||
return std::holds_alternative<MessageTemplate>(result);
|
||||
}
|
||||
V8_INLINE MessageTemplate to_error(ValueOrError result) {
|
||||
return std::get<MessageTemplate>(result);
|
||||
}
|
||||
V8_INLINE WasmValue to_value(ValueOrError result) {
|
||||
return std::get<WasmValue>(result);
|
||||
}
|
||||
|
||||
ValueOrError EvaluateInitExpression(Zone* zone, ConstantExpression expr,
|
||||
ValueType expected, Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "src/common/globals.h"
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/wasm/branch-hint-map.h"
|
||||
#include "src/wasm/constant-expression.h"
|
||||
#include "src/wasm/signature-map.h"
|
||||
#include "src/wasm/struct-types.h"
|
||||
#include "src/wasm/wasm-constants.h"
|
||||
@ -73,99 +74,6 @@ struct WasmFunction {
|
||||
bool declared;
|
||||
};
|
||||
|
||||
// A representation of a constant expression. The most common expression types
|
||||
// are hard-coded, while the rest are represented as a {WireBytesRef}.
|
||||
class ConstantExpression {
|
||||
public:
|
||||
enum Kind {
|
||||
kEmpty,
|
||||
kI32Const,
|
||||
kRefNull,
|
||||
kRefFunc,
|
||||
kWireBytesRef,
|
||||
kLastKind = kWireBytesRef
|
||||
};
|
||||
|
||||
union Value {
|
||||
int32_t i32_value;
|
||||
uint32_t index_or_offset;
|
||||
HeapType::Representation repr;
|
||||
};
|
||||
|
||||
ConstantExpression() : bit_field_(KindField::encode(kEmpty)) {}
|
||||
|
||||
static ConstantExpression I32Const(int32_t value) {
|
||||
return ConstantExpression(ValueField::encode(value) |
|
||||
KindField::encode(kI32Const));
|
||||
}
|
||||
static ConstantExpression RefFunc(uint32_t index) {
|
||||
return ConstantExpression(ValueField::encode(index) |
|
||||
KindField::encode(kRefFunc));
|
||||
}
|
||||
static ConstantExpression RefNull(HeapType::Representation repr) {
|
||||
return ConstantExpression(ValueField::encode(repr) |
|
||||
KindField::encode(kRefNull));
|
||||
}
|
||||
static ConstantExpression WireBytes(uint32_t offset, uint32_t length) {
|
||||
return ConstantExpression(OffsetField::encode(offset) |
|
||||
LengthField::encode(length) |
|
||||
KindField::encode(kWireBytesRef));
|
||||
}
|
||||
|
||||
Kind kind() const { return KindField::decode(bit_field_); }
|
||||
|
||||
bool is_set() const { return kind() != kEmpty; }
|
||||
|
||||
uint32_t index() const {
|
||||
DCHECK_EQ(kind(), kRefFunc);
|
||||
return ValueField::decode(bit_field_);
|
||||
}
|
||||
|
||||
HeapType::Representation repr() const {
|
||||
DCHECK_EQ(kind(), kRefNull);
|
||||
return static_cast<HeapType::Representation>(
|
||||
ValueField::decode(bit_field_));
|
||||
}
|
||||
|
||||
int32_t i32_value() const {
|
||||
DCHECK_EQ(kind(), kI32Const);
|
||||
return ValueField::decode(bit_field_);
|
||||
}
|
||||
|
||||
WireBytesRef wire_bytes_ref() const {
|
||||
DCHECK_EQ(kind(), kWireBytesRef);
|
||||
return WireBytesRef(OffsetField::decode(bit_field_),
|
||||
LengthField::decode(bit_field_));
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kValueBits = 32;
|
||||
static constexpr int kLengthBits = 30;
|
||||
static constexpr int kOffsetBits = 30;
|
||||
static constexpr int kKindBits = 3;
|
||||
|
||||
// There are two possible combinations of fields: offset + length + kind if
|
||||
// kind = kWireBytesRef, or value + kind for anything else.
|
||||
using ValueField = base::BitField<uint32_t, 0, kValueBits, uint64_t>;
|
||||
using OffsetField = base::BitField<uint32_t, 0, kOffsetBits, uint64_t>;
|
||||
using LengthField = OffsetField::Next<uint32_t, kLengthBits>;
|
||||
using KindField = LengthField::Next<Kind, kKindBits>;
|
||||
|
||||
// Make sure we reserve enough bits for a {WireBytesRef}'s length and offset.
|
||||
static_assert(kV8MaxWasmModuleSize <= LengthField::kMax + 1);
|
||||
static_assert(kV8MaxWasmModuleSize <= OffsetField::kMax + 1);
|
||||
// Make sure kind fits in kKindBits.
|
||||
static_assert(kLastKind <= KindField::kMax + 1);
|
||||
|
||||
explicit ConstantExpression(uint64_t bit_field) : bit_field_(bit_field) {}
|
||||
|
||||
uint64_t bit_field_;
|
||||
};
|
||||
|
||||
// We want to keep {ConstantExpression} small to reduce memory usage during
|
||||
// compilation/instantiation.
|
||||
static_assert(sizeof(ConstantExpression) <= 8);
|
||||
|
||||
// Static representation of a wasm global variable.
|
||||
struct WasmGlobal {
|
||||
ValueType type; // type of the global.
|
||||
|
@ -298,7 +298,7 @@ std::ostream& operator<<(std::ostream& os, const PrintName& name) {
|
||||
class InitExprInterface {
|
||||
public:
|
||||
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
|
||||
static constexpr DecodingMode decoding_mode = kInitExpression;
|
||||
static constexpr DecodingMode decoding_mode = kConstantExpression;
|
||||
|
||||
struct Value : public ValueBase<validate> {
|
||||
WasmInitExpr init_expr;
|
||||
@ -538,7 +538,7 @@ void DecodeAndAppendInitExpr(StdoutStream& os, Zone* zone,
|
||||
module_bytes.start() + ref.end_offset());
|
||||
WasmFeatures detected;
|
||||
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface,
|
||||
kInitExpression>
|
||||
kConstantExpression>
|
||||
decoder(zone, module, WasmFeatures::All(), &detected, body, zone);
|
||||
|
||||
decoder.DecodeFunctionBody();
|
||||
|
Loading…
Reference in New Issue
Block a user