📄 isel.c
字号:
/* Marshal args for a call, do the call, and clear the stack. Complexities to consider: * if passBBP is True, %ebp (the baseblock pointer) is to be passed as the first arg. * If the callee claims regparmness of 1, 2 or 3, we must pass the first 1, 2 or 3 args in registers (EAX, EDX, and ECX respectively). To keep things relatively simple, only args of type I32 may be passed as regparms -- just bomb out if anything else turns up. Clearly this depends on the front ends not trying to pass any other types as regparms. */ /* 16 Nov 2004: the regparm handling is complicated by the following problem. Consider a call two a function with two regparm parameters: f(e1,e2). We need to compute e1 into %eax and e2 into %edx. Suppose code is first generated to compute e1 into %eax. Then, code is generated to compute e2 into %edx. Unfortunately, if the latter code sequence uses %eax, it will trash the value of e1 computed by the former sequence. This could happen if (for example) e2 itself involved a function call. In the code below, args are evaluated right-to-left, not left-to-right, but the principle and the problem are the same. One solution is to compute all regparm-bound args into vregs first, and once they are all done, move them to the relevant real regs. This always gives correct code, but it also gives a bunch of vreg-to-rreg moves which are usually redundant but are hard for the register allocator to get rid of. A compromise is to first examine all regparm'd argument expressions. If they are all so simple that it is clear they will be evaluated without use of any fixed registers, use the old compute-directly-to-fixed-target scheme. If not, be safe and use the via-vregs scheme. Note this requires being able to examine an expression and determine whether or not evaluation of it might use a fixed register. That requires knowledge of how the rest of this insn selector works. Currently just the following 3 are regarded as safe -- hopefully they cover the majority of arguments in practice: IRExpr_Tmp IRExpr_Const IRExpr_Get. */ vassert(cee->regparms >= 0 && cee->regparms <= 3); n_args = n_arg_ws = 0; while (args[n_args]) n_args++; not_done_yet = n_args; if (passBBP) not_done_yet++; stack_limit = cee->regparms; if (cee->regparms > 0 && passBBP) stack_limit--; /* ------ BEGIN marshall all arguments ------ */ /* Push (R to L) the stack-passed args, [n_args-1 .. stack_limit] */ for (i = n_args-1; i >= stack_limit; i--) { n_arg_ws += pushArg(env, args[i]); not_done_yet--; } /* args [stack_limit-1 .. 0] and possibly %ebp are to be passed in registers. */ if (cee->regparms > 0) { /* ------ BEGIN deal with regparms ------ */ /* deal with regparms, not forgetting %ebp if needed. */ argregs[0] = hregX86_EAX(); argregs[1] = hregX86_EDX(); argregs[2] = hregX86_ECX(); tmpregs[0] = tmpregs[1] = tmpregs[2] = INVALID_HREG; argreg = cee->regparms; /* In keeping with big comment above, detect potential danger and use the via-vregs scheme if needed. */ danger = False; for (i = stack_limit-1; i >= 0; i--) { if (mightRequireFixedRegs(args[i])) { danger = True; break; } } if (danger) { /* Move via temporaries */ argregX = argreg; for (i = stack_limit-1; i >= 0; i--) { if (0) { vex_printf("x86 host: register param is complex: "); ppIRExpr(args[i]); vex_printf("\n"); } argreg--; vassert(argreg >= 0); vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32); tmpregs[argreg] = iselIntExpr_R(env, args[i]); not_done_yet--; } for (i = stack_limit-1; i >= 0; i--) { argregX--; vassert(argregX >= 0); addInstr( env, mk_iMOVsd_RR( tmpregs[argregX], argregs[argregX] ) ); } } else { /* It's safe to compute all regparm args directly into their target registers. */ for (i = stack_limit-1; i >= 0; i--) { argreg--; vassert(argreg >= 0); vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I32); addInstr(env, X86Instr_Alu32R(Xalu_MOV, iselIntExpr_RMI(env, args[i]), argregs[argreg])); not_done_yet--; } } /* Not forgetting %ebp if needed. */ if (passBBP) { vassert(argreg == 1); addInstr(env, mk_iMOVsd_RR( hregX86_EBP(), argregs[0])); not_done_yet--; } /* ------ END deal with regparms ------ */ } else { /* No regparms. Heave %ebp on the stack if needed. */ if (passBBP) { addInstr(env, X86Instr_Push(X86RMI_Reg(hregX86_EBP()))); n_arg_ws++; not_done_yet--; } } vassert(not_done_yet == 0); /* ------ END marshall all arguments ------ */ /* Now we can compute the condition. We can't do it earlier because the argument computations could trash the condition codes. Be a bit clever to handle the common case where the guard is 1:Bit. */ cc = Xcc_ALWAYS; if (guard) { if (guard->tag == Iex_Const && guard->Iex.Const.con->tag == Ico_U1 && guard->Iex.Const.con->Ico.U1 == True) { /* unconditional -- do nothing */ } else { cc = iselCondCode( env, guard ); } } /* call the helper, and get the args off the stack afterwards. */ callHelperAndClearArgs( env, cc, cee, n_arg_ws );}/* Given a guest-state array descriptor, an index expression and a bias, generate an X86AMode holding the relevant guest state offset. */staticX86AMode* genGuestArrayOffset ( ISelEnv* env, IRArray* descr, IRExpr* off, Int bias ){ HReg tmp, roff; Int elemSz = sizeofIRType(descr->elemTy); Int nElems = descr->nElems; Int shift = 0; /* throw out any cases not generated by an x86 front end. In theory there might be a day where we need to handle them -- if we ever run non-x86-guest on x86 host. */ if (nElems != 8) vpanic("genGuestArrayOffset(x86 host)(1)"); switch (elemSz) { case 1: shift = 0; break; case 4: shift = 2; break; case 8: shift = 3; break; default: vpanic("genGuestArrayOffset(x86 host)(2)"); } /* Compute off into a reg, %off. Then return: movl %off, %tmp addl $bias, %tmp (if bias != 0) andl %tmp, 7 ... base(%ebp, %tmp, shift) ... */ tmp = newVRegI(env); roff = iselIntExpr_R(env, off); addInstr(env, mk_iMOVsd_RR(roff, tmp)); if (bias != 0) { addInstr(env, X86Instr_Alu32R(Xalu_ADD, X86RMI_Imm(bias), tmp)); } addInstr(env, X86Instr_Alu32R(Xalu_AND, X86RMI_Imm(7), tmp)); return X86AMode_IRRS( descr->base, hregX86_EBP(), tmp, shift );}/* Mess with the FPU's rounding mode: set to the default rounding mode (DEFAULT_FPUCW). */static void set_FPU_rounding_default ( ISelEnv* env ){ /* pushl $DEFAULT_FPUCW fldcw 0(%esp) addl $4, %esp */ X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP()); addInstr(env, X86Instr_Push(X86RMI_Imm(DEFAULT_FPUCW))); addInstr(env, X86Instr_FpLdCW(zero_esp)); add_to_esp(env, 4);}/* Mess with the FPU's rounding mode: 'mode' is an I32-typed expression denoting a value in the range 0 .. 3, indicating a round mode encoded as per type IRRoundingMode. Set the x87 FPU to have the same rounding.*/staticvoid set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode ){ HReg rrm = iselIntExpr_R(env, mode); HReg rrm2 = newVRegI(env); X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP()); /* movl %rrm, %rrm2 andl $3, %rrm2 -- shouldn't be needed; paranoia shll $10, %rrm2 orl $DEFAULT_FPUCW, %rrm2 pushl %rrm2 fldcw 0(%esp) addl $4, %esp */ addInstr(env, mk_iMOVsd_RR(rrm, rrm2)); addInstr(env, X86Instr_Alu32R(Xalu_AND, X86RMI_Imm(3), rrm2)); addInstr(env, X86Instr_Sh32(Xsh_SHL, 10, rrm2)); addInstr(env, X86Instr_Alu32R(Xalu_OR, X86RMI_Imm(DEFAULT_FPUCW), rrm2)); addInstr(env, X86Instr_Push(X86RMI_Reg(rrm2))); addInstr(env, X86Instr_FpLdCW(zero_esp)); add_to_esp(env, 4);}/* Generate !src into a new vector register, and be sure that the code is SSE1 compatible. Amazing that Intel doesn't offer a less crappy way to do this. */static HReg do_sse_Not128 ( ISelEnv* env, HReg src ){ HReg dst = newVRegV(env); /* Set dst to zero. If dst contains a NaN then all hell might break loose after the comparison. So, first zero it. */ addInstr(env, X86Instr_SseReRg(Xsse_XOR, dst, dst)); /* And now make it all 1s ... */ addInstr(env, X86Instr_Sse32Fx4(Xsse_CMPEQF, dst, dst)); /* Finally, xor 'src' into it. */ addInstr(env, X86Instr_SseReRg(Xsse_XOR, src, dst)); /* Doesn't that just totally suck? */ return dst;}/* Round an x87 FPU value to 53-bit-mantissa precision, to be used after most non-simple FPU operations (simple = +, -, *, / and sqrt). This could be done a lot more efficiently if needed, by loading zero and adding it to the value to be rounded (fldz ; faddp?).*/static void roundToF64 ( ISelEnv* env, HReg reg ){ X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP()); sub_from_esp(env, 8); addInstr(env, X86Instr_FpLdSt(False/*store*/, 8, reg, zero_esp)); addInstr(env, X86Instr_FpLdSt(True/*load*/, 8, reg, zero_esp)); add_to_esp(env, 8);}/*---------------------------------------------------------*//*--- ISEL: Integer expressions (32/16/8 bit) ---*//*---------------------------------------------------------*//* Select insns for an integer-typed expression, and add them to the code list. Return a reg holding the result. This reg will be a virtual register. THE RETURNED REG MUST NOT BE MODIFIED. If you want to modify it, ask for a new vreg, copy it in there, and modify the copy. The register allocator will do its best to map both vregs to the same real register, so the copies will often disappear later in the game. This should handle expressions of 32, 16 and 8-bit type. All results are returned in a 32-bit register. For 16- and 8-bit expressions, the upper 16/24 bits are arbitrary, so you should mask or sign extend partial values if necessary.*/static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e ){ HReg r = iselIntExpr_R_wrk(env, e); /* sanity checks ... */# if 0 vex_printf("\n"); ppIRExpr(e); vex_printf("\n");# endif vassert(hregClass(r) == HRcInt32); vassert(hregIsVirtual(r)); return r;}/* DO NOT CALL THIS DIRECTLY ! */static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e ){ MatchInfo mi; DECLARE_PATTERN(p_32to1_then_1Uto8); IRType ty = typeOfIRExpr(env->type_env,e); vassert(ty == Ity_I32 || ty == Ity_I16 || ty == Ity_I8); switch (e->tag) { /* --------- TEMP --------- */ case Iex_Tmp: { return lookupIRTemp(env, e->Iex.Tmp.tmp); } /* --------- LOAD --------- */ case Iex_Load: { HReg dst = newVRegI(env); X86AMode* amode = iselIntExpr_AMode ( env, e->Iex.Load.addr ); if (e->Iex.Load.end != Iend_LE) goto irreducible; if (ty == Ity_I32) { addInstr(env, X86Instr_Alu32R(Xalu_MOV, X86RMI_Mem(amode), dst) ); return dst; } if (ty == Ity_I16) { addInstr(env, X86Instr_LoadEX(2,False,amode,dst)); return dst; } if (ty == Ity_I8) { addInstr(env, X86Instr_LoadEX(1,False,amode,dst)); return dst; } break; } /* --------- TERNARY OP --------- */ case Iex_Triop: { /* C3210 flags following FPU partial remainder (fprem), both IEEE compliant (PREM1) and non-IEEE compliant (PREM). */ if (e->Iex.Triop.op == Iop_PRemC3210F64 || e->Iex.Triop.op == Iop_PRem1C3210F64) { HReg junk = newVRegF(env); HReg dst = newVRegI(env); HReg srcL = iselDblExpr(env, e->Iex.Triop.arg2); HReg srcR = iselDblExpr(env, e->Iex.Triop.arg3); /* XXXROUNDINGFIXME */ /* set roundingmode here */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -