From: Brendan Hansen Date: Mon, 15 Jun 2020 03:38:36 +0000 (-0500) Subject: Generating an actual WASM binary! X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fdae17606251ceccc7369b93be1a3a8c1cfc4cbd;p=onyx.git Generating an actual WASM binary! No code section right now however; still lots of work to do. --- diff --git a/include/bh.h b/include/bh.h index a0a33c90..9d6ea6d0 100644 --- a/include/bh.h +++ b/include/bh.h @@ -26,6 +26,8 @@ typedef int64_t i64; typedef int64_t isize; typedef i32 b32; typedef void* ptr; +typedef float f32; +typedef double f64; @@ -110,7 +112,8 @@ inline i64 chars_match(char* ptr1, char* ptr2) { // Converts an unsigned integer to the unsigned LEB128 format u8* uint_to_uleb128(u64 n, i32* output_length); u8* int_to_leb128(i64 n, i32* output_length); - +u8* float_to_ieee754(f32 f); +u8* double_to_ieee754(f64 f); @@ -436,6 +439,7 @@ typedef struct bh_buffer { #endif void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length); +void bh_buffer_free(bh_buffer* buffer); void bh_buffer_grow(bh_buffer* buffer, i32 length); void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length); void bh_buffer_concat(bh_buffer* buffer, bh_buffer other); @@ -924,6 +928,42 @@ u8* int_to_leb128(i64 n, i32* output_length) { return buffer; } +// NOTE: This assumes the underlying implementation of float on the host +// system is already IEEE-754. This is safe to assume in most cases. +u8* float_to_ieee754(f32 f) { + static u8 buffer[4]; + + u8* fmem = (u8*) &f; + buffer[0] = fmem[3]; + buffer[1] = fmem[2]; + buffer[2] = fmem[1]; + buffer[3] = fmem[0]; + + return buffer; +} + +u8* double_to_ieee754(f64 f) { + static u8 buffer[8]; + + u8* fmem = (u8*) &f; + buffer[0] = fmem[7]; + buffer[1] = fmem[6]; + buffer[2] = fmem[5]; + buffer[3] = fmem[4]; + buffer[4] = fmem[3]; + buffer[5] = fmem[2]; + buffer[6] = fmem[1]; + buffer[7] = fmem[0]; + + return buffer; +} + + + + + + + @@ -1395,6 +1435,12 @@ void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) { buffer->data = bh_alloc(alloc, init_size); } +void bh_buffer_free(bh_buffer* buffer) { + bh_free(buffer->allocator, buffer->data); + buffer->length = 0; + buffer->capacity = 0; +} + void bh_buffer_grow(bh_buffer* buffer, i32 length) { if (buffer == NULL) return; diff --git a/include/onyxwasm.h b/include/onyxwasm.h index 17f35599..4f7cf685 100644 --- a/include/onyxwasm.h +++ b/include/onyxwasm.h @@ -237,17 +237,24 @@ typedef struct WasmInstruction { WasmInstructionData data; } WasmInstruction; +typedef struct WasmFuncLocals { + u8 i32_count; + u8 i64_count; + u8 f32_count; + u8 f64_count; +} WasmFuncLocals; typedef struct WasmFunc { i32 type_idx; + WasmFuncLocals locals; bh_arr(WasmInstruction) code; } WasmFunc; typedef enum WasmExportKind { - WASM_EXPORT_FUNCTION, - WASM_EXPORT_TABLE, - WASM_EXPORT_MEMORY, - WASM_EXPORT_GLOBAL, + WASM_EXPORT_FUNCTION = 0x00, + WASM_EXPORT_TABLE = 0x01, + WASM_EXPORT_MEMORY = 0x02, + WASM_EXPORT_GLOBAL = 0x03, } WasmExportKind; typedef struct WasmExport { @@ -273,6 +280,7 @@ typedef struct OnyxWasmModule { i32 next_func_idx; bh_hash(WasmExport) exports; + i32 export_count; } OnyxWasmModule; OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* program); diff --git a/onyx b/onyx index ad067453..eb95d4a8 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/minimal.onyx b/progs/minimal.onyx index 215f6fdf..1f1f75f5 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -18,3 +18,5 @@ export diff_square :: proc (a i32, b i32) -> i64 { return (c * d) as i64; } + +export dummy :: proc () -> void { } diff --git a/src/onyx.c b/src/onyx.c index 5e4ec5b7..2c5e9d2b 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { OnyxWasmModule wasm_mod = onyx_wasm_generate_module(alloc, program); -#if 0 +#if 1 // NOTE: Ensure type table made correctly bh_printf("Type map:\n"); @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) { } #endif -#if 0 +#if 1 // NOTE: Ensure the export table was built correctly bh_printf("Function types:\n"); diff --git a/src/onyxwasm.c b/src/onyxwasm.c index ccf87dff..b719acbe 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -3,16 +3,18 @@ // NOTE: Allows easier testing of types since most of the characters // corresponding to these values are not printable -#if 0 +#if 1 const WasmType WASM_TYPE_INT32 = 0x7F; const WasmType WASM_TYPE_INT64 = 0x7E; const WasmType WASM_TYPE_FLOAT32 = 0x7D; const WasmType WASM_TYPE_FLOAT64 = 0x7C; +const WasmType WASM_TYPE_VOID = 0x00; #else const WasmType WASM_TYPE_INT32 = 'A'; const WasmType WASM_TYPE_INT64 = 'B'; const WasmType WASM_TYPE_FLOAT32 = 'C'; const WasmType WASM_TYPE_FLOAT64 = 'D'; +const WasmType WASM_TYPE_VOID = '\0'; #endif static const char* wi_string(WasmInstructionType wit) { @@ -204,7 +206,7 @@ static WasmType onyx_type_to_wasm_type(OnyxTypeInfo* type) { } // TODO: Should produce an error message if this isn't successful - return WASM_TYPE_INT32; + return WASM_TYPE_VOID; } static void process_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeFuncDef* fd); @@ -222,6 +224,8 @@ static void process_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNo forll (OnyxAstNode, stmt, fd->body->body, next) { process_statement(mod, func, stmt); } + + bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_END, 0x00 })); } static void process_block(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeBlock* block) { @@ -265,7 +269,9 @@ static void process_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* { \ WasmInstructionType instr_type; \ switch (expr->type->kind) { \ + case ONYX_TYPE_INFO_KIND_UINT32: \ case ONYX_TYPE_INFO_KIND_INT32: instr_type = WI_I32_##wasm_binop; break; \ + case ONYX_TYPE_INFO_KIND_UINT64: \ case ONYX_TYPE_INFO_KIND_INT64: instr_type = WI_I64_##wasm_binop; break; \ case ONYX_TYPE_INFO_KIND_FLOAT32: instr_type = WI_F32_##wasm_binop; break; \ case ONYX_TYPE_INFO_KIND_FLOAT64: instr_type = WI_F64_##wasm_binop; break; \ @@ -288,10 +294,12 @@ static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* { WasmInstructionType instr_type; switch (expr->type->kind) { + case ONYX_TYPE_INFO_KIND_UINT32: case ONYX_TYPE_INFO_KIND_INT32: if (expr->type->is_unsigned) instr_type = WI_I32_DIV_U; else instr_type = WI_I32_DIV_S; break; + case ONYX_TYPE_INFO_KIND_UINT64: case ONYX_TYPE_INFO_KIND_INT64: if (expr->type->is_unsigned) instr_type = WI_I64_DIV_U; else instr_type = WI_I64_DIV_S; @@ -463,6 +471,7 @@ static void process_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef* .idx = func_idx, }; bh_hash_put(WasmExport, mod->exports, fd->token->token, wasm_export); + mod->export_count++; onyx_token_null_toggle(*fd->token); } @@ -513,6 +522,7 @@ OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* progra .next_func_idx = 0, .exports = NULL, + .export_count = 0, }; bh_arr_new(alloc, module.functypes, 4); @@ -545,19 +555,180 @@ void onyx_wasm_module_free(OnyxWasmModule* module) { bh_hash_free(module->exports); } + + + +//------------------------------------------------- +// BINARY OUPUT +//------------------------------------------------- + +#define WASM_SECTION_ID_TYPE 1 +#define WASM_SECTION_ID_IMPORT 2 +#define WASM_SECTION_ID_FUNCTION 3 +#define WASM_SECTION_ID_TABLE 4 +#define WASM_SECTION_ID_MEMORY 5 +#define WASM_SECTION_ID_GLOBAL 6 +#define WASM_SECTION_ID_EXPORT 7 +#define WASM_SECTION_ID_START 8 +#define WASM_SECTION_ID_ELEMENT 9 +#define WASM_SECTION_ID_CODE 10 +#define WASM_SECTION_ID_DATA 11 + +typedef i32 vector_func(void*, bh_buffer*); + static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D }; static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 }; +static i32 output_vector(void** arr, i32 stride, i32 arrlen, vector_func elem, bh_buffer* vec_buff) { + i32 len; + u8* leb = uint_to_uleb128((u64) arrlen, &len); + bh_buffer_append(vec_buff, leb, len); + + i32 i = 0; + while (i < arrlen) { + elem(*arr, vec_buff); + arr = bh_pointer_add(arr, stride); + i++; + } + + return vec_buff->length; +} + +static i32 output_functype(WasmFuncType* type, bh_buffer* buff) { + i32 prev_len = buff->length; + + bh_buffer_write_byte(buff, 0x60); + + i32 len; + u8* leb_buff = uint_to_uleb128(type->param_count, &len); + bh_buffer_append(buff, leb_buff, len); + bh_buffer_append(buff, type->param_types, type->param_count); + + if (type->return_type != WASM_TYPE_VOID) { + bh_buffer_write_byte(buff, 0x01); + bh_buffer_write_byte(buff, type->return_type); + } else { + bh_buffer_write_byte(buff, 0x00); + } + + return buff->length - prev_len; +} + +static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + bh_buffer_write_byte(buff, 0x01); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + i32 vec_len = output_vector( + (void**) module->functypes, + sizeof(WasmFuncType*), + bh_arr_length(module->functypes), + (vector_func *) output_functype, + &vec_buff); + + i32 leb_len; + u8* leb = uint_to_uleb128((u64) vec_len, &leb_len); + bh_buffer_append(buff, leb, leb_len); + + bh_buffer_concat(buff, vec_buff); + bh_buffer_free(&vec_buff); + + return buff->length - prev_len; +} + +static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + bh_buffer_write_byte(buff, WASM_SECTION_ID_FUNCTION); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + i32 leb_len; + u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->funcs)), &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + + bh_arr_each(WasmFunc, func, module->funcs) { + leb = uint_to_uleb128((u64) (func->type_idx), &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + } + + leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len); + bh_buffer_append(buff, leb, leb_len); + + bh_buffer_concat(buff, vec_buff); + bh_buffer_free(&vec_buff); + + return buff->length - prev_len; +} + +static i32 output_exportsection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + bh_buffer_write_byte(buff, WASM_SECTION_ID_EXPORT); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + i32 leb_len; + u8* leb = uint_to_uleb128((u64) (module->export_count), &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + + i32 key_len = 0; + bh_hash_each_start(WasmExport, module->exports); + key_len = strlen(key); + leb = uint_to_uleb128((u64) key_len, &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + bh_buffer_append(&vec_buff, key, key_len); + + bh_buffer_write_byte(&vec_buff, (u8) (value.kind)); + leb = uint_to_uleb128((u64) value.idx, &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + bh_hash_each_end; + + leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len); + bh_buffer_append(buff, leb, leb_len); + + bh_buffer_concat(buff, vec_buff); + bh_buffer_free(&vec_buff); + + return buff->length - prev_len; +} + +static i32 output_codesection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + + bh_buffer_write_byte(buff, WASM_SECTION_ID_CODE); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + i32 leb_len; + u8* leb = uint_to_uleb128((u64) bh_arr_length(module->funcs), &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + + bh_arr_each(WasmFunc, func, module->funcs) { + } + + leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len); + bh_buffer_append(buff, leb, leb_len); + + bh_buffer_concat(buff, vec_buff); + bh_buffer_free(&vec_buff); + + return buff->length - prev_len; +} + void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) { bh_buffer master_buffer; bh_buffer_init(&master_buffer, bh_heap_allocator(), 128); bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4); bh_buffer_append(&master_buffer, WASM_VERSION, 4); - - - - + output_typesection(module, &master_buffer); + output_funcsection(module, &master_buffer); + output_exportsection(module, &master_buffer); + output_codesection(module, &master_buffer); bh_file_write(&file, master_buffer.data, master_buffer.length);