blob: 170964309fed4e1e277e94e663725a041f6a3350 [file] [log] [blame]
/*---------------------------------------------------------------*/
/*--- ---*/
/*--- This file (x86h_defs.c) is ---*/
/*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/
/*--- ---*/
/*---------------------------------------------------------------*/
#include <stdio.h>
#include <malloc.h>
#include "basictypes.h"
#include "host_regs.h"
#include "x86h_defs.h"
/* --------- Registers. --------- */
void ppHRegX86 ( FILE* f, HReg reg )
{
Int r;
static Char* ireg32_names[8]
= { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi" };
/* Be generic for all virtual regs. */
if (hregIsVirtual(reg)) {
ppHReg(f, reg);
return;
}
/* But specific for real regs. */
switch (hregClass(reg)) {
case HRcInt:
r = hregNumber(reg);
assert(r >= 0 && r < 8);
fprintf(f, "%s", ireg32_names[r]);
return;
case HRcFloat:
r = hregNumber(reg);
assert(r >= 0 && r < 6);
fprintf(f, "%%fake%d", r);
return;
case HRcVector:
panic("ppHRegX86: real vector reg");
default:
panic("ppHRegX86");
}
}
HReg hregX86_EAX ( void ) { return mkHReg(0, HRcInt, False); }
HReg hregX86_EBX ( void ) { return mkHReg(3, HRcInt, False); }
HReg hregX86_ECX ( void ) { return mkHReg(1, HRcInt, False); }
HReg hregX86_EDX ( void ) { return mkHReg(2, HRcInt, False); }
HReg hregX86_EBP ( void ) { return mkHReg(5, HRcInt, False); }
/* --------- X86AMode: memory address expressions. --------- */
X86AMode* X86AMode_IR ( UInt imm32, HReg reg ) {
X86AMode* am = malloc(sizeof(X86AMode));
am->tag = Xam_IR;
am->Xam.IR.imm = imm32;
am->Xam.IR.reg = reg;
return am;
}
X86AMode* X86AMode_IRRS ( UInt imm32, HReg base, HReg index, Int shift ) {
X86AMode* am = malloc(sizeof(X86AMode));
am->tag = Xam_IRRS;
am->Xam.IRRS.imm = imm32;
am->Xam.IRRS.base = base;
am->Xam.IRRS.index = index;
am->Xam.IRRS.shift = shift;
assert(shift >= 0 && shift <= 3);
return am;
}
void ppX86AMode ( FILE* f, X86AMode* am ) {
switch (am->tag) {
case Xam_IR:
fprintf(f, "0x%x(", am->Xam.IR.imm);
ppHRegX86(f, am->Xam.IR.reg);
fprintf(f, ")");
return;
case Xam_IRRS:
fprintf(f, "0x%x(", am->Xam.IRRS.imm);
ppHRegX86(f, am->Xam.IRRS.base);
fprintf(f, ",");
ppHRegX86(f, am->Xam.IRRS.index);
fprintf(f, ",%d)", am->Xam.IRRS.shift);
return;
default:
panic("ppX86AMode");
}
}
static void addRegUsage_X86AMode ( HRegUsage* u, X86AMode* am ) {
switch (am->tag) {
case Xam_IR:
addHRegUse(u, HRmRead, am->Xam.IR.reg);
return;
case Xam_IRRS:
addHRegUse(u, HRmRead, am->Xam.IRRS.base);
addHRegUse(u, HRmRead, am->Xam.IRRS.index);
return;
default:
panic("addRegUsage_X86AMode");
}
}
static void mapRegs_X86AMode ( HRegRemap* m, X86AMode* am ) {
switch (am->tag) {
case Xam_IR:
am->Xam.IR.reg = lookupHRegRemap(m, am->Xam.IR.reg);
return;
case Xam_IRRS:
am->Xam.IRRS.base = lookupHRegRemap(m, am->Xam.IRRS.base);
am->Xam.IRRS.index = lookupHRegRemap(m, am->Xam.IRRS.index);
return;
default:
panic("mapRegs_X86AMode");
}
}
/* --------- Operand, which can be reg, immediate or memory. --------- */
X86RMI* X86RMI_Imm ( UInt imm32 ) {
X86RMI* op = malloc(sizeof(X86RMI));
op->tag = Xrmi_Imm;
op->Xrmi.Imm.imm32 = imm32;
return op;
}
X86RMI* X86RMI_Reg ( HReg reg ) {
X86RMI* op = malloc(sizeof(X86RMI));
op->tag = Xrmi_Reg;
op->Xrmi.Reg.reg = reg;
return op;
}
X86RMI* X86RMI_Mem ( X86AMode* am ) {
X86RMI* op = malloc(sizeof(X86RMI));
op->tag = Xrmi_Mem;
op->Xrmi.Mem.am = am;
return op;
}
void ppX86RMI ( FILE* f, X86RMI* op ) {
switch (op->tag) {
case Xrmi_Imm:
fprintf(f, "$0x%x", op->Xrmi.Imm.imm32);
return;
case Xrmi_Reg:
ppHRegX86(f, op->Xrmi.Reg.reg);
return;
case Xrmi_Mem:
ppX86AMode(f, op->Xrmi.Mem.am);
return;
default:
panic("ppX86RMI");
}
}
/* An X86RMI can only be used in a "read" context (what would it mean
to write or modify a literal?) and so we enumerate its registers
accordingly. */
static void addRegUsage_X86RMI ( HRegUsage* u, X86RMI* op ) {
switch (op->tag) {
case Xrmi_Imm:
return;
case Xrmi_Reg:
addHRegUse(u, HRmRead, op->Xrmi.Reg.reg);
return;
case Xrmi_Mem:
addRegUsage_X86AMode(u, op->Xrmi.Mem.am);
return;
default:
panic("addRegUsage_X86RMI");
}
}
static void mapRegs_X86RMI ( HRegRemap* m, X86RMI* op ) {
switch (op->tag) {
case Xrmi_Imm:
return;
case Xrmi_Reg:
op->Xrmi.Reg.reg = lookupHRegRemap(m, op->Xrmi.Reg.reg);
return;
case Xrmi_Mem:
mapRegs_X86AMode(m, op->Xrmi.Mem.am);
return;
default:
panic("mapRegs_X86RMI");
}
}
/* --------- Operand, which can be reg or immediate only. --------- */
X86RI* X86RI_Imm ( UInt imm32 ) {
X86RI* op = malloc(sizeof(X86RI));
op->tag = Xri_Imm;
op->Xri.Imm.imm32 = imm32;
return op;
}
X86RI* X86RI_Reg ( HReg reg ) {
X86RI* op = malloc(sizeof(X86RI));
op->tag = Xri_Reg;
op->Xri.Reg.reg = reg;
return op;
}
void ppX86RI ( FILE* f, X86RI* op ) {
switch (op->tag) {
case Xri_Imm:
fprintf(f, "$0x%x", op->Xri.Imm.imm32);
return;
case Xri_Reg:
ppHRegX86(f, op->Xri.Reg.reg);
return;
default:
panic("ppX86RI");
}
}
/* An X86RI can only be used in a "read" context (what would it mean
to write or modify a literal?) and so we enumerate its registers
accordingly. */
static void addRegUsage_X86RI ( HRegUsage* u, X86RI* op ) {
switch (op->tag) {
case Xri_Imm:
return;
case Xri_Reg:
addHRegUse(u, HRmRead, op->Xri.Reg.reg);
return;
default:
panic("addRegUsage_X86RI");
}
}
static void mapRegs_X86RI ( HRegRemap* m, X86RI* op ) {
switch (op->tag) {
case Xri_Imm:
return;
case Xri_Reg:
op->Xri.Reg.reg = lookupHRegRemap(m, op->Xri.Reg.reg);
return;
default:
panic("mapRegs_X86RI");
}
}
/* --------- Operand, which can be reg or memory only. --------- */
X86RM* X86RM_Reg ( HReg reg ) {
X86RM* op = malloc(sizeof(X86RM));
op->tag = Xrm_Reg;
op->Xrm.Reg.reg = reg;
return op;
}
X86RM* X86RM_Mem ( X86AMode* am ) {
X86RM* op = malloc(sizeof(X86RM));
op->tag = Xrm_Mem;
op->Xrm.Mem.am = am;
return op;
}
void ppX86RM ( FILE* f, X86RM* op ) {
switch (op->tag) {
case Xrm_Mem:
ppX86AMode(f, op->Xrm.Mem.am);
return;
case Xrm_Reg:
ppHRegX86(f, op->Xrm.Reg.reg);
return;
default:
panic("ppX86RM");
}
}
/* Because an X86RM can be both a source or destination operand, we
have to supply a mode -- pertaining to the operand as a whole --
indicating how it's being used. */
static void addRegUsage_X86RM ( HRegUsage* u, X86RM* op, HRegMode mode ) {
switch (op->tag) {
case Xrm_Mem:
/* Memory is read, written or modified. So we just want to
know the regs read by the amode. */
addRegUsage_X86AMode(u, op->Xrm.Mem.am);
return;
case Xrm_Reg:
/* reg is read, written or modified. Add it in the
appropriate way. */
addHRegUse(u, mode, op->Xrm.Reg.reg);
return;
default:
panic("addRegUsage_X86RM");
}
}
static void mapRegs_X86RM ( HRegRemap* m, X86RM* op )
{
switch (op->tag) {
case Xrm_Mem:
mapRegs_X86AMode(m, op->Xrm.Mem.am);
return;
case Xrm_Reg:
op->Xrm.Reg.reg = lookupHRegRemap(m, op->Xrm.Reg.reg);
return;
default:
panic("mapRegs_X86RM");
}
}
/* --------- Instructions. --------- */
void ppX86AluOp ( FILE* f, X86AluOp op ) {
Char* name;
switch (op) {
case Xalu_MOV: name = "mov"; break;
case Xalu_ADD: name = "add"; break;
case Xalu_SUB: name = "sub"; break;
case Xalu_ADC: name = "adc"; break;
case Xalu_SBB: name = "sbb"; break;
case Xalu_AND: name = "and"; break;
case Xalu_OR: name = "or"; break;
case Xalu_XOR: name = "xor"; break;
default: panic("ppX86AluOp");
}
fprintf(f, "%s", name);
}
void ppX86ShiftOp ( FILE* f, X86ShiftOp op ) {
Char* name;
switch (op) {
case Xsh_SHL: name = "shl"; break;
case Xsh_SHR: name = "shr"; break;
case Xsh_SAR: name = "sar"; break;
case Xsh_ROL: name = "rol"; break;
case Xsh_ROR: name = "ror"; break;
default: panic("ppX86ShiftOp");
}
fprintf(f, "%s", name);
}
X86Instr* X86Instr_Alu32R ( X86AluOp op, X86RMI* src, HReg dst ) {
X86Instr* i = malloc(sizeof(X86Instr));
i->tag = Xin_Alu32R;
i->Xin.Alu32R.op = op;
i->Xin.Alu32R.src = src;
i->Xin.Alu32R.dst = dst;
return i;
}
X86Instr* X86Instr_Alu32M ( X86AluOp op, X86RI* src, X86AMode* dst ) {
X86Instr* i = malloc(sizeof(X86Instr));
i->tag = Xin_Alu32M;
i->Xin.Alu32M.op = op;
i->Xin.Alu32M.src = src;
i->Xin.Alu32M.dst = dst;
return i;
}
X86Instr* X86Instr_Sh32 ( X86ShiftOp op, UInt src, X86RM* dst ) {
X86Instr* i = malloc(sizeof(X86Instr));
i->tag = Xin_Sh32;
i->Xin.Sh32.op = op;
i->Xin.Sh32.src = src;
i->Xin.Sh32.dst = dst;
return i;
}
X86Instr* X86Instr_RET ( void ) {
X86Instr* i = malloc(sizeof(X86Instr));
i->tag = Xin_RET;
return i;
}
void ppX86Instr ( FILE* f, X86Instr* i ) {
switch (i->tag) {
case Xin_Alu32R:
ppX86AluOp(f, i->Xin.Alu32R.op);
fprintf(f, "l ");
ppX86RMI(f, i->Xin.Alu32R.src);
fprintf(f, ",");
ppHRegX86(f, i->Xin.Alu32R.dst);
return;
case Xin_Alu32M:
ppX86AluOp(f, i->Xin.Alu32M.op);
fprintf(f, "l ");
ppX86RI(f, i->Xin.Alu32M.src);
fprintf(f, ",");
ppX86AMode(f, i->Xin.Alu32M.dst);
return;
case Xin_Sh32:
ppX86ShiftOp(f, i->Xin.Sh32.op);
fprintf(f, "l ");
if (i->Xin.Sh32.src == 0)
fprintf(f, " %%cl,");
else
fprintf(f, " $%d,", i->Xin.Sh32.src);
ppX86RM(f, i->Xin.Sh32.dst);
return;
case Xin_RET:
fprintf(f, "ret");
return;
default:
panic("ppX86Instr");
}
}
/* --------- Helpers for register allocation. --------- */
void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i)
{
initHRegUsage(u);
switch (i->tag) {
case Xin_Alu32R:
addRegUsage_X86RMI(u, i->Xin.Alu32R.src);
if (i->Xin.Alu32R.op == Xalu_MOV)
addHRegUse(u, HRmWrite, i->Xin.Alu32R.dst);
else
addHRegUse(u, HRmModify, i->Xin.Alu32R.dst);
return;
case Xin_Alu32M:
addRegUsage_X86RI(u, i->Xin.Alu32M.src);
addRegUsage_X86AMode(u, i->Xin.Alu32M.dst);
return;
case Xin_Sh32:
addRegUsage_X86RM(u, i->Xin.Sh32.dst, HRmModify);
if (i->Xin.Sh32.src == 0)
addHRegUse(u, HRmRead, hregX86_ECX());
return;
case Xin_RET:
/* Using our calling conventions, %eax is live into a ret,
because we know the dispatcher -- to which we're returning
-- uses that value as the next guest address. Hmm. What
if we're simulating a 64-bit guest on an x86 host? */
addHRegUse(u, HRmRead, hregX86_EAX());
return;
default:
ppX86Instr(stderr, i);
panic("getRegUsage_X86Instr");
}
}
void mapRegs_X86Instr (HRegRemap* m, X86Instr* i)
{
switch (i->tag) {
case Xin_Alu32R:
mapRegs_X86RMI(m, i->Xin.Alu32R.src);
i->Xin.Alu32R.dst = lookupHRegRemap(m, i->Xin.Alu32R.dst);
return;
case Xin_Alu32M:
mapRegs_X86RI(m, i->Xin.Alu32M.src);
mapRegs_X86AMode(m, i->Xin.Alu32M.dst);
return;
case Xin_Sh32:
mapRegs_X86RM(m, i->Xin.Sh32.dst);
return;
case Xin_RET:
return;
default:
ppX86Instr(stderr, i);
panic("mapRegs_X86Instr");
}
}
Bool isMove_X86Instr ( X86Instr* i, HReg* src, HReg* dst )
{
if (i->tag != Xin_Alu32R)
return False;
if (i->Xin.Alu32R.op != Xalu_MOV)
return False;
if (i->Xin.Alu32R.src->tag != Xrmi_Reg)
return False;
*src = i->Xin.Alu32R.src->Xrmi.Reg.reg;
*dst = i->Xin.Alu32R.dst;
return True;
}
X86Instr* genSpill_X86 ( HReg rreg, Int offset )
{
assert(!hregIsVirtual(rreg));
switch (hregClass(rreg)) {
case HRcInt:
return
X86Instr_Alu32M ( Xalu_MOV, X86RI_Reg(rreg),
X86AMode_IR(offset + 0x1000,
hregX86_EBP()));
default:
ppHRegClass(stderr, hregClass(rreg));
panic("genSpill_X86: unimplemented regclass");
}
}
X86Instr* genReload_X86 ( HReg rreg, Int offset )
{
assert(!hregIsVirtual(rreg));
switch (hregClass(rreg)) {
case HRcInt:
return
X86Instr_Alu32R ( Xalu_MOV,
X86RMI_Mem(X86AMode_IR(offset + 0x1000,
hregX86_EBP())),
rreg );
default:
ppHRegClass(stderr, hregClass(rreg));
panic("genReload_X86: unimplemented regclass");
}
}