blob: 94d47e451111f350fe3c413e7f4aa5d13e48c490 [file] [log] [blame]
/*---------------------------------------------------------------*/
/*--- ---*/
/*--- This file (host-x86/isel.c) is ---*/
/*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/
/*--- ---*/
/*---------------------------------------------------------------*/
/*
This file is part of LibVEX, a library for dynamic binary
instrumentation and translation.
Copyright (C) 2004 OpenWorks, LLP.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; Version 2 dated June 1991 of the
license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or liability
for damages. See the GNU General Public License for more details.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "main/vex_util.h"
#include "main/vex_globals.h"
#include "host-generic/h_generic_regs.h"
#include "host-arm/hdefs.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(s) are 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.
- vregmap holds the primary register for the IRTemp.
- 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 {
IRTypeEnv* type_env;
HReg* vregmap;
Int n_vregmap;
HInstrArray* code;
Int vreg_ctr;
}
ISelEnv;
static HReg lookupIRTemp ( ISelEnv* env, IRTemp tmp )
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
return env->vregmap[tmp];
}
static void addInstr ( ISelEnv* env, ARMInstr* instr )
{
addHInstr(env->code, instr);
if (vex_traceflags & VEX_TRACE_VCODE) {
ppARMInstr(instr);
vex_printf("\n");
}
}
static HReg newVRegI ( ISelEnv* env )
{
HReg reg = mkHReg(env->vreg_ctr, HRcInt32, True/*virtual reg*/);
env->vreg_ctr++;
return reg;
}
/*---------------------------------------------------------*/
/*--- ISEL: Forward declarations ---*/
/*---------------------------------------------------------*/
/* These are organised as iselXXX and iselXXX_wrk pairs. The
iselXXX_wrk do the real work, but are not to be called directly.
For each XXX, iselXXX calls its iselXXX_wrk counterpart, then
checks that all returned registers are virtual. You should not
call the _wrk version directly.
*/
static ARMAMode1* iselIntExpr_AMode1_wrk ( ISelEnv* env, IRExpr* e );
static ARMAMode1* iselIntExpr_AMode1 ( ISelEnv* env, IRExpr* e );
static ARMAMode2* iselIntExpr_AMode2_wrk ( ISelEnv* env, IRExpr* e );
static ARMAMode2* iselIntExpr_AMode2 ( ISelEnv* env, IRExpr* e );
static ARMAMode3* iselIntExpr_AMode3_wrk ( ISelEnv* env, IRExpr* e );
static ARMAMode3* iselIntExpr_AMode3 ( ISelEnv* env, IRExpr* e );
static ARMBranchDest* iselIntExpr_BD_wrk ( ISelEnv* env, IRExpr* e );
static ARMBranchDest* iselIntExpr_BD ( ISelEnv* env, IRExpr* e );
static ARMCondCode iselCondCode_wrk ( ISelEnv* env, IRExpr* e );
static ARMCondCode iselCondCode ( ISelEnv* env, IRExpr* e );
static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e );
#if 0
static void iselInt64Expr_wrk ( HReg* rHi, HReg* rLo,
ISelEnv* env, IRExpr* e );
static void iselInt64Expr ( HReg* rHi, HReg* rLo,
ISelEnv* env, IRExpr* e );
#endif
/*---------------------------------------------------------*/
/*--- ISEL: Misc helpers ---*/
/*---------------------------------------------------------*/
#if 0
/* Is this a 32-bit zero expression? */
static Bool isZero32 ( IRExpr* e )
{
return e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U32
&& e->Iex.Const.con->Ico.U32 == 0;
}
#endif
/* Make a int reg-reg move. */
static ARMInstr* mk_iMOVsd_RR ( HReg src, HReg dst )
{
vassert(hregClass(src) == HRcInt32);
vassert(hregClass(dst) == HRcInt32);
return ARMInstr_DPInstr1(ARMalu_MOV, dst, ARMAMode1_ShlI(src, 0));
}
#if 0
/* Advance/retreat stack pointer by n. */
static void add_to_sp ( ISelEnv* env, Int n )
{
HReg tmp;
ARMImm12A imm12a;
vassert(n > 0 && n < 256 && (n%4) == 0);
if ( mk_ARMImm12A( (UInt)n, &imm12a ) ) {
addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD,
GET_SP_REG(), GET_SP_REG(),
ARMAMode1_I12A( imm12a )));
} else {
tmp = newVRegI(env);
addInstr(env, ARMInstr_Literal( tmp, (UInt)n ));
addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD,
GET_SP_REG(), GET_SP_REG(),
ARMAMode1_ShlI( tmp, 0 )));
}
}
static void sub_from_sp ( ISelEnv* env, Int n )
{
HReg tmp;
ARMImm12A imm12a;
vassert(n > 0 && n < 256 && (n%4) == 0);
if ( mk_ARMImm12A( (UInt)n, &imm12a ) ) {
addInstr(env, ARMInstr_DPInstr2(ARMalu_SUB,
GET_SP_REG(), GET_SP_REG(),
ARMAMode1_I12A( imm12a )));
} else {
tmp = newVRegI(env);
addInstr(env, ARMInstr_Literal( tmp, (UInt)n ));
addInstr(env, ARMInstr_DPInstr2(ARMalu_SUB,
GET_SP_REG(), GET_SP_REG(),
ARMAMode1_ShlI( tmp, 0 )));
}
}
#endif
#if 0
/* Push an arg onto the host stack, in preparation for a call to a
helper function of some kind. Returns the number of 32-bit words
pushed. */
static Int pushArg ( ISelEnv* env, IRExpr* arg )
{
IRType arg_ty = typeOfIRExpr(env->type_env, arg);
if (arg_ty == Ity_I32) {
// CAB: This right?
addInstr(env, ARMInstr_StoreW( GET_SP_REG(), iselIntExpr_AMode2(env, arg) ) );
return 1;
}
#if 0
else
if (arg_ty == Ity_I64) {
HReg rHi, rLo;
iselInt64Expr(&rHi, &rLo, env, arg);
addInstr(env, X86Instr_Push(X86RMI_Reg(rHi)));
addInstr(env, X86Instr_Push(X86RMI_Reg(rLo)));
return 2;
}
#endif
ppIRExpr(arg);
vpanic("pushArg(arm): can't handle arg of this type");
}
#endif
#if 0
/* Complete the call to a helper function, by calling the
helper and clearing the args off the stack. */
static
void callHelperAndClearArgs ( ISelEnv* env, ARMCondCode cc,
IRCallee* cee, Int n_arg_ws )
{
/* Complication. Need to decide which reg to use as the fn address
pointer, in a way that doesn't trash regparm-passed
parameters. */
vassert(sizeof(void*) == 4);
// CAB: cee->regparms ?
// addInstr(env, X86Instr_Call( cc, (UInt)cee->addr, cee->regparms));
ARMBranchDest* dst = ARMBranchDest_Imm( (UInt)cee->addr );
addInstr(env, ARMInstr_BranchL(cc, dst));
if (n_arg_ws > 0)
add_to_sp(env, 4*n_arg_ws);
}
#endif
#if 0
/* Used only in doHelperCall. See big comment in doHelperCall re
handling of regparm args. This function figures out whether
evaluation of an expression might require use of a fixed register.
If in doubt return True (safe but suboptimal).
*/
static
Bool mightRequireFixedRegs ( IRExpr* e )
{
switch (e->tag) {
case Iex_Tmp: case Iex_Const: case Iex_Get:
return False;
default:
return True;
}
}
#endif
/* Do a complete function call. guard is a Ity_Bit expression
indicating whether or not the call happens. If guard==NULL, the
call is unconditional. */
static
void doHelperCall ( ISelEnv* env,
Bool passBBP,
IRExpr* guard, IRCallee* cee, IRExpr** args )
{
#if 0
ARMCondCode cc;
HReg argregs[3];
HReg tmpregs[3];
Bool danger;
Int not_done_yet, n_args, n_arg_ws, stack_limit,
i, argreg, argregX;
/* Marshal args for a call, do the call, and clear the stack.
Complexities to consider:
* if passBBP is True, %ebp (the baseblock pointer) is to be
passed as the first arg.
* If the callee claims regparmness of 1, 2 or 3, we must pass the
first 1, 2 or 3 args in registers (EAX, EDX, and ECX
respectively). To keep things relatively simple, only args of
type I32 may be passed as regparms -- just bomb out if anything
else turns up. Clearly this depends on the front ends not
trying to pass any other types as regparms.
*/
/* 16 Nov 2004: the regparm handling is complicated by the
following problem.
Consider a call two a function with two regparm parameters:
f(e1,e2). We need to compute e1 into %eax and e2 into %edx.
Suppose code is first generated to compute e1 into %eax. Then,
code is generated to compute e2 into %edx. Unfortunately, if
the latter code sequence uses %eax, it will trash the value of
e1 computed by the former sequence. This could happen if (for
example) e2 itself involved a function call. In the code below,
args are evaluated right-to-left, not left-to-right, but the
principle and the problem are the same.
One solution is to compute all regparm-bound args into vregs
first, and once they are all done, move them to the relevant
real regs. This always gives correct code, but it also gives
a bunch of vreg-to-rreg moves which are usually redundant but
are hard for the register allocator to get rid of.
A compromise is to first examine all regparm'd argument
expressions. If they are all so simple that it is clear
they will be evaluated without use of any fixed registers,
use the old compute-directly-to-fixed-target scheme. If not,
be safe and use the via-vregs scheme.
Note this requires being able to examine an expression and
determine whether or not evaluation of it might use a fixed
register. That requires knowledge of how the rest of this
insn selector works. Currently just the following 3 are
regarded as safe -- hopefully they cover the majority of
arguments in practice: IRExpr_Tmp IRExpr_Const IRExpr_Get.
*/
vassert(cee->regparms >= 0 && cee->regparms <= 3);
n_args = n_arg_ws = 0;
while (args[n_args]) n_args++;
not_done_yet = n_args;
if (passBBP)
not_done_yet++;
stack_limit = cee->regparms;
if (cee->regparms > 0 && passBBP) stack_limit--;
/* ------ BEGIN marshall all arguments ------ */
/* Push (R to L) the stack-passed args, [n_args-1 .. stack_limit] */
for (i = n_args-1; i >= stack_limit; i--) {
n_arg_ws += pushArg(env, args[i]);
not_done_yet--;
}
/* args [stack_limit-1 .. 0] and possibly %ebp are to be passed in
registers. */
if (cee->regparms > 0) {
/* ------ BEGIN deal with regparms ------ */
/* deal with regparms, not forgetting %ebp if needed. */
argregs[0] = hregX86_EAX();
argregs[1] = hregX86_EDX();
argregs[2] = hregX86_ECX();
tmpregs[0] = tmpregs[1] = tmpregs[2] = INVALID_HREG;
argreg = cee->regparms;
/* In keeping with big comment above, detect potential danger
and use the via-vregs scheme if needed. */
danger = False;
for (i = stack_limit-1; i >= 0; i--) {
if (mightRequireFixedRegs(args[i])) {
danger = True;
break;
}
}
if (danger) {
/* Move via temporaries */
argregX = argreg;
for (i = stack_limit-1; i >= 0; i--) {
if (0) {
vex_printf("x86 host: register param is complex: ");
ppIRExpr(args[i]);
vex_printf("\n");
}
argreg--;
vassert(argreg >= 0);
vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32);
tmpregs[argreg] = iselIntExpr_R(env, args[i]);
not_done_yet--;
}
for (i = stack_limit-1; i >= 0; i--) {
argregX--;
vassert(argregX >= 0);
addInstr( env, mk_iMOVsd_RR( tmpregs[argregX], argregs[argregX] ) );
}
} else {
/* It's safe to compute all regparm args directly into their
target registers. */
for (i = stack_limit-1; i >= 0; i--) {
argreg--;
vassert(argreg >= 0);
vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32);
addInstr(env, X86Instr_Alu32R(Xalu_MOV,
iselIntExpr_RMI(env, args[i]),
argregs[argreg]));
not_done_yet--;
}
}
/* Not forgetting %ebp if needed. */
if (passBBP) {
vassert(argreg == 1);
addInstr(env, mk_iMOVsd_RR( hregX86_EBP(), argregs[0]));
not_done_yet--;
}
/* ------ END deal with regparms ------ */
} else {
/* No regparms. Heave %ebp on the stack if needed. */
if (passBBP) {
addInstr(env, X86Instr_Push(X86RMI_Reg(hregX86_EBP())));
n_arg_ws++;
not_done_yet--;
}
}
vassert(not_done_yet == 0);
/* ------ END marshall all arguments ------ */
/* Now we can compute the condition. We can't do it earlier
because the argument computations could trash the condition
codes. Be a bit clever to handle the common case where the
guard is 1:Bit. */
cc = Xcc_ALWAYS;
if (guard) {
if (guard->tag == Iex_Const
&& guard->Iex.Const.con->tag == Ico_U1
&& guard->Iex.Const.con->Ico.U1 == True) {
/* unconditional -- do nothing */
} else {
cc = iselCondCode( env, guard );
}
}
/* call the helper, and get the args off the stack afterwards. */
callHelperAndClearArgs( env, cc, cee, n_arg_ws );
#endif
}
// CAB: Do we need to deal with elemSz != 8 ?
/* Given a guest-state array descriptor, an index expression and a
bias, generate an ARMAMode holding the relevant guest state
offset. */
static
ARMAMode2* genGuestArrayOffset ( ISelEnv* env, IRArray* descr,
IRExpr* off, Int bias )
{
HReg tmp, tmp2, roff;
Int elemSz = sizeofIRType(descr->elemTy);
Int nElems = descr->nElems;
ARMImm12A imm12a;
/* throw out any cases not generated by an x86 front end. In
theory there might be a day where we need to handle them -- if
we ever run non-x86-guest on x86 host. */
if (nElems != 8 || (elemSz != 1 && elemSz != 8))
vpanic("genGuestArrayOffset(arm host)");
/* Compute off into a reg, %off. Then return:
movl %off, %tmp
addl $bias, %tmp (if bias != 0)
andl %tmp, 7
... base(%ebp, %tmp, shift) ...
*/
tmp = newVRegI(env);
roff = iselIntExpr_R(env, off);
addInstr(env, mk_iMOVsd_RR(roff, tmp));
if (bias != 0) {
if ( mk_ARMImm12A( (UInt)bias, &imm12a ) ) {
addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp,
ARMAMode1_I12A( imm12a )));
} else {
HReg tmp3 = newVRegI(env);
addInstr(env, ARMInstr_Literal( tmp, (UInt)bias ));
addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp,
ARMAMode1_ShlI( tmp3, 0 )));
}
}
mk_ARMImm12A( (UInt)7, &imm12a );
addInstr(env, ARMInstr_DPInstr2(ARMalu_AND, tmp, tmp,
ARMAMode1_I12A( imm12a )));
vassert(elemSz == 1 || elemSz == 8);
// CAB: This anywhere near correct?
// X86AMode_IRRS: Immediate + Reg1 + (Reg2 << Shift)
// return X86AMode_IRRS( descr->base, hregX86_EBP(), tmp, elemSz==8 ? 3 : 0);
tmp2 = newVRegI(env); // tmp2 = GET_BP_REG + (tmp << 3|0)
addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp2, GET_BP_REG(),
ARMAMode1_ShlI(tmp, elemSz==8 ? 3 : 0)));
return ARMAMode2_RI( tmp2, descr->base );
}
/*---------------------------------------------------------*/
/*--- ISEL ... ---*/
/*---------------------------------------------------------*/
/* --------------------- AMODEs --------------------- */
/* Return an AMode which computes the value of the specified
expression, possibly also adding insns to the code list as a
result.
*/
/* ---------------- Addressing Mode 1 ---------------- */
static Bool sane_AMode1 ( ARMAMode1* am )
{
switch (am->tag) {
default:
vpanic("sane_AMode1: unknown arm amode tag");
}
}
static ARMAMode1* iselIntExpr_AMode1 ( ISelEnv* env, IRExpr* e )
{
ARMAMode1* am = iselIntExpr_AMode1_wrk(env, e);
vassert(sane_AMode1(am));
return am;
}
/* DO NOT CALL THIS DIRECTLY ! */
static ARMAMode1* iselIntExpr_AMode1_wrk ( ISelEnv* env, IRExpr* e )
{
IRType ty = typeOfIRExpr(env->type_env,e);
vassert(ty == Ity_I32);
// ARMam1_I12A, /* Imm12A: extended (rotated) immedate */
// ARMam1_ShlI, /* ShlI reg Imm5 */
// ARMam1_ShrI, /* ShrI reg Imm5 */
// ARMam1_SarI, /* SarI reg Imm5 */
// ARMam1_ShlR, /* ShlR reg reg */
// ARMam1_ShrR, /* ShrR reg reg */
// ARMam1_SarR, /* SarR reg reg */
// ALU ops:
/*
ARMalu_And, ARMalu_Orr, ARMalu_Eor, ARMalu_Bic, // Logic
ARMalu_Sub, ARMalu_Rsb, ARMalu_Add, ARMalu_Adc, ARMalu_Sbc, ARMalu_Rsc, // Arith
ARMalu_Tst, ARMalu_Teq, ARMalu_Cmp, ARMalu_Cmn, // test
ARMalu_Mov, ARMalu_Mvn // Move
*/
return NULL;
}
/* ---------------- Addressing Mode 2 ---------------- */
static Bool sane_AMode2 ( ARMAMode2* am )
{
switch (am->tag) {
default:
vpanic("sane_AMode2: unknown arm amode tag");
}
}
static ARMAMode2* iselIntExpr_AMode2_wrk ( ISelEnv* env, IRExpr* e )
{
ARMAMode2* am = iselIntExpr_AMode2_wrk(env, e);
vassert(sane_AMode2(am));
return am;
}
/* DO NOT CALL THIS DIRECTLY ! */
static ARMAMode2* iselIntExpr_AMode2 ( ISelEnv* env, IRExpr* e )
{
IRType ty = typeOfIRExpr(env->type_env,e);
vassert(ty == Ity_I32);
// ARMam2_RI, /* Reg +/- Imm12 */
// ARMam2_RR, /* Reg +/- Reg */
// ARMam2_RRS, /* Reg +/- (Reg << Imm5) */
return NULL;
}
/* ---------------- Addressing Mode 3 ---------------- */
static Bool sane_AMode3 ( ARMAMode3* am )
{
switch (am->tag) {
default:
vpanic("sane_AMode3: unknown arm amode tag");
}
}
static ARMAMode3* iselIntExpr_AMode3 ( ISelEnv* env, IRExpr* e )
{
ARMAMode3* am = iselIntExpr_AMode3_wrk(env, e);
vassert(sane_AMode3(am));
return am;
}
/* DO NOT CALL THIS DIRECTLY ! */
static ARMAMode3* iselIntExpr_AMode3_wrk ( ISelEnv* env, IRExpr* e )
{
IRType ty = typeOfIRExpr(env->type_env,e);
vassert(ty == Ity_I32);
// ARMam3_RI, /* Reg +/- Imm8 */
// ARMam3_RR, /* Reg +/- Reg */
return NULL;
}
/* ---------------- Branch Destination ---------------- */
static ARMBranchDest* iselIntExpr_BD ( ISelEnv* env, IRExpr* e )
{
ARMBranchDest* bd = iselIntExpr_BD_wrk(env, e);
/* sanity checks ... */
switch (bd->tag) {
case ARMbdImm:
return bd;
case ARMbdReg:
vassert(hregClass(bd->ARMbd.Reg.reg) == HRcInt32);
// vassert(hregIsVirtual(bd->ARMbd.Reg.reg)); // CAB ?
return bd;
default:
vpanic("iselIntExpr_BD: unknown arm BD tag");
}
}
/* DO NOT CALL THIS DIRECTLY ! */
static ARMBranchDest* iselIntExpr_BD_wrk ( ISelEnv* env, IRExpr* e )
{
/*
ARMbdImm,
ARMbdReg
*/
return NULL;
}
/* --------------------- CONDCODE --------------------- */
/* Generate code to evaluated a bit-typed expression, returning the
condition code which would correspond when the expression would
notionally have returned 1. */
static ARMCondCode iselCondCode ( ISelEnv* env, IRExpr* e )
{
/* Uh, there's nothing we can sanity check here, unfortunately. */
return iselCondCode_wrk(env, e);
}
/* DO NOT CALL THIS DIRECTLY ! */
static ARMCondCode iselCondCode_wrk ( ISelEnv* env, IRExpr* e )
{
#if 0
MatchInfo mi;
DECLARE_PATTERN(p_32to1);
DECLARE_PATTERN(p_1Uto32_then_32to1);
#endif
return 0;
}
static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e )
{
return iselIntExpr_R_wrk(env, e);
}
/* DO NOT CALL THIS DIRECTLY ! */
static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e )
{
return 0;
}
/*---------------------------------------------------------*/
/*--- ISEL: Statements ---*/
/*---------------------------------------------------------*/
static void iselStmt ( ISelEnv* env, IRStmt* stmt )
{
if (vex_traceflags & VEX_TRACE_VCODE) {
vex_printf("\n-- ");
ppIRStmt(stmt);
vex_printf("\n");
}
switch (stmt->tag) {
/* --------- STORE --------- */
/* little-endian write to memory */
case Ist_Store: {
HReg reg;
IRType tya = typeOfIRExpr(env->type_env, stmt->Ist.Store.addr);
IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Store.data);
IREndness end = stmt->Ist.Store.end;
if (tya != Ity_I32 || end != Iend_LE)
goto stmt_fail;
reg = iselIntExpr_R(env, stmt->Ist.Store.data);
if (tyd == Ity_I8) {
ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.Store.addr);
addInstr(env, ARMInstr_StoreB(reg,am2));
return;
}
if (tyd == Ity_I16) {
ARMAMode3* am3 = iselIntExpr_AMode3(env, stmt->Ist.Store.addr);
addInstr(env, ARMInstr_StoreH(reg,am3));
return;
}
if (tyd == Ity_I32) {
ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.Store.addr);
addInstr(env, ARMInstr_StoreW(reg,am2));
return;
}
}
/* --------- PUT --------- */
/* write guest state, fixed offset */
case Ist_Put: {
IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Put.data);
HReg reg = iselIntExpr_R(env, stmt->Ist.Put.data);
// CAB: This anywhere near right?!
if (tyd == Ity_I32) {
ARMAMode2* am2 = ARMAMode2_RI(GET_BP_REG(), stmt->Ist.Put.offset);
addInstr(env, ARMInstr_StoreW(reg, am2));
return;
}
if (tyd == Ity_I16) {
ARMAMode3* am3 = ARMAMode3_RI(GET_BP_REG(), stmt->Ist.Put.offset);
addInstr(env, ARMInstr_StoreH(reg, am3));
return;
}
if (tyd == Ity_I8) {
ARMAMode2* am2 = ARMAMode2_RI(GET_BP_REG(), stmt->Ist.Put.offset);
addInstr(env, ARMInstr_StoreB(reg, am2));
return;
}
// CAB: Ity_I32, Ity_I16 ?
break;
}
/* --------- Indexed PUT --------- */
/* write guest state, run-time offset */
case Ist_PutI: {
ARMAMode2* am2
= genGuestArrayOffset(
env, stmt->Ist.PutI.descr,
stmt->Ist.PutI.ix, stmt->Ist.PutI.bias );
IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.PutI.data);
if (tyd == Ity_I8) {
HReg reg = iselIntExpr_R(env, stmt->Ist.PutI.data);
addInstr(env, ARMInstr_StoreB(reg, am2));
return;
}
// CAB: Ity_I32, Ity_I16 ?
break;
}
/* --------- TMP --------- */
/* assign value to temporary */
case Ist_Tmp: {
IRTemp tmp = stmt->Ist.Tmp.tmp;
IRType ty = typeOfIRTemp(env->type_env, tmp);
if (ty == Ity_I32 || ty == Ity_I16 || ty == Ity_I8) {
ARMAMode1* am = iselIntExpr_AMode1(env, stmt->Ist.Tmp.data);
HReg dst = lookupIRTemp(env, tmp);
addInstr(env, ARMInstr_DPInstr1(ARMalu_MOV,dst,am));
return;
}
// CAB: Ity_I1 ?
break;
}
/* --------- Call to DIRTY helper --------- */
/* call complex ("dirty") helper function */
case Ist_Dirty: {
//IRType retty;
IRDirty* d = stmt->Ist.Dirty.details;
Bool passBBP = False;
if (d->nFxState == 0)
vassert(!d->needsBBP);
passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Marshal args, do the call, clear stack. */
doHelperCall( env, passBBP, d->guard, d->cee, d->args );
/* Now figure out what to do with the returned value, if any. */
if (d->tmp == IRTemp_INVALID)
/* No return value. Nothing to do. */
return;
//retty = typeOfIRTemp(env->type_env, d->tmp);
// CAB: ? if (retty == Ity_I64) {
#if 0
if (retty == Ity_I32 || retty == Ity_I16 || retty == Ity_I8) {
/* The returned value is in %eax. Park it in the register
associated with tmp. */
HReg dst = lookupIRTemp(env, d->tmp);
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(),dst) );
return;
}
#endif
break;
}
/* --------- EXIT --------- */
/* conditional exit from BB */
case Ist_Exit: {
ARMBranchDest* dst;
ARMCondCode cc;
if (stmt->Ist.Exit.dst->tag != Ico_U32)
vpanic("isel_arm: Ist_Exit: dst is not a 32-bit value");
// CAB: Where does jumpkind fit in ?
// stmt->Ist.Exit.jk
dst = iselIntExpr_BD(env, IRExpr_Const(stmt->Ist.Exit.dst));
cc = iselCondCode(env,stmt->Ist.Exit.guard);
addInstr(env, ARMInstr_Branch(cc, dst));
return;
}
default: break;
}
stmt_fail:
ppIRStmt(stmt);
vpanic("iselStmt");
}
/*---------------------------------------------------------*/
/*--- ISEL: Basic block terminators (Nexts) ---*/
/*---------------------------------------------------------*/
static void iselNext ( ISelEnv* env, IRExpr* next, IRJumpKind jk )
{
ARMBranchDest* bd;
if (vex_traceflags & VEX_TRACE_VCODE) {
vex_printf("\n-- goto {");
ppIRJumpKind(jk);
vex_printf("} ");
ppIRExpr(next);
vex_printf("\n");
}
bd = iselIntExpr_BD(env, next);
// CAB: jk ?
addInstr( env, ARMInstr_Branch(ARMccAL, bd) );
}
/*---------------------------------------------------------*/
/*--- Insn selector top-level ---*/
/*---------------------------------------------------------*/
/* Translate an entire BB to arm code. */
HInstrArray* iselBB_ARM ( IRBB* bb )
{
Int i, j;
/* Make up an initial environment to use. */
ISelEnv* env = LibVEX_Alloc(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->types_used;
env->vregmap = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
/* For each IR temporary, allocate a 32bit virtual register. */
j = 0;
for (i = 0; i < env->n_vregmap; i++) {
env->vregmap[i] = mkHReg(j++, HRcInt32, True);
}
env->vreg_ctr = j;
/* Ok, finally we can iterate over the statements. */
for (i = 0; i < bb->stmts_used; i++)
if (bb->stmts[i])
iselStmt(env,bb->stmts[i]);
iselNext(env,bb->next,bb->jumpkind);
/* record the number of vregs we used. */
env->code->n_vregs = env->vreg_ctr;
return env->code;
}
/*---------------------------------------------------------------*/
/*--- end host-x86/isel.c ---*/
/*---------------------------------------------------------------*/