diff options
-rwxr-xr-x | build.sh | 7 | ||||
-rw-r--r-- | executor/x86_64/asm.c | 11 | ||||
-rw-r--r-- | executor/x86_64/executor.c | 14 | ||||
-rw-r--r-- | include/bytecode/bytecode.h | 20 | ||||
-rw-r--r-- | include/program.h | 3 | ||||
-rw-r--r-- | include/std/buffer_view.h | 1 | ||||
-rw-r--r-- | src/ast.c | 5 | ||||
-rw-r--r-- | src/bytecode/bytecode.c | 48 | ||||
-rw-r--r-- | src/compiler.c | 11 | ||||
-rw-r--r-- | src/parser.c | 2 | ||||
-rw-r--r-- | src/program.c | 162 | ||||
-rw-r--r-- | src/std/arena_allocator.c | 5 | ||||
-rw-r--r-- | src/std/buffer.c | 8 | ||||
-rw-r--r-- | src/std/buffer_view.c | 8 | ||||
-rw-r--r-- | src/tokenizer.c | 11 | ||||
-rw-r--r-- | tests/glfw.amal | 9 | ||||
-rw-r--r-- | tests/main.c | 102 |
17 files changed, 278 insertions, 149 deletions
@@ -70,10 +70,9 @@ build_test() { set +x source_files_test=$(find "tests" -name "*.c") - if [ -z "$NO_TEST" ]; then - set -x - time $CC $source_files_test $CFLAGS $LIBS -o test "./libamalgam.so" - fi + set -x + time $CC $source_files_test $CFLAGS $LIBS -o test "./libamalgam.so" $(pkg-config --cflags --libs glfw3 glew) + set +x build_compile_commands $source_files $source_files_test } diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c index e29130e..c633db8 100644 --- a/executor/x86_64/asm.c +++ b/executor/x86_64/asm.c @@ -210,16 +210,21 @@ static void asm_print_code_hex(Asm *self) { } #endif +typedef union { + u8 *data; + int (*func)(void); +} RawFuncCallPtr; + int asm_execute(Asm *self, u32 offset) { - void (*func)(); + RawFuncCallPtr raw_func_ptr; if(mprotect(self->code, self->allocated_size, PROT_READ | PROT_EXEC) != 0) return -errno; /*asm_print_code_hex(self);*/ /* TODO: Verify if this is valid on all platforms. According to ISO C standard it isn't? */ - *(void**)(&func) = (u8*)self->code + offset; - func(); + raw_func_ptr.data = (u8*)self->code + offset; + raw_func_ptr.func(); return 0; } diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c index c442da8..f747e4a 100644 --- a/executor/x86_64/executor.c +++ b/executor/x86_64/executor.c @@ -284,6 +284,7 @@ int amal_exec_call(amal_executor *self, u32 code_offset, u8 num_args, i8 dst_reg isize asm_offset = asm_get_size(&impl->asm); ASM_ENSURE_CAPACITY + assert(num_args % 2 == 0 && "TODO: Align stack to 16-bytes before calling functions"); assert(code_offset < asm_offset); asm_call_rel32(&impl->asm, (isize)code_offset - asm_offset); @@ -318,7 +319,8 @@ int amal_exec_calle(amal_executor *self, void *func, u8 num_args, i8 dst_reg) { AsmPtr dst; IMPL_START - /* TODO: Support R and XMM registers so more than 5 arguments can be used for functions */ + assert(num_args % 2 == 0 && "TODO: Align stack to 16-bytes before calling functions"); + /* TODO: Support R and XMM registers so more than 4 arguments can be used for functions */ assert(num_args < 5); { /* @@ -427,6 +429,10 @@ int amal_exec_ret(amal_executor *self, i8 reg) { return amal_exec_func_end(self); } +static u32 get_next_uneven_number(u32 value) { + return value + !(value & 1); +} + int amal_exec_func_start(amal_executor *self, u16 num_regs) { /* TODO: Validate stack size, or maybe remove all validation? do we really need validation? @@ -443,7 +449,11 @@ int amal_exec_func_start(amal_executor *self, u16 num_regs) { asm_pushr(&impl->asm, RBX); asm_pushr(&impl->asm, RBP); asm_mov_rr(&impl->asm, RBP, RSP); - asm_sub_rm64_imm(&impl->asm, RSP, num_regs * sizeof(isize)); + /* + Functions are entered with a stack alignment of 8 (because of call return address is pushed to stack). + Make sure to align to to next 16-byte even if the extra bytes are not used. + */ + asm_sub_rm64_imm(&impl->asm, RSP, get_next_uneven_number(num_regs) * sizeof(isize)); return 0; } diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h index c495d7a..a93fe4f 100644 --- a/include/bytecode/bytecode.h +++ b/include/bytecode/bytecode.h @@ -8,8 +8,8 @@ #include <setjmp.h> -/*doc(Opcode) - Variable length opcodes. Sizes range from 1 to 5 bytes. +/*doc(Instructions) + Variable length instructions. Instruction size ranges from 1 to 5 bytes. # Instruction formats Instructions can be in 7 different formats: 1. 1 byte: Opcode(u8) @@ -25,7 +25,8 @@ 6.1 Opcode(u8) + register(i8) + label(i16)\ 6.2 Opcode(u8) + register(i8) + intermediate(u16)\ 6.3 Opcode(u8) + register(i8) + data(u16)\ - 6.4 Opcode(u8) + flags(u8) + num_local_var_reg(u16) + 6.4 Opcode(u8) + flags(u8) + num_local_var_reg(u16)\ + 6.5 Opcode(u8) + stack_offset(i24) 7. 5 bytes: Opcode(u8) + index(u8) + index(u16) + num_args(u8) # Registers Registers have a range of 128. Local variables start from register 0 and increment while parameters start from -1 @@ -96,6 +97,19 @@ typedef struct { /* TODO: Make sure this pragma pack works on all platforms */ #pragma pack(push, 1) typedef struct { + u8 num_params; + u32 params_num_pointers; + u32 params_fixed_size; + + u8 num_return_types; + u32 return_types_num_pointers; + u32 return_types_fixed_size; +} BytecodeHeaderExternFunction; +#pragma pack(pop) + +/* TODO: Make sure this pragma pack works on all platforms */ +#pragma pack(push, 1) +typedef struct { u32 function_index; #define parser_index function_index u32 extern_function_index; diff --git a/include/program.h b/include/program.h index b398a3a..aa318d9 100644 --- a/include/program.h +++ b/include/program.h @@ -50,7 +50,6 @@ typedef struct { typedef struct { Buffer/*<...>*/ data; u32 *string_indices; - u32 *extern_func_indices; u8 *intermediates_start; /* Reference inside @data */ u8 *strings_start; /* Reference inside @data */ u8 *funcs_start; /* Reference inside @data */ @@ -84,7 +83,7 @@ void amal_program_deinit(amal_program *self); returns @AMAL_PROGRAM_EXTERN_FUNC_ALREADY_EXISTS if a function with the name @name already exists in the program */ -CHECK_RESULT int amal_program_add_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size); +CHECK_RESULT int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size); CHECK_RESULT int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode); CHECK_RESULT int amal_program_run(amal_program *self); diff --git a/include/std/buffer_view.h b/include/std/buffer_view.h index 9c0a722..a54f616 100644 --- a/include/std/buffer_view.h +++ b/include/std/buffer_view.h @@ -10,6 +10,7 @@ typedef struct { BufferView create_buffer_view_null(void); BufferView create_buffer_view(const char *data, usize size); +BufferView create_buffer_view_auto(const char *data); bool buffer_view_equals(const BufferView *self, const BufferView *other); #endif @@ -560,6 +560,9 @@ static void lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context, AstR if(ast_is_decl(self->rhs_expr)) { result->type = RESOLVED_TYPE_LHS_EXPR; result->value.lhs_expr = self; + /* Structs resolved type becomes the lhs while functions resolved type becomes the function signature they own */ + if (self->rhs_expr->type == AST_STRUCT_DECL) + self->rhs_expr->resolve_data.type = *result; } else { *result = self->rhs_expr->resolve_data.type; } @@ -753,6 +756,7 @@ static TypeSize variable_type_get_byte_size(VariableType *self) { type_size.fixed_size = 0; switch(self->type) { case VARIABLE_TYPE_NONE: + assert(bool_false && "Variable type not resolved!"); break; case VARIABLE_TYPE_VARIABLE: type_size = resolved_type_get_byte_size(&self->value.variable->resolved_var.resolve_data->type); @@ -943,6 +947,7 @@ TypeSize resolved_type_get_byte_size(AstResolvedType *self) { type_size.fixed_size = 0; switch(self->type) { case RESOLVED_TYPE_NONE: + assert(bool_false && "Type not resolved!"); break; case RESOLVED_TYPE_LHS_EXPR: { /* Resolved type until rhs is StructDecl or FunctionSignature */ diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index 2c43471..c968743 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -155,9 +155,9 @@ static TypeSize function_signature_get_return_types_size(FunctionSignature *self return_types_total_size.num_pointers = 0; return_types_total_size.fixed_size = 0; for(; return_type != return_type_end; ++return_type) { - TypeSize param_size = resolved_type_get_byte_size(&return_type->resolved_type); - return_types_total_size.num_pointers += param_size.num_pointers; - return_types_total_size.fixed_size += param_size.fixed_size; + TypeSize return_size = resolved_type_get_byte_size(&return_type->resolved_type); + return_types_total_size.num_pointers += return_size.num_pointers; + return_types_total_size.fixed_size += return_size.fixed_size; } return return_types_total_size; } @@ -187,7 +187,7 @@ static void add_functions(BytecodeCompilerContext *self) { Buffer *instructions = &self->bytecode->data; SsaFunc *func = buffer_begin(&ssa->funcs); SsaFunc *func_end = buffer_end(&ssa->funcs); - u32 funcs_size = ssa->func_counter * sizeof(BytecodeHeaderFunction); + u32 funcs_size = (u32)ssa->func_counter * sizeof(BytecodeHeaderFunction); assert(sizeof(BytecodeHeaderFunction) == 22); self->bytecode->funcs_index = instructions->size; @@ -224,20 +224,26 @@ static void add_extern_functions(BytecodeCompilerContext *self) { |External function[]|External functions|Multiple external functions, where the number of functions is defined by @num_extern_func| # External function - |Type|Field |Description | - |----|----------|-----------------------------------------------------------------------------------------------------| - |u8 |num_params|The number of parameters the functions has. | - |u8 |name_len |The length of the external function name, in bytes. Excluding the null-terminate character. | - |u8[]|name |The name of the external function, where the size is defined by @name_len. Names are null-terminated.| + |Type|Field |Description | + |----|-------------------------|-----------------------------------------------------------------------------------------------------| + |u8 |num_params |The number of parameters. | + |u32 |params_num_pointers |The number of pointers in the parameters. | + |u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. | + |u8 |num_return_types |The number of return values. | + |u32 |return_types_num_pointers|The number of pointers in the return types. | + |u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. | + |u8 |name_len |The length of the external function name, in bytes. Excluding the null-terminate character. | + |u8[]|name |The name of the external function, where the size is defined by @name_len. Names are null-terminated.| */ Ssa *ssa = self->parser->ssa; Buffer *instructions = &self->bytecode->data; SsaExternFunc *extern_func = buffer_begin(&ssa->extern_funcs); SsaExternFunc *extern_func_end = buffer_end(&ssa->extern_funcs); - u32 extern_funcs_size = 0; + u32 extern_funcs_size = (u32)ssa->extern_func_counter * (sizeof(BytecodeHeaderExternFunction) + sizeof(u8)); + assert(sizeof(BytecodeHeaderExternFunction) == 18); for(; extern_func != extern_func_end; ++extern_func) { - extern_funcs_size += sizeof(u8) + sizeof(u8) + extern_func->name.size + 1; /* +1 for null-termination of string */ + extern_funcs_size += extern_func->name.size + 1; /* +1 for null-termination of string */ } extern_func = buffer_begin(&ssa->extern_funcs); @@ -247,8 +253,19 @@ static void add_extern_functions(BytecodeCompilerContext *self) { throw_if_error(buffer_append(instructions, &extern_funcs_size, sizeof(u32))); for(; extern_func != extern_func_end; ++extern_func) { const char null_s = '\0'; - u8 num_params = buffer_get_size(&extern_func->func_sig->parameters, FunctionParameter); - throw_if_error(buffer_append(instructions, &num_params, sizeof(num_params))); + TypeSize params_total_size = function_signature_get_params_size(extern_func->func_sig); + TypeSize return_types_total_size = function_signature_get_return_types_size(extern_func->func_sig); + BytecodeHeaderExternFunction header_func; + + header_func.num_params = buffer_get_size(&extern_func->func_sig->parameters, FunctionParameter); + header_func.params_num_pointers = params_total_size.num_pointers; + header_func.params_fixed_size = params_total_size.fixed_size; + + header_func.num_return_types = buffer_get_size(&extern_func->func_sig->return_types, FunctionReturnType); + header_func.return_types_num_pointers = return_types_total_size.num_pointers; + header_func.return_types_fixed_size = return_types_total_size.fixed_size; + throw_if_error(buffer_append(instructions, &header_func, sizeof(header_func))); + /* TODO: Add namespace to the function name */ /* u8 is fine, because the max length of a variable is 255 */ throw_if_error(buffer_append(instructions, &extern_func->name.size, sizeof(u8))); @@ -280,10 +297,10 @@ static void add_export_functions(BytecodeCompilerContext *self) { Buffer *instructions = &self->bytecode->data; SsaExportFunc *export_func = buffer_begin(&ssa->export_funcs); SsaExportFunc *export_func_end = buffer_end(&ssa->export_funcs); - u32 export_funcs_size = 0; + u32 export_funcs_size = (u32)ssa->export_func_counter * (sizeof(u32) + sizeof(u8) + sizeof(u8)); for(; export_func != export_func_end; ++export_func) { - export_funcs_size += sizeof(u32) + sizeof(u8) + sizeof(u8) + export_func->name.size + 1; /* +1 for null-termination of string */ + export_funcs_size += export_func->name.size + 1; /* +1 for null-termination of string */ } export_func = buffer_begin(&ssa->export_funcs); @@ -452,7 +469,6 @@ static void add_instructions(BytecodeCompilerContext *self) { u32 num_instructions_index = self->bytecode->data.size; throw_if_error(buffer_append_empty(&self->bytecode->data, sizeof(num_instructions_index))); - /* TODO: Keep all registers under 256 */ while(instruction != instructions_end) { SsaInstruction ins = (SsaInstruction)*instruction++; switch(ins) { diff --git a/src/compiler.c b/src/compiler.c index 8dda1c6..6e6bc4b 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -44,6 +44,7 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char expr->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; expr->resolve_data.type.value.lhs_expr = lhs_expr; expr->resolve_data.status = AST_RESOLVED; + lhs_expr->rhs_expr->resolve_data = expr->resolve_data; return scope_add_child(&compiler->root_scope, expr); } @@ -58,12 +59,12 @@ static CHECK_RESULT int create_default_type_fixed_size(amal_compiler *compiler, static CHECK_RESULT int init_default_types(amal_compiler *compiler) { return_if_error(create_default_type_fixed_size(compiler, "i8", 1, &compiler->default_types.i8)); return_if_error(create_default_type_fixed_size(compiler, "i16", 2, &compiler->default_types.i16)); - return_if_error(create_default_type_fixed_size(compiler, "i32", 3, &compiler->default_types.i32)); - return_if_error(create_default_type_fixed_size(compiler, "i64", 4, &compiler->default_types.i64)); + return_if_error(create_default_type_fixed_size(compiler, "i32", 4, &compiler->default_types.i32)); + return_if_error(create_default_type_fixed_size(compiler, "i64", 8, &compiler->default_types.i64)); return_if_error(create_default_type_fixed_size(compiler, "u8", 1, &compiler->default_types.u8)); return_if_error(create_default_type_fixed_size(compiler, "u16", 2, &compiler->default_types.u16)); - return_if_error(create_default_type_fixed_size(compiler, "u32", 3, &compiler->default_types.u32)); - return_if_error(create_default_type_fixed_size(compiler, "u64", 4, &compiler->default_types.u64)); + return_if_error(create_default_type_fixed_size(compiler, "u32", 4, &compiler->default_types.u32)); + return_if_error(create_default_type_fixed_size(compiler, "u64", 8, &compiler->default_types.u64)); return_if_error(create_default_type_num_pointers(compiler, "isize", 1, &compiler->default_types.isize)); return_if_error(create_default_type_num_pointers(compiler, "usize", 1, &compiler->default_types.usize)); return_if_error(create_default_type_fixed_size(compiler, "f32", 4, &compiler->default_types.f32)); @@ -185,7 +186,7 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *compile result = AMAL_COMPILER_ERR; cleanup_if_error(amal_mutex_lock(&compiler->mutex, "amal_compiler_load_in_this_thread, create arena allocator")); - return_if_error(arena_allocator_alloc(&compiler->allocator, sizeof(ArenaAllocator), (void**)&parser_allocator)); + cleanup_if_error(arena_allocator_alloc(&compiler->allocator, sizeof(ArenaAllocator), (void**)&parser_allocator)); amal_mutex_tryunlock(&compiler->mutex); return_if_error(arena_allocator_init(parser_allocator)); diff --git a/src/parser.c b/src/parser.c index 01c1d9f..ed99eb0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -383,7 +383,7 @@ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { TODO: Implement function declaration inside other functions. Such functions should be moved to the file scope in the bytecode generation */ - assert(parser_is_current_scope_file_scope(self)); + assert(parser_is_current_scope_file_scope(self) && "TODO: Implement function declaration inside other functions."); throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result)); throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator)); diff --git a/src/program.c b/src/program.c index a0a2407..5c07c5d 100644 --- a/src/program.c +++ b/src/program.c @@ -42,7 +42,6 @@ static usize hash_u64(const u8 *data, usize size) { int amal_program_init(amal_program *self) { ignore_result_int(buffer_init(&self->data, NULL)); self->string_indices = NULL; - self->extern_func_indices = NULL; self->intermediates_start = NULL; self->strings_start = NULL; self->extern_funcs_start = NULL; @@ -72,15 +71,13 @@ int amal_program_init(amal_program *self) { void amal_program_deinit(amal_program *self) { arena_allocator_deinit(&self->allocator); - am_free(self->extern_func_indices); am_free(self->string_indices); - self->extern_func_indices = NULL; self->string_indices = NULL; if(self->data.data) buffer_deinit(&self->data); } -int amal_program_add_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size) { +int amal_program_register_extern_func(amal_program *self, BufferView name, void *func_ptr, int args_byte_size) { ProgramExternFunc extern_func; extern_func.func = func_ptr; extern_func.args_byte_size = args_byte_size; @@ -93,44 +90,52 @@ int amal_program_add_extern_func(amal_program *self, BufferView name, void *func static u8* amal_program_get_extern_funcs_start_by_import_index(amal_program *self, u8 import_index) { BytecodeHeaderImport *header_import = (BytecodeHeaderImport*)self->imports_start; + u32 extern_function_index = 0; header_import += import_index; - return (u8*)self->data.data + header_import->extern_function_index; + assert(sizeof(extern_function_index) == sizeof(header_import->extern_function_index)); + am_memcpy(&extern_function_index, &header_import->extern_function_index, sizeof(extern_function_index)); + return (u8*)self->data.data + extern_function_index; +} + +static u32 extern_func_get_total_param_size(BytecodeHeaderExternFunction *self) { + return self->params_fixed_size + self->params_num_pointers * sizeof(void*); } typedef struct { - u8 num_params; + BytecodeHeaderExternFunction extern_func; BufferView name; -} BytecodeHeaderExternFunction; +} BytecodeHeaderExternFunctionFull; /* TODO: Optimize this */ -static void amal_program_get_header_extern_function_by_index(amal_program *self, u8 import_index, u16 index, BytecodeHeaderExternFunction *result) { +static void amal_program_get_header_extern_function_by_index(amal_program *self, u8 import_index, u16 index, BytecodeHeaderExternFunctionFull *result) { u32 i; u8 *extern_funcs_start = amal_program_get_extern_funcs_start_by_import_index(self, import_index); extern_funcs_start += sizeof(u16) + sizeof(u32); for(i = 0; i < (u32)index; ++i) { - u8 name_len = *(extern_funcs_start + sizeof(u8)); - /* +1 for the null-terminated character */ - extern_funcs_start += sizeof(u8) + sizeof(u8) + name_len + 1; + u8 name_len; + extern_funcs_start += sizeof(BytecodeHeaderExternFunction); + name_len = *extern_funcs_start; + extern_funcs_start += 1 + name_len + 1; /* +1 for skipping length byte and the null-terminated character */ } - result->num_params = extern_funcs_start[0]; - result->name.size = extern_funcs_start[1]; - result->name.data = (const char*)extern_funcs_start + sizeof(u8) + sizeof(u8); + am_memcpy(&result->extern_func, extern_funcs_start, sizeof(result->extern_func)); + result->name.size = *(extern_funcs_start + sizeof(result->extern_func)); + result->name.data = (const char*)extern_funcs_start + sizeof(result->extern_func) + sizeof(u8); } static CHECK_RESULT int amal_program_get_extern_func_by_index(amal_program *self, u8 import_index, u16 index, ProgramExternFunc *result) { - BytecodeHeaderExternFunction extern_func; + BytecodeHeaderExternFunctionFull extern_func; amal_program_get_header_extern_function_by_index(self, import_index, index, &extern_func); if(!hash_map_get(&self->extern_funcs_map, extern_func.name, result)) { - amal_log_error("No such extern function: %.*s", extern_func.name.size, extern_func.name.data); + amal_log_error("Extern function \"%.*s\" has not been registered", extern_func.name.size, extern_func.name.data); return AMAL_PROGRAM_NO_SUCH_EXTERNAL_FUNCTION; } /* TODO: This assumes all arguments are of size sizeof(isize) */ - if(result->args_byte_size != -1 && result->args_byte_size != extern_func.num_params * (int)sizeof(isize)) { + if(result->args_byte_size != -1 && result->args_byte_size != (int)extern_func_get_total_param_size(&extern_func.extern_func)) { amal_log_error("Extern function %.*s was registered to take %d byte(s), but the program says it takes %d byte(s)", - extern_func.name.size, extern_func.name.data, result->args_byte_size, extern_func.num_params * sizeof(isize)); + extern_func.name.size, extern_func.name.data, result->args_byte_size, (int)extern_func_get_total_param_size(&extern_func.extern_func)); return AMAL_PROGRAM_NO_SUCH_EXTERNAL_FUNCTION; } return 0; @@ -234,9 +239,6 @@ static CHECK_RESULT int amal_program_advance_section_magic_number(amal_program * static CHECK_RESULT int amal_program_read_intermediates(amal_program *self) { u32 intermediates_size; - /*u32 read_end;*/ - - return_if_error(amal_program_advance_section_magic_number(self)); if(bytes_left_to_read(self) < sizeof(intermediates_size)) { amal_log_error("Not enough space in program to intermediates size"); @@ -262,8 +264,6 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) { u32 strings_size; u32 *string_index_ptr; - return_if_error(amal_program_advance_section_magic_number(self)); - if(!amal_program_read_advance(self, &self->num_strings, sizeof(u16))) return AMAL_PROGRAM_INVALID_STRINGS; @@ -309,8 +309,6 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) { static CHECK_RESULT int amal_program_read_functions(amal_program *self) { u32 funcs_size; - return_if_error(amal_program_advance_section_magic_number(self)); - if(!amal_program_read_advance(self, &self->num_functions, sizeof(u16))) return AMAL_PROGRAM_INVALID_FUNCTIONS; @@ -324,9 +322,6 @@ static CHECK_RESULT int amal_program_read_functions(amal_program *self) { static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) { u32 extern_funcs_size; - u32 *extern_func_index_ptr; - - return_if_error(amal_program_advance_section_magic_number(self)); if(!amal_program_read_advance(self, &self->num_extern_functions, sizeof(u16))) return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS; @@ -337,46 +332,13 @@ static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) if(bytes_left_to_read(self) < extern_funcs_size) return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS_SIZE; - am_free(self->extern_func_indices); - self->extern_func_indices = NULL; - if(am_malloc(sizeof(u32) * self->num_extern_functions, (void**)&self->extern_func_indices) != 0) - return AMAL_PROGRAM_ALLOC_FAILURE; - extern_func_index_ptr = self->extern_func_indices; - - { - const u32 read_start = self->read_index; - const u32 read_end = read_start + extern_funcs_size; - self->extern_funcs_start = (u8*)(self->data.data + self->read_index); - while(self->read_index < read_end) { - u8 num_params; - u8 func_name_size; - - if(bytes_left_to_read(self) < sizeof(num_params) + sizeof(func_name_size)) - return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS; - - *extern_func_index_ptr = self->read_index - read_start; - ++extern_func_index_ptr; - num_params = self->data.data[self->read_index]; - func_name_size = self->data.data[self->read_index + sizeof(num_params)]; - self->read_index += sizeof(num_params) + sizeof(func_name_size); - - /* +1 to skip null-termination character */ - if(bytes_left_to_read(self) < func_name_size + 1U) - return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS; - - self->read_index += func_name_size + 1; /* +1 to skip null-termination character */ - } - assert(self->read_index == read_end); - } - + self->read_index += extern_funcs_size; return AMAL_PROGRAM_OK; } static CHECK_RESULT int amal_program_read_exported_functions(amal_program *self) { u32 export_funcs_size; - return_if_error(amal_program_advance_section_magic_number(self)); - if(!amal_program_read_advance(self, &self->num_exported_functions, sizeof(u16))) return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS; @@ -399,8 +361,6 @@ static CHECK_RESULT int amal_program_read_exported_functions(amal_program *self) static CHECK_RESULT int amal_program_read_imports(amal_program *self) { u32 imports_size; - return_if_error(amal_program_advance_section_magic_number(self)); - if(!amal_program_read_advance(self, &self->num_imports, sizeof(u8))) return AMAL_PROGRAM_INVALID_IMPORTS; @@ -437,20 +397,26 @@ static CHECK_RESULT int amal_program_get_data_by_index(amal_program *self, u16 i static u8* amal_program_get_funcs_start_by_import_index(amal_program *self, u8 import_index) { BytecodeHeaderImport *header_import = (BytecodeHeaderImport*)self->imports_start; + u32 function_index = 0; header_import += import_index; - return (u8*)self->data.data + header_import->function_index; + assert(sizeof(function_index) == sizeof(header_import->function_index)); + am_memcpy(&function_index, &header_import->function_index, sizeof(function_index)); + return (u8*)self->data.data + function_index; } -static BytecodeHeaderFunction* amal_program_get_header_function_by_index(amal_program *self, u8 import_index, u16 index) { +static void amal_program_get_header_function_by_index(amal_program *self, u8 import_index, u16 index, BytecodeHeaderFunction *result) { u8 *funcs_start = amal_program_get_funcs_start_by_import_index(self, import_index); BytecodeHeaderFunction *header_func = (BytecodeHeaderFunction*)(funcs_start + sizeof(u16) + sizeof(u32)); - return header_func + index; + am_memcpy(result, header_func + index, sizeof(BytecodeHeaderFunction)); } static u64 deferred_func_call_get_key(amal_program *self, u8 import_index, u16 func_index) { BytecodeHeaderImport *header_import = (BytecodeHeaderImport*)self->imports_start; + u32 function_index = 0; header_import += import_index; - return ((u64)func_index << 32) | (u64)header_import->function_index; + assert(sizeof(function_index) == sizeof(header_import->function_index)); + am_memcpy(&function_index, &header_import->function_index, sizeof(function_index)); + return ((u64)func_index << 32) | (u64)function_index; } static CHECK_RESULT int resolve_deferred_func_calls(amal_program *self, amal_executor *executor, u16 func_index) { @@ -474,7 +440,8 @@ static CHECK_RESULT int resolve_deferred_func_calls(amal_program *self, amal_exe static void header_func_set_offset(amal_program *self, u16 func_index, u32 code_offset) { BytecodeHeaderFunction *header_func = ((BytecodeHeaderFunction*)self->funcs_start) + func_index; - header_func->func_offset = code_offset; + assert(sizeof(header_func->func_offset) == sizeof(code_offset)); + am_memcpy(&header_func->func_offset, &code_offset, sizeof(code_offset)); } static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_executor *executor) { @@ -489,8 +456,6 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ func_counter = 0; self->return_value_index = 0; - return_if_error(amal_program_advance_section_magic_number(self)); - if(!amal_program_read_advance(self, &instructions_size, sizeof(instructions_size))) return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE; @@ -614,20 +579,22 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ u8 import_index; u16 func_index; u8 num_args; - BytecodeHeaderFunction *func_def; + BytecodeHeaderFunction func_def; i8 dst_reg; am_memcpy(&import_index, self->data.data + self->read_index, sizeof(import_index)); am_memcpy(&func_index, self->data.data + self->read_index + sizeof(import_index), sizeof(func_index)); am_memcpy(&num_args, self->data.data + self->read_index + sizeof(import_index) + sizeof(func_index), sizeof(num_args)); - func_def = amal_program_get_header_function_by_index(self, import_index, func_index); - assert(func_def->num_return_types == 1 && "TODO: Support 0 and more than 1 return values"); + amal_program_get_header_function_by_index(self, import_index, func_index, &func_def); + assert(func_def.num_return_types == 1 && "TODO: Support 0 and more than 1 return values"); assert(self->return_value_index == 1); dst_reg = self->return_values_stack[0]; - self->return_value_index -= func_def->num_return_types; + self->return_value_index -= func_def.num_return_types; - if((char*)func_def < self->data.data + self->read_index) { - return_if_error(amal_exec_call(executor, func_def->func_offset, num_args, dst_reg)); + /* func_offset will only be non-zero when the function has been decoded (FUNC_START) */ + if(func_def.func_offset != 0) { + /* TODO: Instead of pushing num args, push the sum of sizeof the last num_args */ + return_if_error(amal_exec_call(executor, func_def.func_offset, num_args, dst_reg)); } else { /* The code for the function has not been generated yet (the function is defined after the current location). @@ -746,6 +713,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ int amal_program_run(amal_program *self) { int result; amal_executor *executor; + if(self->data.size > PROGRAM_MAX_SIZE) { amal_log_error("Program is too large. Max size is 1GB"); return AMAL_PROGRAM_ERR; @@ -755,12 +723,25 @@ int amal_program_run(amal_program *self) { cleanup_if_error(amal_program_read_header(self)); while(bytes_left_to_read(self) > 0) { + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_intermediates(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_strings(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_functions(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_external_functions(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_exported_functions(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_imports(self)); + + cleanup_if_error(amal_program_advance_section_magic_number(self)); cleanup_if_error(amal_program_read_instructions(self, executor)); } if(self->main_func_instruction_offset == ~(u32)0U) { @@ -776,21 +757,22 @@ int amal_program_run(amal_program *self) { } int amal_program_save(amal_program *self, const char *filepath) { - FILE *file; - file = fopen(filepath, "wb"); + int err = 0; + FILE *file = fopen(filepath, "wb"); if(!file) { - int err; - err = errno; - perror(filepath); - return -err; + err = -errno; + goto cleanup; } + if(fwrite(self->data.data, 1, self->data.size, file) != self->data.size) { - int err; - err = errno; - perror(filepath); - return -err; + err = -errno; + goto cleanup; } - fclose(file); - return 0; -} + cleanup: + if(err != 0) + perror(filepath); + if(file) + fclose(file); + return err; +} diff --git a/src/std/arena_allocator.c b/src/std/arena_allocator.c index 11fb40d..8b0083d 100644 --- a/src/std/arena_allocator.c +++ b/src/std/arena_allocator.c @@ -74,7 +74,8 @@ static usize align_ptr_ceil_offset(void *ptr, uintptr_t alignment) { return (uintptr_t)align_ptr_ceil(ptr, alignment) - (uintptr_t)ptr; } -#define ALLOC_ALIGNMENT 8 +/* 16-byte alignment allows SIMD instructions to be operated on the data */ +#define ALLOC_ALIGNMENT 16 int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) { ArenaAllocatorNode *current; @@ -103,6 +104,6 @@ int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) { int arena_allocator_add_mem(ArenaAllocator *self, usize *result_index) { void *null_data; null_data = NULL; - *result_index = buffer_get_size(&self->mems, void*); + *result_index = self->mems.size; return buffer_append(&self->mems, &null_data, sizeof(void*)); } diff --git a/src/std/buffer.c b/src/std/buffer.c index 021fce8..dca3b26 100644 --- a/src/std/buffer.c +++ b/src/std/buffer.c @@ -28,7 +28,6 @@ void buffer_deinit(Buffer *self) { static CHECK_RESULT int buffer_ensure_capacity(Buffer *self, usize new_capacity) { usize capacity; void *new_mem; - int alloc_result; if(self->capacity >= new_capacity) return BUFFER_OK; @@ -45,15 +44,14 @@ static CHECK_RESULT int buffer_ensure_capacity(Buffer *self, usize new_capacity) capacity = cap; } - alloc_result = am_realloc(self->data, capacity, &new_mem); - if(alloc_result != ALLOC_OK) + if(am_realloc(self->data, capacity, &new_mem) != ALLOC_OK) return BUFFER_ALLOC_FAIL; self->data = new_mem; self->capacity = capacity; /* Update list of buffers in the allocator with the new address of the buffer data */ if(self->allocator) - am_memcpy(self->allocator->mems.data + sizeof(void*) * self->allocator_index, &self->data, sizeof(void*)); + am_memcpy(self->allocator->mems.data + self->allocator_index, &self->data, sizeof(void*)); return BUFFER_OK; } @@ -103,7 +101,7 @@ int buffer_set_capacity(Buffer *self, usize new_capacity) { self->size = self->capacity; /* Update list of buffers in the allocator with the new address of the buffer data */ if(self->allocator) - am_memcpy(self->allocator->mems.data + sizeof(void*) * self->allocator_index, &self->data, sizeof(void*)); + am_memcpy(self->allocator->mems.data + self->allocator_index, &self->data, sizeof(void*)); return BUFFER_OK; } diff --git a/src/std/buffer_view.c b/src/std/buffer_view.c index 249928b..8763be2 100644 --- a/src/std/buffer_view.c +++ b/src/std/buffer_view.c @@ -1,5 +1,6 @@ #include "../../include/std/buffer_view.h" #include "../../include/std/mem.h" +#include <string.h> BufferView create_buffer_view_null(void) { BufferView buffer_view; @@ -15,6 +16,13 @@ BufferView create_buffer_view(const char *data, usize size) { return buffer_view; } +BufferView create_buffer_view_auto(const char *data) { + BufferView buffer_view; + buffer_view.data = data; + buffer_view.size = strlen(data); + return buffer_view; +} + bool buffer_view_equals(const BufferView *self, const BufferView *other) { return self->size == other->size && am_memeql(self->data, other->data, self->size); } diff --git a/src/tokenizer.c b/src/tokenizer.c index fd516f6..de467c3 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -7,6 +7,7 @@ #include <limits.h> #include <stdio.h> #include <stdarg.h> +#include <string.h> static int isAlpha(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); @@ -399,16 +400,6 @@ int tokenizer_next(Tokenizer *self, Token *token) { return result; } -static usize strlen(const char *str) { - usize len; - len = 0; - while(*str != '\0') { - ++len; - ++str; - } - return len; -} - /* static const char* binop_to_string(BinopType binop_type) { switch(binop_type) { diff --git a/tests/glfw.amal b/tests/glfw.amal new file mode 100644 index 0000000..8957127 --- /dev/null +++ b/tests/glfw.amal @@ -0,0 +1,9 @@ +extern const glfwInit: fn() i32; +extern const glfwTerminate: fn() i32; // should return void.... +extern const glfwCreateWindow: fn(x: i32, y: i32, title: str, monitor: usize, share: usize) usize; + +const main = fn { + glfwInit(); + glfwCreateWindow(50, 50, "hello, world", 0, 0); + glfwTerminate(); +}
\ No newline at end of file diff --git a/tests/main.c b/tests/main.c index eec3a78..1a86769 100644 --- a/tests/main.c +++ b/tests/main.c @@ -1,12 +1,12 @@ -#include <stdio.h> -#include <string.h> #include "../include/compiler.h" #include "../include/std/hash_map.h" #include "../include/std/hash.h" #include "../include/std/log.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> +#include <GLFW/glfw3.h> static int num_threads = 0; static int num_successful_tests = 0; @@ -142,6 +142,93 @@ static int print_extern_num(i64 num) { return 0; } +static void test_load_gl(void) { + #if 0 + GLFWwindow* window; + + /* Initialize the library */ + if (!glfwInit()) + FAIL_TEST(full_path); + + /* Create a windowed mode window and its OpenGL context */ + window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL); + if (!window) { + glfwTerminate(); + FAIL_TEST(full_path); + } + + /* Make the window's context current */ + glfwMakeContextCurrent(window); + + /* Loop until the user closes the window */ + while (!glfwWindowShouldClose(window)) + { + /* Render here */ + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + + /* Swap front and back buffers */ + glfwSwapBuffers(window); + + /* Poll for and process events */ + glfwPollEvents(); + } + + glfwTerminate(); + #endif + char *full_path = get_full_path("tests/glfw.amal"); + amal_compiler_options options; + amal_program program; + int result; + + amal_compiler_options_init(&options); + options.num_threads = num_threads; + ++num_tests_run; + + if(amal_program_init(&program) != 0) { + fprintf(stderr, "Failed to initialize amal program\n"); + FAIL_TEST_CLEANUP(full_path); + } + + printf("glfw init ptr: %p, glfw terminate: %p\n", glfwInit, glfwTerminate); + + if(amal_program_register_extern_func(&program, create_buffer_view_auto("glfwInit"), glfwInit, 0) != 0) { + fprintf(stderr, "Unexpected error (alloc failure)\n"); + FAIL_TEST_CLEANUP(full_path); + } + if(amal_program_register_extern_func(&program, create_buffer_view_auto("glfwTerminate"), glfwTerminate, 0) != 0) { + fprintf(stderr, "Unexpected error (alloc failure)\n"); + FAIL_TEST_CLEANUP(full_path); + } + if(amal_program_register_extern_func(&program, create_buffer_view_auto("glfwCreateWindow"), glfwCreateWindow, sizeof(int) + sizeof(int) + sizeof(const char*) + sizeof(void*) + sizeof(void*)) != 0) { + fprintf(stderr, "Unexpected error (alloc failure)\n"); + FAIL_TEST_CLEANUP(full_path); + } + if(amal_program_register_extern_func(&program, create_buffer_view_auto("print_extern_num"), print_extern_num, sizeof(i64)) != 0) { + fprintf(stderr, "Unexpected error (alloc failure)\n"); + FAIL_TEST_CLEANUP(full_path); + } + + result = amal_compiler_load_file(&options, &program, full_path); + if(result != AMAL_COMPILER_OK) { + fprintf(stderr, "Failed to load file %s, result: %d\n", full_path, result); + FAIL_TEST_CLEANUP(full_path); + } + + result = amal_program_run(&program); + if(result != 0) { + fprintf(stderr, "Failed to run the program %s, result: %d\n", full_path, result); + FAIL_TEST_CLEANUP(full_path); + } + + fprintf(stderr, "Test succeeded as expected: %s\n", full_path); + ++num_successful_tests; + + cleanup: + amal_program_deinit(&program); + free(full_path); +} + static void test_load(const char *filepath) { amal_compiler_options options; amal_program program; @@ -155,14 +242,14 @@ static void test_load(const char *filepath) { if(amal_program_init(&program) != 0) { fprintf(stderr, "Failed to initialize amal program\n"); - FAIL_TEST(full_path); + FAIL_TEST_CLEANUP(full_path); } - if(amal_program_add_extern_func(&program, create_buffer_view("print_extern", 12), print_extern, 0) != 0) { + if(amal_program_register_extern_func(&program, create_buffer_view_auto("print_extern"), print_extern, 0) != 0) { fprintf(stderr, "Unexpected error (alloc failure)\n"); FAIL_TEST_CLEANUP(full_path); } - if(amal_program_add_extern_func(&program, create_buffer_view("print_extern_num", 16), print_extern_num, sizeof(i64)) != 0) { + if(amal_program_register_extern_func(&program, create_buffer_view_auto("print_extern_num"), print_extern_num, sizeof(i64)) != 0) { fprintf(stderr, "Unexpected error (alloc failure)\n"); FAIL_TEST_CLEANUP(full_path); } @@ -301,7 +388,10 @@ int main(int argc, char **argv) { if(argc == 1) { run_all_tests(); } else if(argc == 2) { - test_load(argv[1]); + if(strcmp(argv[1], "opengl") == 0) + test_load_gl(); + else + test_load(argv[1]); } else { fprintf(stderr, "usage: test [test-file-path]\n"); fprintf(stderr, "If you run test without any files then all tests will run.\n"); |