skyeye动态翻译过程包括两方面的工作:打tag和翻译。打tag主要目的是扫描出需要翻译代码的边界信息,即基本块的信息。基本块都有一个结束符,包括条件执行,跳转,函数调用等。获取了这些基本块的信息之后接下来要做的工作就是将这些基本块的指令翻译成中间码指令,当然中间码所在的基本块跟翻译之前的基本块也存在一种直接的对应关系。打tag函数函数调用关系:tag_start --> tag_recursive,主体函数就是沿着函数执行流获取tag信息。
static void tag_recursive(cpu_t *cpu, addr_t pc, int level)
{
int bytes;
tag_t tag;
addr_t new_pc, next_pc;
if ((cpu->dyncom_engine->flags_codegen & CPU_CODEGEN_TAG_LIMIT)
&& (level == LIMIT_TAGGING_DFS))
return;
if ((cpu->dyncom_engine->flags_codegen & CPU_CODEGEN_TAG_LIMIT)
&& (level == 0))
{
LOG("tag start at %x\n", pc);
/* save tag start address */
cpu->dyncom_engine->tag_start = pc;
}
for(;;) {
if(!cpu->mem_ops.is_inside_page(cpu, pc) && !is_user_mode(cpu))
return;
if (!is_inside_code_area(cpu, pc)){
LOG("In %s pc = %x start = %x end = %x\n",
__FUNCTION__, pc, cpu->dyncom_engine->code_start, cpu->dyncom_engine->code_end);
return;
}
if (LOGGING) {
LOG("%*s", level, "");
// disasm_instr(cpu, pc);
}
/* clear tag when instruction re-transalted. */
tag = get_tag(cpu, pc);
bytes = cpu->f.tag_instr(cpu, pc, &tag, &new_pc, &next_pc);
/* temporary fix: in case the previous instr at pc had changed,
we remove instr dependant tags. They will be set again anyway */
selective_clear_tag(cpu, pc, TAG_BRANCH | TAG_CONDITIONAL | TAG_RET | TAG_STOP | TAG_CONTINUE | TAG_TRAP | TAG_MEMORY | TAG_END_PAGE);
or_tag(cpu, pc, tag | TAG_CODE);
#ifdef OPT_LOCAL_REGISTERS
#if 0
if (is_inside_code_area(cpu, next_pc)){
tag_t tmp_tag;
addr_t tmp_newpc, tmp_nextpc;
cpu->f.tag_instr(cpu, next_pc, &tmp_tag, &tmp_newpc, &tmp_nextpc);
if(tmp_tag & TAG_SYSCALL){
or_tag(cpu, pc, TAG_BEFORE_SYSCALL);
}
if(tag & TAG_SYSCALL){
or_tag(cpu, next_pc, TAG_AFTER_SYSCALL);
}
}
#endif
#endif
LOG("In %s, pc=0x%x, tag=0x%x\n", __FUNCTION__, pc, tag);
if ((tag & TAG_MEMORY) && !is_user_mode(cpu)) {
or_tag(cpu, next_pc, TAG_AFTER_COND);
}
if (tag & (TAG_CONDITIONAL))
or_tag(cpu, next_pc, TAG_AFTER_COND);
if (tag & TAG_TRAP) {
/* regular trap - no code after it */
if (!(cpu->dyncom_engine->flags_hint & (CPU_HINT_TRAP_RETURNS | CPU_HINT_TRAP_RETURNS_TWICE)))
//return;
break;
/*
* client hints that a trap will likely return,
* so tag code after it (optimization for usermode
* code that makes syscalls)
*/
or_tag(cpu, next_pc, TAG_AFTER_TRAP);
/*
* client hints that a trap will likely return
* - to the next instruction AND
* - to the instruction after that
if (cpu->dyncom_engine->flags_hint & CPU_HINT_TRAP_RETURNS_TWICE) {
tag_t dummy1;
addr_t next_pc2, dummy2;
next_pc2 = next_pc + cpu->f.tag_instr(cpu, next_pc, &dummy1, &dummy2, &dummy2);
or_tag(cpu, next_pc2, TAG_AFTER_TRAP);
tag_recursive(cpu, next_pc2, level+1);
}
}
if (tag & TAG_CALL) {
/* tag subroutine, then continue with next instruction */
or_tag(cpu, new_pc, TAG_SUBROUTINE);
or_tag(cpu, next_pc, TAG_AFTER_CALL);
tag_recursive(cpu, new_pc, level+1);
}
if (tag & (TAG_BRANCH)) {
or_tag(cpu, new_pc, TAG_BRANCH_TARGET);
// printf("new_pc : %x pc : %x\n", new_pc, pc);
if (new_pc != NEW_PC_NONE) {
new_pc = (pc & 0xfffff000) + (new_pc & 0xfff);
tag_recursive(cpu, new_pc, level+1);
}
if (!(tag & (TAG_CONDITIONAL)))
//return;
break;
}
if (is_translated(cpu, next_pc)) {
or_tag(cpu, pc, tag | TAG_STOP | TAG_LAST_INST);
//return;
}
if(cpu->mem_ops.is_page_start(cpu, pc) && !is_user_mode(cpu))
or_tag(cpu, pc, tag | TAG_START_PAGE);
if(cpu->mem_ops.is_page_end(cpu, pc) && !is_user_mode(cpu)){
LOG("In %s. TAG_END_PAGE for pc=0x%x\n", __FUNCTION__, pc);
or_tag(cpu, pc, tag | TAG_STOP | TAG_END_PAGE);
xor_tag(cpu, pc, TAG_CONTINUE);
/* if the memory related insn is located at the end of page,
check_mm needs PC to parse the instruction */
if(tag & TAG_MEMORY){
LOG("In %s. TAG_NEED_PC for pc=0x%x\n", __FUNCTION__, pc);
or_tag(cpu, pc, tag | TAG_NEED_PC);
}
break;
}
if ((tag & TAG_EXCEPTION) && !is_user_mode(cpu)) {
or_tag(cpu, next_pc, TAG_AFTER_EXCEPTION);
xor_tag(cpu, pc, TAG_CONTINUE);
break;
}
if (tag & (TAG_RET | TAG_STOP)) /* execution ends here, the follwing location is not reached */
//return;
break;
save_startbb_addr(cpu, pc);
pc = next_pc;
/* save tag end address */
}
save_startbb_addr(cpu, pc);
cpu->dyncom_engine->tag_end = pc;
LOG("tag end at %x\n", pc);
LOG("next pc is %x\n", next_pc);
}
这个函数首先判断递归深度是不是超过了限制,对这个进行限制主要是防止递归过深,影响系统的启动时间。该函数主要部分在for循环内,首先也是一些基本判断,看翻译是否超出了代码段的界限。get_tag是获取pc对应的tag信息,每条指令的tag信息预先分配了存储tag的内存。接来来就是cpu->f.tag_instr(cpu, pc, &tag, &new_pc, &next_pc),这个函数是获取pc对应指令的tag信息,其中new_pc是转移指令跳转后的新地址,非跳转指令不关心这个。next_pc是指接下来那条指令的地址。selective_clear_tag将之前存的tag信息清理掉,保证存的是正确的tag信息。
if ((tag & TAG_MEMORY) && !is_user_mode(cpu)) {
or_tag(cpu, next_pc, TAG_AFTER_COND);
}
如果该tag是访问内存的操作而且是非用户态,如ld,st等 。这里用户态是指没有mmu,tlb等跑的程序,对于这些程序来说因为不存在mmu等地址转换,所以不需要进行标识。这里的内存访问是为了模拟真实处理器的地址转换而设置的。同理标识条件执行。接下来是对tag的处理,一般情况下trap后面没有代码,所以就直接返回了。
if (tag & TAG_CALL) {
/* tag subroutine, then continue with next instruction */
or_tag(cpu, new_pc, TAG_SUBROUTINE);
or_tag(cpu, next_pc, TAG_AFTER_CALL);
tag_recursive(cpu, new_pc, level+1);
}
如果是函数调用,对即将跳转后的地址继续打tag。
if (tag & (TAG_BRANCH)) {
or_tag(cpu, new_pc, TAG_BRANCH_TARGET);
// printf("new_pc : %x pc : %x\n", new_pc, pc);
if (new_pc != NEW_PC_NONE) {
new_pc = (pc & 0xfffff000) + (new_pc & 0xfff);
tag_recursive(cpu, new_pc, level+1);
}
if (!(tag & (TAG_CONDITIONAL)))
//return;
break;
}
如果是分支指令对于目的地址已知的新地址,继续打tag。这里是页内跳转,这里是旧版本,需要更新。
if(cpu->mem_ops.is_page_start(cpu, pc) && !is_user_mode(cpu))
or_tag(cpu, pc, tag | TAG_START_PAGE);
标识页首的指令。
if(cpu->mem_ops.is_page_end(cpu, pc) && !is_user_mode(cpu)){
LOG("In %s. TAG_END_PAGE for pc=0x%x\n", __FUNCTION__, pc);
or_tag(cpu, pc, tag | TAG_STOP | TAG_END_PAGE);
xor_tag(cpu, pc, TAG_CONTINUE);
/* if the memory related insn is located at the end of page,
check_mm needs PC to parse the instruction */
if(tag & TAG_MEMORY){
LOG("In %s. TAG_NEED_PC for pc=0x%x\n", __FUNCTION__, pc);
or_tag(cpu, pc, tag | TAG_NEED_PC);
}
break;
}
对于页尾的指令,将标识为stop,同时将取消continue。