struct AstNamedValue { AstTyped_base; AstTyped* value; };
struct AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
struct AstNumLit { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
-struct AstStrLit { AstTyped_base; u64 addr; u64 length; b32 is_cstr: 1; };
+struct AstStrLit { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; };
struct AstLocal { AstTyped_base; };
struct AstDereference { AstTyped_base; AstTyped *expr; };
struct AstSizeOf { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
AstTyped *filename_expr;
char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
- u32 addr, size;
+ u32 data_id, size;
};
struct AstUnaryFieldAccess {
AstTyped_base;
struct AstInclude { AstNode_base; AstTyped* name_node; char* name; };
struct AstMemRes {
AstTyped_base;
- u64 addr;
AstTyped *initial_value;
struct Entity *type_entity;
b32 threadlocal : 1;
+
+ // Set and used in the wasm emission.
+ u32 data_id;
+ u32 tls_offset;
};
struct AstGlobal {
AstTyped_base;
extern AstBasicType basic_type_auto_return;
extern OnyxToken builtin_package_token;
-extern AstNumLit builtin_heap_start;
+extern AstGlobal builtin_heap_start;
extern AstGlobal builtin_stack_top;
extern AstGlobal builtin_tls_base;
extern AstGlobal builtin_tls_size;
} WasmImport;
typedef struct WasmDatum {
- u32 offset, length;
+ u32 id;
+ u32 offset_, alignment;
+ u32 length;
ptr data;
} WasmDatum;
+typedef enum DatumPatchInfoKind {
+ Datum_Patch_Instruction,
+ Datum_Patch_Data,
+ Datum_Patch_Relative,
+} DatumPatchInfoKind;
+
+//
+// This represents a pointer that should be filled in
+// later when the corresponding data element is placed.
+//
+// There are three kinds of patches:
+// - Instruction
+// - Data
+// - Relative
+//
+// In all cases, the `data_id` member is set to the id
+// of the WasmDatum entry that will be the base address,
+// and then the `offset` member will be added to that.
+//
+// In instruction patches, the `index` member is set
+// to the index of the function where the instruction should
+// be patched. The `location` member is set to the instruction
+// that needs to have its data changed.
+//
+// In data patches, the `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. It is assumed that 4 bytes will be reserved
+// to be replaced with the pointer value.
+//
+// In relative patches, `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. The difference between `Data` and `Relative`
+// is that `Relative` *adds* the base address to the current
+// value in the 4 bytes, as opposed to replacing it. As a
+// convenience, if the value is 0 (null), it will remain as
+// 0.
+//
+typedef struct DatumPatchInfo {
+ DatumPatchInfoKind kind;
+ u32 data_id;
+ u32 offset;
+ u32 location;
+ u32 index;
+} DatumPatchInfo;
+
+// Context used when building a constexpr buffer
+typedef struct ConstExprContext {
+ struct OnyxWasmModule *module;
+ ptr data;
+ u32 data_id;
+} ConstExprContext;
+
typedef enum DeferredStmtType {
Deferred_Stmt_Node,
Deferred_Stmt_Code,
} AllocatedSpace;
typedef struct StrLitInfo {
- u32 addr;
+ u32 data_id;
u32 len;
} StrLitInfo;
// NOTE: Mapping from local ast node ptrs to indicies or offsets, depending on the mode
bh_imap local_map;
+ i32 current_func_idx;
LocalAllocator* local_alloc;
// NOTE: Mapping ptrs to elements
bh_arr(AllocatedSpace) local_allocations;
bh_arr(PatchInfo) stack_leave_patches;
+ bh_arr(DatumPatchInfo) data_patches;
bh_arr(ForRemoveInfo) for_remove_info;
u32 next_type_idx;
u32 next_func_idx;
u32 next_global_idx;
- u32 next_datum_offset;
u32 next_tls_offset;
u32 next_elem_idx;
u32 foreign_function_count;
i32 *stack_top_ptr;
i32 *tls_size_ptr;
+ i32 *heap_start_ptr;
u64 stack_base_idx;
CallingConvention curr_cc;
i32 null_proc_func_idx;
b32 has_stack_locals : 1;
} OnyxWasmModule;
+typedef struct OnyxWasmLinkOptions {
+} OnyxWasmLinkOptions;
+
OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options);
void onyx_wasm_module_free(OnyxWasmModule* module);
void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer);
void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
static OnyxToken builtin_stack_top_token = { Token_Type_Symbol, 11, "__stack_top ", { 0 } };
static OnyxToken builtin_tls_base_token = { Token_Type_Symbol, 10, "__tls_base ", { 0 } };
static OnyxToken builtin_tls_size_token = { Token_Type_Symbol, 10, "__tls_size ", { 0 } };
-AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
+AstGlobal builtin_heap_start = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_stack_top = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_tls_base = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
AstGlobal builtin_tls_size = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
memset(filename, 0, sizeof(AstStrLit));
filename->kind = Ast_Kind_StrLit;
filename->token = str_token;
- filename->addr = 0;
+ filename->data_id = 0;
add_entities_for_node(NULL, (AstNode *) filename, NULL, NULL);
callsite->filename = filename;
}
add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
}
static CompilerProgress onyx_flush_module() {
+ onyx_wasm_module_link(context.wasm_module, NULL);
// NOTE: Output to file
bh_file output_file;
#ifdef ENABLE_RUN_WITH_WASMER
static b32 onyx_run() {
+ onyx_wasm_module_link(context.wasm_module, NULL);
+
bh_buffer code_buffer;
onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer);
case Token_Type_Literal_String: {
AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
str_node->token = expect_token(parser, Token_Type_Literal_String);
- str_node->addr = 0;
+ str_node->data_id = 0;
str_node->flags |= Ast_Flag_Comptime;
ENTITY_SUBMIT(str_node);
AstStrLit* filename = make_node(AstStrLit, Ast_Kind_StrLit);
filename->token = str_token;
- filename->addr = 0;
+ filename->data_id = 0;
ENTITY_SUBMIT(filename);
retval = (AstTyped *) filename;
// Copy pasted from above.
AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
str_node->token = expect_token(parser, Token_Type_Literal_String);
- str_node->addr = 0;
+ str_node->data_id = 0;
str_node->flags |= Ast_Flag_Comptime;
str_node->is_cstr = 1;
local_raw_free(mod->local_alloc, type2); \
}
#define SUBMIT_PATCH(patch_arr, offset) bh_arr_push((patch_arr), ((PatchInfo) { bh_arr_length(code) - offset }))
+#define NEXT_DATA_ID(mod) ((u32) bh_arr_length((mod)->data) + 1)
EMIT_FUNC(function_body, AstFunction* fd);
EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers);
EMIT_FUNC(statement, AstNode* stmt);
EMIT_FUNC(local_allocation, AstTyped* stmt);
EMIT_FUNC_NO_ARGS(free_local_allocations);
+EMIT_FUNC(data_relocation, u32 data_id);
EMIT_FUNC(assignment, AstBinaryOp* assign);
EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right);
EMIT_FUNC(compound_assignment, AstBinaryOp* assign);
EMIT_FUNC(enter_structured_block, StructuredBlockType sbt);
EMIT_FUNC_NO_ARGS(leave_structured_block);
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node);
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node);
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum);
+
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset);
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset);
#include "wasm_intrinsics.h"
#include "wasm_type_table.h"
}
}
+EMIT_FUNC(data_relocation, u32 data_id) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u32 instr_idx = bh_arr_length(code);
+ WID(WI_PTR_CONST, 0);
+ assert(mod->current_func_idx >= 0);
+
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Instruction;
+ patch.index = mod->current_func_idx;
+ patch.location = instr_idx;
+ patch.data_id = data_id;
+ patch.offset = 0;
+ bh_arr_push(mod->data_patches, patch);
+
+ *pcode = code;
+}
+
EMIT_FUNC(assignment, AstBinaryOp* assign) {
bh_arr(WasmInstruction) code = *pcode;
EMIT_FUNC(memory_reservation_location, AstMemRes* memres) {
bh_arr(WasmInstruction) code = *pcode;
- WID(WI_PTR_CONST, memres->addr);
if (memres->threadlocal) {
u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base);
+ WID(WI_PTR_CONST, memres->tls_offset);
WIL(WI_GLOBAL_GET, tls_base_idx);
WI(WI_PTR_ADD);
+
+ } else {
+ // :ProperLinking
+ assert(memres->data_id != 0);
+ emit_data_relocation(mod, &code, memres->data_id);
}
*pcode = code;
}
case Ast_Kind_StrLit: {
- WID(WI_PTR_CONST, ((AstStrLit *) expr)->addr);
+ // :ProperLinking
+ AstStrLit *strlit = (AstStrLit *) expr;
+ assert(strlit->data_id > 0);
+ emit_data_relocation(mod, &code, strlit->data_id);
- if (((AstStrLit *) expr)->is_cstr == 0)
- WID(WI_I32_CONST, ((AstStrLit *) expr)->length);
+ if (strlit->is_cstr == 0)
+ WID(WI_I32_CONST, strlit->length);
break;
}
case Ast_Kind_File_Contents: {
AstFileContents* fc = (AstFileContents *) expr;
- assert(fc->addr > 0);
+ assert(fc->data_id > 0);
assert(fc->size > 0);
- WID(WI_PTR_CONST, fc->addr);
+ // :ProperLinking
+ emit_data_relocation(mod, &code, fc->data_id);
WID(WI_I32_CONST, fc->size);
break;
}
bh_arr_new(mod->allocator, wasm_func.code, 4);
i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
+ mod->current_func_idx = func_idx;
if (fd == builtin_initialize_data_segments && context.options->use_post_mvp_features) {
emit_initialize_data_segments_body(mod, &wasm_func.code);
bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+ mod->current_func_idx = -1;
return;
}
emit_run_init_procedures(mod, &wasm_func.code);
bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+ mod->current_func_idx = -1;
return;
}
bh_imap_clear(&mod->local_map);
bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+ mod->current_func_idx = -1;
}
static void emit_foreign_function(OnyxWasmModule* mod, AstFunction* fd) {
if (global == &builtin_stack_top)
module->stack_top_ptr = &module->globals[global_idx].initial_value[0].data.i1;
- if (global == &builtin_tls_size) {
+ if (global == &builtin_heap_start)
+ module->heap_start_ptr = &module->globals[global_idx].initial_value[0].data.i1;
+
+ if (global == &builtin_tls_size)
module->globals[global_idx].initial_value[0].data.i1 = module->next_tls_offset;
- }
}
static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
i8* strdata = bh_alloc_array(global_heap_allocator, i8, strlit->token->length + 1);
i32 length = string_process_escape_seqs(strdata, strlit->token->text, strlit->token->length);
- // Warning for having '%' in a string literal (because that probably is being used for a old print format)
- /*
- if (charset_contains((const char *) strdata, '%')) {
- onyx_report_warning(strlit->token->pos, "Found string literal with '%%'");
- }
- */
-
i32 index = shgeti(mod->string_literals, (char *) strdata);
if (index != -1) {
StrLitInfo sti = mod->string_literals[index].value;
- strlit->addr = sti.addr;
- strlit->length = sti.len + (strlit->is_cstr ? 1 : 0);
+ strlit->data_id = sti.data_id;
+ strlit->length = sti.len;
bh_free(global_heap_allocator, strdata);
return;
}
-
+
+ // :ProperLinking
u32 actual_length = length + (strlit->is_cstr ? 1 : 0);
WasmDatum datum = {
- .offset = mod->next_datum_offset,
+ .alignment = 1,
.length = actual_length,
.data = strdata,
};
- strlit->addr = (u32) mod->next_datum_offset,
- strlit->length = length;
- mod->next_datum_offset += actual_length;
+ strlit->data_id = emit_data_entry(mod, &datum);
+ strlit->length = length;
- shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->addr, strlit->length }));
+ // :ProperLinking
+ shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->data_id, strlit->length }));
+}
- bh_arr_push(mod->data, datum);
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) {
+ datum->offset_ = 0;
+ datum->id = NEXT_DATA_ID(mod);
+ bh_arr_push(mod->data, *datum);
+ return datum->id;
}
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node) {
- if (!emit_raw_data_(mod, data, node)) {
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+ if (!emit_constexpr_(ctx, node, offset)) {
onyx_report_error(node->token->pos, Error_Critical,
"Cannot generate constant data for '%s'.",
onyx_ast_node_kind_string(node->kind));
}
}
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node) {
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+#define CE(type, off) (*((type *) bh_pointer_add(ctx->data, offset + (off))))
+ assert(ctx->data_id);
+ assert(ctx->data);
+
b32 retval = 1;
node = (AstTyped *) strip_aliases((AstNode *) node);
if (node_is_type((AstNode *) node)) {
Type* constructed_type = type_build_from_ast(context.ast_alloc, (AstType *) node);
- ((i32 *) data)[0] = constructed_type->id;
+ CE(i32, 0) = constructed_type->id;
return 1;
}
i32 elem_size = type_size_of(al->type->Array.elem);
bh_arr_each(AstTyped *, expr, al->values) {
- retval &= emit_raw_data_(mod, bh_pointer_add(data, i * elem_size), *expr);
+ retval &= emit_constexpr_(ctx, *expr, i * elem_size + offset);
i++;
}
fori (i, 0, mem_count) {
type_lookup_member_by_idx(sl_type, i, &smem);
- retval &= emit_raw_data_(mod, bh_pointer_add(data, smem.offset), sl->args.values[i]);
+ retval &= emit_constexpr_(ctx, sl->args.values[i], smem.offset + offset);
}
break;
case Ast_Kind_StrLit: {
AstStrLit* sl = (AstStrLit *) node;
- // NOTE: This assumes the address and the length fields have been filled out
+ // NOTE: This assumes the data_id and the length fields have been filled out
// by emit_string_literal.
- u32* sdata = (u32 *) data;
if (POINTER_SIZE == 4) {
- sdata[0] = sl->addr;
- sdata[1] = sl->length;
+ CE(u32, 0) = 0;
+ CE(u32, 4) = sl->length;
} else {
- sdata[0] = sl->addr;
- sdata[1] = 0;
- sdata[2] = sl->length;
- sdata[3] = 0;
+ CE(u64, 0) = 0;
+ CE(u64, 8) = sl->length;
}
+
+ assert(sl->data_id > 0);
+
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.index = ctx->data_id;
+ patch.location = offset;
+ patch.data_id = sl->data_id;
+ patch.offset = 0;
+ bh_arr_push(ctx->module->data_patches, patch);
+
break;
}
case Ast_Kind_Enum_Value: {
AstEnumValue* ev = (AstEnumValue *) node;
- retval &= emit_raw_data_(mod, data, (AstTyped *) ev->value);
+ retval &= emit_constexpr_(ctx, (AstTyped *) ev->value, offset);
break;
}
case Ast_Kind_Function: {
AstFunction* func = (AstFunction *) node;
- *((u32 *) data) = get_element_idx(mod, func);
+ CE(u32, 0) = get_element_idx(ctx->module, func);
break;
}
case Ast_Kind_Size_Of: {
AstSizeOf* so = (AstSizeOf *) node;
- *((u32 *) data) = so->size;
+ CE(u32, 0) = so->size;
break;
}
case Ast_Kind_Align_Of: {
AstAlignOf* ao = (AstAlignOf *) node;
- *((u32 *) data) = ao->alignment;
+ CE(u32, 0) = ao->alignment;
break;
}
case Ast_Kind_Zero_Value: {
- memset(data, 0, type_size_of(node->type));
+ memset(bh_pointer_add(ctx->data, offset), 0, type_size_of(node->type));
break;
}
case Basic_Kind_Bool:
case Basic_Kind_I8:
case Basic_Kind_U8:
- *((i8 *) data) = (i8) ((AstNumLit *) node)->value.i;
+ CE(i8, 0) = (i8) ((AstNumLit *) node)->value.i;
return retval;
case Basic_Kind_I16:
case Basic_Kind_U16:
- *((i16 *) data) = (i16) ((AstNumLit *) node)->value.i;
+ CE(i16, 0) = (i16) ((AstNumLit *) node)->value.i;
return retval;
case Basic_Kind_I32:
case Basic_Kind_U32:
case Basic_Kind_Rawptr:
- *((i32 *) data) = ((AstNumLit *) node)->value.i;
+ CE(i32, 0) = ((AstNumLit *) node)->value.i;
return retval;
case Basic_Kind_I64:
case Basic_Kind_U64:
- *((i64 *) data) = ((AstNumLit *) node)->value.l;
+ CE(i64, 0) = ((AstNumLit *) node)->value.l;
return retval;
case Basic_Kind_F32:
- *((f32 *) data) = ((AstNumLit *) node)->value.f;
+ CE(f32, 0) = ((AstNumLit *) node)->value.f;
return retval;
case Basic_Kind_F64:
- *((f64 *) data) = ((AstNumLit *) node)->value.d;
+ CE(f64, 0) = ((AstNumLit *) node)->value.d;
return retval;
default: break;
}
return retval;
+
+#undef CE
}
static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
+ // :ProperLinking
Type* effective_type = memres->type;
u64 alignment = type_alignment_of(effective_type);
if (type_table_node != NULL && (AstMemRes *) type_table_node == memres) {
u64 table_location = build_type_table(mod);
- memres->addr = table_location;
-
+ memres->data_id = table_location;
return;
}
if (foreign_blocks_node != NULL && (AstMemRes *) foreign_blocks_node == memres) {
u64 foreign_blocks_location = build_foreign_blocks(mod);
- memres->addr = foreign_blocks_location;
-
+ memres->data_id = foreign_blocks_location;
return;
}
if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
u64 tagged_procedures_location = build_tagged_procedures(mod);
- memres->addr = tagged_procedures_location;
-
+ memres->data_id = tagged_procedures_location;
return;
}
if (memres->threadlocal) {
- memres->addr = mod->next_tls_offset;
- bh_align(memres->addr, alignment);
- mod->next_tls_offset = memres->addr + size;
+ memres->tls_offset = mod->next_tls_offset;
+ bh_align(memres->tls_offset, alignment);
+ mod->next_tls_offset = memres->tls_offset + size;
} else {
- memres->addr = mod->next_datum_offset;
- bh_align(memres->addr, alignment);
- mod->next_datum_offset = memres->addr + size;
- }
-
- if (memres->initial_value != NULL) {
- assert(!memres->threadlocal);
-
- u8* data = bh_alloc(global_heap_allocator, size);
- emit_raw_data(mod, data, memres->initial_value);
+ // :ProperLinking
+ u8* data = NULL;
+ if (memres->initial_value != NULL) {
+ assert(!memres->threadlocal);
+ data = bh_alloc(global_heap_allocator, size);
+ }
WasmDatum datum = {
- .offset = memres->addr,
+ .alignment = alignment,
.length = size,
.data = data,
};
-
- bh_arr_push(mod->data, datum);
+ memres->data_id = emit_data_entry(mod, &datum);
+
+ if (memres->initial_value != NULL) {
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = mod;
+ constexpr_ctx.data = data;
+ constexpr_ctx.data_id = memres->data_id;
+ emit_constexpr(&constexpr_ctx, memres->initial_value, 0);
+ }
}
}
i32 index = shgeti(mod->loaded_file_info, fc->filename);
if (index != -1) {
StrLitInfo info = mod->loaded_file_info[index].value;
- fc->addr = info.addr;
- fc->size = info.len;
+ fc->data_id = info.data_id;
+ fc->size = info.len;
return;
}
- u32 offset = mod->next_datum_offset;
- bh_align(offset, 16);
-
if (!bh_file_exists(fc->filename)) {
onyx_report_error(fc->token->pos, Error_Critical,
"Unable to open file for reading, '%s'.",
actual_data[contents.length] = 0;
bh_file_contents_free(&contents);
- shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
- .addr = offset,
- .len = length - 1,
- }));
-
- fc->addr = offset;
- fc->size = length - 1;
-
WasmDatum datum = {
- .offset = offset,
+ .alignment = 16,
.length = length,
.data = actual_data,
};
+ fc->data_id = emit_data_entry(mod, &datum);
+ fc->size = length - 1;
- bh_arr_push(mod->data, datum);
-
- mod->next_datum_offset = offset + length;
+ shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
+ .data_id = fc->data_id,
+ .len = fc->size,
+ }));
}
OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
.next_global_idx = 0,
.data = NULL,
- .next_datum_offset = 32, // Starting offset so null pointers don't immediately
- // break constant data. - brendanfh 2020/12/16
+ .data_patches = NULL,
.next_tls_offset = 0,
.tls_size_ptr = NULL,
.stack_leave_patches = NULL,
.deferred_stmts = NULL,
+ .heap_start_ptr = NULL,
+
.stack_top_ptr = NULL,
.stack_base_idx = 0,
bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
-
- if (context.options->use_multi_threading) {
- WasmImport mem_import = {
- .kind = WASM_FOREIGN_MEMORY,
- .min = 1024,
- .max = 65536, // NOTE: Why not use all 4 Gigs of memory?
- .shared = context.options->runtime == Runtime_Js,
-
- .mod = "onyx",
- .name = "memory",
- };
-
- bh_arr_push(module.imports, mem_import);
- }
-
- WasmExport mem_export = {
- .kind = WASM_FOREIGN_MEMORY,
- .idx = 0,
- };
-
- shput(module.exports, "memory", mem_export);
- module.export_count++;
-
- WasmExport func_table_export = {
- .kind = WASM_FOREIGN_TABLE,
- .idx = 0,
- };
- shput(module.exports, "func_type", func_table_export);
- module.export_count++;
+ bh_arr_new(global_heap_allocator, module.data_patches, 4);
return module;
}
void emit_entity(Entity* ent) {
OnyxWasmModule* module = context.wasm_module;
-
- if (module->stack_top_ptr) {
- *module->stack_top_ptr = module->next_datum_offset;
-
- if (*module->stack_top_ptr % 16 != 0) {
- *module->stack_top_ptr += 16 - (*module->stack_top_ptr % 16);
- }
-
- builtin_heap_start.value.i = *module->stack_top_ptr + (1 << 20);
- if (builtin_heap_start.value.i % 16 != 0) {
- builtin_heap_start.value.i += 16 - (builtin_heap_start.value.i % 16);
- }
- }
+ module->current_func_idx = -1;
switch (ent->type) {
case Entity_Type_Foreign_Function_Header:
ent->state = Entity_State_Finalized;
}
+void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) {
+ // If the pointer size is going to change,
+ // the code will probably need to be altered.
+ static_assert(POINTER_SIZE == 4);
+
+ if (context.options->use_multi_threading) {
+ WasmImport mem_import = {
+ .kind = WASM_FOREIGN_MEMORY,
+ .min = 1024,
+ .max = 65536, // NOTE: Why not use all 4 Gigs of memory?
+ .shared = context.options->runtime == Runtime_Js,
+
+ .mod = "onyx",
+ .name = "memory",
+ };
+
+ bh_arr_push(module->imports, mem_import);
+ }
+
+ WasmExport mem_export = {
+ .kind = WASM_FOREIGN_MEMORY,
+ .idx = 0,
+ };
+
+ shput(module->exports, "memory", mem_export);
+ module->export_count++;
+
+ WasmExport func_table_export = {
+ .kind = WASM_FOREIGN_TABLE,
+ .idx = 0,
+ };
+ shput(module->exports, "func_type", func_table_export);
+ module->export_count++;
+
+ u32 datum_offset = 32; // :LinkOption
+ bh_arr_each(WasmDatum, datum, module->data) {
+ assert(datum->id > 0);
+
+ bh_align(datum_offset, datum->alignment);
+ datum->offset_ = datum_offset;
+
+ // printf("Data ID %d -> %d\n", datum->id, datum->offset);
+
+ datum_offset += datum->length;
+ }
+
+ bh_arr_each(DatumPatchInfo, patch, module->data_patches) {
+ assert(patch->data_id > 0);
+ WasmDatum *datum = &module->data[patch->data_id - 1];
+ assert(datum->id == patch->data_id);
+
+ switch (patch->kind) {
+ case Datum_Patch_Instruction: {
+ WasmFunc *func = &module->funcs[patch->index - module->foreign_function_count];
+ // printf("Patching instruction %d in func[%d] with %d\n", patch->location, patch->index, datum->offset);
+
+ assert(func->code[patch->location].type == WI_PTR_CONST);
+ func->code[patch->location].data.l = (u64) datum->offset_ + patch->offset;
+ break;
+ }
+
+ case Datum_Patch_Data: {
+ WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+ assert(datum_to_alter->id == patch->index);
+ // printf("Patching data %d in data[%d] with %d + %d\n", patch->location, patch->index, target_datum->offset, patch->offset);
+
+ *((u32 *) bh_pointer_add(datum_to_alter->data, patch->location)) = (u32) datum->offset_ + patch->offset;
+ break;
+ }
+
+ case Datum_Patch_Relative: {
+ WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+ assert(datum_to_alter->id == patch->index);
+
+ u32 *addr = (u32 *) bh_pointer_add(datum_to_alter->data, patch->location);
+ if (*addr != 0) {
+ // printf("Patching data %d in data[%d] with %d + %d + %d\n", patch->location, patch->index, target_datum->offset, *addr, patch->offset);
+ *addr += (u32) datum->offset_ + patch->offset;
+ }
+ break;
+ }
+
+ default: assert(0);
+ }
+ }
+
+ assert(module->stack_top_ptr && module->heap_start_ptr);
+
+ *module->stack_top_ptr = datum_offset;
+ bh_align(*module->stack_top_ptr, 16); // :LinkOption
+
+ *module->heap_start_ptr = *module->stack_top_ptr + (1 << 20); // :LinkOption
+ bh_align(*module->heap_start_ptr, 16);
+}
+
void onyx_wasm_module_free(OnyxWasmModule* module) {
if (module->extended_instr_data != NULL)
bh_arena_free(module->extended_instr_data);
}
EMIT_FUNC_NO_ARGS(initialize_data_segments_body) {
+ // :ProperLinking
if (!context.options->use_multi_threading || !context.options->use_post_mvp_features) return;
bh_arr(WasmInstruction) code = *pcode;
+ //
+ // Because this code is generated direction in the function
+ // it is assumed that EVERY data entry will be entered by
+ // this point. If data section entries can be entered after
+ // function body generation starts, this code will have to
+ // move to a link phase thing.
i32 index = 0;
bh_arr_each(WasmDatum, datum, mod->data) {
- WID(WI_PTR_CONST, datum->offset);
+ assert(datum->id > 0);
+ if (datum->data == NULL) { index++; continue; }
+
+ emit_data_relocation(mod, &code, datum->id);
WID(WI_PTR_CONST, 0);
WID(WI_I32_CONST, datum->length);
WID(WI_MEMORY_INIT, ((WasmInstructionData) { index, 0 }));
}
static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
+ // :ProperLinking
if (context.options->use_multi_threading) return 0;
i32 prev_len = buff->length;
// FIXME: This needs to be dynamically chosen depending on the size of
// the data section and stack size pre-requeseted.
- // :WasmMemory
+ // :WasmMemory :ProperLinking
output_limits(1024, -1, 0, &vec_buff);
leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
bh_buffer_append(&vec_buff, leb, leb_len);
bh_arr_each(WasmDatum, datum, module->data) {
- assert(datum->data != NULL);
-
i32 memory_flags = 0x00;
+ // :ProperLinking
if (context.options->use_multi_threading) memory_flags |= 0x01;
bh_buffer_write_byte(&vec_buff, memory_flags);
+ // :ProperLinking
if (!context.options->use_multi_threading) {
bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
- leb = int_to_leb128((i64) datum->offset, &leb_len);
+ leb = int_to_leb128((i64) datum->offset_, &leb_len);
bh_buffer_append(&vec_buff, leb, leb_len);
bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
}
- leb = uint_to_uleb128((u64) datum->length, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
+ if (datum->data != NULL) {
+ leb = uint_to_uleb128((u64) datum->length, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
+ } else {
+ leb = uint_to_uleb128(0, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ }
}
leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
bh_buffer table_buffer;
bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
+ u32 type_table_info_data_id = NEXT_DATA_ID(module);
+
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = module;
+ constexpr_ctx.data_id = type_table_info_data_id;
+
// Write a "NULL" at the beginning so nothing will have to point to the first byte of the buffer.
bh_buffer_write_u64(&table_buffer, 0);
u32 size = type_size_of(sln->value->type);
bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
- if (emit_raw_data_(module, buffer, sln->value)) {
+ constexpr_ctx.data = table_buffer.data;
+ if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
table_buffer.length += size;
break;
}
bh_buffer_align(&table_buffer, type_alignment_of(value->type));
bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
-
- if (!emit_raw_data_(module, buffer, value)) {
+ constexpr_ctx.data = table_buffer.data;
+ if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
// Failed to generate raw data
// onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
value_locations[i++] = 0;
meta_tag_locations[j] = table_buffer.length;
bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
-
- assert(emit_raw_data_(module, buffer, value));
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
table_buffer.length += size;
j += 1;
struct_tag_locations[i] = table_buffer.length;
bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
-
- assert(emit_raw_data_(module, buffer, value));
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
table_buffer.length += size;
i += 1;
tag_locations[i] = table_buffer.length;
bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
- assert(emit_raw_data_(module, buffer, value));
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
table_buffer.length += size;
i += 1;
bh_printf("Type table size: %d bytes.\n", table_buffer.length);
}
- u32 offset = module->next_datum_offset;
- bh_align(offset, 8);
+ WasmDatum type_info_data = {
+ .alignment = 8,
+ .length = table_buffer.length,
+ .data = table_buffer.data,
+ };
+ emit_data_entry(module, &type_info_data);
+ assert(type_info_data.id == type_table_info_data_id);
- u64 type_table_location = offset;
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = type_info_data.id;
+ patch.offset = 0;
+ patch.index = type_info_data.id;
+ patch.location = *patch_loc;
+ bh_arr_push(module->data_patches, patch);
+ }
WasmDatum type_table_data = {
- .offset = offset,
+ .alignment = POINTER_SIZE,
.length = type_count * POINTER_SIZE,
.data = table_info,
};
- bh_arr_push(module->data, type_table_data);
-
- offset += type_table_data.length;
+ emit_data_entry(module, &type_table_data);
fori (i, 0, type_count) {
- table_info[i] += offset;
- }
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- if (POINTER_SIZE == 4) {
- u32* loc = bh_pointer_add(table_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
- if (POINTER_SIZE == 8) {
- u64* loc = bh_pointer_add(table_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = type_info_data.id;
+ patch.offset = table_info[i];
+ patch.index = type_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
}
- WasmDatum type_info_data = {
- .offset = offset,
- .length = table_buffer.length,
- .data = table_buffer.data,
- };
- bh_arr_push(module->data, type_info_data);
- offset += type_info_data.length;
-
- u64 global_data_ptr = offset;
-
Table_Info_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = type_table_location;
+ tmp_data[0] = 0;
tmp_data[1] = type_count;
WasmDatum type_table_global_data = {
- .offset = offset,
+ .alignment = POINTER_SIZE,
.length = 2 * POINTER_SIZE,
.data = tmp_data,
};
- bh_arr_push(module->data, type_table_global_data);
- offset += type_table_global_data.length;
-
- module->next_datum_offset = offset;
+ emit_data_entry(module, &type_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = type_table_data.id;
+ patch.offset = 0;
+ patch.index = type_table_global_data.id;
+ patch.location = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
- return global_data_ptr;
+ return type_table_global_data.id;
#undef WRITE_SLICE
#undef WRITE_PTR
bh_printf("Foreign blocks size: %d bytes.\n", foreign_buffer.length);
}
- u32 offset = module->next_datum_offset;
- bh_align(offset, 8);
+ WasmDatum foreign_info_data = {
+ .alignment = 8,
+ .length = foreign_buffer.length,
+ .data = foreign_buffer.data,
+ };
+ emit_data_entry(module, &foreign_info_data);
- u64 foreign_blocks_location = offset;
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = foreign_info_data.id;
+ patch.offset = 0;
+ patch.index = foreign_info_data.id;
+ patch.location = *patch_loc;
+ bh_arr_push(module->data_patches, patch);
+ }
- WasmDatum foreign_blocks_data = {
- .offset = offset,
+ WasmDatum foreign_table_data = {
+ .alignment = POINTER_SIZE,
.length = block_count * POINTER_SIZE,
.data = foreign_info,
};
- bh_arr_push(module->data, foreign_blocks_data);
-
- offset += foreign_blocks_data.length;
+ emit_data_entry(module, &foreign_table_data);
fori (i, 0, block_count) {
- foreign_info[i] += offset;
- }
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- if (POINTER_SIZE == 4) {
- u32* loc = bh_pointer_add(foreign_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
- if (POINTER_SIZE == 8) {
- u64* loc = bh_pointer_add(foreign_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = foreign_info_data.id;
+ patch.offset = foreign_info[i];
+ patch.index = foreign_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
}
- WasmDatum foreign_blocks_info_data = {
- .offset = offset,
- .length = foreign_buffer.length,
- .data = foreign_buffer.data,
- };
- bh_arr_push(module->data, foreign_blocks_info_data);
- offset += foreign_blocks_info_data.length;
-
- u64 global_data_ptr = offset;
-
Foreign_Block_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = foreign_blocks_location;
+ tmp_data[0] = 0;
tmp_data[1] = block_count;
- WasmDatum foreign_blocks_global_data = {
- .offset = offset,
+ WasmDatum foreign_table_global_data = {
+ .alignment = POINTER_SIZE,
.length = 2 * POINTER_SIZE,
.data = tmp_data,
};
- bh_arr_push(module->data, foreign_blocks_global_data);
- offset += foreign_blocks_global_data.length;
-
- module->next_datum_offset = offset;
+ emit_data_entry(module, &foreign_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = foreign_table_data.id;
+ patch.location = 0;
+ patch.index = foreign_table_global_data.id;
+ patch.offset = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
- return global_data_ptr;
+ return foreign_table_global_data.id;
#undef WRITE_SLICE
#undef WRITE_PTR
bh_buffer tag_proc_buffer;
bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
+ u32 proc_info_data_id = NEXT_DATA_ID(module);
+
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = module;
+ constexpr_ctx.data_id = proc_info_data_id;
+
//
// This is necessary because 0 is an invalid offset to store in this
// buffer, as 0 will map to NULL. This could be a single byte insertion,
u32 size = type_size_of(tag->type);
bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
- u8* buffer = tag_proc_buffer.data + tag_proc_buffer.length;
- emit_raw_data(module, buffer, tag);
+
+ constexpr_ctx.data = tag_proc_buffer.data;
+ emit_constexpr(&constexpr_ctx, tag, tag_proc_buffer.length);
tag_proc_buffer.length += size;
}
bh_printf("Tagged procedure size: %d bytes.\n", tag_proc_buffer.length);
}
- u32 offset = module->next_datum_offset;
- bh_align(offset, 8);
+ WasmDatum proc_info_data = {
+ .alignment = 8,
+ .length = tag_proc_buffer.length,
+ .data = tag_proc_buffer.data,
+ };
+ emit_data_entry(module, &proc_info_data);
+ assert(proc_info_data.id == proc_info_data_id);
- u64 tagged_procedures_location = offset;
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = proc_info_data.id;
+ patch.location = *patch_loc;
+ patch.index = proc_info_data.id;
+ patch.offset = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
- WasmDatum tagged_procedures_data = {
- .offset = offset,
+ WasmDatum proc_table_data = {
+ .alignment = POINTER_SIZE,
.length = proc_count * POINTER_SIZE,
.data = tag_proc_info,
};
- bh_arr_push(module->data, tagged_procedures_data);
-
- offset += tagged_procedures_data.length;
+ emit_data_entry(module, &proc_table_data);
fori (i, 0, proc_count) {
- tag_proc_info[i] += offset;
- }
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- if (POINTER_SIZE == 4) {
- u32* loc = bh_pointer_add(tag_proc_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
- if (POINTER_SIZE == 8) {
- u64* loc = bh_pointer_add(tag_proc_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = proc_info_data.id;
+ patch.offset = tag_proc_info[i];
+ patch.index = proc_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
}
- WasmDatum tagged_procedures_info_data = {
- .offset = offset,
- .length = tag_proc_buffer.length,
- .data = tag_proc_buffer.data,
- };
- bh_arr_push(module->data, tagged_procedures_info_data);
- offset += tagged_procedures_info_data.length;
-
- u64 global_data_ptr = offset;
-
Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = tagged_procedures_location;
+ tmp_data[0] = 0;
tmp_data[1] = proc_count;
- WasmDatum tagged_procedures_global_data = {
- .offset = offset,
+ WasmDatum proc_table_global_data = {
+ .alignment = POINTER_SIZE,
.length = 2 * POINTER_SIZE,
.data = tmp_data,
};
- bh_arr_push(module->data, tagged_procedures_global_data);
- offset += tagged_procedures_global_data.length;
-
- module->next_datum_offset = offset;
+ emit_data_entry(module, &proc_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.offset = 0;
+ patch.data_id = proc_table_data.id;
+ patch.index = proc_table_global_data.id;
+ patch.location = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
- return global_data_ptr;
+ return proc_table_global_data.id;
#undef WRITE_SLICE
#undef WRITE_PTR