void instrument_WrTmp_Mux0X(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRExpr* data = st->Ist.WrTmp.data; IRExpr* cond = data->Iex.Mux0X.cond; IRExpr* expr0 = data->Iex.Mux0X.expr0; IRExpr* exprX = data->Iex.Mux0X.exprX; Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, expr0)); IRDirty* di; tl_assert(cond->tag == Iex_RdTmp); tl_assert(isIRAtom(expr0)); tl_assert(isIRAtom(exprX)); tl_assert(typeOfIRTemp(sb_out->tyenv, tmp) == typeOfIRExpr(sb_out->tyenv, expr0)); tl_assert(typeOfIRTemp(sb_out->tyenv, tmp) == typeOfIRExpr(sb_out->tyenv, exprX)); di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_Mux0X", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_Mux0X), mkIRExprVec_5(mkIRExpr_HWord(tmp), assignNew_HWord(sb_out, cond), mkIRExpr_HWord((expr0->tag == Iex_RdTmp) ? expr0->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord((exprX->tag == Iex_RdTmp) ? exprX->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
void instrument_WrTmp_Binop(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRExpr* data = st->Ist.WrTmp.data; IROp op = data->Iex.Binop.op; IRExpr* arg1 = data->Iex.Binop.arg1; IRExpr* arg2 = data->Iex.Binop.arg2; UInt arg1_value = 0, arg2_value = 0; IRExpr* expr = IRExpr_Binop(op, arg1, arg2); Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, expr)); IRDirty* di; // we don't care about floating point and SIMD operations if (op > Iop_AddF64) return; tl_assert(isIRAtom(arg1)); tl_assert(isIRAtom(arg2)); tl_assert(typeOfIRTemp(sb_out->tyenv, tmp) == typeOfIRExpr(sb_out->tyenv, expr)); if (arg1->tag == Iex_Const) { switch (arg1->Iex.Const.con->tag) { case Ico_U1: arg1_value = arg1->Iex.Const.con->Ico.U1; break; case Ico_U8: arg1_value = arg1->Iex.Const.con->Ico.U8; break; case Ico_U16: arg1_value = arg1->Iex.Const.con->Ico.U16; break; case Ico_U32: arg1_value = arg1->Iex.Const.con->Ico.U32; break; case Ico_U64: arg1_value = arg1->Iex.Const.con->Ico.U64; break; default: VG_(tool_panic)("instrument_WrTmp_Binop"); } } if (arg2->tag == Iex_Const) { switch (arg2->Iex.Const.con->tag) { case Ico_U1: arg2_value = arg2->Iex.Const.con->Ico.U1; break; case Ico_U8: arg2_value = arg2->Iex.Const.con->Ico.U8; break; case Ico_U16: arg2_value = arg2->Iex.Const.con->Ico.U16; break; case Ico_U32: arg2_value = arg2->Iex.Const.con->Ico.U32; break; case Ico_U64: arg2_value = arg2->Iex.Const.con->Ico.U64; break; default: VG_(tool_panic)("instrument_WrTmp_Binop"); } } di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_Binop", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_Binop), mkIRExprVec_7(mkIRExpr_HWord(tmp), mkIRExpr_HWord((arg1->tag == Iex_RdTmp) ? arg1->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord((arg2->tag == Iex_RdTmp) ? arg2->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(op), mkIRExpr_HWord(size), (arg1->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, arg1) : mkIRExpr_HWord(arg1_value), (arg2->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, arg2) : mkIRExpr_HWord(arg2_value)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
static Bool loadStoreAddrsMatch(IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { // I'm assuming that for 'modify' instructions, that Vex always makes // the loadAddrExpr and storeAddrExpr be of the same type, ie. both Tmp // expressions, or both Const expressions. CLG_ASSERT(isIRAtom(loadAddrExpr)); CLG_ASSERT(isIRAtom(storeAddrExpr)); return eqIRAtom(loadAddrExpr, storeAddrExpr); }
static void addEvent_Dw ( IRSB* sb, char *fnname, IRAtom* daddr, Int dsize ) { Event* lastEvt; Event* evt; tl_assert(clo_trace_mem); tl_assert(isIRAtom(daddr)); tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); char *buf = (char *)VG_(malloc)("addEvent_Dw",100*sizeof(char)); tl_assert(buf!=NULL); VG_(strcpy)(buf,fnname); /*// Is it possible to merge this write with the preceding read? lastEvt = &events[events_used-1]; if (events_used > 0 && lastEvt->ekind == Event_Dr && lastEvt->size == dsize && eqIRAtom(lastEvt->addr, daddr)) { lastEvt->ekind = Event_Dm; return; }*/ // No. Add as normal. tl_assert(events_used >= 0 && events_used < N_EVENTS); evt = &events[events_used]; evt->ekind = Event_Dw; evt->size = dsize; evt->addr = daddr; evt->fnname = buf; events_used++; //if (events_used == N_EVENTS) flushEvents(sb); }
/* Add an ordinary write event. Try to merge it with an immediately preceding ordinary read event of the same size to the same address. */ static void addEvent_Dw ( IRSB* sb, IRAtom* daddr, Int dsize ) { Event* lastEvt; Event* evt; tl_assert(clo_trace_mem); tl_assert(isIRAtom(daddr)); tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); // Is it possible to merge this write with the preceding read? lastEvt = &events[events_used-1]; if (events_used > 0 && lastEvt->ekind == Event_Dr && lastEvt->size == dsize && lastEvt->guard == NULL && eqIRAtom(lastEvt->addr, daddr)) { lastEvt->ekind = Event_Dm; return; } // No. Add as normal. if (events_used == N_EVENTS) flushEvents(sb); tl_assert(events_used >= 0 && events_used < N_EVENTS); evt = &events[events_used]; evt->ekind = Event_Dw; evt->size = dsize; evt->addr = daddr; evt->guard = NULL; events_used++; }
static void addEvent_Dw ( CgState* cgs, InstrInfo* inode, Int datasize, IRAtom* ea ) { Event* lastEvt; Event* evt; tl_assert(isIRAtom(ea)); tl_assert(datasize >= 1 && datasize <= MIN_LINE_SIZE); /* Is it possible to merge this write with the preceding read? */ lastEvt = &cgs->events[cgs->events_used-1]; if (cgs->events_used > 0 && lastEvt->ekind == Event_Dr && lastEvt->datasize == datasize && lastEvt->inode == inode && eqIRAtom(lastEvt->dataEA, ea)) { lastEvt->ekind = Event_Dm; return; } /* No. Add as normal. */ if (cgs->events_used == N_EVENTS) flushEvents(cgs); tl_assert(cgs->events_used >= 0 && cgs->events_used < N_EVENTS); evt = &cgs->events[cgs->events_used]; evt->ekind = Event_Dw; evt->inode = inode; evt->datasize = datasize; evt->dataEA = ea; cgs->events_used++; }
/* cc_op add/sub/mul adc/sbb shl/Shl/sar tmp = cond(cc_op(cc_dep1, cc_dep2)) and/or/xor inc/dec rol/ror tmp = cond(cc_op(cc_dep1, 0)) The taintness of tmp depends on taintness of both args. (we can't handle and(cc_dep1, 0) which gives an untainted result) cf. valgrind guest_x86_defs.h */ void instrument_WrTmp_CCall(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRExpr* data = st->Ist.WrTmp.data; IRCallee* cee = data->Iex.CCall.cee; IRExpr** args = data->Iex.CCall.args; IRDirty* di; if (VG_(strcmp)(cee->name, "x86g_calculate_condition") == 0) { IRExpr* cond = args[0]; IRExpr* cc_op = args[1]; IRExpr* cc_dep1 = args[2]; IRExpr* cc_dep2 = args[3]; tl_assert(cond->tag == Iex_Const && cond->Iex.Const.con->tag == Ico_U32); tl_assert(isIRAtom(cc_op)); tl_assert(isIRAtom(cc_dep1)); tl_assert(isIRAtom(cc_dep2)); if (cc_op->tag == Iex_Const) tl_assert(cc_op->Iex.Const.con->tag == Ico_U32); if (cc_dep1->tag == Iex_Const) tl_assert(cc_dep1->Iex.Const.con->tag == Ico_U32); if (cc_dep2->tag == Iex_Const) tl_assert(cc_dep2->Iex.Const.con->tag == Ico_U32); // typeOf(x86g_calculate_condition) == typeOf(tmp) == I32 di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_CCall_x86g_calculate_condition", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_CCall_x86g_calculate_condition), mkIRExprVec_7(mkIRExpr_HWord(tmp), mkIRExpr_HWord((cc_dep1->tag == Iex_RdTmp) ? cc_dep1->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord((cc_dep2->tag == Iex_RdTmp) ? cc_dep2->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(cond->Iex.Const.con->Ico.U32), (cc_op->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, cc_op) : mkIRExpr_HWord(cc_op->Iex.Const.con->Ico.U32), (cc_dep1->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, cc_dep1) : mkIRExpr_HWord(cc_dep1->Iex.Const.con->Ico.U32), (cc_dep2->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, cc_dep2) : mkIRExpr_HWord(cc_dep2->Iex.Const.con->Ico.U32)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); } else { di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_CCall_else", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_CCall_else), mkIRExprVec_0() ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); } }
void instrument_CAS_double_element(IRStmt* st, IRSB* sb_out) { IRCAS* cas = st->Ist.CAS.details; IRTemp oldHi = cas->oldHi, oldLo = cas->oldLo; IREndness end = cas->end; IRExpr* addr = cas->addr; IRExpr *expdHi = cas->expdHi, *expdLo = cas->expdLo; IRExpr *dataHi = cas->dataHi, *dataLo = cas->dataLo; Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, dataLo)); IROp op; IRExpr *expr, *expr2; IRDirty* di; tl_assert(isIRAtom(addr)); tl_assert(end == Iend_LE); // we assume endianness is little endian tl_assert(isIRAtom(dataLo)); tl_assert(isIRAtom(dataHi)); if (addr->tag == Iex_Const) tl_assert(addr->Iex.Const.con->tag == Ico_U32); tl_assert(typeOfIRExpr(sb_out->tyenv, addr) == typeOfIRExpr(sb_out->tyenv, dataLo)); switch (size) { case 8: op = Iop_CasCmpEQ8; break; case 16: op = Iop_CasCmpEQ16; break; case 32: op = Iop_CasCmpEQ32; break; default: VG_(tool_panic)("instrument_CAS_double_element"); } expr = assignNew(sb_out, IRExpr_Binop(op, IRExpr_RdTmp(oldLo), expdLo)); // statement has to be flat expr2 = assignNew(sb_out, IRExpr_Binop(op, IRExpr_RdTmp(oldHi), expdHi)); // statement has to be flat di = unsafeIRDirty_0_N(0, "helper_instrument_CAS_double_element", VG_(fnptr_to_fnentry)(helper_instrument_CAS_double_element), mkIRExprVec_6((addr->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, addr) : mkIRExpr_HWord(addr->Iex.Const.con->Ico.U32), mkIRExpr_HWord((dataLo->tag == Iex_RdTmp) ? dataLo->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord((dataHi->tag == Iex_RdTmp) ? dataHi->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(size), assignNew_HWord(sb_out, expr), assignNew_HWord(sb_out, expr2)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
void instrument_Store(IRStmt* st, IRSB* sb_out) { IRExpr* addr = st->Ist.Store.addr; IRExpr* data = st->Ist.Store.data; Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, st->Ist.Store.data)); IRDirty* di; tl_assert(isIRAtom(addr)); tl_assert(isIRAtom(data)); if (addr->tag == Iex_Const) tl_assert(addr->Iex.Const.con->tag == Ico_U32); // the data transfer type is the type of data di = unsafeIRDirty_0_N(0, "helper_instrument_Store", VG_(fnptr_to_fnentry)(helper_instrument_Store), mkIRExprVec_3((addr->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, addr) : mkIRExpr_HWord(addr->Iex.Const.con->Ico.U32), mkIRExpr_HWord((data->tag == Iex_RdTmp) ? data->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
void instrument_LLSC_Store_Conditional(IRStmt* st, IRSB* sb_out) { IRTemp result = st->Ist.LLSC.result; IRExpr* addr = st->Ist.LLSC.addr; IRExpr* storedata = st->Ist.LLSC.storedata; Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, storedata)); IRExpr* result_expr = IRExpr_RdTmp(result); IRDirty* di; tl_assert(isIRAtom(addr)); tl_assert(isIRAtom(storedata)); if (addr->tag == Iex_Const) tl_assert(addr->Iex.Const.con->tag == Ico_U32); // the data transfer type is the type of storedata di = unsafeIRDirty_0_N(0, "helper_instrument_LLSC_Store_Conditional", VG_(fnptr_to_fnentry)(helper_instrument_LLSC_Store_Conditional), mkIRExprVec_4((addr->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, addr) : mkIRExpr_HWord(addr->Iex.Const.con->Ico.U32), mkIRExpr_HWord((storedata->tag == Iex_RdTmp) ? storedata->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(size), assignNew_HWord(sb_out, result_expr)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
static void addEvent_Dr ( IRSB* sb, IRAtom* daddr, Int dsize ) { Event* evt; tl_assert(isIRAtom(daddr)); tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); if (events_used == N_EVENTS) flushEvents(sb); tl_assert(events_used >= 0 && events_used < N_EVENTS); evt = &events[events_used]; evt->ekind = Event_Dr; evt->addr = daddr; evt->size = dsize; events_used++; }
static void addEvent_Dr ( CgState* cgs, InstrInfo* inode, Int datasize, IRAtom* ea ) { Event* evt; tl_assert(isIRAtom(ea)); tl_assert(datasize >= 1 && datasize <= MIN_LINE_SIZE); if (cgs->events_used == N_EVENTS) flushEvents(cgs); tl_assert(cgs->events_used >= 0 && cgs->events_used < N_EVENTS); evt = &cgs->events[cgs->events_used]; evt->ekind = Event_Dr; evt->inode = inode; evt->datasize = datasize; evt->dataEA = ea; cgs->events_used++; }
/* Add a guarded write event. */ static void addEvent_Dw_guarded ( IRSB* sb, IRAtom* daddr, Int dsize, IRAtom* guard ) { Event* evt; tl_assert(clo_trace_mem); tl_assert(isIRAtom(daddr)); tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); if (events_used == N_EVENTS) flushEvents(sb); tl_assert(events_used >= 0 && events_used < N_EVENTS); evt = &events[events_used]; evt->ekind = Event_Dw; evt->addr = daddr; evt->size = dsize; evt->guard = guard; events_used++; }
static void addEvent_RegW ( IRSB* sb, char *fnname, Int reg_no, IRAtom* tmp_val) { IRExpr** argv; IRDirty* di; tl_assert(clo_trace_mem); tl_assert(isIRAtom(tmp_val)); char *buf = (char *)VG_(malloc)("addEvent_RegW",100*sizeof(char)); tl_assert(buf!=NULL); VG_(strcpy)(buf,fnname); argv = mkIRExprVec_3( mkIRExpr_HWord( (HWord) buf ), mkIRExpr_HWord( reg_no ), tmp_val ); di = unsafeIRDirty_0_N( /*regparms*/3, "trace_regw", VG_(fnptr_to_fnentry)( trace_regw ), argv ); if(events_used > 0) flushEvents(sb); addStmtToIRSB( sb, IRStmt_Dirty(di) ); }
void instrument_Put(IRStmt* st, IRSB* sb_out) { Int offset = st->Ist.Put.offset; IRExpr* data = st->Ist.Put.data; Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, data)); IRDirty* di; tl_assert(isIRAtom(data)); // the data transfer type is the type of data di = unsafeIRDirty_0_N(0, "helper_instrument_Put", VG_(fnptr_to_fnentry)(helper_instrument_Put), mkIRExprVec_3(mkIRExpr_HWord(offset), mkIRExpr_HWord((data->tag == Iex_RdTmp) ? data->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
void instrument_LLSC_Load_Linked(IRStmt* st, IRSB* sb_out) { IRTemp result = st->Ist.LLSC.result; IRExpr* addr = st->Ist.LLSC.addr; Int size = sizeofIRType_bits(typeOfIRTemp(sb_out->tyenv, result)); IRDirty* di; tl_assert(isIRAtom(addr)); if (addr->tag == Iex_Const) tl_assert(addr->Iex.Const.con->tag == Ico_U32); // the data transfer type is the type of result di = unsafeIRDirty_0_N(0, "helper_instrument_LLSC_Load_Linked", VG_(fnptr_to_fnentry)(helper_instrument_LLSC_Load_Linked), mkIRExprVec_3(mkIRExpr_HWord(result), (addr->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, addr) : mkIRExpr_HWord(addr->Iex.Const.con->Ico.U32), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
static void addEvent_Dr ( IRSB* sb, char *fnname, IRAtom* daddr, Int dsize ) { Event* evt; tl_assert(clo_trace_mem); tl_assert(isIRAtom(daddr)); tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); char *buf = (char *)VG_(malloc)("addEvent_Dr",100*sizeof(char)); tl_assert(buf!=NULL); VG_(strcpy)(buf,fnname); tl_assert(events_used >= 0 && events_used < N_EVENTS); evt = &events[events_used]; evt->ekind = Event_Dr; evt->addr = daddr; evt->size = dsize; evt->fnname = buf; events_used++; //if (events_used == N_EVENTS) flushEvents(sb); }
void instrument_WrTmp_Load(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRExpr* data = st->Ist.WrTmp.data; IRExpr* addr = data->Iex.Load.addr; Int size = sizeofIRType_bits(data->Iex.Load.ty); IRDirty* di; tl_assert(isIRAtom(addr)); if (addr->tag == Iex_Const) tl_assert(addr->Iex.Const.con->tag == Ico_U32); tl_assert(typeOfIRTemp(sb_out->tyenv, tmp) == data->Iex.Load.ty); di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_Load", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_Load), mkIRExprVec_3(mkIRExpr_HWord(tmp), (addr->tag == Iex_RdTmp) ? assignNew_HWord(sb_out, addr) : mkIRExpr_HWord(addr->Iex.Const.con->Ico.U32), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
void instrument_WrTmp_Unop(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRExpr* data = st->Ist.WrTmp.data; IROp op = data->Iex.Unop.op; IRExpr* arg = data->Iex.Unop.arg; IRExpr* expr = IRExpr_Unop(op, arg); Int size = sizeofIRType_bits(typeOfIRExpr(sb_out->tyenv, expr)); IRDirty* di; tl_assert(isIRAtom(arg)); tl_assert(typeOfIRTemp(sb_out->tyenv, tmp) == typeOfIRExpr(sb_out->tyenv, expr)); di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_Unop", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_Unop), mkIRExprVec_4(mkIRExpr_HWord(tmp), mkIRExpr_HWord((arg->tag == Iex_RdTmp) ? arg->Iex.RdTmp.tmp : IRTemp_INVALID), mkIRExpr_HWord(op), mkIRExpr_HWord(size)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
static IRBB* cg_instrument ( IRBB* bbIn, VexGuestLayout* layout, Addr64 orig_addr_noredir, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Int i, isize; IRStmt* st; Addr64 cia; /* address of current insn */ CgState cgs; IRTypeEnv* tyenv = bbIn->tyenv; InstrInfo* curr_inode = NULL; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up BB, including copying of the where-next stuff. */ cgs.bbOut = emptyIRBB(); cgs.bbOut->tyenv = dopyIRTypeEnv(tyenv); tl_assert( isIRAtom(bbIn->next) ); cgs.bbOut->next = dopyIRExpr(bbIn->next); cgs.bbOut->jumpkind = bbIn->jumpkind; // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRBB( cgs.bbOut, bbIn->stmts[i] ); i++; } // Get the first statement, and initial cia from it tl_assert(bbIn->stmts_used > 0); tl_assert(i < bbIn->stmts_used); st = bbIn->stmts[i]; tl_assert(Ist_IMark == st->tag); cia = st->Ist.IMark.addr; // Set up running state and get block info cgs.events_used = 0; cgs.bbInfo = get_BB_info(bbIn, (Addr)orig_addr_noredir); cgs.bbInfo_i = 0; if (DEBUG_CG) VG_(printf)("\n\n---------- cg_instrument ----------\n"); // Traverse the block, initialising inodes, adding events and flushing as // necessary. for (/*use current i*/; i < bbIn->stmts_used; i++) { st = bbIn->stmts[i]; tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_IMark: cia = st->Ist.IMark.addr; isize = st->Ist.IMark.len; // If Vex fails to decode an instruction, the size will be zero. // Pretend otherwise. if (isize == 0) isize = VG_MIN_INSTR_SZB; // Sanity-check size. tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB) || VG_CLREQ_SZB == isize ); // Get space for and init the inode, record it as the current one. // Subsequent Dr/Dw/Dm events from the same instruction will // also use it. curr_inode = setup_InstrInfo(&cgs, cia, isize); addEvent_Ir( &cgs, curr_inode ); break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; // Note also, endianness info is ignored. I guess // that's not interesting. addEvent_Dr( &cgs, curr_inode, sizeofIRType(data->Iex.Load.ty), aexpr ); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; addEvent_Dw( &cgs, curr_inode, sizeofIRType(typeOfIRExpr(tyenv, data)), aexpr ); break; } case Ist_Dirty: { Int dataSize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dataSize = d->mSize; // Large (eg. 28B, 108B, 512B on x86) data-sized // instructions will be done inaccurately, but they're // very rare and this avoids errors from hitting more // than two cache lines in the simulation. if (dataSize > MIN_LINE_SIZE) dataSize = MIN_LINE_SIZE; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( &cgs, curr_inode, dataSize, d->mAddr ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( &cgs, curr_inode, dataSize, d->mAddr ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } case Ist_Exit: /* We may never reach the next statement, so need to flush all outstanding transactions now. */ flushEvents( &cgs ); break; default: tl_assert(0); break; } /* Copy the original statement */ addStmtToIRBB( cgs.bbOut, st ); if (DEBUG_CG) { ppIRStmt(st); VG_(printf)("\n"); } } /* At the end of the bb. Flush outstandings. */ flushEvents( &cgs ); /* done. stay sane ... */ tl_assert(cgs.bbInfo_i == cgs.bbInfo->n_instrs); if (DEBUG_CG) { VG_(printf)( "goto {"); ppIRJumpKind(bbIn->jumpkind); VG_(printf)( "} "); ppIRExpr( bbIn->next ); VG_(printf)( "}\n"); } return cgs.bbOut; }
static void collectStatementInfo(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize, IRType hWordTy) { CLG_ASSERT(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: break; case Ist_AbiHint: /* ABI hints aren't interesting. Ignore. */ break; case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; CLG_ASSERT( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; CLG_ASSERT( isIRAtom(aexpr) ); if ( NULL == *storeAddrExpr ) { /* this is a kludge: ignore all except the first store from an instruction. */ *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); } break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ CLG_ASSERT(d->mAddr != NULL); CLG_ASSERT(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { CLG_ASSERT(d->mAddr == NULL); CLG_ASSERT(d->mSize == 0); } break; } case Ist_Put: case Ist_PutI: case Ist_MFence: case Ist_Exit: break; default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Callgrind: unhandled IRStmt"); } }
static EventSet* insert_simcall(IRBB* bbOut, InstrInfo* ii, UInt dataSize, Bool instrIssued, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { HChar* helperName; void* helperAddr; Int argc; EventSet* es; IRExpr *arg1, *arg2 = 0, *arg3 = 0, **argv; IRDirty* di; /* Check type of original instruction regarding memory access, * and collect info to be able to generate fitting helper call */ if (!loadAddrExpr && !storeAddrExpr) { // no load/store CLG_ASSERT(0 == dataSize); if (instrIssued) { helperName = 0; helperAddr = 0; } else { helperName = CLG_(cachesim).log_1I0D_name; helperAddr = CLG_(cachesim).log_1I0D; } argc = 1; es = CLG_(sets).D0; } else if (loadAddrExpr && !storeAddrExpr) { // load CLG_ASSERT( isIRAtom(loadAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dr_name; helperAddr = CLG_(cachesim).log_0I1Dr; } else { helperName = CLG_(cachesim).log_1I1Dr_name; helperAddr = CLG_(cachesim).log_1I1Dr; } argc = 2; arg2 = loadAddrExpr; es = CLG_(sets).D1r; } else if (!loadAddrExpr && storeAddrExpr) { // store CLG_ASSERT( isIRAtom(storeAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { CLG_ASSERT( loadAddrExpr && storeAddrExpr ); CLG_ASSERT( isIRAtom(loadAddrExpr) ); CLG_ASSERT( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { /* modify: suppose write access, as this is * more resource consuming (as in callgrind for VG2) * Cachegrind does a read here (!) * DISCUSS: Best way depends on simulation model? */ if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { // load/store if (instrIssued) { helperName = CLG_(cachesim).log_0I2D_name; helperAddr = CLG_(cachesim).log_0I2D; } else { helperName = CLG_(cachesim).log_1I2D_name; helperAddr = CLG_(cachesim).log_1I2D; } argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; es = CLG_(sets).D2; } } /* helper could be unset depending on the simulator used */ if (helperAddr == 0) return 0; /* Setup 1st arg: InstrInfo */ arg1 = mkIRExpr_HWord( (HWord)ii ); // Add call to the instrumentation function if (argc == 1) argv = mkIRExprVec_1(arg1); else if (argc == 2) argv = mkIRExprVec_2(arg1, arg2); else if (argc == 3) argv = mkIRExprVec_3(arg1, arg2, arg3); else VG_(tool_panic)("argc... not 1 or 2 or 3?"); di = unsafeIRDirty_0_N( argc, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); return es; }
static Bool handleOneStatement(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, IRStmt* st2, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize) { tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_Exit: { // This is a conditional jump. Most of the time, we want to add the // instrumentation before it, to ensure it gets executed. Eg, (1) if // this conditional jump is just before an IMark: // // t108 = Not1(t107) // [add instrumentation here] // if (t108) goto {Boring} 0x3A96637D:I32 // ------ IMark(0x3A966370, 7) ------ // // or (2) if this conditional jump is the last thing before the // block-ending unconditional jump: // // t111 = Not1(t110) // [add instrumentation here] // if (t111) goto {Boring} 0x3A96637D:I32 // goto {Boring} 0x3A966370:I32 // // One case (3) where we want the instrumentation after the conditional // jump is when the conditional jump is for an x86 REP instruction: // // ------ IMark(0x3A967F13, 2) ------ // t1 = GET:I32(4) // t6 = CmpEQ32(t1,0x0:I32) // if (t6) goto {Boring} 0x3A967F15:I32 # ignore this cond jmp // t7 = Sub32(t1,0x1:I32) // PUT(4) = t7 // ... // t56 = Not1(t55) // [add instrumentation here] // if (t56) goto {Boring} 0x3A967F15:I32 // // Therefore, we return true if the next statement is an IMark, or if // there is no next statement (which matches case (2), as the final // unconditional jump is not represented in the IRStmt list). // // Note that this approach won't do in the long run for supporting // PPC, but it's good enough for x86/AMD64 for the 3.0.X series. if (NULL == st2 || Ist_IMark == st2->tag) return True; else return False; } case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; tl_assert( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; tl_assert( isIRAtom(aexpr) ); tl_assert( NULL == *storeAddrExpr ); // XXX: ??? *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Cachegrind: unhandled IRStmt"); } return False; }
// Instrumentation for the end of each original instruction. static void instrumentInstr(IRBB* bbOut, instr_info* i_node, Bool bbSeenBefore, UInt instrAddr, UInt instrLen, UInt dataSize, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { IRDirty* di; IRExpr *arg1, *arg2, *arg3, **argv; Int argc; Char* helperName; void* helperAddr; IRType wordTy; // Stay sane ... tl_assert(sizeof(HWord) == sizeof(void*)); if (sizeof(HWord) == 4) { wordTy = Ity_I32; } else if (sizeof(HWord) == 8) { wordTy = Ity_I64; } else { VG_(tool_panic)("instrumentInstr: strange word size"); } if (loadAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, loadAddrExpr)); if (storeAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, storeAddrExpr)); // Nb: instrLen will be zero if Vex failed to decode it. tl_assert( 0 == instrLen || (instrLen >= VG_MIN_INSTR_SZB && instrLen <= VG_MAX_INSTR_SZB) ); // Large (eg. 28B, 108B, 512B on x86) data-sized instructions will be // done inaccurately, but they're very rare and this avoids errors from // hitting more than two cache lines in the simulation. if (dataSize > MIN_LINE_SIZE) dataSize = MIN_LINE_SIZE; // Setup 1st arg: instr_info node's address // Believed to be 64-bit clean do_details(i_node, bbSeenBefore, instrAddr, instrLen, dataSize ); arg1 = mkIRExpr_HWord( (HWord)i_node ); if (!loadAddrExpr && !storeAddrExpr) { // no load/store tl_assert(0 == dataSize); helperName = "log_1I_0D_cache_access"; helperAddr = &log_1I_0D_cache_access; argc = 1; argv = mkIRExprVec_1(arg1); } else if (loadAddrExpr && !storeAddrExpr) { // load tl_assert( isIRAtom(loadAddrExpr) ); helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else if (!loadAddrExpr && storeAddrExpr) { // store tl_assert( isIRAtom(storeAddrExpr) ); helperName = "log_1I_1Dw_cache_access"; helperAddr = &log_1I_1Dw_cache_access; argc = 2; arg2 = storeAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { tl_assert( loadAddrExpr && storeAddrExpr ); tl_assert( isIRAtom(loadAddrExpr) ); tl_assert( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { // modify helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { // load/store helperName = "log_1I_2D_cache_access"; helperAddr = &log_1I_2D_cache_access; argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; argv = mkIRExprVec_3(arg1, arg2, arg3); } } // Add call to the instrumentation function di = unsafeIRDirty_0_N( argc, helperName, helperAddr, argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); }