qemu指令计数

来源:互联网 发布:怎么样删除淘宝评价 编辑:程序博客网 时间:2024/06/07 17:38

使用全局变量use_icount标记qemu如何进行指令计数。

use_icout=0:表示不统计执行的指令数;

use_icout=1:表示精确同时执行的指令数;

use_icout=2:表示对执行的指令数进行适应性估计。


在configure_icount函数中对use_icount进行设置

void configure_icount(const char *option);

输入参数optionNULL时,use_icount=0;为"auto"时,use_icount=1;否则use_icount=1.

use_icount1时,option中还包含着icount_time_shift的信息:     icount_time_shift = strtol(option, NULL, 0);

use_icount2时,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;

//所以,加上countqemu_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_extraenv->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_startgen_icount_end。但在use_icount0时,它们不起任何作用。下面我们来看看这两个函数的内容,并分析它们所起的作用是什么。

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中的临时参数0xdeadbeefnum_insns,即所翻译TB中的目标指令数。

综合gen_icount_startgen_icount_end函数,它们的作用就是让TCG引擎生成完成下面操作的代码:

比较env->icount_decr.u32num_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();}}


原创粉丝点击