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

[wasm] Refactor initializer expression handling

Design doc: https://bit.ly/3xPxWUe

This CL introduces two main changes:
- Initializer expressions are now decoded by WasmFullDecoder. With
  wasm-gc, initializer expressions are no longer just constants, and
  require complex decoding (including stack tracking). This resulted in
  extensive code duplication.
- Initializer expressions are not stored explicitly by module-decoder as
  an AST (WasmInitExpr), but rather as a WireBytesRef, and are decoded
  again during module instantiation. This should reduce memory
  consumption for globals and other module elements with initializer
  expressions (which has been observed in the 40MB range in some
  real-world benchmarks.

Summary of changes:
- Add a static parameter {kFunctionBody, kInitExpression} to the
  WasmDecoder. Use it to specialize validation to function bodies/init.
  expressions.
- Introduce a new Interface for the WasmFullDecoder for init.
  expressions.
- Differentiate between constant and non-constant opcodes in
  WasmFullDecoder.
- Change representation of init. expressions in WasmModule to
  WireBytesRef.
- Reimplement EvaluateInitExpression in module-instantiate to re-decode
  initializer expressions.
- Remove some now-invalid module decoder tests.

Pending changes:
- Also refactor initializer expressions for element segment entries.
- Reintroduce deleted tests.

Bug: v8:11895
Change-Id: I76512bfe1386c8338667d30fa6db93880a1e4b42
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2972910Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75476}
parent 444fdfde
......@@ -1937,6 +1937,8 @@ 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",
......
......@@ -3167,6 +3167,7 @@ v8_header_set("v8_internal_headers") {
"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",
......@@ -4103,6 +4104,7 @@ v8_source_set("v8_base_without_compiler") {
"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",
......
This diff is collapsed.
// 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/init-expr-interface.h"
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/objects/fixed-array-inl.h"
#include "src/objects/oddball.h"
#include "src/roots/roots-inl.h"
#include "src/wasm/decoder.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
namespace wasm {
void InitExprInterface::I32Const(FullDecoder* decoder, Value* result,
int32_t value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
}
void InitExprInterface::I64Const(FullDecoder* decoder, Value* result,
int64_t value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
}
void InitExprInterface::F32Const(FullDecoder* decoder, Value* result,
float value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
}
void InitExprInterface::F64Const(FullDecoder* decoder, Value* result,
double value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
}
void InitExprInterface::S128Const(FullDecoder* decoder,
Simd128Immediate<validate>& imm,
Value* result) {
if (isolate_ == nullptr) return;
result->runtime_value = WasmValue(imm.value, kWasmS128);
}
void InitExprInterface::RefNull(FullDecoder* decoder, ValueType type,
Value* result) {
if (isolate_ == nullptr) return;
result->runtime_value = WasmValue(isolate_->factory()->null_value(), type);
}
void InitExprInterface::RefFunc(FullDecoder* decoder, uint32_t function_index,
Value* result) {
if (isolate_ != nullptr) {
auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction(
isolate_, instance_, function_index);
result->runtime_value = WasmValue(
function, ValueType::Ref(module_->functions[function_index].sig_index,
kNonNullable));
} else {
outer_module_->functions[function_index].declared = true;
}
}
void InitExprInterface::GlobalGet(FullDecoder* decoder, Value* result,
const GlobalIndexImmediate<validate>& imm) {
if (isolate_ == nullptr) return;
const WasmGlobal& global = module_->globals[imm.index];
result->runtime_value =
global.type.is_numeric()
? WasmValue(GetRawUntaggedGlobalPtr(global), global.type)
: WasmValue(handle(tagged_globals_->get(global.offset), isolate_),
global.type);
}
void InitExprInterface::StructNewWithRtt(
FullDecoder* decoder, const StructIndexImmediate<validate>& imm,
const Value& rtt, const Value args[], Value* result) {
if (isolate_ == nullptr) return;
std::vector<WasmValue> field_values(imm.struct_type->field_count());
for (size_t i = 0; i < field_values.size(); i++) {
field_values[i] = args[i].runtime_value;
}
result->runtime_value =
WasmValue(isolate_->factory()->NewWasmStruct(
imm.struct_type, field_values.data(),
Handle<Map>::cast(rtt.runtime_value.to_ref())),
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) {
if (isolate_ == nullptr) return;
std::vector<WasmValue> element_values;
for (Value elem : elements) element_values.push_back(elem.runtime_value);
result->runtime_value =
WasmValue(isolate_->factory()->NewWasmArray(
imm.array_type, element_values,
Handle<Map>::cast(rtt.runtime_value.to_ref())),
ValueType::Ref(HeapType(imm.index), kNonNullable));
}
void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,
Value* result) {
if (isolate_ == nullptr) return;
result->runtime_value = WasmValue(
handle(instance_->managed_object_maps().get(type_index), isolate_),
ValueType::Rtt(type_index, 0));
}
void InitExprInterface::RttSub(FullDecoder* decoder, uint32_t type_index,
const Value& parent, Value* result,
WasmRttSubMode mode) {
if (isolate_ == nullptr) return;
ValueType type = parent.type.has_depth()
? ValueType::Rtt(type_index, parent.type.depth() + 1)
: ValueType::Rtt(type_index);
result->runtime_value =
WasmValue(Handle<Object>::cast(AllocateSubRtt(
isolate_, instance_, type_index,
Handle<Map>::cast(parent.runtime_value.to_ref()), mode)),
type);
}
void InitExprInterface::DoReturn(FullDecoder* decoder,
uint32_t /*drop_values*/) {
end_found_ = true;
// End decoding on "end".
decoder->set_end(decoder->pc() + 1);
if (isolate_ != nullptr) result_ = decoder->stack_value(1)->runtime_value;
}
byte* InitExprInterface::GetRawUntaggedGlobalPtr(const WasmGlobal& global) {
return reinterpret_cast<byte*>(untagged_globals_->backing_store()) +
global.offset;
}
} // namespace wasm
} // namespace internal
} // namespace v8
// 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.
#if !V8_ENABLE_WEBASSEMBLY
#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_
#include "src/wasm/decoder.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/wasm-value.h"
namespace v8 {
namespace internal {
class WasmInstanceObject;
class JSArrayBuffer;
namespace wasm {
// An interface for WasmFullDecoder used to decode initializer 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 {
public:
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
static constexpr DecodingMode decoding_mode = kInitExpression;
struct Value : public ValueBase<validate> {
WasmValue runtime_value;
template <typename... Args>
explicit Value(Args&&... args) V8_NOEXCEPT
: ValueBase(std::forward<Args>(args)...) {}
};
using Control = ControlBase<Value, validate>;
using FullDecoder =
WasmFullDecoder<validate, InitExprInterface, decoding_mode>;
InitExprInterface(const WasmModule* module, Isolate* isolate,
Handle<WasmInstanceObject> instance,
Handle<FixedArray> tagged_globals,
Handle<JSArrayBuffer> untagged_globals)
: module_(module),
outer_module_(nullptr),
isolate_(isolate),
instance_(instance),
tagged_globals_(tagged_globals),
untagged_globals_(untagged_globals) {
DCHECK_NOT_NULL(isolate);
}
explicit InitExprInterface(WasmModule* outer_module)
: module_(nullptr), outer_module_(outer_module), isolate_(nullptr) {}
#define EMPTY_INTERFACE_FUNCTION(name, ...) \
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) {}
INTERFACE_META_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
INTERFACE_NON_CONSTANT_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
#undef EMPTY_INTERFACE_FUNCTION
#define DECLARE_INTERFACE_FUNCTION(name, ...) \
void name(FullDecoder* decoder, ##__VA_ARGS__);
INTERFACE_CONSTANT_FUNCTIONS(DECLARE_INTERFACE_FUNCTION)
#undef DECLARE_INTERFACE_FUNCTION
WasmValue result() {
DCHECK_NOT_NULL(isolate_);
return result_;
}
bool end_found() { return end_found_; }
private:
byte* GetRawUntaggedGlobalPtr(const WasmGlobal& global);
bool end_found_ = false;
WasmValue result_;
const WasmModule* module_;
WasmModule* outer_module_;
Isolate* isolate_;
Handle<WasmInstanceObject> instance_;
Handle<FixedArray> tagged_globals_;
Handle<JSArrayBuffer> untagged_globals_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_INIT_EXPR_INTERFACE_H_
This diff is collapsed.
......@@ -171,7 +171,7 @@ V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunctionForTesting(
const WasmModule* module, const byte* function_start,
const byte* function_end, Counters* counters);
V8_EXPORT_PRIVATE WasmInitExpr
V8_EXPORT_PRIVATE WireBytesRef
DecodeWasmInitExprForTesting(const WasmFeatures& enabled, const byte* start,
const byte* end, ValueType expected);
......
......@@ -15,6 +15,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/module-compiler.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-engine.h"
......@@ -22,6 +23,7 @@
#include "src/wasm/wasm-import-wrapper-cache.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes-inl.h"
#include "src/wasm/wasm-subtyping.h"
#include "src/wasm/wasm-value.h"
......@@ -387,7 +389,7 @@ class InstanceBuilder {
// Process initialization of globals.
void InitGlobals(Handle<WasmInstanceObject> instance);
WasmValue EvaluateInitExpression(const WasmInitExpr& init,
WasmValue EvaluateInitExpression(WireBytesRef init, ValueType expected,
Handle<WasmInstanceObject> instance);
// Process the exports, creating wrappers for functions, tables, memories,
......@@ -865,7 +867,8 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
size_t dest_offset;
if (module_->is_memory64) {
uint64_t dest_offset_64 =
EvaluateInitExpression(segment.dest_addr, instance).to_u64();
EvaluateInitExpression(segment.dest_addr, kWasmI64, instance)
.to_u64();
// Clamp to {std::numeric_limits<size_t>::max()}, which is always an
// invalid offset.
DCHECK_GT(std::numeric_limits<size_t>::max(), instance->memory_size());
......@@ -873,7 +876,8 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
dest_offset_64, uint64_t{std::numeric_limits<size_t>::max()}));
} else {
dest_offset =
EvaluateInitExpression(segment.dest_addr, instance).to_u32();
EvaluateInitExpression(segment.dest_addr, kWasmI32, instance)
.to_u32();
}
if (!base::IsInBounds<size_t>(dest_offset, size, instance->memory_size())) {
......@@ -1532,78 +1536,25 @@ T* InstanceBuilder::GetRawUntaggedGlobalPtr(const WasmGlobal& global) {
}
WasmValue InstanceBuilder::EvaluateInitExpression(
const WasmInitExpr& init, Handle<WasmInstanceObject> instance) {
switch (init.kind()) {
case WasmInitExpr::kNone:
UNREACHABLE();
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: {
const WasmGlobal& global = module_->globals[init.immediate().index];
if (global.type.is_numeric()) {
return WasmValue(GetRawUntaggedGlobalPtr<byte>(global), global.type);
} else {
return WasmValue(handle(tagged_globals_->get(global.offset), isolate_),
init.type(module_, enabled_));
}
}
case WasmInitExpr::kStructNewWithRtt: {
const StructType* type = module_->struct_type(init.immediate().index);
std::vector<WasmValue> fields(type->field_count());
for (uint32_t i = 0; i < type->field_count(); i++) {
fields[i] = EvaluateInitExpression(init.operands()[i], instance);
}
auto rtt = Handle<Map>::cast(
EvaluateInitExpression(init.operands().back(), instance).to_ref());
return WasmValue(
isolate_->factory()->NewWasmStruct(type, fields.data(), rtt),
init.type(module_, enabled_));
}
case WasmInitExpr::kArrayInit: {
const ArrayType* type = module_->array_type(init.immediate().index);
std::vector<WasmValue> elements(init.operands().size() - 1);
for (uint32_t i = 0; i < elements.size(); i++) {
elements[i] = EvaluateInitExpression(init.operands()[i], instance);
}
auto rtt = Handle<Map>::cast(
EvaluateInitExpression(init.operands().back(), instance).to_ref());
return WasmValue(isolate_->factory()->NewWasmArray(type, elements, rtt),
init.type(module_, enabled_));
}
case WasmInitExpr::kRttCanon: {
int map_index = init.immediate().index;
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;
WasmValue parent = EvaluateInitExpression(init.operands()[0], 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_));
}
}
WireBytesRef init, ValueType expected,
Handle<WasmInstanceObject> instance) {
AccountingAllocator allocator;
Zone zone(&allocator, "consume_init_expr");
base::Vector<const byte> module_bytes =
instance->module_object().native_module()->wire_bytes();
FunctionBody body(FunctionSig::Build(&zone, {expected}, {}), init.offset(),
module_bytes.begin() + init.offset(),
module_bytes.begin() + init.end_offset());
WasmFeatures detected;
// We use kFullValidation so we do not have to create another instance of
// WasmFullDecoder, which would cost us >50Kb binary code size.
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface, kInitExpression>
decoder(&zone, module_, WasmFeatures::All(), &detected, body, module_,
isolate_, instance, tagged_globals_, untagged_globals_);
decoder.DecodeFunctionBody();
return decoder.interface().result();
}
// Process initialization of globals.
......@@ -1611,9 +1562,10 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
for (const WasmGlobal& global : module_->globals) {
if (global.mutability && global.imported) continue;
// Happens with imported globals.
if (global.init.kind() == WasmInitExpr::kNone) continue;
if (!global.init.is_set()) continue;
WasmValue value = EvaluateInitExpression(global.init, instance);
WasmValue value =
EvaluateInitExpression(global.init, global.type, instance);
if (global.type.is_reference()) {
tagged_globals_->set(global.offset, *value.to_ref());
......@@ -1832,16 +1784,16 @@ void InstanceBuilder::InitializeIndirectFunctionTables(
}
if (!table.type.is_defaultable()) {
// Function constant is currently the only viable initializer.
DCHECK(table.initial_value.kind() == WasmInitExpr::kRefFuncConst);
uint32_t func_index = table.initial_value.immediate().index;
WasmValue value =
EvaluateInitExpression(table.initial_value, table.type, instance);
DCHECK(WasmExportedFunction::IsWasmExportedFunction(*value.to_ref()));
auto wasm_external_function =
Handle<WasmExportedFunction>::cast(value.to_ref());
uint32_t func_index = wasm_external_function->function_index();
uint32_t sig_id =
module_->canonicalized_type_ids[module_->functions[func_index]
.sig_index];
MaybeHandle<WasmExternalFunction> wasm_external_function =
WasmInstanceObject::GetWasmExternalFunction(isolate_, instance,
func_index);
auto table_object = handle(
WasmTableObject::cast(instance->tables().get(table_index)), isolate_);
for (uint32_t entry_index = 0; entry_index < table.initial_size;
......@@ -1857,8 +1809,7 @@ void InstanceBuilder::InitializeIndirectFunctionTables(
WasmTableObject::SetFunctionTablePlaceholder(
isolate_, table_object, entry_index, instance, func_index);
} else {
table_object->entries().set(
entry_index, *wasm_external_function.ToHandleChecked());
table_object->entries().set(entry_index, *wasm_external_function);
}
// UpdateDispatchTables() updates all other dispatch tables, since
// we have not yet added the dispatch table we are currently building.
......@@ -1964,7 +1915,8 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
uint32_t table_index = elem_segment.table_index;
uint32_t dst =
EvaluateInitExpression(elem_segment.offset, instance).to_u32();
EvaluateInitExpression(elem_segment.offset, kWasmI32, instance)
.to_u32();
uint32_t src = 0;
size_t count = elem_segment.entries.size();
......
......@@ -71,7 +71,7 @@ struct WasmFunction {
struct WasmGlobal {
ValueType type; // type of the global.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
WireBytesRef init; // the initialization expression of the global.
union {
uint32_t index; // index of imported mutable global.
uint32_t offset; // offset into global memory (if not imported & mutable).
......@@ -95,13 +95,13 @@ struct WasmException {
// Static representation of a wasm data segment.
struct WasmDataSegment {
// Construct an active segment.
explicit WasmDataSegment(WasmInitExpr dest_addr)
explicit WasmDataSegment(WireBytesRef dest_addr)
: dest_addr(std::move(dest_addr)), active(true) {}
// Construct a passive segment, which has no dest_addr.
WasmDataSegment() : active(false) {}
WasmInitExpr dest_addr; // destination memory address of the data.
WireBytesRef dest_addr; // destination memory address of the data.
WireBytesRef source; // start offset in the module bytes.
bool active = true; // true if copied automatically during instantiation.
};
......@@ -109,7 +109,7 @@ struct WasmDataSegment {
// Static representation of wasm element segment (table initializer).
struct WasmElemSegment {
// Construct an active segment.
WasmElemSegment(ValueType type, uint32_t table_index, WasmInitExpr offset)
WasmElemSegment(ValueType type, uint32_t table_index, WireBytesRef offset)
: type(type),
table_index(table_index),
offset(std::move(offset)),
......@@ -134,7 +134,7 @@ struct WasmElemSegment {
ValueType type;
uint32_t table_index;
WasmInitExpr offset;
WireBytesRef offset;
std::vector<WasmInitExpr> entries;
enum Status {
kStatusActive, // copied automatically during instantiation.
......@@ -377,7 +377,7 @@ struct WasmTable {
bool has_maximum_size = false; // true if there is a maximum size.
bool imported = false; // true if imported.
bool exported = false; // true if exported.
WasmInitExpr initial_value;
WireBytesRef initial_value;
};
inline bool is_asmjs_module(const WasmModule* module) {
......
......@@ -325,7 +325,7 @@ const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) {
byte size = type.element_size_bytes();
global_offset = (global_offset + size - 1) & ~(size - 1); // align
test_module_->globals.push_back(
{type, true, WasmInitExpr(), {global_offset}, false, false});
{type, true, {}, {global_offset}, false, false});
global_offset += size;
// limit number of globals.
CHECK_LT(global_offset, kMaxGlobalsSize);
......
......@@ -194,6 +194,48 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) {
}
return os << ")";
}
// Appends an initializer expression encoded in {wire_bytes}, in the offset
// contained in {expr}.
// TODO(7748): Find a way to implement other expressions here.
void AppendInitExpr(std::ostream& os, ModuleWireBytes wire_bytes,
WireBytesRef expr) {
Decoder decoder(wire_bytes.module_bytes());
const byte* pc = wire_bytes.module_bytes().begin() + expr.offset();
uint32_t length;
os << "WasmInitExpr.";
switch (static_cast<WasmOpcode>(pc[0])) {
case kExprGlobalGet:
os << "GlobalGet("
<< decoder.read_u32v<Decoder::kNoValidation>(pc + 1, &length);
break;
case kExprI32Const:
os << "I32Const("
<< decoder.read_i32v<Decoder::kNoValidation>(pc + 1, &length);
break;
case kExprI64Const:
os << "I64Const("
<< decoder.read_i64v<Decoder::kNoValidation>(pc + 1, &length);
break;
case kExprF32Const: {
uint32_t result = decoder.read_u32<Decoder::kNoValidation>(pc + 1);
os << "F32Const(" << bit_cast<float>(result);
break;
}
case kExprF64Const: {
uint64_t result = decoder.read_u64<Decoder::kNoValidation>(pc + 1);
os << "F64Const(" << bit_cast<double>(result);
break;
}
case kExprRefFunc:
os << "RefFunc("
<< decoder.read_u32v<Decoder::kNoValidation>(pc + 1, &length);
break;
default:
UNREACHABLE();
}
os << ")";
}
} // namespace
void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
......@@ -249,7 +291,9 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
for (WasmGlobal& glob : module->globals) {
os << "builder.addGlobal(" << ValueTypeToConstantName(glob.type) << ", "
<< glob.mutability << ", " << glob.init << ");\n";
<< glob.mutability << ", ";
AppendInitExpr(os, wire_bytes, glob.init);
os << ");\n";
}
// TODO(7748): Support array/struct types.
......@@ -287,7 +331,9 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
: "Declarative";
os << "builder.add" << status_str << "ElementSegment(";
if (elem_segment.status == WasmElemSegment::kStatusActive) {
os << elem_segment.table_index << ", " << elem_segment.offset << ", ";
os << elem_segment.table_index << ", ";
AppendInitExpr(os, wire_bytes, elem_segment.offset);
os << ", ";
}
os << "[";
for (uint32_t i = 0; i < elem_segment.entries.size(); i++) {
......
......@@ -82,8 +82,7 @@ class TestModuleBuilder {
mod.origin = origin;
}
byte AddGlobal(ValueType type, bool mutability = true) {
mod.globals.push_back(
{type, mutability, WasmInitExpr(), {0}, false, false});
mod.globals.push_back({type, mutability, {}, {0}, false, false});
CHECK_LE(mod.globals.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.globals.size() - 1);
}
......
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