Generating an actual WASM binary!
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 15 Jun 2020 03:38:36 +0000 (22:38 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 15 Jun 2020 03:38:36 +0000 (22:38 -0500)
No code section right now however; still lots of work to do.

include/bh.h
include/onyxwasm.h
onyx
progs/minimal.onyx
src/onyx.c
src/onyxwasm.c

index a0a33c908978a028be6e60024e6edbbbc993fcb0..9d6ea6d073e786dd21151f59fba4484b2411cc65 100644 (file)
@@ -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;
 
index 17f35599b359e0d5cbb2afa0b704a07c99799e19..4f7cf685fe2c3dd2414d572739903dcb1f18715c 100644 (file)
@@ -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 ad0674531f06f9a329c80daa897e65112079d6a3..eb95d4a86bb1df2067243c148fd329359f830082 100755 (executable)
Binary files a/onyx and b/onyx differ
index 215f6fdfdfbcb9e723825ad62dd5dc48f64516f4..1f1f75f5141c0c008369370dd81c608131841358 100644 (file)
@@ -18,3 +18,5 @@ export diff_square :: proc (a i32, b i32) -> i64 {
 
        return (c * d) as i64;
 }
+
+export dummy :: proc () -> void { }
index 5e4ec5b79c3017a4853364210187b103247fe28e..2c5e9d2b9c11201bcd054ca7f00ec5b25e69693f 100644 (file)
@@ -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");
index ccf87dffb0ec594c1d48cba37adaea6fd37f9b0f..b719acbe76e740de56a131e728680a1ddc14601f 100644 (file)
@@ -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);