242 lines
6.7 KiB
C
242 lines
6.7 KiB
C
#include "flat_register.h"
|
|
#include "../format.h"
|
|
#include "../x86encode.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct fr_label_info {
|
|
symbol symbol;
|
|
uint8_t argc;
|
|
fr_type types[MAX_ARGS];
|
|
} fr_label_info;
|
|
|
|
typedef struct fr_var_info {
|
|
fr_type type;
|
|
_Bool literal;
|
|
uint64_t value;
|
|
} fr_var_info;
|
|
|
|
static size_t fr_labelc = 0;
|
|
static fr_label_info fr_labels[MAX_LABELS];
|
|
static size_t fr_varc = 0;
|
|
static fr_var_info fr_vars[MAX_VARS];
|
|
static uint32_t fr_var_offset = 0;
|
|
|
|
static fr_var fr_push(fr_type type) {
|
|
fr_var_info* info = &fr_vars[fr_varc];
|
|
info->type = type;
|
|
info->literal = false;
|
|
info->value = fr_var_offset;
|
|
fr_var_offset += type.size;
|
|
return fr_varc++;
|
|
}
|
|
|
|
fr_label fr_declare(size_t typec, fr_type* types) {
|
|
fr_label_info* info = &fr_labels[fr_labelc];
|
|
info->symbol = new_symbol();
|
|
info->argc = typec;
|
|
memcpy(info->types, types, typec * sizeof(fr_type));
|
|
return fr_labelc++;
|
|
}
|
|
|
|
|
|
void fr_define(fr_label l, fr_var* vars) {
|
|
fr_label_info label = fr_labels[l];
|
|
fr_varc = 0;
|
|
fr_var_offset = 0;
|
|
for (uint8_t i = 0; i < label.argc; i++) {
|
|
vars[i] = fr_push(label.types[i]);
|
|
}
|
|
}
|
|
|
|
fr_var fr_lit(fr_type type, uint64_t val) {
|
|
fr_var_info* info = &fr_vars[fr_varc];
|
|
info->type = type;
|
|
info->literal = true;
|
|
info->value = val;
|
|
return fr_varc++;
|
|
}
|
|
|
|
// TODO: register allocation instead of infinitely-growing stack
|
|
void fr_load_reg(reg reg, fr_var v) {
|
|
fr_var_info var = fr_vars[v];
|
|
if (var.literal && var.type.size == 8) {
|
|
x86_inst_mov_r64_imm(reg, var.value);
|
|
} else if (var.type.size == 8 || var.type.tag == FR_BOX) {
|
|
x86_inst_mov_r64_m64_disp(reg, BP, var.value);
|
|
} else if (var.literal && var.type.size == 1) {
|
|
x86_inst_mov_r8_m8_disp(reg, BP, var.value);
|
|
} else if (var.type.size == 1) {
|
|
x86_inst_mov_r8_imm8(reg, var.value);
|
|
} else if (var.type.tag == FR_REF) {
|
|
x86_inst_lea_r64_m64_disp(reg, BP, var.value);
|
|
} else {
|
|
fprintf(stderr, "unsupported variable size, for now\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void fr_store_reg(reg reg, fr_var v) {
|
|
fr_var_info var = fr_vars[v];
|
|
if (var.type.size == 8 || var.type.tag == FR_BOX) {
|
|
x86_inst_mov_m64_r64_disp(reg, BP, var.value);
|
|
} else if (var.type.size == 1) {
|
|
x86_inst_mov_m8_r8_disp(reg, BP, var.value);
|
|
} else {
|
|
fprintf(stderr, "unsupported variable size, for now\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
fr_var fr_index(fr_var box, fr_var index) {
|
|
// TODO: optimized constant multiplies
|
|
// TODO: fuse into indirect addressing modes
|
|
fr_load_reg(AX, index);
|
|
x86_inst_mov_r64_imm32(DX, fr_vars[index].type.size);
|
|
x86_inst_mul_r64(DX);
|
|
fr_type type = {
|
|
.tag = FR_BOX,
|
|
.size = 8,
|
|
};
|
|
fr_var var = fr_push(type);
|
|
fr_store_reg(AX, var);
|
|
return var;
|
|
}
|
|
|
|
void fr_set(fr_var box, fr_var index, fr_var ref) {
|
|
// TODO: use fused addressing modes, optimize for small types
|
|
// TODO: use `lea`, optimize for constant index
|
|
fr_load_reg(AX, index);
|
|
fr_load_reg(DI, box);
|
|
fr_load_reg(SI, ref);
|
|
x86_inst_mov_r64_imm(CX, fr_vars[box].type.size);
|
|
x86_inst_mul_r64(CX);
|
|
x86_inst_add_r64_r64(DI, AX);
|
|
x86_inst_rep_movsb();
|
|
}
|
|
|
|
fr_var fr_load(fr_var box, fr_type type, uint32_t offset) {
|
|
// TODO: optimized copy for small (i.e. almost all) types
|
|
fr_var dest = fr_push(type);
|
|
fr_load_reg(SI, box);
|
|
x86_inst_mov_r64_imm(CX, type.size);
|
|
x86_inst_lea_r64_m64_disp(DI, BP, offset);
|
|
x86_inst_rep_movsb();
|
|
return dest;
|
|
}
|
|
|
|
fr_var fr_struct(uint32_t memberc, fr_var* members) {
|
|
uint32_t size = 0;
|
|
for (uint32_t i = 0; i < memberc; i++) {
|
|
size += fr_vars[members[i]].type.size;
|
|
}
|
|
fr_type type = {
|
|
.tag = FR_REF,
|
|
.size = size,
|
|
};
|
|
fr_var var = fr_push(type);
|
|
uint32_t offset = fr_vars[var].value;
|
|
|
|
// TODO: optimized copy for small (i.e. almost all) types
|
|
for (uint32_t i = 0; i < memberc; i++) {
|
|
fr_var_info member = fr_vars[members[i]];
|
|
fr_load_reg(AX, members[i]);
|
|
if (member.type.size == 1) {
|
|
x86_inst_mov_m8_r8_disp(AX, BP, offset);
|
|
offset += 1;
|
|
} else if (member.type.size == 8 || member.type.tag == FR_BOX) {
|
|
x86_inst_mov_m64_r64_disp(AX, BP, offset);
|
|
offset += 8;
|
|
} else {
|
|
x86_inst_lea_r64_m64_disp(SI, BP, member.value);
|
|
x86_inst_lea_r64_m64_disp(DI, BP, offset);
|
|
x86_inst_mov_r64_imm(CX, member.type.size);
|
|
x86_inst_rep_movsb();
|
|
}
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
static void fr_prepare_jump(size_t argc, fr_var* args) {
|
|
// TODO: avoid unnecessary copying
|
|
fr_var scratch = fr_struct(argc, args);
|
|
fr_load_reg(SI, scratch);
|
|
x86_inst_mov_r64_r64(DI, BP);
|
|
x86_inst_mov_r64_imm(CX, fr_vars[scratch].type.size);
|
|
x86_inst_rep_movsb();
|
|
}
|
|
|
|
void inst_jump(symbol sym) {
|
|
int32_t disp = symbol_offset(sym, X86_JMP_DISP8_SIZE);
|
|
if (disp >= INT8_MIN && disp <= INT8_MAX) {
|
|
x86_inst_jmp_disp8(disp);
|
|
} else {
|
|
x86_inst_jmp_disp32_op();
|
|
relocate_pc32(sym);
|
|
}
|
|
// TODO: support 64-bit jumps?
|
|
}
|
|
|
|
void inst_jump_if_zero(symbol sym) {
|
|
int32_t disp = symbol_offset(sym, X86_JZ_DISP8_SIZE);
|
|
if (disp >= INT8_MIN && disp <= INT8_MAX) {
|
|
x86_inst_jz_disp8(disp);
|
|
} else {
|
|
x86_inst_jz_disp32_op();
|
|
relocate_pc32(sym);
|
|
}
|
|
}
|
|
|
|
|
|
void inst_jump_if_not_zero(symbol sym) {
|
|
int32_t disp = symbol_offset(sym, X86_JNZ_DISP8_SIZE);
|
|
if (disp >= INT8_MIN && disp <= INT8_MAX) {
|
|
x86_inst_jnz_disp8(disp);
|
|
} else {
|
|
x86_inst_jnz_disp32_op();
|
|
relocate_pc32(sym);
|
|
}
|
|
}
|
|
|
|
void fr_if(fr_cond cond, fr_var x, fr_var y, fr_label ifl, fr_label elsel, size_t argc, fr_var* args) {
|
|
fr_load_reg(BX, x);
|
|
fr_load_reg(DX, y);
|
|
fr_prepare_jump(argc, args);
|
|
x86_inst_cmp_r64_r64(BX, DX);
|
|
symbol ifs = fr_labels[ifl].symbol;
|
|
symbol elses = fr_labels[elsel].symbol;
|
|
switch (cond) {
|
|
case FR_EQ:
|
|
inst_jump_if_zero(ifs);
|
|
inst_jump(elses);
|
|
break;
|
|
case FR_NE:
|
|
inst_jump_if_not_zero(ifs);
|
|
inst_jump(elses);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void fr_switch(fr_var index, size_t labelc, fr_label* labels, size_t argc, fr_var* args) {
|
|
// TODO:
|
|
/*
|
|
symbol table = new_symbol();
|
|
fr_load_reg(CX, index);
|
|
x86_inst_lea_r64_ip_disp32(AX, X86_JMP_M64_SIZE);
|
|
x86_inst_add_r64_r64(AX, CX);
|
|
x86_inst_jmp_m64(
|
|
*/
|
|
fprintf(stderr, "jump tables not yet implemented\n");
|
|
exit(1);
|
|
}
|
|
|
|
void fr_jump(fr_label label, size_t argc, fr_var* args) {
|
|
fr_prepare_jump(argc, args);
|
|
inst_jump(fr_labels[label].symbol);
|
|
}
|