249 lines
8.2 KiB
C
249 lines
8.2 KiB
C
#include "build_header.h"
|
|
#include "report.h"
|
|
#include "resolve.h"
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
char* ident;
|
|
Loc loc;
|
|
uint16_t ip;
|
|
} HeaderGlobalSym;
|
|
|
|
typedef struct {
|
|
size_t id;
|
|
char* ident;
|
|
} HeaderExternSym;
|
|
|
|
typedef struct {
|
|
size_t id;
|
|
uint16_t ip;
|
|
} HeaderExternRef;
|
|
|
|
#define DEFINE_VEC_FIELDS(T, NAME) \
|
|
size_t NAME##_capacity; \
|
|
T* NAME; \
|
|
size_t NAME##_size;
|
|
|
|
struct HeaderBuilder {
|
|
DEFINE_VEC_FIELDS(HeaderGlobalSym, global_syms)
|
|
DEFINE_VEC_FIELDS(HeaderExternSym, extern_syms)
|
|
DEFINE_VEC_FIELDS(HeaderExternRef, 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(HeaderGlobalSym, global_syms, 8),
|
|
VEC_CTOR_INIT(HeaderExternSym, extern_syms, 8),
|
|
VEC_CTOR_INIT(HeaderExternRef, 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, HeaderGlobalSym, global_syms, ident, loc, ip);
|
|
}
|
|
|
|
void header_builder_add_extern_sym(
|
|
HeaderBuilder* builder, size_t id, char* ident)
|
|
{
|
|
VEC_PUSH(builder, HeaderExternSym, extern_syms, id, ident);
|
|
}
|
|
|
|
void header_builder_add_extern_ref(
|
|
HeaderBuilder* builder, size_t id, uint16_t ip)
|
|
{
|
|
VEC_PUSH(builder, HeaderExternRef, 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) + 1) / 2;
|
|
for (size_t i = 0; i < builder->extern_syms_size; ++i)
|
|
header_size += 1 + 1 + (strlen(builder->extern_syms[i].ident) + 1) / 2;
|
|
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 HeaderGlobalSym* 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 HeaderExternSym* 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 HeaderExternRef* 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) {
|
|
HeaderGlobalSym* 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) {
|
|
HeaderExternSym* 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) {
|
|
HeaderExternRef* ref = &builder->extern_refs[i];
|
|
printf("%3ld %04x\n", ref->id, ref->ip);
|
|
}
|
|
}
|