blob: b62f3f8e7611948a2207b32ef1a7037b0983ad12 [file] [log] [blame]
/*---------------------------------------------------------------*/
/*--- ---*/
/*--- This file (isel_x86.c) is ---*/
/*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/
/*--- ---*/
/*---------------------------------------------------------------*/
#include <stdio.h>
#include <malloc.h>
#include "basictypes.h"
#include "ir_defs.h"
#include "host_regs.h"
#include "x86h_defs.h"
/*---------------------------------------------------------*/
/*--- ISelEnv ---*/
/*---------------------------------------------------------*/
/* This carries around:
- A mapping from IRTemp to IRType, giving the type of any IRTemp we
might encounter. This is computed before insn selection starts,
and does not change.
- A mapping from IRTemp to HReg. This tells the insn selector
which virtual register is associated with each IRTemp temporary.
This is computed before insn selection starts, and does not
change. We expect this mapping to map precisely the same set
of IRTemps as the type mapping does.
- The code array, that is, the insns selected so far.
- A counter, for generating new virtual registers.
Note, this is all host-independent.
*/
typedef
struct {
IRTemp ir_name;
HReg vreg;
}
VRegMaplet;
typedef
struct {
IRTypeEnv* type_env;
VRegMaplet* vregmap;
Int n_vregmap;
HInstrArray* code;
Int vreg_ctr;
}
ISelEnv;
static HReg lookupIRTemp ( ISelEnv* env, IRTemp tmp )
{
Int i;
for (i = 0; i < env->n_vregmap; i++)
if (env->vregmap[i].ir_name == tmp)
return env->vregmap[i].vreg;
panic("lookupIRTemp");
}
static void addInstr ( ISelEnv* env, X86Instr* instr )
{
addHInstr(env->code, instr);
ppX86Instr(stdout, instr);
printf("\n");
}
static HReg newVRegI ( ISelEnv* env )
{
HReg reg = mkHReg(env->vreg_ctr, HRcInt, True/*virtual reg*/);
env->vreg_ctr++;
return reg;
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expressions ---*/
/*---------------------------------------------------------*/
/* forwards ... */
static X86RMI* iselIntExpr_RMI ( ISelEnv* env, IRExpr* e );
static X86Instr* mk_MOV_RR ( HReg src, HReg dst )
{
assert(hregClass(src) == HRcInt);
assert(hregClass(dst) == HRcInt);
return X86Instr_Alu32R(Xalu_MOV, X86RMI_Reg(src), dst);
}
/* Select insns for an integer-typed expression, and add them to the
code list. Return a vreg holding the result. The vreg MUST NOT BE
MODIFIED. If you want to modify it, ask for a new vreg, copy it in
there, and modify the copy. The register allocator will do its
best to map both vregs to the same real register, so the copies
will often disappear later in the game.
*/
static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e )
{
assert(e);
assert(typeOfIRExpr(env->type_env,e) == Ity_I32);
switch (e->tag) {
case Iex_Tmp:
return lookupIRTemp(env, e->Iex.Tmp.tmp);
case Iex_Binop:
/* Add32(x,y). For commutative ops we assume any literal
values are on the second operand. */
if (e->Iex.Binop.op == Iop_Add32) {
HReg dst = newVRegI(env);
HReg reg = iselIntExpr_R(env, e->Iex.Binop.arg1);
X86RMI* rmi = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
addInstr(env, mk_MOV_RR(reg,dst));
addInstr(env, X86Instr_Alu32R(Xalu_ADD, rmi, dst));
return dst;
}
if (e->Iex.Binop.op == Iop_Shl32) {
HReg dst = newVRegI(env);
HReg regL = iselIntExpr_R(env, e->Iex.Binop.arg1);
HReg regR = iselIntExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, mk_MOV_RR(regL,dst));
addInstr(env, mk_MOV_RR(regR,hregX86_ECX()));
addInstr(env, X86Instr_Sh32(Xsh_SHL, 0/* %cl */, X86RM_Reg(dst)));
return dst;
}
#if 0
/* 32-bit literals */
case Iex_Const: {
switch (e->Iex.Const.con->tag) {
case Ico_U32: {
HReg r = newVRegI(env);
addInstr(env,
X86Instr_Mov32(X86Operand_Imm(e->Iex.Const.con->Ico.U32),
X86Operand_Reg(r)));
return r;
}
default: break;
}
}
#endif
default:
break;
} /* switch (e->tag) */
/* We get here if no pattern matched. */
ppIRExpr(stderr, e);
panic("iselExprI: cannot reduce tree");
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expression auxiliaries ---*/
/*---------------------------------------------------------*/
/* Return an AMode which computes the value of the specified
expression, possibly also adding insns to the code list as a
result.
*/
static X86AMode* iselIntExpr_AMode ( ISelEnv* env, IRExpr* e )
{
assert(e);
assert(typeOfIRExpr(env->type_env,e) == Ity_I32);
/* Add32(expr1, Shl32(expr2, imm)) */
if (e->tag == Iex_Binop
&& e->Iex.Binop.op == Iop_Add32
&& e->Iex.Binop.arg2->tag == Iex_Binop
&& e->Iex.Binop.arg2->Iex.Binop.op == Iop_Shl32
&& e->Iex.Binop.arg2->Iex.Binop.arg2->tag == Iex_Const
&& e->Iex.Binop.arg2->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U32) {
UInt shift = e->Iex.Binop.arg2->Iex.Binop.arg2->Iex.Const.con->Ico.U32;
if (shift == 2 || shift == 4 || shift == 8) {
HReg r1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
HReg r2 = iselIntExpr_R(env, e->Iex.Binop.arg2->Iex.Binop.arg1 );
return X86AMode_IRRS(0, r1, r2, shift);
}
}
/* Add32(expr,i) */
if (e->tag == Iex_Binop
&& e->Iex.Binop.op == Iop_Add32
&& e->Iex.Binop.arg2->tag == Iex_Const
&& e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U32) {
HReg r1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
return X86AMode_IR(r1, e->Iex.Binop.arg2->Iex.Const.con->Ico.U32);
}
/* Doesn't match anything in particular. Generate it into
a register and use that. */
{
HReg r1 = iselIntExpr_R(env, e);
return X86AMode_IR(r1, 0);
}
}
/* Similarly, calculate an expression into an X86RMI operand. */
static X86RMI* iselIntExpr_RMI ( ISelEnv* env, IRExpr* e )
{
assert(e);
assert(typeOfIRExpr(env->type_env,e) == Ity_I32);
/* special case: immediate */
if (e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U32) {
return X86RMI_Imm(e->Iex.Const.con->Ico.U32);
}
/* special case: load from memory */
/* default case: calculate into a register and return that */
{
HReg r = iselIntExpr_R ( env, e );
return X86RMI_Reg(r);
}
}
/* Calculate an expression into an X86RI operand. */
static X86RI* iselIntExpr_RI ( ISelEnv* env, IRExpr* e )
{
assert(e);
assert(typeOfIRExpr(env->type_env,e) == Ity_I32);
/* special case: immediate */
if (e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U32) {
return X86RI_Imm(e->Iex.Const.con->Ico.U32);
}
/* default case: calculate into a register and return that */
{
HReg r = iselIntExpr_R ( env, e );
return X86RI_Reg(r);
}
}
/*---------------------------------------------------------*/
/*--- ISEL: Statements ---*/
/*---------------------------------------------------------*/
void iselStmt ( ISelEnv* env, IRStmt* stmt )
{
fprintf(stdout, "-- ");
ppIRStmt(stdout, stmt);
fprintf(stdout, "\n");
switch (stmt->tag) {
case Ist_Put:
if (stmt->Ist.Put.size == 4) {
/* We're going to write to memory, so compute the
RHS into an X86RI. */
X86RI* ri = iselIntExpr_RI(env, stmt->Ist.Put.expr);
addInstr(env,
X86Instr_Alu32M(
Xalu_MOV,
ri,
X86AMode_IR(stmt->Ist.Put.offset,hregX86_EBP())
));
return;
}
case Ist_Tmp: {
IRTemp tmp = stmt->Ist.Tmp.tmp;
IRType ty = lookupIRTypeEnv(env->type_env, tmp);
if (ty == Ity_I32) {
X86RMI* rmi = iselIntExpr_RMI(env, stmt->Ist.Tmp.expr);
HReg dst = lookupIRTemp(env, tmp);
addInstr(env,
X86Instr_Alu32R(Xalu_MOV,rmi,dst));
return;
}
}
default: break;
}
ppIRStmt(stderr, stmt);
panic("iselStmt");
}
/*---------------------------------------------------------*/
/*--- ISEL: Basic block terminators (Nexts) ---*/
/*---------------------------------------------------------*/
void iselNext ( ISelEnv* env, IRNext* next )
{
fprintf(stdout, "-- ");
ppIRNext(stdout, next);
fprintf(stdout, "\n");
switch (next->tag) {
case Inx_UJump: {
assert(next->Inx.UJump.dst->tag == Ico_U32);
addInstr(env, X86Instr_Alu32R(
Xalu_MOV,
X86RMI_Imm(next->Inx.UJump.dst->Ico.U32),
hregX86_EAX()));
addInstr(env, X86Instr_RET());
return;
}
default:
ppIRNext(stderr, next);
panic("iselNext");
}
}
/*---------------------------------------------------------*/
/*--- Insn selector top-level ---*/
/*---------------------------------------------------------*/
/* Translate an entire BB to x86 code. */
HInstrArray* iselBB_X86Instr ( IRBB* bb )
{
Int i;
HReg hreg;
IRStmt* stmt;
/* Make up an initial environment to use. */
ISelEnv* env = malloc(sizeof(ISelEnv));
env->vreg_ctr = 0;
/* Set up output code array. */
env->code = newHInstrArray();
/* Copy BB's type env. */
env->type_env = bb->tyenv;
/* Make up an IRTemp -> virtual HReg mapping. This doesn't
change as we go along. */
env->n_vregmap = bb->tyenv->map_used;
env->vregmap = malloc(env->n_vregmap * sizeof(VRegMaplet));
/* For each IR temporary, allocate a suitably-kinded virtual
register. */
for (i = 0; i < env->n_vregmap; i++) {
env->vregmap[i].ir_name = bb->tyenv->map[i].name;
switch (bb->tyenv->map[i].type) {
case Ity_I32: hreg = mkHReg(i, HRcInt, True); break;
default: panic("iselBB: IRTemp type");
}
env->vregmap[i].vreg = hreg;
}
env->vreg_ctr = env->n_vregmap;
/* Ok, finally we can iterate over the statements. */
for (stmt = bb->stmts; stmt; stmt=stmt->link)
iselStmt(env,stmt);
iselNext(env,bb->next);
/* record the number of vregs we used. */
env->code->n_vregs = env->vreg_ctr;
return env->code;
}