/* Used by the back end to look up addresses of helper
function calls inserted by bbToIR_X86Instr. */
extern
-Addr64 x86guest_findhelper ( Char* function_name );
+HWord x86guest_findhelper ( Char* function_name );
/* Used by the optimiser to specialise calls to helpers. */
extern
static ULong calculate_RCR ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz );
/* --- DIRTY HELPERS --- */
-static ULong loadF80le ( VexGuestX86State*, UInt );
-static void storeF80le ( VexGuestX86State*, UInt, ULong );
-static void dirtyhelper_CPUID ( VexGuestX86State* st );
+static ULong loadF80le ( UInt );
+static void storeF80le ( UInt, ULong );
+static void dirtyhelper_CPUID ( VexGuestX86State* );
/* This file contains helper functions for x86 guest code.
this file will be compiled to host machine code, so that
all makes sense.
- x86guest_findhelper() is the only exported function.
-
Only change the signatures of these helper functions very
carefully. If you change the signature here, you'll have to change
the parameters passed to it in the IR calls constructed by
}
-Addr64 x86guest_findhelper ( Char* function_name )
+HWord x86guest_findhelper ( Char* function_name )
{
if (vex_streq(function_name, "calculate_condition"))
- return (Addr64)(Addr32)(& calculate_condition);
+ return (HWord)(& calculate_condition);
if (vex_streq(function_name, "calculate_eflags_c"))
- return (Addr64)(Addr32)(& calculate_eflags_c);
+ return (HWord)(& calculate_eflags_c);
if (vex_streq(function_name, "calculate_eflags_all"))
- return (Addr64)(Addr32)(& calculate_eflags_all);
+ return (HWord)(& calculate_eflags_all);
if (vex_streq(function_name, "calculate_FXAM"))
- return (Addr64)(Addr32)(& calculate_FXAM);
+ return (HWord)(& calculate_FXAM);
if (vex_streq(function_name, "storeF80le"))
- return (Addr64)(Addr32)(& storeF80le);
+ return (HWord)(& storeF80le);
if (vex_streq(function_name, "loadF80le"))
- return (Addr64)(Addr32)(& loadF80le);
+ return (HWord)(& loadF80le);
if (vex_streq(function_name, "calculate_RCR"))
- return (Addr64)(Addr32)(& calculate_RCR);
+ return (HWord)(& calculate_RCR);
if (vex_streq(function_name, "dirtyhelper_CPUID"))
- return (Addr64)(Addr32)(& dirtyhelper_CPUID);
- vex_printf("\nx86 guest: can't find helper: %s\n", function_name);
- vpanic("x86guest_findhelper");
+ return (HWord)(& dirtyhelper_CPUID);
+ return 0; /* not found */
}
/* Used by the optimiser to try specialisations. Returns an
/* CALLED FROM GENERATED CODE */
/* DIRTY HELPER (reads guest memory) */
-static ULong loadF80le ( VexGuestX86State* st, UInt addrU )
+static ULong loadF80le ( UInt addrU )
{
ULong f64;
convert_f80le_to_f64le ( (UChar*)addrU, (UChar*)&f64 );
/* CALLED FROM GENERATED CODE */
/* DIRTY HELPER (writes guest memory) */
-static void storeF80le ( VexGuestX86State* st, UInt addrU, ULong f64 )
+static void storeF80le ( UInt addrU, ULong f64 )
{
convert_f64le_to_f80le( (UChar*)&f64, (UChar*)addrU );
}
ULong loadF80le ( VexGuestX86State*, UInt )
addr holds the address. First, do a dirty call to
get hold of the data. */
- /* give details of args, and where to call */
- IRDirty* d;
- DIP("fldt %s", dis_buf);
- d = emptyIRDirty();
- d->name = "loadF80le";
- d->args = LibVEX_Alloc(2 * sizeof(IRTemp));
- d->args[0] = mkexpr(addr);
- d->args[1] = NULL;
- d->tmp = newTemp(Ity_I64);
+ IRTemp val = newTemp(Ity_I64);
+ IRExpr** args = mkIRExprVec_1 ( mkexpr(addr) );
+
+ IRDirty* d = unsafeIRDirty_1_N ( val, "loadF80le", args );
/* declare that we're reading memory */
d->mFx = Ifx_Read;
d->mAddr = mkexpr(addr);
d->mSize = 10;
- /* declare that we don't mess with guest state */
- d->nFxState = 0;
- /* execute the dirty call, dumping the result in d->tmp. */
+
+ /* execute the dirty call, dumping the result in val. */
stmt( IRStmt_Dirty(d) );
fp_push();
- put_ST(0, unop(Iop_ReinterpI64asF64, mkexpr(d->tmp)));
+ put_ST(0, unop(Iop_ReinterpI64asF64, mkexpr(val)));
+
+ DIP("fldt %s", dis_buf);
break;
}
case 7: { /* FSTP extended-real */
/* Uses dirty helper: void storeF80le ( UInt, ULong ) */
- IRDirty* d;
- DIP("fldt %s", dis_buf);
- d = emptyIRDirty();
- d->name = "storeF80le";
- /* takes 2 args */
- d->args = LibVEX_Alloc(3 * sizeof(IRTemp));
- d->args[0] = mkexpr(addr);
- d->args[1] = unop(Iop_ReinterpF64asI64, get_ST(0));
- d->args[2] = NULL;
- /* returns nothing */
- d->tmp = INVALID_IRTEMP;
+ IRExpr** args
+ = mkIRExprVec_2( mkexpr(addr),
+ unop(Iop_ReinterpF64asI64, get_ST(0)) );
+
+ IRDirty* d = unsafeIRDirty_0_N ( "storeF80le", args );
/* declare we're writing memory */
d->mFx = Ifx_Write;
d->mAddr = mkexpr(addr);
d->mSize = 10;
- /* declare that we don't mess with guest state */
- d->nFxState = 0;
+
/* execute the dirty call. */
stmt( IRStmt_Dirty(d) );
fp_pop();
+
+ DIP("fstpt %s", dis_buf);
break;
}
void dirtyhelper_CPUID ( VexGuestX86State* )
declared to mod eax, wr ebx, ecx, edx
*/
- /* give details of args, and where to call */
- IRDirty* d;
- d = emptyIRDirty();
- d->name = "dirtyhelper_CPUID";
- d->args = LibVEX_Alloc(1 * sizeof(IRTemp));
- d->args[0] = NULL;
- d->tmp = INVALID_IRTEMP;
- /* declare that we're not accessing memory */
- d->mFx = Ifx_None;
- d->mAddr = NULL;
- d->mSize = 0;
+ IRExpr** args = mkIRExprVec_0();
+ IRDirty* d = unsafeIRDirty_0_N ( "dirtyhelper_CPUID", args );
/* declare guest state effects */
+ d->needsBBP = True;
d->nFxState = 4;
d->fxState[0].fx = Ifx_Modify;
d->fxState[0].offset = OFFB_EAX;
extern X86Instr* genSpill_X86 ( HReg rreg, Int offset );
extern X86Instr* genReload_X86 ( HReg rreg, Int offset );
extern void getAllocableRegs_X86 ( Int*, HReg** );
-extern HInstrArray* iselBB_X86 ( IRBB*, Addr64(*)(Char*) );
+extern HInstrArray* iselBB_X86 ( IRBB*, HWord(*)(Char*),
+ HWord(*)(Char*) );
#endif /* ndef __LIBVEX_X86H_DEFS_H */
/* This carries around:
- - A function for looking up the address of helper functions.
+ - A function for looking up the address of vex front-end supplied
+ helper functions.
+
+ - A function for looking up the address of tool-supplied helper
+ functions.
- A mapping from IRTemp to IRType, giving the type of any IRTemp we
might encounter. This is computed before insn selection starts,
typedef
struct {
- Addr64 (*find_helper)(Char*);
+ HWord (*find_helper_vex)(Char*);
+ HWord (*find_helper_tool)(Char*);
IRTypeEnv* type_env;
vpanic("pushArg(x86): can't handle arg of this type");
}
+
+/* Find the address of a helper function, first by consulting vex's
+ own front end, and if that doesn't work, by consulting the
+ tool-supplied lookup function. */
+
+static
+HWord findHelper ( ISelEnv* env, Char* name )
+{
+ HWord helper;
+
+ /* Since the host -- for which we are generating code, and on which
+ the helpers are compiled -- is a 32-bit machine (x86) -- HWord
+ must be a 32-bit type. */
+ vassert(sizeof(HWord) == 4);
+
+ /* First see if vex's own front end has a binding for this fn. */
+ helper = env->find_helper_vex(name);
+
+ /* No? Try the tool-supplied function. */
+ if (helper == 0 && env->find_helper_tool)
+ helper = env->find_helper_tool(name);
+
+ if (helper != 0)
+ return helper;
+
+ vex_printf("\nvex x86 back end: can't find helper: %s\n", name);
+ vpanic("findHelper(x86,host)");
+}
+
+
/* Complete the call to a helper function, by calling the
helper and clearing the args off the stack. */
static
void callHelperAndClearArgs ( ISelEnv* env, Char* name, Int n_arg_ws )
{
- Addr64 helper;
- UInt target;
-
- /* Find the function to call. Since the host -- for which we are
- generating code -- is a 32-bit machine (x86) -- the upper 32
- bits of the helper address should be zero. */
- helper = env->find_helper(name);
- vassert((helper & 0xFFFFFFFF00000000LL) == 0);
- target = helper & 0xFFFFFFFF;
+ HWord helper = findHelper( env, name );
+ vassert(sizeof(HWord) == 4);
+
addInstr(env, X86Instr_Alu32R(
Xalu_MOV,
- X86RMI_Imm(target),
+ X86RMI_Imm(helper),
hregX86_EAX()));
addInstr(env, X86Instr_Call(hregX86_EAX()));
if (n_arg_ws > 0)
for (i = n_args-1; i >= 0; i--)
n_arg_ws += pushArg(env, d->args[i]);
- /* and finally the leftmost argument, the guest state pointer,
- which is in %ebp. */
- addInstr(env, X86Instr_Push(X86RMI_Reg(hregX86_EBP())));
- n_arg_ws++;
+ if (d->nFxState == 0)
+ vassert(!d->needsBBP);
+ if (d->nFxState > 0 && d->needsBBP) {
+ /* If the helper claims it accesses guest state, give it a
+ first argument which is the guest state pointer (%ebp). */
+ addInstr(env, X86Instr_Push(X86RMI_Reg(hregX86_EBP())));
+ n_arg_ws++;
+ }
/* call the helper, and get the args off the stack afterwards. */
callHelperAndClearArgs( env, d->name, n_arg_ws );
/* Translate an entire BB to x86 code. */
-HInstrArray* iselBB_X86 ( IRBB* bb, Addr64(*find_helper)(Char*) )
+HInstrArray* iselBB_X86 ( IRBB* bb,
+ HWord(*find_helper_vex)(Char*),
+ HWord(*find_helper_tool)(Char*) )
{
Int i, j;
HReg hreg, hregHI;
ISelEnv* env = LibVEX_Alloc(sizeof(ISelEnv));
env->vreg_ctr = 0;
- /* Register helper-function-finder. */
- env->find_helper = find_helper;
+ /* Register helper-function-finders. */
+ env->find_helper_vex = find_helper_vex;
+ env->find_helper_tool = find_helper_tool;
/* Set up output code array. */
env->code = newHInstrArray();
void ppIRDirty ( IRDirty* d )
{
Int i;
- vex_printf("DIRTY ");
+ vex_printf("DIRTY");
+ if (d->needsBBP)
+ vex_printf(" NeedsBBP");
if (d->mFx != Ifx_None) {
ppIREffect(d->mFx);
vex_printf("-mem(");
ppIREffect(d->fxState[i].fx);
vex_printf("-gst(%d,%d) ", d->fxState[i].offset, d->fxState[i].size);
}
- vex_printf("::: ");
+ vex_printf(" ::: ");
if (d->tmp != INVALID_IRTEMP) {
ppIRTemp(d->tmp);
vex_printf(" = ");
}
+/* Constructors for NULL-terminated IRExpr expression vectors,
+ suitable for use as arg lists in clean/dirty helper calls. */
+
+IRExpr** mkIRExprVec_0 ( void ) {
+ IRExpr** vec = LibVEX_Alloc(1 * sizeof(IRExpr*));
+ vec[0] = NULL;
+ return vec;
+}
+IRExpr** mkIRExprVec_1 ( IRExpr* arg1 ) {
+ IRExpr** vec = LibVEX_Alloc(2 * sizeof(IRExpr*));
+ vec[0] = arg1;
+ vec[1] = NULL;
+ return vec;
+}
+IRExpr** mkIRExprVec_2 ( IRExpr* arg1, IRExpr* arg2 ) {
+ IRExpr** vec = LibVEX_Alloc(3 * sizeof(IRExpr*));
+ vec[0] = arg1;
+ vec[1] = arg2;
+ vec[2] = NULL;
+ return vec;
+}
+
+
/* Constructors -- IRDirty */
-IRDirty* emptyIRDirty ( void )
-{
+IRDirty* emptyIRDirty ( void ) {
IRDirty* d = LibVEX_Alloc(sizeof(IRDirty));
d->name = NULL;
d->args = NULL;
d->mFx = Ifx_None;
d->mAddr = NULL;
d->mSize = 0;
+ d->needsBBP = False;
d->nFxState = 0;
return d;
}
+IRDirty* unsafeIRDirty_0_N ( Char* name, IRExpr** args ) {
+ IRDirty* d = emptyIRDirty();
+ d->name = name;
+ d->args = args;
+ return d;
+}
+IRDirty* unsafeIRDirty_1_N ( IRTemp dst, Char* name, IRExpr** args ) {
+ IRDirty* d = emptyIRDirty();
+ d->name = name;
+ d->args = args;
+ d->tmp = dst;
+ return d;
+}
+
/* Constructors -- IRStmt */
d2->mFx = d->mFx;
d2->mAddr = d->mAddr==NULL ? NULL : dopyIRExpr(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];
}
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;
Int* host_bytes_used,
/* IN: optionally, an instrumentation function. */
IRBB* (*instrument) ( IRBB* ),
+ HWord (*tool_findhelper) ( Char* ),
/* IN: optionally, an access check function for guest code. */
Bool (*byte_accessible) ( Addr64 ),
/* IN: if > 0, use this verbosity for this bb */
HInstr* (*genReload) ( HReg, Int );
void (*ppInstr) ( HInstr* );
void (*ppReg) ( HReg );
- HInstrArray* (*iselBB) ( IRBB*, Addr64(*)(Char*) );
+ HInstrArray* (*iselBB) ( IRBB*, HWord(*)(Char*), HWord(*)(Char*) );
IRBB* (*bbToIR) ( UChar*, Addr64, Int*,
Bool(*)(Addr64), Bool );
Int (*emit) ( UChar*, Int, HInstr* );
- Addr64 (*findHelper) ( Char* );
+ HWord (*findHelper) ( Char* );
IRExpr* (*specHelper) ( Char*, IRExpr** );
Bool (*preciseMemExnsFn) ( Int, Int );
HInstrArray* rcode;
Int i, j, k, out_used, saved_verbosity, guest_sizeB;
UChar insn_bytes[32];
+ IRType guest_word_size;
available_real_regs = NULL;
n_available_real_regs = 0;
findHelper = NULL;
specHelper = NULL;
preciseMemExnsFn = NULL;
+ guest_word_size = Ity_INVALID;
saved_verbosity = vex_verbosity;
if (bb_verbosity > 0)
findHelper = x86guest_findhelper;
specHelper = x86guest_spechelper;
guest_sizeB = sizeof(VexGuestX86State);
+ guest_word_size = Ity_I32;
break;
default:
vpanic("LibVEX_Translate: unsupported guest insn set");
}
/* Sanity check the initial IR. */
- sanityCheckIRBB(irbb, Ity_I32);
+ sanityCheckIRBB(irbb, guest_word_size);
/* Clean it up, hopefully a lot. */
irbb = do_iropt_BB ( irbb, specHelper, preciseMemExnsFn,
guest_bytes_addr );
- sanityCheckIRBB(irbb, Ity_I32);
+ sanityCheckIRBB(irbb, guest_word_size);
if (vex_verbosity > 0) {
vex_printf("\n-------- After IR optimisation --------\n");
}
/* Get the thing instrumented. */
- if (instrument)
+ if (instrument) {
irbb = (*instrument)(irbb);
+ sanityCheckIRBB(irbb, guest_word_size);
+ }
/* Turn it into virtual-registerised code. */
- vcode = iselBB ( irbb, findHelper );
+ vcode = iselBB ( irbb, findHelper, tool_findhelper );
if (vex_verbosity > 0) {
vex_printf("\n-------- Virtual registerised code --------\n");
/* Assemble */
out_used = 0; /* tracks along the host_bytes array */
for (i = 0; i < rcode->arr_used; i++) {
- if (vex_verbosity > 1) {
+ if (vex_verbosity > 2) {
ppInstr(rcode->arr[i]);
vex_printf("\n");
}
j = (*emit)( insn_bytes, 32, rcode->arr[i] );
- if (vex_verbosity > 1) {
+ if (vex_verbosity > 2) {
for (k = 0; k < j; k++)
if (insn_bytes[k] < 16)
vex_printf("0%x ", (UInt)insn_bytes[k]);
Int* host_bytes_used,
/* IN: optionally, an instrumentation function. */
IRBB* (*instrument) ( IRBB* ),
+ HWord (*tool_findhelper) ( Char* ),
/* IN: optionally, an access check function for guest code. */
Bool (*byte_accessible) ( Addr64 ),
/* IN: if > 0, use this verbosity for this bb */
details about what the helper does (and you better be telling the
truth, otherwise any derived instrumentation will be wrong). Also
IRStmt_Dirty inhibits various IR optimisations and so can cause
- quite poor code to be generated. Use it as little as possible, and
- in non-performance-critical situations only.
+ quite poor code to be generated. Try to avoid it.
*/
/* The possible kinds of expressions are as follows: */
extern IRExpr* IRExpr_Mux0X ( IRExpr* cond, IRExpr* expr0, IRExpr* exprX );
extern IRExpr* dopyIRExpr ( IRExpr* );
-extern IRExpr** sopyIRExprVec ( IRExpr** );
-extern IRExpr** dopyIRExprVec ( IRExpr** );
-
extern void ppIRExpr ( IRExpr* );
+/* NULL-terminated IRExpr expression vectors, suitable for use as arg
+ lists in clean/dirty helper calls. */
+
+extern IRExpr** mkIRExprVec_0 ( void );
+extern IRExpr** mkIRExprVec_1 ( IRExpr* arg1 );
+extern IRExpr** mkIRExprVec_2 ( IRExpr* arg1, IRExpr* arg2 );
+
+extern IRExpr** sopyIRExprVec ( IRExpr** );
+extern IRExpr** dopyIRExprVec ( IRExpr** );
+
/* ------------------ Dirty helper calls ------------------ */
results and/or do different things when called repeated with the
same arguments, by means of storing private state.
- The supplied arguments are all IRTemps, to attempt to sidestep any
- semantic difficulties created by allowing them to be arbitrary
- expressions (although I can't think of any such). If a value is
- returned, it is assigned to the nominated return temporary.
+ If a value is returned, it is assigned to the nominated return
+ temporary.
Dirty calls are statements rather than expressions for obvious
- reasons. IRTemps may or may not stay alive across them. A precise
- statement is: any IRTemp which depends, directly or indirectly, on
- any memory or guest state segments which the call claims to write
- or modify, must be regarded as invalid after the call and therefore
- may not be used after it.
-
- It would be nice to automatically check this in the sanity checker,
- but doing so exactly is difficult (although probably possible).
- Some approximation scheme will be needed. This would rule out some
- constructions which are actually valid, but not interesting, whilst
- still catching all invalid constructions.
-
- One obvious if pessimistic approximation is to say that *all*
- IRTemps are killed across a Dirty call. That's easy to
- mechanically check, but it's not going to work with
- instrumentation, as doing the shadow state updates after the call
- will surely require reading temps defined before the call. Needs
- further consideration.
+ reasons. If a dirty call is stated as writing guest state, any
+ values derived from the written parts of the guest state are
+ invalid. Similarly, if the dirty call is stated as writing
+ memory, any loaded values are invalidated by it.
In order that instrumentation is possible, the call must state, and
state correctly
* whether it reads, writes or modifies guest state, and if so which
pieces (several pieces may be stated, and currently their extents
must be known at translation-time).
+
+ 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.
*/
#define VEX_N_FXSTATE 4 /* enough for CPUID on x86 */
Int mSize; /* of access, or zero if mFx==Ifx_None */
/* Guest state effects; up to N allowed */
- Int nFxState; /* must be 0 .. VEX_N_FXSTATE */
+ Bool needsBBP; /* True => also pass guest state ptr to callee */
+ Int nFxState; /* must be 0 .. VEX_N_FXSTATE */
struct {
IREffect fx; /* read, write or modify? */
Int offset;
extern IRDirty* dopyIRDirty ( IRDirty* );
+/* A handy function which takes some of the tedium out of constructing
+ dirty helper calls. The called function impliedly does not return
+ any value. The call is marked as accessing neither guest state nor
+ memory (hence the "unsafe" designation) -- you can mess with this
+ later if need be.*/
+extern IRDirty* unsafeIRDirty_0_N ( Char* name, IRExpr** args );
+
+/* Similarly, make a zero-annotation dirty call which returns a value,
+ and assign that to the given temp. */
+extern IRDirty* unsafeIRDirty_1_N ( IRTemp dst, Char* name, IRExpr** args );
+
/* ------------------ Statements ------------------ */