Commit d68a48e5 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Decode struct types

Behind --experimental-wasm-gc flag.

Bug: v8:7748
Change-Id: Ib96af9c5bde33f1b88862286a37872dbe70d856b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154198
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67251}
parent b89397c5
......@@ -3062,6 +3062,7 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/signature-map.h",
"src/wasm/streaming-decoder.cc",
"src/wasm/streaming-decoder.h",
"src/wasm/struct-types.h",
"src/wasm/value-type.h",
"src/wasm/wasm-arguments.h",
"src/wasm/wasm-code-manager.cc",
......
......@@ -2785,7 +2785,7 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
LoadIndirectFunctionTable(table_index, &ift_size, &ift_sig_ids, &ift_targets,
&ift_instances);
const wasm::FunctionSig* sig = env_->module->signatures[sig_index];
const wasm::FunctionSig* sig = env_->module->signature(sig_index);
MachineOperatorBuilder* machine = mcgraph()->machine();
Node* key = args[0];
......
......@@ -1018,10 +1018,10 @@ class WasmDecoder : public Decoder {
inline bool Complete(const byte* pc, CallIndirectImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr &&
imm.sig_index < module_->signatures.size())) {
module_->has_signature(imm.sig_index))) {
return false;
}
imm.sig = module_->signatures[imm.sig_index];
imm.sig = module_->signature(imm.sig_index);
return true;
}
......@@ -1116,17 +1116,17 @@ class WasmDecoder : public Decoder {
inline bool Complete(BlockTypeImmediate<validate>& imm) {
if (imm.type != kWasmBottom) return true;
if (!VALIDATE(module_ && imm.sig_index < module_->signatures.size())) {
if (!VALIDATE(module_ && module_->has_signature(imm.sig_index))) {
return false;
}
imm.sig = module_->signatures[imm.sig_index];
imm.sig = module_->signature(imm.sig_index);
return true;
}
inline bool Validate(BlockTypeImmediate<validate>& imm) {
if (!Complete(imm)) {
errorf(pc_, "block type index %u out of bounds (%zu signatures)",
imm.sig_index, module_ ? module_->signatures.size() : 0);
errorf(pc_, "block type index %u out of bounds (%zu types)",
imm.sig_index, module_ ? module_->types.size() : 0);
return false;
}
return true;
......
......@@ -13,6 +13,8 @@
#include "src/utils/ostreams.h"
#include "src/wasm/decoder.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/struct-types.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-limits.h"
......@@ -550,14 +552,41 @@ class ModuleDecoderImpl : public Decoder {
void DecodeTypeSection() {
uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes);
module_->signatures.reserve(signatures_count);
module_->types.reserve(signatures_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->signatures.push_back(s);
uint32_t id = s ? module_->signature_map.FindOrInsert(*s) : 0;
module_->signature_ids.push_back(id);
uint8_t kind = consume_u8("type kind");
switch (kind) {
case kWasmFunctionTypeCode: {
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s);
break;
}
case kWasmStructTypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(), "struct types are part of the GC proposal");
break;
}
const StructType* s = consume_struct(module_->signature_zone.get());
module_->add_struct_type(s);
// TODO(7748): Should we canonicalize struct types, like
// {signature_map} does for function signatures?
break;
}
case kWasmArrayTypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(), "array types are part of the GC proposal");
break;
}
const ArrayType* type = consume_array(module_->signature_zone.get());
module_->add_array_type(type);
break;
}
default:
errorf(pc(), "unknown type form: %d", kind);
break;
}
}
module_->signature_map.Freeze();
}
......@@ -1257,6 +1286,8 @@ class ModuleDecoderImpl : public Decoder {
const WasmModule* module,
std::unique_ptr<WasmFunction> function) {
pc_ = start_;
expect_u8("type form", kWasmFunctionTypeCode);
if (!ok()) return FunctionResult{std::move(intermediate_error_)};
function->sig = consume_sig(zone);
function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)};
......@@ -1274,6 +1305,7 @@ class ModuleDecoderImpl : public Decoder {
// Decodes a single function signature at {start}.
const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) {
pc_ = start;
if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr;
const FunctionSig* result = consume_sig(zone);
return ok() ? result : nullptr;
}
......@@ -1438,13 +1470,13 @@ class ModuleDecoderImpl : public Decoder {
uint32_t consume_sig_index(WasmModule* module, const FunctionSig** sig) {
const byte* pos = pc_;
uint32_t sig_index = consume_u32v("signature index");
if (sig_index >= module->signatures.size()) {
if (!module->has_signature(sig_index)) {
errorf(pos, "signature index %u out of bounds (%d signatures)", sig_index,
static_cast<int>(module->signatures.size()));
static_cast<int>(module->types.size()));
*sig = nullptr;
return 0;
}
*sig = module->signatures[sig_index];
*sig = module->signature(sig_index);
return sig_index;
}
......@@ -1773,8 +1805,7 @@ class ModuleDecoderImpl : public Decoder {
}
const FunctionSig* consume_sig(Zone* zone) {
if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr;
// parse parameter types
// Parse parameter types.
uint32_t param_count =
consume_count("param count", kV8MaxWasmFunctionParams);
if (failed()) return nullptr;
......@@ -1784,7 +1815,7 @@ class ModuleDecoderImpl : public Decoder {
params.push_back(param);
}
std::vector<ValueType> returns;
// parse return types
// Parse return types.
const size_t max_return_count = enabled_features_.has_mv()
? kV8MaxWasmFunctionMultiReturns
: kV8MaxWasmFunctionReturns;
......@@ -1806,6 +1837,28 @@ class ModuleDecoderImpl : public Decoder {
return new (zone) FunctionSig(return_count, param_count, buffer);
}
const StructType* consume_struct(Zone* zone) {
// TODO(7748): Introduce a proper maximum.
uint32_t field_count = consume_count("field count", 999);
if (failed()) return nullptr;
std::vector<ValueType> fields;
for (uint32_t i = 0; ok() && i < field_count; ++i) {
ValueType field = consume_value_type();
fields.push_back(field);
}
if (failed()) return nullptr;
ValueType* buffer = zone->NewArray<ValueType>(field_count);
for (uint32_t i = 0; i < field_count; i++) buffer[i] = fields[i];
uint32_t* offsets = zone->NewArray<uint32_t>(field_count);
return new (zone) StructType(field_count, offsets, buffer);
}
const ArrayType* consume_array(Zone* zone) {
ValueType field = consume_value_type();
if (failed()) return nullptr;
return new (zone) ArrayType(field);
}
// Consume the attribute field of an exception.
uint32_t consume_exception_attribute() {
const byte* pos = pc_;
......
// Copyright 2020 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.
#ifndef V8_WASM_STRUCT_TYPES_H_
#define V8_WASM_STRUCT_TYPES_H_
#include "src/base/iterator.h"
#include "src/base/macros.h"
#include "src/common/globals.h"
#include "src/wasm/value-type.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace wasm {
class StructType : public ZoneObject {
public:
StructType(uint32_t field_count, uint32_t* field_offsets,
const ValueType* reps)
: field_count_(field_count), field_offsets_(field_offsets), reps_(reps) {
InitializeOffsets();
}
uint32_t field_count() const { return field_count_; }
ValueType field(uint32_t index) const {
DCHECK_LT(index, field_count_);
return reps_[index];
}
// Iteration support.
base::iterator_range<const ValueType*> fields() const {
return {reps_, reps_ + field_count_};
}
bool operator==(const StructType& other) const {
if (this == &other) return true;
if (field_count() != other.field_count()) return false;
return std::equal(fields().begin(), fields().end(), other.fields().begin());
}
bool operator!=(const StructType& other) const { return !(*this == other); }
uint32_t field_offset(uint32_t index) const {
DCHECK_LT(index, field_count());
if (index == 0) return 0;
return field_offsets_[index - 1];
}
uint32_t total_fields_size() const {
return field_offsets_[field_count() - 1];
}
void InitializeOffsets() {
uint32_t offset = field(0).element_size_bytes();
for (uint32_t i = 1; i < field_count(); i++) {
uint32_t field_size = field(i).element_size_bytes();
offset = RoundUp(offset, field_size);
field_offsets_[i - 1] = offset;
offset += field_size;
}
offset = RoundUp(offset, kTaggedSize);
field_offsets_[field_count() - 1] = offset;
}
// For incrementally building StructTypes.
class Builder {
public:
Builder(Zone* zone, uint32_t field_count)
: field_count_(field_count),
zone_(zone),
cursor_(0),
buffer_(zone->NewArray<ValueType>(static_cast<int>(field_count))) {}
void AddField(ValueType type) {
DCHECK_LT(cursor_, field_count_);
buffer_[cursor_++] = type;
}
StructType* Build() {
DCHECK_EQ(cursor_, field_count_);
uint32_t* offsets = zone_->NewArray<uint32_t>(field_count_);
return new (zone_) StructType(field_count_, offsets, buffer_);
}
private:
const uint32_t field_count_;
Zone* zone_;
uint32_t cursor_;
ValueType* buffer_;
};
private:
uint32_t field_count_;
uint32_t* field_offsets_;
const ValueType* reps_;
};
class ArrayType : public ZoneObject {
public:
constexpr explicit ArrayType(ValueType rep) : rep_(rep) {}
bool operator==(const ArrayType& other) const { return rep_ == other.rep_; }
bool operator!=(const ArrayType& other) const { return rep_ != other.rep_; }
private:
const ValueType rep_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STRUCT_TYPES_H_
......@@ -38,6 +38,8 @@ enum ValueTypeCode : uint8_t {
};
// Binary encoding of other types.
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
// Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t {
......
......@@ -819,7 +819,7 @@ class SideTable : public ZoneObject {
BlockTypeImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(), &i,
i.pc());
if (imm.type == kWasmBottom) {
imm.sig = module->signatures[imm.sig_index];
imm.sig = module->signature(imm.sig_index);
}
TRACE("control @%u: %s, arity %d->%d\n", i.pc_offset(),
is_loop ? "Loop" : "Block", imm.in_arity(), imm.out_arity());
......@@ -835,7 +835,7 @@ class SideTable : public ZoneObject {
BlockTypeImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(), &i,
i.pc());
if (imm.type == kWasmBottom) {
imm.sig = module->signatures[imm.sig_index];
imm.sig = module->signature(imm.sig_index);
}
TRACE("control @%u: If, arity %d->%d\n", i.pc_offset(),
imm.in_arity(), imm.out_arity());
......@@ -870,7 +870,7 @@ class SideTable : public ZoneObject {
BlockTypeImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(), &i,
i.pc());
if (imm.type == kWasmBottom) {
imm.sig = module->signatures[imm.sig_index];
imm.sig = module->signature(imm.sig_index);
}
TRACE("control @%u: Try, arity %d->%d\n", i.pc_offset(),
imm.in_arity(), imm.out_arity());
......@@ -4046,7 +4046,7 @@ class ThreadImpl {
HandleScope handle_scope(isolate_); // Avoid leaking handles.
uint32_t expected_sig_id = module()->signature_ids[sig_index];
DCHECK_EQ(expected_sig_id,
module()->signature_map.Find(*module()->signatures[sig_index]));
module()->signature_map.Find(*module()->signature(sig_index)));
// Bounds check against table size.
if (entry_index >=
static_cast<uint32_t>(WasmInstanceObject::IndirectFunctionTableSize(
......@@ -4061,7 +4061,7 @@ class ThreadImpl {
return {ExternalCallResult::SIGNATURE_MISMATCH};
}
const FunctionSig* signature = module()->signatures[sig_index];
const FunctionSig* signature = module()->signature(sig_index);
Handle<Object> object_ref = handle(entry.object_ref(), isolate_);
WasmCode* code = GetTargetCode(isolate_, entry.target());
......
......@@ -657,11 +657,11 @@ size_t EstimateStoredSize(const WasmModule* module) {
return sizeof(WasmModule) + VectorSize(module->globals) +
(module->signature_zone ? module->signature_zone->allocation_size()
: 0) +
VectorSize(module->signatures) + VectorSize(module->signature_ids) +
VectorSize(module->functions) + VectorSize(module->data_segments) +
VectorSize(module->tables) + VectorSize(module->import_table) +
VectorSize(module->export_table) + VectorSize(module->exceptions) +
VectorSize(module->elem_segments);
VectorSize(module->types) + VectorSize(module->type_kinds) +
VectorSize(module->signature_ids) + VectorSize(module->functions) +
VectorSize(module->data_segments) + VectorSize(module->tables) +
VectorSize(module->import_table) + VectorSize(module->export_table) +
VectorSize(module->exceptions) + VectorSize(module->elem_segments);
}
size_t PrintSignature(Vector<char> buffer, const wasm::FunctionSig* sig) {
......
......@@ -12,6 +12,7 @@
#include "src/handles/handles.h"
#include "src/utils/vector.h"
#include "src/wasm/signature-map.h"
#include "src/wasm/struct-types.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-opcodes.h"
......@@ -248,6 +249,17 @@ class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
std::unique_ptr<AsmJsOffsets> decoded_offsets_;
};
struct TypeDefinition {
explicit TypeDefinition(const FunctionSig* sig) : function_sig(sig) {}
explicit TypeDefinition(const StructType* type) : struct_type(type) {}
explicit TypeDefinition(const ArrayType* type) : array_type(type) {}
union {
const FunctionSig* function_sig;
const StructType* struct_type;
const ArrayType* array_type;
};
};
// Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone;
......@@ -272,8 +284,44 @@ struct V8_EXPORT_PRIVATE WasmModule {
uint32_t num_declared_data_segments = 0; // From the DataCount section.
WireBytesRef code = {0, 0};
WireBytesRef name = {0, 0};
std::vector<const FunctionSig*> signatures; // by signature index
std::vector<uint32_t> signature_ids; // by signature index
std::vector<TypeDefinition> types; // by type index
std::vector<uint8_t> type_kinds; // by type index
std::vector<uint32_t> signature_ids; // by signature index
void add_signature(const FunctionSig* sig) {
types.push_back(TypeDefinition(sig));
type_kinds.push_back(kWasmFunctionTypeCode);
uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
signature_ids.push_back(canonical_id);
}
const FunctionSig* signature(uint32_t index) const {
DCHECK(type_kinds[index] == kWasmFunctionTypeCode);
return types[index].function_sig;
}
bool has_signature(uint32_t index) const {
return index < types.size() && type_kinds[index] == kWasmFunctionTypeCode;
}
void add_struct_type(const StructType* type) {
types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmStructTypeCode);
}
const StructType* struct_type(uint32_t index) const {
DCHECK(type_kinds[index] == kWasmStructTypeCode);
return types[index].struct_type;
}
bool has_struct(uint32_t index) const {
return index < types.size() && type_kinds[index] == kWasmStructTypeCode;
}
void add_array_type(const ArrayType* type) {
types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmArrayTypeCode);
}
const ArrayType* array_type(uint32_t index) const {
DCHECK(type_kinds[index] == kWasmArrayTypeCode);
return types[index].array_type;
}
bool has_array(uint32_t index) const {
return index < types.size() && type_kinds[index] == kWasmArrayTypeCode;
}
std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments;
std::vector<WasmTable> tables;
......
......@@ -150,12 +150,10 @@ class TestingModuleBuilder {
}
byte AddSignature(const FunctionSig* sig) {
DCHECK_EQ(test_module_->signatures.size(),
test_module_->signature_ids.size());
test_module_->signatures.push_back(sig);
auto canonical_sig_num = test_module_->signature_map.FindOrInsert(*sig);
test_module_->signature_ids.push_back(canonical_sig_num);
size_t size = test_module_->signatures.size();
// TODO(7748): This will need updating for struct/array types support.
DCHECK_EQ(test_module_->types.size(), test_module_->signature_ids.size());
test_module_->add_signature(sig);
size_t size = test_module_->types.size();
CHECK_GT(127, size);
return static_cast<byte>(size - 1);
}
......
......@@ -172,7 +172,14 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
<< glob.mutability << ");\n";
}
for (const FunctionSig* sig : module->signatures) {
// TODO(7748): Support array/struct types.
#if DEBUG
for (uint8_t kind : module->type_kinds) {
DCHECK_EQ(kWasmFunctionTypeCode, kind);
}
#endif
for (TypeDefinition type : module->types) {
const FunctionSig* sig = type.function_sig;
os << "builder.addType(makeSig(" << PrintParameters(sig) << ", "
<< PrintReturns(sig) << "));\n";
}
......
......@@ -217,9 +217,9 @@ class TestModuleBuilder {
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(const FunctionSig* sig) {
mod.signatures.push_back(sig);
CHECK_LE(mod.signatures.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.signatures.size() - 1);
mod.add_signature(sig);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.types.size() - 1);
}
byte AddFunction(const FunctionSig* sig, bool declared = true) {
mod.functions.push_back({sig, // sig
......@@ -239,7 +239,7 @@ class TestModuleBuilder {
}
byte AddException(WasmExceptionSig* sig) {
mod.exceptions.emplace_back(sig);
CHECK_LE(mod.signatures.size(), kMaxByteSizedLeb128);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.exceptions.size() - 1);
}
......
......@@ -827,15 +827,15 @@ TEST_F(WasmModuleVerifyTest, MultipleSignatures) {
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(3u, result.value()->signatures.size());
if (result.value()->signatures.size() == 3) {
EXPECT_EQ(0u, result.value()->signatures[0]->return_count());
EXPECT_EQ(1u, result.value()->signatures[1]->return_count());
EXPECT_EQ(1u, result.value()->signatures[2]->return_count());
EXPECT_EQ(0u, result.value()->signatures[0]->parameter_count());
EXPECT_EQ(1u, result.value()->signatures[1]->parameter_count());
EXPECT_EQ(2u, result.value()->signatures[2]->parameter_count());
EXPECT_EQ(3u, result.value()->types.size());
if (result.value()->types.size() == 3) {
EXPECT_EQ(0u, result.value()->signature(0)->return_count());
EXPECT_EQ(1u, result.value()->signature(1)->return_count());
EXPECT_EQ(1u, result.value()->signature(2)->return_count());
EXPECT_EQ(0u, result.value()->signature(0)->parameter_count());
EXPECT_EQ(1u, result.value()->signature(1)->parameter_count());
EXPECT_EQ(2u, result.value()->signature(2)->parameter_count());
}
EXPECT_OFF_END_FAILURE(data, 1);
......@@ -1037,7 +1037,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
if (result.ok()) {
EXPECT_EQ(1u, result.value()->signatures.size());
EXPECT_EQ(1u, result.value()->types.size());
EXPECT_EQ(1u, result.value()->functions.size());
EXPECT_EQ(1u, result.value()->tables.size());
EXPECT_EQ(1u, result.value()->tables[0].initial_size);
......@@ -1123,7 +1123,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) {
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(1u, result.value()->signatures.size());
EXPECT_EQ(1u, result.value()->types.size());
EXPECT_EQ(1u, result.value()->functions.size());
EXPECT_EQ(1u, result.value()->tables.size());
EXPECT_EQ(1u, result.value()->tables[0].initial_size);
......@@ -1151,7 +1151,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(2u, result.value()->signatures.size());
EXPECT_EQ(2u, result.value()->types.size());
EXPECT_EQ(4u, result.value()->functions.size());
EXPECT_EQ(1u, result.value()->tables.size());
EXPECT_EQ(8u, result.value()->tables[0].initial_size);
......
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