//Save Return address void MipsCode::SaveContext() { addInstr(MIPSType::sw, $fp, "0", $sp); addInstr(MIPSType::move, $fp, $sp); addInstr(MIPSType::addi, $sp, $sp, "4"); addInstr(MIPSType::sw, $ra, "0", $sp); addInstr(MIPSType::addi, $sp, $sp, "4"); }
//Format: ARRASS dst,src1,src2 // src1[src2] = dst void MipsCode::ArrAssToMips(MiddleInstr* middle) { SignaryItem* target = middle->desAddr; SignaryItem* src1 = middle->srcAddr1; SignaryItem* src2 = middle->srcAddr2; VarToReg($t0, src2); addInstr(MIPSType::li, $t1,"4"); addInstr(MIPSType::mult, $t0, $t0, $t1); VarToReg($t2, src1); addInstr(MIPSType::add, $t0, $t0, $t2); VarToReg($t1, target); addInstr(MIPSType::sw, $t1, "0", $t0); }
void MipsCode::VarToReg(string _$i, SignaryItem* _var) { SignaryItem* tmp = cur_sig->querySignary(_var->name); if (tmp == NULL) { //sw $t0,0($t1) //sw $t0,4($fp) addInstr(MIPSType::lw, _$i, tostring(_var->offset), $global_fp); } else { addInstr(MIPSType::lw, _$i, tostring(_var->offset), $fp); } }
void createAndAddBlockWithLabel( std::vector<std::string> directives, std::string label = "") { auto block = createBlockWithLabel(label); for (auto directive: directives) block.addInstr(directive); addBlock(block); }
static void sub_from_sp ( ISelEnv* env, Int n ) { HReg tmp; ARMImm12A imm12a; vassert(n > 0 && n < 256 && (n%4) == 0); if ( mk_ARMImm12A( (UInt)n, &imm12a ) ) { addInstr(env, ARMInstr_DPInstr2(ARMalu_SUB, GET_SP_REG(), GET_SP_REG(), ARMAMode1_I12A( imm12a ))); } else { tmp = newVRegI(env); addInstr(env, ARMInstr_Literal( tmp, (UInt)n )); addInstr(env, ARMInstr_DPInstr2(ARMalu_SUB, GET_SP_REG(), GET_SP_REG(), ARMAMode1_ShlI( tmp, 0 ))); } }
//Format: BXX dst,src1,src2 // if src1 xx src2 goto dst // src1, src2 : variable // dst : label void MipsCode::BranchToMips(MiddleInstr* middle) { SignaryItem* target = middle->desAddr; SignaryItem* src1 = middle->srcAddr1; SignaryItem* src2 = middle->srcAddr2; VarToReg($t0, src1); VarToReg($t1, src2); if (middle->type == MIType::BEQ) { addInstr(MIPSType::beq, target->name, $t0, $t1); } else if (middle->type == MIType::BNE) { addInstr(MIPSType::bne, target->name, $t0, $t1); } else if (middle->type == MIType::BLE) { addInstr(MIPSType::ble, target->name, $t0, $t1); } else if (middle->type == MIType::BLS) { addInstr(MIPSType::blt, target->name, $t0, $t1); } else if (middle->type == MIType::BGE) { addInstr(MIPSType::bge, target->name, $t0, $t1); } else if (middle->type == MIType::BGR) { addInstr(MIPSType::bgt, target->name, $t0, $t1); } }
static Int pushArg ( ISelEnv* env, IRExpr* arg ) { IRType arg_ty = typeOfIRExpr(env->type_env, arg); if (arg_ty == Ity_I32) { // CAB: This right? addInstr(env, ARMInstr_StoreW( GET_SP_REG(), iselIntExpr_AMode2(env, arg) ) ); return 1; } #if 0 else if (arg_ty == Ity_I64) { HReg rHi, rLo; iselInt64Expr(&rHi, &rLo, env, arg); addInstr(env, X86Instr_Push(X86RMI_Reg(rHi))); addInstr(env, X86Instr_Push(X86RMI_Reg(rLo))); return 2; } #endif ppIRExpr(arg); vpanic("pushArg(arm): can't handle arg of this type"); }
static void iselNext ( ISelEnv* env, IRExpr* next, IRJumpKind jk ) { ARMBranchDest* bd; if (vex_traceflags & VEX_TRACE_VCODE) { vex_printf("\n-- goto {"); ppIRJumpKind(jk); vex_printf("} "); ppIRExpr(next); vex_printf("\n"); } bd = iselIntExpr_BD(env, next); // CAB: jk ? addInstr( env, ARMInstr_Branch(ARMccAL, bd) ); }
static ARMAMode2* genGuestArrayOffset ( ISelEnv* env, IRRegArray* descr, IRExpr* off, Int bias ) { HReg tmp, tmp2, roff; Int elemSz = sizeofIRType(descr->elemTy); Int nElems = descr->nElems; ARMImm12A imm12a; /* 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 || (elemSz != 1 && elemSz != 8)) vpanic("genGuestArrayOffset(arm host)"); /* 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) { if ( mk_ARMImm12A( (UInt)bias, &imm12a ) ) { addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp, ARMAMode1_I12A( imm12a ))); } else { HReg tmp3 = newVRegI(env); addInstr(env, ARMInstr_Literal( tmp, (UInt)bias )); addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp, ARMAMode1_ShlI( tmp3, 0 ))); } } mk_ARMImm12A( (UInt)7, &imm12a ); addInstr(env, ARMInstr_DPInstr2(ARMalu_AND, tmp, tmp, ARMAMode1_I12A( imm12a ))); vassert(elemSz == 1 || elemSz == 8); // CAB: This anywhere near correct? // X86AMode_IRRS: Immediate + Reg1 + (Reg2 << Shift) // return X86AMode_IRRS( descr->base, hregX86_EBP(), tmp, elemSz==8 ? 3 : 0); tmp2 = newVRegI(env); // tmp2 = GET_BP_REG + (tmp << 3|0) addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp2, GET_BP_REG(), ARMAMode1_ShlI(tmp, elemSz==8 ? 3 : 0))); return ARMAMode2_RI( tmp2, descr->base ); }
static void callHelperAndClearArgs ( ISelEnv* env, ARMCondCode cc, IRCallee* cee, Int n_arg_ws ) { /* Complication. Need to decide which reg to use as the fn address pointer, in a way that doesn't trash regparm-passed parameters. */ vassert(sizeof(void*) == 4); // CAB: cee->regparms ? // addInstr(env, X86Instr_Call( cc, (UInt)cee->addr, cee->regparms)); ARMBranchDest* dst = ARMBranchDest_Imm( (UInt)cee->addr ); addInstr(env, ARMInstr_BranchL(cc, dst)); if (n_arg_ws > 0) add_to_sp(env, 4*n_arg_ws); }
static void iselStmt ( ISelEnv* env, IRStmt* stmt ) { if (vex_traceflags & VEX_TRACE_VCODE) { vex_printf("\n-- "); ppIRStmt(stmt); vex_printf("\n"); } switch (stmt->tag) { /* --------- STORE --------- */ /* little-endian write to memory */ case Ist_Store: { HReg reg; IRType tya = typeOfIRExpr(env->type_env, stmt->Ist.Store.addr); IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Store.data); IREndness end = stmt->Ist.Store.end; if (tya != Ity_I32 || end != Iend_LE) goto stmt_fail; reg = iselIntExpr_R(env, stmt->Ist.Store.data); if (tyd == Ity_I8) { ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.Store.addr); addInstr(env, ARMInstr_StoreB(reg,am2)); return; } if (tyd == Ity_I16) { ARMAMode3* am3 = iselIntExpr_AMode3(env, stmt->Ist.Store.addr); addInstr(env, ARMInstr_StoreH(reg,am3)); return; } if (tyd == Ity_I32) { ARMAMode2* am2 = iselIntExpr_AMode2(env, stmt->Ist.Store.addr); addInstr(env, ARMInstr_StoreW(reg,am2)); return; } } /* --------- PUT --------- */ /* write guest state, fixed offset */ case Ist_Put: { IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Put.data); HReg reg = iselIntExpr_R(env, stmt->Ist.Put.data); // CAB: This anywhere near right?! if (tyd == Ity_I32) { ARMAMode2* am2 = ARMAMode2_RI(GET_BP_REG(), stmt->Ist.Put.offset); addInstr(env, ARMInstr_StoreW(reg, am2)); return; } if (tyd == Ity_I16) { ARMAMode3* am3 = ARMAMode3_RI(GET_BP_REG(), stmt->Ist.Put.offset); addInstr(env, ARMInstr_StoreH(reg, am3)); return; } if (tyd == Ity_I8) { ARMAMode2* am2 = ARMAMode2_RI(GET_BP_REG(), stmt->Ist.Put.offset); addInstr(env, ARMInstr_StoreB(reg, am2)); return; } // CAB: Ity_I32, Ity_I16 ? break; } /* --------- Indexed PUT --------- */ /* write guest state, run-time offset */ case Ist_PutI: { ARMAMode2* am2 = genGuestArrayOffset( env, stmt->Ist.PutI.descr, stmt->Ist.PutI.ix, stmt->Ist.PutI.bias ); IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.PutI.data); if (tyd == Ity_I8) { HReg reg = iselIntExpr_R(env, stmt->Ist.PutI.data); addInstr(env, ARMInstr_StoreB(reg, am2)); return; } // CAB: Ity_I32, Ity_I16 ? break; } /* --------- TMP --------- */ /* assign value to temporary */ case Ist_WrTmp: { IRTemp tmp = stmt->Ist.WrTmp.tmp; IRType ty = typeOfIRTemp(env->type_env, tmp); if (ty == Ity_I32 || ty == Ity_I16 || ty == Ity_I8) { ARMAMode1* am = iselIntExpr_AMode1(env, stmt->Ist.WrTmp.data); HReg dst = lookupIRTemp(env, tmp); addInstr(env, ARMInstr_DPInstr1(ARMalu_MOV,dst,am)); return; } // CAB: Ity_I1 ? break; } /* --------- Call to DIRTY helper --------- */ /* call complex ("dirty") helper function */ case Ist_Dirty: { //IRType retty; IRDirty* d = stmt->Ist.Dirty.details; Bool passBBP = False; if (d->nFxState == 0) vassert(!d->needsBBP); passBBP = toBool(d->nFxState > 0 && d->needsBBP); /* Marshal args, do the call, clear stack. */ doHelperCall( env, passBBP, d->guard, d->cee, d->args ); /* Now figure out what to do with the returned value, if any. */ if (d->tmp == IRTemp_INVALID) /* No return value. Nothing to do. */ return; //retty = typeOfIRTemp(env->type_env, d->tmp); // CAB: ? if (retty == Ity_I64) { #if 0 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; } #endif break; } /* --------- EXIT --------- */ /* conditional exit from BB */ case Ist_Exit: { ARMBranchDest* dst; ARMCondCode cc; if (stmt->Ist.Exit.dst->tag != Ico_U32) vpanic("isel_arm: Ist_Exit: dst is not a 32-bit value"); // CAB: Where does jumpkind fit in ? // stmt->Ist.Exit.jk dst = iselIntExpr_BD(env, IRExpr_Const(stmt->Ist.Exit.dst)); cc = iselCondCode(env,stmt->Ist.Exit.guard); addInstr(env, ARMInstr_Branch(cc, dst)); return; } default: break; } stmt_fail: ppIRStmt(stmt); vpanic("iselStmt"); }
static void doHelperCall ( ISelEnv* env, Bool passBBP, IRExpr* guard, IRCallee* cee, IRExpr** args ) { #if 0 ARMCondCode cc; HReg argregs[3]; HReg tmpregs[3]; Bool danger; Int not_done_yet, n_args, n_arg_ws, stack_limit, i, argreg, argregX; /* 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 ); #endif }
//Restore Return address void MipsCode::RestoreContext() { addInstr(MIPSType::lw, $ra, "4", $fp); addInstr(MIPSType::move, $sp, $fp); addInstr(MIPSType::lw, $fp, "0", $fp); }
//Format: JUMP label void MipsCode::JumpToMips(MiddleInstr* middle) { addInstr(MIPSType::j, middle->desAddr->name); }
//Format: SETL label void MipsCode::SetLabelToMips(MiddleInstr* middle) { addInstr(MIPSType::label, middle->desAddr->name); }