#include "../include/program.h" #include "../include/std/mem.h" #include "../include/std/alloc.h" #include "../include/std/log.h" #include #include #include /* 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) { 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) { FILE *file; file = fopen(filepath, "wb"); if(!file) { int err; err = errno; perror(filepath); return -err; } if(fwrite(self->data.data, 1, self->data.size, file) != self->data.size) { int err; err = errno; perror(filepath); return -err; } fclose(file); return 0; }