diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bytecode/bytecode.c | 51 | ||||
-rw-r--r-- | src/compiler.c | 1 | ||||
-rw-r--r-- | src/program.c | 257 |
3 files changed, 272 insertions, 37 deletions
diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index 7c4fe08..9bf5f24 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -58,47 +58,24 @@ static CHECK_RESULT usize ssa_extract_jump(u8 *instruction_data, SsaInsJump *res return sizeof(result->jump_offset); } -static void add_header(BytecodeCompilerContext *self) { - /*doc(Bytecode header) - # Header layout - |Size|Name |Description | - |----|-------------|----------------------------------------------------------------------------| - |4 |Magic number |The magic number used to identify an amalgam bytecode file. | - |1 |Major version|The major version of the bytecode. Updates in this is a breaking change. | - |1 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.| - |1 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.| - The versions in the header only changes for every release, not every change. - */ - - const u32 magic_number = 0xdec05eba; - const u8 major_version = 1; - const u8 minor_version = 0; - const u8 patch_version = 0; - - Buffer *instructions; - instructions = &self->bytecode.data; - throw_if_error(buffer_append(instructions, &magic_number, 4)); - throw_if_error(buffer_append(instructions, &major_version, 1)); - throw_if_error(buffer_append(instructions, &minor_version, 1)); - throw_if_error(buffer_append(instructions, &patch_version, 1)); -} - static void add_intermediates(BytecodeCompilerContext *self) { Ssa *ssa; Buffer *instructions; SsaNumber *intermediate; SsaNumber *intermediates_end; + u32 intemediates_size; ssa = self->parser->ssa; instructions = &self->bytecode.data; intermediate = buffer_begin(&ssa->intermediates); intermediates_end = buffer_end(&ssa->intermediates); - throw_if_error(buffer_expand(instructions, - sizeof(u16) + (sizeof(u8) + sizeof(u64)) * ssa->intermediates.size)); - throw_if_error(buffer_append(instructions, &ssa->intermediates.size, sizeof(u16))); + intemediates_size = (sizeof(u8) + sizeof(u64)) * buffer_get_size(&ssa->intermediates, SsaNumber); + throw_if_error(buffer_expand(instructions, sizeof(u32) + intemediates_size)); + throw_if_error(buffer_append(instructions, &intemediates_size, sizeof(u32))); for(; intermediate != intermediates_end; ++intermediate) { throw_if_error(buffer_append(instructions, &intermediate->type, sizeof(u8))); + /* TODO: Store value using an encoding that will save space when using low numbers */ throw_if_error(buffer_append(instructions, &intermediate->value.integer, sizeof(u64))); } } @@ -108,12 +85,18 @@ void add_strings(BytecodeCompilerContext *self) { Buffer *instructions; BufferView *string; BufferView *strings_end; + u16 num_strings; u32 strings_size; ssa = self->parser->ssa; instructions = &self->bytecode.data; string = buffer_begin(&ssa->strings); strings_end = buffer_end(&ssa->strings); + if(strings_end - string > UINT16_MAX) { + amal_log_error("Too many strings in the program"); + throw(-1); + } + num_strings = strings_end - string; strings_size = 0; for(; string != strings_end; ++string) { @@ -121,7 +104,8 @@ void add_strings(BytecodeCompilerContext *self) { } string = buffer_begin(&ssa->strings); - throw_if_error(buffer_expand(instructions, sizeof(u32) + strings_size)); + throw_if_error(buffer_expand(instructions, sizeof(u16) + sizeof(u32) + strings_size)); + throw_if_error(buffer_append(instructions, &num_strings, sizeof(u16))); throw_if_error(buffer_append(instructions, &strings_size, sizeof(u32))); for(; string != strings_end; ++string) { throw_if_error(buffer_append(instructions, &string->size, sizeof(u16))); @@ -383,17 +367,19 @@ static void add_instructions(BytecodeCompilerContext *self) { } #else + /* TODO: Keep all registers under 256 */ + while(instruction != instructions_end) { SsaInstruction ins = (SsaInstruction)*instruction++; switch(ins) { case SSA_ASSIGN_INTER: { instruction += ssa_extract_form1(instruction, &ssa_ins_form1); - add_ins3(self, AMAL_OP_MOVI, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "movi r%d, i%d"); + add_ins6(self, AMAL_OP_MOVI, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "movi r%d, i%d"); break; } case SSA_ASSIGN_STRING: { instruction += ssa_extract_form1(instruction, &ssa_ins_form1); - add_ins3(self, AMAL_OP_MOVD, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "movd r%d, s%d"); + add_ins6(self, AMAL_OP_MOVD, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "movd r%d, s%d"); break; } case SSA_ASSIGN_REG: { @@ -479,7 +465,7 @@ static void add_instructions(BytecodeCompilerContext *self) { /* Prepend instructions with its size */ { u32 instructions_size; - instructions_size = self->bytecode.data.size - num_instructions_index; + instructions_size = self->bytecode.data.size - num_instructions_index - sizeof(instructions_size); /* Remove the count itself from the size of the instructions size */ am_memcpy(&self->bytecode.data.data[num_instructions_index], &instructions_size, sizeof(instructions_size)); } @@ -488,7 +474,6 @@ static void add_instructions(BytecodeCompilerContext *self) { } void generate_bytecode_from_ssa(BytecodeCompilerContext *self) { - add_header(self); add_intermediates(self); add_strings(self); /* TODO: Also add strings in ssa, so we can index them */ diff --git a/src/compiler.c b/src/compiler.c index 1af9537..05fae78 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -446,6 +446,7 @@ static CHECK_RESULT int amal_compiler_dispatch_generic(amal_compiler *self, Thre } static CHECK_RESULT int amal_compiler_generate_program(amal_compiler *self) { + /* TODO: Copying the bytecode to the program can be done using multiple threads */ Parser **parser; Parser **parser_end; parser = buffer_begin(&self->parsers); diff --git a/src/program.c b/src/program.c index aa39a4c..167f4c4 100644 --- a/src/program.c +++ b/src/program.c @@ -1,23 +1,272 @@ #include "../include/program.h" +#include "../include/std/mem.h" +#include "../include/std/alloc.h" +#include "../include/std/log.h" #include <stdio.h> #include <errno.h> +#include <assert.h> -void amal_program_init(amal_program *self) { +/* TODO: If system is big-endian, then do endian conversion for all reads */ + +/* This matches SsaNumberType */ +typedef enum { + NUMBER_TYPE_INTEGER, + NUMBER_TYPE_FLOAT +} NumberType; + +typedef union { + i64 integer; + f64 floating; +} NumberUnion; + +int amal_program_init(amal_program *self) { ignore_result_int(buffer_init(&self->data, NULL)); + ignore_result_int(buffer_init(&self->string_indices, NULL)); + self->intermediates_start = NULL; + self->strings_start = NULL; + self->read_index = 0; + + /*doc(Bytecode header) + # Header layout + |Size|Name |Description | + |----|-------------|----------------------------------------------------------------------------| + |4 |Magic number |The magic number used to identify an amalgam bytecode file. | + |1 |Major version|The major version of the bytecode. Updates in this is a breaking change. | + |1 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.| + |1 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.| + The versions in the header only changes for every release, not every change. + */ + + const u32 magic_number = AMAL_PROGRAM_MAGIC_NUMBER; + const u8 major_version = AMAL_PROGRAM_MAJOR_VERSION; + const u8 minor_version = AMAL_PROGRAM_MINOR_VERSION; + const u8 patch_version = AMAL_PROGRAM_PATCH_VERSION; + + return_if_error(buffer_append(&self->data, &magic_number, 4)); + return_if_error(buffer_append(&self->data, &major_version, 1)); + return_if_error(buffer_append(&self->data, &minor_version, 1)); + return_if_error(buffer_append(&self->data, &patch_version, 1)); + + return 0; } void amal_program_deinit(amal_program *self) { buffer_deinit(&self->data); + buffer_deinit(&self->string_indices); } int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode) { return buffer_append(&self->data, bytecode->data.data, bytecode->data.size); } +static usize bytes_left_to_read(amal_program *self) { + assert(self->read_index <= self->data.size); + return self->data.size - self->read_index; +} + +static CHECK_RESULT int amal_program_read_header(amal_program *self) { + u32 magic_number; + u8 major_version; + u8 minor_version; + u8 patch_version; + + if(bytes_left_to_read(self) < sizeof(u32) + sizeof(u8) * 3) + return AMAL_PROGRAM_INVALID_HEADER; + + am_memcpy(&magic_number, &self->data.data[self->read_index], sizeof(magic_number)); + self->read_index += sizeof(u32); + am_memcpy(&major_version, &self->data.data[self->read_index], sizeof(major_version)); + self->read_index += sizeof(u8); + am_memcpy(&minor_version, &self->data.data[self->read_index], sizeof(minor_version)); + self->read_index += sizeof(u8); + am_memcpy(&patch_version, &self->data.data[self->read_index], sizeof(patch_version)); + self->read_index += sizeof(u8); + + if(magic_number != AMAL_PROGRAM_MAGIC_NUMBER) + return AMAL_PROGRAM_INVALID_MAGIC_NUMBER; + + /* + A program is only incompatible if the major version is newer than the version that is used to run it. + TODO: Implement backwards compatible reads, starting from when the program bytecode breaks backwards compatibility + */ + if(major_version > AMAL_PROGRAM_MAJOR_VERSION) + return AMAL_PROGRAM_INCOMPATIBLE; + + return AMAL_PROGRAM_OK; +} + +static CHECK_RESULT int amal_program_read_intermediates(amal_program *self) { + u32 intermediates_size; + /*u32 read_end;*/ + + if(bytes_left_to_read(self) < sizeof(intermediates_size)) { + amal_log_error("Not enough space in program to intermediates size"); + return AMAL_PROGRAM_INVALID_INTERMEDIATES; + } + + am_memcpy(&intermediates_size, &self->data.data[self->read_index], sizeof(intermediates_size)); + self->read_index += sizeof(intermediates_size); + + if(bytes_left_to_read(self) < intermediates_size) { + amal_log_error("Not enough space in program to read all intermediates"); + return AMAL_PROGRAM_INVALID_INTERMEDIATES_SIZE; + } + + self->intermediates_start = &self->data.data[self->read_index]; + /* + read_end = self->read_index + intermediates_size; + while(self->read_index < read_end) { + NumberType type; + NumberUnion value; + am_memcpy(&type, &self->data.data[self->read_index], sizeof(u8)); + am_memcpy(&value, &self->data.data[self->read_index + sizeof(u8)], sizeof(u64)); + self->read_index += sizeof(u8) + sizeof(u64); + } + */ + self->read_index += intermediates_size; + + return AMAL_PROGRAM_OK; +} + +static CHECK_RESULT int amal_program_read_strings(amal_program *self) { + u16 num_strings; + u32 strings_size; + u32 read_start; + u32 read_end; + u32 *string_index_ptr; + + if(bytes_left_to_read(self) < sizeof(num_strings)) + return AMAL_PROGRAM_INVALID_STRINGS; + + am_memcpy(&num_strings, &self->data.data[self->read_index], sizeof(num_strings)); + self->read_index += sizeof(num_strings); + + if(buffer_append_empty(&self->string_indices, num_strings) != 0) + return AMAL_PROGRAM_STRING_ALLOC_FAILURE; + string_index_ptr = (u32*)self->string_indices.data; + + if(bytes_left_to_read(self) < sizeof(strings_size)) + return AMAL_PROGRAM_INVALID_STRINGS; + + am_memcpy(&strings_size, &self->data.data[self->read_index], sizeof(strings_size)); + self->read_index += sizeof(strings_size); + + if(bytes_left_to_read(self) < strings_size) + return AMAL_PROGRAM_INVALID_STRINGS_SIZE; + + read_start = self->read_index; + read_end = read_start + strings_size; + self->strings_start = &self->data.data[self->read_index]; + while(self->read_index < read_end) { + u16 string_size; + + if(bytes_left_to_read(self) < sizeof(string_size)) + return AMAL_PROGRAM_INVALID_STRINGS; + + *string_index_ptr = self->read_index - read_start; + ++string_index_ptr; + am_memcpy(&string_size, &self->data.data[self->read_index], sizeof(string_size)); + self->read_index += sizeof(string_size); + + if(bytes_left_to_read(self) < string_size) + return AMAL_PROGRAM_INVALID_STRINGS; + + self->read_index += string_size; + } + + assert(self->read_index == read_end); + return AMAL_PROGRAM_OK; +} + +static CHECK_RESULT int amal_program_read_instructions(amal_program *self) { + u32 instructions_size; + u32 read_end; + + if(bytes_left_to_read(self) < sizeof(instructions_size)) + return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE; + + am_memcpy(&instructions_size, &self->data.data[self->read_index], sizeof(instructions_size)); + self->read_index += sizeof(instructions_size); + + if(bytes_left_to_read(self) < instructions_size) + return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE; + + read_end = self->read_index + instructions_size; + while(self->read_index < read_end) { + AmalOpcode opcode; + opcode = self->data.data[self->read_index]; + self->read_index += sizeof(AmalOpcodeType); + switch(opcode) { + case AMAL_OP_NOP: + break; + case AMAL_OP_SETZ: + self->read_index += 1; + break; + case AMAL_OP_MOV: + self->read_index += 2; + break; + case AMAL_OP_MOVI: + self->read_index += 3; + break; + case AMAL_OP_MOVD: + self->read_index += 3; + break; + case AMAL_OP_ADD: + self->read_index += 3; + break; + case AMAL_OP_SUB: + self->read_index += 3; + break; + case AMAL_OP_MUL: + self->read_index += 3; + break; + case AMAL_OP_DIV: + self->read_index += 3; + break; + case AMAL_OP_PUSH: + self->read_index += 1; + break; + case AMAL_OP_PUSHI: + self->read_index += 2; + break; + case AMAL_OP_PUSHD: + self->read_index += 2; + break; + case AMAL_OP_CALL: + self->read_index += 2; + break; + case AMAL_OP_CALLR: + self->read_index += 1; + break; + case AMAL_OP_CMP: + self->read_index += 3; + break; + case AMAL_OP_JZ: + self->read_index += 3; + break; + case AMAL_OP_JMP: + self->read_index += 2; + break; + case AMAL_OP_RET: + break; + case AMAL_OP_FUNC_START: + break; + case AMAL_OP_FUNC_END: + break; + } + } + + return AMAL_PROGRAM_OK; +} + int amal_program_run(amal_program *self) { - /* TODO: Implement */ - (void)self; - return 0; + return_if_error(amal_program_read_header(self)); + while(bytes_left_to_read(self) > 0) { + return_if_error(amal_program_read_intermediates(self)); + return_if_error(amal_program_read_strings(self)); + return_if_error(amal_program_read_instructions(self)); + } + return AMAL_PROGRAM_OK; } int amal_program_save(amal_program *self, const char *filepath) { |