blob: 867392422b56799611fb9293f10e90d34abff47a [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/assembler/assembler_wasm.h"
#include "platform/text_buffer.h"
#include "vm/log.h"
namespace wasm {
namespace {
using ::dart::FLAG_trace_wasm_compilation; // For use in WasmTrace.
using ::dart::GrowableArray;
using ::dart::Log; // For use in WasmTrace.
using ::dart::OS;
using ::dart::ReAlloc;
using ::dart::SExpInteger; // Uses int64_t internally, so it should fit most
// serialized Wasm integers.
using ::dart::SExpList;
using ::dart::SExpression;
using ::dart::SExpSymbol;
using ::dart::ValueObject;
using ::dart::WriteStream;
using ::dart::Zone;
} // namespace
void WasmTrace(const char* format, ...) {
if (FLAG_trace_wasm_compilation) {
va_list args;
va_start(args, format);
THR_VPrint(format, args);
va_end(args);
}
}
// PushBytecountFrontScope is an instance of the RAII programming pattern.
// It is used in the WRITE_BYTECOUNT() macro, which is to only be used
// inside the Output methods of the Wasm classes (i.e. when a WriteStream*
// stream is available for use).
// Example usage and semantics:
//
// The following *scoped* code inside an Output method:
// {
// WRITE_BYTECOUNT();
// WRITE_UNSIGNED(23);
// WRITE_UNSIGNED(98);
// }
//
// Has the same effect as the following:
// {
// WRITE_UNSIGNED(2); // Since 2 bytes follow in the enclosing scope.
// WRITE_UNSIGNED(23);
// WRITE_UNSIGNED(98);
// }
//
// Both code fragments write the list [2, 23, 98].
class PushBytecountFrontScope : public ValueObject {
public:
// The current WriteStream* is replaced by new stream (and internal buffer)
// each time the WRITE_BYTECOUNT() macro occurs in a scope. Then,
// upon leaving the scope, the old stream (and bufer) is restored, with
// the new contents and their bytecount appended (the latter coming first).
explicit PushBytecountFrontScope(WriteStream** current_stream_location)
: replacement_buffer_(nullptr),
old_stream_location_(current_stream_location),
old_stream_(*current_stream_location),
new_stream_(&replacement_buffer_, old_stream_->alloc(), 16) {
*current_stream_location = &new_stream_;
}
~PushBytecountFrontScope() {
const intptr_t bytes_written = new_stream_.bytes_written();
WasmTrace("Copying %" Pd " bytes to parent WriteStream.\n", bytes_written);
// The Wasm specification treats bytes_written as an unsigned 32 bit integer.
// While an overflow is technically possible, code of this size is unlikely
// to occur in practice.
old_stream_->WriteUnsigned(bytes_written);
old_stream_->WriteBytes(replacement_buffer_, bytes_written);
*old_stream_location_ = old_stream_;
}
private:
uint8_t* replacement_buffer_;
WriteStream** const old_stream_location_;
WriteStream* const old_stream_;
WriteStream new_stream_;
DISALLOW_COPY_AND_ASSIGN(PushBytecountFrontScope);
};
#define WRITE_BYTE(m) stream->WriteFixed(static_cast<uint8_t>(m))
#define WRITE_UNSIGNED(m) stream->WriteUnsigned(m)
#define WRITE_SIGNED(m) stream->Write(m)
// Note: in general, this should be UTF8-compliant, but for our purposes
// here we do not need the non ASCII-cases, so this implementation
// of WRITE_STRING works for the time being.
#define WRITE_STRING(m) stream->WriteBytes(m, strlen(m));
// It is not permitted to have two occurences of this macro in the same
// enclosing scope. This is enforced by a variable name clash if this is
// not respected. This requires users to explicitly open a new scope whenever
// they need multiply-nested PushBytecountFrontScopes, consistent with the
// example uses above.
#define WRITE_BYTECOUNT() \
PushBytecountFrontScope push_bytecount_front_scope(&stream)
SExpression* NumType::Serialize(Zone* zone) {
switch (kind_) {
case Kind::kI32:
return new (zone) SExpSymbol("i32");
case Kind::kI64:
return new (zone) SExpSymbol("i64");
case Kind::kF32:
return new (zone) SExpSymbol("f32");
case Kind::kF64:
return new (zone) SExpSymbol("f64");
case Kind::kV128:
return new (zone) SExpSymbol("v128");
default:
UNREACHABLE();
}
}
void NumType::OutputBinary(WriteStream* stream) {
switch (kind_) {
case Kind::kI32:
WRITE_BYTE(0x7F);
break;
case Kind::kI64:
WRITE_BYTE(0x7E);
break;
case Kind::kF32:
WRITE_BYTE(0x7D);
break;
case Kind::kF64:
WRITE_BYTE(0x7C);
break;
case Kind::kV128:
WRITE_BYTE(0x7B);
break;
default:
UNREACHABLE();
}
}
SExpression* RefType::Serialize(Zone* zone) {
ASSERT(heap_type_ != nullptr);
// First, try to use the shorthand notations.
if (!nullable_ && heap_type_->kind_ == HeapType::Kind::kI31) {
// ref i31 = i31ref.
return new (zone) SExpSymbol("i31ref");
} else if (nullable_) {
// ref null {func/extern/any/eq} = {func/extern/any/eq}ref.
switch (heap_type_->kind_) {
case HeapType::Kind::kFunc:
return new (zone) SExpSymbol("funcref");
case HeapType::Kind::kExtern:
return new (zone) SExpSymbol("externref");
case HeapType::Kind::kAny:
return new (zone) SExpSymbol("anyref");
case HeapType::Kind::kEq:
return new (zone) SExpSymbol("eqref");
default:
break;
}
}
// Otherwise, use the general case.
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("ref"));
if (nullable_) {
sexp->Add(new (zone) SExpSymbol("null"));
}
sexp->Add(heap_type_->Serialize(zone));
return sexp;
}
void RefType::OutputBinary(WriteStream* stream) {
ASSERT(heap_type_ != nullptr);
// First, try to use the shorthand notations.
if (!nullable_ && heap_type_->kind_ == HeapType::Kind::kI31) {
// ref i31 = i31ref.
WRITE_SIGNED(-0x16);
return;
} else if (nullable_) {
// ref null {func/extern/any/eq} = {func/extern/any/eq}ref.
switch (heap_type_->kind_) {
case HeapType::Kind::kFunc:
case HeapType::Kind::kExtern:
case HeapType::Kind::kAny:
case HeapType::Kind::kEq:
heap_type_->OutputBinary(stream);
return;
default:
break;
}
}
// Otherwise, use the general case.
if (nullable_) {
WRITE_SIGNED(-0x14);
} else {
WRITE_SIGNED(-0x15);
}
heap_type_->OutputBinary(stream);
}
SExpression* HeapType::Serialize(Zone* zone) {
switch (kind_) {
case Kind::kFunc:
return new (zone) SExpSymbol("func");
case Kind::kExtern:
return new (zone) SExpSymbol("extern");
case Kind::kTypeidx:
return new (zone) SExpInteger(def_type_->index());
case Kind::kAny:
return new (zone) SExpSymbol("any");
case Kind::kEq:
return new (zone) SExpSymbol("eq");
case Kind::kI31:
return new (zone) SExpSymbol("i31");
default:
UNREACHABLE();
}
}
void HeapType::OutputBinary(WriteStream* stream) {
switch (kind_) {
case Kind::kFunc:
WRITE_SIGNED(-0x10);
break;
case Kind::kExtern:
WRITE_SIGNED(-0x11);
break;
case Kind::kTypeidx:
WRITE_SIGNED(def_type_->index());
break;
case Kind::kAny:
// https://github.com/v8/v8/blob/master/src/wasm/wasm-constants.h#L36
FATAL("any/anyref not implemented by V8");
WRITE_SIGNED(-0x12);
break;
case Kind::kEq:
WRITE_SIGNED(-0x13);
break;
case Kind::kI31:
// Note: https://github.com/WebAssembly/gc/blob/master/proposals/gc/MVP.md
// lists this as -0x17, but V8 uses -0x16 in their spec and code:
// - Spec: https://bit.ly/3cWcm6Q
WRITE_SIGNED(-0x16);
break;
default:
UNREACHABLE();
}
}
SExpression* Rtt::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new SExpSymbol("rtt"));
sexp->Add(new SExpInteger(depth_));
sexp->Add(heap_type_->Serialize(zone));
return sexp;
}
void Rtt::OutputBinary(WriteStream* stream) {
WRITE_SIGNED(-0x17);
WRITE_UNSIGNED(depth_);
heap_type_->OutputBinary(stream);
}
SExpression* Field::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(field_type_->Serialize(zone));
sexp->Add(new (zone) SExpSymbol("fieldidx ="));
sexp->Add(new (zone) SExpInteger(index_));
return sexp;
}
void Field::OutputBinary(WriteStream* stream) {
field_type_->OutputBinary(stream);
}
SExpression* FieldType::Serialize(Zone* zone) {
SExpression* sexp = nullptr;
switch (packed_type_) {
case PackedType::kNoType:
sexp = value_type_->Serialize(zone);
break;
case PackedType::kI8:
sexp = new (zone) SExpSymbol("i8");
break;
case PackedType::kI16:
sexp = new (zone) SExpSymbol("i16");
break;
default:
UNREACHABLE();
}
// Add mutable atom if present.
if (mut_) {
const auto sexp_list = new (zone) SExpList(zone);
sexp_list->Add(new (zone) SExpSymbol("mut"));
sexp_list->Add(sexp);
return sexp_list;
} else {
return sexp;
}
}
void FieldType::OutputBinary(WriteStream* stream) {
switch (packed_type_) {
case PackedType::kNoType:
value_type_->OutputBinary(stream);
break;
case PackedType::kI8:
WRITE_BYTE(0x7A);
break;
case PackedType::kI16:
WRITE_BYTE(0x79);
break;
default:
UNREACHABLE();
}
// Add mutable atom if present.
if (mut_) {
WRITE_BYTE(0x01);
} else {
WRITE_BYTE(0x00);
}
}
FuncType::FuncType(Zone* zone, int index)
: DefType(zone, index), param_types_(zone, 16), result_types_(zone, 16) {}
SExpression* FuncType::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("func"));
// Add "param" atoms.
for (ValueType* param_type : param_types_) {
const auto atom = new (zone) SExpList(zone);
atom->Add(new (zone) SExpSymbol("param"));
atom->Add(param_type->Serialize(zone));
sexp->Add(atom);
}
// Add "result" atoms.
for (ValueType* result_type : result_types_) {
const auto atom = new (zone) SExpList(zone);
atom->Add(new (zone) SExpSymbol("result"));
atom->Add(result_type->Serialize(zone));
sexp->Add(atom);
}
return sexp;
}
void FuncType::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x60);
WRITE_UNSIGNED(param_types_.length());
for (ValueType* type : param_types_) {
type->OutputBinary(stream);
}
WRITE_UNSIGNED(result_types_.length());
for (ValueType* type : result_types_) {
type->OutputBinary(stream);
}
}
void FuncType::AddParam(ValueType* param_type) {
param_types_.Add(param_type);
}
void FuncType::AddResult(ValueType* result_type) {
result_types_.Add(result_type);
}
StructType::StructType(Zone* zone, int index)
: DefType(zone, index), fields_(zone, 16) {}
SExpression* StructType::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("struct"));
for (Field* field : fields_) {
sexp->Add(field->Serialize(zone));
}
return sexp;
}
void StructType::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x5F);
WRITE_UNSIGNED(fields_.length());
for (Field* field : fields_) {
field->OutputBinary(stream);
}
}
void StructType::CopyFieldsTo(StructType* dest) {
GrowableArray<Field*>& dest_fields = dest->fields();
for (intptr_t i = 0; i < fields_.length(); ++i) {
dest_fields.Add(new (zone_) Field(*fields_.At(i)));
dest_fields.Last()->set_struct_type_(dest);
}
}
SExpression* ArrayType::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("array"));
sexp->Add(field_type_->Serialize(zone));
return sexp;
}
void ArrayType::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x5E);
field_type_->OutputBinary(stream);
}
Field* StructType::AddField(FieldType* field_type) {
fields_.Add(new (zone_) Field(this, field_type, fields_.length()));
return fields_.Last();
}
Field* StructType::AddField(ValueType* value_type, bool mut) {
return AddField(new (zone_) FieldType(value_type, mut));
}
Field* StructType::AddField(FieldType::PackedType packed_type, bool mut) {
return AddField(new (zone_) FieldType(packed_type, mut));
}
SExpression* LocalGet::Serialize(Zone* zone) {
return new (zone)
SExpSymbol(OS::SCreate(zone, "local.get %" Pu32, local_->index()));
}
void LocalGet::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x20);
WRITE_UNSIGNED(local_->index());
}
SExpression* LocalSet::Serialize(Zone* zone) {
return new (zone)
SExpSymbol(OS::SCreate(zone, "local.set %" Pu32, local_->index()));
}
void LocalSet::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x21);
WRITE_UNSIGNED(local_->index());
}
SExpression* GlobalGet::Serialize(Zone* zone) {
return new (zone)
SExpSymbol(OS::SCreate(zone, "global.get %" Pu32, global_->index()));
}
void GlobalGet::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x23);
WRITE_UNSIGNED(global_->index());
}
SExpression* IntOp::Serialize(Zone* zone) {
uint32_t bits = 0;
switch (int_kind_) {
case IntegerKind::kI32:
bits = 32;
break;
case IntegerKind::kI64:
bits = 64;
break;
default:
UNREACHABLE();
}
switch (op_kind_) {
case OpKind::kAdd:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".add", bits));
case OpKind::kSub:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".sub", bits));
case OpKind::kMult:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".mul", bits));
case OpKind::kDiv:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".div_s", bits));
case OpKind::kMod:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".mod_s", bits));
case OpKind::kAnd:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".and", bits));
case OpKind::kOr:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".or", bits));
case OpKind::kXor:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".xor", bits));
case OpKind::kEq:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".eq", bits));
case OpKind::kNeq:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".ne", bits));
case OpKind::kLt:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".lt_s", bits));
case OpKind::kGt:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".gt_s", bits));
case OpKind::kLe:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".le_s", bits));
case OpKind::kGe:
return new (zone) SExpSymbol(OS::SCreate(zone, "i%" Pu32 ".ge_s", bits));
default:
UNREACHABLE();
}
}
void IntOp::OutputBinary(WriteStream* stream) {
uint32_t offset_arithmetic = 0;
uint32_t offset_relational = 0;
if (int_kind_ == IntegerKind::kI64) {
offset_arithmetic = 0x7C - 0x6A;
offset_relational = 0x51 - 0x46;
}
switch (op_kind_) {
case OpKind::kAdd:
WRITE_BYTE(0x6A + offset_arithmetic);
break;
case OpKind::kSub:
WRITE_BYTE(0x6B + offset_arithmetic);
break;
case OpKind::kMult:
WRITE_BYTE(0x6C + offset_arithmetic);
break;
case OpKind::kDiv:
WRITE_BYTE(0x6D + offset_arithmetic);
break;
case OpKind::kMod:
WRITE_BYTE(0x6F + offset_arithmetic);
break;
case OpKind::kAnd:
WRITE_BYTE(0x71 + offset_arithmetic);
break;
case OpKind::kOr:
WRITE_BYTE(0x72 + offset_arithmetic);
break;
case OpKind::kXor:
WRITE_BYTE(0x73 + offset_arithmetic);
break;
case OpKind::kEq:
WRITE_BYTE(0x46 + offset_relational);
break;
case OpKind::kNeq:
WRITE_BYTE(0x47 + offset_relational);
break;
case OpKind::kLt:
WRITE_BYTE(0x48 + offset_relational);
break;
case OpKind::kGt:
WRITE_BYTE(0x4A + offset_relational);
break;
case OpKind::kLe:
WRITE_BYTE(0x4C + offset_relational);
break;
case OpKind::kGe:
WRITE_BYTE(0x4E + offset_relational);
break;
default:
UNREACHABLE();
}
}
IntOp::OpKind IntOp::NegateOpKind(OpKind op_kind) {
switch (op_kind) {
case OpKind::kEq:
return OpKind::kNeq;
case OpKind::kNeq:
return OpKind::kEq;
case OpKind::kLt:
return OpKind::kGe;
case OpKind::kGt:
return OpKind::kLe;
case OpKind::kLe:
return OpKind::kGt;
case OpKind::kGe:
return OpKind::kLt;
default:
FATAL("Operator can not be negated");
}
}
SExpression* IntConstant::Serialize(Zone* zone) {
switch (kind_) {
case Kind::kI32:
return new (zone) SExpSymbol(
OS::SCreate(zone, "i32.const %" Pu32, static_cast<uint32_t>(value_)));
case Kind::kI64:
return new (zone)
SExpSymbol(OS::SCreate(zone, "i64.const %" Pu64, value_));
default:
UNREACHABLE();
}
}
void IntConstant::OutputBinary(WriteStream* stream) {
switch (kind_) {
case Kind::kI32:
WRITE_BYTE(0x41);
WRITE_SIGNED(static_cast<int32_t>(value_));
break;
case Kind::kI64:
WRITE_BYTE(0x42);
WRITE_SIGNED(static_cast<int64_t>(value_));
break;
default:
UNREACHABLE();
}
}
SExpression* Int32WrapInt64::Serialize(Zone* zone) {
return new (zone) SExpSymbol("i32.wrap_i64");
}
void Int32WrapInt64::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xA7);
}
Block::Block(FuncType* const block_type, Zone* zone)
: StructuredInstr(block_type), body_(new (zone) InstructionList(zone)) {}
SExpression* Block::Serialize(Zone* zone) {
// Serialize result type.
const auto sexp_type = new (zone) SExpList(zone);
sexp_type->Add(new SExpSymbol("block_type ="));
sexp_type->Add(block_type_->Serialize(zone));
// Serialize body.
const auto sexp_body = new (zone) SExpList(zone);
sexp_body->Add(body_->Serialize(zone));
// Produce final SExpression.
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new SExpSymbol("block"));
sexp->Add(sexp_type);
sexp->Add(sexp_body);
return sexp;
}
void Block::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x02);
WRITE_SIGNED(block_type_->index());
body_->OutputBinary(stream);
WRITE_BYTE(0x0B);
}
Loop::Loop(FuncType* const block_type, Zone* zone)
: StructuredInstr(block_type), body_(new (zone) InstructionList(zone)) {}
SExpression* Loop::Serialize(Zone* zone) {
// Serialize result type.
const auto sexp_type = new (zone) SExpList(zone);
sexp_type->Add(new SExpSymbol("block_type ="));
sexp_type->Add(block_type_->Serialize(zone));
// Serialize body.
const auto sexp_body = new (zone) SExpList(zone);
sexp_body->Add(body_->Serialize(zone));
// Produce final SExpression.
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new SExpSymbol("loop"));
sexp->Add(sexp_type);
sexp->Add(sexp_body);
return sexp;
}
void Loop::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x03);
WRITE_SIGNED(block_type_->index());
body_->OutputBinary(stream);
WRITE_BYTE(0x0B);
}
If::If(FuncType* const block_type, Zone* zone)
: StructuredInstr(block_type),
then_(new (zone) InstructionList(zone)),
otherwise_(new (zone) InstructionList(zone)) {}
SExpression* If::Serialize(Zone* zone) {
// Serialize result type.
const auto sexp_type = new (zone) SExpList(zone);
sexp_type->Add(new SExpSymbol("block_type ="));
sexp_type->Add(block_type_->Serialize(zone));
// Serialize then branch.
const auto sexp_then = new (zone) SExpList(zone);
sexp_then->Add(new SExpSymbol("then"));
sexp_then->Add(then_->Serialize(zone));
// Serialize otherwise branch.
const auto sexp_otherwise = new (zone) SExpList(zone);
sexp_then->Add(new SExpSymbol("else"));
sexp_then->Add(otherwise_->Serialize(zone));
// Produce final SExpression.
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new SExpSymbol("if"));
sexp->Add(sexp_type);
sexp->Add(sexp_then);
sexp->Add(sexp_otherwise);
return sexp;
}
void If::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x04);
WRITE_SIGNED(block_type_->index());
then_->OutputBinary(stream);
if (!otherwise_->instructions().is_empty()) {
WRITE_BYTE(0x05);
otherwise_->OutputBinary(stream);
}
WRITE_BYTE(0x0B);
}
SExpression* Br::Serialize(Zone* zone) {
if (is_if_) {
return new (zone) SExpSymbol(OS::SCreate(zone, "br_if %" Pu32, label_));
} else {
return new (zone) SExpSymbol(OS::SCreate(zone, "br %" Pu32, label_));
}
}
void Br::OutputBinary(WriteStream* stream) {
if (is_if_) {
WRITE_BYTE(0x0D);
} else {
WRITE_BYTE(0x0C);
}
WRITE_UNSIGNED(label_);
}
SExpression* RttCanon::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("rtt.canon"));
sexp->Add(type_->Serialize(zone));
return sexp;
}
void RttCanon::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
WRITE_BYTE(0x30);
type_->OutputBinary(stream);
}
SExpression* RttSub::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("rtt.sub"));
sexp->Add(new (zone) SExpInteger(depth_));
sexp->Add(supertype_->Serialize(zone));
sexp->Add(type_->Serialize(zone));
return sexp;
}
void RttSub::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
WRITE_BYTE(0x31);
// V8 currently doesn't implement these two immediates.
// So, we leave them out of our output too. They don't seem neccesary, so
// it's unclear whether the GC spec will keep them in the long run
// (see e.g. https://github.com/WebAssembly/function-references/pull/31).
// WRITE_UNSIGNED(depth_);
// supertype_->OutputBinary(stream);
type_->OutputBinary(stream);
}
SExpression* StructGet::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("struct.get"));
sexp->Add(new (zone) SExpInteger(struct_type_->index()));
sexp->Add(new (zone) SExpInteger(field_->index()));
return sexp;
}
void StructGet::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
WRITE_BYTE(0x03);
WRITE_UNSIGNED(struct_type_->index());
WRITE_UNSIGNED(field_->index());
}
SExpression* StructSet::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("struct.set"));
sexp->Add(new (zone) SExpInteger(struct_type_->index()));
sexp->Add(new (zone) SExpInteger(field_->index()));
return sexp;
}
void StructSet::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
WRITE_BYTE(0x06);
WRITE_UNSIGNED(struct_type_->index());
WRITE_UNSIGNED(field_->index());
}
SExpression* StructNewWithRtt::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
if (def_) {
sexp->Add(new (zone) SExpSymbol("struct.new_default_with_rtt"));
} else {
sexp->Add(new (zone) SExpSymbol("struct.new_with_rtt"));
}
sexp->Add(new (zone) SExpInteger(struct_type_->index()));
return sexp;
}
void StructNewWithRtt::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
if (def_) {
WRITE_BYTE(0x02);
} else {
WRITE_BYTE(0x01);
}
WRITE_UNSIGNED(struct_type_->index());
}
SExpression* Call::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("call"));
sexp->Add(new (zone) SExpInteger(function_->index()));
return sexp;
}
void Call::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x10);
WRITE_UNSIGNED(function_->index());
}
SExpression* CallIndirect::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("call_indirect"));
sexp->Add(new (zone) SExpInteger(func_type_->index()));
return sexp;
}
void CallIndirect::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x11);
WRITE_UNSIGNED(func_type_->index());
WRITE_BYTE(0x00);
}
SExpression* Drop::Serialize(Zone* zone) {
return new (zone) SExpSymbol("drop");
}
void Drop::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x1A);
}
SExpression* Return::Serialize(Zone* zone) {
return new (zone) SExpSymbol("return");
}
void Return::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0x0F);
}
SExpression* RefNull::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("ref.null"));
sexp->Add(type_->Serialize(zone));
return sexp;
}
void RefNull::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xD0);
type_->OutputBinary(stream);
}
SExpression* RefEq::Serialize(Zone* zone) {
return new (zone) SExpSymbol("ref.eq");
}
void RefEq::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xD5);
}
SExpression* RefCast::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("ref.cast"));
sexp->Add(from_type_->Serialize(zone));
sexp->Add(to_type_->Serialize(zone));
return sexp;
}
void RefCast::OutputBinary(WriteStream* stream) {
WRITE_BYTE(0xFB);
WRITE_BYTE(0x41);
from_type_->OutputBinary(stream);
to_type_->OutputBinary(stream);
}
dart::SExpression* Table::Serialize(dart::Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("table"));
sexp->Add(new (zone) SExpInteger(min_size_));
sexp->Add(new (zone) SExpInteger(max_size_));
return sexp;
}
void Table::OutputBinary(dart::WriteStream* stream) {
WRITE_BYTE(0x70);
WRITE_BYTE(0x01);
WRITE_UNSIGNED(min_size_);
WRITE_UNSIGNED(max_size_);
}
dart::SExpression* SingleElemSegment::Serialize(dart::Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("elem"));
sexp->Add(new (zone) SExpInteger(offset_));
sexp->Add(new (zone) SExpInteger(function_->index()));
return sexp;
}
void SingleElemSegment::OutputBinary(dart::WriteStream* stream) {
// Table id, will always be zero in our case.
WRITE_UNSIGNED(0);
// i32.const offset
WRITE_BYTE(0x41);
WRITE_SIGNED(static_cast<int32_t>(offset_));
WRITE_BYTE(0x0B);
// Function index.
WRITE_UNSIGNED(1);
WRITE_UNSIGNED(function_->index());
}
SExpression* Local::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
switch (kind_) {
case Kind::kLocal:
sexp->Add(new (zone) SExpSymbol("local"));
break;
case Kind::kParam:
sexp->Add(new (zone) SExpSymbol("param"));
break;
default:
UNREACHABLE();
}
if (strcmp(name_, "") != 0) {
sexp->Add(new (zone) SExpSymbol(OS::SCreate(zone, "$%s", name_)));
}
sexp->Add(type_->Serialize(zone));
return sexp;
}
void Local::OutputBinary(WriteStream* stream) {
type_->OutputBinary(stream);
}
Global::Global(Zone* zone, ValueType* type, bool mut, uint32_t index)
: type_(type),
mut_(mut),
init_(new (zone) InstructionList(zone)),
index_(index) {}
SExpression* Global::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("global"));
sexp->Add(type_->Serialize(zone));
if (mut_) {
sexp->Add(new (zone) SExpSymbol("var"));
} else {
sexp->Add(new (zone) SExpSymbol("const"));
}
sexp->Add(init_->Serialize(zone));
return sexp;
}
void Global::OutputBinary(WriteStream* stream) {
type_->OutputBinary(stream);
if (mut_) {
WRITE_BYTE(0x01);
} else {
WRITE_BYTE(0x00);
}
init_->OutputBinary(stream);
WRITE_BYTE(0x0B);
}
InstructionList::InstructionList(Zone* zone)
: zone_(zone), instructions_(zone, 16) {}
SExpression* InstructionList::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
for (Instruction* instr : instructions_) {
sexp->Add(instr->Serialize(zone));
}
return sexp;
}
void InstructionList::OutputBinary(WriteStream* stream) {
for (Instruction* instr : instructions_) {
instr->OutputBinary(stream);
}
}
LocalGet* InstructionList::AddLocalGet(Local* local) {
LocalGet* const instr = new (zone_) LocalGet(local);
instructions_.Add(instr);
return instr;
}
LocalSet* InstructionList::AddLocalSet(Local* local) {
LocalSet* const instr = new (zone_) LocalSet(local);
instructions_.Add(instr);
return instr;
}
GlobalGet* InstructionList::AddGlobalGet(Global* global) {
GlobalGet* const instr = new (zone_) GlobalGet(global);
instructions_.Add(instr);
return instr;
}
IntOp* InstructionList::AddIntOp(IntOp::IntegerKind integer_kind,
IntOp::OpKind op_kind) {
IntOp* const instr = new (zone_) IntOp(integer_kind, op_kind);
instructions_.Add(instr);
return instr;
}
IntConstant* InstructionList::AddI32Constant(uint32_t value) {
IntConstant* const instr =
new (zone_) IntConstant(IntConstant::Kind::kI32, value);
instructions_.Add(instr);
return instr;
}
IntConstant* InstructionList::AddI64Constant(uint64_t value) {
IntConstant* const instr =
new (zone_) IntConstant(IntConstant::Kind::kI64, value);
instructions_.Add(instr);
return instr;
}
Int32WrapInt64* InstructionList::AddInt32WrapInt64() {
Int32WrapInt64* const instr = new (zone_) Int32WrapInt64;
instructions_.Add(instr);
return instr;
}
Block* InstructionList::AddBlock(FuncType* block_type) {
Block* const instr = new (zone_) Block(block_type, zone_);
instructions_.Add(instr);
return instr;
}
Loop* InstructionList::AddLoop(FuncType* block_type) {
Loop* const instr = new (zone_) Loop(block_type, zone_);
instructions_.Add(instr);
return instr;
}
If* InstructionList::AddIf(FuncType* block_type) {
If* const instr = new (zone_) If(block_type, zone_);
instructions_.Add(instr);
return instr;
}
RttCanon* InstructionList::AddRttCanon(HeapType* type) {
RttCanon* const instr = new (zone_) RttCanon(type);
instructions_.Add(instr);
return instr;
}
RttSub* InstructionList::AddRttSub(uint32_t depth,
HeapType* supertype,
HeapType* type) {
RttSub* const instr = new (zone_) RttSub(depth, supertype, type);
instructions_.Add(instr);
return instr;
}
StructGet* InstructionList::AddStructGet(StructType* struct_type,
Field* field) {
StructGet* const instr = new (zone_) StructGet(struct_type, field);
instructions_.Add(instr);
return instr;
}
StructSet* InstructionList::AddStructSet(StructType* struct_type,
Field* field) {
StructSet* const instr = new (zone_) StructSet(struct_type, field);
instructions_.Add(instr);
return instr;
}
StructNewWithRtt* InstructionList::AddStructNewWithRtt(
StructType* struct_type) {
StructNewWithRtt* const instr =
new (zone_) StructNewWithRtt(struct_type, /*def =*/false);
instructions_.Add(instr);
return instr;
}
StructNewWithRtt* InstructionList::AddStructNewDefaultWithRtt(
StructType* struct_type) {
StructNewWithRtt* const instr =
new (zone_) StructNewWithRtt(struct_type, /*def =*/true);
instructions_.Add(instr);
return instr;
}
Call* InstructionList::AddCall(Function* function) {
Call* const instr = new (zone_) Call(function);
instructions_.Add(instr);
return instr;
}
CallIndirect* InstructionList::AddCallIndirect(FuncType* function_type) {
CallIndirect* const instr = new (zone_) CallIndirect(function_type);
instructions_.Add(instr);
return instr;
}
Drop* InstructionList::AddDrop() {
Drop* const instr = new (zone_) Drop;
instructions_.Add(instr);
return instr;
}
Return* InstructionList::AddReturn() {
Return* const instr = new (zone_) Return();
instructions_.Add(instr);
return instr;
}
Br* InstructionList::AddBr(uint32_t label) {
Br* const instr = new (zone_) Br(/*is_if =*/false, label);
instructions_.Add(instr);
return instr;
}
Br* InstructionList::AddBrIf(uint32_t label) {
Br* const instr = new (zone_) Br(/*is_if =*/true, label);
instructions_.Add(instr);
return instr;
}
RefNull* InstructionList::AddRefNull(HeapType* type) {
RefNull* const instr = new (zone_) RefNull(type);
instructions_.Add(instr);
return instr;
}
RefEq* InstructionList::AddRefEq() {
RefEq* const instr = new (zone_) RefEq();
instructions_.Add(instr);
return instr;
}
RefCast* InstructionList::AddRefCast(HeapType* from_type, HeapType* to_type) {
RefCast* const instr = new (zone_) RefCast(from_type, to_type);
instructions_.Add(instr);
return instr;
}
Function::Function(Zone* zone,
const char* module_name,
const char* name,
uint32_t index,
FuncType* type)
: zone_(zone),
imported_module_name_(module_name),
name_(name),
index_(index),
type_(type),
locals_(zone, 16),
body_(nullptr) {}
SExpression* Function::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("func"));
// Serialize whether the function is imported.
if (imported_module_name_ != nullptr) {
sexp->Add(new (zone) SExpSymbol(
OS::SCreate(zone, "(imported) $%s", imported_module_name_)));
}
// Serialize function name.
if (strcmp(name_, "") != 0) {
sexp->Add(new (zone) SExpSymbol(OS::SCreate(zone, "$%s", name_)));
}
// Serialize type.
const auto sexp_type = new (zone) SExpList(zone);
sexp_type->Add(new (zone) SExpSymbol("type"));
sexp_type->Add(new (zone) SExpInteger(type_->index()));
sexp->Add(sexp_type);
// Serialize locals.
for (Local* it : locals_) {
if (it->kind() == Local::Kind::kLocal) {
sexp->Add(it->Serialize(zone));
}
}
// Serialize the body.
if (body_ != nullptr) {
sexp->Add(body_->Serialize(zone));
} else {
sexp->Add(new (zone) SExpSymbol("<missing body>"));
}
return sexp;
}
void Function::OutputBinary(WriteStream* stream) {
if (IsImported()) {
FATAL("Imported functions should never be output explicitly");
}
// Count locals whose kind is not kParam.
intptr_t num_non_param_locals = 0;
for (Local* local : locals_) {
if (local->kind() == Local::Kind::kLocal) {
++num_non_param_locals;
}
}
// First, output the locals.
WRITE_UNSIGNED(num_non_param_locals);
for (Local* local : locals_) {
if (local->kind() == Local::Kind::kLocal) {
WRITE_BYTE(1); // One local description follows. Wasm permits
// compressing multiple consecutive identical locals
// into one. We choose not to use this feature.
local->OutputBinary(stream);
}
}
// Then, output the function body.
if (body_ != nullptr) {
body_->OutputBinary(stream);
} else {
WasmTrace("WASM MISSING FUNCTION BODY OMITTED!\n");
}
WRITE_BYTE(0x0B);
}
Local* Function::AddLocal(Local::Kind kind, ValueType* type, const char* name) {
// No further params can be declared after the first
// local in a Wasm function header.
ASSERT(kind == Local::Kind::kLocal || locals_.is_empty() ||
locals_.Last()->kind() == Local::Kind::kParam);
locals_.Add(new (zone_) Local(this, kind, type, name, locals_.length()));
return locals_.Last();
}
Local* Function::GetLocalByName(const char* name) {
for (Local* it : locals_) {
if (strcmp(it->name(), name) == 0) {
return it;
}
}
FATAL("Local variable by name lookup returned no matching result.");
}
Local* Function::GetLocalByIndex(int id) {
ASSERT(0 <= id && id < locals_.length());
return locals_[id];
}
InstructionList* Function::MakeNewBodyAndClearLocals() {
locals_.Clear();
body_ = new (zone_) InstructionList(zone_);
return body_;
}
WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
: i32_(NumType::Kind::kI32),
i64_(NumType::Kind::kI64),
f32_(NumType::Kind::kF32),
f64_(NumType::Kind::kF64),
func_(HeapType::Kind::kFunc),
ext_(HeapType::Kind::kExtern),
any_(HeapType::Kind::kAny),
eq_(HeapType::Kind::kEq),
i31_(HeapType::Kind::kI31),
funcref_(/*nullable =*/true, &func_),
externref_(/*nullable =*/true, &ext_),
anyref_(/*nullable =*/true, &any_),
eqref_(/*nullable =*/true, &eq_),
i31ref_(/*nullable =*/false, &i31_),
start_function_(nullptr),
num_imported_functions_(0),
num_non_imported_functions_(0),
zone_(zone),
types_(zone, 16),
functions_(zone, 16),
table_(nullptr),
globals_(zone, 16),
elem_segments_(zone, 16) {}
SExpression* WasmModuleBuilder::Serialize(Zone* zone) {
const auto sexp = new (zone) SExpList(zone);
sexp->Add(new (zone) SExpSymbol("module"));
// Types section.
for (DefType* def_type : types_) {
const auto sexp_type = new (zone) SExpList(zone);
sexp_type->Add(new (zone) SExpSymbol("type"));
sexp_type->Add(def_type->Serialize(zone));
sexp->Add(sexp_type);
}
// Table section.
if (table_ != nullptr) {
sexp->Add(table_->Serialize(zone));
}
// Global section.
for (Global* global : globals_) {
sexp->Add(global->Serialize(zone));
}
// Start section.
if (start_function_ != nullptr) {
const auto sexp_start = new (zone) SExpList(zone);
sexp_start->Add(new (zone) SExpSymbol("start"));
sexp_start->Add(new (zone) SExpInteger(start_function_->index()));
sexp->Add(sexp_start);
}
// Element section.
for (SingleElemSegment* elem : elem_segments_) {
sexp->Add(elem->Serialize(zone));
}
// Import + Function + Code sections.
// Note that in Wasm Binary Format function bodies would
// be stored separately, in the code section.
for (Function* fct : functions_) {
sexp->Add(fct->Serialize(zone));
}
return sexp;
}
void WasmModuleBuilder::OutputTypeSection(WriteStream* stream) {
// Type section has index 1.
WRITE_BYTE(1);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(types_.length());
for (DefType* def_type : types_) {
def_type->OutputBinary(stream);
}
}
void WasmModuleBuilder::OutputImportSection(WriteStream* stream) {
// Type section has index 2.
WRITE_BYTE(2);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(num_imported_functions_);
for (Function* function : functions_) {
if (function->IsImported()) {
// Output toplevel namespace name.
WRITE_UNSIGNED(strlen(function->imported_module_name_));
WRITE_STRING(function->imported_module_name_);
// Output imported function name.
WRITE_UNSIGNED(strlen(function->name_));
WRITE_STRING(function->name_);
WRITE_BYTE(0x00); // Prefix for imported functions.
WRITE_UNSIGNED(function->type()->index());
}
}
}
void WasmModuleBuilder::OutputFunctionSection(WriteStream* stream) {
// Function section has index 3.
WRITE_BYTE(3);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(num_non_imported_functions_);
for (Function* function : functions_) {
if (!function->IsImported()) {
WRITE_UNSIGNED(function->type()->index());
}
}
}
void WasmModuleBuilder::OutputTableSection(dart::WriteStream* stream) {
// Table section has index 4.
WRITE_BYTE(4);
WRITE_BYTECOUNT();
if (table_ == nullptr) {
WRITE_UNSIGNED(0);
} else {
WRITE_UNSIGNED(1);
table_->OutputBinary(stream);
}
}
void WasmModuleBuilder::OutputGlobalSection(WriteStream* stream) {
// Code section has index 6.
WRITE_BYTE(6);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(globals_.length());
for (Global* global : globals_) {
global->OutputBinary(stream);
}
}
void WasmModuleBuilder::OutputStartSection(WriteStream* stream) {
if (start_function_ != nullptr) {
// Start section has index 8.
WRITE_BYTE(8);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(start_function_->index());
}
}
void WasmModuleBuilder::OutputElementSection(WriteStream* stream) {
// Element section has index 9.
WRITE_BYTE(9);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(elem_segments_.length());
for (SingleElemSegment* elem : elem_segments_) {
elem->OutputBinary(stream);
}
}
void WasmModuleBuilder::OutputCodeSection(WriteStream* stream) {
// Code section has index 10.
WRITE_BYTE(10);
WRITE_BYTECOUNT();
WRITE_UNSIGNED(num_non_imported_functions_);
for (Function* function : functions_) {
if (!function->IsImported()) {
// The code of each function is preceded by its bytecount to allow
// the embedder to lazily compile functions.
WRITE_BYTECOUNT();
function->OutputBinary(stream);
}
}
}
void WasmModuleBuilder::OutputBinary(WriteStream* stream) {
ASSERT(stream != nullptr);
// Magic.
WRITE_BYTE('\0');
WRITE_BYTE('a');
WRITE_BYTE('s');
WRITE_BYTE('m');
// Version.
WRITE_BYTE(0x01);
WRITE_BYTE(0x00);
WRITE_BYTE(0x00);
WRITE_BYTE(0x00);
// Sections come in ascending order of their indices.
OutputTypeSection(stream);
OutputImportSection(stream);
OutputFunctionSection(stream);
OutputTableSection(stream);
OutputGlobalSection(stream);
OutputStartSection(stream);
OutputElementSection(stream);
OutputCodeSection(stream);
}
FieldType* WasmModuleBuilder::MakeFieldType(ValueType* value_type, bool mut) {
return new (zone_) FieldType(value_type, mut);
}
FieldType* WasmModuleBuilder::MakeFieldType(FieldType::PackedType packed_type,
bool mut) {
return new (zone_) FieldType(packed_type, mut);
}
ArrayType* WasmModuleBuilder::MakeArrayType(FieldType* field_type) {
const auto array_type =
new (zone_) ArrayType(zone_, types_.length(), field_type);
types_.Add(array_type);
return array_type;
}
ArrayType* WasmModuleBuilder::MakeArrayType(ValueType* value_type, bool mut) {
return MakeArrayType(MakeFieldType(value_type, mut));
}
ArrayType* WasmModuleBuilder::MakeArrayType(FieldType::PackedType packed_type,
bool mut) {
return MakeArrayType(MakeFieldType(packed_type, mut));
}
HeapType* WasmModuleBuilder::MakeHeapType(DefType* def_type) {
return new (zone_) HeapType(def_type);
}
RefType* WasmModuleBuilder::MakeRefType(bool nullable, HeapType* heap_type) {
return new (zone_) RefType(nullable, heap_type);
}
RefType* WasmModuleBuilder::MakeRefType(bool nullable, DefType* def_type) {
return MakeRefType(nullable, MakeHeapType(def_type));
}
Global* WasmModuleBuilder::MakeRttCanon(StructType* type) {
HeapType* heap_type = MakeHeapType(type);
Rtt* const rtt = new (zone_) Rtt(1, heap_type);
Global* const global = AddGlobal(rtt, /*mut =*/false);
global->init()->AddRttCanon(heap_type);
return global;
}
Global* WasmModuleBuilder::MakeRttChild(StructType* type,
Global* parent_global) {
Rtt* const parent_rtt = reinterpret_cast<Rtt*>(parent_global->type_);
HeapType* heap_type = MakeHeapType(type);
const intptr_t rtt_depth = 1 + parent_rtt->depth();
Rtt* const rtt = new (zone_) Rtt(rtt_depth, heap_type);
Global* const global = AddGlobal(rtt, /*mut =*/false);
global->init()->AddGlobalGet(parent_global);
global->init()->AddRttSub(rtt_depth, parent_rtt->heap_type(), heap_type);
return global;
}
FuncType* WasmModuleBuilder::MakeFuncType() {
const auto fct_type = new (zone_) FuncType(zone_, types_.length());
types_.Add(fct_type);
return fct_type;
}
StructType* WasmModuleBuilder::MakeStructType() {
const auto str_type = new (zone_) StructType(zone_, types_.length());
types_.Add(str_type);
return str_type;
}
Global* WasmModuleBuilder::AddGlobal(ValueType* type, bool mut) {
Global* global = new (zone_) Global(zone_, type, mut, globals_.length());
globals_.Add(global);
return global;
}
Function* WasmModuleBuilder::AddFunction(const char* name, FuncType* type) {
++num_non_imported_functions_;
functions_.Add(new (zone_) Function(zone_, /*module_name=*/nullptr, name,
functions_.length(), type));
return functions_.Last();
}
Function* WasmModuleBuilder::AddImportedFunction(const char* module_name,
const char* name,
FuncType* type) {
if (!functions_.is_empty() && !functions_.Last()->IsImported()) {
FATAL("Wasm imported functions need to precede all other functions");
}
++num_imported_functions_;
functions_.Add(new (zone_) Function(zone_, module_name, name,
functions_.length(), type));
return functions_.Last();
}
Table* WasmModuleBuilder::AddFunctionsTable(uint32_t min_size,
uint32_t max_size) {
if (table_ != nullptr) {
FATAL("Only one table per module can exist.");
}
table_ = new (zone_) Table(min_size, max_size);
return table_;
}
SingleElemSegment* WasmModuleBuilder::AddElemTableInitializer(
uint32_t offset,
Function* function) {
// This restriction shall not neccesarily be enforced, and may be checked
// later on during module serialization, but it is still good pratice.
if (table_ == nullptr) {
FATAL("Table can not be initialized before being declared.");
}
SingleElemSegment* const elem_initializer =
new (zone_) SingleElemSegment(offset, function);
elem_segments_.Add(elem_initializer);
return elem_initializer;
}
} // namespace wasm