前面分析了skyeye的打tag过程,之后需要翻译。翻译是将汇编翻译成中间码,当然在具体翻译之前必须创建函数。在llvm基本概念里面函数包含基本块,基本块包含指令。因此翻译之前需要创建函数。
cpu_translate_function(cpu_t *cpu, addr_t addr)
{
BasicBlock *bb_ret, *bb_trap, *label_entry, *bb_start, *bb_timeout;
static int jit_num = 0;
//addr_t start_addr = cpu->f.get_pc(cpu, cpu->rf.grf);
addr_t start_addr = addr;
/* create function and fill it with std basic blocks */
cpu->dyncom_engine->cur_func = cpu_create_function(cpu, "jitmain", &bb_ret, &bb_trap, &bb_timeout, &label_entry);
/* TRANSLATE! */
UPDATE_TIMING(cpu, TIMER_FE, true);
#ifndef __WIN32__
if (cpu->dyncom_engine->flags_debug & CPU_DEBUG_SINGLESTEP) {
bb_start = cpu_translate_singlestep(cpu, bb_ret, bb_trap);
} else if (cpu->dyncom_engine->flags_debug & CPU_DEBUG_SINGLESTEP_BB) {
bb_start = cpu_translate_singlestep_bb(cpu, bb_ret, bb_trap);
} else {
bb_start = cpu_translate_all(cpu, bb_ret, bb_trap, bb_timeout);
}
#else
bb_start = cpu_translate_all(cpu, bb_ret, bb_trap, bb_timeout);
#endif
UPDATE_TIMING(cpu, TIMER_FE, false);
/* finish entry basicblock */
BranchInst::Create(bb_start, label_entry);
jit_num++;
if (cpu->dyncom_engine->flags_debug & CPU_DEBUG_PRINT_IR)
cpu->dyncom_engine->mod->dump();
/* make sure everything is OK */
if (cpu->dyncom_engine->flags_codegen & CPU_CODEGEN_VERIFY)
verifyFunction(*cpu->dyncom_engine->cur_func, AbortProcessAction);
if (cpu->dyncom_engine->flags_codegen & CPU_CODEGEN_OPTIMIZE) {
UPDATE_TIMING(cpu, TIMER_OPT, true);
LOG("*** Optimizing...");
optimize(cpu);
LOG("done.\n");
UPDATE_TIMING(cpu, TIMER_OPT, false);
if (cpu->dyncom_engine->flags_debug & CPU_DEBUG_PRINT_IR_OPTIMIZED)
cpu->dyncom_engine->mod->dump();
}
LOG("*** Translating...");
UPDATE_TIMING(cpu, TIMER_BE, true);
cpu->dyncom_engine->fp[cpu->dyncom_engine->functions] = cpu->dyncom_engine->exec_engine->getPointerToFunction(cpu->dyncom_engine->cur_func);
//cpu->dyncom_engine->fmap[start_addr] = cpu->dyncom_engine->fp[cpu->dyncom_engine->functions];
save_addr_in_func(cpu, cpu->dyncom_engine->fp[cpu->dyncom_engine->functions]);
LOG("Generate native code for %x\n", start_addr);
UPDATE_TIMING(cpu, TIMER_BE, false);
LOG("done.\n");
cpu->dyncom_engine->functions++;/* Bug."functions" member could not be reset. */
if(cpu->dyncom_engine->functions == JIT_NUM){
printf("JIT function number is %d, Please set your cache bigger.\n", cpu->dyncom_engine->functions);
skyeye_exit(0);
}
}
这里主要的流程是,先创建jit主函数,这个是llvm里面所说的函数主体,我们将重点分析这个函数。这个函数主要是创建了函数的基本框架,如入口地址,异常块,返回块等。
接下来就是真正的翻译过程,主要是根据之前获取的tag信息,创建llvm的基本块,然后在里面填充需要翻译的中间码。
BranchInst::Create(bb_start, label_entry);
跳转到翻译好的基本块里面。接下来就是验证和优化中间码过程,直接调用llvm相应接口即可。
cpu->dyncom_engine->fp[cpu->dyncom_engine->functions] = cpu->dyncom_engine->exec_engine->getPointerToFunction(cpu->dyncom_engine->cur_func);
启动翻译过程获取翻译后的native code地址并记录下来。下面我们重点分析下cpu_create_function这个函数:
Function* cpu_create_function(cpu_t *cpu, const char *name,
BasicBlock **p_bb_ret,
BasicBlock **p_bb_trap,
BasicBlock **p_bb_timeout,
BasicBlock **p_label_entry)
{
Function *func;
// Type Definitions
// - struct reg
StructType *type_struct_reg_t = get_struct_reg(cpu);
cpu->dyncom_engine->mod->addTypeName("struct.reg_t", type_struct_reg_t);
// - struct reg *
PointerType *type_pstruct_reg_t = PointerType::get(type_struct_reg_t, 0);
// - struct spr_reg
StructType *type_struct_spr_reg_t = get_struct_spr_reg(cpu);
cpu->dyncom_engine->mod->addTypeName("struct.spr_reg_t", type_struct_spr_reg_t);
// - struct spr_reg *
PointerType *type_pstruct_spr_reg_t = PointerType::get(type_struct_spr_reg_t, 0);
// - struct fp_reg
StructType *type_struct_fp_reg_t = get_struct_fp_reg(cpu);
cpu->dyncom_engine->mod->addTypeName("struct.fp_reg_t", type_struct_fp_reg_t);
// - struct fp_reg *
PointerType *type_pstruct_fp_reg_t = PointerType::get(type_struct_fp_reg_t, 0);
// - uint8_t *
PointerType *type_pi8 = PointerType::get(getIntegerType(8), 0);
// - intptr *
PointerType *type_intptr = PointerType::get(cpu->dyncom_engine->exec_engine->getTargetData()->getIntPtrType(_CTX()), 0);
// - uint32_t
const IntegerType *type_i32 = IntegerType::get(_CTX(), 32);
/* read memory in IR */
std::vector<const Type*> type_func_read_memory_args;
type_func_read_memory_args.push_back(type_intptr); /* intptr *cpu */
type_func_read_memory_args.push_back(type_i32); /* i32 */
type_func_read_memory_args.push_back(type_i32); /* i32 */
FunctionType *type_func_read_memory_callout = FunctionType::get(
getIntegerType(32), /* Result */
type_func_read_memory_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_pread_memory = PointerType::get(type_func_read_memory_callout, 0);
/* write memory in IR */
std::vector<const Type*> type_func_write_memory_args;
type_func_write_memory_args.push_back(type_intptr); /* intptr *cpu */
type_func_write_memory_args.push_back(type_i32); /* i32 */
type_func_write_memory_args.push_back(type_i32); /* i32 */
type_func_write_memory_args.push_back(type_i32); /* i32 */
FunctionType *type_func_write_memory_callout = FunctionType::get(
XgetType(VoidTy), /* Result */
type_func_write_memory_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_pwrite_memory = PointerType::get(type_func_write_memory_callout, 0);
std::vector<const Type*> type_func_check_mm_args;
type_func_check_mm_args.push_back(type_intptr); /* intptr *cpu */
type_func_check_mm_args.push_back(type_i32); /* virtual address */
type_func_check_mm_args.push_back(type_i32); /* count */
type_func_check_mm_args.push_back(type_i32); /* read flag */
FunctionType *type_func_check_mm_callout = FunctionType::get(
getIntegerType(32), /* Result */
type_func_check_mm_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_check_mm = PointerType::get(type_func_check_mm_callout, 0);
// - (*f)(uint8_t *, reg_t *, fp_reg_t *, (*)(...)) [jitmain() function pointer)
std::vector<const Type*>type_func_args;
type_func_args.push_back(type_pi8); /* uint8_t *RAM */
type_func_args.push_back(type_pstruct_reg_t); /* reg_t *reg */
type_func_args.push_back(type_pstruct_spr_reg_t); /* spr_reg_t */
type_func_args.push_back(type_pstruct_fp_reg_t); /* fp_reg_t *fp_reg */
type_func_args.push_back(cpu->dyncom_engine->type_pread_memory);
std::vector<const Type*> type_func_write_memory_args;
type_func_write_memory_args.push_back(type_intptr); /* intptr *cpu */
type_func_write_memory_args.push_back(type_i32); /* i32 */
type_func_write_memory_args.push_back(type_i32); /* i32 */
type_func_write_memory_args.push_back(type_i32); /* i32 */
FunctionType *type_func_write_memory_callout = FunctionType::get(
XgetType(VoidTy), /* Result */
type_func_write_memory_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_pwrite_memory = PointerType::get(type_func_write_memory_callout, 0);
std::vector<const Type*> type_func_check_mm_args;
type_func_check_mm_args.push_back(type_intptr); /* intptr *cpu */
type_func_check_mm_args.push_back(type_i32); /* virtual address */
type_func_check_mm_args.push_back(type_i32); /* count */
type_func_check_mm_args.push_back(type_i32); /* read flag */
FunctionType *type_func_check_mm_callout = FunctionType::get(
getIntegerType(32), /* Result */
type_func_check_mm_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_check_mm = PointerType::get(type_func_check_mm_callout, 0);
// - (*f)(uint8_t *, reg_t *, fp_reg_t *, (*)(...)) [jitmain() function pointer)
std::vector<const Type*>type_func_args;
type_func_args.push_back(type_pi8); /* uint8_t *RAM */
type_func_args.push_back(type_pstruct_reg_t); /* reg_t *reg */
type_func_args.push_back(type_pstruct_spr_reg_t); /* spr_reg_t */
type_func_args.push_back(type_pstruct_fp_reg_t); /* fp_reg_t *fp_reg */
type_func_args.push_back(cpu->dyncom_engine->type_pread_memory);
type_func_args.push_back(cpu->dyncom_engine->type_pwrite_memory);
type_func_args.push_back(cpu->dyncom_engine->type_check_mm);
FunctionType* type_func = FunctionType::get(
getIntegerType(32), /* Result */
type_func_args, /* Params */
false); /* isVarArg */
// Function Declarations
func = Function::Create(
type_func, /* Type */
GlobalValue::ExternalLinkage, /* Linkage */
name, cpu->dyncom_engine->mod); /* Name */
func->setCallingConv(CallingConv::C);
AttrListPtr func_PAL;
{
SmallVector<AttributeWithIndex, 4> Attrs;
AttributeWithIndex PAWI;
PAWI.Index = 1U; PAWI.Attrs = 0 | Attribute::NoCapture;
Attrs.push_back(PAWI);
PAWI.Index = 4294967295U; PAWI.Attrs = 0 | Attribute::NoUnwind;
Attrs.push_back(PAWI);
func_PAL = AttrListPtr::get(Attrs.begin(), Attrs.end());
}
func->setAttributes(func_PAL);
// args
Function::arg_iterator args = func->arg_begin();
cpu->dyncom_engine->ptr_RAM = args++;
cpu->dyncom_engine->ptr_RAM->setName("RAM");
cpu->dyncom_engine->ptr_grf = args++;
cpu->dyncom_engine->ptr_grf->setName("grf");
cpu->dyncom_engine->ptr_srf = args++;
cpu->dyncom_engine->ptr_srf->setName("srf");
cpu->dyncom_engine->ptr_frf = args++;
cpu->dyncom_engine->ptr_frf->setName("frf");
if(!is_user_mode(cpu)){
cpu->dyncom_engine->ptr_func_read_memory = args++;
cpu->dyncom_engine->ptr_func_read_memory->setName("readmemory");
cpu->dyncom_engine->ptr_func_write_memory = args++;
cpu->dyncom_engine->ptr_func_write_memory->setName("writememory");
cpu->dyncom_engine->ptr_func_check_mm = args++;
cpu->dyncom_engine->ptr_func_check_mm->setName("checkmm");
}
// entry basicblock
BasicBlock *label_entry = BasicBlock::Create(_CTX(), "entry", func, 0);
emit_decode_reg(cpu, label_entry);
// create exit code
Value *exit_code = new AllocaInst(getIntegerType(32), "exit_code", label_entry);
new StoreInst(ConstantInt::get(XgetType(Int32Ty),
(cpu->dyncom_engine->flags_debug & (CPU_DEBUG_SINGLESTEP | CPU_DEBUG_SINGLESTEP_BB)) ? JIT_RETURN_SINGLESTEP :
JIT_RETURN_FUNCNOTFOUND), exit_code, false, 0, label_entry);
#if 0 // bad for debugging, minimal speedup
/* make the RAM pointer a constant */
PointerType* type_pi8 = PointerType::get(IntegerType::get(8), 0);
cpu->ptr_RAM = ConstantExpr::getCast(Instruction::IntToPtr, ConstantInt::get(Type::Int64Ty, (uint64_t)(long)cpu->dyncom_engine->RAM), type_pi8);
#endif
// create ret basicblock
BasicBlock *bb_ret = BasicBlock::Create(_CTX(), "ret", func, 0);
spill_reg_state(cpu, bb_ret);
ReturnInst::Create(_CTX(), new LoadInst(exit_code, "", false, 0, bb_ret), bb_ret);
// create trap return basicblock
BasicBlock *bb_trap = BasicBlock::Create(_CTX(), "trap", func, 0);
new StoreInst(ConstantInt::get(XgetType(Int32Ty), JIT_RETURN_TRAP), exit_code, false, 0, bb_trap);
// return
BranchInst::Create(bb_ret, bb_trap);
BasicBlock *bb_timeout = BasicBlock::Create(_CTX(), "timeout", func, 0);
new StoreInst(ConstantInt::get(XgetType(Int32Ty), JIT_RETURN_TIMEOUT), exit_code, false, 0, bb_timeout);
BranchInst::Create(bb_ret, bb_timeout);
*p_bb_ret = bb_ret;
*p_bb_trap = bb_trap;
*p_bb_timeout = bb_timeout;
}
首先的明确一点,我们需要将需要仿真的状态映射到llvm中,这样中间码才能操作到我们仿真的数据。也就是说这里存在着一个基本过程:需要仿真的状态映射到llvm中-->llvm中间码操作数据--->llvm数据再导回我们的仿真数据中。这里面存在两种策略,一种是在llvm里面直接更新我们的仿真数据;另外一种是我们先把仿真数据导到llvm的临时变量中,然后中间码直接操作这些临时数据,最后在函数结束的时候再导回。前面都是将自定义类型结构加入llvm系统,相当于注册。
std::vector<const Type*> type_func_read_memory_args;
type_func_read_memory_args.push_back(type_intptr); /* intptr *cpu */
type_func_read_memory_args.push_back(type_i32); /* i32 */
type_func_read_memory_args.push_back(type_i32); /* i32 */
FunctionType *type_func_read_memory_callout = FunctionType::get(
getIntegerType(32), /* Result */
type_func_read_memory_args, /* Params */
false); /* isVarArg */
cpu->dyncom_engine->type_pread_memory = PointerType::get(type_func_read_memory_callout, 0);
这里获取了一个读取内存的函数指针类型,同理还获取了写内存,check_mm等函数,以后中间码使用。
std::vector<const Type*>type_func_args;
type_func_args.push_back(type_pi8); /* uint8_t *RAM */
type_func_args.push_back(type_pstruct_reg_t); /* reg_t *reg */
type_func_args.push_back(type_pstruct_spr_reg_t); /* spr_reg_t */
type_func_args.push_back(type_pstruct_fp_reg_t); /* fp_reg_t *fp_reg */
type_func_args.push_back(cpu->dyncom_engine->type_pread_memory);
type_func_args.push_back(cpu->dyncom_engine->type_pwrite_memory);
type_func_args.push_back(cpu->dyncom_engine->type_check_mm);
FunctionType* type_func = FunctionType::get(
getIntegerType(32), /* Result */
type_func_args, /* Params */
false); /* isVarArg */
// Function Declarations
func = Function::Create(
type_func, /* Type */
GlobalValue::ExternalLinkage, /* Linkage */
name, cpu->dyncom_engine->mod); /* Name */
func->setCallingConv(CallingConv::C);
AttrListPtr func_PAL;
{
SmallVector<AttributeWithIndex, 4> Attrs;
AttributeWithIndex PAWI;
PAWI.Index = 1U; PAWI.Attrs = 0 | Attribute::NoCapture;
Attrs.push_back(PAWI);
PAWI.Index = 4294967295U; PAWI.Attrs = 0 | Attribute::NoUnwind;
Attrs.push_back(PAWI);
func_PAL = AttrListPtr::get(Attrs.begin(), Attrs.end());
}
func->setAttributes(func_PAL);
以上就是我们创建的主函数。然后对参数进行了安排。然后创建函数入口地址。将仿真数据导入llvm中。这里只介绍保存gpr的部分
static void emit_decode_reg_helper(cpu_t *cpu, uint32_t count, uint32_t width,
uint32_t offset, Value *rf, Value **in_ptr_r, Value **ptr_r,
char const *rcname, BasicBlock *bb)
{
#ifdef OPT_LOCAL_REGISTERS
// decode struct reg and copy the registers into local variables
for (uint32_t i = 0; i < count; i++) {
char reg_name[16];
snprintf(reg_name, sizeof(reg_name), "%s_%u", rcname, i);
in_ptr_r[i] = get_struct_member_pointer(rf, i + offset, bb);
ptr_r[i] = new AllocaInst(getIntegerType(width), reg_name, bb);
LoadInst* v = new LoadInst(in_ptr_r[i], "", false, bb);
new StoreInst(v, ptr_r[i], false, bb);
}
#else
// just decode struct reg
for (uint32_t i = 0; i < count; i++)
ptr_r[i] = get_struct_member_pointer(rf, i + offset, bb);
#endif
}
如果优化本地访问的话,我们会现将skyeye的仿真数据先导到栈里面,之后直接操作栈里面的数据,函数结束之后在将栈中的结构导会仿真中。如果不是这样的话,就直接操作外面的仿真数据,就不需要分配栈空间保存了。
接下来再分配一个函数返回的状态码,创建函数的基本框架,trap块,超时块,返回块等。
在返回块里面我们需要将llvm中的状态再导到仿真中,然后回去。在trap块中先将exit_code设置成trap,然后跳到返回块中,超时块也累似地处理。