mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-14 23:04:26 +00:00
dead code -- it is better at spotting it than gcc is. git-svn-id: svn://svn.valgrind.org/vex/trunk@735
960 lines
27 KiB
C
960 lines
27 KiB
C
|
|
/*---------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- 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_STle: {
|
|
HReg reg;
|
|
IRType tya = typeOfIRExpr(env->type_env, stmt->Ist.STle.addr);
|
|
IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.STle.data);
|
|
vassert(tya == Ity_I32);
|
|
reg = iselIntExpr_R(env, stmt->Ist.STle.data);
|
|
|
|
if (tyd == Ity_I8) {
|
|
ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.STle.addr);
|
|
addInstr(env, ARMInstr_StoreB(reg,am2));
|
|
return;
|
|
}
|
|
if (tyd == Ity_I16) {
|
|
ARMAMode3* am3 = iselIntExpr_AMode3(env, stmt->Ist.STle.addr);
|
|
addInstr(env, ARMInstr_StoreH(reg,am3));
|
|
return;
|
|
}
|
|
if (tyd == Ity_I32) {
|
|
ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.STle.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 = 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;
|
|
}
|
|
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 ---*/
|
|
/*---------------------------------------------------------------*/
|