📄 isel.c
字号:
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 ){ AMD64CondCode cc; HReg argregs[6]; HReg tmpregs[6]; 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 6x64 integer bits in total can be passed. In fact the only supported arg type is I64. 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 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)) vpanic("doHelperCall(AMD64): cannot currently handle > 6 args"); argregs[0] = hregAMD64_RDI(); argregs[1] = hregAMD64_RSI(); argregs[2] = hregAMD64_RDX(); argregs[3] = hregAMD64_RCX(); argregs[4] = hregAMD64_R8(); argregs[5] = hregAMD64_R9(); tmpregs[0] = tmpregs[1] = tmpregs[2] = tmpregs[3] = tmpregs[4] = tmpregs[5] = 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_iMOVsd_RR( hregAMD64_RBP(), argregs[argreg])); argreg++; } for (i = 0; i < n_args; i++) { vassert(argreg < 6); vassert(typeOfIRExpr(env->type_env, args[i]) == Ity_I64); addInstr(env, AMD64Instr_Alu64R( Aalu_MOV, iselIntExpr_RMI(env, args[i]), argregs[argreg] ) ); argreg++; } /* Fast scheme only applies for unconditional calls. Hence: */ cc = Acc_ALWAYS; } else { /* SLOW SCHEME; move via temporaries */ 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++; } 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++; } /* 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 = Acc_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++) { /* 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. */ addInstr(env, AMD64Instr_Call( cc, Ptr_to_ULong(cee->addr), n_args + (passBBP ? 1 : 0) ) );}/* Given a guest-state array descriptor, an index expression and a bias, generate an AMD64AMode holding the relevant guest state offset. */staticAMD64AMode* genGuestArrayOffset ( ISelEnv* env, IRArray* descr, IRExpr* off, Int bias ){ HReg tmp, roff; Int elemSz = sizeofIRType(descr->elemTy); Int nElems = descr->nElems; /* Throw out any cases not generated by an amd64 front end. In theory there might be a day where we need to handle them -- if we ever run non-amd64-guest on amd64 host. */ if (nElems != 8 || (elemSz != 1 && elemSz != 8)) vpanic("genGuestArrayOffset(amd64 host)"); /* Compute off into a reg, %off. Then return: movq %off, %tmp addq $bias, %tmp (if bias != 0) andq %tmp, 7 ... base(%rbp, %tmp, shift) ... */ tmp = newVRegI(env); roff = iselIntExpr_R(env, off); addInstr(env, mk_iMOVsd_RR(roff, tmp)); if (bias != 0) { /* Make sure the bias is sane, in the sense that there are no significant bits above bit 30 in it. */ vassert(-10000 < bias && bias < 10000); addInstr(env, AMD64Instr_Alu64R(Aalu_ADD, AMD64RMI_Imm(bias), tmp)); } addInstr(env, AMD64Instr_Alu64R(Aalu_AND, AMD64RMI_Imm(7), tmp)); vassert(elemSz == 1 || elemSz == 8); return AMD64AMode_IRRS( descr->base, hregAMD64_RBP(), tmp, elemSz==8 ? 3 : 0);}/* Set the SSE unit's rounding mode to default (%mxcsr = 0x1F80) */staticvoid set_SSE_rounding_default ( ISelEnv* env ){ /* pushq $DEFAULT_MXCSR ldmxcsr 0(%rsp) addq $8, %rsp */ AMD64AMode* zero_rsp = AMD64AMode_IR(0, hregAMD64_RSP()); addInstr(env, AMD64Instr_Push(AMD64RMI_Imm(DEFAULT_MXCSR))); addInstr(env, AMD64Instr_LdMXCSR(zero_rsp)); add_to_rsp(env, 8);}/* Mess with the FPU's rounding mode: set to the default rounding mode (DEFAULT_FPUCW). */static void set_FPU_rounding_default ( ISelEnv* env ){ /* movq $DEFAULT_FPUCW, -8(%rsp) fldcw -8(%esp) */ AMD64AMode* m8_rsp = AMD64AMode_IR(-8, hregAMD64_RSP()); addInstr(env, AMD64Instr_Alu64M( Aalu_MOV, AMD64RI_Imm(DEFAULT_FPUCW), m8_rsp)); addInstr(env, AMD64Instr_A87LdCW(m8_rsp));}/* Mess with the SSE unit'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 SSE machinery to have the same rounding.*/staticvoid set_SSE_rounding_mode ( ISelEnv* env, IRExpr* mode ){ /* Note: this sequence only makes sense because DEFAULT_MXCSR has both rounding bits == 0. If that wasn't the case, we couldn't create a new rounding field simply by ORing the new value into place. */ /* movq $3, %reg andq [[mode]], %reg -- shouldn't be needed; paranoia shlq $13, %reg orq $DEFAULT_MXCSR, %reg pushq %reg ldmxcsr 0(%esp) addq $8, %rsp */ HReg reg = newVRegI(env); AMD64AMode* zero_rsp = AMD64AMode_IR(0, hregAMD64_RSP()); addInstr(env, AMD64Instr_Alu64R(Aalu_MOV, AMD64RMI_Imm(3), reg)); addInstr(env, AMD64Instr_Alu64R(Aalu_AND, iselIntExpr_RMI(env, mode), reg)); addInstr(env, AMD64Instr_Sh64(Ash_SHL, 13, reg)); addInstr(env, AMD64Instr_Alu64R( Aalu_OR, AMD64RMI_Imm(DEFAULT_MXCSR), reg)); addInstr(env, AMD64Instr_Push(AMD64RMI_Reg(reg))); addInstr(env, AMD64Instr_LdMXCSR(zero_rsp)); add_to_rsp(env, 8);}/* 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); AMD64AMode* m8_rsp = AMD64AMode_IR(-8, hregAMD64_RSP()); /* movq %rrm, %rrm2 andq $3, %rrm2 -- shouldn't be needed; paranoia shlq $10, %rrm2 orq $DEFAULT_FPUCW, %rrm2 movq %rrm2, -8(%rsp) fldcw -8(%esp) */ addInstr(env, mk_iMOVsd_RR(rrm, rrm2)); addInstr(env, AMD64Instr_Alu64R(Aalu_AND, AMD64RMI_Imm(3), rrm2)); addInstr(env, AMD64Instr_Sh64(Ash_SHL, 10, rrm2)); addInstr(env, AMD64Instr_Alu64R(Aalu_OR, AMD64RMI_Imm(DEFAULT_FPUCW), rrm2)); addInstr(env, AMD64Instr_Alu64M(Aalu_MOV, AMD64RI_Reg(rrm2), m8_rsp)); addInstr(env, AMD64Instr_A87LdCW(m8_rsp));}/* Generate all-zeroes into a new vector register.*/static HReg generate_zeroes_V128 ( ISelEnv* env ){ HReg dst = newVRegV(env); addInstr(env, AMD64Instr_SseReRg(Asse_XOR, dst, dst)); return dst;}/* Generate all-ones into a new vector register.*/static HReg generate_ones_V128 ( ISelEnv* env ){ HReg dst = newVRegV(env); addInstr(env, AMD64Instr_SseReRg(Asse_CMPEQ32, dst, dst)); return dst;}/* Generate !src into a new vector register. Amazing that there isn't a less crappy way to do this.*/static HReg do_sse_NotV128 ( ISelEnv* env, HReg src ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -