267 lines
9.7 KiB
C
267 lines
9.7 KiB
C
#include "ibm.h"
|
|
#include "codegen.h"
|
|
#include "codegen_backend.h"
|
|
#include "codegen_ir_defs.h"
|
|
#include "codegen_reg.h"
|
|
|
|
uint8_t reg_last_version[IREG_COUNT];
|
|
uint8_t reg_version_refcount[IREG_COUNT][256];
|
|
|
|
ir_reg_t invalid_ir_reg = {IREG_INVALID};
|
|
|
|
ir_reg_t host_regs[CODEGEN_HOST_REGS];
|
|
static uint8_t host_reg_dirty[CODEGEN_HOST_REGS];
|
|
|
|
static uint8_t host_regs_locked;
|
|
|
|
enum
|
|
{
|
|
REG_BYTE,
|
|
REG_WORD,
|
|
REG_DWORD,
|
|
REG_QWORD,
|
|
REG_POINTER
|
|
};
|
|
|
|
struct
|
|
{
|
|
int native_size;
|
|
void *p;
|
|
} ireg_data[IREG_COUNT] =
|
|
{
|
|
[IREG_EAX] = {REG_DWORD, &EAX},
|
|
[IREG_ECX] = {REG_DWORD, &ECX},
|
|
[IREG_EDX] = {REG_DWORD, &EDX},
|
|
[IREG_EBX] = {REG_DWORD, &EBX},
|
|
[IREG_ESP] = {REG_DWORD, &ESP},
|
|
[IREG_EBP] = {REG_DWORD, &EBP},
|
|
[IREG_ESI] = {REG_DWORD, &ESI},
|
|
[IREG_EDI] = {REG_DWORD, &EDI},
|
|
|
|
[IREG_flags_op] = {REG_DWORD, &cpu_state.flags_op},
|
|
[IREG_flags_res] = {REG_DWORD, &cpu_state.flags_res},
|
|
[IREG_flags_op1] = {REG_DWORD, &cpu_state.flags_op1},
|
|
[IREG_flags_op2] = {REG_DWORD, &cpu_state.flags_op2},
|
|
|
|
[IREG_pc] = {REG_DWORD, &cpu_state.pc},
|
|
[IREG_oldpc] = {REG_DWORD, &cpu_state.oldpc},
|
|
|
|
[IREG_eaaddr] = {REG_DWORD, &cpu_state.eaaddr},
|
|
[IREG_ea_seg] = {REG_POINTER, &cpu_state.ea_seg},
|
|
|
|
[IREG_op32] = {REG_DWORD, &cpu_state.op32},
|
|
[IREG_ssegs] = {REG_BYTE, &cpu_state.ssegs},
|
|
|
|
[IREG_rm_mod_reg] = {REG_DWORD, &cpu_state.rm_data.rm_mod_reg_data},
|
|
|
|
/*Temporary registers are stored on the stack, and are not guaranteed to
|
|
be preserved across uOPs. They will not be written back if they will
|
|
not be read again.*/
|
|
[IREG_temp0] = {REG_DWORD, NULL},
|
|
[IREG_temp1] = {REG_DWORD, NULL},
|
|
[IREG_temp2] = {REG_DWORD, NULL},
|
|
[IREG_temp3] = {REG_DWORD, NULL}
|
|
};
|
|
|
|
void codegen_reg_reset()
|
|
{
|
|
int c;
|
|
|
|
for (c = 0; c < IREG_COUNT; c++)
|
|
{
|
|
reg_last_version[c] = 0;
|
|
reg_version_refcount[c][0] = 0;
|
|
}
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
host_regs[c] = invalid_ir_reg;
|
|
}
|
|
|
|
static inline int ir_reg_is_invalid(ir_reg_t ir_reg)
|
|
{
|
|
return (ir_reg.reg == IREG_INVALID);
|
|
}
|
|
|
|
static void codegen_reg_load(codeblock_t *block, int c, ir_reg_t ir_reg)
|
|
{
|
|
switch (ireg_data[ir_reg.reg].native_size)
|
|
{
|
|
case REG_DWORD:
|
|
codegen_direct_read_32(block, codegen_host_reg_list[c], ireg_data[ir_reg.reg].p);
|
|
break;
|
|
|
|
default:
|
|
fatal("codegen_reg_load - native_size=%i\n", ireg_data[ir_reg.reg].native_size);
|
|
}
|
|
|
|
host_regs[c] = ir_reg;
|
|
}
|
|
|
|
static void codegen_reg_writeback(codeblock_t *block, int c)
|
|
{
|
|
int ir_reg = host_regs[c].reg;
|
|
|
|
switch (ireg_data[ir_reg].native_size)
|
|
{
|
|
case REG_BYTE:
|
|
codegen_direct_write_8(block, ireg_data[ir_reg].p, codegen_host_reg_list[c]);
|
|
break;
|
|
|
|
case REG_DWORD:
|
|
codegen_direct_write_32(block, ireg_data[ir_reg].p, codegen_host_reg_list[c]);
|
|
break;
|
|
|
|
case REG_POINTER:
|
|
codegen_direct_write_ptr(block, ireg_data[ir_reg].p, codegen_host_reg_list[c]);
|
|
break;
|
|
|
|
default:
|
|
fatal("codegen_reg_flush - native_size=%i\n", ireg_data[ir_reg].native_size);
|
|
}
|
|
|
|
host_regs[c] = invalid_ir_reg;
|
|
}
|
|
|
|
void codegen_reg_alloc_register(ir_reg_t dest_reg_a, ir_reg_t src_reg_a, ir_reg_t src_reg_b)
|
|
{
|
|
int c;
|
|
int dest_reference = 0;
|
|
|
|
host_regs_locked = 0;
|
|
|
|
/* pclog("alloc_register: dst=%i.%i src_a=%i.%i src_b=%i.%i\n", dest_reg_a.reg, dest_reg_a.version,
|
|
src_reg_a.reg, src_reg_a.version,
|
|
src_reg_b.reg, src_reg_b.version);*/
|
|
|
|
if (!ir_reg_is_invalid(dest_reg_a))
|
|
{
|
|
if (!ir_reg_is_invalid(src_reg_a) && src_reg_a.reg == dest_reg_a.reg && src_reg_a.version == dest_reg_a.version-1)
|
|
dest_reference++;
|
|
if (!ir_reg_is_invalid(src_reg_b) && src_reg_b.reg == dest_reg_a.reg && src_reg_b.version == dest_reg_a.version-1)
|
|
dest_reference++;
|
|
}
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!ir_reg_is_invalid(src_reg_a) && host_regs[c].reg == src_reg_a.reg)
|
|
{
|
|
if (host_regs[c].version != src_reg_a.version)
|
|
fatal("codegen_reg_alloc_register - host_regs[c].version != src_reg_a.version\n");
|
|
host_regs_locked |= (1 << c);
|
|
}
|
|
if (!ir_reg_is_invalid(src_reg_b) && host_regs[c].reg == src_reg_b.reg)
|
|
{
|
|
if (host_regs[c].version != src_reg_b.version)
|
|
fatal("codegen_reg_alloc_register - host_regs[c].version != src_reg_b.version\n");
|
|
host_regs_locked |= (1 << c);
|
|
}
|
|
if (!ir_reg_is_invalid(dest_reg_a) && host_regs[c].reg == dest_reg_a.reg)
|
|
{
|
|
if (host_regs[c].version == dest_reg_a.version || (host_regs[c].version == (dest_reg_a.version-1) && reg_version_refcount[host_regs[c].reg][host_regs[c].version] == dest_reference))
|
|
host_regs_locked |= (1 << c);
|
|
else
|
|
fatal("codegen_reg_alloc_register - host_regs[c].version != dest_reg_a.version %i,%i %i\n", host_regs[c].version, dest_reg_a.version, dest_reference);
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_host_reg_t codegen_reg_alloc_read_reg(codeblock_t *block, ir_reg_t ir_reg)
|
|
{
|
|
int c;
|
|
|
|
/*Search for required register*/
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!ir_reg_is_invalid(host_regs[c]) && host_regs[c].reg == ir_reg.reg && host_regs[c].version == ir_reg.version)
|
|
break;
|
|
|
|
if (!ir_reg_is_invalid(host_regs[c]) && host_regs[c].reg == ir_reg.reg && reg_version_refcount[host_regs[c].reg][host_regs[c].version])
|
|
fatal("codegen_reg_alloc_read_reg - version mismatch!\n");
|
|
}
|
|
|
|
if (c == CODEGEN_HOST_REGS)
|
|
{
|
|
/*No unused registers. Search for an unlocked register*/
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!(host_regs_locked & (1 << c)))
|
|
break;
|
|
}
|
|
if (c == CODEGEN_HOST_REGS)
|
|
fatal("codegen_reg_alloc_read_reg - out of registers\n");
|
|
if (host_reg_dirty[c])
|
|
codegen_reg_writeback(block, c);
|
|
codegen_reg_load(block, c, ir_reg);
|
|
host_regs_locked |= (1 << c);
|
|
// fatal("codegen_reg_alloc_read_reg - read %i.%i to %i\n", ir_reg.reg,ir_reg.version, c);
|
|
// codegen_reg_writeback(block, c);
|
|
}
|
|
|
|
reg_version_refcount[host_regs[c].reg][host_regs[c].version]--;
|
|
if (reg_version_refcount[host_regs[c].reg][host_regs[c].version] < 0)
|
|
fatal("codegen_reg_alloc_read_reg - refcount < 0\n");
|
|
|
|
host_reg_dirty[c] = 0;
|
|
// pclog(" codegen_reg_alloc_read_reg: %i.%i %i\n", ir_reg.reg, ir_reg.version, codegen_host_reg_list[c]);
|
|
return codegen_host_reg_list[c];
|
|
}
|
|
|
|
ir_host_reg_t codegen_reg_alloc_write_reg(codeblock_t *block, ir_reg_t ir_reg)
|
|
{
|
|
int c;
|
|
|
|
/*Search for previous version in host register*/
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!ir_reg_is_invalid(host_regs[c]) && host_regs[c].reg == ir_reg.reg)
|
|
{
|
|
if (host_regs[c].version == ir_reg.version-1)
|
|
{
|
|
if (reg_version_refcount[host_regs[c].reg][host_regs[c].version] != 0)
|
|
fatal("codegen_reg_alloc_write_reg - previous version refcount != 0\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c == CODEGEN_HOST_REGS)
|
|
{
|
|
/*Search for unused registers*/
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (ir_reg_is_invalid(host_regs[c]))
|
|
break;
|
|
}
|
|
|
|
if (c == CODEGEN_HOST_REGS)
|
|
{
|
|
/*No unused registers. Search for an unlocked register*/
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!(host_regs_locked & (1 << c)))
|
|
break;
|
|
}
|
|
if (c == CODEGEN_HOST_REGS)
|
|
fatal("codegen_reg_alloc_write_reg - out of registers\n");
|
|
if (host_reg_dirty[c])
|
|
codegen_reg_writeback(block, c);
|
|
}
|
|
}
|
|
|
|
host_regs[c].reg = ir_reg.reg;
|
|
host_regs[c].version = ir_reg.version;
|
|
host_reg_dirty[c] = 1;
|
|
// pclog(" codegen_reg_alloc_write_reg: %i.%i %i\n", ir_reg.reg, ir_reg.version, codegen_host_reg_list[c]);
|
|
return codegen_host_reg_list[c];
|
|
}
|
|
|
|
void codegen_reg_flush(ir_data_t *ir, codeblock_t *block)
|
|
{
|
|
int c;
|
|
|
|
for (c = 0; c < CODEGEN_HOST_REGS; c++)
|
|
{
|
|
if (!ir_reg_is_invalid(host_regs[c]))
|
|
{
|
|
codegen_reg_writeback(block, c);
|
|
}
|
|
}
|
|
}
|