#include "build_header.h" #include "report.h" #include "resolve.h" #include #include #include #include #include typedef struct { char* ident; Loc loc; uint16_t ip; } GlobalSym; typedef struct { size_t id; char* ident; } ExternSym; typedef struct { size_t id; uint16_t ip; } ExternRef; #define DEFINE_VEC_FIELDS(T, NAME) \ size_t NAME##_capacity; \ T* NAME; \ size_t NAME##_size; struct HeaderBuilder { DEFINE_VEC_FIELDS(GlobalSym, global_syms) DEFINE_VEC_FIELDS(ExternSym, extern_syms) DEFINE_VEC_FIELDS(ExternRef, extern_refs) size_t program_size; }; #define VEC_CTOR_INIT(T, NAME, CAPACITY) \ .NAME##_capacity = (CAPACITY), .NAME = malloc(sizeof(T) * (CAPACITY)), \ .NAME##_size = 0 HeaderBuilder* header_builder_new(void) { const size_t global_syms_capacity = 8; const size_t extern_syms_capacity = 8; const size_t extern_refs_capacity = 8; HeaderBuilder* builder = malloc(sizeof(HeaderBuilder)); *builder = (HeaderBuilder) { VEC_CTOR_INIT(GlobalSym, global_syms, 8), VEC_CTOR_INIT(ExternSym, extern_syms, 8), VEC_CTOR_INIT(ExternRef, extern_refs, 8), .program_size = 0, }; return builder; } void header_builder_free(HeaderBuilder* builder) { for (size_t i = 0; i < builder->global_syms_size; ++i) { free(builder->global_syms[i].ident); } free(builder->global_syms); for (size_t i = 0; i < builder->extern_syms_size; ++i) { free(builder->extern_syms[i].ident); } free(builder->extern_syms); free(builder->extern_refs); free(builder); } #define VEC_PUSH(STRUCT, T, NAME, ...) \ if ((STRUCT)->NAME##_size + 1 > (STRUCT)->NAME##_capacity) { \ (STRUCT)->NAME##_capacity *= 2; \ (STRUCT)->NAME \ = realloc((STRUCT)->NAME, sizeof(T) * (STRUCT)->NAME##_capacity); \ } \ (STRUCT)->NAME[(STRUCT)->NAME##_size++] = (T) { __VA_ARGS__ }; void header_builder_add_global_sym( HeaderBuilder* builder, char* ident, Loc loc, uint16_t ip) { VEC_PUSH(builder, GlobalSym, global_syms, ident, loc, ip); } void header_builder_add_extern_sym( HeaderBuilder* builder, size_t id, char* ident) { VEC_PUSH(builder, ExternSym, extern_syms, id, ident); } void header_builder_add_extern_ref( HeaderBuilder* builder, size_t id, uint16_t ip) { VEC_PUSH(builder, ExternRef, extern_refs, id, ip); } int header_builder_resolve_global_syms( HeaderBuilder* builder, IdentResolver* resolver, Reporter* rep) { int result = 0; for (size_t i = 0; i < builder->global_syms_size; ++i) { const IdentResol* res = ident_resolver_resolve(resolver, builder->global_syms[i].ident); if (res == NULL) { REPORTF_ERROR( "undefined global '%s'", builder->global_syms[i].ident); REPORTF_INFO( "'%s' declared global here", builder->global_syms[i].ident); reporter_print_loc(rep, builder->global_syms[i].loc); result |= -1; continue; } else if (res->ty != IdentResolTy_Label) { REPORTF_ERROR("'%s' cannot be declared global", res->ident); reporter_print_loc(rep, res->loc); REPORTF_INFO( "'%s' declared global here", builder->global_syms[i].ident); reporter_print_loc(rep, builder->global_syms[i].loc); result |= -1; continue; } builder->global_syms[i].ip = res->ip; } return result; } typedef struct { FILE* fp; const char* filename; } Writer; static inline size_t write_words( Writer* writer, const uint16_t* values, size_t values_size) { size_t amount_written = fwrite(values, sizeof(uint16_t), values_size, writer->fp); if (amount_written != values_size) { REPORTF_ERROR("could not write to output file '%s': %s", writer->filename, strerror(errno)); return 0; } return amount_written; } static inline size_t write_word(Writer* writer, uint16_t value) { return write_words(writer, &value, 1); } int header_builder_write(const HeaderBuilder* builder, FILE* fp, size_t* total_written, const char* filename) { typedef uint16_t W; size_t header_size = 5 + 1 + 1 + 1 + 1 + 1; for (size_t i = 0; i < builder->global_syms_size; ++i) header_size += 1 + 1 + strlen(builder->global_syms[i].ident); for (size_t i = 0; i < builder->extern_syms_size; ++i) header_size += 1 + 1 + strlen(builder->extern_syms[i].ident); for (size_t i = 0; i < builder->extern_refs_size; ++i) header_size += 1 + 1; header_size *= 2; #define TRY_WRITE_WORDS(VALUES, VALUES_SIZE) \ do { \ if ((res = write_words(w, (VALUES), (VALUES_SIZE))) == 0) \ return -1; \ *total_written += res; \ } while (0) #define TRY_WRITE_WORD(VALUE) \ do { \ if ((res = write_word(w, (VALUE))) == 0) \ return -1; \ *total_written += res; \ } while (0) Writer writer = { fp, filename }; Writer* w = &writer; *total_written = 0; size_t res; const char* ident = "vc3-object"; TRY_WRITE_WORDS((W*)ident, 5); TRY_WRITE_WORD((W)header_size); TRY_WRITE_WORD((W)builder->global_syms_size); for (size_t i = 0; i < builder->global_syms_size; ++i) { const GlobalSym* sym = &builder->global_syms[i]; TRY_WRITE_WORD(sym->ip); size_t ident_len = strlen(sym->ident); TRY_WRITE_WORD((W)ident_len); // This is safe because of null termination. TRY_WRITE_WORDS((W*)sym->ident, (ident_len + 1) / 2); } TRY_WRITE_WORD((W)builder->extern_syms_size); for (size_t i = 0; i < builder->extern_syms_size; ++i) { const ExternSym* sym = &builder->extern_syms[i]; TRY_WRITE_WORD((W)sym->id); size_t ident_len = strlen(sym->ident); TRY_WRITE_WORD((W)ident_len); // This is safe because of null termination. TRY_WRITE_WORDS((W*)sym->ident, (ident_len + 1) / 2); } TRY_WRITE_WORD((W)builder->extern_refs_size); for (size_t i = 0; i < builder->extern_refs_size; ++i) { const ExternRef* ref = &builder->extern_refs[i]; TRY_WRITE_WORD((W)ref->id); TRY_WRITE_WORD(ref->ip); } TRY_WRITE_WORD((uint16_t)(builder->program_size * 2)); #undef TRY_WRITE_WORDS #undef TRY_WRITE_WORD return 0; } void header_builder_print(const HeaderBuilder* builder) { printf("globals:\n"); printf("address symbol\n"); printf("-----------------------\n"); for (size_t i = 0; i < builder->global_syms_size; ++i) { GlobalSym* sym = &builder->global_syms[i]; printf("%04x, %s\n", sym->ip, sym->ident); } printf("\nexterns:\n"); printf("id symbol\n"); printf("--------------------\n"); for (size_t i = 0; i < builder->extern_syms_size; ++i) { ExternSym* sym = &builder->extern_syms[i]; printf("%-3ld %s\n", sym->id, sym->ident); } printf("\nextern uses:\n"); printf("id address\n"); printf("-----------\n"); for (size_t i = 0; i < builder->extern_refs_size; ++i) { ExternRef* ref = &builder->extern_refs[i]; printf("%3ld %04x\n", ref->id, ref->ip); } }