📄 isel.c
字号:
/* Given an amode, return one which references 4 bytes further along. */static PPC32AMode* advance4 ( ISelEnv* env, PPC32AMode* am ){ PPC32AMode* am4 = dopyPPC32AMode(am); if (am4->tag == Pam_IR && am4->Pam.IR.index + 4 <= 32767) { am4->Pam.IR.index += 4; } else { vpanic("advance4(ppc32,host)"); } return am4;}/* Used only in doHelperCall. See big comment in doHelperCall re handling of register-parameter args. This function figures out whether evaluation of an expression might require use of a fixed register. If in doubt return True (safe but suboptimal).*/staticBool mightRequireFixedRegs ( IRExpr* e ){ switch (e->tag) { case Iex_Tmp: case Iex_Const: case Iex_Get: return False; default: return True; }}/* 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. */staticvoid doHelperCall ( ISelEnv* env, Bool passBBP, IRExpr* guard, IRCallee* cee, IRExpr** args ){ PPC32CondCode cc; HReg argregs[PPC32_N_REGPARMS]; HReg tmpregs[PPC32_N_REGPARMS]; Bool go_fast; Int n_args, i, argreg; /* 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 PPC32_N_REGPARMSx32 integer bits in total can be passed. In fact the only supported arg type is I32. 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 doHelperCall() in priv/host-x86/isel.c. Here, we use a variant of the method described in those comments. The problem is split into two cases: the fast scheme and the slow scheme. In the fast scheme, arguments are computed directly into the target (real) registers. This is only safe when we can be sure that computation of each argument will not trash any real registers set by computation of any other argument. In the slow scheme, all args are first computed into vregs, and once they are all done, they are moved 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. To decide which scheme to use, all argument expressions are first examined. If they are all so simple that it is clear they will be evaluated without use of any fixed registers, use the 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. 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. */ /* Note that the cee->regparms field is meaningless on PPC32 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 (PPC32_N_REGPARMS < n_args + (passBBP ? 1 : 0)) { vpanic("doHelperCall(PPC32): cannot currently handle > 8 args"); // PPC32_N_REGPARMS } argregs[0] = hregPPC32_GPR3(); argregs[1] = hregPPC32_GPR4(); argregs[2] = hregPPC32_GPR5(); argregs[3] = hregPPC32_GPR6(); argregs[4] = hregPPC32_GPR7(); argregs[5] = hregPPC32_GPR8(); argregs[6] = hregPPC32_GPR9(); argregs[7] = hregPPC32_GPR10(); tmpregs[0] = tmpregs[1] = tmpregs[2] = tmpregs[3] = tmpregs[4] = tmpregs[5] = tmpregs[6] = tmpregs[7] = 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) { if (guard->tag == Iex_Const && guard->Iex.Const.con->tag == Ico_U1 && guard->Iex.Const.con->Ico.U1 == True) { /* unconditional */ } else { /* Not manifestly unconditional -- be conservative. */ go_fast = False; } } if (go_fast) { for (i = 0; i < n_args; i++) { if (mightRequireFixedRegs(args[i])) { go_fast = False; break; } } } /* At this point the scheme to use has been established. Generate code to get the arg values into the argument rregs. */ if (go_fast) { /* FAST SCHEME */ argreg = 0; if (passBBP) { addInstr(env, mk_iMOVds_RR( argregs[argreg], GuestStatePtr )); argreg++; } for (i = 0; i < n_args; i++) { vassert(argreg < PPC32_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) { addInstr(env, mk_iMOVds_RR( argregs[argreg], iselIntExpr_R(env, args[i]) )); } else { // Ity_I64 HReg rHi, rLo; if (argreg%2 == 1) // ppc32 abi spec for passing a LONG_LONG argreg++; // XXX: odd argreg => even rN vassert(argreg < PPC32_N_REGPARMS-1); iselInt64Expr(&rHi,&rLo, env, args[i]); addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi )); addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo)); } argreg++; } /* Fast scheme only applies for unconditional calls. Hence: */ cc.test = Pct_ALWAYS; } 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], GuestStatePtr )); argreg++; } for (i = 0; i < n_args; i++) { vassert(argreg < PPC32_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) { tmpregs[argreg] = iselIntExpr_R(env, args[i]); } else { // Ity_I64 HReg rHi, rLo; if (argreg%2 == 1) // ppc32 abi spec for passing a LONG_LONG argreg++; // XXX: odd argreg => even rN vassert(argreg < PPC32_N_REGPARMS-1); iselInt64Expr(&rHi,&rLo, env, args[i]); tmpregs[argreg++] = rHi; tmpregs[argreg] = rLo; } argreg++; } /* 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.test = Pct_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 ); } } /* Move the args to their final destinations. */ for (i = 0; i < argreg; i++) { if (tmpregs[i] == INVALID_HREG) // Skip invalid regs continue; /* None of these insns, including any spill code that might be generated, may alter the condition codes. */ addInstr( env, mk_iMOVds_RR( argregs[i], tmpregs[i] ) ); } } /* Finally, the call itself. */ addInstr(env, PPC32Instr_Call( cc, (Addr32)toUInt(Ptr_to_ULong(cee->addr)), n_args + (passBBP ? 1 : 0) ));}/* Set FPU's rounding mode to the default */static void set_FPU_rounding_default ( ISelEnv* env ){ HReg fr_src = newVRegF(env); HReg r_srcHi = newVRegI(env); HReg r_srcLo = newVRegI(env); /* Default rounding mode = 0x0 Only supporting the rounding-mode bits - the rest of FPSCR is 0x0 - so we can set the whole register at once (faster) */ addInstr(env, PPC32Instr_LI32(r_srcLo, 0x0)); // r_srcHi = 0: upper 32 bits ignored by FpLdFPSCR addInstr(env, PPC32Instr_LI32(r_srcHi, 0x0)); fr_src = mk_LoadRRtoFPR( env, r_srcHi, r_srcLo ); addInstr(env, PPC32Instr_FpLdFPSCR( fr_src ));}/* Convert IR rounding mode to PPC32 encoding */static HReg roundModeIRtoPPC32 ( ISelEnv* env, HReg r_rmIR ){/* rounding mode | PPC | IR ------------------------ to nearest | 00 | 00 to zero | 01 | 11 to +infinity | 10 | 10 to -infinity | 11 | 01*/ HReg r_rmPPC32 = newVRegI(env); HReg r_tmp = newVRegI(env); // AND r_rmRI,3 -- shouldn't be needed; paranoia addInstr(env, PPC32Instr_Alu32(Palu_AND, r_rmIR, r_rmIR, PPC32RH_Imm(False,3))); // r_rmPPC32 = XOR( r_rmIR, (r_rmIR << 1) & 2) addInstr(env, PPC32Instr_Alu32(Palu_SHL, r_tmp, r_rmIR, PPC32RH_Imm(False,1))); addInstr(env, PPC32Instr_Alu32(Palu_AND, r_tmp, r_tmp, PPC32RH_Imm(False,2))); addInstr(env, PPC32Instr_Alu32(Palu_XOR, r_rmPPC32, r_rmIR, PPC32RH_Reg(r_tmp))); return r_rmPPC32;}/* 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 PPC32 FPSCR to have the same rounding. For speed & simplicity, we're setting the *entire* FPSCR here.*/staticvoid set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode ){ HReg fr_src = newVRegF(env); HReg r_srcHi = newVRegI(env); /* Only supporting the rounding-mode bits - the rest of FPSCR is 0x0 - so we can set the whole register at once (faster) */ // Resolve rounding mode and convert to PPC32 representation HReg r_srcLo = roundModeIRtoPPC32( env, iselIntExpr_R(env, mode) ); // srcHi = 0: upper 32 bits ignored by FpLdFPSCR addInstr(env, PPC32Instr_LI32(r_srcHi, 0)); // Load 2*I32 regs to fp reg: fr_src = mk_LoadRRtoFPR( env, r_srcHi, r_srcLo ); // Move to FPSCR addInstr(env, PPC32Instr_FpLdFPSCR( fr_src ));}//.. /* 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. Not strictly necessary, but the idea of doing//.. a FP comparison on whatever junk happens to be floating around//.. in it is just too scary. *///.. 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));//.. 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -