summaryrefslogtreecommitdiff
path: root/src/binary-writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/binary-writer.cc')
-rw-r--r--src/binary-writer.cc947
1 files changed, 947 insertions, 0 deletions
diff --git a/src/binary-writer.cc b/src/binary-writer.cc
new file mode 100644
index 00000000..0c94b079
--- /dev/null
+++ b/src/binary-writer.cc
@@ -0,0 +1,947 @@
+/*
+ * 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 "ast.h"
+#include "binary.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
+
+/* 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__)
+
+typedef struct Reloc {
+ WabtRelocType type;
+ size_t offset;
+} Reloc;
+WABT_DEFINE_VECTOR(reloc, Reloc);
+
+typedef struct RelocSection {
+ const char* name;
+ WabtBinarySection section_code;
+ RelocVector relocations;
+} RelocSection;
+WABT_DEFINE_VECTOR(reloc_section, RelocSection);
+
+typedef struct Context {
+ WabtStream stream;
+ WabtStream* log_stream;
+ const WabtWriteBinaryOptions* options;
+
+ RelocSectionVector reloc_sections;
+ RelocSection* current_reloc_section;
+
+ size_t last_section_offset;
+ size_t last_section_leb_size_guess;
+ WabtBinarySection last_section_type;
+ size_t last_section_payload_offset;
+} Context;
+
+void wabt_destroy_reloc_section(RelocSection* reloc_section) {
+ wabt_destroy_reloc_vector(&reloc_section->relocations);
+}
+
+static uint8_t log2_u32(uint32_t x) {
+ uint8_t result = 0;
+ while (x > 1) {
+ x >>= 1;
+ result++;
+ }
+ return result;
+}
+
+static void write_header(Context* ctx, const char* name, int index) {
+ if (ctx->log_stream) {
+ if (index == PRINT_HEADER_NO_INDEX) {
+ wabt_writef(ctx->log_stream, "; %s\n", name);
+ } else {
+ wabt_writef(ctx->log_stream, "; %s %d\n", name, index);
+ }
+ }
+}
+
+#define LEB128_LOOP_UNTIL(end_cond) \
+ do { \
+ uint8_t byte = value & 0x7f; \
+ value >>= 7; \
+ if (end_cond) { \
+ data[i++] = byte; \
+ break; \
+ } else { \
+ data[i++] = byte | 0x80; \
+ } \
+ } while (1)
+
+uint32_t wabt_u32_leb128_length(uint32_t value) {
+ uint8_t data[MAX_U32_LEB128_BYTES] WABT_UNUSED;
+ uint32_t i = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ return i;
+}
+
+/* returns the length of the leb128 */
+uint32_t wabt_write_u32_leb128_at(WabtStream* stream,
+ uint32_t offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ uint32_t i = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ uint32_t length = i;
+ wabt_write_data_at(stream, offset, data, length, WABT_DONT_PRINT_CHARS, desc);
+ return length;
+}
+
+uint32_t wabt_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;
+}
+
+uint32_t wabt_write_fixed_u32_leb128_at(WabtStream* stream,
+ uint32_t offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ uint32_t length =
+ wabt_write_fixed_u32_leb128_raw(data, data + MAX_U32_LEB128_BYTES, value);
+ wabt_write_data_at(stream, offset, data, length, WABT_DONT_PRINT_CHARS, desc);
+ return length;
+}
+
+void wabt_write_u32_leb128(WabtStream* stream,
+ uint32_t value,
+ const char* desc) {
+ uint32_t length =
+ wabt_write_u32_leb128_at(stream, stream->offset, value, desc);
+ stream->offset += length;
+}
+
+void wabt_write_fixed_u32_leb128(WabtStream* stream,
+ uint32_t value,
+ const char* desc) {
+ uint32_t length =
+ wabt_write_fixed_u32_leb128_at(stream, stream->offset, value, desc);
+ stream->offset += length;
+}
+
+void wabt_write_i32_leb128(WabtStream* stream,
+ int32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ uint32_t i = 0;
+ if (value < 0)
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ else
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+
+ uint32_t length = i;
+ wabt_write_data_at(stream, stream->offset, data, length,
+ WABT_DONT_PRINT_CHARS, desc);
+ stream->offset += length;
+}
+
+static void write_i64_leb128(WabtStream* stream,
+ int64_t value,
+ const char* desc) {
+ uint8_t data[MAX_U64_LEB128_BYTES];
+ uint32_t i = 0;
+ if (value < 0)
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ else
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+
+ int length = i;
+ wabt_write_data_at(stream, stream->offset, data, length,
+ WABT_DONT_PRINT_CHARS, desc);
+ stream->offset += length;
+}
+
+#undef LEB128_LOOP_UNTIL
+
+static uint32_t size_u32_leb128(uint32_t value) {
+ uint32_t size = 0;
+ do {
+ value >>= 7;
+ size++;
+ } while (value != 0);
+ return size;
+}
+
+/* returns offset of leb128 */
+static uint32_t write_u32_leb128_space(Context* ctx,
+ 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 = ctx->stream.offset;
+ uint32_t bytes_to_write =
+ ctx->options->canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES;
+ wabt_write_data(&ctx->stream, data, bytes_to_write, desc);
+ return result;
+}
+
+static void write_fixup_u32_leb128_size(Context* ctx,
+ uint32_t offset,
+ uint32_t leb_size_guess,
+ const char* desc) {
+ if (ctx->options->canonicalize_lebs) {
+ uint32_t size = ctx->stream.offset - offset - leb_size_guess;
+ uint32_t leb_size = size_u32_leb128(size);
+ if (leb_size != leb_size_guess) {
+ uint32_t src_offset = offset + leb_size_guess;
+ uint32_t dst_offset = offset + leb_size;
+ wabt_move_data(&ctx->stream, dst_offset, src_offset, size);
+ }
+ wabt_write_u32_leb128_at(&ctx->stream, offset, size, desc);
+ ctx->stream.offset += leb_size - leb_size_guess;
+ } else {
+ uint32_t size = ctx->stream.offset - offset - MAX_U32_LEB128_BYTES;
+ wabt_write_fixed_u32_leb128_at(&ctx->stream, offset, size, desc);
+ }
+}
+
+void wabt_write_str(WabtStream* stream,
+ const char* s,
+ size_t length,
+ WabtPrintChars print_chars,
+ const char* desc) {
+ wabt_write_u32_leb128(stream, length, "string length");
+ wabt_write_data_at(stream, stream->offset, s, length, print_chars, desc);
+ stream->offset += length;
+}
+
+void wabt_write_opcode(WabtStream* stream, WabtOpcode opcode) {
+ wabt_write_u8(stream, opcode, wabt_get_opcode_name(opcode));
+}
+
+void wabt_write_type(WabtStream* stream, WabtType type) {
+ wabt_write_i32_leb128(stream, type, wabt_get_type_name(type));
+}
+
+static void write_inline_signature_type(WabtStream* stream,
+ const WabtBlockSignature* sig) {
+ if (sig->size == 0) {
+ wabt_write_type(stream, WABT_TYPE_VOID);
+ } else if (sig->size == 1) {
+ wabt_write_type(stream, sig->data[0]);
+ } else {
+ /* this is currently unrepresentable */
+ wabt_write_u8(stream, 0xff, "INVALID INLINE SIGNATURE");
+ }
+}
+
+static void begin_known_section(Context* ctx,
+ WabtBinarySection section_code,
+ size_t leb_size_guess) {
+ assert(ctx->last_section_leb_size_guess == 0);
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)",
+ wabt_get_section_name(section_code), section_code);
+ write_header(ctx, desc, PRINT_HEADER_NO_INDEX);
+ wabt_write_u8(&ctx->stream, section_code, "section code");
+ ctx->last_section_type = section_code;
+ ctx->last_section_leb_size_guess = leb_size_guess;
+ ctx->last_section_offset =
+ write_u32_leb128_space(ctx, leb_size_guess, "section size (guess)");
+ ctx->last_section_payload_offset = ctx->stream.offset;
+}
+
+static void begin_custom_section(Context* ctx,
+ const char* name,
+ size_t leb_size_guess) {
+ assert(ctx->last_section_leb_size_guess == 0);
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name);
+ write_header(ctx, desc, PRINT_HEADER_NO_INDEX);
+ wabt_write_u8(&ctx->stream, WABT_BINARY_SECTION_CUSTOM,
+ "custom section code");
+ ctx->last_section_type = WABT_BINARY_SECTION_CUSTOM;
+ ctx->last_section_leb_size_guess = leb_size_guess;
+ ctx->last_section_offset =
+ write_u32_leb128_space(ctx, leb_size_guess, "section size (guess)");
+ ctx->last_section_payload_offset = ctx->stream.offset;
+ wabt_write_str(&ctx->stream, name, strlen(name), WABT_PRINT_CHARS,
+ "custom section name");
+}
+
+static void end_section(Context* ctx) {
+ assert(ctx->last_section_leb_size_guess != 0);
+ write_fixup_u32_leb128_size(ctx, ctx->last_section_offset,
+ ctx->last_section_leb_size_guess,
+ "FIXUP section size");
+ ctx->last_section_leb_size_guess = 0;
+}
+
+static uint32_t get_label_var_depth(Context* ctx, const WabtVar* var) {
+ assert(var->type == WABT_VAR_TYPE_INDEX);
+ return var->index;
+}
+
+static void write_expr_list(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func,
+ const WabtExpr* first_expr);
+
+static void add_reloc(Context* ctx, WabtRelocType reloc_type) {
+ // Add a new reloc section if needed
+ if (!ctx->current_reloc_section ||
+ ctx->current_reloc_section->section_code != ctx->last_section_type) {
+ ctx->current_reloc_section =
+ wabt_append_reloc_section(&ctx->reloc_sections);
+ ctx->current_reloc_section->name =
+ wabt_get_section_name(ctx->last_section_type);
+ ctx->current_reloc_section->section_code = ctx->last_section_type;
+ }
+
+ // Add a new relocation to the curent reloc section
+ Reloc* r = wabt_append_reloc(&ctx->current_reloc_section->relocations);
+ r->type = reloc_type;
+ r->offset = ctx->stream.offset - ctx->last_section_payload_offset;
+}
+
+static void write_u32_leb128_with_reloc(Context* ctx,
+ uint32_t value,
+ const char* desc,
+ WabtRelocType reloc_type) {
+ if (ctx->options->relocatable) {
+ add_reloc(ctx, reloc_type);
+ wabt_write_fixed_u32_leb128(&ctx->stream, value, desc);
+ } else {
+ wabt_write_u32_leb128(&ctx->stream, value, desc);
+ }
+}
+
+static void write_expr(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func,
+ const WabtExpr* expr) {
+ switch (expr->type) {
+ case WABT_EXPR_TYPE_BINARY:
+ wabt_write_opcode(&ctx->stream, expr->binary.opcode);
+ break;
+ case WABT_EXPR_TYPE_BLOCK:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_BLOCK);
+ write_inline_signature_type(&ctx->stream, &expr->block.sig);
+ write_expr_list(ctx, module, func, expr->block.first);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_END);
+ break;
+ case WABT_EXPR_TYPE_BR:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_BR);
+ wabt_write_u32_leb128(
+ &ctx->stream, get_label_var_depth(ctx, &expr->br.var), "break depth");
+ break;
+ case WABT_EXPR_TYPE_BR_IF:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_BR_IF);
+ wabt_write_u32_leb128(&ctx->stream,
+ get_label_var_depth(ctx, &expr->br_if.var),
+ "break depth");
+ break;
+ case WABT_EXPR_TYPE_BR_TABLE: {
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_BR_TABLE);
+ wabt_write_u32_leb128(&ctx->stream, expr->br_table.targets.size,
+ "num targets");
+ size_t i;
+ uint32_t depth;
+ for (i = 0; i < expr->br_table.targets.size; ++i) {
+ depth = get_label_var_depth(ctx, &expr->br_table.targets.data[i]);
+ wabt_write_u32_leb128(&ctx->stream, depth, "break depth");
+ }
+ depth = get_label_var_depth(ctx, &expr->br_table.default_target);
+ wabt_write_u32_leb128(&ctx->stream, depth, "break depth for default");
+ break;
+ }
+ case WABT_EXPR_TYPE_CALL: {
+ int index = wabt_get_func_index_by_var(module, &expr->call.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_CALL);
+ write_u32_leb128_with_reloc(ctx, index, "function index",
+ WABT_RELOC_FUNC_INDEX_LEB);
+ break;
+ }
+ case WABT_EXPR_TYPE_CALL_INDIRECT: {
+ int index =
+ wabt_get_func_type_index_by_var(module, &expr->call_indirect.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_CALL_INDIRECT);
+ wabt_write_u32_leb128(&ctx->stream, index, "signature index");
+ wabt_write_u32_leb128(&ctx->stream, 0, "call_indirect reserved");
+ break;
+ }
+ case WABT_EXPR_TYPE_COMPARE:
+ wabt_write_opcode(&ctx->stream, expr->compare.opcode);
+ break;
+ case WABT_EXPR_TYPE_CONST:
+ switch (expr->const_.type) {
+ case WABT_TYPE_I32: {
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_I32_CONST);
+ wabt_write_i32_leb128(&ctx->stream, (int32_t)expr->const_.u32,
+ "i32 literal");
+ break;
+ }
+ case WABT_TYPE_I64:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_I64_CONST);
+ write_i64_leb128(&ctx->stream, (int64_t)expr->const_.u64,
+ "i64 literal");
+ break;
+ case WABT_TYPE_F32:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_F32_CONST);
+ wabt_write_u32(&ctx->stream, expr->const_.f32_bits, "f32 literal");
+ break;
+ case WABT_TYPE_F64:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_F64_CONST);
+ wabt_write_u64(&ctx->stream, expr->const_.f64_bits, "f64 literal");
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case WABT_EXPR_TYPE_CONVERT:
+ wabt_write_opcode(&ctx->stream, expr->convert.opcode);
+ break;
+ case WABT_EXPR_TYPE_CURRENT_MEMORY:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_CURRENT_MEMORY);
+ wabt_write_u32_leb128(&ctx->stream, 0, "current_memory reserved");
+ break;
+ case WABT_EXPR_TYPE_DROP:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_DROP);
+ break;
+ case WABT_EXPR_TYPE_GET_GLOBAL: {
+ int index = wabt_get_global_index_by_var(module, &expr->get_global.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_GET_GLOBAL);
+ write_u32_leb128_with_reloc(ctx, index, "global index",
+ WABT_RELOC_GLOBAL_INDEX_LEB);
+ break;
+ }
+ case WABT_EXPR_TYPE_GET_LOCAL: {
+ int index = wabt_get_local_index_by_var(func, &expr->get_local.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_GET_LOCAL);
+ wabt_write_u32_leb128(&ctx->stream, index, "local index");
+ break;
+ }
+ case WABT_EXPR_TYPE_GROW_MEMORY:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_GROW_MEMORY);
+ wabt_write_u32_leb128(&ctx->stream, 0, "grow_memory reserved");
+ break;
+ case WABT_EXPR_TYPE_IF:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_IF);
+ write_inline_signature_type(&ctx->stream, &expr->if_.true_.sig);
+ write_expr_list(ctx, module, func, expr->if_.true_.first);
+ if (expr->if_.false_) {
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_ELSE);
+ write_expr_list(ctx, module, func, expr->if_.false_);
+ }
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_END);
+ break;
+ case WABT_EXPR_TYPE_LOAD: {
+ wabt_write_opcode(&ctx->stream, expr->load.opcode);
+ uint32_t align =
+ wabt_get_opcode_alignment(expr->load.opcode, expr->load.align);
+ wabt_write_u8(&ctx->stream, log2_u32(align), "alignment");
+ wabt_write_u32_leb128(&ctx->stream, (uint32_t)expr->load.offset,
+ "load offset");
+ break;
+ }
+ case WABT_EXPR_TYPE_LOOP:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_LOOP);
+ write_inline_signature_type(&ctx->stream, &expr->loop.sig);
+ write_expr_list(ctx, module, func, expr->loop.first);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_END);
+ break;
+ case WABT_EXPR_TYPE_NOP:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_NOP);
+ break;
+ case WABT_EXPR_TYPE_RETURN:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_RETURN);
+ break;
+ case WABT_EXPR_TYPE_SELECT:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_SELECT);
+ break;
+ case WABT_EXPR_TYPE_SET_GLOBAL: {
+ int index = wabt_get_global_index_by_var(module, &expr->get_global.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_SET_GLOBAL);
+ write_u32_leb128_with_reloc(ctx, index, "global index",
+ WABT_RELOC_GLOBAL_INDEX_LEB);
+ break;
+ }
+ case WABT_EXPR_TYPE_SET_LOCAL: {
+ int index = wabt_get_local_index_by_var(func, &expr->get_local.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_SET_LOCAL);
+ wabt_write_u32_leb128(&ctx->stream, index, "local index");
+ break;
+ }
+ case WABT_EXPR_TYPE_STORE: {
+ wabt_write_opcode(&ctx->stream, expr->store.opcode);
+ uint32_t align =
+ wabt_get_opcode_alignment(expr->store.opcode, expr->store.align);
+ wabt_write_u8(&ctx->stream, log2_u32(align), "alignment");
+ wabt_write_u32_leb128(&ctx->stream, (uint32_t)expr->store.offset,
+ "store offset");
+ break;
+ }
+ case WABT_EXPR_TYPE_TEE_LOCAL: {
+ int index = wabt_get_local_index_by_var(func, &expr->get_local.var);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_TEE_LOCAL);
+ wabt_write_u32_leb128(&ctx->stream, index, "local index");
+ break;
+ }
+ case WABT_EXPR_TYPE_UNARY:
+ wabt_write_opcode(&ctx->stream, expr->unary.opcode);
+ break;
+ case WABT_EXPR_TYPE_UNREACHABLE:
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_UNREACHABLE);
+ break;
+ }
+}
+
+static void write_expr_list(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func,
+ const WabtExpr* first) {
+ const WabtExpr* expr;
+ for (expr = first; expr; expr = expr->next)
+ write_expr(ctx, module, func, expr);
+}
+
+static void write_init_expr(Context* ctx,
+ const WabtModule* module,
+ const WabtExpr* expr) {
+ if (expr)
+ write_expr_list(ctx, module, NULL, expr);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_END);
+}
+
+static void write_func_locals(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func,
+ const WabtTypeVector* local_types) {
+ if (local_types->size == 0) {
+ wabt_write_u32_leb128(&ctx->stream, 0, "local decl count");
+ return;
+ }
+
+ uint32_t num_params = wabt_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->data[x - num_params])
+
+ /* loop through once to count the number of local declaration runs */
+ WabtType current_type = GET_LOCAL_TYPE(FIRST_LOCAL_INDEX);
+ uint32_t local_decl_count = 1;
+ uint32_t i;
+ for (i = FIRST_LOCAL_INDEX + 1; i < LAST_LOCAL_INDEX; ++i) {
+ WabtType type = GET_LOCAL_TYPE(i);
+ if (current_type != type) {
+ local_decl_count++;
+ current_type = type;
+ }
+ }
+
+ /* loop through again to write everything out */
+ wabt_write_u32_leb128(&ctx->stream, local_decl_count, "local decl count");
+ current_type = GET_LOCAL_TYPE(FIRST_LOCAL_INDEX);
+ uint32_t local_type_count = 1;
+ for (i = FIRST_LOCAL_INDEX + 1; i <= LAST_LOCAL_INDEX; ++i) {
+ /* loop through an extra time to catch the final type transition */
+ WabtType type = i == LAST_LOCAL_INDEX ? WABT_TYPE_VOID : GET_LOCAL_TYPE(i);
+ if (current_type == type) {
+ local_type_count++;
+ } else {
+ wabt_write_u32_leb128(&ctx->stream, local_type_count, "local type count");
+ wabt_write_type(&ctx->stream, current_type);
+ local_type_count = 1;
+ current_type = type;
+ }
+ }
+}
+
+static void write_func(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func) {
+ write_func_locals(ctx, module, func, &func->local_types);
+ write_expr_list(ctx, module, func, func->first_expr);
+ wabt_write_opcode(&ctx->stream, WABT_OPCODE_END);
+}
+
+void wabt_write_limits(WabtStream* stream, const WabtLimits* limits) {
+ uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0;
+ wabt_write_u32_leb128(stream, flags, "limits: flags");
+ wabt_write_u32_leb128(stream, limits->initial, "limits: initial");
+ if (limits->has_max)
+ wabt_write_u32_leb128(stream, limits->max, "limits: max");
+}
+
+static void write_table(Context* ctx, const WabtTable* table) {
+ wabt_write_type(&ctx->stream, WABT_TYPE_ANYFUNC);
+ wabt_write_limits(&ctx->stream, &table->elem_limits);
+}
+
+static void write_memory(Context* ctx, const WabtMemory* memory) {
+ wabt_write_limits(&ctx->stream, &memory->page_limits);
+}
+
+static void write_global_header(Context* ctx, const WabtGlobal* global) {
+ wabt_write_type(&ctx->stream, global->type);
+ wabt_write_u8(&ctx->stream, global->mutable_, "global mutability");
+}
+
+static void write_reloc_section(Context* ctx, RelocSection* reloc_section) {
+ char section_name[128];
+ sprintf(section_name, "%s.%s", WABT_BINARY_SECTION_RELOC,
+ reloc_section->name);
+ begin_custom_section(ctx, section_name, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, reloc_section->section_code,
+ "reloc section type");
+ RelocVector* relocs = &reloc_section->relocations;
+ wabt_write_u32_leb128(&ctx->stream, relocs->size, "num relocs");
+
+ size_t i;
+ for (i = 0; i < relocs->size; i++) {
+ wabt_write_u32_leb128(&ctx->stream, relocs->data[i].type, "reloc type");
+ wabt_write_u32_leb128(&ctx->stream, relocs->data[i].offset, "reloc offset");
+ }
+
+ end_section(ctx);
+}
+
+static WabtResult write_module(Context* ctx, const WabtModule* module) {
+ size_t i;
+ wabt_write_u32(&ctx->stream, WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
+ wabt_write_u32(&ctx->stream, WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
+
+ if (module->func_types.size) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_TYPE, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->func_types.size, "num types");
+ for (i = 0; i < module->func_types.size; ++i) {
+ const WabtFuncType* func_type = module->func_types.data[i];
+ const WabtFuncSignature* sig = &func_type->sig;
+ write_header(ctx, "type", i);
+ wabt_write_type(&ctx->stream, WABT_TYPE_FUNC);
+
+ size_t j;
+ uint32_t num_params = sig->param_types.size;
+ uint32_t num_results = sig->result_types.size;
+ wabt_write_u32_leb128(&ctx->stream, num_params, "num params");
+ for (j = 0; j < num_params; ++j)
+ wabt_write_type(&ctx->stream, sig->param_types.data[j]);
+
+ wabt_write_u32_leb128(&ctx->stream, num_results, "num results");
+ for (j = 0; j < num_results; ++j)
+ wabt_write_type(&ctx->stream, sig->result_types.data[j]);
+ }
+ end_section(ctx);
+ }
+
+ if (module->imports.size) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_IMPORT,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->imports.size, "num imports");
+
+ for (i = 0; i < module->imports.size; ++i) {
+ const WabtImport* import = module->imports.data[i];
+ write_header(ctx, "import header", i);
+ wabt_write_str(&ctx->stream, import->module_name.start,
+ import->module_name.length, WABT_PRINT_CHARS,
+ "import module name");
+ wabt_write_str(&ctx->stream, import->field_name.start,
+ import->field_name.length, WABT_PRINT_CHARS,
+ "import field name");
+ wabt_write_u8(&ctx->stream, import->kind, "import kind");
+ switch (import->kind) {
+ case WABT_EXTERNAL_KIND_FUNC:
+ wabt_write_u32_leb128(&ctx->stream, wabt_get_func_type_index_by_decl(
+ module, &import->func.decl),
+ "import signature index");
+ break;
+ case WABT_EXTERNAL_KIND_TABLE:
+ write_table(ctx, &import->table);
+ break;
+ case WABT_EXTERNAL_KIND_MEMORY:
+ write_memory(ctx, &import->memory);
+ break;
+ case WABT_EXTERNAL_KIND_GLOBAL:
+ write_global_header(ctx, &import->global);
+ break;
+ case WABT_NUM_EXTERNAL_KINDS:
+ assert(0);
+ break;
+ }
+ }
+ end_section(ctx);
+ }
+
+ assert(module->funcs.size >= module->num_func_imports);
+ uint32_t num_funcs = module->funcs.size - module->num_func_imports;
+ if (num_funcs) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_FUNCTION,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, num_funcs, "num functions");
+
+ for (i = 0; i < num_funcs; ++i) {
+ const WabtFunc* func = module->funcs.data[i + module->num_func_imports];
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index",
+ i);
+ wabt_write_u32_leb128(
+ &ctx->stream, wabt_get_func_type_index_by_decl(module, &func->decl),
+ desc);
+ }
+ end_section(ctx);
+ }
+
+ assert(module->tables.size >= module->num_table_imports);
+ uint32_t num_tables = module->tables.size - module->num_table_imports;
+ if (num_tables) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_TABLE, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, num_tables, "num tables");
+ for (i = 0; i < num_tables; ++i) {
+ const WabtTable* table =
+ module->tables.data[i + module->num_table_imports];
+ write_header(ctx, "table", i);
+ write_table(ctx, table);
+ }
+ end_section(ctx);
+ }
+
+ assert(module->memories.size >= module->num_memory_imports);
+ uint32_t num_memories = module->memories.size - module->num_memory_imports;
+ if (num_memories) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_MEMORY,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, num_memories, "num memories");
+ for (i = 0; i < num_memories; ++i) {
+ const WabtMemory* memory =
+ module->memories.data[i + module->num_memory_imports];
+ write_header(ctx, "memory", i);
+ write_memory(ctx, memory);
+ }
+ end_section(ctx);
+ }
+
+ assert(module->globals.size >= module->num_global_imports);
+ uint32_t num_globals = module->globals.size - module->num_global_imports;
+ if (num_globals) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_GLOBAL,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, num_globals, "num globals");
+
+ for (i = 0; i < num_globals; ++i) {
+ const WabtGlobal* global =
+ module->globals.data[i + module->num_global_imports];
+ write_global_header(ctx, global);
+ write_init_expr(ctx, module, global->init_expr);
+ }
+ end_section(ctx);
+ }
+
+ if (module->exports.size) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_EXPORT,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->exports.size, "num exports");
+
+ for (i = 0; i < module->exports.size; ++i) {
+ const WabtExport* export_ = module->exports.data[i];
+ wabt_write_str(&ctx->stream, export_->name.start, export_->name.length,
+ WABT_PRINT_CHARS, "export name");
+ wabt_write_u8(&ctx->stream, export_->kind, "export kind");
+ switch (export_->kind) {
+ case WABT_EXTERNAL_KIND_FUNC: {
+ int index = wabt_get_func_index_by_var(module, &export_->var);
+ wabt_write_u32_leb128(&ctx->stream, index, "export func index");
+ break;
+ }
+ case WABT_EXTERNAL_KIND_TABLE: {
+ int index = wabt_get_table_index_by_var(module, &export_->var);
+ wabt_write_u32_leb128(&ctx->stream, index, "export table index");
+ break;
+ }
+ case WABT_EXTERNAL_KIND_MEMORY: {
+ int index = wabt_get_memory_index_by_var(module, &export_->var);
+ wabt_write_u32_leb128(&ctx->stream, index, "export memory index");
+ break;
+ }
+ case WABT_EXTERNAL_KIND_GLOBAL: {
+ int index = wabt_get_global_index_by_var(module, &export_->var);
+ wabt_write_u32_leb128(&ctx->stream, index, "export global index");
+ break;
+ }
+ case WABT_NUM_EXTERNAL_KINDS:
+ assert(0);
+ break;
+ }
+ }
+ end_section(ctx);
+ }
+
+ if (module->start) {
+ int start_func_index = wabt_get_func_index_by_var(module, module->start);
+ if (start_func_index != -1) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_START,
+ LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, start_func_index, "start func index");
+ end_section(ctx);
+ }
+ }
+
+ if (module->elem_segments.size) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_ELEM, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->elem_segments.size,
+ "num elem segments");
+ for (i = 0; i < module->elem_segments.size; ++i) {
+ WabtElemSegment* segment = module->elem_segments.data[i];
+ int table_index =
+ wabt_get_table_index_by_var(module, &segment->table_var);
+ write_header(ctx, "elem segment header", i);
+ wabt_write_u32_leb128(&ctx->stream, table_index, "table index");
+ write_init_expr(ctx, module, segment->offset);
+ wabt_write_u32_leb128(&ctx->stream, segment->vars.size,
+ "num function indices");
+ size_t j;
+ for (j = 0; j < segment->vars.size; ++j) {
+ int index = wabt_get_func_index_by_var(module, &segment->vars.data[j]);
+ write_u32_leb128_with_reloc(ctx, index, "function index",
+ WABT_RELOC_FUNC_INDEX_LEB);
+ }
+ }
+ end_section(ctx);
+ }
+
+ if (num_funcs) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_CODE, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, num_funcs, "num functions");
+
+ for (i = 0; i < num_funcs; ++i) {
+ write_header(ctx, "function body", i);
+ const WabtFunc* func = module->funcs.data[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 =
+ write_u32_leb128_space(ctx, leb_size_guess, "func body size (guess)");
+ write_func(ctx, module, func);
+ write_fixup_u32_leb128_size(ctx, body_size_offset, leb_size_guess,
+ "FIXUP func body size");
+ }
+ end_section(ctx);
+ }
+
+ if (module->data_segments.size) {
+ begin_known_section(ctx, WABT_BINARY_SECTION_DATA, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->data_segments.size,
+ "num data segments");
+ for (i = 0; i < module->data_segments.size; ++i) {
+ const WabtDataSegment* segment = module->data_segments.data[i];
+ write_header(ctx, "data segment header", i);
+ int memory_index =
+ wabt_get_memory_index_by_var(module, &segment->memory_var);
+ wabt_write_u32_leb128(&ctx->stream, memory_index, "memory index");
+ write_init_expr(ctx, module, segment->offset);
+ wabt_write_u32_leb128(&ctx->stream, segment->size, "data segment size");
+ write_header(ctx, "data segment data", i);
+ wabt_write_data(&ctx->stream, segment->data, segment->size,
+ "data segment data");
+ }
+ end_section(ctx);
+ }
+
+ if (ctx->options->write_debug_names) {
+ WabtStringSliceVector index_to_name;
+ WABT_ZERO_MEMORY(index_to_name);
+
+ char desc[100];
+ begin_custom_section(ctx, WABT_BINARY_SECTION_NAME, LEB_SECTION_SIZE_GUESS);
+ wabt_write_u32_leb128(&ctx->stream, module->funcs.size, "num functions");
+ for (i = 0; i < module->funcs.size; ++i) {
+ const WabtFunc* func = module->funcs.data[i];
+ uint32_t num_params = wabt_get_num_params(func);
+ uint32_t num_locals = func->local_types.size;
+ uint32_t num_params_and_locals = wabt_get_num_params_and_locals(func);
+
+ wabt_snprintf(desc, sizeof(desc), "func name %" PRIzd, i);
+ wabt_write_str(&ctx->stream, func->name.start, func->name.length,
+ WABT_PRINT_CHARS, desc);
+ wabt_write_u32_leb128(&ctx->stream, num_params_and_locals, "num locals");
+
+ if (num_params_and_locals) {
+ wabt_make_type_binding_reverse_mapping(
+ &func->decl.sig.param_types, &func->param_bindings, &index_to_name);
+ size_t j;
+ for (j = 0; j < num_params; ++j) {
+ WabtStringSlice name = index_to_name.data[j];
+ wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
+ wabt_write_str(&ctx->stream, name.start, name.length,
+ WABT_PRINT_CHARS, desc);
+ }
+
+ wabt_make_type_binding_reverse_mapping(
+ &func->local_types, &func->local_bindings, &index_to_name);
+ for (j = 0; j < num_locals; ++j) {
+ WabtStringSlice name = index_to_name.data[j];
+ wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd,
+ num_params + j);
+ wabt_write_str(&ctx->stream, name.start, name.length,
+ WABT_PRINT_CHARS, desc);
+ }
+ }
+ }
+ end_section(ctx);
+
+ wabt_destroy_string_slice_vector(&index_to_name);
+ }
+
+ if (ctx->options->relocatable) {
+ for (i = 0; i < ctx->reloc_sections.size; i++) {
+ write_reloc_section(ctx, &ctx->reloc_sections.data[i]);
+ }
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(ctx->reloc_sections, reloc_section);
+ }
+
+ return ctx->stream.result;
+}
+
+WabtResult wabt_write_binary_module(WabtWriter* writer,
+ const WabtModule* module,
+ const WabtWriteBinaryOptions* options) {
+ Context ctx;
+ WABT_ZERO_MEMORY(ctx);
+ ctx.options = options;
+ ctx.log_stream = options->log_stream;
+ wabt_init_stream(&ctx.stream, writer, ctx.log_stream);
+ return write_module(&ctx, module);
+}