"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/onyx",
- "args": ["progs/ufc.onyx"],
+ "args": ["progs/fcf.onyx"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
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),
#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)
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; };
u32 next_type_idx;
u32 export_count;
+ u32 element_count;
u32 next_func_idx;
u32 next_foreign_func_idx;
u32 next_global_idx;
--- /dev/null
+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));
+}
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;
}
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;
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");
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;
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;
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;
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);
if (parser->curr->type == ':') {
consume_token(parser);
expect_token(parser, ':');
-
+
evalue->value = parse_numeric_literal(parser);
}
add_node_to_process(parser, (AstNode *) upack);
return NULL;
-
+
} else {
AstIncludeFile* include = make_node(AstIncludeFile, Ast_Kind_Include_File);
include->token = use_token;
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,
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);
}
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;
}
}
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.");
if (enum_node->flags & Ast_Flag_Enum_Is_Flags) {
next_assign_value <<= 1;
- } else {
+ } else {
next_assign_value++;
}
}
+#define BH_DEBUG
#include "onyxtypes.h"
#include "onyxastnodes.h"
#include "onyxutils.h"
}
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:
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);
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);
}
s_type->Struct.aligment = alignment;
-
+
if (offset % alignment != 0) {
offset += alignment - (offset % alignment);
}
return type->Enum.name;
else
return "<anonymous enum>";
+
+ case Type_Kind_Function: return bh_aprintf(global_scratch_allocator, "proc (...) -> %s", type_get_name(type->Function.return_type));
+
default: return "unknown";
}
}
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;
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__)
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);
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);
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;
}
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;
*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
}
*(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';
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 = {
.data = NULL,
.next_datum_offset = 0,
+ .element_count = 0,
+
.structured_jump_target = NULL,
};
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)
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 =
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);
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;
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:
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);