使用全局变量use_icount标记qemu如何进行指令计数。
use_icout=0:表示不统计执行的指令数;
use_icout=1:表示精确同时执行的指令数;
use_icout=2:表示对执行的指令数进行适应性估计。
在configure_icount函数中对use_icount进行设置
void configure_icount(const char *option);
输入参数option为NULL时,use_icount=0;为"auto"时,use_icount=1;否则use_icount=1.
use_icount为1时,option中还包含着icount_time_shift的信息: icount_time_shift = strtol(option, NULL, 0);
use_icount为2时,icount_time_shift = 3;
qemu中使用全局变量qemu_icount记录执行的指令数,但这个计数值会大于实际执行的指令数,因为其中包含了尚未执行的一些指令。利用这个全局变量计数的功能在qemu_cpu_exec函数中实现。
if (use_icount) {
int64_t count;
int decr;
qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
env->icount_decr.u16.low = 0;
env->icount_extra = 0;
count = qemu_icount_round (qemu_next_deadline());
//count是距离下次事件发生还需执行的指令数,这些指令尚未执行
qemu_icount += count;
//所以,加上count后qemu_icount表示的就不仅仅是已经执行的指令数
decr = (count > 0xffff) ? 0xffff : count;
count -= decr;
//count分成了两部分,一部分存放在icount_decr.u16.low中,这用来控制TB的执行,防止连续执行的指令过多,以至于超出限制。
env->icount_decr.u16.low = decr;
env->icount_extra = count;
}
ret = cpu_exec(env);
//cpu_exec会连续执行,但执行指令数不会超过env->icount_extra与env->icount_decr.u16.low之和。
if (use_icount) {
qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
//这时的qemu_icount是实际执行的指令数
env->icount_decr.u32 = 0;
env->icount_extra = 0;
}
TB执行时是如何进行指令计数呢?
在生成中间码函数gen_intermediate_code_internal中,调用了两个函数来生成指令计数代码,它们可记录下来所翻译TB中包含的目标指令数目。这两个函数就是gen_icount_start与gen_icount_end。但在use_icount为0时,它们不起任何作用。下面我们来看看这两个函数的内容,并分析它们所起的作用是什么。
static inline void gen_icount_start(void)
{
TCGv_i32 count;
if (!use_icount)
return;
icount_label = gen_new_label();
count = tcg_temp_local_new_i32();
tcg_gen_ld_i32(count, cpu_env, offsetof(CPUState, icount_decr.u32));
/* This is a horrid hack to allow fixing up the value later. */
icount_arg = gen_opparam_ptr + 1;
tcg_gen_subi_i32(count, count, 0xdeadbeef);
tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label);
tcg_gen_st16_i32(count, cpu_env, offsetof(CPUState, icount_decr.u16.low));
tcg_temp_free_i32(count);
}
use_icount不为0时,gen_icount_start函数执行后产生一系列的中间码,根据这些中间码,TCG引擎将会生成关于指令计数的代码。
函数中的常量0xdeadbeef并无实际意义,它只是起辅助作用,因为用它生成的中间码在TCG之前还会被修改。icount_arg指向存放0xdeadbeef的中间码参数指针,在gen_icount_end函数中会对其修改。
static void gen_icount_end(TranslationBlock *tb, int num_insns)
{
if (use_icount) {
*icount_arg = num_insns;
gen_set_label(icount_label);
tcg_gen_exit_tb((long)tb + 2);
}
}
可以看到,gen_icount_end中语句:
*icount_arg = num_insns;
作用是修改gen_icount_start中的临时参数0xdeadbeef为num_insns,即所翻译TB中的目标指令数。
综合gen_icount_start和gen_icount_end函数,它们的作用就是让TCG引擎生成完成下面操作的代码:
比较env->icount_decr.u32与num_insns,如果小于0,跳过TB代码,并返回(long)tb+2;否则,env->icount_decr.u32 -= num_insns。
返回(long)tb+2后,在cpu_exec函数中执行下面语句:
next_tb = tcg_qemu_tb_exec(tc_ptr);
if ((next_tb & 3) == 2) {
/* Instruction counter expired. */
int insns_left;
tb = (TranslationBlock *)(long)(next_tb & ~3);
/* Restore PC. */
cpu_pc_from_tb(env, tb);
insns_left = env->icount_decr.u32;
if (env->icount_extra && insns_left >= 0) {
/* Refill decrementer and continue execution. */
env->icount_extra += insns_left;
if (env->icount_extra > 0xffff) {
insns_left = 0xffff;
} else {
insns_left = env->icount_extra;
}
env->icount_extra -= insns_left;
env->icount_decr.u16.low = insns_left;
} else {
if (insns_left > 0) {
/* Execute remaining instructions. */
cpu_exec_nocache(insns_left, tb);
}
env->exception_index = EXCP_INTERRUPT;
next_tb = 0;
cpu_loop_exit();
}
}