0/*regparms*/,
"amd64g_dirtyhelper_FLDENV",
&amd64g_dirtyhelper_FLDENV,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
- d->tmp = w64;
+ d->tmp = w64;
/* declare we're reading memory */
d->mFx = Ifx_Read;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"amd64g_dirtyhelper_FSTENV",
&amd64g_dirtyhelper_FSTENV,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're writing memory */
d->mFx = Ifx_Write;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"amd64g_dirtyhelper_FINIT",
&amd64g_dirtyhelper_FINIT,
- mkIRExprVec_0()
+ mkIRExprVec_1( IRExprP__BBPTR )
);
- d->needsBBP = True;
/* declare we're writing guest state */
d->nFxState = 5;
0/*regparms*/,
"amd64g_dirtyhelper_FRSTOR",
&amd64g_dirtyhelper_FRSTOR,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
d->mSize = 108;
}
- d->needsBBP = True;
- d->tmp = w64;
+ d->tmp = w64;
/* declare we're reading memory */
d->mFx = Ifx_Read;
d->mAddr = mkexpr(addr);
IRDirty *d;
if ( have66(pfx) ) {
/* Uses dirty helper:
- void amd64g_dirtyhelper_FNSAVES ( VexGuestX86State*, HWord ) */
+ void amd64g_dirtyhelper_FNSAVES ( VexGuestAMD64State*,
+ HWord ) */
d = unsafeIRDirty_0_N (
0/*regparms*/,
"amd64g_dirtyhelper_FNSAVES",
d->mSize = 94;
} else {
/* Uses dirty helper:
- void amd64g_dirtyhelper_FNSAVE ( VexGuestX86State*, HWord ) */
+ void amd64g_dirtyhelper_FNSAVE ( VexGuestAMD64State*,
+ HWord ) */
d = unsafeIRDirty_0_N (
0/*regparms*/,
"amd64g_dirtyhelper_FNSAVE",
&amd64g_dirtyhelper_FNSAVE,
- mkIRExprVec_1( mkexpr(addr) )
- );
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
+ );
d->mSize = 108;
}
- d->needsBBP = True;
+
/* declare we're writing memory */
d->mFx = Ifx_Write;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"amd64g_dirtyhelper_FXSAVE",
&amd64g_dirtyhelper_FXSAVE,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're writing memory */
d->mFx = Ifx_Write;
0/*regparms*/,
"amd64g_dirtyhelper_FXRSTOR",
&amd64g_dirtyhelper_FXRSTOR,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're reading memory */
d->mFx = Ifx_Read;
IRExpr* gstOffLe = mkU64(gstOffL);
IRExpr* gstOffRe = mkU64(gstOffR);
IRExpr** args
- = mkIRExprVec_4( opc4, gstOffDe, gstOffLe, gstOffRe );
+ = mkIRExprVec_5( IRExprP__BBPTR, opc4, gstOffDe, gstOffLe, gstOffRe );
IRDirty* d = unsafeIRDirty_0_N( 0/*regparms*/, nm, fn, args );
- /* It's not really a dirty call, but we can't use the clean
- helper mechanism here for the very lame reason that we can't
- pass 2 x V128s by value to a helper, nor get one back. Hence
- this roundabout scheme. */
- d->needsBBP = True;
+ /* It's not really a dirty call, but we can't use the clean helper
+ mechanism here for the very lame reason that we can't pass 2 x
+ V128s by value to a helper. Hence this roundabout scheme. */
d->nFxState = 2;
vex_bzero(&d->fxState, sizeof(d->fxState));
/* AES{ENC,ENCLAST,DEC,DECLAST} read both registers, and writes
IRExpr* gstOffLe = mkU64(gstOffL);
IRExpr* gstOffRe = mkU64(gstOffR);
IRExpr** args
- = mkIRExprVec_3( imme, gstOffLe, gstOffRe );
+ = mkIRExprVec_4( IRExprP__BBPTR, imme, gstOffLe, gstOffRe );
IRDirty* d = unsafeIRDirty_0_N( 0/*regparms*/, nm, fn, args );
- /* It's not really a dirty call, but we can't use the clean
- helper mechanism here for the very lame reason that we can't
- pass 2 x V128s by value to a helper, nor get one back. Hence
- this roundabout scheme. */
- d->needsBBP = True;
+ /* It's not really a dirty call, but we can't use the clean helper
+ mechanism here for the very lame reason that we can't pass 2 x
+ V128s by value to a helper. Hence this roundabout scheme. */
d->nFxState = 2;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Read;
IRExpr* edxIN = isISTRx ? mkU64(0) : getIRegRDX(8);
IRExpr* eaxIN = isISTRx ? mkU64(0) : getIRegRAX(8);
IRExpr** args
- = mkIRExprVec_5( opc4_and_imm, gstOffLe, gstOffRe, edxIN, eaxIN );
+ = mkIRExprVec_6( IRExprP__BBPTR,
+ opc4_and_imm, gstOffLe, gstOffRe, edxIN, eaxIN );
IRTemp resT = newTemp(Ity_I64);
IRDirty* d = unsafeIRDirty_1_N( resT, 0/*regparms*/, nm, fn, args );
/* It's not really a dirty call, but we can't use the clean helper
mechanism here for the very lame reason that we can't pass 2 x
- V128s by value to a helper, nor get one back. Hence this
- roundabout scheme. */
- d->needsBBP = True;
+ V128s by value to a helper. Hence this roundabout scheme. */
d->nFxState = 2;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Read;
void* fAddr = &amd64g_dirtyhelper_RDTSCP;
IRDirty* d
= unsafeIRDirty_0_N ( 0/*regparms*/,
- fName, fAddr, mkIRExprVec_0() );
+ fName, fAddr, mkIRExprVec_1(IRExprP__BBPTR) );
/* declare guest state effects */
- d->needsBBP = True;
d->nFxState = 3;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Write;
vassert(fName); vassert(fAddr);
d = unsafeIRDirty_0_N ( 0/*regparms*/,
- fName, fAddr, mkIRExprVec_0() );
+ fName, fAddr, mkIRExprVec_1(IRExprP__BBPTR) );
/* declare guest state effects */
- d->needsBBP = True;
d->nFxState = 4;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Modify;
extern ULong mips64_dirtyhelper_dmfc0 ( UInt rd, UInt sel );
-extern void mips32_dirtyhelper_sync ( UInt sync );
#if defined(__mips__) && ((defined(__mips_isa_rev) && __mips_isa_rev >= 2))
extern ULong mips64_dirtyhelper_rdhwr ( ULong rt, ULong rd );
#define ASM_VOLATILE_CASE(rd, sel) \
case rd: asm volatile ("dmfc0 %0, $" #rd ", "#sel"\n\t" :"=r" (x) ); break;
-#define ASM_VOLATILE_SYNC(stype) \
- asm volatile ("sync \n\t");
-
-void mips32_dirtyhelper_sync(UInt stype)
-{
-#if defined(__mips__) && ((defined(__mips_isa_rev) && __mips_isa_rev >= 2))
- ASM_VOLATILE_SYNC(0);
-#endif
-}
-
#if defined(__mips__) && ((defined(__mips_isa_rev) && __mips_isa_rev >= 2))
ULong mips64_dirtyhelper_rdhwr ( ULong rt, ULong rd )
{
if (rs == 0) { /* MFC0 */
DIP("mfc0 r%d, r%d, %d", rt, rd, sel);
IRTemp val = newTemp(Ity_I32);
- IRExpr** args = mkIRExprVec_2 (mkU32(rd), mkU32(sel));
+ IRExpr** args = mkIRExprVec_3 (IRExprP__BBPTR, mkU32(rd), mkU32(sel));
IRDirty *d = unsafeIRDirty_1_N(val,
0,
"mips32_dirtyhelper_mfc0",
/* Doubleword Move from Coprocessor 0 - DMFC0; MIPS64 */
DIP("dmfc0 r%d, r%d, %d", rt, rd, sel);
IRTemp val = newTemp(Ity_I64);
- IRExpr** args = mkIRExprVec_2 (mkU64(rd), mkU64(sel));
+ IRExpr** args = mkIRExprVec_3 (IRExprP__BBPTR, mkU64(rd), mkU64(sel));
IRDirty *d = unsafeIRDirty_1_N(val,
0,
"mips64_dirtyhelper_dmfc0",
#if defined(__mips__) && ((defined(__mips_isa_rev) && __mips_isa_rev >= 2))
} else if (rd == 1) {
IRTemp val = newTemp(Ity_I64);
- IRExpr** args = mkIRExprVec_2 (mkU64(rt), mkU64(rd));
+ IRExpr** args = mkIRExprVec_3 (IRExprP__BBPTR,
+ mkU64(rt), mkU64(rd));
IRDirty *d = unsafeIRDirty_1_N(val,
0,
"mips64_dirtyhelper_rdhwr",
break;
goto decode_failure;
- case 0x0F: { /* SYNC */
+ case 0x0F: /* SYNC */
DIP("sync 0x%x", sel);
- lsb = get_lsb(cins);
- IRDirty *d = unsafeIRDirty_0_N(0,
- "mips32_dirtyhelper_sync",
- &mips32_dirtyhelper_sync,
- mkIRExprVec_1
- (mkU32(lsb)));
-
- d->needsBBP = False;
- d->nFxState = 0;
-
- stmt(IRStmt_Dirty(d));
+ /* Just ignore it. */
break;
- }
case 0x2C: { /* Doubleword Add - DADD; MIPS64 */
DIP("dadd r%d, r%d, r%d", rd, rs, rt);
case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
IRDirty* d;
UInt vD_off = vectorGuestRegOffset(vD_addr);
- IRExpr** args = mkIRExprVec_3(
+ IRExpr** args = mkIRExprVec_4(
+ IRExprP__BBPTR,
mkU32(vD_off),
binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)),
mkU32(0xF)),
}
DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
/* declare guest state effects */
- d->needsBBP = True;
d->nFxState = 1;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Write;
case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
IRDirty* d;
UInt vD_off = vectorGuestRegOffset(vD_addr);
- IRExpr** args = mkIRExprVec_3(
+ IRExpr** args = mkIRExprVec_4(
+ IRExprP__BBPTR,
mkU32(vD_off),
binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)),
mkU32(0xF)),
}
DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
/* declare guest state effects */
- d->needsBBP = True;
d->nFxState = 1;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Write;
IRDirty *d;
IRTemp cc = newTemp(Ity_I64);
+ /* IRExprP__BBPTR => Need to pass pointer to guest state to helper */
d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_STFLE",
&s390x_dirtyhelper_STFLE,
- mkIRExprVec_1(mkexpr(op2addr)));
-
- d->needsBBP = 1; /* Need to pass pointer to guest state to helper */
+ mkIRExprVec_2(IRExprP__BBPTR, mkexpr(op2addr)));
d->nFxState = 1;
vex_bzero(&d->fxState, sizeof(d->fxState));
0/*regparms*/,
"x86g_dirtyhelper_FLDENV",
&x86g_dirtyhelper_FLDENV,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
- d->tmp = ew;
+ d->tmp = ew;
/* declare we're reading memory */
d->mFx = Ifx_Read;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"x86g_dirtyhelper_FSTENV",
&x86g_dirtyhelper_FSTENV,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're writing memory */
d->mFx = Ifx_Write;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"x86g_dirtyhelper_FINIT",
&x86g_dirtyhelper_FINIT,
- mkIRExprVec_0()
+ mkIRExprVec_1(IRExprP__BBPTR)
);
- d->needsBBP = True;
/* declare we're writing guest state */
d->nFxState = 5;
0/*regparms*/,
"x86g_dirtyhelper_FRSTOR",
&x86g_dirtyhelper_FRSTOR,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
- d->tmp = ew;
+ d->tmp = ew;
/* declare we're reading memory */
d->mFx = Ifx_Read;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"x86g_dirtyhelper_FSAVE",
&x86g_dirtyhelper_FSAVE,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're writing memory */
d->mFx = Ifx_Write;
d->mAddr = mkexpr(addr);
0/*regparms*/,
"x86g_dirtyhelper_FXSAVE",
&x86g_dirtyhelper_FXSAVE,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're writing memory */
d->mFx = Ifx_Write;
0/*regparms*/,
"x86g_dirtyhelper_FXRSTOR",
&x86g_dirtyhelper_FXRSTOR,
- mkIRExprVec_1( mkexpr(addr) )
+ mkIRExprVec_2( IRExprP__BBPTR, mkexpr(addr) )
);
- d->needsBBP = True;
/* declare we're reading memory */
d->mFx = Ifx_Read;
vassert(fName); vassert(fAddr);
d = unsafeIRDirty_0_N ( 0/*regparms*/,
- fName, fAddr, mkIRExprVec_0() );
+ fName, fAddr, mkIRExprVec_1(IRExprP__BBPTR) );
/* declare guest state effects */
- d->needsBBP = True;
d->nFxState = 4;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Modify;
i->Ain.Call.regparms = regparms;
i->Ain.Call.rloc = rloc;
vassert(regparms >= 0 && regparms <= 6);
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
}
case Ain_Call: {
- if (i->Ain.Call.cond != Acc_ALWAYS && i->Ain.Call.rloc != RetLocNone) {
+ if (i->Ain.Call.cond != Acc_ALWAYS
+ && i->Ain.Call.rloc.pri != RLPri_None) {
/* The call might not happen (it isn't unconditional) and it
returns a result. In this case we will need to generate a
control flow diamond to put 0x555..555 in the return
HReg dst,
IRExpr* e )
{
+ /* Per comments in doHelperCall below, appearance of
+ IRExprP__VECRET implies ill-formed IR. */
+ vassert(e != IRExprP__VECRET);
+
+ /* In this case we give out a copy of the BaseBlock pointer. */
+ if (UNLIKELY(e == IRExprP__BBPTR)) {
+ return mk_iMOVsd_RR( hregAMD64_RBP(), dst );
+ }
+
vassert(typeOfIRExpr(env->type_env, e) == Ity_I64);
if (e->tag == Iex_Const) {
}
-/* Do a complete function call. guard is a Ity_Bit expression
+/* 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. */
+ call is unconditional. |retloc| is set to indicate where the
+ return value is after the call. The caller (of this fn) must
+ generate code to add |stackAdjustAfterCall| to the stack pointer
+ after the call is done. */
static
-void doHelperCall ( ISelEnv* env,
- Bool passBBP,
- IRExpr* guard, IRCallee* cee, IRExpr** args,
- RetLoc rloc )
+void doHelperCall ( /*OUT*/UInt* stackAdjustAfterCall,
+ /*OUT*/RetLoc* retloc,
+ ISelEnv* env,
+ IRExpr* guard,
+ IRCallee* cee, IRType retTy, IRExpr** args )
{
AMD64CondCode cc;
HReg argregs[6];
HReg tmpregs[6];
AMD64Instr* fastinstrs[6];
- Int n_args, i, argreg;
+ UInt n_args, i;
- /* Marshal args for a call and do the call.
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
- If passBBP is True, %rbp (the baseblock pointer) is to be passed
- as the first arg.
+ /* These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
+ /* Marshal args for a call and do the call.
This function only deals with a tiny set of possibilities, which
cover all helpers in practice. The restrictions are that only
bits in total can be passed. In fact the only supported arg
type is I64.
+ The return type can be I{64,32,16,8} or V{128,256}. In the
+ latter two cases, it is expected that |args| will contain the
+ special value IRExprP__VECRET, in which case this routine
+ generates code to allocate space on the stack for the vector
+ return value. Since we are not passing any scalars on the
+ stack, it is enough to preallocate the return space before
+ marshalling any arguments, in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the
+ value in %rbp is passed as the corresponding argument.
+
Generating code which is both efficient and correct when
parameters are to be passed in registers is difficult, for the
reasons elaborated in detail in comments attached to
fast scheme, else use the slow scheme. Note also that only
unconditional calls may use the fast scheme, since having to
compute a condition expression could itself trash real
- registers.
+ registers. Note that for simplicity, in the case where
+ IRExprP__VECRET is present, we use the slow scheme. This is
+ motivated by the desire to avoid any possible complexity
+ w.r.t. nested calls.
Note this requires being able to examine an expression and
determine whether or not evaluation of it might use a fixed
/* Note that the cee->regparms field is meaningless on AMD64 host
(since there is only one calling convention) and so we always
ignore it. */
-
n_args = 0;
for (i = 0; args[i]; i++)
n_args++;
- if (6 < n_args + (passBBP ? 1 : 0))
+ if (n_args > 6)
vpanic("doHelperCall(AMD64): cannot currently handle > 6 args");
argregs[0] = hregAMD64_RDI();
assume the fast scheme, and select slow if any contraindications
(wow) appear. */
+ /* We'll need space on the stack for the return value. Avoid
+ possible complications with nested calls by using the slow
+ scheme. */
+ if (retTy == Ity_V128 || retTy == Ity_V256)
+ goto slowscheme;
+
if (guard) {
if (guard->tag == Iex_Const
&& guard->Iex.Const.con->tag == Ico_U1
in a buffer and emit that if we're successful. */
/* FAST SCHEME */
- argreg = 0;
- if (passBBP) {
- fastinstrs[argreg] = mk_iMOVsd_RR( hregAMD64_RBP(), argregs[argreg]);
- argreg++;
- }
-
+ /* In this loop, we process args that can be computed into the
+ destination (real) register with a single instruction, without
+ using any fixed regs. That also includes IRExprP__BBPTR, but
+ not IRExprP__VECRET. Indeed, if the IR is well-formed, we can
+ never see IRExprP__VECRET at this point, since the return-type
+ check above should ensure all those cases use the slow scheme
+ instead. */
+ vassert(n_args >= 0 && n_args <= 6);
for (i = 0; i < n_args; i++) {
- vassert(argreg < 6);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- fastinstrs[argreg]
- = iselIntExpr_single_instruction( env, argregs[argreg], args[i] );
- if (fastinstrs[argreg] == NULL)
+ IRExpr* arg = args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg))) {
+ vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
+ }
+ fastinstrs[i]
+ = iselIntExpr_single_instruction( env, argregs[i], args[i] );
+ if (fastinstrs[i] == NULL)
goto slowscheme;
- argreg++;
}
/* Looks like we're in luck. Emit the accumulated instructions and
move on to doing the call itself. */
- vassert(argreg <= 6);
- for (i = 0; i < argreg; i++)
+ for (i = 0; i < n_args; i++)
addInstr(env, fastinstrs[i]);
/* Fast scheme only applies for unconditional calls. Hence: */
/* SLOW SCHEME; move via temporaries */
slowscheme:
+ {}
# if 0 /* debug only */
if (n_args > 0) {for (i = 0; args[i]; i++) {
ppIRExpr(args[i]); vex_printf(" "); }
vex_printf("\n");}
# endif
- argreg = 0;
-
- if (passBBP) {
- /* This is pretty stupid; better to move directly to rdi
- after the rest of the args are done. */
- tmpregs[argreg] = newVRegI(env);
- addInstr(env, mk_iMOVsd_RR( hregAMD64_RBP(), tmpregs[argreg]));
- argreg++;
+
+ /* If we have a vector return type, allocate a place for it on the
+ stack and record its address. */
+ HReg r_vecRetAddr = INVALID_HREG;
+ if (retTy == Ity_V128) {
+ r_vecRetAddr = newVRegI(env);
+ sub_from_rsp(env, 16);
+ addInstr(env, mk_iMOVsd_RR( hregAMD64_RSP(), r_vecRetAddr ));
+ }
+ else if (retTy == Ity_V256) {
+ vassert(0); //ATC
+ r_vecRetAddr = newVRegI(env);
+ sub_from_rsp(env, 32);
+ addInstr(env, mk_iMOVsd_RR( hregAMD64_RSP(), r_vecRetAddr ));
}
+ vassert(n_args >= 0 && n_args <= 6);
for (i = 0; i < n_args; i++) {
- vassert(argreg < 6);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- tmpregs[argreg] = iselIntExpr_R(env, args[i]);
- argreg++;
+ IRExpr* arg = args[i];
+ if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ tmpregs[i] = newVRegI(env);
+ addInstr(env, mk_iMOVsd_RR( hregAMD64_RBP(), tmpregs[i]));
+ nBBPTRs++;
+ }
+ else if (UNLIKELY(arg == IRExprP__VECRET)) {
+ /* We stashed the address of the return slot earlier, so just
+ retrieve it now. */
+ vassert(!hregIsInvalid(r_vecRetAddr));
+ tmpregs[i] = r_vecRetAddr;
+ nVECRETs++;
+ }
+ else {
+ vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
+ tmpregs[i] = iselIntExpr_R(env, args[i]);
+ }
}
/* Now we can compute the condition. We can't do it earlier
}
/* Move the args to their final destinations. */
- for (i = 0; i < argreg; i++) {
+ for (i = 0; i < n_args; i++) {
/* None of these insns, including any spill code that might
be generated, may alter the condition codes. */
addInstr( env, mk_iMOVsd_RR( tmpregs[i], argregs[i] ) );
}
- /* Finally, the call itself. */
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
handle_call:
- addInstr(env, AMD64Instr_Call(
- cc,
- Ptr_to_ULong(cee->addr),
- n_args + (passBBP ? 1 : 0), rloc
- )
- );
+
+ if (retTy == Ity_V128 || retTy == Ity_V256) {
+ vassert(nVECRETs == 1);
+ } else {
+ vassert(nVECRETs == 0);
+ }
+
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
+ *stackAdjustAfterCall = 16;
+ break;
+ case Ity_V256:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
+ *stackAdjustAfterCall = 32;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
+
+ /* Finally, generate the call itself. This needs the *retloc value
+ set in the switch above, which is why it's at the end. */
+ addInstr(env,
+ AMD64Instr_Call(cc, Ptr_to_ULong(cee->addr), n_args, *retloc));
}
addInstr(env, AMD64Instr_MovxLQ(False, argR, argR));
addInstr(env, mk_iMOVsd_RR(argL, hregAMD64_RDI()) );
addInstr(env, mk_iMOVsd_RR(argR, hregAMD64_RSI()) );
- addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 2, RetLocInt ));
+ addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 2,
+ mk_RetLoc_simple(RLPri_Int) ));
addInstr(env, mk_iMOVsd_RR(hregAMD64_RAX(), dst));
return dst;
}
fn = (HWord)h_generic_calc_GetMSBs8x8;
addInstr(env, mk_iMOVsd_RR(arg, hregAMD64_RDI()) );
addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn,
- 1, RetLocInt ));
+ 1, mk_RetLoc_simple(RLPri_Int) ));
/* MovxLQ is not exactly the right thing here. We just
need to get the bottom 8 bits of RAX into dst, and zero
out everything else. Assuming that the helper returns
AMD64RMI_Mem(m16_rsp),
hregAMD64_RSI() )); /* 2nd arg */
addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn,
- 2, RetLocInt ));
+ 2, mk_RetLoc_simple(RLPri_Int) ));
/* MovxLQ is not exactly the right thing here. We just
need to get the bottom 16 bits of RAX into dst, and zero
out everything else. Assuming that the helper returns
HReg dst = newVRegI(env);
HReg arg = iselIntExpr_R(env, e->Iex.Unop.arg);
addInstr(env, mk_iMOVsd_RR(arg, hregAMD64_RDI()) );
- addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 1, RetLocInt ));
+ addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 1,
+ mk_RetLoc_simple(RLPri_Int) ));
addInstr(env, mk_iMOVsd_RR(hregAMD64_RAX(), dst));
return dst;
}
vassert(ty == e->Iex.CCall.retty);
/* be very restrictive for now. Only 64-bit ints allowed for
- args, and 64 or 32 bits for return type. Don't forget to
- change the RetLoc if more types are allowed in future. */
+ args, and 64 or 32 bits for return type. */
if (e->Iex.CCall.retty != Ity_I64 && e->Iex.CCall.retty != Ity_I32)
goto irreducible;
/* Marshal args, do the call. */
- doHelperCall( env, False, NULL, e->Iex.CCall.cee, e->Iex.CCall.args,
- RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ e->Iex.CCall.cee, e->Iex.CCall.retty, e->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
/* Move to dst, and zero out the top 32 bits if the result type is
Ity_I32. Probably overkill, but still .. */
vassert(cal->Iex.CCall.retty == Ity_I64); /* else ill-typed IR */
vassert(con->Iex.Const.con->tag == Ico_U64);
/* Marshal args, do the call. */
- doHelperCall( env, False, NULL, cal->Iex.CCall.cee, cal->Iex.CCall.args,
- RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ cal->Iex.CCall.cee,
+ cal->Iex.CCall.retty, cal->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ /* */
addInstr(env, AMD64Instr_Imm64(con->Iex.Const.con->Ico.U64, tmp));
addInstr(env, AMD64Instr_Alu64R(Aalu_CMP,
AMD64RMI_Reg(hregAMD64_RAX()), tmp));
/* call the helper */
addInstr(env, AMD64Instr_Call( Acc_ALWAYS,
(ULong)(HWord)h_generic_calc_MAddF32,
- 4, RetLocNone ));
+ 4, mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 4, dst,
/* call the helper */
addInstr(env, AMD64Instr_Call( Acc_ALWAYS,
(ULong)(HWord)h_generic_calc_MAddF64,
- 4, RetLocNone ));
+ 4, mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 8, dst,
AMD64AMode_IR(0, hregAMD64_RDX())));
/* call the helper */
addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn,
- 3, RetLocNone ));
+ 3, mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 16, dst,
/* call the helper */
addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn,
- 3, RetLocNone ));
+ 3, mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 16, dst,
addInstr(env, AMD64Instr_SseLdSt(False/*!isLoad*/, 16, argRlo,
AMD64AMode_IR(48, hregAMD64_RDX())));
/* call the helper */
- addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3, RetLocNone ));
+ addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3,
+ mk_RetLoc_simple(RLPri_None) ));
/* Prepare 3 arg regs:
leaq 48(%r_argp), %rdi
leaq 64(%r_argp), %rsi
addInstr(env, AMD64Instr_Lea64(AMD64AMode_IR(80, argp),
hregAMD64_RDX()));
/* call the helper */
- addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3, RetLocNone ));
+ addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3,
+ mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 16, dstHi,
addInstr(env, AMD64Instr_SseLdSt(False/*!isLoad*/, 16, argRhi,
AMD64AMode_IR(16, hregAMD64_RDX())));
/* call the helper */
- addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3, RetLocNone ));
+ addInstr(env, AMD64Instr_Call( Acc_ALWAYS, (ULong)fn, 3,
+ mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, AMD64Instr_SseLdSt(True/*isLoad*/, 16, dstLo,
/* --------- Call to DIRTY helper --------- */
case Ist_Dirty: {
IRDirty* d = stmt->Ist.Dirty.details;
- Bool passBBP = False;
-
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
-
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Figure out the return type, if any. */
IRType retty = Ity_INVALID;
if (d->tmp != IRTemp_INVALID)
retty = typeOfIRTemp(env->type_env, d->tmp);
- /* Marshal args, do the call, clear stack, set the return value
- to 0x555..555 if this is a conditional call that returns a
- value and the call is skipped. We need to set the ret-loc
- correctly in order to implement the IRDirty semantics that
- the return value is 0x555..555 if the call doesn't happen. */
- RetLoc rloc = RetLocINVALID;
+ /* Throw out any return types we don't know about. */
+ Bool retty_ok = False;
switch (retty) {
case Ity_INVALID: /* function doesn't return anything */
- rloc = RetLocNone; break;
- case Ity_I64:
- case Ity_I32: case Ity_I16: case Ity_I8:
- rloc = RetLocInt; break;
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ case Ity_V128:
+ retty_ok = True; break;
default:
break;
}
- if (rloc == RetLocINVALID)
+ if (!retty_ok)
break; /* will go to stmt_fail: */
- /* Marshal args, do the call, clear stack. */
- doHelperCall( env, passBBP, d->guard, d->cee, d->args, rloc );
+ /* Marshal args, do the call, and set the return value to
+ 0x555..555 if this is a conditional call that returns a value
+ and the call is skipped. */
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, d->guard, d->cee, retty, d->args );
+ vassert(is_sane_RetLoc(rloc));
/* Now figure out what to do with the returned value, if any. */
- if (d->tmp == IRTemp_INVALID)
- /* No return value. Nothing to do. */
- return;
-
- if (retty == Ity_I64 || retty == Ity_I32
- || retty == Ity_I16 || retty == Ity_I8) {
- /* The returned value is in %rax. Park it in the register
- associated with tmp. */
- HReg dst = lookupIRTemp(env, d->tmp);
- addInstr(env, mk_iMOVsd_RR(hregAMD64_RAX(),dst) );
- return;
+ switch (retty) {
+ case Ity_INVALID: {
+ /* No return value. Nothing to do. */
+ vassert(d->tmp == IRTemp_INVALID);
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8: {
+ /* The returned value is in %rax. Park it in the register
+ associated with tmp. */
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVsd_RR(hregAMD64_RAX(),dst) );
+ return;
+ }
+ case Ity_V128: {
+ /* The returned value is on the stack, and *retloc tells
+ us where. Fish it off the stack and then move the
+ stack pointer upwards to clear it, as directed by
+ doHelperCall. */
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(addToSp >= 16);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ AMD64AMode* am = AMD64AMode_IR(rloc.spOff, hregAMD64_RSP());
+ addInstr(env, AMD64Instr_SseLdSt( True/*load*/, 16, dst, am ));
+ add_to_rsp(env, addToSp);
+ return;
+ }
+ default:
+ /*NOTREACHED*/
+ vassert(0);
}
break;
}
i->ARMin.Call.target = target;
i->ARMin.Call.nArgRegs = nArgRegs;
i->ARMin.Call.rloc = rloc;
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
ARMInstr* ARMInstr_Mul ( ARMMulOp op ) {
the call doesn't happen, just do the simple thing and emit
straight-line code. We hope this is the common case. */
if (i->ARMin.Call.cond == ARMcc_AL/*call always happens*/
- || i->ARMin.Call.rloc == RetLocNone/*no fixup action*/) {
+ || i->ARMin.Call.rloc.pri == RLPri_None/*no fixup action*/) {
// r"scratchNo" = &target
p = imm32_to_iregNo( (UInt*)p,
scratchNo, (UInt)i->ARMin.Call.target );
= XX______(1 ^ i->ARMin.Call.cond, X1010) | (delta & 0xFFFFFF);
/* Do the 'else' actions */
- switch (i->ARMin.Call.rloc) {
- case RetLocInt:
+ switch (i->ARMin.Call.rloc.pri) {
+ case RLPri_Int:
p = imm32_to_iregNo_EXACTLY2(p, /*r*/0, 0x55555555);
break;
- case RetLoc2Int:
+ case RLPri_2Int:
vassert(0); //ATC
p = imm32_to_iregNo_EXACTLY2(p, /*r*/0, 0x55555555);
/* mov r1, r0 */
*p++ = 0xE1A01000;
break;
- case RetLocNone: case RetLocINVALID: default:
+ case RLPri_None: case RLPri_INVALID: default:
vassert(0);
}
static
Bool mightRequireFixedRegs ( IRExpr* e )
{
+ if (UNLIKELY(is_IRExprP__VECRET_or_BBPTR(e))) {
+ // These are always "safe" -- either a copy of r13(sp) in some
+ // arbitrary vreg, or a copy of r8, respectively.
+ return False;
+ }
+ /* Else it's a "normal" expression. */
switch (e->tag) {
case Iex_RdTmp: case Iex_Const: case Iex_Get:
return False;
}
-/* Do a complete function call. guard is a Ity_Bit expression
+/* 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. Returns True iff it managed to handle this
+ call is unconditional. |retloc| is set to indicate where the
+ return value is after the call. The caller (of this fn) must
+ generate code to add |stackAdjustAfterCall| to the stack pointer
+ after the call is done. Returns True iff it managed to handle this
combination of arg/return types, else returns False. */
static
-Bool doHelperCall ( ISelEnv* env,
- Bool passBBP,
- IRExpr* guard, IRCallee* cee, IRExpr** args,
- RetLoc rloc )
+Bool doHelperCall ( /*OUT*/UInt* stackAdjustAfterCall,
+ /*OUT*/RetLoc* retloc,
+ ISelEnv* env,
+ IRExpr* guard,
+ IRCallee* cee, IRType retTy, IRExpr** args )
{
ARMCondCode cc;
HReg argregs[ARM_N_ARGREGS];
vassert(ARM_N_ARGREGS == 4);
- /* Marshal args for a call and do the call.
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
- If passBBP is True, r8 (the baseblock pointer) is to be passed
- as the first arg.
+ /* These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
+ /* Marshal args for a call and do the call.
This function only deals with a tiny set of possibilities, which
cover all helpers in practice. The restrictions are that only
x 32 integer bits in total can be passed. In fact the only
supported arg types are I32 and I64.
+ The return type can be I{64,32} or V128. In the V128 case, it
+ is expected that |args| will contain the special value
+ IRExprP__VECRET, in which case this routine generates code to
+ allocate space on the stack for the vector return value. Since
+ we are not passing any scalars on the stack, it is enough to
+ preallocate the return space before marshalling any arguments,
+ in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the
+ value in r8 is passed as the corresponding argument.
+
Generating code which is both efficient and correct when
parameters are to be passed in registers is difficult, for the
reasons elaborated in detail in comments attached to
ignore it. */
n_args = 0;
- for (i = 0; args[i]; i++)
+ for (i = 0; args[i]; i++) {
+ IRExpr* arg = args[i];
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ nVECRETs++;
+ } else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ nBBPTRs++;
+ }
n_args++;
+ }
argregs[0] = hregARM_R0();
argregs[1] = hregARM_R1();
}
}
}
+
+ if (go_fast) {
+ if (retTy == Ity_V128 || retTy == Ity_V256)
+ go_fast = False;
+ }
+
/* At this point the scheme to use has been established. Generate
code to get the arg values into the argument rregs. If we run
out of arg regs, give up. */
/* FAST SCHEME */
nextArgReg = 0;
- if (passBBP) {
- addInstr(env, mk_iMOVds_RR( argregs[nextArgReg],
- hregARM_R8() ));
- nextArgReg++;
- }
for (i = 0; i < n_args; i++) {
- IRType aTy = typeOfIRExpr(env->type_env, args[i]);
+ IRExpr* arg = args[i];
+
+ IRType aTy = Ity_INVALID;
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ aTy = typeOfIRExpr(env->type_env, arg);
+
if (nextArgReg >= ARM_N_ARGREGS)
return False; /* out of argregs */
+
if (aTy == Ity_I32) {
addInstr(env, mk_iMOVds_RR( argregs[nextArgReg],
- iselIntExpr_R(env, args[i]) ));
+ iselIntExpr_R(env, arg) ));
nextArgReg++;
}
else if (aTy == Ity_I64) {
if (nextArgReg >= ARM_N_ARGREGS)
return False; /* out of argregs */
HReg raHi, raLo;
- iselInt64Expr(&raHi, &raLo, env, args[i]);
+ iselInt64Expr(&raHi, &raLo, env, arg);
addInstr(env, mk_iMOVds_RR( argregs[nextArgReg], raLo ));
nextArgReg++;
addInstr(env, mk_iMOVds_RR( argregs[nextArgReg], raHi ));
nextArgReg++;
}
+ else if (arg == IRExprP__BBPTR) {
+ vassert(0); //ATC
+ addInstr(env, mk_iMOVds_RR( argregs[nextArgReg],
+ hregARM_R8() ));
+ nextArgReg++;
+ }
+ else if (arg == IRExprP__VECRET) {
+ // If this happens, it denotes ill-formed IR
+ vassert(0);
+ }
else
return False; /* unhandled arg type */
}
/* SLOW SCHEME; move via temporaries */
nextArgReg = 0;
- if (passBBP) {
- /* This is pretty stupid; better to move directly to r0
- after the rest of the args are done. */
- tmpregs[nextArgReg] = newVRegI(env);
- addInstr(env, mk_iMOVds_RR( tmpregs[nextArgReg],
- hregARM_R8() ));
- nextArgReg++;
- }
-
for (i = 0; i < n_args; i++) {
- IRType aTy = typeOfIRExpr(env->type_env, args[i]);
+ IRExpr* arg = args[i];
+
+ IRType aTy = Ity_INVALID;
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ aTy = typeOfIRExpr(env->type_env, arg);
+
if (nextArgReg >= ARM_N_ARGREGS)
return False; /* out of argregs */
+
if (aTy == Ity_I32) {
tmpregs[nextArgReg] = iselIntExpr_R(env, args[i]);
nextArgReg++;
tmpregs[nextArgReg] = raHi;
nextArgReg++;
}
+ else if (arg == IRExprP__BBPTR) {
+ vassert(0); //ATC
+ tmpregs[nextArgReg] = hregARM_R8();
+ nextArgReg++;
+ }
+ else if (arg == IRExprP__VECRET) {
+ // If this happens, it denotes ill-formed IR
+ vassert(0);
+ }
+ else
+ return False; /* unhandled arg type */
}
/* Now we can compute the condition. We can't do it earlier
/* Should be assured by checks above */
vassert(nextArgReg <= ARM_N_ARGREGS);
- target = (HWord)Ptr_to_ULong(cee->addr);
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+ vassert(nVECRETs == (retTy == Ity_V128 || retTy == Ity_V256) ? 1 : 0);
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64:
+ *retloc = mk_RetLoc_simple(RLPri_2Int);
+ break;
+ case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
+ *stackAdjustAfterCall = 16;
+ break;
+ case Ity_V256:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
+ *stackAdjustAfterCall = 32;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
+
+ /* Finally, generate the call itself. This needs the *retloc value
+ set in the switch above, which is why it's at the end. */
/* nextArgReg doles out argument registers. Since these are
assigned in the order r0, r1, r2, r3, its numeric value at this
instruction, a bitmask indicating which of r0/1/2/3 carry live
values. But that's too much hassle. */
- /* Finally, the call itself. */
- addInstr(env, ARMInstr_Call( cc, target, nextArgReg, rloc ));
+ target = (HWord)Ptr_to_ULong(cee->addr);
+ addInstr(env, ARMInstr_Call( cc, target, nextArgReg, *retloc ));
return True; /* success */
}
addInstr(env, mk_iMOVds_RR(hregARM_R0(), regL));
addInstr(env, mk_iMOVds_RR(hregARM_R1(), regR));
addInstr(env, ARMInstr_Call( ARMcc_AL, (HWord)Ptr_to_ULong(fn),
- 2, RetLocInt ));
+ 2, mk_RetLoc_simple(RLPri_Int) ));
addInstr(env, mk_iMOVds_RR(res, hregARM_R0()));
return res;
}
HReg res = newVRegI(env);
addInstr(env, mk_iMOVds_RR(hregARM_R0(), arg));
addInstr(env, ARMInstr_Call( ARMcc_AL, (HWord)Ptr_to_ULong(fn),
- 1, RetLocInt ));
+ 1, mk_RetLoc_simple(RLPri_Int) ));
addInstr(env, mk_iMOVds_RR(res, hregARM_R0()));
return res;
}
goto irreducible;
/* Marshal args, do the call, clear stack. */
- Bool ok = doHelperCall( env, False,
- NULL, e->Iex.CCall.cee, e->Iex.CCall.args,
- RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ Bool ok = doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ e->Iex.CCall.cee, e->Iex.CCall.retty,
+ e->Iex.CCall.args );
+ /* */
if (ok) {
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
addInstr(env, mk_iMOVds_RR(dst, hregARM_R0()));
return dst;
}
/* call complex ("dirty") helper function */
case Ist_Dirty: {
IRDirty* d = stmt->Ist.Dirty.details;
- Bool passBBP = False;
-
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
-
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Figure out the return type, if any. */
IRType retty = Ity_INVALID;
if (d->tmp != IRTemp_INVALID)
retty = typeOfIRTemp(env->type_env, d->tmp);
- /* Marshal args, do the call, clear stack, set the return value
- to 0x555..555 if this is a conditional call that returns a
- value and the call is skipped. We need to set the ret-loc
- correctly in order to implement the IRDirty semantics that
- the return value is 0x555..555 if the call doesn't happen. */
- RetLoc rloc = RetLocINVALID;
+ Bool retty_ok = False;
switch (retty) {
case Ity_INVALID: /* function doesn't return anything */
- rloc = RetLocNone; break;
- case Ity_I64:
- rloc = RetLoc2Int; break;
- case Ity_I32: case Ity_I16: case Ity_I8:
- rloc = RetLocInt; break;
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ //case Ity_V128: //ATC
+ retty_ok = True; break;
default:
break;
}
- if (rloc == RetLocINVALID)
+ if (!retty_ok)
break; /* will go to stmt_fail: */
- Bool ok = doHelperCall( env, passBBP, d->guard, d->cee, d->args, rloc );
- if (!ok)
- break; /* will go to stmt_fail: */
+ /* Marshal args, do the call, and set the return value to 0x555..555
+ if this is a conditional call that returns a value and the
+ call is skipped. */
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, d->guard, d->cee, retty, d->args );
+ vassert(is_sane_RetLoc(rloc));
/* Now figure out what to do with the returned value, if any. */
- if (d->tmp == IRTemp_INVALID)
- /* No return value. Nothing to do. */
- return;
-
- if (retty == Ity_I64) {
- if (env->hwcaps & VEX_HWCAPS_ARM_NEON) {
- HReg tmp = lookupIRTemp(env, d->tmp);
- addInstr(env, ARMInstr_VXferD(True, tmp, hregARM_R1(),
- hregARM_R0()));
- } else {
- HReg dstHi, dstLo;
- /* The returned value is in r1:r0. Park it in the
- register-pair associated with tmp. */
- lookupIRTemp64( &dstHi, &dstLo, env, d->tmp);
- addInstr(env, mk_iMOVds_RR(dstHi, hregARM_R1()) );
- addInstr(env, mk_iMOVds_RR(dstLo, hregARM_R0()) );
+ switch (retty) {
+ case Ity_INVALID: {
+ /* No return value. Nothing to do. */
+ vassert(d->tmp == IRTemp_INVALID);
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+ return;
}
- return;
- }
- if (retty == Ity_I32 || retty == Ity_I16 || retty == Ity_I8) {
- /* The returned value is in r0. Park it in the register
- associated with tmp. */
- HReg dst = lookupIRTemp(env, d->tmp);
- addInstr(env, mk_iMOVds_RR(dst, hregARM_R0()) );
- return;
+ case Ity_I64: {
+ vassert(rloc.pri == RLPri_2Int);
+ vassert(addToSp == 0);
+ if (env->hwcaps & VEX_HWCAPS_ARM_NEON) {
+ HReg tmp = lookupIRTemp(env, d->tmp);
+ addInstr(env, ARMInstr_VXferD(True, tmp, hregARM_R1(),
+ hregARM_R0()));
+ } else {
+ HReg dstHi, dstLo;
+ /* The returned value is in r1:r0. Park it in the
+ register-pair associated with tmp. */
+ lookupIRTemp64( &dstHi, &dstLo, env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(dstHi, hregARM_R1()) );
+ addInstr(env, mk_iMOVds_RR(dstLo, hregARM_R0()) );
+ }
+ return;
+ }
+ case Ity_I32: case Ity_I16: case Ity_I8: {
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ /* The returned value is in r0. Park it in the register
+ associated with tmp. */
+ HReg dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(dst, hregARM_R0()) );
+ return;
+ }
+ case Ity_V128: {
+ vassert(0); // ATC. The code that this produces really
+ // needs to be looked at, to verify correctness.
+ // I don't think this can ever happen though, since the
+ // ARM front end never produces 128-bit loads/stores.
+ // Hence the following is mostly theoretical.
+ /* The returned value is on the stack, and *retloc tells
+ us where. Fish it off the stack and then move the
+ stack pointer upwards to clear it, as directed by
+ doHelperCall. */
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(rloc.spOff < 256); // else ARMRI84_I84(_,0) can't encode it
+ vassert(addToSp >= 16);
+ vassert(addToSp < 256); // ditto reason as for rloc.spOff
+ HReg dst = lookupIRTemp(env, d->tmp);
+ HReg tmp = newVRegI(env);
+ HReg r13 = hregARM_R13(); // sp
+ addInstr(env, ARMInstr_Alu(ARMalu_ADD,
+ tmp, r13, ARMRI84_I84(rloc.spOff,0)));
+ ARMAModeN* am = mkARMAModeN_R(tmp);
+ addInstr(env, ARMInstr_NLdStQ(True/*load*/, dst, am));
+ addInstr(env, ARMInstr_Alu(ARMalu_ADD,
+ r13, r13, ARMRI84_I84(addToSp,0)));
+ return;
+ }
+ default:
+ /*NOTREACHED*/
+ vassert(0);
}
-
break;
}
void ppRetLoc ( RetLoc ska )
{
- switch (ska) {
- case RetLocINVALID: vex_printf("RetLocINVALID"); return;
- case RetLocNone: vex_printf("RetLocNone"); return;
- case RetLocInt: vex_printf("RetLocInt"); return;
- case RetLoc2Int: vex_printf("RetLoc2Int"); return;
- default: vpanic("ppRetLoc");
+ switch (ska.pri) {
+ case RLPri_INVALID:
+ vex_printf("RLPri_INVALID"); return;
+ case RLPri_None:
+ vex_printf("RLPri_None"); return;
+ case RLPri_Int:
+ vex_printf("RLPri_Int"); return;
+ case RLPri_2Int:
+ vex_printf("RLPri_2Int"); return;
+ case RLPri_V128SpRel:
+ vex_printf("RLPri_V128SpRel(%d)", ska.spOff); return;
+ case RLPri_V256SpRel:
+ vex_printf("RLPri_V256SpRel(%d)", ska.spOff); return;
+ default:
+ vpanic("ppRetLoc");
}
}
typedef
enum {
- RetLocINVALID, /* INVALID */
- RetLocNone, /* no return value (a.k.a C "void") */
- RetLocInt, /* in the primary int return reg */
- RetLoc2Int /* in both primary and secondary int ret regs */
+ RLPri_INVALID, /* INVALID */
+ RLPri_None, /* no return value (a.k.a C "void") */
+ RLPri_Int, /* in the primary int return reg */
+ RLPri_2Int, /* in both primary and secondary int ret regs */
+ RLPri_V128SpRel, /* 128-bit value, on the stack */
+ RLPri_V256SpRel /* 256-bit value, on the stack */
+ }
+ RetLocPrimary;
+
+typedef
+ struct {
+ /* Primary description */
+ RetLocPrimary pri;
+ /* For .pri == RLPri_V128SpRel or RLPri_V256SpRel only, gives
+ the offset of the lowest addressed byte of the value,
+ relative to the stack pointer. For all other .how values,
+ has no meaning and should be zero. */
+ Int spOff;
}
RetLoc;
extern void ppRetLoc ( RetLoc rloc );
+static inline RetLoc mk_RetLoc_simple ( RetLocPrimary pri ) {
+ vassert(pri >= RLPri_INVALID && pri <= RLPri_2Int);
+ return (RetLoc){pri, 0};
+}
+
+static inline RetLoc mk_RetLoc_spRel ( RetLocPrimary pri, Int off ) {
+ vassert(pri >= RLPri_V128SpRel && pri <= RLPri_V256SpRel);
+ return (RetLoc){pri, off};
+}
+
+static inline Bool is_sane_RetLoc ( RetLoc rloc ) {
+ switch (rloc.pri) {
+ case RLPri_None: case RLPri_Int: case RLPri_2Int:
+ return rloc.spOff == 0;
+ case RLPri_V128SpRel: case RLPri_V256SpRel:
+ return True;
+ default:
+ return False;
+ }
+}
+
+static inline RetLoc mk_RetLoc_INVALID ( void ) {
+ return (RetLoc){RLPri_INVALID, 0};
+}
+
+static inline Bool is_RetLoc_INVALID ( RetLoc rl ) {
+ return rl.pri == RLPri_INVALID && rl.spOff == 0;
+}
+
/*---------------------------------------------------------*/
/*--- Reg alloc: TODO: move somewhere else ---*/
mask = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9)
| (1 << 10) | (1 << 11);
vassert(0 == (argiregs & ~mask));
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
mask = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9)
| (1 << 10) | (1 << 11);
vassert(0 == (argiregs & ~mask));
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
}
case Min_Call: {
- if (i->Min.Call.cond != MIPScc_AL && i->Min.Call.rloc != RetLocNone) {
+ if (i->Min.Call.cond != MIPScc_AL
+ && i->Min.Call.rloc.pri != RLPri_None) {
/* The call might not happen (it isn't unconditional) and
it returns a result. In this case we will need to
generate a control flow diamond to put 0x555..555 in
return fr_dst;
}
-/* Do a complete function call. guard is a Ity_Bit expression
+/* 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, RetLoc rloc)
+ call is unconditional. |retloc| is set to indicate where the
+ return value is after the call. The caller (of this fn) must
+ generate code to add |stackAdjustAfterCall| to the stack pointer
+ after the call is done. */
+
+static void doHelperCall(/*OUT*/UInt* stackAdjustAfterCall,
+ /*OUT*/RetLoc* retloc,
+ ISelEnv* env,
+ IRExpr* guard,
+ IRCallee* cee, IRType retTy, IRExpr** args )
{
MIPSCondCode cc;
HReg argregs[MIPS_N_REGPARMS];
Bool go_fast;
Int n_args, i, argreg;
UInt argiregs;
- ULong target;
HReg src = INVALID_HREG;
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
+
+ /* These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
/* MIPS O32 calling convention: up to four registers ($a0 ... $a3)
are allowed to be used for passing integer arguments. They correspond
to regs GPR4 ... GPR7. Note that the cee->regparms field is meaningless
to regs GPR4 ... GPR11. Note that the cee->regparms field is meaningless
on MIPS host (since we only implement one calling convention) and so we
always ignore it. */
+
+ /* The return type can be I{64,32,16,8} or V{128,256}. In the
+ latter two cases, it is expected that |args| will contain the
+ special value IRExprP__VECRET, in which case this routine
+ generates code to allocate space on the stack for the vector
+ return value. Since we are not passing any scalars on the
+ stack, it is enough to preallocate the return space before
+ marshalling any arguments, in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the value
+ in the guest state pointer register is passed as the
+ corresponding argument. */
+
n_args = 0;
- for (i = 0; args[i]; i++)
+ for (i = 0; args[i]; i++) {
+ IRExpr* arg = args[i];
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ nVECRETs++;
+ } else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ nBBPTRs++;
+ }
n_args++;
+ }
- if (MIPS_N_REGPARMS < n_args + (passBBP ? 1 : 0)) {
+ if (n_args > MIPS_N_REGPARMS) {
vpanic("doHelperCall(MIPS): cannot currently handle > 4 or 8 args");
}
if (mode64) {
argregs[6] = hregMIPS_GPR10(mode64);
argregs[7] = hregMIPS_GPR11(mode64);
argiregs = 0;
+ tmpregs[0] = tmpregs[1] = tmpregs[2] =
+ tmpregs[3] = tmpregs[4] = tmpregs[5] =
+ tmpregs[6] = tmpregs[7] = INVALID_HREG;
} else {
argregs[0] = hregMIPS_GPR4(mode64);
argregs[1] = hregMIPS_GPR5(mode64);
argregs[2] = hregMIPS_GPR6(mode64);
argregs[3] = hregMIPS_GPR7(mode64);
argiregs = 0;
+ tmpregs[0] = tmpregs[1] = tmpregs[2] = tmpregs[3] = INVALID_HREG;
}
- tmpregs[0] = tmpregs[1] = tmpregs[2] = tmpregs[3] = INVALID_HREG;
-
/* First decide which scheme (slow or fast) is to be used. First assume the
fast scheme, and select slow if any contraindications (wow) appear. */
go_fast = True;
- if (guard) {
+ /* We'll need space on the stack for the return value. Avoid
+ possible complications with nested calls by using the slow
+ scheme. */
+ if (retTy == Ity_V128 || retTy == Ity_V256)
+ go_fast = False;
+
+ if (go_fast && guard) {
if (guard->tag == Iex_Const && guard->Iex.Const.con->tag == Ico_U1
&& guard->Iex.Const.con->Ico.U1 == True) {
/* unconditional */
if (go_fast) {
/* FAST SCHEME */
argreg = 0;
- if (passBBP) {
- argiregs |= (1 << (argreg + 4));
- addInstr(env, mk_iMOVds_RR(argregs[argreg],
- GuestStatePointer(mode64)));
- argreg++;
- }
for (i = 0; i < n_args; i++) {
+ IRExpr* arg = args[i];
vassert(argreg < MIPS_N_REGPARMS);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32
- || typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- if (typeOfIRExpr(env->type_env, args[i]) == Ity_I32 || mode64) {
+
+ IRType aTy = Ity_INVALID;
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ aTy = typeOfIRExpr(env->type_env, arg);
+
+ if (aTy == Ity_I32 || mode64) {
argiregs |= (1 << (argreg + 4));
- addInstr(env, mk_iMOVds_RR(argregs[argreg], iselWordExpr_R(env,
- args[i])));
- } else { /* Ity_I64 */
+ addInstr(env, mk_iMOVds_RR(argregs[argreg],
+ iselWordExpr_R(env, arg)));
+ argreg++;
+ } else if (aTy == Ity_I64) { /* Ity_I64 */
if (argreg & 1) {
argreg++;
argiregs |= (1 << (argreg + 4));
}
HReg rHi, rLo;
- iselInt64Expr(&rHi, &rLo, env, args[i]);
+ iselInt64Expr(&rHi, &rLo, env, arg);
argiregs |= (1 << (argreg + 4));
addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi ));
argiregs |= (1 << (argreg + 4));
addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo));
+ argreg++;
+ } else if (arg == IRExprP__BBPTR) {
+ vassert(0); // ATC
+ addInstr(env, mk_iMOVds_RR(argregs[argreg],
+ GuestStatePointer(mode64)));
+ argreg++;
+ } else if (arg == IRExprP__VECRET) {
+ // If this happens, it denotes ill-formed IR.
+ vassert(0);
}
- argreg++;
}
/* Fast scheme only applies for unconditional calls. Hence: */
cc = MIPScc_AL;
} else {
/* SLOW SCHEME; move via temporaries */
argreg = 0;
- if (passBBP) {
- /* This is pretty stupid; better to move directly to r3
- after the rest of the args are done. */
- tmpregs[argreg] = newVRegI(env);
- addInstr(env, mk_iMOVds_RR(tmpregs[argreg],
- GuestStatePointer(mode64)));
- argreg++;
- }
+
for (i = 0; i < n_args; i++) {
vassert(argreg < MIPS_N_REGPARMS);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32
- || typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- if (typeOfIRExpr(env->type_env, args[i]) == Ity_I32 || mode64) {
- tmpregs[argreg] = iselWordExpr_R(env, args[i]);
- } else { /* Ity_I64 */
+ IRExpr* arg = args[i];
+
+ IRType aTy = Ity_INVALID;
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ aTy = typeOfIRExpr(env->type_env, arg);
+
+ if (aTy == Ity_I32 || mode64) {
+ tmpregs[argreg] = iselWordExpr_R(env, arg);
+ argreg++;
+ } else if (aTy == Ity_I64) { /* Ity_I64 */
if (argreg & 1)
argreg++;
if (argreg + 1 >= MIPS_N_REGPARMS)
vassert(0); /* out of argregs */
HReg raHi, raLo;
- iselInt64Expr(&raHi, &raLo, env, args[i]);
+ iselInt64Expr(&raHi, &raLo, env, arg);
tmpregs[argreg] = raLo;
argreg++;
tmpregs[argreg] = raHi;
+ argreg++;
+ } else if (arg == IRExprP__BBPTR) {
+ vassert(0); // ATC
+ tmpregs[argreg] = GuestStatePointer(mode64);
+ argreg++;
+ }
+ else if (arg == IRExprP__VECRET) {
+ // If this happens, it denotes ill-formed IR
+ vassert(0);
}
- argreg++;
}
/* Now we can compute the condition. We can't do it earlier
}
}
- target = mode64 ? Ptr_to_ULong(cee->addr) :
- toUInt(Ptr_to_ULong(cee->addr));
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+ vassert(nVECRETs == (retTy == Ity_V128 || retTy == Ity_V256) ? 1 : 0);
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64:
+ *retloc = mk_RetLoc_simple(mode64 ? RLPri_Int : RLPri_2Int);
+ break;
+ case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
+ *stackAdjustAfterCall = 16;
+ break;
+ case Ity_V256:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
+ *stackAdjustAfterCall = 32;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
- /* Finally, the call itself. */
+ ULong target = mode64 ? Ptr_to_ULong(cee->addr) :
+ toUInt(Ptr_to_ULong(cee->addr));
+
+ /* Finally, generate the call itself. This needs the *retloc value
+ set in the switch above, which is why it's at the end. */
if (cc == MIPScc_AL)
- addInstr(env, MIPSInstr_CallAlways(cc, (Addr64)target, argiregs, rloc));
+ addInstr(env, MIPSInstr_CallAlways(cc, (Addr64)target, argiregs,
+ *retloc));
else
- addInstr(env, MIPSInstr_Call(cc, (Addr64)target, argiregs, src, rloc));
+ addInstr(env, MIPSInstr_Call(cc, (Addr64)target, argiregs, src, *retloc));
}
/*---------------------------------------------------------*/
}
/* What's the retloc? */
- RetLoc rloc = RetLocINVALID;
+ RetLoc rloc = mk_RetLoc_INVALID();
if (ty == Ity_I32) {
- rloc = RetLocInt;
+ rloc = mk_RetLoc_simple(RLPri_Int);
}
else if (ty == Ity_I64) {
- rloc = mode64 ? RetLocInt : RetLoc2Int;
+ rloc = mode64 ? mk_RetLoc_simple(RLPri_Int) :
+ mk_RetLoc_simple(RLPri_2Int);
}
else {
goto irreducible;
break;
}
- RetLoc rloc = RetLocINVALID;
+ RetLoc rloc = mk_RetLoc_INVALID();
if (ty == Ity_I32) {
- rloc = RetLocInt;
+ rloc = mk_RetLoc_simple(RLPri_Int);
}
else if (ty == Ity_I64) {
- rloc = mode64 ? RetLocInt : RetLoc2Int;
+ rloc = mode64 ? mk_RetLoc_simple(RLPri_Int) :
+ mk_RetLoc_simple(RLPri_2Int);
}
else {
goto irreducible;
/* be very restrictive for now. Only 32/64-bit ints allowed for
args, and 32 bits for return type. Don't forget to change
the RetLoc if more return types are allowed in future. */
- if (e->Iex.CCall.retty != Ity_I32 && !mode64)
+ if (e->Iex.CCall.retty != Ity_I32)
goto irreducible;
- /* What's the retloc? */
- RetLoc rloc = RetLocINVALID;
- if (ty == Ity_I32) {
- rloc = RetLocInt;
- }
- else if (ty == Ity_I64) {
- rloc = mode64 ? RetLocInt : RetLoc2Int;
- }
- else {
- goto irreducible;
- }
-
/* Marshal args, do the call, clear stack. */
- doHelperCall(env, False, NULL, e->Iex.CCall.cee, e->Iex.CCall.args, rloc);
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall(&addToSp, &rloc, env, NULL/*guard*/, e->Iex.CCall.cee,
+ e->Iex.CCall.retty, e->Iex.CCall.args );
+
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
addInstr(env, mk_iMOVds_RR(r_dst, hregMIPS_GPR2(mode64)));
return r_dst;
}
/* --------- Call to DIRTY helper --------- */
case Ist_Dirty: {
IRDirty *d = stmt->Ist.Dirty.details;
- Bool passBBP = False;
-
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
-
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Figure out the return type, if any. */
IRType retty = Ity_INVALID;
if (d->tmp != IRTemp_INVALID)
retty = typeOfIRTemp(env->type_env, d->tmp);
- /* Marshal args, do the call, clear stack, set the return
- value to 0x555..555 if this is a conditional call that
- returns a value and the call is skipped. We need to set
- the ret-loc correctly in order to implement the IRDirty
- semantics that the return value is 0x555..555 if the call
- doesn't happen. */
- RetLoc rloc = RetLocINVALID;
+ /* Throw out any return types we don't know about. */
+ Bool retty_ok = False;
switch (retty) {
- case Ity_INVALID: /* function doesn't return anything */
- rloc = RetLocNone; break;
- case Ity_I64:
- rloc = mode64 ? RetLocInt : RetLoc2Int; break;
- case Ity_I32: case Ity_I16: case Ity_I8:
- rloc = RetLocInt; break;
+ case Ity_INVALID: /* Function doesn't return anything. */
+ case Ity_V128:
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ retty_ok = True; break;
default:
break;
}
- if (rloc == RetLocINVALID)
+
+ if (!retty_ok)
break; /* will go to stmt_fail: */
- /* Marshal args, do the call, clear stack. */
- doHelperCall(env, passBBP, d->guard, d->cee, d->args, rloc);
+ /* Marshal args, do the call, clear stack, set the return value
+ to 0x555..555 if this is a conditional call that returns a
+ value and the call is skipped. */
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, d->guard, d->cee, retty, d->args );
+ vassert(is_sane_RetLoc(rloc));
/* Now figure out what to do with the returned value, if any. */
- if (d->tmp == IRTemp_INVALID)
- /* No return value. Nothing to do. */
- return;
+ switch (retty) {
+ case Ity_INVALID: {
+ /* No return value. Nothing to do. */
+ vassert(d->tmp == IRTemp_INVALID);
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I32: case Ity_I16: case Ity_I8: {
+ /* The returned value is in $v0. Park it in the register
+ associated with tmp. */
+ HReg r_dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(r_dst, hregMIPS_GPR2(mode64)));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I64: {
+ if (mode64) {
+ /* The returned value is in $v0. Park it in the register
+ associated with tmp. */
+ HReg r_dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(r_dst, hregMIPS_GPR2(mode64)));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ return;
+ } else {
+ HReg rHi = newVRegI(env);
+ HReg rLo = newVRegI(env);
+ HReg dstHi, dstLo;
+ addInstr(env, mk_iMOVds_RR(rLo, hregMIPS_GPR2(mode64)));
+ addInstr(env, mk_iMOVds_RR(rHi, hregMIPS_GPR3(mode64)));
+ lookupIRTemp64(&dstHi, &dstLo, env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(dstHi, rHi));
+ addInstr(env, mk_iMOVds_RR(dstLo, rLo));
+ return;
+ }
+ }
+ case Ity_V128: {
+ /* ATC. The code that this produces really
+ needs to be looked at, to verify correctness.
+ I don't think this can ever happen though, since the
+ MIPS front end never produces 128-bit loads/stores. */
+ vassert(0);
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(addToSp >= 16);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ MIPSAMode* am = MIPSAMode_IR(rloc.spOff, StackPointer(mode64));
+ addInstr(env, MIPSInstr_Load(mode64 ? 8 : 4, dst, am, mode64));
+ add_to_sp(env, addToSp);
+ return;
- if (retty == Ity_I64 && !mode64) {
- HReg rHi = newVRegI(env);
- HReg rLo = newVRegI(env);
- HReg dstHi, dstLo;
- addInstr(env, mk_iMOVds_RR(rLo, hregMIPS_GPR2(mode64)));
- addInstr(env, mk_iMOVds_RR(rHi, hregMIPS_GPR3(mode64)));
- lookupIRTemp64(&dstHi, &dstLo, env, d->tmp);
- addInstr(env, mk_iMOVds_RR(dstHi, rHi));
- addInstr(env, mk_iMOVds_RR(dstLo, rLo));
- return;
- }
- if (retty == Ity_I8 || retty == Ity_I16 || retty == Ity_I32
- || (retty == Ity_I64 && mode64)) {
- /* The returned value is in %r2. Park it in the register
- associated with tmp. */
- HReg r_dst = lookupIRTemp(env, d->tmp);
- addInstr(env, mk_iMOVds_RR(r_dst, hregMIPS_GPR2(mode64)));
- return;
+ }
+ default:
+ /*NOTREACHED*/
+ vassert(0);
}
- break;
}
/* --------- Load Linked or Store Conditional --------- */
/* Only r3 .. r10 inclusive may be used as arg regs. Hence: */
mask = (1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10);
vassert(0 == (argiregs & ~mask));
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
PPCInstr* PPCInstr_XDirect ( Addr64 dstGA, PPCAMode* amCIA,
case Pin_Call: {
if (i->Pin.Call.cond.test != Pct_ALWAYS
- && i->Pin.Call.rloc != RetLocNone) {
+ && i->Pin.Call.rloc.pri != RLPri_None) {
/* The call might not happen (it isn't unconditional) and it
returns a result. In this case we will need to generate a
control flow diamond to put 0x555..555 in the return
static void add_to_sp ( ISelEnv* env, UInt n )
{
HReg sp = StackFramePtr(env->mode64);
- vassert(n < 256 && (n%16) == 0);
+ vassert(n <= 1024 && (n%16) == 0);
addInstr(env, PPCInstr_Alu( Palu_ADD, sp, sp,
PPCRH_Imm(True,toUShort(n)) ));
}
static void sub_from_sp ( ISelEnv* env, UInt n )
{
HReg sp = StackFramePtr(env->mode64);
- vassert(n < 256 && (n%16) == 0);
+ vassert(n <= 1024 && (n%16) == 0);
addInstr(env, PPCInstr_Alu( Palu_SUB, sp, sp,
PPCRH_Imm(True,toUShort(n)) ));
}
}
-/* Do a complete function call. guard is a Ity_Bit expression
+/* 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. */
+ call is unconditional. |retloc| is set to indicate where the
+ return value is after the call. The caller (of this fn) must
+ generate code to add |stackAdjustAfterCall| to the stack pointer
+ after the call is done. */
static
-void doHelperCall ( ISelEnv* env,
- Bool passBBP,
- IRExpr* guard, IRCallee* cee, IRExpr** args,
- RetLoc rloc )
+void doHelperCall ( /*OUT*/UInt* stackAdjustAfterCall,
+ /*OUT*/RetLoc* retloc,
+ ISelEnv* env,
+ IRExpr* guard,
+ IRCallee* cee, IRType retTy, IRExpr** args )
{
PPCCondCode cc;
HReg argregs[PPC_N_REGPARMS];
Bool go_fast;
Int n_args, i, argreg;
UInt argiregs;
- ULong target;
Bool mode64 = env->mode64;
- /* Do we need to force use of an odd-even reg pair for 64-bit
- args? */
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
+
+ /* These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
+ /* Do we need to force use of an odd-even reg pair for 64-bit args?
+ JRS 31-07-2013: is this still relevant, now that we are not
+ generating code for 32-bit AIX ? */
Bool regalign_int64s
= (!mode64) && env->vbi->host_ppc32_regalign_int64_args;
/* Marshal args for a call and do the call.
- If passBBP is True, %rbp (the baseblock pointer) is to be passed
- as the first arg.
-
This function only deals with a tiny set of possibilities, which
cover all helpers in practice. The restrictions are that only
arguments in registers are supported, hence only PPC_N_REGPARMS x
(mode32:32 | mode64:64) integer bits in total can be passed.
In fact the only supported arg type is (mode32:I32 | mode64:I64).
+ The return type can be I{64,32,16,8} or V{128,256}. In the
+ latter two cases, it is expected that |args| will contain the
+ special value IRExprP__VECRET, in which case this routine
+ generates code to allocate space on the stack for the vector
+ return value. Since we are not passing any scalars on the
+ stack, it is enough to preallocate the return space before
+ marshalling any arguments, in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the value
+ in the guest state pointer register is passed as the
+ corresponding argument.
+
Generating code which is both efficient and correct when
parameters are to be passed in registers is difficult, for the
reasons elaborated in detail in comments attached to
for (i = 0; args[i]; i++)
n_args++;
- if (PPC_N_REGPARMS < n_args + (passBBP ? 1 : 0)) {
+ if (n_args > PPC_N_REGPARMS) {
vpanic("doHelperCall(PPC): cannot currently handle > 8 args");
// PPC_N_REGPARMS
}
+
+ /* This is kind of stupid .. the arrays are sized as PPC_N_REGPARMS
+ but we then assume that that value is 8. */
+ vassert(PPC_N_REGPARMS == 8);
argregs[0] = hregPPC_GPR3(mode64);
argregs[1] = hregPPC_GPR4(mode64);
go_fast = True;
- if (guard) {
+ /* We'll need space on the stack for the return value. Avoid
+ possible complications with nested calls by using the slow
+ scheme. */
+ if (retTy == Ity_V128 || retTy == Ity_V256)
+ go_fast = False;
+
+ if (go_fast && guard) {
if (guard->tag == Iex_Const
&& guard->Iex.Const.con->tag == Ico_U1
&& guard->Iex.Const.con->Ico.U1 == True) {
if (go_fast) {
for (i = 0; i < n_args; i++) {
- if (mightRequireFixedRegs(args[i])) {
+ IRExpr* arg = args[i];
+ if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ /* that's OK */
+ }
+ else if (UNLIKELY(arg == IRExprP__VECRET)) {
+ /* This implies ill-formed IR, since if the IR was
+ well-formed, the return-type test above would have
+ filtered it out. */
+ vpanic("doHelperCall(PPC): invalid IR");
+ }
+ else if (mightRequireFixedRegs(arg)) {
go_fast = False;
break;
}
/* FAST SCHEME */
argreg = 0;
- if (passBBP) {
- argiregs |= (1 << (argreg+3));
- addInstr(env, mk_iMOVds_RR( argregs[argreg],
- GuestStatePtr(mode64) ));
- argreg++;
- }
for (i = 0; i < n_args; i++) {
+ IRExpr* arg = args[i];
vassert(argreg < PPC_N_REGPARMS);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32 ||
- typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- if (!mode64) {
- if (typeOfIRExpr(env->type_env, args[i]) == Ity_I32) {
- argiregs |= (1 << (argreg+3));
- addInstr(env,
- mk_iMOVds_RR( argregs[argreg],
- iselWordExpr_R(env, args[i]) ));
- } else { // Ity_I64
- HReg rHi, rLo;
- if (regalign_int64s && (argreg%2) == 1)
- // ppc32 ELF abi spec for passing LONG_LONG
- argreg++; // XXX: odd argreg => even rN
- vassert(argreg < PPC_N_REGPARMS-1);
- iselInt64Expr(&rHi,&rLo, env, args[i]);
- argiregs |= (1 << (argreg+3));
- addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi ));
- argiregs |= (1 << (argreg+3));
- addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo));
- }
- } else { // mode64
+
+ if (arg == IRExprP__BBPTR) {
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg],
- iselWordExpr_R(env, args[i]) ));
- }
- argreg++;
+ GuestStatePtr(mode64) ));
+ argreg++;
+ } else {
+ vassert(arg != IRExprP__VECRET);
+ IRType ty = typeOfIRExpr(env->type_env, arg);
+ vassert(ty == Ity_I32 || ty == Ity_I64);
+ if (!mode64) {
+ if (ty == Ity_I32) {
+ argiregs |= (1 << (argreg+3));
+ addInstr(env,
+ mk_iMOVds_RR( argregs[argreg],
+ iselWordExpr_R(env, arg) ));
+ } else { // Ity_I64 in 32-bit mode
+ HReg rHi, rLo;
+ if (regalign_int64s && (argreg%2) == 1)
+ // ppc32 ELF abi spec for passing LONG_LONG
+ argreg++; // XXX: odd argreg => even rN
+ vassert(argreg < PPC_N_REGPARMS-1);
+ iselInt64Expr(&rHi,&rLo, env, arg);
+ argiregs |= (1 << (argreg+3));
+ addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi ));
+ argiregs |= (1 << (argreg+3));
+ addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo));
+ }
+ } else { // mode64
+ argiregs |= (1 << (argreg+3));
+ addInstr(env, mk_iMOVds_RR( argregs[argreg],
+ iselWordExpr_R(env, arg) ));
+ }
+ argreg++;
+ } /* if (arg == IRExprP__BBPR) */
}
/* Fast scheme only applies for unconditional calls. Hence: */
/* SLOW SCHEME; move via temporaries */
argreg = 0;
- if (passBBP) {
- /* This is pretty stupid; better to move directly to r3
- after the rest of the args are done. */
- tmpregs[argreg] = newVRegI(env);
- addInstr(env, mk_iMOVds_RR( tmpregs[argreg],
- GuestStatePtr(mode64) ));
- argreg++;
- }
-
+ /* If we have a vector return type, allocate a place for it on
+ the stack and record its address. Rather than figure out the
+ complexities of PPC{32,64} ELF ABI stack frame layout, simply
+ drop the SP by 1024 and allocate the return point in the
+ middle. I think this should comfortably clear any ABI
+ mandated register save areas. Note that it doesn't maintain
+ the backchain as it should, since we're not doing st{d,w}u to
+ adjust the SP, but .. that doesn't seem to be a big deal.
+ Since we're not expecting to have to unwind out of here. */
+ HReg r_vecRetAddr = INVALID_HREG;
+ if (retTy == Ity_V128) {
+ r_vecRetAddr = newVRegI(env);
+ sub_from_sp(env, 512);
+ addInstr(env, mk_iMOVds_RR( r_vecRetAddr, StackFramePtr(mode64) ));
+ sub_from_sp(env, 512);
+ }
+ else if (retTy == Ity_V256) {
+ vassert(0); //ATC
+ r_vecRetAddr = newVRegI(env);
+ sub_from_sp(env, 512);
+ addInstr(env, mk_iMOVds_RR( r_vecRetAddr, StackFramePtr(mode64) ));
+ sub_from_sp(env, 512);
+ }
+
+ vassert(n_args >= 0 && n_args <= 8);
for (i = 0; i < n_args; i++) {
+ IRExpr* arg = args[i];
vassert(argreg < PPC_N_REGPARMS);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32 ||
- typeOfIRExpr(env->type_env, args[i]) == Ity_I64);
- if (!mode64) {
- if (typeOfIRExpr(env->type_env, args[i]) == Ity_I32) {
- tmpregs[argreg] = iselWordExpr_R(env, args[i]);
- } else { // Ity_I64
- HReg rHi, rLo;
- if (regalign_int64s && (argreg%2) == 1)
- // ppc32 ELF abi spec for passing LONG_LONG
- argreg++; // XXX: odd argreg => even rN
- vassert(argreg < PPC_N_REGPARMS-1);
- iselInt64Expr(&rHi,&rLo, env, args[i]);
- tmpregs[argreg++] = rHi;
- tmpregs[argreg] = rLo;
+ if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ tmpregs[argreg] = newVRegI(env);
+ addInstr(env, mk_iMOVds_RR( tmpregs[argreg],
+ GuestStatePtr(mode64) ));
+ nBBPTRs++;
+ }
+ else if (UNLIKELY(arg == IRExprP__VECRET)) {
+ /* We stashed the address of the return slot earlier, so just
+ retrieve it now. */
+ vassert(!hregIsInvalid(r_vecRetAddr));
+ tmpregs[i] = r_vecRetAddr;
+ nVECRETs++;
+ }
+ else {
+ IRType ty = typeOfIRExpr(env->type_env, arg);
+ vassert(ty == Ity_I32 || ty == Ity_I64);
+ if (!mode64) {
+ if (ty == Ity_I32) {
+ tmpregs[argreg] = iselWordExpr_R(env, arg);
+ } else { // Ity_I64 in 32-bit mode
+ HReg rHi, rLo;
+ if (regalign_int64s && (argreg%2) == 1)
+ // ppc32 ELF abi spec for passing LONG_LONG
+ argreg++; // XXX: odd argreg => even rN
+ vassert(argreg < PPC_N_REGPARMS-1);
+ iselInt64Expr(&rHi,&rLo, env, arg);
+ tmpregs[argreg++] = rHi;
+ tmpregs[argreg] = rLo;
+ }
+ } else { // mode64
+ tmpregs[argreg] = iselWordExpr_R(env, arg);
}
- } else { // mode64
- tmpregs[argreg] = iselWordExpr_R(env, args[i]);
}
argreg++;
}
}
- target = mode64 ? Ptr_to_ULong(cee->addr) :
- toUInt(Ptr_to_ULong(cee->addr));
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
+ if (retTy == Ity_V128 || retTy == Ity_V256) {
+ vassert(nVECRETs == 1);
+ } else {
+ vassert(nVECRETs == 0);
+ }
- /* Finally, the call itself. */
- addInstr(env, PPCInstr_Call( cc, (Addr64)target, argiregs, rloc ));
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64:
+ *retloc = mk_RetLoc_simple(mode64 ? RLPri_Int : RLPri_2Int);
+ break;
+ case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ /* Result is 512 bytes up the stack, and after it has been
+ retrieved, adjust SP upwards by 1024. */
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 512);
+ *stackAdjustAfterCall = 1024;
+ break;
+ case Ity_V256:
+ vassert(0); // ATC
+ /* Ditto */
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 512);
+ *stackAdjustAfterCall = 1024;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
+
+ /* Finally, generate the call itself. This needs the *retloc value
+ set in the switch above, which is why it's at the end. */
+
+ ULong target = mode64 ? Ptr_to_ULong(cee->addr)
+ : toUInt(Ptr_to_ULong(cee->addr));
+ addInstr(env, PPCInstr_Call( cc, (Addr64)target, argiregs, *retloc ));
}
fdescr = (HWord*)h_calc_BCDtoDPB;
addInstr(env, PPCInstr_Call( cc, (Addr64)(fdescr[0]),
- argiregs, RetLocInt) );
+ argiregs, mk_RetLoc_simple(RLPri_Int)) );
addInstr(env, mk_iMOVds_RR(r_dst, argregs[0]));
return r_dst;
fdescr = (HWord*)h_calc_DPBtoBCD;
addInstr(env, PPCInstr_Call( cc, (Addr64)(fdescr[0]),
- argiregs, RetLocInt ) );
+ argiregs, mk_RetLoc_simple(RLPri_Int) ) );
addInstr(env, mk_iMOVds_RR(r_dst, argregs[0]));
return r_dst;
vassert(ty == Ity_I32);
/* be very restrictive for now. Only 32/64-bit ints allowed for
- args, and 32 bits for return type. Don't forget to change +
- the RetLoc if more return types are allowed in future. */
+ args, and 32 bits for return type. */
if (e->Iex.CCall.retty != Ity_I32)
goto irreducible;
-
+
/* Marshal args, do the call, clear stack. */
- doHelperCall( env, False, NULL,
- e->Iex.CCall.cee, e->Iex.CCall.args, RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ e->Iex.CCall.cee, e->Iex.CCall.retty, e->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
/* GPR3 now holds the destination address from Pin_Goto */
addInstr(env, mk_iMOVds_RR(r_dst, hregPPC_GPR3(mode64)));
target = toUInt( Ptr_to_ULong(h_calc_BCDtoDPB ) );
addInstr( env, PPCInstr_Call( cc, (Addr64)target,
- argiregs, RetLoc2Int ) );
+ argiregs,
+ mk_RetLoc_simple(RLPri_2Int) ) );
addInstr( env, mk_iMOVds_RR( tHi, argregs[argreg-1] ) );
addInstr( env, mk_iMOVds_RR( tLo, argregs[argreg] ) );
target = toUInt( Ptr_to_ULong( h_calc_DPBtoBCD ) );
- addInstr(env, PPCInstr_Call( cc, (Addr64)target,
- argiregs, RetLoc2Int ) );
+ addInstr(env, PPCInstr_Call( cc, (Addr64)target, argiregs,
+ mk_RetLoc_simple(RLPri_2Int) ) );
addInstr(env, mk_iMOVds_RR(tHi, argregs[argreg-1]));
addInstr(env, mk_iMOVds_RR(tLo, argregs[argreg]));
/* --------- Call to DIRTY helper --------- */
case Ist_Dirty: {
IRDirty* d = stmt->Ist.Dirty.details;
- Bool passBBP = False;
-
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Figure out the return type, if any. */
IRType retty = Ity_INVALID;
if (d->tmp != IRTemp_INVALID)
retty = typeOfIRTemp(env->type_env, d->tmp);
- /* Marshal args, do the call, clear stack, set the return value
- to 0x555..555 if this is a conditional call that returns a
- value and the call is skipped. We need to set the ret-loc
- correctly in order to implement the IRDirty semantics that
- the return value is 0x555..555 if the call doesn't happen. */
- RetLoc rloc = RetLocINVALID;
- switch (retty) {
- case Ity_INVALID: /* function doesn't return anything */
- rloc = RetLocNone; break;
- case Ity_I64:
- rloc = mode64 ? RetLocInt : RetLoc2Int; break;
- case Ity_I32: case Ity_I16: case Ity_I8:
- rloc = RetLocInt; break;
- default:
- break;
+ /* Throw out any return types we don't know about. */
+ Bool retty_ok = False;
+ if (mode64) {
+ switch (retty) {
+ case Ity_INVALID: /* function doesn't return anything */
+ case Ity_V128:
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ retty_ok = True; break;
+ default:
+ break;
+ }
+ } else {
+ switch (retty) {
+ case Ity_INVALID: /* function doesn't return anything */
+ case Ity_V128:
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ retty_ok = True; break;
+ default:
+ break;
+ }
}
- if (rloc == RetLocINVALID)
+ if (!retty_ok)
break; /* will go to stmt_fail: */
- /* Marshal args, do the call, clear stack. */
- doHelperCall( env, passBBP, d->guard, d->cee, d->args, rloc );
+ /* Marshal args, do the call, clear stack, set the return value
+ to 0x555..555 if this is a conditional call that returns a
+ value and the call is skipped. */
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, d->guard, d->cee, retty, d->args );
+ vassert(is_sane_RetLoc(rloc));
/* Now figure out what to do with the returned value, if any. */
- if (d->tmp == IRTemp_INVALID)
- /* No return value. Nothing to do. */
- return;
-
- if (!mode64 && retty == Ity_I64) {
- HReg r_dstHi, r_dstLo;
- /* The returned value is in %r3:%r4. Park it in the
- register-pair associated with tmp. */
- lookupIRTempPair( &r_dstHi, &r_dstLo, env, d->tmp);
- addInstr(env, mk_iMOVds_RR(r_dstHi, hregPPC_GPR3(mode64)));
- addInstr(env, mk_iMOVds_RR(r_dstLo, hregPPC_GPR4(mode64)));
- vassert(rloc == RetLoc2Int);
- return;
- }
- if (retty == Ity_I8 || retty == Ity_I16 ||
- retty == Ity_I32 || ((retty == Ity_I64) && mode64)) {
- /* The returned value is in %r3. Park it in the register
- associated with tmp. */
- HReg r_dst = lookupIRTemp(env, d->tmp);
- addInstr(env, mk_iMOVds_RR(r_dst, hregPPC_GPR3(mode64)));
- vassert(rloc == RetLocInt);
- return;
+ switch (retty) {
+ case Ity_INVALID: {
+ /* No return value. Nothing to do. */
+ vassert(d->tmp == IRTemp_INVALID);
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I32: case Ity_I16: case Ity_I8: {
+ /* The returned value is in %r3. Park it in the register
+ associated with tmp. */
+ HReg r_dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(r_dst, hregPPC_GPR3(mode64)));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I64:
+ if (mode64) {
+ /* The returned value is in %r3. Park it in the register
+ associated with tmp. */
+ HReg r_dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(r_dst, hregPPC_GPR3(mode64)));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ } else {
+ /* The returned value is in %r3:%r4. Park it in the
+ register-pair associated with tmp. */
+ HReg r_dstHi = INVALID_HREG;
+ HReg r_dstLo = INVALID_HREG;
+ lookupIRTempPair( &r_dstHi, &r_dstLo, env, d->tmp);
+ addInstr(env, mk_iMOVds_RR(r_dstHi, hregPPC_GPR3(mode64)));
+ addInstr(env, mk_iMOVds_RR(r_dstLo, hregPPC_GPR4(mode64)));
+ vassert(rloc.pri == RLPri_2Int);
+ vassert(addToSp == 0);
+ }
+ return;
+ case Ity_V128: {
+ /* The returned value is on the stack, and *retloc tells
+ us where. Fish it off the stack and then move the
+ stack pointer upwards to clear it, as directed by
+ doHelperCall. */
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(addToSp >= 16);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ PPCAMode* am = PPCAMode_IR(rloc.spOff, StackFramePtr(mode64));
+ addInstr(env, PPCInstr_AvLdSt( True/*load*/, 16, dst, am ));
+ add_to_sp(env, addToSp);
+ return;
+ }
+ default:
+ /*NOTREACHED*/
+ vassert(0);
}
- break;
}
/* --------- MEM FENCE --------- */
for (i = 1; i <= 5; ++i) {
addHRegUse(u, HRmWrite, mkHReg(i, HRcInt64, False));
}
- if (! hregIsInvalid(insn->variant.helper_call.dst))
- addHRegUse(u, HRmWrite, insn->variant.helper_call.dst);
/* Ditto for floating point registers. f0 - f7 are volatile */
for (i = 0; i <= 7; ++i) {
/* The registers that are used for passing arguments will be read.
Not all of them may, but in general we need to assume that. */
- for (i = 0; i < insn->variant.helper_call.num_args; ++i) {
+ for (i = 0; i < insn->variant.helper_call.details->num_args; ++i) {
addHRegUse(u, HRmRead, mkHReg(s390_gprno_from_arg_index(i),
HRcInt64, False));
}
As for the arguments of the helper call -- they will be loaded into
non-virtual registers. Again, we don't need to do anything for those
here. */
- if (! hregIsInvalid(insn->variant.helper_call.dst))
- insn->variant.helper_call.dst = lookupHRegRemap(m, insn->variant.helper_call.dst);
break;
case S390_INSN_BFP_TRIOP:
s390_insn *
s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
- const HChar *name, HReg dst)
+ const HChar *name, RetLoc rloc)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+ s390_helper_call *helper_call = LibVEX_Alloc(sizeof(s390_helper_call));
insn->tag = S390_INSN_HELPER_CALL;
insn->size = 0; /* does not matter */
- insn->variant.helper_call.cond = cond;
- insn->variant.helper_call.target = target;
- insn->variant.helper_call.num_args = num_args;
- insn->variant.helper_call.name = name;
- insn->variant.helper_call.dst = dst;
+ insn->variant.helper_call.details = helper_call;
+
+ helper_call->cond = cond;
+ helper_call->target = target;
+ helper_call->num_args = num_args;
+ helper_call->name = name;
+ helper_call->rloc = rloc;
+
+ vassert(is_sane_RetLoc(rloc));
return insn;
}
break;
case S390_INSN_HELPER_CALL: {
- if (! hregIsInvalid(insn->variant.helper_call.dst)) {
- s390_sprintf(buf, "%M if (%C) %R = %s{%I}(%L)", "v-call",
- insn->variant.helper_call.cond,
- insn->variant.helper_call.dst,
- insn->variant.helper_call.name,
- insn->variant.helper_call.target,
- insn->variant.helper_call.num_args);
- } else {
- s390_sprintf(buf, "%M if (%C) %s{%I}(%L)", "v-call",
- insn->variant.helper_call.cond,
- insn->variant.helper_call.name,
- insn->variant.helper_call.target,
- insn->variant.helper_call.num_args);
- }
+ s390_helper_call *helper_call = insn->variant.helper_call.details;
+ s390_sprintf(buf, "%M if (%C) %s{%I}(%L)", "v-call",
+ helper_call->cond,
+ helper_call->name,
+ helper_call->target,
+ helper_call->num_args);
return buf; /* avoid printing "size = ..." which is meaningless */
}
ULong target;
UChar *ptmp = buf;
UChar *bufIN = buf;
+ s390_helper_call *helper_call = insn->variant.helper_call.details;
- cond = insn->variant.helper_call.cond;
- target = insn->variant.helper_call.target;
+ cond = helper_call->cond;
+ target = helper_call->target;
if (cond != S390_CC_ALWAYS
- && ! hregIsInvalid(insn->variant.helper_call.dst)) {
+ && helper_call->rloc.pri != RLPri_None) {
/* The call might not happen (it isn't unconditional) and it
returns a result. In this case we will need to generate a
control flow diamond to put 0x555..555 in the return
buf = s390_emit_BASR(buf, S390_REGNO_LINK_REGISTER, 1); // call helper
- /* Move the return value to the destination register */
- if (! hregIsInvalid(insn->variant.helper_call.dst)) {
- buf = s390_emit_LGR(buf, hregNumber(insn->variant.helper_call.dst),
- S390_REGNO_RETURN_VALUE);
- }
-
buf = s390_emit_LFPC(buf, S390_REGNO_STACK_POINTER, // restore FPC
S390_OFFSET_SAVED_FPC_C);
HReg r1; /* clobbered register GPR #1 */
} s390_fp_convert;
+/* Pseudo-insn for representing a helper call.
+ TARGET is the absolute address of the helper function
+ NUM_ARGS says how many arguments are being passed.
+ All arguments have integer type and are being passed according to ABI,
+ i.e. in registers r2, r3, r4, r5, and r6, with argument #0 being
+ passed in r2 and so forth. */
+typedef struct {
+ s390_cc_t cond : 16;
+ UInt num_args : 16;
+ RetLoc rloc; /* where the return value will be */
+ Addr64 target;
+ const HChar *name; /* callee's name (for debugging) */
+} s390_helper_call;
+
typedef struct {
s390_insn_tag tag;
/* Usually, this is the size of the result of an operation.
struct {
s390_cdas *details;
} cdas;
- /* Pseudo-insn for representing a helper call.
- TARGET is the absolute address of the helper function
- NUM_ARGS says how many arguments are being passed.
- All arguments have integer type and are being passed according to ABI,
- i.e. in registers r2, r3, r4, r5, and r6, with argument #0 being
- passed in r2 and so forth. */
- struct {
- s390_cc_t cond : 16;
- UInt num_args : 16;
- HReg dst; /* if not INVALID_HREG, put return value here */
- Addr64 target;
- const HChar *name; /* callee's name (for debugging) */
+ struct {
+ s390_helper_call *details;
} helper_call;
/* Floating point instructions (including conversion to/from floating
s390_insn *s390_insn_compare(UChar size, HReg dst, s390_opnd_RMI opnd,
Bool signed_comparison);
s390_insn *s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
- const HChar *name, HReg dst);
+ const HChar *name, RetLoc rloc);
s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst,
HReg op2, HReg op3);
s390_insn *s390_insn_bfp_binop(UChar size, s390_bfp_binop_t, HReg dst,
of the register allocator to throw out those reg-to-reg moves.
*/
static void
-doHelperCall(ISelEnv *env, Bool passBBP, IRExpr *guard,
- IRCallee *callee, IRExpr **args, HReg dst)
+doHelperCall(/*OUT*/UInt *stackAdjustAfterCall,
+ /*OUT*/RetLoc *retloc,
+ ISelEnv *env, IRExpr *guard,
+ IRCallee *callee, IRType retTy, IRExpr **args)
{
UInt n_args, i, argreg, size;
ULong target;
HReg tmpregs[S390_NUM_GPRPARMS];
s390_cc_t cc;
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
+
+ /* The return type can be I{64,32,16,8} or V{128,256}. In the
+ latter two cases, it is expected that |args| will contain the
+ special value IRExprP__VECRET, in which case this routine
+ generates code to allocate space on the stack for the vector
+ return value. Since we are not passing any scalars on the
+ stack, it is enough to preallocate the return space before
+ marshalling any arguments, in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the value
+ in the guest state pointer register is passed as the
+ corresponding argument.
+
+ These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
n_args = 0;
for (i = 0; args[i]; i++)
++n_args;
- if (n_args > (S390_NUM_GPRPARMS - (passBBP ? 1 : 0))) {
+ if (n_args > S390_NUM_GPRPARMS) {
vpanic("doHelperCall: too many arguments");
}
*/
Int arg_errors = 0;
for (i = 0; i < n_args; ++i) {
- IRType type = typeOfIRExpr(env->type_env, args[i]);
- if (type != Ity_I64) {
- ++arg_errors;
- vex_printf("calling %s: argument #%d has type ", callee->name, i);
- ppIRType(type);
- vex_printf("; Ity_I64 is required\n");
+ if (UNLIKELY(args[i] == IRExprP__VECRET)) {
+ nVECRETs++;
+ } else if (UNLIKELY(args[i] == IRExprP__BBPTR)) {
+ nBBPTRs++;
+ } else {
+ IRType type = typeOfIRExpr(env->type_env, args[i]);
+ if (type != Ity_I64) {
+ ++arg_errors;
+ vex_printf("calling %s: argument #%d has type ", callee->name, i);
+ ppIRType(type);
+ vex_printf("; Ity_I64 is required\n");
+ }
}
}
if (arg_errors)
vpanic("cannot continue due to errors in argument passing");
- argreg = 0;
+ /* If this fails, the IR is ill-formed */
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+
+ /* If we have a VECRET, allocate space on the stack for the return
+ value, and record the stack pointer after that. */
+ HReg r_vecRetAddr = INVALID_HREG;
+ if (nVECRETs == 1) {
+ /* we do not handle vector types yet */
+ vassert(0);
+ HReg sp = make_gpr(S390_REGNO_STACK_POINTER);
+ vassert(retTy == Ity_V128 || retTy == Ity_V256);
+ vassert(retTy != Ity_V256); // we don't handle that yet (if ever)
+ r_vecRetAddr = newVRegI(env);
+ addInstr(env, s390_insn_alu(4, S390_ALU_SUB, sp, s390_opnd_imm(16)));
+ addInstr(env, s390_insn_move(sizeof(ULong), r_vecRetAddr, sp));
- /* If we need the guest state pointer put it in a temporary arg reg */
- if (passBBP) {
- tmpregs[argreg] = newVRegI(env);
- addInstr(env, s390_insn_move(sizeof(ULong), tmpregs[argreg],
- s390_hreg_guest_state_pointer()));
- argreg++;
+ } else {
+ // If either of these fail, the IR is ill-formed
+ vassert(retTy != Ity_V128 && retTy != Ity_V256);
+ vassert(nVECRETs == 0);
}
+ argreg = 0;
+
/* Compute the function arguments into a temporary register each */
for (i = 0; i < n_args; i++) {
- tmpregs[argreg] = s390_isel_int_expr(env, args[i]);
+ IRExpr *arg = args[i];
+ if(UNLIKELY(arg == IRExprP__VECRET)) {
+ /* we do not handle vector types yet */
+ vassert(0);
+ addInstr(env, s390_insn_move(sizeof(ULong), tmpregs[argreg],
+ r_vecRetAddr));
+ } else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ /* If we need the guest state pointer put it in a temporary arg reg */
+ tmpregs[argreg] = newVRegI(env);
+ addInstr(env, s390_insn_move(sizeof(ULong), tmpregs[argreg],
+ s390_hreg_guest_state_pointer()));
+ } else {
+ tmpregs[argreg] = s390_isel_int_expr(env, args[i]);
+ }
argreg++;
}
target = Ptr_to_ULong(callee->addr);
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ /* we do not handle vector types yet */
+ vassert(0);
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
+ *stackAdjustAfterCall = 16;
+ break;
+ case Ity_V256:
+ /* we do not handle vector types yet */
+ vassert(0);
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
+ *stackAdjustAfterCall = 32;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
+
/* Finally, the call itself. */
addInstr(env, s390_insn_helper_call(cc, (Addr64)target, n_args,
- callee->name, dst));
+ callee->name, *retloc));
}
/* --------- CCALL --------- */
case Iex_CCall: {
HReg dst = newVRegI(env);
+ HReg ret = make_gpr(S390_REGNO_RETURN_VALUE);
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+
+ doHelperCall(&addToSp, &rloc, env, NULL, expr->Iex.CCall.cee,
+ expr->Iex.CCall.retty, expr->Iex.CCall.args);
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ addInstr(env, s390_insn_move(sizeof(ULong), dst, ret));
- doHelperCall(env, False, NULL, expr->Iex.CCall.cee,
- expr->Iex.CCall.args, dst);
return dst;
}
case Ist_Dirty: {
IRType retty;
IRDirty* d = stmt->Ist.Dirty.details;
- Bool passBBP;
HReg dst;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ UInt addToSp = 0;
Int i;
/* Invalidate tracked values of those guest state registers that are
}
}
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
-
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
-
if (d->tmp == IRTemp_INVALID) {
/* No return value. */
- dst = INVALID_HREG;
- doHelperCall(env, passBBP, d->guard, d->cee, d->args, dst);
+ retty = Ity_INVALID;
+ doHelperCall(&addToSp, &rloc, env, d->guard, d->cee, retty,
+ d->args);
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+
return;
}
if (retty == Ity_I64 || retty == Ity_I32
|| retty == Ity_I16 || retty == Ity_I8) {
/* Move the returned value to the destination register */
+ HReg ret = make_gpr(S390_REGNO_RETURN_VALUE);
+
+ dst = lookupIRTemp(env, d->tmp);
+ doHelperCall(&addToSp, &rloc, env, d->guard, d->cee, retty,
+ d->args);
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ addInstr(env, s390_insn_move(sizeof(ULong), dst, ret));
+
+ return;
+ }
+ if (retty == Ity_V128) {
+ /* we do not handle vector types yet */
+ vassert(0);
+ HReg sp = make_gpr(S390_REGNO_STACK_POINTER);
+ s390_amode *am;
+
dst = lookupIRTemp(env, d->tmp);
- doHelperCall(env, passBBP, d->guard, d->cee, d->args, dst);
+ doHelperCall(&addToSp, &rloc, env, d->guard, d->cee, retty,
+ d->args);
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(addToSp >= 16);
+
+ /* rloc.spOff should be zero for s390 */
+ /* cannot use fits_unsigned_12bit(rloc.spOff), so doing
+ it explicitly */
+ vassert((rloc.spOff & 0xFFF) == rloc.spOff);
+ am = s390_amode_b12(rloc.spOff, sp);
+ // JRS 2013-Aug-08: is this correct? Looks like we're loading
+ // only 64 bits from memory, when in fact we should be loading 128.
+ addInstr(env, s390_insn_load(8, dst, am));
+ addInstr(env, s390_insn_alu(4, S390_ALU_ADD, sp,
+ s390_opnd_imm(addToSp)));
return;
+ } else {/* if (retty == Ity_V256) */
+ /* we do not handle vector types yet */
+ vassert(0);
}
break;
}
i->Xin.Call.regparms = regparms;
i->Xin.Call.rloc = rloc;
vassert(regparms >= 0 && regparms <= 3);
- vassert(rloc != RetLocINVALID);
+ vassert(is_sane_RetLoc(rloc));
return i;
}
X86Instr* X86Instr_XDirect ( Addr32 dstGA, X86AMode* amEIP,
}
case Xin_Call:
- if (i->Xin.Call.cond != Xcc_ALWAYS && i->Xin.Call.rloc != RetLocNone) {
+ if (i->Xin.Call.cond != Xcc_ALWAYS
+ && i->Xin.Call.rloc.pri != RLPri_None) {
/* The call might not happen (it isn't unconditional) and it
returns a result. In this case we will need to generate a
control flow diamond to put 0x555..555 in the return
/* 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 )
+ pushed. If we encounter an IRExprP__VECRET then we expect that
+ r_vecRetAddr will be a valid register, that holds the relevant
+ address.
+*/
+static Int pushArg ( ISelEnv* env, IRExpr* arg, HReg r_vecRetAddr )
{
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ vassert(0); //ATC
+ vassert(!hregIsInvalid(r_vecRetAddr));
+ addInstr(env, X86Instr_Push(X86RMI_Reg(r_vecRetAddr)));
+ return 1;
+ }
+ if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ addInstr(env, X86Instr_Push(X86RMI_Reg(hregX86_EBP())));
+ return 1;
+ }
+ /* Else it's a "normal" expression. */
IRType arg_ty = typeOfIRExpr(env->type_env, arg);
if (arg_ty == Ity_I32) {
addInstr(env, X86Instr_Push(iselIntExpr_RMI(env, arg)));
static
Bool mightRequireFixedRegs ( IRExpr* e )
{
+ if (UNLIKELY(is_IRExprP__VECRET_or_BBPTR(e))) {
+ // These are always "safe" -- either a copy of %esp in some
+ // arbitrary vreg, or a copy of %ebp, respectively.
+ return False;
+ }
+ /* Else it's a "normal" expression. */
switch (e->tag) {
case Iex_RdTmp: case Iex_Const: case Iex_Get:
return False;
}
-/* Do a complete function call. guard is a Ity_Bit expression
+/* 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. */
+ call is unconditional. |retloc| is set to indicate where the
+ return value is after the call. The caller (of this fn) must
+ generate code to add |stackAdjustAfterCall| to the stack pointer
+ after the call is done. */
static
-void doHelperCall ( ISelEnv* env,
- Bool passBBP,
- IRExpr* guard, IRCallee* cee, IRExpr** args,
- RetLoc rloc )
+void doHelperCall ( /*OUT*/UInt* stackAdjustAfterCall,
+ /*OUT*/RetLoc* retloc,
+ ISelEnv* env,
+ IRExpr* guard,
+ IRCallee* cee, IRType retTy, IRExpr** args )
{
X86CondCode cc;
HReg argregs[3];
Int not_done_yet, n_args, n_arg_ws, stack_limit,
i, argreg, argregX;
+ /* Set default returns. We'll update them later if needed. */
+ *stackAdjustAfterCall = 0;
+ *retloc = mk_RetLoc_INVALID();
+
+ /* These are used for cross-checking that IR-level constraints on
+ the use of IRExprP__VECRET and IRExprP__BBPTR are observed. */
+ UInt nVECRETs = 0;
+ UInt nBBPTRs = 0;
+
/* 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.
+ * The return type can be I{64,32,16,8} or V128. In the V128
+ case, it is expected that |args| will contain the special
+ value IRExprP__VECRET, in which case this routine generates
+ code to allocate space on the stack for the vector return
+ value. Since we are not passing any scalars on the stack, it
+ is enough to preallocate the return space before marshalling
+ any arguments, in this case.
+
+ |args| may also contain IRExprP__BBPTR, in which case the
+ value in %ebp is passed as the corresponding argument.
* 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
*/
vassert(cee->regparms >= 0 && cee->regparms <= 3);
+ /* Count the number of args and also the VECRETs */
n_args = n_arg_ws = 0;
- while (args[n_args]) n_args++;
+ while (args[n_args]) {
+ IRExpr* arg = args[n_args];
+ n_args++;
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ nVECRETs++;
+ } else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ nBBPTRs++;
+ }
+ }
+
+ /* If this fails, the IR is ill-formed */
+ vassert(nBBPTRs == 0 || nBBPTRs == 1);
+
+ /* If we have a VECRET, allocate space on the stack for the return
+ value, and record the stack pointer after that. */
+ HReg r_vecRetAddr = INVALID_HREG;
+ if (nVECRETs == 1) {
+ vassert(retTy == Ity_V128 || retTy == Ity_V256);
+ vassert(retTy != Ity_V256); // we don't handle that yet (if ever)
+ r_vecRetAddr = newVRegI(env);
+ sub_from_esp(env, 16);
+ addInstr(env, mk_iMOVsd_RR( hregX86_ESP(), r_vecRetAddr ));
+ } else {
+ // If either of these fail, the IR is ill-formed
+ vassert(retTy != Ity_V128 && retTy != Ity_V256);
+ vassert(nVECRETs == 0);
+ }
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]);
+ n_arg_ws += pushArg(env, args[i], r_vecRetAddr);
not_done_yet--;
}
vex_printf("\n");
}
+ IRExpr* arg = args[i];
argreg--;
vassert(argreg >= 0);
- vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32);
- tmpregs[argreg] = iselIntExpr_R(env, args[i]);
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ vassert(0); //ATC
+ }
+ else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ vassert(0); //ATC
+ } else {
+ vassert(typeOfIRExpr(env->type_env, arg) == Ity_I32);
+ tmpregs[argreg] = iselIntExpr_R(env, arg);
+ }
not_done_yet--;
}
for (i = stack_limit-1; i >= 0; i--) {
/* It's safe to compute all regparm args directly into their
target registers. */
for (i = stack_limit-1; i >= 0; i--) {
+ IRExpr* arg = args[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]));
+ if (UNLIKELY(arg == IRExprP__VECRET)) {
+ vassert(!hregIsInvalid(r_vecRetAddr));
+ addInstr(env, X86Instr_Alu32R(Xalu_MOV,
+ X86RMI_Reg(r_vecRetAddr),
+ argregs[argreg]));
+ }
+ else if (UNLIKELY(arg == IRExprP__BBPTR)) {
+ vassert(0); //ATC
+ } else {
+ vassert(typeOfIRExpr(env->type_env, arg) == Ity_I32);
+ addInstr(env, X86Instr_Alu32R(Xalu_MOV,
+ iselIntExpr_RMI(env, arg),
+ 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);
}
}
- /* call the helper, and get the args off the stack afterwards. */
- callHelperAndClearArgs( env, cc, cee, n_arg_ws, rloc );
+ /* Do final checks, set the return values, and generate the call
+ instruction proper. */
+ vassert(*stackAdjustAfterCall == 0);
+ vassert(is_RetLoc_INVALID(*retloc));
+ switch (retTy) {
+ case Ity_INVALID:
+ /* Function doesn't return a value. */
+ *retloc = mk_RetLoc_simple(RLPri_None);
+ break;
+ case Ity_I64:
+ *retloc = mk_RetLoc_simple(RLPri_2Int);
+ break;
+ case Ity_I32: case Ity_I16: case Ity_I8:
+ *retloc = mk_RetLoc_simple(RLPri_Int);
+ break;
+ case Ity_V128:
+ *retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
+ *stackAdjustAfterCall = 16;
+ break;
+ case Ity_V256:
+ vassert(0); // ATC
+ *retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
+ *stackAdjustAfterCall = 32;
+ break;
+ default:
+ /* IR can denote other possible return types, but we don't
+ handle those here. */
+ vassert(0);
+ }
+
+ /* Finally, generate the call itself. This needs the *retloc value
+ set in the switch above, which is why it's at the end. */
+ callHelperAndClearArgs( env, cc, cee, n_arg_ws, *retloc );
}
addInstr(env, X86Instr_Push(X86RMI_Reg(xHi)));
addInstr(env, X86Instr_Push(X86RMI_Reg(xLo)));
addInstr(env, X86Instr_Call( Xcc_ALWAYS, (UInt)fn,
- 0, RetLocInt ));
+ 0, mk_RetLoc_simple(RLPri_Int) ));
add_to_esp(env, 2*4);
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), dst));
return dst;
goto irreducible;
/* Marshal args, do the call, clear stack. */
- doHelperCall( env, False, NULL, e->Iex.CCall.cee,
- e->Iex.CCall.args, RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ e->Iex.CCall.cee, e->Iex.CCall.retty, e->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), dst));
return dst;
vassert(cal->Iex.CCall.retty == Ity_I32); /* else ill-typed IR */
vassert(con->Iex.Const.con->tag == Ico_U32);
/* Marshal args, do the call. */
- doHelperCall( env, False, NULL, cal->Iex.CCall.cee,
- cal->Iex.CCall.args, RetLocInt );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ cal->Iex.CCall.cee,
+ cal->Iex.CCall.retty, cal->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ /* */
addInstr(env, X86Instr_Alu32R(Xalu_CMP,
X86RMI_Imm(con->Iex.Const.con->Ico.U32),
hregX86_EAX()));
addInstr(env, X86Instr_Push(X86RMI_Reg(xHi)));
addInstr(env, X86Instr_Push(X86RMI_Reg(xLo)));
addInstr(env, X86Instr_Call( Xcc_ALWAYS, (UInt)fn,
- 0, RetLoc2Int ));
+ 0, mk_RetLoc_simple(RLPri_2Int) ));
add_to_esp(env, 4*4);
addInstr(env, mk_iMOVsd_RR(hregX86_EDX(), tHi));
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), tLo));
addInstr(env, X86Instr_Push(X86RMI_Reg(xHi)));
addInstr(env, X86Instr_Push(X86RMI_Reg(xLo)));
addInstr(env, X86Instr_Call( Xcc_ALWAYS, (UInt)fn,
- 0, RetLoc2Int ));
+ 0, mk_RetLoc_simple(RLPri_2Int) ));
add_to_esp(env, 3*4);
addInstr(env, mk_iMOVsd_RR(hregX86_EDX(), tHi));
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), tLo));
addInstr(env, X86Instr_Push(X86RMI_Reg(xHi)));
addInstr(env, X86Instr_Push(X86RMI_Reg(xLo)));
addInstr(env, X86Instr_Call( Xcc_ALWAYS, (UInt)fn,
- 0, RetLoc2Int ));
+ 0, mk_RetLoc_simple(RLPri_2Int) ));
add_to_esp(env, 2*4);
addInstr(env, mk_iMOVsd_RR(hregX86_EDX(), tHi));
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), tLo));
HReg tHi = newVRegI(env);
/* Marshal args, do the call, clear stack. */
- doHelperCall( env, False, NULL, e->Iex.CCall.cee,
- e->Iex.CCall.args, RetLoc2Int );
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, NULL/*guard*/,
+ e->Iex.CCall.cee,
+ e->Iex.CCall.retty, e->Iex.CCall.args );
+ vassert(is_sane_RetLoc(rloc));
+ vassert(rloc.pri == RLPri_2Int);
+ vassert(addToSp == 0);
+ /* */
addInstr(env, mk_iMOVsd_RR(hregX86_EDX(), tHi));
addInstr(env, mk_iMOVsd_RR(hregX86_EAX(), tLo));
X86AMode_IR(0, hregX86_ECX())));
/* call the helper */
addInstr(env, X86Instr_Call( Xcc_ALWAYS, (Addr32)fn,
- 3, RetLocNone ));
+ 3, mk_RetLoc_simple(RLPri_None) ));
/* fetch the result from memory, using %r_argp, which the
register allocator will keep alive across the call. */
addInstr(env, X86Instr_SseLdSt(True/*isLoad*/, dst,
/* --------- Call to DIRTY helper --------- */
case Ist_Dirty: {
IRDirty* d = stmt->Ist.Dirty.details;
- Bool passBBP = False;
-
- if (d->nFxState == 0)
- vassert(!d->needsBBP);
-
- passBBP = toBool(d->nFxState > 0 && d->needsBBP);
/* Figure out the return type, if any. */
IRType retty = Ity_INVALID;
if (d->tmp != IRTemp_INVALID)
retty = typeOfIRTemp(env->type_env, d->tmp);
- /* Marshal args, do the call, clear stack, set the return value
- to 0x555..555 if this is a conditional call that returns a
- value and the call is skipped. We need to set the ret-loc
- correctly in order to implement the IRDirty semantics that
- the return value is 0x555..555 if the call doesn't happen. */
- RetLoc rloc = RetLocINVALID;
+ Bool retty_ok = False;
switch (retty) {
case Ity_INVALID: /* function doesn't return anything */
- rloc = RetLocNone; break;
- case Ity_I64:
- rloc = RetLoc2Int; break;
- case Ity_I32: case Ity_I16: case Ity_I8:
- rloc = RetLocInt; break;
+ case Ity_I64: case Ity_I32: case Ity_I16: case Ity_I8:
+ case Ity_V128:
+ retty_ok = True; break;
default:
break;
}
- if (rloc == RetLocINVALID)
+ if (!retty_ok)
break; /* will go to stmt_fail: */
- /* Marshal args, do the call, clear stack. */
- doHelperCall( env, passBBP, d->guard, d->cee, d->args, rloc );
+ /* Marshal args, do the call, and set the return value to
+ 0x555..555 if this is a conditional call that returns a value
+ and the call is skipped. */
+ UInt addToSp = 0;
+ RetLoc rloc = mk_RetLoc_INVALID();
+ doHelperCall( &addToSp, &rloc, env, d->guard, d->cee, retty, d->args );
+ vassert(is_sane_RetLoc(rloc));
/* Now figure out what to do with the returned value, if any. */
- if (d->tmp == IRTemp_INVALID)
- /* No return value. Nothing to do. */
- return;
-
- if (retty == Ity_I64) {
- HReg dstHi, dstLo;
- /* The returned value is in %edx:%eax. Park it in the
- register-pair associated with tmp. */
- lookupIRTemp64( &dstHi, &dstLo, env, d->tmp);
- addInstr(env, mk_iMOVsd_RR(hregX86_EDX(),dstHi) );
- addInstr(env, mk_iMOVsd_RR(hregX86_EAX(),dstLo) );
- return;
- }
- 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;
+ switch (retty) {
+ case Ity_INVALID: {
+ /* No return value. Nothing to do. */
+ vassert(d->tmp == IRTemp_INVALID);
+ vassert(rloc.pri == RLPri_None);
+ vassert(addToSp == 0);
+ return;
+ }
+ case Ity_I32: case Ity_I16: case Ity_I8: {
+ /* The returned value is in %eax. Park it in the register
+ associated with tmp. */
+ vassert(rloc.pri == RLPri_Int);
+ vassert(addToSp == 0);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ addInstr(env, mk_iMOVsd_RR(hregX86_EAX(),dst) );
+ return;
+ }
+ case Ity_I64: {
+ /* The returned value is in %edx:%eax. Park it in the
+ register-pair associated with tmp. */
+ vassert(rloc.pri == RLPri_2Int);
+ vassert(addToSp == 0);
+ HReg dstHi, dstLo;
+ lookupIRTemp64( &dstHi, &dstLo, env, d->tmp);
+ addInstr(env, mk_iMOVsd_RR(hregX86_EDX(),dstHi) );
+ addInstr(env, mk_iMOVsd_RR(hregX86_EAX(),dstLo) );
+ return;
+ }
+ case Ity_V128: {
+ /* The returned value is on the stack, and *retloc tells
+ us where. Fish it off the stack and then move the
+ stack pointer upwards to clear it, as directed by
+ doHelperCall. */
+ vassert(rloc.pri == RLPri_V128SpRel);
+ vassert(addToSp >= 16);
+ HReg dst = lookupIRTemp(env, d->tmp);
+ X86AMode* am = X86AMode_IR(rloc.spOff, hregX86_ESP());
+ addInstr(env, X86Instr_SseLdSt( True/*load*/, dst, am ));
+ add_to_esp(env, addToSp);
+ return;
+ }
+ default:
+ /*NOTREACHED*/
+ vassert(0);
}
break;
}
ppIRCallee(e->Iex.CCall.cee);
vex_printf("(");
for (i = 0; e->Iex.CCall.args[i] != NULL; i++) {
- ppIRExpr(e->Iex.CCall.args[i]);
- if (e->Iex.CCall.args[i+1] != NULL)
+ IRExpr* arg = e->Iex.CCall.args[i];
+ /* We don't actually expect VECRET or BBPTR here -- BBPTR is
+ never allowable; VECRET is in principle allowable but at
+ present isn't supported. But they are handled for
+ completeness anyway. */
+ if (arg == IRExprP__VECRET) {
+ vex_printf("VECRET");
+ } else if (arg == IRExprP__BBPTR) {
+ vex_printf("BBPTR");
+ } else {
+ ppIRExpr(arg);
+ }
+ if (e->Iex.CCall.args[i+1] != NULL) {
vex_printf(",");
+ }
}
vex_printf("):");
ppIRType(e->Iex.CCall.retty);
}
vex_printf("DIRTY ");
ppIRExpr(d->guard);
- if (d->needsBBP)
- vex_printf(" NeedsBBP");
if (d->mFx != Ifx_None) {
vex_printf(" ");
ppIREffect(d->mFx);
ppIRCallee(d->cee);
vex_printf("(");
for (i = 0; d->args[i] != NULL; i++) {
- ppIRExpr(d->args[i]);
+ IRExpr* arg = d->args[i];
+ if (arg == IRExprP__VECRET) {
+ vex_printf("VECRET");
+ } else if (arg == IRExprP__BBPTR) {
+ vex_printf("BBPTR");
+ } else {
+ ppIRExpr(arg);
+ }
if (d->args[i+1] != NULL) {
vex_printf(",");
}
d->mFx = Ifx_None;
d->mAddr = NULL;
d->mSize = 0;
- d->needsBBP = False;
d->nFxState = 0;
return d;
}
d2->mFx = d->mFx;
d2->mAddr = d->mAddr==NULL ? NULL : deepCopyIRExpr(d->mAddr);
d2->mSize = d->mSize;
- d2->needsBBP = d->needsBBP;
d2->nFxState = d->nFxState;
for (i = 0; i < d2->nFxState; i++)
d2->fxState[i] = d->fxState[i];
}
*/
+static inline Bool isIRAtom_or_VECRET_or_BBPTR ( IRExpr* e ) {
+ /* Use this rather roundabout scheme so as to try and have the
+ number of additional conditional branches be 1 in the common
+ (non-VECRET, non-BBPTR) case, rather than 2. */
+ if (UNLIKELY(((HWord)e) & 1)) {
+ return e == IRExprP__VECRET || e == IRExprP__BBPTR;
+ } else {
+ return isIRAtom(e);
+ }
+}
+
Bool isFlatIRStmt ( IRStmt* st )
{
Int i;
if (!isIRAtom(di->guard))
return False;
for (i = 0; di->args[i]; i++)
- if (!isIRAtom(di->args[i]))
+ if (!isIRAtom_or_VECRET_or_BBPTR(di->args[i]))
return False;
if (di->mAddr && !isIRAtom(di->mAddr))
return False;
case Iex_Const:
break;
case Iex_CCall:
- for (i = 0; expr->Iex.CCall.args[i]; i++)
- useBeforeDef_Expr(bb,stmt,expr->Iex.CCall.args[i],def_counts);
+ for (i = 0; expr->Iex.CCall.args[i]; i++) {
+ IRExpr* arg = expr->Iex.CCall.args[i];
+ if (UNLIKELY(((HWord)arg) & 1)) {
+ /* These aren't allowed in CCall lists. Let's detect
+ and throw them out here, though, rather than
+ segfaulting a bit later on. */
+ sanityCheckFail(bb,stmt, "IRExprP__* value in CCall arg list");
+ } else {
+ useBeforeDef_Expr(bb,stmt,arg,def_counts);
+ }
+ }
break;
case Iex_ITE:
useBeforeDef_Expr(bb,stmt,expr->Iex.ITE.cond,def_counts);
break;
case Ist_Dirty:
d = stmt->Ist.Dirty.details;
- for (i = 0; d->args[i] != NULL; i++)
- useBeforeDef_Expr(bb,stmt,d->args[i],def_counts);
+ for (i = 0; d->args[i] != NULL; i++) {
+ IRExpr* arg = d->args[i];
+ if (UNLIKELY(((HWord)arg) & 1)) {
+ /* This is ensured by isFlatIRStmt */
+ vassert(arg == IRExprP__VECRET || arg == IRExprP__BBPTR);
+ } else {
+ useBeforeDef_Expr(bb,stmt,arg,def_counts);
+ }
+ }
if (d->mFx != Ifx_None)
useBeforeDef_Expr(bb,stmt,d->mAddr,def_counts);
break;
for (i = 0; expr->Iex.CCall.args[i]; i++) {
if (i >= 32)
sanityCheckFail(bb,stmt,"Iex.CCall: > 32 args");
- tcExpr(bb,stmt, expr->Iex.CCall.args[i], gWordTy);
+ IRExpr* arg = expr->Iex.CCall.args[i];
+ if (UNLIKELY(is_IRExprP__VECRET_or_BBPTR(arg)))
+ sanityCheckFail(bb,stmt,"Iex.CCall.args: is VECRET/BBPTR");
+ tcExpr(bb,stmt, arg, gWordTy);
}
if (expr->Iex.CCall.retty == Ity_I1)
sanityCheckFail(bb,stmt,"Iex.CCall.retty: cannot return :: Ity_I1");
!= typeOfIRExpr(tyenv, expr->Iex.ITE.iffalse))
sanityCheckFail(bb,stmt,"Iex.ITE: iftrue/iffalse mismatch");
break;
- default:
+ default:
vpanic("tcExpr");
}
}
}
break;
}
- case Ist_Dirty:
+ case Ist_Dirty: {
/* Mostly check for various kinds of ill-formed dirty calls. */
d = stmt->Ist.Dirty.details;
if (d->cee == NULL) goto bad_dirty;
}
if (d->nFxState < 0 || d->nFxState > VEX_N_FXSTATE)
goto bad_dirty;
- if (d->nFxState == 0 && d->needsBBP)
- goto bad_dirty;
for (i = 0; i < d->nFxState; i++) {
if (d->fxState[i].fx == Ifx_None) goto bad_dirty;
if (d->fxState[i].size <= 0) goto bad_dirty;
if (typeOfIRExpr(tyenv, d->guard) != Ity_I1)
sanityCheckFail(bb,stmt,"IRStmt.Dirty.guard not :: Ity_I1");
/* check types, minimally */
- if (d->tmp != IRTemp_INVALID
- && typeOfIRTemp(tyenv, d->tmp) == Ity_I1)
- sanityCheckFail(bb,stmt,"IRStmt.Dirty.dst :: Ity_I1");
+ IRType retTy = Ity_INVALID;
+ if (d->tmp != IRTemp_INVALID) {
+ retTy = typeOfIRTemp(tyenv, d->tmp);
+ if (retTy == Ity_I1)
+ sanityCheckFail(bb,stmt,"IRStmt.Dirty.dst :: Ity_I1");
+ }
+ UInt nVECRETs = 0, nBBPTRs = 0;
for (i = 0; d->args[i] != NULL; i++) {
if (i >= 32)
sanityCheckFail(bb,stmt,"IRStmt.Dirty: > 32 args");
- if (typeOfIRExpr(tyenv, d->args[i]) == Ity_I1)
- sanityCheckFail(bb,stmt,"IRStmt.Dirty.arg[i] :: Ity_I1");
+ IRExpr* arg = d->args[i];
+ if (UNLIKELY(((HWord)arg) & 1)) {
+ if (arg == IRExprP__VECRET) {
+ nVECRETs++;
+ } else
+ if (arg == IRExprP__BBPTR) {
+ nBBPTRs++;
+ } else {
+ /* The impossibility of failure is ensured by
+ isFlatIRStmt */
+ vassert(0);
+ }
+ } else {
+ if (typeOfIRExpr(tyenv, arg) == Ity_I1)
+ sanityCheckFail(bb,stmt,"IRStmt.Dirty.arg[i] :: Ity_I1");
+ }
+ if (nBBPTRs > 1) {
+ sanityCheckFail(bb,stmt,"IRStmt.Dirty.args: > 1 BBPTR arg");
+ }
+ if (nVECRETs == 1) {
+ /* Fn must return V128 or V256. */
+ if (retTy != Ity_V128 && retTy != Ity_V256)
+ sanityCheckFail(bb,stmt,
+ "IRStmt.Dirty.args: VECRET present, "
+ "but fn does not return V128 or V256");
+ } else if (nVECRETs == 0) {
+ /* Fn must not return V128 or V256 */
+ if (retTy == Ity_V128 || retTy == Ity_V256)
+ sanityCheckFail(bb,stmt,
+ "IRStmt.Dirty.args: VECRET not present, "
+ "but fn returns V128 or V256");
+ } else {
+ sanityCheckFail(bb,stmt,
+ "IRStmt.Dirty.args: > 1 VECRET present");
+ }
}
- break;
+ if (nBBPTRs > 1) {
+ sanityCheckFail(bb,stmt,
+ "IRStmt.Dirty.args: > 1 BBPTR present");
+ }
+ /* If you ask for the baseblock pointer, you have to make
+ some declaration about access to the guest state too. */
+ if (d->nFxState == 0 && nBBPTRs != 0) {
+ sanityCheckFail(bb,stmt,
+ "IRStmt.Dirty.args: BBPTR requested, "
+ "but no fxState declared");
+ }
+ break;
bad_dirty:
sanityCheckFail(bb,stmt,"IRStmt.Dirty: ill-formed");
break;
+ }
case Ist_NoOp:
break;
case Ist_MBE:
vassert(d2->mAddr == NULL);
}
d2->guard = flatten_Expr(bb, d2->guard);
- for (i = 0; d2->args[i]; i++)
- d2->args[i] = flatten_Expr(bb, d2->args[i]);
+ for (i = 0; d2->args[i]; i++) {
+ IRExpr* arg = d2->args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ d2->args[i] = flatten_Expr(bb, arg);
+ }
addStmtToIRSB(bb, IRStmt_Dirty(d2));
break;
case Ist_NoOp:
vassert(isIRAtom(d2->guard));
d2->guard = fold_Expr(env, subst_Expr(env, d2->guard));
for (i = 0; d2->args[i]; i++) {
- vassert(isIRAtom(d2->args[i]));
- d2->args[i] = fold_Expr(env, subst_Expr(env, d2->args[i]));
+ IRExpr* arg = d2->args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg))) {
+ vassert(isIRAtom(arg));
+ d2->args[i] = fold_Expr(env, subst_Expr(env, arg));
+ }
}
return IRStmt_Dirty(d2);
}
if (d->mFx != Ifx_None)
addUses_Expr(set, d->mAddr);
addUses_Expr(set, d->guard);
- for (i = 0; d->args[i] != NULL; i++)
- addUses_Expr(set, d->args[i]);
+ for (i = 0; d->args[i] != NULL; i++) {
+ IRExpr* arg = d->args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ addUses_Expr(set, arg);
+ }
return;
case Ist_NoOp:
case Ist_IMark:
if (d->mFx != Ifx_None)
aoccCount_Expr(uses, d->mAddr);
aoccCount_Expr(uses, d->guard);
- for (i = 0; d->args[i]; i++)
- aoccCount_Expr(uses, d->args[i]);
+ for (i = 0; d->args[i]; i++) {
+ IRExpr* arg = d->args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ aoccCount_Expr(uses, arg);
+ }
return;
case Ist_NoOp:
case Ist_IMark:
if (d2->mFx != Ifx_None)
d2->mAddr = atbSubst_Expr(env, d2->mAddr);
d2->guard = atbSubst_Expr(env, d2->guard);
- for (i = 0; d2->args[i]; i++)
- d2->args[i] = atbSubst_Expr(env, d2->args[i]);
+ for (i = 0; d2->args[i]; i++) {
+ IRExpr* arg = d2->args[i];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ d2->args[i] = atbSubst_Expr(env, arg);
+ }
return IRStmt_Dirty(d2);
default:
vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
Int i;
/* Passing the guest state pointer opens the door to modifying the
- guest state under the covers. It's not allowed, but let's be
+ guest state under the covers. It's not allowed, but let's be
extra conservative and assume the worst. */
- if (d->needsBBP) {
- *requiresPreciseMemExns = True;
- return True;
+ for (i = 0; d->args[i]; i++) {
+ if (UNLIKELY(d->args[i] == IRExprP__BBPTR)) {
+ *requiresPreciseMemExns = True;
+ return True;
+ }
}
/* Check the side effects on the guest state */
Int nRepeats = d->fxState[i].nRepeats;
Int repeatLen = d->fxState[i].repeatLen;
- if (preciseMemExnsFn(offset, offset + nRepeats * repeatLen + size - 1)) {
+ if (preciseMemExnsFn(offset,
+ offset + nRepeats * repeatLen + size - 1)) {
*requiresPreciseMemExns = True;
return True;
}
case Ist_Dirty:
d = st->Ist.Dirty.details;
vassert(isIRAtom(d->guard));
- for (j = 0; d->args[j]; j++)
- vassert(isIRAtom(d->args[j]));
+ for (j = 0; d->args[j]; j++) {
+ IRExpr* arg = d->args[j];
+ if (LIKELY(!is_IRExprP__VECRET_or_BBPTR(arg)))
+ vassert(isIRAtom(arg));
+ }
if (d->mFx != Ifx_None)
vassert(isIRAtom(d->mAddr));
break;
emit = (Int(*)(Bool*,UChar*,Int,HInstr*,Bool,
void*,void*,void*,void*))
emit_MIPSInstr;
-#if defined(VKI_LITTLE_ENDIAN)
+# if defined(VKI_LITTLE_ENDIAN)
host_is_bigendian = False;
-#elif defined(VKI_BIG_ENDIAN)
+# elif defined(VKI_BIG_ENDIAN)
host_is_bigendian = True;
-#endif
+# endif
host_word_type = Ity_I32;
vassert(are_valid_hwcaps(VexArchMIPS32, vta->archinfo_host.hwcaps));
break;
emit = (Int(*)(Bool*,UChar*,Int,HInstr*,Bool,
void*,void*,void*,void*))
emit_MIPSInstr;
-#if defined(VKI_LITTLE_ENDIAN)
+# if defined(VKI_LITTLE_ENDIAN)
host_is_bigendian = False;
-#elif defined(VKI_BIG_ENDIAN)
+# elif defined(VKI_BIG_ENDIAN)
host_is_bigendian = True;
-#endif
+# endif
host_word_type = Ity_I64;
vassert(are_valid_hwcaps(VexArchMIPS64, vta->archinfo_host.hwcaps));
break;
Ist_Dirty inhibits various IR optimisations and so can cause
quite poor code to be generated. Try to avoid it.
+ In principle it would be allowable to have the arg vector
+ contain the special value IRExprP__VECRET, although not
+ IRExprP__BBPTR. However, at the moment there is no
+ requirement for clean helper calls to be able to return V128
+ or V256 values. Hence this is not allowed.
+
ppIRExpr output: <cee>(<args>):<retty>
eg. foo{0x80489304}(t1, t2):I32
*/
IRExpr* arg4; /* operand 4 */
};
+
+/* Two special constants of type IRExpr*, which can ONLY be used in
+ argument lists for dirty helper calls (IRDirty.args) and in NO
+ OTHER PLACES. And then only in very limited ways. These constants
+ are not pointer-aligned and hence can't be confused with real
+ IRExpr*s nor with NULL. */
+
+/* Denotes an argument which (in the helper) takes a pointer to a
+ (naturally aligned) V128 or V256, into which the helper is expected
+ to write its result. Use of IRExprP__VECRET is strictly
+ controlled. If the helper returns a V128 or V256 value then
+ IRExprP__VECRET must appear exactly once in the arg list, although
+ it can appear anywhere, and the helper must have a C 'void' return
+ type. If the helper returns any other type, IRExprP__VECRET may
+ not appear in the argument list. */
+#define IRExprP__VECRET ((IRExpr*)9)
+
+/* Denotes an void* argument which is passed to the helper, which at
+ run time will point to the thread's guest state area. This can
+ only appear at most once in an argument list, and it may not appear
+ at all in argument lists for clean helper calls. */
+#define IRExprP__BBPTR ((IRExpr*)17)
+
+static inline Bool is_IRExprP__VECRET_or_BBPTR ( IRExpr* e ) {
+ return e == IRExprP__VECRET || e == IRExprP__BBPTR;
+}
+
+
/* Expression constructors. */
extern IRExpr* IRExpr_Binder ( Int binder );
extern IRExpr* IRExpr_Get ( Int off, IRType ty );
number of times at a fixed interval, if required.
Normally, code is generated to pass just the args to the helper.
- However, if .needsBBP is set, then an extra first argument is
- passed, which is the baseblock pointer, so that the callee can
- access the guest state. It is invalid for .nFxState to be zero
- but .needsBBP to be True, since .nFxState==0 is a claim that the
- call does not access guest state.
+ However, if IRExprP__BBPTR is present in the argument list (at most
+ one instance is allowed), then the baseblock pointer is passed for
+ that arg, so that the callee can access the guest state. It is
+ invalid for .nFxState to be zero but IRExprP__BBPTR to be present,
+ since .nFxState==0 is a claim that the call does not access guest
+ state.
IMPORTANT NOTE re GUARDS: Dirty calls are strict, very strict. The
arguments and 'mFx' are evaluated REGARDLESS of the guard value.
allowed. */
IRCallee* cee; /* where to call */
IRExpr* guard; /* :: Ity_Bit. Controls whether call happens */
- IRExpr** args; /* arg list, ends in NULL */
+ /* The args vector may contain IRExprP__BBPTR and/or
+ IRExprP__VECRET, in both cases, at most once. */
+ IRExpr** args; /* arg vector, ends in NULL. */
IRTemp tmp; /* to assign result to, or IRTemp_INVALID if none */
/* Mem effects; we allow only one R/W/M region to be stated */
Int mSize; /* of access, or zero if mFx==Ifx_None */
/* Guest state effects; up to N allowed */
- Bool needsBBP; /* True => also pass guest state ptr to callee */
Int nFxState; /* must be 0 .. VEX_N_FXSTATE */
struct {
IREffect fx:16; /* read, write or modify? Ifx_None is invalid. */