void LibVEX_ShowAllocStats ( void ) { vex_printf("vex storage: T total %lld bytes allocated\n", (Long)temporary_bytes_allocd_TOT ); vex_printf("vex storage: P total %lld bytes allocated\n", (Long)(permanent_curr - permanent_first) ); }
void ppHRegClass ( HRegClass hrc ) { switch (hrc) { case HRcInt32: vex_printf("HRcInt32"); break; case HRcInt64: vex_printf("HRcInt64"); break; case HRcFlt64: vex_printf("HRcFlt64"); break; case HRcVec64: vex_printf("HRcVec64"); break; case HRcVec128: vex_printf("HRcVec128"); break; default: vpanic("ppHRegClass"); } }
void ppHRegRemap ( HRegRemap* map ) { Int i; vex_printf("HRegRemap {\n"); for (i = 0; i < map->n_used; i++) { vex_printf(" "); ppHReg(map->orig[i]); vex_printf(" --> "); ppHReg(map->replacement[i]); vex_printf("\n"); } vex_printf("}\n"); }
/* Generic printing for registers. */ void ppHReg ( HReg r ) { HChar* maybe_v = hregIsVirtual(r) ? "v" : ""; Int regNo = hregNumber(r); switch (hregClass(r)) { case HRcInt32: vex_printf("%%%sr%d", maybe_v, regNo); return; case HRcInt64: vex_printf("%%%sR%d", maybe_v, regNo); return; case HRcFlt64: vex_printf("%%%sF%d", maybe_v, regNo); return; case HRcVec64: vex_printf("%%%sv%d", maybe_v, regNo); return; case HRcVec128: vex_printf("%%%sV%d", maybe_v, regNo); return; default: vpanic("ppHReg"); } }
void private_LibVEX_alloc_OOM(void) { const char* pool = "???"; if (private_LibVEX_alloc_first == &temporary[0]) pool = "TEMP"; if (private_LibVEX_alloc_first == &permanent[0]) pool = "PERM"; vex_printf("VEX temporary storage exhausted.\n"); vex_printf("Pool = %s, start %p curr %p end %p (size %lld)\n", pool, private_LibVEX_alloc_first, private_LibVEX_alloc_curr, private_LibVEX_alloc_last, (Long)(private_LibVEX_alloc_last + 1 - private_LibVEX_alloc_first)); vpanic("VEX temporary storage exhausted.\n" "Increase N_{TEMPORARY,PERMANENT}_BYTES and recompile."); }
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) ); }
void vex_assert_fail ( const HChar* expr, const HChar* file, Int line, const HChar* fn ) { vex_printf( "\nvex: %s:%d (%s): Assertion `%s' failed.\n", file, line, fn, expr ); (*vex_failure_exit)(); }
/*static*/ UInt armg_calculate_condition ( UInt/*ARMCondcode*/ cond, UInt cc_op, UInt cc_dep1, UInt cc_dep2 ) { UInt nf,zf,vf,cf; UInt inv = cond & 1; UInt nzvc = armg_calculate_flags_all(cc_op, cc_dep1, cc_dep2); switch (cond) { case ARMCondEQ: // Z=1 => z case ARMCondNE: // Z=0 zf = nzvc >> ARMG_CC_SHIFT_Z; return 1 & (inv ^ zf); case ARMCondHS: // C=1 => c case ARMCondLO: // C=0 cf = nzvc >> ARMG_CC_SHIFT_C; return 1 & (inv ^ cf); case ARMCondMI: // N=1 => n case ARMCondPL: // N=0 nf = nzvc >> ARMG_CC_SHIFT_N; return 1 & (inv ^ nf); case ARMCondVS: // V=1 => v case ARMCondVC: // V=0 vf = nzvc >> ARMG_CC_SHIFT_V; return 1 & (inv ^ vf); case ARMCondHI: // C=1 && Z=0 => c & ~z case ARMCondLS: // C=0 || Z=1 cf = nzvc >> ARMG_CC_SHIFT_C; zf = nzvc >> ARMG_CC_SHIFT_Z; return 1 & (inv ^ (cf & ~zf)); case ARMCondGE: // N=V => ~(n^v) case ARMCondLT: // N!=V nf = nzvc >> ARMG_CC_SHIFT_N; vf = nzvc >> ARMG_CC_SHIFT_V; return 1 & (inv ^ ~(nf ^ vf)); case ARMCondGT: // Z=0 && N=V => (~z & ~(n^v) => ~(z | (n^v) case ARMCondLE: // Z=1 || N!=V nf = nzvc >> ARMG_CC_SHIFT_N; vf = nzvc >> ARMG_CC_SHIFT_V; zf = nzvc >> ARMG_CC_SHIFT_Z; return 1 & (inv ^ ~(zf | (nf ^ vf))); case ARMCondAL: // should never get here: Always => no flags to calc case ARMCondNV: // should never get here: Illegal instr default: /* shouldn't really make these calls from generated code */ vex_printf("armg_calculate_condition(ARM)( %u, %u, 0x%x, 0x%x )\n", cond, cc_op, cc_dep1, cc_dep2 ); vpanic("armg_calculate_condition(ARM)"); } }
static void addInstr ( ISelEnv* env, ARMInstr* instr ) { addHInstr(env->code, instr); if (vex_traceflags & VEX_TRACE_VCODE) { ppARMInstr(instr); vex_printf("\n"); } }
void ppHRegUsage ( HRegUsage* tab ) { Int i; HChar* str; vex_printf("HRegUsage {\n"); for (i = 0; i < tab->n_used; i++) { switch (tab->mode[i]) { case HRmRead: str = "Read "; break; case HRmWrite: str = "Write "; break; case HRmModify: str = "Modify "; break; default: vpanic("ppHRegUsage"); } vex_printf(" %s ", str); ppHReg(tab->hreg[i]); vex_printf("\n"); } vex_printf("}\n"); }
void ppHRegUsage ( const RRegUniverse* univ, HRegUsage* tab ) { /* This is going to fail miserably if N_RREGUNIVERSE_REGS exceeds 64. So let's cause it to fail in an obvious way. */ vassert(N_RREGUNIVERSE_REGS == 64); vex_printf("HRegUsage {\n"); /* First print the real regs */ for (UInt i = 0; i < N_RREGUNIVERSE_REGS; i++) { Bool rRd = (tab->rRead & (1ULL << i)) != 0; Bool rWr = (tab->rWritten & (1ULL << i)) != 0; const HChar* str = "Modify "; /**/ if (!rRd && !rWr) { continue; } else if ( rRd && !rWr) { str = "Read "; } else if (!rRd && rWr) { str = "Write "; } /* else "Modify" is correct */ vex_printf(" %s ", str); ppHReg(univ->regs[i]); vex_printf("\n"); } /* and now the virtual registers */ for (UInt i = 0; i < tab->n_vRegs; i++) { const HChar* str = NULL; switch (tab->vMode[i]) { case HRmRead: str = "Read "; break; case HRmWrite: str = "Write "; break; case HRmModify: str = "Modify "; break; default: vpanic("ppHRegUsage"); } vex_printf(" %s ", str); ppHReg(tab->vRegs[i]); vex_printf("\n"); } vex_printf("}\n"); }
void vexAudioDebugChipTone() { int i; vsl_ct *p; if( VSL_ctarray != NULL ) { for(i=0;i<ctSize;i++) { p = &(VSL_ctarray[ i ]); #ifdef VSL_AMP_PER_TONE vex_printf("%d %d %d\r\n", p->freq, p->amplitude, p->timems ); #else vex_printf("%d %d %d\r\n", p->freq, ctAmplitude, p->timems ); #endif } } return; }
/* Double the size of the real-reg live-range array, if needed. */ static void ensureRRLRspace ( RRegLR** info, Int* size, Int used ) { Int k; RRegLR* arr2; if (used < *size) return; if (0) vex_printf("ensureRRISpace: %d -> %d\n", *size, 2 * *size); vassert(used == *size); arr2 = LibVEX_Alloc(2 * *size * sizeof(RRegLR)); for (k = 0; k < *size; k++) arr2[k] = (*info)[k]; *size *= 2; *info = arr2; }
void ppARMAMode1 ( ARMAMode1* am ) { switch (am->tag) { case ARMam1_I12A: case ARMam1_ShlI: case ARMam1_ShrI: case ARMam1_SarI: case ARMam1_ShlR: case ARMam1_ShrR: case ARMam1_SarR: vex_printf("ppARMAMode1: Not implemented"); break; default: vpanic("ppARMAMode1"); } }
/* Calculate all the 4 flags from the supplied thunk parameters. */ UInt armg_calculate_flags_all ( UInt cc_op, UInt cc_dep1_formal, UInt cc_dep2_formal ) { switch (cc_op) { case ARMG_CC_OP_LOGIC: ACTIONS_LOGIC(); case ARMG_CC_OP_ADD: ACTIONS_ADD(); case ARMG_CC_OP_SUB: ACTIONS_SUB(); default: /* shouldn't really make these calls from generated code */ vex_printf("armg_calculate_flags_all(ARM)( %u, 0x%x, 0x%x )\n", cc_op, cc_dep1_formal, cc_dep2_formal ); vpanic("armg_calculate_flags_all(ARM)"); } }
void ppHRegARM ( HReg reg ) { Int r; static HChar* ireg32_names[16] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; /* Be generic for all virtual regs. */ if (hregIsVirtual(reg)) { ppHReg(reg); return; } /* But specific for real regs. */ switch (hregClass(reg)) { case HRcInt32: r = hregNumber(reg); vassert(r >= 0 && r < 16); vex_printf("%s", ireg32_names[r]); return; default: vpanic("ppHRegARM"); } }
/* Generic printing for registers. */ void ppHReg ( HReg r ) { if (hregIsInvalid(r)) { vex_printf("HReg_INVALID"); return; } const Bool isV = hregIsVirtual(r); const HChar* maybe_v = isV ? "v" : ""; const UInt regNN = isV ? hregIndex(r) : hregEncoding(r); /* For real registers, we show the encoding. But the encoding is always zero for virtual registers, so that's pointless -- hence show the index number instead. */ switch (hregClass(r)) { case HRcInt32: vex_printf("%%%sr%u", maybe_v, regNN); return; case HRcInt64: vex_printf("%%%sR%u", maybe_v, regNN); return; case HRcFlt32: vex_printf("%%%sF%u", maybe_v, regNN); return; case HRcFlt64: vex_printf("%%%sD%u", maybe_v, regNN); return; case HRcVec64: vex_printf("%%%sv%u", maybe_v, regNN); return; case HRcVec128: vex_printf("%%%sV%u", maybe_v, regNN); return; default: vpanic("ppHReg"); } }
void ppRetLoc ( RetLoc ska ) { switch (ska.pri) { case RLPri_INVALID: vex_printf("RLPri_INVALID"); return; case RLPri_None: vex_printf("RLPri_None"); return; case RLPri_Int: vex_printf("RLPri_Int"); return; case RLPri_2Int: vex_printf("RLPri_2Int"); return; case RLPri_V128SpRel: vex_printf("RLPri_V128SpRel(%d)", ska.spOff); return; case RLPri_V256SpRel: vex_printf("RLPri_V256SpRel(%d)", ska.spOff); return; default: vpanic("ppRetLoc"); } }
/* Inject IR stmts depending on the data provided in the control block iricb. */ void vex_inject_ir(IRSB *irsb, IREndness endian) { IRExpr *data, *rounding_mode, *opnd1, *opnd2, *opnd3, *opnd4; rounding_mode = NULL; if (iricb.rounding_mode != NO_ROUNDING_MODE) { rounding_mode = mkU32(iricb.rounding_mode); } switch (iricb.num_operands) { case 1: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); if (rounding_mode) data = binop(iricb.op, rounding_mode, opnd1); else data = unop(iricb.op, opnd1); break; case 2: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); if (iricb.shift_amount_is_immediate) { // This implies that the IROp is a shift op vassert(iricb.t_opnd2 == Ity_I8); opnd2 = mkU8(*((Char *)iricb.opnd2)); } else { opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); } if (rounding_mode) data = triop(iricb.op, rounding_mode, opnd1, opnd2); else data = binop(iricb.op, opnd1, opnd2); break; case 3: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); if (rounding_mode) data = qop(iricb.op, rounding_mode, opnd1, opnd2, opnd3); else data = triop(iricb.op, opnd1, opnd2, opnd3); break; case 4: vassert(rounding_mode == NULL); opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); opnd4 = load(endian, iricb.t_opnd4, iricb.opnd4); data = qop(iricb.op, opnd1, opnd2, opnd3, opnd4); break; default: vpanic("unsupported operator"); } store(irsb, endian, iricb.result, data); if (0) { vex_printf("BEGIN inject\n"); if (iricb.t_result == Ity_I1 || sizeofIRType(iricb.t_result) <= 8) { ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); } else if (sizeofIRType(iricb.t_result) == 16) { ppIRStmt(irsb->stmts[irsb->stmts_used - 2]); vex_printf("\n"); ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); } vex_printf("\nEND inject\n"); } }
void traceIRStmt (IRSB * sb, IRStmt* st , ULong caMainBBCounter) { // unsigned long n_old_typestates_state_instance = 0; IRExpr * rhs; static Addr current_addr = 0; counter = caMainBBCounter; if (!st) { vex_printf("IRStmt* which is NULL !!!"); return; } switch (st->tag) { case Ist_NoOp: break; /* Nothing to emit. */ case Ist_IMark: current_addr = st->Ist.IMark.addr; // VG_(message)(Vg_UserMsg, "addr is %lx \n", current_addr); // VG_(message)(Vg_UserMsg, "(void*)current_addr is %p\n ", (void*)current_addr); // VG_(message)(Vg_UserMsg, "&malloc is %p\n ", &malloc); /* if((void*)current_addr == VG_(fnptr_to_fnentry)( &SC_malloc ) || (void*)current_addr == VG_(fnptr_to_fnentry)( &SC__builtin_new ) || (void*)current_addr == VG_(fnptr_to_fnentry)( &SC__builtin_vec_new ) ) { argNum = 1; } if((void*)current_addr == VG_(fnptr_to_fnentry)( &SC_calloc ) || (void*)current_addr == VG_(fnptr_to_fnentry)( &SC_memalign ) ) { argNum = 2; } if((void*)current_addr == VG_(fnptr_to_fnentry)( &SC_realloc ) ) { isRealloc = 1; argNum = 1; } */ if(current_addr == 0x4c2b20d ) { argNum = 1; } if(current_addr == 0x4c294e0 ) { argNum = 2; } if(current_addr == 0x4c2b3b8 ) { isRealloc = 1; argNum = 1; } break; case Ist_AbiHint: break; /* Nothing to emit. */ case Ist_Put: /* TODO: update map of names -> typestate vars */ /* PUT(30) = t32 --> VarOf(reg.30) := VarOf(eipX.t32) */ /* PUT(30) = const --> VarOf(reg.30) := NONE */ rhs = st->Ist.Put.data; AddPutHelper(sb, st->Ist.Put.offset,rhs); break; case Ist_PutI: /* TODO: update map of names -> typestate vars */ break; case Ist_WrTmp: // rhs = st->Ist.Tmp.data; traceIRExpr(sb,st,current_addr); break; case Ist_Store: /* TODO: update map of names -> typestate vars */ AddStoreHelper(sb,st->Ist.Store.addr,st->Ist.Store.data); break; case Ist_Dirty: /* TODO: handle common dirty helpers here */ break; case Ist_MBE: break; /* Nothing to emit. */ case Ist_Exit: /* No need to emit constraints here; catch earlier at CMP or */ /* at x86g_calc_condition. Do nothing. */ break; default: return; } }
IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge, /*IN*/ void* callback_opaque, /*IN*/ DisOneInstrFn dis_instr_fn, /*IN*/ UChar* guest_code, /*IN*/ Addr64 guest_IP_bbstart, /*IN*/ Bool (*chase_into_ok)(void*,Addr64), /*IN*/ Bool host_bigendian, /*IN*/ VexArch arch_guest, /*IN*/ VexArchInfo* archinfo_guest, /*IN*/ IRType guest_word_type, /*IN*/ Bool do_self_check, /*IN*/ Bool (*preamble_function)(void*,IRBB*), /*IN*/ Int offB_TISTART, /*IN*/ Int offB_TILEN ) { Long delta; Int i, n_instrs, first_stmt_idx; Bool resteerOK, need_to_put_IP, debug_print; DisResult dres; IRStmt* imark; static Int n_resteers = 0; Int d_resteers = 0; Int selfcheck_idx = 0; IRBB* irbb; Addr64 guest_IP_curr_instr; IRConst* guest_IP_bbstart_IRConst = NULL; Bool (*resteerOKfn)(void*,Addr64) = NULL; debug_print = toBool(vex_traceflags & VEX_TRACE_FE); /* Note: for adler32 to work without % operation for the self check, need to limit length of stuff it scans to 5552 bytes. Therefore limiting the max bb len to 100 insns seems generously conservative. */ /* check sanity .. */ vassert(sizeof(HWord) == sizeof(void*)); vassert(vex_control.guest_max_insns >= 1); vassert(vex_control.guest_max_insns < 100); vassert(vex_control.guest_chase_thresh >= 0); vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns); vassert(guest_word_type == Ity_I32 || guest_word_type == Ity_I64); /* Start a new, empty extent. */ vge->n_used = 1; vge->base[0] = guest_IP_bbstart; vge->len[0] = 0; /* And a new IR BB to dump the result into. */ irbb = emptyIRBB(); /* Delta keeps track of how far along the guest_code array we have so far gone. */ delta = 0; n_instrs = 0; /* Guest addresses as IRConsts. Used in the two self-checks generated. */ if (do_self_check) { guest_IP_bbstart_IRConst = guest_word_type==Ity_I32 ? IRConst_U32(toUInt(guest_IP_bbstart)) : IRConst_U64(guest_IP_bbstart); } /* If asked to make a self-checking translation, leave 5 spaces in which to put the check statements. We'll fill them in later when we know the length and adler32 of the area to check. */ if (do_self_check) { selfcheck_idx = irbb->stmts_used; addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); } /* If the caller supplied a function to add its own preamble, use it now. */ if (preamble_function) { Bool stopNow = preamble_function( callback_opaque, irbb ); if (stopNow) { /* The callback has completed the IR block without any guest insns being disassembled into it, so just return it at this point, even if a self-check was requested - as there is nothing to self-check. The five self-check no-ops will still be in place, but they are harmless. */ return irbb; } } /* Process instructions. */ while (True) { vassert(n_instrs < vex_control.guest_max_insns); /* Regardless of what chase_into_ok says, is chasing permissible at all right now? Set resteerOKfn accordingly. */ resteerOK = toBool( n_instrs < vex_control.guest_chase_thresh /* If making self-checking translations, don't chase .. it makes the checks too complicated. We only want to scan just one sequence of bytes in the check, not a whole bunch. */ && !do_self_check /* we can't afford to have a resteer once we're on the last extent slot. */ && vge->n_used < 3 ); resteerOKfn = resteerOK ? chase_into_ok : const_False; /* This is the IP of the instruction we're just about to deal with. */ guest_IP_curr_instr = guest_IP_bbstart + delta; /* This is the irbb statement array index of the first stmt in this insn. That will always be the instruction-mark descriptor. */ first_stmt_idx = irbb->stmts_used; /* Add an instruction-mark statement. We won't know until after disassembling the instruction how long it instruction is, so just put in a zero length and we'll fix it up later. */ addStmtToIRBB( irbb, IRStmt_IMark( guest_IP_curr_instr, 0 )); /* for the first insn, the dispatch loop will have set %IP, but for all the others we have to do it ourselves. */ need_to_put_IP = toBool(n_instrs > 0); /* Finally, actually disassemble an instruction. */ dres = dis_instr_fn ( irbb, need_to_put_IP, resteerOKfn, callback_opaque, guest_code, delta, guest_IP_curr_instr, arch_guest, archinfo_guest, host_bigendian ); /* stay sane ... */ vassert(dres.whatNext == Dis_StopHere || dres.whatNext == Dis_Continue || dres.whatNext == Dis_Resteer); vassert(dres.len >= 0 && dres.len <= 20); if (dres.whatNext != Dis_Resteer) vassert(dres.continueAt == 0); /* Fill in the insn-mark length field. */ vassert(first_stmt_idx >= 0 && first_stmt_idx < irbb->stmts_used); imark = irbb->stmts[first_stmt_idx]; vassert(imark); vassert(imark->tag == Ist_IMark); vassert(imark->Ist.IMark.len == 0); imark->Ist.IMark.len = toUInt(dres.len); /* Print the resulting IR, if needed. */ if (vex_traceflags & VEX_TRACE_FE) { for (i = first_stmt_idx; i < irbb->stmts_used; i++) { vex_printf(" "); ppIRStmt(irbb->stmts[i]); vex_printf("\n"); } } /* If dis_instr_fn terminated the BB at this point, check it also filled in the irbb->next field. */ if (dres.whatNext == Dis_StopHere) { vassert(irbb->next != NULL); if (debug_print) { vex_printf(" "); vex_printf( "goto {"); ppIRJumpKind(irbb->jumpkind); vex_printf( "} "); ppIRExpr( irbb->next ); vex_printf( "\n"); } } /* Update the VexGuestExtents we are constructing. */ /* If vex_control.guest_max_insns is required to be < 100 and each insn is at max 20 bytes long, this limit of 5000 then seems reasonable since the max possible extent length will be 100 * 20 == 2000. */ vassert(vge->len[vge->n_used-1] < 5000); vge->len[vge->n_used-1] = toUShort(toUInt( vge->len[vge->n_used-1] + dres.len )); n_instrs++; if (debug_print) vex_printf("\n"); /* Advance delta (inconspicuous but very important :-) */ delta += (Long)dres.len; switch (dres.whatNext) { case Dis_Continue: vassert(irbb->next == NULL); if (n_instrs < vex_control.guest_max_insns) { /* keep going */ } else { /* We have to stop. */ irbb->next = IRExpr_Const( guest_word_type == Ity_I32 ? IRConst_U32(toUInt(guest_IP_bbstart+delta)) : IRConst_U64(guest_IP_bbstart+delta) ); goto done; } break; case Dis_StopHere: vassert(irbb->next != NULL); goto done; case Dis_Resteer: /* Check that we actually allowed a resteer .. */ vassert(resteerOK); vassert(irbb->next == NULL); /* figure out a new delta to continue at. */ vassert(resteerOKfn(callback_opaque,dres.continueAt)); delta = dres.continueAt - guest_IP_bbstart; /* we now have to start a new extent slot. */ vge->n_used++; vassert(vge->n_used <= 3); vge->base[vge->n_used-1] = dres.continueAt; vge->len[vge->n_used-1] = 0; n_resteers++; d_resteers++; if (0 && (n_resteers & 0xFF) == 0) vex_printf("resteer[%d,%d] to 0x%llx (delta = %lld)\n", n_resteers, d_resteers, dres.continueAt, delta); break; default: vpanic("bb_to_IR"); } } /*NOTREACHED*/ vassert(0); done: /* We're done. The only thing that might need attending to is that a self-checking preamble may need to be created. */ if (do_self_check) { UInt len2check, adler32; IRTemp tistart_tmp, tilen_tmp; vassert(vge->n_used == 1); len2check = vge->len[0]; if (len2check == 0) len2check = 1; adler32 = genericg_compute_adler32( (HWord)guest_code, len2check ); /* Set TISTART and TILEN. These will describe to the despatcher the area of guest code to invalidate should we exit with a self-check failure. */ tistart_tmp = newIRTemp(irbb->tyenv, guest_word_type); tilen_tmp = newIRTemp(irbb->tyenv, guest_word_type); irbb->stmts[selfcheck_idx+0] = IRStmt_Tmp(tistart_tmp, IRExpr_Const(guest_IP_bbstart_IRConst) ); irbb->stmts[selfcheck_idx+1] = IRStmt_Tmp(tilen_tmp, guest_word_type==Ity_I32 ? IRExpr_Const(IRConst_U32(len2check)) : IRExpr_Const(IRConst_U64(len2check)) ); irbb->stmts[selfcheck_idx+2] = IRStmt_Put( offB_TISTART, IRExpr_Tmp(tistart_tmp) ); irbb->stmts[selfcheck_idx+3] = IRStmt_Put( offB_TILEN, IRExpr_Tmp(tilen_tmp) ); irbb->stmts[selfcheck_idx+4] = IRStmt_Exit( IRExpr_Binop( Iop_CmpNE32, mkIRExprCCall( Ity_I32, 2/*regparms*/, "genericg_compute_adler32", #if defined(__powerpc__) && defined(__powerpc64__) (void*)((ULong*)(&genericg_compute_adler32))[0], #else &genericg_compute_adler32, #endif mkIRExprVec_2( mkIRExpr_HWord( (HWord)guest_code ), mkIRExpr_HWord( (HWord)len2check ) ) ), IRExpr_Const(IRConst_U32(adler32)) ), Ijk_TInval, guest_IP_bbstart_IRConst ); } return irbb; }
void vpanic ( HChar* str ) { vex_printf("\nvex: the `impossible' happened:\n %s\n", str); (*vex_failure_exit)(); }
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 }
/* Inject IR stmts depending on the data provided in the control block iricb. */ void vex_inject_ir(IRSB *irsb, IREndness endian) { IRExpr *data, *rounding_mode, *opnd1, *opnd2, *opnd3, *opnd4; rounding_mode = NULL; if (iricb.rounding_mode != NO_ROUNDING_MODE) { rounding_mode = mkU32(iricb.rounding_mode); } switch (iricb.num_operands) { case 1: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); if (rounding_mode) data = binop(iricb.op, rounding_mode, opnd1); else data = unop(iricb.op, opnd1); break; case 2: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); /* HACK, compiler warning ‘opnd2’ may be used uninitialized */ opnd2 = opnd1; /* immediate_index = 0 immediate value is not used. * immediate_index = 2 opnd2 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 2); if (iricb.immediate_index == 2) { vassert((iricb.t_opnd2 == Ity_I8) || (iricb.t_opnd2 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); /* Interpret the memory as an ULong. */ if (iricb.immediate_type == Ity_I8) { opnd2 = mkU8(*((ULong *)iricb.opnd2)); } else if (iricb.immediate_type == Ity_I16) { opnd2 = mkU16(*((ULong *)iricb.opnd2)); } else if (iricb.immediate_type == Ity_I32) { opnd2 = mkU32(*((ULong *)iricb.opnd2)); } } else { opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); } if (rounding_mode) data = triop(iricb.op, rounding_mode, opnd1, opnd2); else data = binop(iricb.op, opnd1, opnd2); break; case 3: opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); /* HACK, compiler warning ‘opnd3’ may be used uninitialized */ opnd3 = opnd2; /* immediate_index = 0 immediate value is not used. * immediate_index = 3 opnd3 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 3); if (iricb.immediate_index == 3) { vassert((iricb.t_opnd3 == Ity_I8) || (iricb.t_opnd3 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); if (iricb.immediate_type == Ity_I8) { opnd3 = mkU8(*((ULong *)iricb.opnd3)); } else if (iricb.immediate_type == Ity_I16) { opnd3 = mkU16(*((ULong *)iricb.opnd3)); } else if (iricb.immediate_type == Ity_I32) { opnd3 = mkU32(*((ULong *)iricb.opnd3)); } } else { opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); } if (rounding_mode) data = qop(iricb.op, rounding_mode, opnd1, opnd2, opnd3); else data = triop(iricb.op, opnd1, opnd2, opnd3); break; case 4: vassert(rounding_mode == NULL); opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); /* HACK, compiler warning ‘opnd4’ may be used uninitialized */ opnd4 = opnd3; /* immediate_index = 0 immediate value is not used. * immediate_index = 4 opnd4 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 4); if (iricb.immediate_index == 4) { vassert((iricb.t_opnd3 == Ity_I8) || (iricb.t_opnd3 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); if (iricb.immediate_type == Ity_I8) { opnd4 = mkU8(*((ULong *)iricb.opnd4)); } else if (iricb.immediate_type == Ity_I16) { opnd4 = mkU16(*((ULong *)iricb.opnd4)); } else if (iricb.immediate_type == Ity_I32) { opnd4 = mkU32(*((ULong *)iricb.opnd4)); } } else { opnd4 = load(endian, iricb.t_opnd4, iricb.opnd4); } data = qop(iricb.op, opnd1, opnd2, opnd3, opnd4); break; default: vpanic("unsupported operator"); } store(irsb, endian, iricb.result, data); if (0) { vex_printf("BEGIN inject\n"); if (iricb.t_result == Ity_I1 || sizeofIRType(iricb.t_result) <= 8) { ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); } else if (sizeofIRType(iricb.t_result) == 16) { ppIRStmt(irsb->stmts[irsb->stmts_used - 2]); vex_printf("\n"); ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); } vex_printf("\nEND inject\n"); } }
/* A target-independent register allocator. Requires various functions which it uses to deal abstractly with instructions and registers, since it cannot have any target-specific knowledge. Returns a new list of instructions, which, as a result of the behaviour of mapRegs, will be in-place modifications of the original instructions. Requires that the incoming code has been generated using vreg numbers 0, 1 .. n_vregs-1. Appearance of a vreg outside that range is a checked run-time error. Takes an expandable array of pointers to unallocated insns. Returns an expandable array of pointers to allocated insns. */ HInstrArray* doRegisterAllocation ( /* Incoming virtual-registerised code. */ HInstrArray* instrs_in, /* An array listing all the real registers the allocator may use, in no particular order. */ HReg* available_real_regs, Int n_available_real_regs, /* Return True iff the given insn is a reg-reg move, in which case also return the src and dst regs. */ Bool (*isMove) (HInstr*, HReg*, HReg*), /* Get info about register usage in this insn. */ void (*getRegUsage) (HRegUsage*, HInstr*), /* Apply a reg-reg mapping to an insn. */ void (*mapRegs) (HRegRemap*, HInstr*), /* Return an insn to spill/restore a real reg to a spill slot byte offset. */ HInstr* (*genSpill) ( HReg, Int ), HInstr* (*genReload) ( HReg, Int ), Int guest_sizeB, /* For debug printing only. */ void (*ppInstr) ( HInstr* ), void (*ppReg) ( HReg ) ) { # define N_SPILL64S (LibVEX_N_SPILL_BYTES / 8) /* Iterators and temporaries. */ Int ii, j, k, m, spillee, k_suboptimal; HReg rreg, vreg, vregS, vregD; HRegUsage reg_usage; /* Info on vregs and rregs. Computed once and remains unchanged. */ Int n_vreg_lrs; VRegLR* vreg_lrs; /* [0 .. n_vreg_lrs-1] */ RRegLR* rreg_lrs; Int rreg_lrs_size; Int rreg_lrs_used; /* Used when constructing vreg_lrs (for allocating stack slots). */ Int ss_busy_until_before[N_SPILL64S]; /* Used when constructing rreg_lrs. */ Int* rreg_live_after; Int* rreg_dead_before; /* Running state of the core allocation algorithm. */ RRegState* state; Int n_state; /* The vreg -> rreg map constructed and then applied to each instr. */ HRegRemap remap; /* The output array of instructions. */ HInstrArray* instrs_out; vassert(0 == LibVEX_N_SPILL_BYTES % 16); vassert(0 == guest_sizeB % 8); /* The live range numbers are signed shorts, and so limiting the number of insns to 10000 comfortably guards against them overflowing 32k. */ vassert(instrs_in->arr_used <= 10000); # define INVALID_INSTRNO (-2) # define EMIT_INSTR(_instr) \ do { \ HInstr* _tmp = (_instr); \ if (DEBUG_REGALLOC) { \ vex_printf("** "); \ (*ppInstr)(_tmp); \ vex_printf("\n\n"); \ } \ addHInstr ( instrs_out, _tmp ); \ } while (0) # define PRINT_STATE \ do { \ Int z; \ for (z = 0; z < n_state; z++) { \ vex_printf(" "); \ (*ppReg)(state[z].rreg); \ vex_printf("\t "); \ switch (state[z].disp) { \ case Free: vex_printf("Free\n"); break; \ case Unavail: vex_printf("Unavail\n"); break; \ case Bound: vex_printf("BoundTo "); \ (*ppReg)(state[z].vreg); \ vex_printf("\n"); break; \ } \ } \ } while (0) /* --------- Stage 0: set up output array. --------- */ instrs_out = newHInstrArray(); /* ... and initialise running state. */ /* n_state is no more than a short name for n_available_real_regs. */ n_state = n_available_real_regs; state = LibVEX_Alloc(n_available_real_regs * sizeof(RRegState)); for (j = 0; j < n_state; j++) { state[j].rreg = available_real_regs[j]; state[j].has_hlrs = False; state[j].disp = Free; state[j].vreg = INVALID_HREG; state[j].is_spill_cand = False; } /* --------- Stage 1: compute vreg live ranges. --------- */ /* --------- Stage 2: compute rreg live ranges. --------- */ /* ------ start of SET UP TO COMPUTE VREG LIVE RANGES ------ */ /* This is relatively simple, because (1) we only seek the complete end-to-end live range of each vreg, and are not interested in any holes in it, and (2) the vregs are conveniently numbered 0 .. n_vreg_lrs-1, so we can just dump the results in a pre-allocated array. */ n_vreg_lrs = instrs_in->n_vregs; vreg_lrs = NULL; if (n_vreg_lrs > 0) vreg_lrs = LibVEX_Alloc(sizeof(VRegLR) * n_vreg_lrs); for (j = 0; j < n_vreg_lrs; j++) { vreg_lrs[j].live_after = INVALID_INSTRNO; vreg_lrs[j].dead_before = INVALID_INSTRNO; vreg_lrs[j].spill_offset = 0; vreg_lrs[j].spill_size = 0; vreg_lrs[j].reg_class = HRcINVALID; } /* ------ end of SET UP TO COMPUTE VREG LIVE RANGES ------ */ /* ------ start of SET UP TO COMPUTE RREG LIVE RANGES ------ */ /* This is more complex than Stage 1, because we need to compute exactly all the live ranges of all the allocatable real regs, and we don't know in advance how many there will be. */ rreg_lrs_used = 0; rreg_lrs_size = 4; rreg_lrs = LibVEX_Alloc(rreg_lrs_size * sizeof(RRegLR)); /* We'll need to track live range start/end points seperately for each rreg. Sigh. */ vassert(n_available_real_regs > 0); rreg_live_after = LibVEX_Alloc(n_available_real_regs * sizeof(Int)); rreg_dead_before = LibVEX_Alloc(n_available_real_regs * sizeof(Int)); for (j = 0; j < n_available_real_regs; j++) { rreg_live_after[j] = rreg_dead_before[j] = INVALID_INSTRNO; } /* ------ end of SET UP TO COMPUTE RREG LIVE RANGES ------ */ /* ------ start of ITERATE OVER INSNS ------ */ for (ii = 0; ii < instrs_in->arr_used; ii++) { (*getRegUsage)( ®_usage, instrs_in->arr[ii] ); # if 0 vex_printf("\n%d stage1: ", ii); (*ppInstr)(instrs_in->arr[ii]); vex_printf("\n"); ppHRegUsage(®_usage); # endif /* ------ start of DEAL WITH VREG LIVE RANGES ------ */ /* for each reg mentioned in the insn ... */ for (j = 0; j < reg_usage.n_used; j++) { vreg = reg_usage.hreg[j]; /* only interested in virtual registers right now. */ if (!hregIsVirtual(vreg)) continue; k = hregNumber(vreg); if (k < 0 || k >= n_vreg_lrs) { vex_printf("\n"); (*ppInstr)(instrs_in->arr[ii]); vex_printf("\n"); vex_printf("vreg %d, n_vreg_lrs %d\n", k, n_vreg_lrs); vpanic("doRegisterAllocation: out-of-range vreg"); } /* Take the opportunity to note its regclass. We'll need that when allocating spill slots. */ if (vreg_lrs[k].reg_class == HRcINVALID) { /* First mention of this vreg. */ vreg_lrs[k].reg_class = hregClass(vreg); } else { /* Seen it before, so check for consistency. */ vassert(vreg_lrs[k].reg_class == hregClass(vreg)); } /* Now consider live ranges. */ switch (reg_usage.mode[j]) { case HRmRead: if (vreg_lrs[k].live_after == INVALID_INSTRNO) { vex_printf("\n\nOFFENDING VREG = %d\n", k); vpanic("doRegisterAllocation: " "first event for vreg is Read"); } vreg_lrs[k].dead_before = toShort(ii + 1); break; case HRmWrite: if (vreg_lrs[k].live_after == INVALID_INSTRNO) vreg_lrs[k].live_after = toShort(ii); vreg_lrs[k].dead_before = toShort(ii + 1); break; case HRmModify: if (vreg_lrs[k].live_after == INVALID_INSTRNO) { vex_printf("\n\nOFFENDING VREG = %d\n", k); vpanic("doRegisterAllocation: " "first event for vreg is Modify"); } vreg_lrs[k].dead_before = toShort(ii + 1); break; default: vpanic("doRegisterAllocation(1)"); } /* switch */ } /* iterate over registers */ /* ------ end of DEAL WITH VREG LIVE RANGES ------ */ /* ------ start of DEAL WITH RREG LIVE RANGES ------ */ /* for each reg mentioned in the insn ... */ for (j = 0; j < reg_usage.n_used; j++) { /* Dummy initialisations of flush_la and flush_db to avoid possible bogus uninit-var warnings from gcc. */ Int flush_la = INVALID_INSTRNO, flush_db = INVALID_INSTRNO; Bool flush; rreg = reg_usage.hreg[j]; /* only interested in real registers right now. */ if (hregIsVirtual(rreg)) continue; /* Furthermore, we're not interested in this rreg unless it's one of the allocatable ones. For example, it could be a stack pointer register, or some other register beyond our control, in which case we should just ignore it. */ for (k = 0; k < n_available_real_regs; k++) if (available_real_regs[k] == rreg) break; if (k == n_available_real_regs) continue; /* not found -- ignore. */ flush = False; switch (reg_usage.mode[j]) { case HRmWrite: flush_la = rreg_live_after[k]; flush_db = rreg_dead_before[k]; if (flush_la != INVALID_INSTRNO && flush_db != INVALID_INSTRNO) flush = True; rreg_live_after[k] = ii; rreg_dead_before[k] = ii+1; break; case HRmRead: if (rreg_live_after[k] == INVALID_INSTRNO) { vex_printf("\nOFFENDING RREG = "); (*ppReg)(available_real_regs[k]); vex_printf("\n"); vex_printf("\nOFFENDING instr = "); (*ppInstr)(instrs_in->arr[ii]); vex_printf("\n"); vpanic("doRegisterAllocation: " "first event for rreg is Read"); } rreg_dead_before[k] = ii+1; break; case HRmModify: if (rreg_live_after[k] == INVALID_INSTRNO) { vex_printf("\nOFFENDING RREG = "); (*ppReg)(available_real_regs[k]); vex_printf("\n"); vex_printf("\nOFFENDING instr = "); (*ppInstr)(instrs_in->arr[ii]); vex_printf("\n"); vpanic("doRegisterAllocation: " "first event for rreg is Modify"); } rreg_dead_before[k] = ii+1; break; default: vpanic("doRegisterAllocation(2)"); } if (flush) { vassert(flush_la != INVALID_INSTRNO); vassert(flush_db != INVALID_INSTRNO); ensureRRLRspace(&rreg_lrs, &rreg_lrs_size, rreg_lrs_used); if (0) vex_printf("FLUSH 1 (%d,%d)\n", flush_la, flush_db); rreg_lrs[rreg_lrs_used].rreg = rreg; rreg_lrs[rreg_lrs_used].live_after = toShort(flush_la); rreg_lrs[rreg_lrs_used].dead_before = toShort(flush_db); rreg_lrs_used++; } } /* iterate over regs in the instr */ /* ------ end of DEAL WITH RREG LIVE RANGES ------ */ } /* iterate over insns */ /* ------ end of ITERATE OVER INSNS ------ */ /* ------ start of FINALISE RREG LIVE RANGES ------ */ /* Now finish up any live ranges left over. */ for (j = 0; j < n_available_real_regs; j++) { # if 0 vex_printf("residual %d: %d %d\n", j, rreg_live_after[j], rreg_dead_before[j]); # endif vassert( (rreg_live_after[j] == INVALID_INSTRNO && rreg_dead_before[j] == INVALID_INSTRNO) || (rreg_live_after[j] != INVALID_INSTRNO && rreg_dead_before[j] != INVALID_INSTRNO) ); if (rreg_live_after[j] == INVALID_INSTRNO) continue; ensureRRLRspace(&rreg_lrs, &rreg_lrs_size, rreg_lrs_used); if (0) vex_printf("FLUSH 2 (%d,%d)\n", rreg_live_after[j], rreg_dead_before[j]); rreg_lrs[rreg_lrs_used].rreg = available_real_regs[j]; rreg_lrs[rreg_lrs_used].live_after = toShort(rreg_live_after[j]); rreg_lrs[rreg_lrs_used].dead_before = toShort(rreg_dead_before[j]); rreg_lrs_used++; } /* Compute summary hints for choosing real regs. If a real reg is involved in a hard live range, record that fact in the fixed part of the running state. Later, when offered a choice between rregs, it's better to choose one which is not marked as having any HLRs, since ones with HLRs may need to be spilled around their HLRs. Correctness of final assignment is unaffected by this mechanism -- it is an optimisation only. */ for (j = 0; j < rreg_lrs_used; j++) { rreg = rreg_lrs[j].rreg; vassert(!hregIsVirtual(rreg)); /* rreg is involved in a HLR. Record this info in the array, if there is space. */ for (k = 0; k < n_state; k++) if (state[k].rreg == rreg) break; vassert(k < n_state); /* else rreg was not found in state?! */ state[k].has_hlrs = True; } if (0) { for (j = 0; j < n_state; j++) { if (!state[j].has_hlrs) continue; ppReg(state[j].rreg); vex_printf(" hinted\n"); } } /* ------ end of FINALISE RREG LIVE RANGES ------ */ # if DEBUG_REGALLOC for (j = 0; j < n_vreg_lrs; j++) { vex_printf("vreg %d: la = %d, db = %d\n", j, vreg_lrs[j].live_after, vreg_lrs[j].dead_before ); } # endif # if DEBUG_REGALLOC for (j = 0; j < rreg_lrs_used; j++) { (*ppReg)(rreg_lrs[j].rreg); vex_printf(" la = %d, db = %d\n", rreg_lrs[j].live_after, rreg_lrs[j].dead_before ); } # endif /* --------- Stage 3: allocate spill slots. --------- */ /* Each spill slot is 8 bytes long. For 128-bit vregs we have to allocate two spill slots. Do a rank-based allocation of vregs to spill slot numbers. We put as few values as possible in spill slows, but nevertheless need to have a spill slot available for all vregs, just in case. */ /* max_ss_no = -1; */ for (j = 0; j < N_SPILL64S; j++) ss_busy_until_before[j] = 0; for (j = 0; j < n_vreg_lrs; j++) { /* True iff this vreg is unused. In which case we also expect that the reg_class field for it has not been set. */ if (vreg_lrs[j].live_after == INVALID_INSTRNO) { vassert(vreg_lrs[j].reg_class == HRcINVALID); continue; } /* The spill slots are 64 bits in size. That means, to spill a Vec128-class vreg, we'll need to find two adjacent spill slots to use. Note, this special-casing needs to happen for all 128-bit sized register classes. Currently though HRcVector is the only such class. */ if (vreg_lrs[j].reg_class != HRcVec128) { /* The ordinary case -- just find a single spill slot. */ /* Find the lowest-numbered spill slot which is available at the start point of this interval, and assign the interval to it. */ for (k = 0; k < N_SPILL64S; k++) if (ss_busy_until_before[k] <= vreg_lrs[j].live_after) break; if (k == N_SPILL64S) { vpanic("LibVEX_N_SPILL_BYTES is too low. " "Increase and recompile."); } ss_busy_until_before[k] = vreg_lrs[j].dead_before; } else { /* Find two adjacent free slots in which to spill a 128-bit vreg. */ for (k = 0; k < N_SPILL64S-1; k++) if (ss_busy_until_before[k] <= vreg_lrs[j].live_after && ss_busy_until_before[k+1] <= vreg_lrs[j].live_after) break; if (k == N_SPILL64S-1) { vpanic("LibVEX_N_SPILL_BYTES is too low. " "Increase and recompile."); } ss_busy_until_before[k+0] = vreg_lrs[j].dead_before; ss_busy_until_before[k+1] = vreg_lrs[j].dead_before; } /* This reflects LibVEX's hard-wired knowledge of the baseBlock layout: the guest state, then an equal sized area following it for shadow state, and then the spill area. */ vreg_lrs[j].spill_offset = toShort(guest_sizeB * 2 + k * 8); /* if (j > max_ss_no) */ /* max_ss_no = j; */ } # if 0 vex_printf("\n\n"); for (j = 0; j < n_vreg_lrs; j++) vex_printf("vreg %d --> spill offset %d\n", j, vreg_lrs[j].spill_offset); # endif /* --------- Stage 4: establish rreg preferences --------- */ /* It may be advantageous to allocating certain vregs to specific rregs, as a way of avoiding reg-reg moves later. Here we establish which, if any, rreg each vreg would prefer to be in. Note that this constrains the allocator -- ideally we end up with as few as possible vregs expressing a preference. This is an optimisation: if the .preferred_rreg field is never set to anything different from INVALID_HREG, the allocator still works. */ /* 30 Dec 04: removed this mechanism as it does not seem to help. */ /* --------- Stage 5: process instructions --------- */ /* This is the main loop of the allocator. First, we need to correctly set up our running state, which tracks the status of each real register. */ /* ------ BEGIN: Process each insn in turn. ------ */ for (ii = 0; ii < instrs_in->arr_used; ii++) { # if DEBUG_REGALLOC vex_printf("\n====----====---- Insn %d ----====----====\n", ii); vex_printf("---- "); (*ppInstr)(instrs_in->arr[ii]); vex_printf("\n\nInitial state:\n"); PRINT_STATE; vex_printf("\n"); # endif /* ------------ Sanity checks ------------ */ /* Sanity check 1: all rregs with a hard live range crossing this insn must be marked as unavailable in the running state. */ for (j = 0; j < rreg_lrs_used; j++) { if (rreg_lrs[j].live_after < ii && ii < rreg_lrs[j].dead_before) { /* ii is the middle of a hard live range for some real reg. Check it's marked as such in the running state. */ # if 0 vex_printf("considering la %d .. db %d reg = ", rreg_lrs[j].live_after, rreg_lrs[j].dead_before); (*ppReg)(rreg_lrs[j].rreg); vex_printf("\n"); # endif /* find the state entry for this rreg */ for (k = 0; k < n_state; k++) if (state[k].rreg == rreg_lrs[j].rreg) break; /* and assert that this rreg is marked as unavailable */ vassert(state[k].disp == Unavail); } } /* Sanity check 2: conversely, all rregs marked as unavailable in the running state must have a corresponding hard live range entry in the rreg_lrs array. */ for (j = 0; j < n_available_real_regs; j++) { vassert(state[j].disp == Bound || state[j].disp == Free || state[j].disp == Unavail); if (state[j].disp != Unavail) continue; for (k = 0; k < rreg_lrs_used; k++) if (rreg_lrs[k].rreg == state[j].rreg && rreg_lrs[k].live_after < ii && ii < rreg_lrs[k].dead_before) break; /* If this vassertion fails, we couldn't find a corresponding HLR. */ vassert(k < rreg_lrs_used); } /* Sanity check 3: No vreg is bound to more than one rreg. */ for (j = 0; j < n_state; j++) { if (state[j].disp != Bound) continue; for (k = j+1; k < n_state; k++) if (state[k].disp == Bound) vassert(state[k].vreg != state[j].vreg); } /* Sanity check 4: all vreg-rreg bindings must bind registers of the same class. */ for (j = 0; j < n_state; j++) { if (state[j].disp != Bound) continue; vassert(hregClass(state[j].rreg) == hregClass(state[j].vreg)); vassert( hregIsVirtual(state[j].vreg)); vassert(!hregIsVirtual(state[j].rreg)); } /* ------------ end of Sanity checks ------------ */ /* Do various optimisations pertaining to register coalescing and preferencing: MOV v <-> v coalescing (done here). MOV v <-> r coalescing (not yet, if ever) */ /* If doing a reg-reg move between two vregs, and the src's live range ends here and the dst's live range starts here, bind the dst to the src's rreg, and that's all. */ if ( (*isMove)( instrs_in->arr[ii], &vregS, &vregD ) ) { if (!hregIsVirtual(vregS)) goto cannot_coalesce; if (!hregIsVirtual(vregD)) goto cannot_coalesce; /* Check that *isMove is not telling us a bunch of lies ... */ vassert(hregClass(vregS) == hregClass(vregD)); k = hregNumber(vregS); m = hregNumber(vregD); vassert(k >= 0 && k < n_vreg_lrs); vassert(m >= 0 && m < n_vreg_lrs); if (vreg_lrs[k].dead_before != ii + 1) goto cannot_coalesce; if (vreg_lrs[m].live_after != ii) goto cannot_coalesce; # if DEBUG_REGALLOC vex_printf("COALESCE "); (*ppReg)(vregS); vex_printf(" -> "); (*ppReg)(vregD); vex_printf("\n\n"); # endif /* Find the state entry for vregS. */ for (m = 0; m < n_state; m++) if (state[m].disp == Bound && state[m].vreg == vregS) break; if (m == n_state) /* We failed to find a binding for vregS, which means it's currently not in a register. So we can't do the coalescing. Give up. */ goto cannot_coalesce; /* Finally, we can do the coalescing. It's trivial -- merely claim vregS's register for vregD. */ state[m].vreg = vregD; /* Move on to the next insn. We skip the post-insn stuff for fixed registers, since this move should not interact with them in any way. */ continue; } cannot_coalesce: /* ------ Free up rregs bound to dead vregs ------ */ /* Look for vregs whose live range has just ended, and mark the associated rreg as free. */ for (j = 0; j < n_state; j++) { if (state[j].disp != Bound) continue; vreg = hregNumber(state[j].vreg); vassert(vreg >= 0 && vreg < n_vreg_lrs); if (vreg_lrs[vreg].dead_before <= ii) { state[j].disp = Free; if (DEBUG_REGALLOC) { vex_printf("free up "); (*ppReg)(state[j].rreg); vex_printf("\n"); } } } /* ------ Pre-instruction actions for fixed rreg uses ------ */ /* Now we have to deal with rregs which are about to be made live by this instruction -- in other words, are entering into one of their live ranges. If any such rreg holds a vreg, we will have to free up the rreg. The simplest solution which is correct is to spill the rreg. Note we could do better: * Could move it into some other free rreg, if one is available Simplest way to do this is to iterate over the collection of rreg live ranges. */ for (j = 0; j < rreg_lrs_used; j++) { if (rreg_lrs[j].live_after == ii) { /* rreg_lrs[j].rreg needs to be freed up. Find the associated state entry. */ /* Note, re rreg_lrs[j].live_after == ii. Real register live ranges are guaranteed to be well-formed in that they start with a write to the register -- Stage 2 rejects any code not satisfying this. So the correct question to ask is whether rreg_lrs[j].live_after == ii, that is, whether the reg becomes live after this insn -- rather than before it. */ # if DEBUG_REGALLOC vex_printf("need to free up rreg: "); (*ppReg)(rreg_lrs[j].rreg); vex_printf("\n\n"); # endif for (k = 0; k < n_state; k++) if (state[k].rreg == rreg_lrs[j].rreg) break; /* If this fails, we don't have an entry for this rreg. Which we should. */ vassert(k < n_state); if (state[k].disp == Bound) { /* Yes, there is an associated vreg. Spill it if it's still live. */ m = hregNumber(state[k].vreg); vassert(m >= 0 && m < n_vreg_lrs); if (vreg_lrs[m].dead_before > ii) { vassert(vreg_lrs[m].reg_class != HRcINVALID); EMIT_INSTR( (*genSpill)( state[k].rreg, vreg_lrs[m].spill_offset ) ); } } state[k].disp = Unavail; state[k].vreg = INVALID_HREG; } } # if DEBUG_REGALLOC vex_printf("After pre-insn actions for fixed regs:\n"); PRINT_STATE; vex_printf("\n"); # endif /* ------ Deal with the current instruction. ------ */ /* Finally we can begin the processing of this instruction itself. The aim is to free up enough rregs for this insn. This may generate spill stores since we may have to evict some vregs currently in rregs. Also generates spill loads. We also build up the final vreg->rreg mapping to be applied to the insn. */ (*getRegUsage)( ®_usage, instrs_in->arr[ii] ); initHRegRemap(&remap); /* for each reg mentioned in the insn ... */ for (j = 0; j < reg_usage.n_used; j++) { vreg = reg_usage.hreg[j]; /* only interested in virtual registers right now. */ if (!hregIsVirtual(vreg)) continue; # if 0 vex_printf("considering "); (*ppReg)(vreg); vex_printf("\n"); # endif /* Now we're trying to find a rreg for "vreg". First of all, if it already has an rreg assigned, we don't need to do anything more. Search the current state to find out. */ for (k = 0; k < n_state; k++) if (state[k].vreg == vreg && state[k].disp == Bound) break; if (k < n_state) { addToHRegRemap(&remap, vreg, state[k].rreg); continue; } /* No luck. The next thing to do is see if there is a currently free rreg available, of the correct class. If so, bag it. NOTE, we could improve this by selecting an rreg for which the next live-range event is as far ahead as possible. */ k_suboptimal = -1; for (k = 0; k < n_state; k++) { if (state[k].disp != Free || hregClass(state[k].rreg) != hregClass(vreg)) continue; if (state[k].has_hlrs) { /* Well, at least we can use k_suboptimal if we really have to. Keep on looking for a better candidate. */ k_suboptimal = k; } else { /* Found a preferable reg. Use it. */ k_suboptimal = -1; break; } } if (k_suboptimal >= 0) k = k_suboptimal; if (k < n_state) { state[k].disp = Bound; state[k].vreg = vreg; addToHRegRemap(&remap, vreg, state[k].rreg); /* Generate a reload if needed. */ if (reg_usage.mode[j] != HRmWrite) { m = hregNumber(vreg); vassert(m >= 0 && m < n_vreg_lrs); vassert(vreg_lrs[m].reg_class != HRcINVALID); EMIT_INSTR( (*genReload)( state[k].rreg, vreg_lrs[m].spill_offset ) ); } continue; } /* Well, now we have no option but to spill a vreg. It's important to make a good choice of vreg to spill, and of course we need to be careful not to spill a vreg which is needed by this insn. */ /* First, mark in the state, those rregs which are not spill candidates, due to holding a vreg mentioned by this instruction. Or being of the wrong class. */ for (k = 0; k < n_state; k++) { state[k].is_spill_cand = False; if (state[k].disp != Bound) continue; if (hregClass(state[k].rreg) != hregClass(vreg)) continue; state[k].is_spill_cand = True; for (m = 0; m < reg_usage.n_used; m++) { if (state[k].vreg == reg_usage.hreg[m]) { state[k].is_spill_cand = False; break; } } } /* We can choose to spill any rreg satisfying state[r].is_spill_cand (so to speak). Choose r so that the next use of its associated vreg is as far ahead as possible, in the hope that this will minimise the number of consequent reloads required. */ spillee = findMostDistantlyMentionedVReg ( getRegUsage, instrs_in, ii+1, state, n_state ); if (spillee == -1) { /* Hmmmmm. There don't appear to be any spill candidates. We're hosed. */ vex_printf("reg_alloc: can't find a register in class: "); ppHRegClass(hregClass(vreg)); vex_printf("\n"); vpanic("reg_alloc: can't create a free register."); } /* Right. So we're going to spill state[spillee]. */ vassert(spillee >= 0 && spillee < n_state); vassert(state[spillee].disp == Bound); /* check it's the right class */ vassert(hregClass(state[spillee].rreg) == hregClass(vreg)); /* check we're not ejecting the vreg for which we are trying to free up a register. */ vassert(state[spillee].vreg != vreg); m = hregNumber(state[spillee].vreg); vassert(m >= 0 && m < n_vreg_lrs); /* So here's the spill store. Assert that we're spilling a live vreg. */ vassert(vreg_lrs[m].dead_before > ii); vassert(vreg_lrs[m].reg_class != HRcINVALID); EMIT_INSTR( (*genSpill)( state[spillee].rreg, vreg_lrs[m].spill_offset ) ); /* Update the state to reflect the new assignment for this rreg. */ state[spillee].vreg = vreg; /* Now, if this vreg is being read or modified (as opposed to written), we have to generate a reload for it. */ if (reg_usage.mode[j] != HRmWrite) { m = hregNumber(vreg); vassert(m >= 0 && m < n_vreg_lrs); vassert(vreg_lrs[m].reg_class != HRcINVALID); EMIT_INSTR( (*genReload)( state[spillee].rreg, vreg_lrs[m].spill_offset ) ); } /* So after much twisting and turning, we have vreg mapped to state[furthest_k].rreg. Note that in the map. */ addToHRegRemap(&remap, vreg, state[spillee].rreg); } /* iterate over registers in this instruction. */ /* We've finished clowning around with registers in this instruction. Three results: - the running state[] has been updated - a suitable vreg->rreg mapping for this instruction has been constructed - spill and reload instructions may have been emitted. The final step is to apply the mapping to the instruction, and emit that. */ /* NOTE, DESTRUCTIVELY MODIFIES instrs_in->arr[ii]. */ (*mapRegs)( &remap, instrs_in->arr[ii] ); EMIT_INSTR( instrs_in->arr[ii] ); # if DEBUG_REGALLOC vex_printf("After dealing with current insn:\n"); PRINT_STATE; vex_printf("\n"); # endif /* ------ Post-instruction actions for fixed rreg uses ------ */ /* Now we need to check for rregs exiting fixed live ranges after this instruction, and if so mark them as free. */ for (j = 0; j < rreg_lrs_used; j++) { if (rreg_lrs[j].dead_before == ii+1) { /* rreg_lrs[j].rreg is exiting a hard live range. Mark it as such in the main state array. */ for (k = 0; k < n_state; k++) if (state[k].rreg == rreg_lrs[j].rreg) break; /* If this vassertion fails, we don't have an entry for this rreg. Which we should. */ vassert(k < n_state); vassert(state[k].disp == Unavail); state[k].disp = Free; state[k].vreg = INVALID_HREG; } } # if DEBUG_REGALLOC vex_printf("After post-insn actions for fixed regs:\n"); PRINT_STATE; vex_printf("\n"); # endif } /* iterate over insns */ /* ------ END: Process each insn in turn. ------ */ /* free(state); */ /* free(rreg_lrs); */ /* if (vreg_lrs) free(vreg_lrs); */ /* Paranoia */ for (j = 0; j < n_state; j++) vassert(state[j].rreg == available_real_regs[j]); return instrs_out; # undef INVALID_INSTRNO # undef EMIT_INSTR # undef PRINT_STATE }