Example #1
0
static
EventSet* insert_simcall(IRSB* 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);
    addStmtToIRSB( bbOut, IRStmt_Dirty(di) );

    return es;
}
Example #2
0
void AddGetHelper(IRSB* sb, IRTemp lhs, Int offset, Addr addr)
{
  ULong offset_cur = (HWord)offset;
  ULong lhs_int = (HWord)lhs;
  IRDirty* d1,*d2;
 
  d1 = unsafeIRDirty_0_N(0, "EmitGetTmp2RegHelper",
		        &EmitGetTmp2RegHelper,
		        mkIRExprVec_3(
				      mkIRExpr_HWord(lhs_int),
				      mkIRExpr_HWord(offset_cur),
				      mkIRExpr_HWord(counter)
				     )
		       );
  setHelperAnns(d1);
  addStmtToIRSB(sb,IRStmt_Dirty(d1));
  
  Addr relateaddr = getAddrOf(getVarOf(locToHashKey(offset_cur,0,RegLoc)));

  ThreadId tid = VG_(get_running_tid)();
  tl_assert(VG_INVALID_THREADID != tid);
  ThreadState* tst = VG_(get_ThreadState)(tid);

//  SizeT argRDI = tst->arch.vex.guest_RDI;
//  SizeT argRSI = tst->arch.vex.guest_RSI;
  VexGuestArchState* t = &(tst->arch.vex);
  ULong argRDI;
  ULong argRSI;
/*
  if(offset == 64)
  {
	argRSI = *(&(t->guest_RSI));
	VG_(message)(Vg_UserMsg, "argRSI         %ld \n", argRSI);
	VG_(message)(Vg_UserMsg, "(SSizeT)argRSI %ld \n", (SSizeT)argRSI);
  }
*/
  //VG_(message)(Vg_UserMsg, "addr is %lx \n", addr);

  if(isRealloc)
    {
	if(argNum == 1 && offset == 64)
	  {
		//argRSI = *(&(tst->arch.vex) + offset);
		argRSI = *(&(t->guest_RSI));
//		VG_(message)(Vg_UserMsg, "realloc arg %lu \n", argRSI);
/*		if((SSizeT)argRSI < 0)
		  {
			VG_(message)(Vg_UserMsg, "ERROR realloc arg %ld ", (SSizeT)argRSI);
			//VG_printf("ERROR realloc arg %ld ",(SSizeT)argRSI);
			if(relateaddr != 0)
			  VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr);
			  //VG_printf("(come from 0x%x)",relateaddr);
			VG_(message)(Vg_UserMsg, "\n");
		 	//VG_printf("\n");
		  	
		  }	
*/
		
  		d2 = unsafeIRDirty_0_N(2, "trace_error",
		        		&trace_error,
		        		mkIRExprVec_3(
				      			mkIRExpr_HWord(argRSI),
							mkIRExpr_HWord(addr),
				      			mkIRExpr_HWord(relateaddr)
				     			)
					);
  		setHelperAnns(d2);
  		addStmtToIRSB(sb,IRStmt_Dirty(d2));	

		argNum--;
		isRealloc = 0;	
	  }
    }
  else if(argNum > 0)
    {
	if(offset == 72)
	  {
		argRDI = *(&(t->guest_RDI));
//		VG_(message)(Vg_UserMsg, "alloc arg %lu \n", argRDI);
/*		if((SSizeT)argRDI < 0)
		  {
			VG_(message)(Vg_UserMsg, "ERROR malloc/calloc/new/[] new first arg %ld ", (SSizeT)argRDI);
			//VG_printf("ERROR malloc/calloc/new/[] new first arg %ld ",(SSizeT)argRDI);
			if(relateaddr != 0)
			  VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr);
			  //VG_printf("(come from 0x%x)",relateaddr);
			VG_(message)(Vg_UserMsg, "\n");
		 	//VG_printf("\n");
		  	
		  }
*/
  		d2 = unsafeIRDirty_0_N(2, "trace_error",
		        		&trace_error,
		        		mkIRExprVec_3(
				      			mkIRExpr_HWord(argRDI),
							mkIRExpr_HWord(addr),
				      			mkIRExpr_HWord(relateaddr)
				     			)
					);
  		setHelperAnns(d2);
  		addStmtToIRSB(sb,IRStmt_Dirty(d2));

		argNum--;
	  }
	else if(offset == 64)
	  {
		argRSI = *(&(t->guest_RSI));
//		VG_(message)(Vg_UserMsg, "alloc arg %lu \n", argRSI);
/*		if((SSizeT)argRSI < 0)
		  {
			VG_(message)(Vg_UserMsg, "ERROR calloc/memalign second arg %ld ", (SSizeT)argRSI);
			//VG_printf("ERROR calloc/memalign second arg %ld ",(SSizeT)argRSI);
			if(relateaddr != -1)
		 	  VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr);
			  //VG_printf("(come from 0x%x)",relateaddr);
			VG_(message)(Vg_UserMsg, "\n");
		 	//VG_printf("\n");
		  	
		  }
*/
  		d2 = unsafeIRDirty_0_N(2, "trace_error",
		        		&trace_error,
		        		mkIRExprVec_3(
				      			mkIRExpr_HWord(argRSI),
							mkIRExpr_HWord(addr),
				      			mkIRExpr_HWord(relateaddr)
				     			)
					);
  		setHelperAnns(d2);
  		addStmtToIRSB(sb,IRStmt_Dirty(d2));

		argNum--;
	  }
    }

  return;
}
Example #3
0
static void flushEvents ( CgState* cgs )
{
   Int        i, regparms;
   Char*      helperName;
   void*      helperAddr;
   IRExpr**   argv;
   IRExpr*    i_node_expr;
   IRDirty*   di;
   Event*     ev;
   Event*     ev2;
   Event*     ev3;

   i = 0;
   while (i < cgs->events_used) {

      helperName = NULL;
      helperAddr = NULL;
      argv       = NULL;
      regparms   = 0;

      /* generate IR to notify event i and possibly the ones
         immediately following it. */
      tl_assert(i >= 0 && i < cgs->events_used);

      ev  = &cgs->events[i];
      ev2 = ( i < cgs->events_used-1 ? &cgs->events[i+1] : NULL );
      ev3 = ( i < cgs->events_used-2 ? &cgs->events[i+2] : NULL );
      
      if (DEBUG_CG) {
         VG_(printf)("   flush "); 
         showEvent( ev );
      }

      i_node_expr = mkIRExpr_HWord( (HWord)ev->inode );

      /* Decide on helper fn to call and args to pass it, and advance
         i appropriately. */
      switch (ev->ekind) {
         case Event_Ir:
            /* Merge with a following Dr/Dm if it is from this insn. */
            if (ev2 && (ev2->ekind == Event_Dr || ev2->ekind == Event_Dm)) {
               tl_assert(ev2->inode == ev->inode);
               helperName = "log_1I_1Dr_cache_access";
               helperAddr = &log_1I_1Dr_cache_access;
               argv = mkIRExprVec_3( i_node_expr,
                                     ev2->dataEA,
                                     mkIRExpr_HWord( ev2->datasize ) );
               regparms = 3;
               i += 2;
            }
            /* Merge with a following Dw if it is from this insn. */
            else
            if (ev2 && ev2->ekind == Event_Dw) {
               tl_assert(ev2->inode == ev->inode);
               helperName = "log_1I_1Dw_cache_access";
               helperAddr = &log_1I_1Dw_cache_access;
               argv = mkIRExprVec_3( i_node_expr,
                                     ev2->dataEA,
                                     mkIRExpr_HWord( ev2->datasize ) );
               regparms = 3;
               i += 2;
            }
            /* Merge with two following Irs if possible. */
            else
            if (ev2 && ev3 && ev2->ekind == Event_Ir && ev3->ekind == Event_Ir)
            {
               helperName = "log_3I_0D_cache_access";
               helperAddr = &log_3I_0D_cache_access;
               argv = mkIRExprVec_3( i_node_expr, 
                                     mkIRExpr_HWord( (HWord)ev2->inode ), 
                                     mkIRExpr_HWord( (HWord)ev3->inode ) );
               regparms = 3;
               i += 3;
            }
            /* Merge with a following Ir if possible. */
            else
            if (ev2 && ev2->ekind == Event_Ir) {
               helperName = "log_2I_0D_cache_access";
               helperAddr = &log_2I_0D_cache_access;
               argv = mkIRExprVec_2( i_node_expr,
                                     mkIRExpr_HWord( (HWord)ev2->inode ) );
               regparms = 2;
               i += 2;
            }
            /* No merging possible; emit as-is. */
            else {
               // Assertion: this Event_Ir must be the last one in the
               // events buffer, otherwise it would have been merged with a
               // following event.
               tl_assert(!ev2 && !ev3);
               helperName = "log_1I_0D_cache_access";
               helperAddr = &log_1I_0D_cache_access;
               argv = mkIRExprVec_1( i_node_expr );
               regparms = 1;
               i++;
            }
            break;
         case Event_Dr:
         case Event_Dm:
            helperName = "log_0I_1Dr_cache_access";
            helperAddr = &log_0I_1Dr_cache_access;
            argv = mkIRExprVec_3( i_node_expr, 
                                  ev->dataEA, 
                                  mkIRExpr_HWord( ev->datasize ) );
            regparms = 3;
            i++;
            break;
         case Event_Dw:
            helperName = "log_0I_1Dw_cache_access";
            helperAddr = &log_0I_1Dw_cache_access;
            argv = mkIRExprVec_3( i_node_expr,
                                  ev->dataEA, 
                                  mkIRExpr_HWord( ev->datasize ) );
            regparms = 3;
            i++;
            break;
         default:
            tl_assert(0);
      }

      /* Add the helper. */
      tl_assert(helperName);
      tl_assert(helperAddr);
      tl_assert(argv);
      di = unsafeIRDirty_0_N( regparms, 
                              helperName, VG_(fnptr_to_fnentry)( helperAddr ), 
                              argv );
      addStmtToIRSB( cgs->sbOut, IRStmt_Dirty(di) );
   }

   cgs->events_used = 0;
}
Example #4
0
// Instrumentation for the end of each original instruction.
static
void endOfInstr(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)("endOfInstr: 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 >= VGA_MIN_INSTR_SZB && 
               instrLen <= VGA_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) );
}
Example #5
0
/* This version of flushEvents (currently unused) is similar to the one in
   lackey with the primary difference that it groups together pairs of
   events for a single callback.  This helps to reduce the total amount of
   function call overhead. */
static void flushEventsCB(IRSB* sb)
{
    IRDirty*   di;
    Int        i;
    for (i = 0; i < events_used; i++) {

        Event*       ev = &events[i];

        const HChar* helperName;
        void*        helperAddr;
        IRExpr**     argv;
        Event*       ev2;
        Int          regparms;

        ev2 = i < events_used-1 ? &events[i+1] : NULL;

        if (ev2 &&
                ev->ekind == ev2->ekind &&
                ev->size == ev2->size)
        {
            // Decide on helper fn to call and args to pass it.
            switch (ev->ekind) {
                case Event_Ir: helperName = "trace_2instr";
                               helperAddr =  trace_2instr;  break;

                case Event_Dr: helperName = "trace_2load";
                               helperAddr =  trace_2load;   break;

                case Event_Dw: helperName = "trace_2store";
                               helperAddr =  trace_2store;  break;

                case Event_Dm: helperName = "trace_2modify";
                               helperAddr =  trace_2modify; break;
                default:
                               tl_assert(0);
            }

            argv = mkIRExprVec_3( ev->addr,
                                  ev2->addr, mkIRExpr_HWord( ev->size ));
            regparms = 3;

            // Skip the next event, since we paired it
            i++;
        }
        else if (ev2 &&
                ev->ekind == Event_Dr &&
                ev2->ekind == Event_Dw &&
                ev->size == ev2->size)
        {
            // Load then store
            helperName = "trace_loadstore";
            helperAddr = trace_loadstore;

            argv = mkIRExprVec_3( ev->addr,
                                  ev2->addr, mkIRExpr_HWord( ev->size ));
            regparms = 3;
            i++;
        }
        else if (ev2 &&
                ev->ekind == Event_Dw &&
                ev2->ekind == Event_Dr &&
                ev->size == ev2->size)
        {
            // Store then load
            helperName = "trace_storeload";
            helperAddr = trace_storeload;

            argv = mkIRExprVec_3( ev->addr,
                                  ev2->addr, mkIRExpr_HWord( ev->size ));
            regparms = 3;
            i++;
        }
        else
        {

            // Decide on helper fn to call and args to pass it.
            switch (ev->ekind) {
                case Event_Ir: helperName = "trace_instr";
                               helperAddr =  trace_instr;  break;

                case Event_Dr: helperName = "trace_load";
                               helperAddr =  trace_load;   break;

                case Event_Dw: helperName = "trace_store";
                               helperAddr =  trace_store;  break;

                case Event_Dm: helperName = "trace_modify";
                               helperAddr =  trace_modify; break;
                default:
                               tl_assert(0);
            }

            argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) );
            regparms = 2;
        }

        // Add the helper.
        di   = unsafeIRDirty_0_N(regparms,
                helperName, VG_(fnptr_to_fnentry)( helperAddr ),
                argv );

        addStmtToIRSB( sb, IRStmt_Dirty(di) );
    }

    events_used = 0;
}
Example #6
0
static IRExpr *
log_reads_expr(unsigned tid, IRSB *sb, IRExpr *exp)
{
	if (!exp)
		return NULL;

	switch (exp->tag) {
	case Iex_Get:
	case Iex_FreeVariable:
	case Iex_EntryPoint:
	case Iex_ControlFlow:
		return exp;
	case Iex_GetI: {
		IRExprGetI *e = (IRExprGetI *)exp;
		return IRExpr_GetI(e->descr,
				   log_reads_expr(tid, sb, e->ix),
				   e->bias,
				   e->tid);
	}
	case Iex_Qop: {
		IRExprQop *e = (IRExprQop *)exp;
		return IRExpr_Qop(e->op,
				  log_reads_expr(tid, sb, e->arg1),
				  log_reads_expr(tid, sb, e->arg2),
				  log_reads_expr(tid, sb, e->arg3),
				  log_reads_expr(tid, sb, e->arg4));
	}
	case Iex_Triop: {
		IRExprTriop *e = (IRExprTriop *)exp;
		return IRExpr_Triop(e->op,
				    log_reads_expr(tid, sb, e->arg1),
				    log_reads_expr(tid, sb, e->arg2),
				    log_reads_expr(tid, sb, e->arg3));
	}
	case Iex_Binop: {
		IRExprBinop *e = (IRExprBinop *)exp;
		return IRExpr_Binop(e->op,
				    log_reads_expr(tid, sb, e->arg1),
				    log_reads_expr(tid, sb, e->arg2));
	}
	case Iex_Associative: {
		IRExprAssociative *e = (IRExprAssociative *)exp;
		IRExpr **newArgs = alloc_irexpr_array(e->nr_arguments);
		for (int x = 0;
		     x < e->nr_arguments;
		     x++)
			newArgs[x] =
				log_reads_expr(tid, sb, e->contents[x]);
		return IRExpr_Associative_Claim(e->op, e->nr_arguments, newArgs);
	}
	case Iex_Unop: {
		IRExprUnop *e = (IRExprUnop *)exp;
		return IRExpr_Unop(e->op,
				   log_reads_expr(tid, sb, e->arg));
	}
	case Iex_Load: {
		IRExprLoad *e = (IRExprLoad *)exp;
		IRExpr **args;
		void *helper;
		const char *helper_name;
		IRTemp dest;
		IRDirty *f;

		assert(e->addr->type() == Ity_I64);

		/* Shut compiler up */
		helper = (void *)0xf001;
		helper_name = (const char *)0xdead;

#define HLP(x) helper_name = "helper_load_" #x ; helper = (void *)helper_load_ ## x ;
		switch (e->ty) {
		case Ity_INVALID:
			abort();
		case Ity_I1:
			abort();
		case Ity_I8:
			HLP(8);
			break;
		case Ity_I16:
			HLP(16);
			break;
		case Ity_I32:
			HLP(32);
			break;
		case Ity_I64:
			HLP(64);
			break;
		case Ity_I128:
			HLP(128);
			break;
		}
#undef HLP

		args = mkIRExprVec_3(log_reads_expr(tid, sb, e->addr),
				     IRExpr_Get(OFFSET_amd64_RSP, Ity_I64, tid, 0),
				     IRExpr_Get(OFFSET_amd64_RIP, Ity_I64, tid, 0));
		dest = newIRTemp(sb->tyenv);
		f = unsafeIRDirty_1_N(threadAndRegister::temp(tid, dest, 0),
				      0,
				      helper_name,
				      helper,
				      args);
		addStmtToIRSB(sb, IRStmt_Dirty(f));
		return IRExpr_RdTmp(dest, e->ty, tid, 0);
	}
	case Iex_Const:
		return exp;
	case Iex_CCall: {
		IRExprCCall *e = (IRExprCCall *)exp;
		IRExpr **args;
		int x;
		int nr_args;
		for (nr_args = 0; e->args[nr_args]; nr_args++)
			;
		args = alloc_irexpr_array(nr_args + 1);
		args[nr_args] = NULL;
		for (x = 0; x < nr_args; x++)
			args[x] = log_reads_expr(tid, sb, e->args[x]);
		return IRExpr_CCall(e->cee,
				    e->retty,
				    args);
	}
	case Iex_Mux0X: {
		IRExprMux0X *e = (IRExprMux0X *)exp;
		return IRExpr_Mux0X(log_reads_expr(tid, sb, e->cond),
				    log_reads_expr(tid, sb, e->expr0),
				    log_reads_expr(tid, sb, e->exprX));
	}
	case Iex_HappensBefore:
		abort();
	}
	abort();
}