From: Brendan Hansen Date: Mon, 27 Jul 2020 20:29:26 +0000 (-0500) Subject: Add indirect function calls (function pointers) X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=40fa4802b4290291843cf397ae4e3e85e96067f4;p=onyx.git Add indirect function calls (function pointers) --- diff --git a/.vimspector.json b/.vimspector.json index 8a5ecd89..d4b26ba9 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -6,7 +6,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/onyx", - "args": ["progs/ufc.onyx"], + "args": ["progs/fcf.onyx"], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 1238eed7..72fb15eb 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -137,7 +137,7 @@ typedef enum AstFlags { Ast_Flag_Expr_Ignored = BH_BIT(8), Ast_Flag_Param_Splatted = BH_BIT(9), Ast_Flag_Param_Use = BH_BIT(10), - + // Type flags Ast_Flag_Type_Is_Resolved = BH_BIT(8), @@ -230,7 +230,7 @@ typedef enum OnyxIntrinsic { #define AstNode_base struct AstNode_members; struct AstNode AstNode_members; -// NOTE: 'type_node' is filled out by the parser. +// NOTE: 'type_node' is filled out by the parser. // For a type such as '^^i32', the tree would look something like // // Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node) @@ -255,7 +255,7 @@ 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; }; struct AstLocal { AstTyped_base; AstLocal *prev_local; }; -struct AstCall { AstTyped_base; AstArgument *arguments; u64 arg_count; AstNode *callee; }; +struct AstCall { AstTyped_base; AstArgument *arguments; u64 arg_count; AstTyped *callee; }; struct AstIntrinsicCall { AstTyped_base; AstArgument *arguments; u64 arg_count; OnyxIntrinsic intrinsic; }; struct AstArgument { AstTyped_base; AstTyped *value; }; struct AstAddressOf { AstTyped_base; AstTyped *expr; }; diff --git a/include/onyxwasm.h b/include/onyxwasm.h index 4a5dfcb7..72a5493e 100644 --- a/include/onyxwasm.h +++ b/include/onyxwasm.h @@ -311,6 +311,7 @@ typedef struct OnyxWasmModule { u32 next_type_idx; u32 export_count; + u32 element_count; u32 next_func_idx; u32 next_foreign_func_idx; u32 next_global_idx; diff --git a/onyx b/onyx index cbe7e0ba..c90b1111 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/fcf.onyx b/progs/fcf.onyx new file mode 100644 index 00000000..f1303890 --- /dev/null +++ b/progs/fcf.onyx @@ -0,0 +1,44 @@ +use "progs/print_funcs" +use "progs/intrinsics" + +use package printing + +call_me :: proc (f: proc (i32), val: i32) { + f(val); +} + +funcs : [5] proc (i32, i32) -> i32 + +add :: proc (a: i32, b: i32) -> i32 { return a + b; } +sub :: proc (a: i32, b: i32) -> i32 { return a - b; } +mul :: proc (a: i32, b: i32) -> i32 { return a * b; } +div :: proc (a: i32, b: i32) -> i32 { return a / b; } +mod :: proc (a: i32, b: i32) -> i32 { return a % b; } + +DeferredCall :: struct { + func : proc (i32, i32) -> i32; + left : i32; + right : i32; +} + +execute :: proc (use dc: ^DeferredCall) -> i32 { + return func(left, right); +} + +proc #export "main" { + call_me(print_i32, 10); + + funcs[0] = add; + funcs[1] = sub; + funcs[3] = div; + funcs[4] = mod; + + print(funcs[0](10, 3)); + + dc := __heap_start as ^DeferredCall; + dc.func = mod; + dc.left = 40; + dc.right = 19; + + print(execute(dc)); +} diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 232e4984..5d5c7c10 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -165,6 +165,8 @@ CHECK(call, AstCall* call) { return 1; } + if (check_expression(&call->callee)) return 1; + // NOTE: Check arguments and splat structs AstNode** prev_param = (AstNode **) &call->arguments; AstArgument* actual_param = call->arguments; @@ -212,7 +214,7 @@ CHECK(call, AstCall* call) { } if (callee->kind == Ast_Kind_Overloaded_Function) { - call->callee = (AstNode *) match_overloaded_function(call, (AstOverloadedFunction *) callee); + call->callee = match_overloaded_function(call, (AstOverloadedFunction *) callee); callee = (AstFunction *) call->callee; if (callee == NULL) return 1; @@ -293,36 +295,34 @@ CHECK(call, AstCall* call) { call->type = callee->type->Function.return_type; - AstLocal* formal_param = callee->params; + Type** formal_param = &callee->type->Function.params[0]; actual_param = call->arguments; i32 arg_pos = 0; while (formal_param != NULL && actual_param != NULL) { - fill_in_type((AstTyped *) formal_param); - - if (!types_are_compatible(formal_param->type, actual_param->type)) { + if (!types_are_compatible(*formal_param, actual_param->type)) { onyx_message_add(Msg_Type_Function_Param_Mismatch, actual_param->token->pos, callee->token->text, callee->token->length, - type_get_name(formal_param->type), + type_get_name(*formal_param), arg_pos, type_get_name(actual_param->type)); return 1; } arg_pos++; - formal_param = (AstLocal *) formal_param->next; + formal_param++; actual_param = (AstArgument *) actual_param->next; } - if (formal_param != NULL && actual_param == NULL) { + if (arg_pos < callee->type->Function.param_count) { onyx_message_add(Msg_Type_Literal, call->token->pos, "too few arguments to function call"); return 1; } - if (formal_param == NULL && actual_param != NULL) { + if (arg_pos > callee->type->Function.param_count) { onyx_message_add(Msg_Type_Literal, call->token->pos, "too many arguments to function call"); @@ -652,8 +652,11 @@ CHECK(expression, AstTyped** pexpr) { assert(expr->type != NULL); break; + case Ast_Kind_Function: + expr->flags |= Ast_Flag_Function_Used; + break; + case Ast_Kind_StrLit: break; - case Ast_Kind_Function: break; case Ast_Kind_Overloaded_Function: break; case Ast_Kind_Enum_Value: break; @@ -919,7 +922,7 @@ void onyx_type_check() { case Entity_Type_Function_Header: if (entity->function->flags & Ast_Flag_Foreign) semstate.program->foreign_func_count++; - + if (check_function_header(entity->function)) return; break; diff --git a/src/onyxparser.c b/src/onyxparser.c index 79958c57..7555e237 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -302,7 +302,7 @@ static AstTyped* parse_factor(OnyxParser* parser) { case '(': { AstCall* call_node = make_node(AstCall, Ast_Kind_Call); call_node->token = expect_token(parser, '('); - call_node->callee = (AstNode *) retval; + call_node->callee = retval; call_node->arg_count = 0; AstArgument** prev = &call_node->arguments; @@ -807,6 +807,42 @@ static AstType* parse_type(OnyxParser* parser) { next_insertion = &new->elem; } + else if (parser->curr->type == Token_Type_Keyword_Proc) { + OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc); + + bh_arr(AstType *) params = NULL; + bh_arr_new(global_scratch_allocator, params, 4); + + expect_token(parser, '('); + while (parser->curr->type != ')') { + AstType* param_type = parse_type(parser); + bh_arr_push(params, param_type); + + if (parser->curr->type != ')') + expect_token(parser, ','); + } + consume_token(parser); + + AstType* return_type = (AstType *) &basic_type_void; + if (parser->curr->type == Token_Type_Right_Arrow) { + consume_token(parser); + return_type = parse_type(parser); + } + + u64 param_count = bh_arr_length(params); + AstFunctionType* new = onyx_ast_node_new(parser->allocator, + sizeof(AstFunctionType) + sizeof(AstType) * param_count, + Ast_Kind_Function_Type); + new->token = proc_token; + new->param_count = param_count; + new->return_type = return_type; + + fori (i, 0, param_count - 1) new->params[i] = params[i]; + + *next_insertion = (AstType *) new; + next_insertion = NULL; + } + else if (parser->curr->type == Token_Type_Symbol) { AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol); symbol_node->token = expect_token(parser, Token_Type_Symbol); @@ -1124,7 +1160,7 @@ static AstEnumType* parse_enum_declaration(OnyxParser* parser) { if (parser->curr->type == ':') { consume_token(parser); expect_token(parser, ':'); - + evalue->value = parse_numeric_literal(parser); } @@ -1213,7 +1249,7 @@ static AstNode* parse_top_level_statement(OnyxParser* parser) { add_node_to_process(parser, (AstNode *) upack); return NULL; - + } else { AstIncludeFile* include = make_node(AstIncludeFile, Ast_Kind_Include_File); include->token = use_token; diff --git a/src/onyxsymres.c b/src/onyxsymres.c index f3fe21bf..1d286f98 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -85,7 +85,7 @@ static AstType* symres_type(AstType* type) { if (type->kind == Ast_Kind_Field_Access) { AstFieldAccess* field = (AstFieldAccess *) type; symres_field_access(&field); - + if (!node_is_type((AstNode *) field)) { onyx_message_add(Msg_Type_Literal, type->token->pos, @@ -154,19 +154,23 @@ static void symres_call(AstCall* call) { symres_expression((AstTyped **) &call->callee); if (call->callee == NULL) return; - if (call->callee->kind == Ast_Kind_Field_Access) { - AstFieldAccess* fa = (AstFieldAccess *) call->callee; - if (fa->expr == NULL) return; + // NOTE: Disabling UFC now. Causes too many problems. + // Especially since structs can have function pointers as members, + // so x.y(...) is ambiguous. + // + // if (call->callee->kind == Ast_Kind_Field_Access) { + // AstFieldAccess* fa = (AstFieldAccess *) call->callee; + // if (fa->expr == NULL) return; - AstArgument* implicit_arg = onyx_ast_node_new(semstate.node_allocator, sizeof(AstArgument), Ast_Kind_Argument); - implicit_arg->value = fa->expr; - implicit_arg->token = fa->expr->token; - implicit_arg->next = (AstNode *) call->arguments; + // AstArgument* implicit_arg = onyx_ast_node_new(semstate.node_allocator, sizeof(AstArgument), Ast_Kind_Argument); + // implicit_arg->value = fa->expr; + // implicit_arg->token = fa->expr->token; + // implicit_arg->next = (AstNode *) call->arguments; - call->callee = symbol_resolve(semstate.curr_scope, fa->token); - call->arguments = implicit_arg; - call->arg_count++; - } + // call->callee = (AstTyped *) symbol_resolve(semstate.curr_scope, fa->token); + // call->arguments = implicit_arg; + // call->arg_count++; + // } symres_statement_chain((AstNode *) call->arguments, (AstNode **) &call->arguments); } @@ -217,39 +221,35 @@ static void symres_unaryop(AstUnaryOp** unaryop) { static void symres_expression(AstTyped** expr) { switch ((*expr)->kind) { - case Ast_Kind_Binary_Op: - symres_expression(&((AstBinaryOp *)(*expr))->left); - symres_expression(&((AstBinaryOp *)(*expr))->right); - break; - - case Ast_Kind_Unary_Op: symres_unaryop((AstUnaryOp **) expr); break; - case Ast_Kind_Call: symres_call((AstCall *) *expr); break; - case Ast_Kind_Block: symres_block((AstBlock *) *expr); break; - case Ast_Kind_Symbol: *expr = (AstTyped *) symbol_resolve(semstate.curr_scope, ((AstNode *) *expr)->token); break; - - case Ast_Kind_Function: - case Ast_Kind_NumLit: - case Ast_Kind_StrLit: - (*expr)->type_node = symres_type((*expr)->type_node); + case Ast_Kind_Binary_Op: + symres_expression(&((AstBinaryOp *)(*expr))->left); + symres_expression(&((AstBinaryOp *)(*expr))->right); break; + case Ast_Kind_Unary_Op: symres_unaryop((AstUnaryOp **) expr); break; + case Ast_Kind_Call: symres_call((AstCall *) *expr); break; + case Ast_Kind_Block: symres_block((AstBlock *) *expr); break; case Ast_Kind_Address_Of: symres_expression(&((AstAddressOf *)(*expr))->expr); break; case Ast_Kind_Dereference: symres_expression(&((AstDereference *)(*expr))->expr); break; case Ast_Kind_Field_Access: symres_field_access((AstFieldAccess **) expr); break; case Ast_Kind_Size_Of: symres_size_of((AstSizeOf *)*expr); break; case Ast_Kind_Align_Of: symres_align_of((AstAlignOf *)*expr); break; + case Ast_Kind_Function: + case Ast_Kind_NumLit: + case Ast_Kind_StrLit: + (*expr)->type_node = symres_type((*expr)->type_node); + break; + case Ast_Kind_Array_Access: symres_expression(&((AstArrayAccess *)(*expr))->addr); symres_expression(&((AstArrayAccess *)(*expr))->expr); break; - // NOTE: This is a good case, since it means the symbol is already resolved - case Ast_Kind_Local: default: break; } } @@ -347,12 +347,12 @@ static void symres_function(AstFunction* func) { for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->next) { param->type_node = symres_type(param->type_node); + param->type = type_build_from_ast(semstate.allocator, param->type_node); symbol_introduce(semstate.curr_scope, param->token, (AstNode *) param); if (param->flags & Ast_Flag_Param_Use) { - if (param->type_node->kind != Ast_Kind_Pointer_Type - || ((AstPointerType *) param->type_node)->elem->kind != Ast_Kind_Struct_Type) { + if (param->type->kind != Type_Kind_Pointer || param->type->Pointer.elem->kind != Type_Kind_Struct) { onyx_message_add(Msg_Type_Literal, param->token->pos, "can only 'use' pointers to structures."); @@ -474,7 +474,7 @@ static void symres_enum(AstEnumType* enum_node) { if (enum_node->flags & Ast_Flag_Enum_Is_Flags) { next_assign_value <<= 1; - } else { + } else { next_assign_value++; } } diff --git a/src/onyxtypes.c b/src/onyxtypes.c index 85664e2c..71cab661 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -1,3 +1,4 @@ +#define BH_DEBUG #include "onyxtypes.h" #include "onyxastnodes.h" #include "onyxutils.h" @@ -144,8 +145,20 @@ b32 types_are_compatible(Type* t1, Type* t2) { } case Type_Kind_Enum: { - if (t2->kind != Type_Kind_Enum) return 0; - return t1 == t2; + return 0; + // if (t2->kind != Type_Kind_Enum) return 0; + // return t1 == t2; + } + + case Type_Kind_Function: { + if (t2->kind != Type_Kind_Function) return 0; + if (t1->Function.param_count != t2->Function.param_count) return 0; + + fori (i, 0, t1->Function.param_count - 1) { + if (!types_are_compatible(t1->Function.params[i], t2->Function.params[i])) return 0; + } + + return 1; } default: @@ -162,7 +175,7 @@ u32 type_size_of(Type* type) { switch (type->kind) { case Type_Kind_Basic: return type->Basic.size; case Type_Kind_Pointer: return 4; - case Type_Kind_Function: return 0; + case Type_Kind_Function: return 4; case Type_Kind_Array: return type->Array.size; case Type_Kind_Struct: return type->Struct.size; case Type_Kind_Enum: return type_size_of(type->Enum.backing); @@ -176,7 +189,7 @@ u32 type_alignment_of(Type* type) { switch (type->kind) { case Type_Kind_Basic: return type->Basic.alignment; case Type_Kind_Pointer: return 4; - case Type_Kind_Function: return 1; + case Type_Kind_Function: return 4; case Type_Kind_Array: return type_alignment_of(type->Array.elem); case Type_Kind_Struct: return type->Struct.aligment; case Type_Kind_Enum: return type_alignment_of(type->Enum.backing); @@ -280,7 +293,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { } s_type->Struct.aligment = alignment; - + if (offset % alignment != 0) { offset += alignment - (offset % alignment); } @@ -371,6 +384,9 @@ const char* type_get_name(Type* type) { return type->Enum.name; else return ""; + + case Type_Kind_Function: return bh_aprintf(global_scratch_allocator, "proc (...) -> %s", type_get_name(type->Function.return_type)); + default: return "unknown"; } } diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 409fb1d0..966b9ca8 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -217,6 +217,10 @@ static WasmType onyx_type_to_wasm_type(Type* type) { return WASM_TYPE_INT32; } + if (type->kind == Type_Kind_Function) { + return WASM_TYPE_INT32; + } + if (type->kind == Type_Kind_Basic) { TypeBasic* basic = &type->Basic; if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32; @@ -235,6 +239,8 @@ static WasmType onyx_type_to_wasm_type(Type* type) { return WASM_TYPE_VOID; } +static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft); + #define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })) #define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data })) #define COMPILE_FUNC(kind, ...) static void compile_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__) @@ -398,6 +404,10 @@ COMPILE_FUNC(store_instruction, Type* type, u32 alignment, u32 offset) { type = type->Enum.backing; } + if (type->kind == Type_Kind_Function) { + type = &basic_types[Basic_Kind_U32]; + } + i32 store_size = type_size_of(type); i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer; i32 is_pointer = is_basic && (type->Basic.flags & Basic_Flag_Pointer); @@ -430,6 +440,10 @@ COMPILE_FUNC(load_instruction, Type* type, u32 offset) { type = type->Enum.backing; } + if (type->kind == Type_Kind_Function) { + type = &basic_types[Basic_Kind_U32]; + } + i32 load_size = type_size_of(type); i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer; i32 is_pointer = is_basic && (type->Basic.flags & Basic_Flag_Pointer); @@ -718,8 +732,15 @@ COMPILE_FUNC(call, AstCall* call) { compile_expression(mod, &code, arg->value); } - i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee); - bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx })); + if (call->callee->kind == Ast_Kind_Function) { + i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee); + bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx })); + } else { + compile_expression(mod, &code, call->callee); + + i32 type_idx = generate_type_idx(mod, call->callee->type); + WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 })); + } *pcode = code; } @@ -893,6 +914,12 @@ COMPILE_FUNC(expression, AstTyped* expr) { break; } + case Ast_Kind_Function: { + i32 funcidx = bh_imap_get(&mod->index_map, (u64) expr); + WID(WI_I32_CONST, funcidx); + break; + } + case Ast_Kind_Block: compile_block(mod, &code, (AstBlock *) expr); break; case Ast_Kind_Call: compile_call(mod, &code, (AstCall *) expr); break; case Ast_Kind_Intrinsic_Call: compile_intrinsic_call(mod, &code, (AstIntrinsicCall *) expr); break; @@ -1113,12 +1140,14 @@ COMPILE_FUNC(return, AstReturn* ret) { *pcode = code; } -static i32 generate_type_idx(OnyxWasmModule* mod, AstFunction* fd) { +static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) { + if (ft->kind != Type_Kind_Function) return -1; + static char type_repr_buf[128]; char* t = type_repr_buf; - Type** param_type = fd->type->Function.params; - i32 param_count = fd->type->Function.param_count; + Type** param_type = ft->Function.params; + i32 param_count = ft->Function.param_count; i32 params_left = param_count; while (params_left-- > 0) { // HACK: Using these directly as part of a string feels weird but they are @@ -1128,7 +1157,7 @@ static i32 generate_type_idx(OnyxWasmModule* mod, AstFunction* fd) { } *(t++) = ':'; - WasmType return_type = onyx_type_to_wasm_type(fd->type->Function.return_type); + WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type); *(t++) = (char) return_type; *t = '\0'; @@ -1177,7 +1206,7 @@ static inline b32 should_compile_function(AstFunction* fd) { static void compile_function(OnyxWasmModule* mod, AstFunction* fd) { if (!should_compile_function(fd)) return; - i32 type_idx = generate_type_idx(mod, fd); + i32 type_idx = generate_type_idx(mod, fd->type); if (fd->flags & Ast_Flag_Foreign) { WasmImport import = { @@ -1394,6 +1423,8 @@ OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) { .data = NULL, .next_datum_offset = 0, + .element_count = 0, + .structured_jump_target = NULL, }; @@ -1431,6 +1462,7 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) { switch (entity->type) { case Entity_Type_Function_Header: { if (!should_compile_function(entity->function)) break; + module->element_count++; u64 func_idx; if ((entity->function->flags & Ast_Flag_Foreign) != 0) @@ -1467,7 +1499,7 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) { case Entity_Type_Memory_Reservation: { compile_memory_reservation(module, (AstMemRes *) entity->mem_res); - + // HACK: To put this here // NOTE: Round up to the nearest multiple of 16 builtin_heap_start.value.i = @@ -1629,6 +1661,30 @@ static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) { return buff->length - prev_len; } +static i32 output_tablesection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + bh_buffer_write_byte(buff, WASM_SECTION_ID_TABLE); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + i32 leb_len; + u8* leb = uint_to_uleb128((u64) 1, &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + + // NOTE: funcrefs are the only valid table element type + bh_buffer_write_byte(&vec_buff, 0x70); + output_limits(module->element_count, -1, &vec_buff); + + 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_memorysection(OnyxWasmModule* module, bh_buffer* buff) { i32 prev_len = buff->length; bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY); @@ -1774,6 +1830,46 @@ static i32 output_startsection(OnyxWasmModule* module, bh_buffer* buff) { return buff->length - prev_len; } +static i32 output_elemsection(OnyxWasmModule* module, bh_buffer* buff) { + i32 prev_len = buff->length; + + bh_buffer_write_byte(buff, WASM_SECTION_ID_ELEMENT); + + bh_buffer vec_buff; + bh_buffer_init(&vec_buff, buff->allocator, 128); + + u32 func_count = bh_arr_length(module->funcs) + module->next_foreign_func_idx; + + i32 leb_len; + u8* leb; + + // NOTE: 0x01 count of elems + bh_buffer_write_byte(&vec_buff, 0x01); + + // NOTE: 0x00 table index + bh_buffer_write_byte(&vec_buff, 0x00); + + bh_buffer_write_byte(&vec_buff, WI_I32_CONST); + bh_buffer_write_byte(&vec_buff, 0x00); + bh_buffer_write_byte(&vec_buff, WI_BLOCK_END); + + leb = uint_to_uleb128((u64) func_count, &leb_len); + bh_buffer_append(&vec_buff, leb, leb_len); + + fori (funcidx, 0, func_count - 1) { + leb = uint_to_uleb128((u64) funcidx, &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_locals(WasmFunc* func, bh_buffer* buff) { i32 prev_len = buff->length; @@ -1836,6 +1932,7 @@ static void output_instruction(WasmInstruction* instr, bh_buffer* buff) { bh_buffer_append(buff, leb, leb_len); break; + case WI_CALL_INDIRECT: case WI_I32_STORE: case WI_I32_STORE_8: case WI_I32_STORE_16: @@ -1978,10 +2075,12 @@ void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) { output_typesection(module, &master_buffer); output_importsection(module, &master_buffer); output_funcsection(module, &master_buffer); + output_tablesection(module, &master_buffer); output_memorysection(module, &master_buffer); output_globalsection(module, &master_buffer); output_exportsection(module, &master_buffer); output_startsection(module, &master_buffer); + output_elemsection(module, &master_buffer); output_codesection(module, &master_buffer); output_datasection(module, &master_buffer);