blob: 915a3949cc26f2d13d579e6d11dab8e556053fd5 [file] [log] [blame] [edit]
/*
* Copyright 2016 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "binary-writer.h"
#include "config.h"
#include <assert.h>
#include <math.h>
#include <memory.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include "binary.h"
#include "ir.h"
#include "stream.h"
#include "writer.h"
#define PRINT_HEADER_NO_INDEX -1
#define MAX_U32_LEB128_BYTES 5
#define MAX_U64_LEB128_BYTES 10
namespace wabt {
// TODO(binji): move the LEB128 functions somewhere else.
uint32_t u32_leb128_length(uint32_t value) {
uint32_t size = 0;
do {
value >>= 7;
size++;
} while (value != 0);
return size;
}
#define LEB128_LOOP_UNTIL(end_cond) \
do { \
uint8_t byte = value & 0x7f; \
value >>= 7; \
if (end_cond) { \
data[length++] = byte; \
break; \
} else { \
data[length++] = byte | 0x80; \
} \
} while (1)
uint32_t write_fixed_u32_leb128_at(Stream* stream,
uint32_t offset,
uint32_t value,
const char* desc) {
uint8_t data[MAX_U32_LEB128_BYTES];
uint32_t length =
write_fixed_u32_leb128_raw(data, data + MAX_U32_LEB128_BYTES, value);
stream->WriteDataAt(offset, data, length, desc);
return length;
}
void write_u32_leb128(Stream* stream, uint32_t value, const char* desc) {
uint8_t data[MAX_U32_LEB128_BYTES];
uint32_t length = 0;
LEB128_LOOP_UNTIL(value == 0);
stream->WriteData(data, length, desc);
}
void write_fixed_u32_leb128(Stream* stream, uint32_t value, const char* desc) {
uint8_t data[MAX_U32_LEB128_BYTES];
uint32_t length =
write_fixed_u32_leb128_raw(data, data + MAX_U32_LEB128_BYTES, value);
stream->WriteData(data, length, desc);
}
/* returns the length of the leb128 */
uint32_t write_u32_leb128_at(Stream* stream,
uint32_t offset,
uint32_t value,
const char* desc) {
uint8_t data[MAX_U32_LEB128_BYTES];
uint32_t length = 0;
LEB128_LOOP_UNTIL(value == 0);
stream->WriteDataAt(offset, data, length, desc);
return length;
}
uint32_t write_fixed_u32_leb128_raw(uint8_t* data,
uint8_t* end,
uint32_t value) {
if (end - data < MAX_U32_LEB128_BYTES)
return 0;
data[0] = (value & 0x7f) | 0x80;
data[1] = ((value >> 7) & 0x7f) | 0x80;
data[2] = ((value >> 14) & 0x7f) | 0x80;
data[3] = ((value >> 21) & 0x7f) | 0x80;
data[4] = ((value >> 28) & 0x0f);
return MAX_U32_LEB128_BYTES;
}
void write_i32_leb128(Stream* stream, int32_t value, const char* desc) {
uint8_t data[MAX_U32_LEB128_BYTES];
uint32_t length = 0;
if (value < 0)
LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
else
LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
stream->WriteData(data, length, desc);
}
static void write_i64_leb128(Stream* stream, int64_t value, const char* desc) {
uint8_t data[MAX_U64_LEB128_BYTES];
uint32_t length = 0;
if (value < 0)
LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
else
LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
stream->WriteData(data, length, desc);
}
#undef LEB128_LOOP_UNTIL
void write_str(Stream* stream,
const char* s,
size_t length,
const char* desc,
PrintChars print_chars) {
write_u32_leb128(stream, length, "string length");
stream->WriteData(s, length, desc, print_chars);
}
void write_opcode(Stream* stream, Opcode opcode) {
stream->WriteU8Enum(opcode, get_opcode_name(opcode));
}
void write_type(Stream* stream, Type type) {
write_i32_leb128_enum(stream, type, get_type_name(type));
}
void write_limits(Stream* stream, const Limits* limits) {
uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0;
write_u32_leb128(stream, flags, "limits: flags");
write_u32_leb128(stream, limits->initial, "limits: initial");
if (limits->has_max)
write_u32_leb128(stream, limits->max, "limits: max");
}
namespace {
/* TODO(binji): better leb size guess. Some sections we know will only be 1
byte, but others we can be fairly certain will be larger. */
static const size_t LEB_SECTION_SIZE_GUESS = 1;
#define ALLOC_FAILURE \
fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__)
struct RelocSection {
RelocSection(const char* name, BinarySection code);
const char* name;
BinarySection section_code;
std::vector<Reloc> relocations;
};
RelocSection::RelocSection(const char* name, BinarySection code)
: name(name), section_code(code) {}
class BinaryWriter {
WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter);
public:
BinaryWriter(Writer*, const WriteBinaryOptions* options);
Result WriteModule(const Module* module);
private:
void WriteHeader(const char* name, int index);
uint32_t WriteU32Leb128Space(uint32_t leb_size_guess, const char* desc);
void WriteFixupU32Leb128Size(uint32_t offset,
uint32_t leb_size_guess,
const char* desc);
void BeginKnownSection(BinarySection section_code, size_t leb_size_guess);
void BeginCustomSection(const char* name, size_t leb_size_guess);
void EndSection();
void BeginSubsection(const char* name, size_t leb_size_guess);
void EndSubsection();
uint32_t GetLabelVarDepth(const Var* var);
void AddReloc(RelocType reloc_type, uint32_t index);
void WriteU32Leb128WithReloc(uint32_t index,
const char* desc,
RelocType reloc_type);
void WriteExpr(const Module* module, const Func* func, const Expr* expr);
void WriteExprList(const Module* module, const Func* func, const Expr* first);
void WriteInitExpr(const Module* module, const Expr* expr);
void WriteFuncLocals(const Module* module,
const Func* func,
const TypeVector& local_types);
void WriteFunc(const Module* module, const Func* func);
void WriteTable(const Table* table);
void WriteMemory(const Memory* memory);
void WriteGlobalHeader(const Global* global);
void WriteRelocSection(const RelocSection* reloc_section);
Stream stream_;
const WriteBinaryOptions* options_ = nullptr;
std::vector<RelocSection> reloc_sections_;
RelocSection* current_reloc_section_ = nullptr;
size_t last_section_offset_ = 0;
size_t last_section_leb_size_guess_ = 0;
BinarySection last_section_type_ = BinarySection::Invalid;
size_t last_section_payload_offset_ = 0;
size_t last_subsection_offset_ = 0;
size_t last_subsection_leb_size_guess_ = 0;
size_t last_subsection_payload_offset_ = 0;
};
static uint8_t log2_u32(uint32_t x) {
uint8_t result = 0;
while (x > 1) {
x >>= 1;
result++;
}
return result;
}
BinaryWriter::BinaryWriter(Writer* writer, const WriteBinaryOptions* options)
: stream_(writer, options->log_stream), options_(options) {}
void BinaryWriter::WriteHeader(const char* name, int index) {
if (stream_.has_log_stream()) {
if (index == PRINT_HEADER_NO_INDEX) {
stream_.log_stream().Writef("; %s\n", name);
} else {
stream_.log_stream().Writef("; %s %d\n", name, index);
}
}
}
/* returns offset of leb128 */
uint32_t BinaryWriter::WriteU32Leb128Space(uint32_t leb_size_guess,
const char* desc) {
assert(leb_size_guess <= MAX_U32_LEB128_BYTES);
uint8_t data[MAX_U32_LEB128_BYTES] = {0};
uint32_t result = stream_.offset();
uint32_t bytes_to_write =
options_->canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES;
stream_.WriteData(data, bytes_to_write, desc);
return result;
}
void BinaryWriter::WriteFixupU32Leb128Size(uint32_t offset,
uint32_t leb_size_guess,
const char* desc) {
if (options_->canonicalize_lebs) {
uint32_t size = stream_.offset() - offset - leb_size_guess;
uint32_t leb_size = u32_leb128_length(size);
if (leb_size != leb_size_guess) {
uint32_t src_offset = offset + leb_size_guess;
uint32_t dst_offset = offset + leb_size;
stream_.MoveData(dst_offset, src_offset, size);
}
write_u32_leb128_at(&stream_, offset, size, desc);
stream_.AddOffset(leb_size - leb_size_guess);
} else {
uint32_t size = stream_.offset() - offset - MAX_U32_LEB128_BYTES;
write_fixed_u32_leb128_at(&stream_, offset, size, desc);
}
}
static void write_inline_signature_type(Stream* stream,
const BlockSignature& sig) {
if (sig.size() == 0) {
write_type(stream, Type::Void);
} else if (sig.size() == 1) {
write_type(stream, sig[0]);
} else {
/* this is currently unrepresentable */
stream->WriteU8(0xff, "INVALID INLINE SIGNATURE");
}
}
void BinaryWriter::BeginKnownSection(BinarySection section_code,
size_t leb_size_guess) {
assert(last_section_leb_size_guess_ == 0);
char desc[100];
wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)",
get_section_name(section_code),
static_cast<unsigned>(section_code));
WriteHeader(desc, PRINT_HEADER_NO_INDEX);
stream_.WriteU8Enum(section_code, "section code");
last_section_type_ = section_code;
last_section_leb_size_guess_ = leb_size_guess;
last_section_offset_ =
WriteU32Leb128Space(leb_size_guess, "section size (guess)");
last_section_payload_offset_ = stream_.offset();
}
void BinaryWriter::BeginCustomSection(const char* name, size_t leb_size_guess) {
assert(last_section_leb_size_guess_ == 0);
char desc[100];
wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name);
WriteHeader(desc, PRINT_HEADER_NO_INDEX);
stream_.WriteU8Enum(BinarySection::Custom, "custom section code");
last_section_type_ = BinarySection::Custom;
last_section_leb_size_guess_ = leb_size_guess;
last_section_offset_ =
WriteU32Leb128Space(leb_size_guess, "section size (guess)");
last_section_payload_offset_ = stream_.offset();
write_str(&stream_, name, strlen(name), "custom section name",
PrintChars::Yes);
}
void BinaryWriter::EndSection() {
assert(last_section_leb_size_guess_ != 0);
WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_,
"FIXUP section size");
last_section_leb_size_guess_ = 0;
}
void BinaryWriter::BeginSubsection(const char* name, size_t leb_size_guess) {
assert(last_subsection_leb_size_guess_ == 0);
last_subsection_leb_size_guess_ = leb_size_guess;
last_subsection_offset_ =
WriteU32Leb128Space(leb_size_guess, "subsection size (guess)");
last_subsection_payload_offset_ = stream_.offset();
}
void BinaryWriter::EndSubsection() {
assert(last_subsection_leb_size_guess_ != 0);
WriteFixupU32Leb128Size(last_subsection_offset_,
last_subsection_leb_size_guess_,
"FIXUP subsection size");
last_subsection_leb_size_guess_ = 0;
}
uint32_t BinaryWriter::GetLabelVarDepth(const Var* var) {
assert(var->type == VarType::Index);
return var->index;
}
void BinaryWriter::AddReloc(RelocType reloc_type, uint32_t index) {
// Add a new reloc section if needed
if (!current_reloc_section_ ||
current_reloc_section_->section_code != last_section_type_) {
reloc_sections_.emplace_back(get_section_name(last_section_type_),
last_section_type_);
current_reloc_section_ = &reloc_sections_.back();
}
// Add a new relocation to the curent reloc section
size_t offset = stream_.offset() - last_section_payload_offset_;
current_reloc_section_->relocations.emplace_back(reloc_type, offset, index);
}
void BinaryWriter::WriteU32Leb128WithReloc(uint32_t index,
const char* desc,
RelocType reloc_type) {
if (options_->relocatable) {
AddReloc(reloc_type, index);
write_fixed_u32_leb128(&stream_, index, desc);
} else {
write_u32_leb128(&stream_, index, desc);
}
}
void BinaryWriter::WriteExpr(const Module* module,
const Func* func,
const Expr* expr) {
switch (expr->type) {
case ExprType::Binary:
write_opcode(&stream_, expr->binary.opcode);
break;
case ExprType::Block:
write_opcode(&stream_, Opcode::Block);
write_inline_signature_type(&stream_, expr->block->sig);
WriteExprList(module, func, expr->block->first);
write_opcode(&stream_, Opcode::End);
break;
case ExprType::Br:
write_opcode(&stream_, Opcode::Br);
write_u32_leb128(&stream_, GetLabelVarDepth(&expr->br.var),
"break depth");
break;
case ExprType::BrIf:
write_opcode(&stream_, Opcode::BrIf);
write_u32_leb128(&stream_, GetLabelVarDepth(&expr->br_if.var),
"break depth");
break;
case ExprType::BrTable: {
write_opcode(&stream_, Opcode::BrTable);
write_u32_leb128(&stream_, expr->br_table.targets->size(), "num targets");
uint32_t depth;
for (const Var& var : *expr->br_table.targets) {
depth = GetLabelVarDepth(&var);
write_u32_leb128(&stream_, depth, "break depth");
}
depth = GetLabelVarDepth(&expr->br_table.default_target);
write_u32_leb128(&stream_, depth, "break depth for default");
break;
}
case ExprType::Call: {
int index = get_func_index_by_var(module, &expr->call.var);
write_opcode(&stream_, Opcode::Call);
WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
break;
}
case ExprType::CallIndirect: {
int index = get_func_type_index_by_var(module, &expr->call_indirect.var);
write_opcode(&stream_, Opcode::CallIndirect);
write_u32_leb128(&stream_, index, "signature index");
write_u32_leb128(&stream_, 0, "call_indirect reserved");
break;
}
case ExprType::Compare:
write_opcode(&stream_, expr->compare.opcode);
break;
case ExprType::Const:
switch (expr->const_.type) {
case Type::I32: {
write_opcode(&stream_, Opcode::I32Const);
write_i32_leb128(&stream_, expr->const_.u32, "i32 literal");
break;
}
case Type::I64:
write_opcode(&stream_, Opcode::I64Const);
write_i64_leb128(&stream_, expr->const_.u64, "i64 literal");
break;
case Type::F32:
write_opcode(&stream_, Opcode::F32Const);
stream_.WriteU32(expr->const_.f32_bits, "f32 literal");
break;
case Type::F64:
write_opcode(&stream_, Opcode::F64Const);
stream_.WriteU64(expr->const_.f64_bits, "f64 literal");
break;
default:
assert(0);
}
break;
case ExprType::Convert:
write_opcode(&stream_, expr->convert.opcode);
break;
case ExprType::CurrentMemory:
write_opcode(&stream_, Opcode::CurrentMemory);
write_u32_leb128(&stream_, 0, "current_memory reserved");
break;
case ExprType::Drop:
write_opcode(&stream_, Opcode::Drop);
break;
case ExprType::GetGlobal: {
int index = get_global_index_by_var(module, &expr->get_global.var);
write_opcode(&stream_, Opcode::GetGlobal);
WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
break;
}
case ExprType::GetLocal: {
int index = get_local_index_by_var(func, &expr->get_local.var);
write_opcode(&stream_, Opcode::GetLocal);
write_u32_leb128(&stream_, index, "local index");
break;
}
case ExprType::GrowMemory:
write_opcode(&stream_, Opcode::GrowMemory);
write_u32_leb128(&stream_, 0, "grow_memory reserved");
break;
case ExprType::If:
write_opcode(&stream_, Opcode::If);
write_inline_signature_type(&stream_, expr->if_.true_->sig);
WriteExprList(module, func, expr->if_.true_->first);
if (expr->if_.false_) {
write_opcode(&stream_, Opcode::Else);
WriteExprList(module, func, expr->if_.false_);
}
write_opcode(&stream_, Opcode::End);
break;
case ExprType::Load: {
write_opcode(&stream_, expr->load.opcode);
uint32_t align =
get_opcode_alignment(expr->load.opcode, expr->load.align);
stream_.WriteU8(log2_u32(align), "alignment");
write_u32_leb128(&stream_, expr->load.offset, "load offset");
break;
}
case ExprType::Loop:
write_opcode(&stream_, Opcode::Loop);
write_inline_signature_type(&stream_, expr->loop->sig);
WriteExprList(module, func, expr->loop->first);
write_opcode(&stream_, Opcode::End);
break;
case ExprType::Nop:
write_opcode(&stream_, Opcode::Nop);
break;
case ExprType::Return:
write_opcode(&stream_, Opcode::Return);
break;
case ExprType::Select:
write_opcode(&stream_, Opcode::Select);
break;
case ExprType::SetGlobal: {
int index = get_global_index_by_var(module, &expr->get_global.var);
write_opcode(&stream_, Opcode::SetGlobal);
WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
break;
}
case ExprType::SetLocal: {
int index = get_local_index_by_var(func, &expr->get_local.var);
write_opcode(&stream_, Opcode::SetLocal);
write_u32_leb128(&stream_, index, "local index");
break;
}
case ExprType::Store: {
write_opcode(&stream_, expr->store.opcode);
uint32_t align =
get_opcode_alignment(expr->store.opcode, expr->store.align);
stream_.WriteU8(log2_u32(align), "alignment");
write_u32_leb128(&stream_, expr->store.offset, "store offset");
break;
}
case ExprType::TeeLocal: {
int index = get_local_index_by_var(func, &expr->get_local.var);
write_opcode(&stream_, Opcode::TeeLocal);
write_u32_leb128(&stream_, index, "local index");
break;
}
case ExprType::Unary:
write_opcode(&stream_, expr->unary.opcode);
break;
case ExprType::Unreachable:
write_opcode(&stream_, Opcode::Unreachable);
break;
}
}
void BinaryWriter::WriteExprList(const Module* module,
const Func* func,
const Expr* first) {
for (const Expr* expr = first; expr; expr = expr->next)
WriteExpr(module, func, expr);
}
void BinaryWriter::WriteInitExpr(const Module* module, const Expr* expr) {
if (expr)
WriteExprList(module, nullptr, expr);
write_opcode(&stream_, Opcode::End);
}
void BinaryWriter::WriteFuncLocals(const Module* module,
const Func* func,
const TypeVector& local_types) {
if (local_types.size() == 0) {
write_u32_leb128(&stream_, 0, "local decl count");
return;
}
uint32_t num_params = get_num_params(func);
#define FIRST_LOCAL_INDEX (num_params)
#define LAST_LOCAL_INDEX (num_params + local_types.size())
#define GET_LOCAL_TYPE(x) (local_types[x - num_params])
/* loop through once to count the number of local declaration runs */
Type current_type = GET_LOCAL_TYPE(FIRST_LOCAL_INDEX);
uint32_t local_decl_count = 1;
for (uint32_t i = FIRST_LOCAL_INDEX + 1; i < LAST_LOCAL_INDEX; ++i) {
Type type = GET_LOCAL_TYPE(i);
if (current_type != type) {
local_decl_count++;
current_type = type;
}
}
/* loop through again to write everything out */
write_u32_leb128(&stream_, local_decl_count, "local decl count");
current_type = GET_LOCAL_TYPE(FIRST_LOCAL_INDEX);
uint32_t local_type_count = 1;
for (uint32_t i = FIRST_LOCAL_INDEX + 1; i <= LAST_LOCAL_INDEX; ++i) {
/* loop through an extra time to catch the final type transition */
Type type = i == LAST_LOCAL_INDEX ? Type::Void : GET_LOCAL_TYPE(i);
if (current_type == type) {
local_type_count++;
} else {
write_u32_leb128(&stream_, local_type_count, "local type count");
write_type(&stream_, current_type);
local_type_count = 1;
current_type = type;
}
}
}
void BinaryWriter::WriteFunc(const Module* module, const Func* func) {
WriteFuncLocals(module, func, func->local_types);
WriteExprList(module, func, func->first_expr);
write_opcode(&stream_, Opcode::End);
}
void BinaryWriter::WriteTable(const Table* table) {
write_type(&stream_, Type::Anyfunc);
write_limits(&stream_, &table->elem_limits);
}
void BinaryWriter::WriteMemory(const Memory* memory) {
write_limits(&stream_, &memory->page_limits);
}
void BinaryWriter::WriteGlobalHeader(const Global* global) {
write_type(&stream_, global->type);
stream_.WriteU8(global->mutable_, "global mutability");
}
void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) {
char section_name[128];
wabt_snprintf(section_name, sizeof(section_name), "%s.%s",
WABT_BINARY_SECTION_RELOC, reloc_section->name);
BeginCustomSection(section_name, LEB_SECTION_SIZE_GUESS);
write_u32_leb128_enum(&stream_, reloc_section->section_code,
"reloc section type");
const std::vector<Reloc>& relocs = reloc_section->relocations;
write_u32_leb128(&stream_, relocs.size(), "num relocs");
for (const Reloc& reloc : relocs) {
write_u32_leb128_enum(&stream_, reloc.type, "reloc type");
write_u32_leb128(&stream_, reloc.offset, "reloc offset");
write_u32_leb128(&stream_, reloc.index, "reloc index");
switch (reloc.type) {
case RelocType::MemoryAddressLEB:
case RelocType::MemoryAddressSLEB:
case RelocType::MemoryAddressI32:
write_u32_leb128(&stream_, reloc.addend, "reloc addend");
break;
default:
break;
}
}
EndSection();
}
Result BinaryWriter::WriteModule(const Module* module) {
stream_.WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
stream_.WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
if (module->func_types.size()) {
BeginKnownSection(BinarySection::Type, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->func_types.size(), "num types");
for (size_t i = 0; i < module->func_types.size(); ++i) {
const FuncType* func_type = module->func_types[i];
const FuncSignature* sig = &func_type->sig;
WriteHeader("type", i);
write_type(&stream_, Type::Func);
uint32_t num_params = sig->param_types.size();
uint32_t num_results = sig->result_types.size();
write_u32_leb128(&stream_, num_params, "num params");
for (size_t j = 0; j < num_params; ++j)
write_type(&stream_, sig->param_types[j]);
write_u32_leb128(&stream_, num_results, "num results");
for (size_t j = 0; j < num_results; ++j)
write_type(&stream_, sig->result_types[j]);
}
EndSection();
}
if (module->imports.size()) {
BeginKnownSection(BinarySection::Import, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->imports.size(), "num imports");
for (size_t i = 0; i < module->imports.size(); ++i) {
const Import* import = module->imports[i];
WriteHeader("import header", i);
write_str(&stream_, import->module_name.start, import->module_name.length,
"import module name", PrintChars::Yes);
write_str(&stream_, import->field_name.start, import->field_name.length,
"import field name", PrintChars::Yes);
stream_.WriteU8Enum(import->kind, "import kind");
switch (import->kind) {
case ExternalKind::Func:
write_u32_leb128(
&stream_,
get_func_type_index_by_decl(module, &import->func->decl),
"import signature index");
break;
case ExternalKind::Table:
WriteTable(import->table);
break;
case ExternalKind::Memory:
WriteMemory(import->memory);
break;
case ExternalKind::Global:
WriteGlobalHeader(import->global);
break;
}
}
EndSection();
}
assert(module->funcs.size() >= module->num_func_imports);
uint32_t num_funcs = module->funcs.size() - module->num_func_imports;
if (num_funcs) {
BeginKnownSection(BinarySection::Function, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, num_funcs, "num functions");
for (size_t i = 0; i < num_funcs; ++i) {
const Func* func = module->funcs[i + module->num_func_imports];
char desc[100];
wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index",
i);
write_u32_leb128(&stream_,
get_func_type_index_by_decl(module, &func->decl), desc);
}
EndSection();
}
assert(module->tables.size() >= module->num_table_imports);
uint32_t num_tables = module->tables.size() - module->num_table_imports;
if (num_tables) {
BeginKnownSection(BinarySection::Table, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, num_tables, "num tables");
for (size_t i = 0; i < num_tables; ++i) {
const Table* table = module->tables[i + module->num_table_imports];
WriteHeader("table", i);
WriteTable(table);
}
EndSection();
}
assert(module->memories.size() >= module->num_memory_imports);
uint32_t num_memories = module->memories.size() - module->num_memory_imports;
if (num_memories) {
BeginKnownSection(BinarySection::Memory, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, num_memories, "num memories");
for (size_t i = 0; i < num_memories; ++i) {
const Memory* memory = module->memories[i + module->num_memory_imports];
WriteHeader("memory", i);
WriteMemory(memory);
}
EndSection();
}
assert(module->globals.size() >= module->num_global_imports);
uint32_t num_globals = module->globals.size() - module->num_global_imports;
if (num_globals) {
BeginKnownSection(BinarySection::Global, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, num_globals, "num globals");
for (size_t i = 0; i < num_globals; ++i) {
const Global* global = module->globals[i + module->num_global_imports];
WriteGlobalHeader(global);
WriteInitExpr(module, global->init_expr);
}
EndSection();
}
if (module->exports.size()) {
BeginKnownSection(BinarySection::Export, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->exports.size(), "num exports");
for (const Export* export_ : module->exports) {
write_str(&stream_, export_->name.start, export_->name.length,
"export name", PrintChars::Yes);
stream_.WriteU8Enum(export_->kind, "export kind");
switch (export_->kind) {
case ExternalKind::Func: {
int index = get_func_index_by_var(module, &export_->var);
write_u32_leb128(&stream_, index, "export func index");
break;
}
case ExternalKind::Table: {
int index = get_table_index_by_var(module, &export_->var);
write_u32_leb128(&stream_, index, "export table index");
break;
}
case ExternalKind::Memory: {
int index = get_memory_index_by_var(module, &export_->var);
write_u32_leb128(&stream_, index, "export memory index");
break;
}
case ExternalKind::Global: {
int index = get_global_index_by_var(module, &export_->var);
write_u32_leb128(&stream_, index, "export global index");
break;
}
}
}
EndSection();
}
if (module->start) {
int start_func_index = get_func_index_by_var(module, module->start);
if (start_func_index != -1) {
BeginKnownSection(BinarySection::Start, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, start_func_index, "start func index");
EndSection();
}
}
if (module->elem_segments.size()) {
BeginKnownSection(BinarySection::Elem, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->elem_segments.size(),
"num elem segments");
for (size_t i = 0; i < module->elem_segments.size(); ++i) {
ElemSegment* segment = module->elem_segments[i];
int table_index = get_table_index_by_var(module, &segment->table_var);
WriteHeader("elem segment header", i);
write_u32_leb128(&stream_, table_index, "table index");
WriteInitExpr(module, segment->offset);
write_u32_leb128(&stream_, segment->vars.size(), "num function indices");
for (const Var& var : segment->vars) {
int index = get_func_index_by_var(module, &var);
WriteU32Leb128WithReloc(index, "function index",
RelocType::FuncIndexLEB);
}
}
EndSection();
}
if (num_funcs) {
BeginKnownSection(BinarySection::Code, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, num_funcs, "num functions");
for (size_t i = 0; i < num_funcs; ++i) {
WriteHeader("function body", i);
const Func* func = module->funcs[i + module->num_func_imports];
/* TODO(binji): better guess of the size of the function body section */
const uint32_t leb_size_guess = 1;
uint32_t body_size_offset =
WriteU32Leb128Space(leb_size_guess, "func body size (guess)");
WriteFunc(module, func);
WriteFixupU32Leb128Size(body_size_offset, leb_size_guess,
"FIXUP func body size");
}
EndSection();
}
if (module->data_segments.size()) {
BeginKnownSection(BinarySection::Data, LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->data_segments.size(),
"num data segments");
for (size_t i = 0; i < module->data_segments.size(); ++i) {
const DataSegment* segment = module->data_segments[i];
WriteHeader("data segment header", i);
int memory_index = get_memory_index_by_var(module, &segment->memory_var);
write_u32_leb128(&stream_, memory_index, "memory index");
WriteInitExpr(module, segment->offset);
write_u32_leb128(&stream_, segment->size, "data segment size");
WriteHeader("data segment data", i);
stream_.WriteData(segment->data, segment->size, "data segment data");
}
EndSection();
}
if (options_->write_debug_names) {
std::vector<std::string> index_to_name;
char desc[100];
BeginCustomSection(WABT_BINARY_SECTION_NAME, LEB_SECTION_SIZE_GUESS);
size_t named_functions = 0;
for (const Func* func : module->funcs) {
if (func->name.length > 0)
named_functions++;
}
if (named_functions > 0) {
write_u32_leb128(&stream_, 1, "function name type");
BeginSubsection("function name subsection", LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, named_functions, "num functions");
for (size_t i = 0; i < module->funcs.size(); ++i) {
const Func* func = module->funcs[i];
if (func->name.length == 0)
continue;
write_u32_leb128(&stream_, i, "function index");
wabt_snprintf(desc, sizeof(desc), "func name %" PRIzd, i);
write_str(&stream_, func->name.start, func->name.length, desc,
PrintChars::Yes);
}
EndSubsection();
}
write_u32_leb128(&stream_, 2, "local name type");
BeginSubsection("local name subsection", LEB_SECTION_SIZE_GUESS);
write_u32_leb128(&stream_, module->funcs.size(), "num functions");
for (size_t i = 0; i < module->funcs.size(); ++i) {
const Func* func = module->funcs[i];
uint32_t num_params = get_num_params(func);
uint32_t num_locals = func->local_types.size();
uint32_t num_params_and_locals = get_num_params_and_locals(func);
write_u32_leb128(&stream_, i, "function index");
write_u32_leb128(&stream_, num_params_and_locals, "num locals");
make_type_binding_reverse_mapping(func->decl.sig.param_types,
func->param_bindings, &index_to_name);
for (size_t j = 0; j < num_params; ++j) {
const std::string& name = index_to_name[j];
wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
write_u32_leb128(&stream_, j, "local index");
write_str(&stream_, name.data(), name.length(), desc, PrintChars::Yes);
}
make_type_binding_reverse_mapping(func->local_types, func->local_bindings,
&index_to_name);
for (size_t j = 0; j < num_locals; ++j) {
const std::string& name = index_to_name[j];
wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, num_params + j);
write_u32_leb128(&stream_, num_params + j, "local index");
write_str(&stream_, name.data(), name.length(), desc, PrintChars::Yes);
}
}
EndSubsection();
EndSection();
}
if (options_->relocatable) {
for (RelocSection& section : reloc_sections_) {
WriteRelocSection(&section);
}
}
return stream_.result();
}
} // namespace
Result write_binary_module(Writer* writer,
const Module* module,
const WriteBinaryOptions* options) {
BinaryWriter binary_writer(writer, options);
return binary_writer.WriteModule(module);
}
} // namespace wabt