skyeye动态翻译之translate framework

深入分析了skyeye在汇编翻译过程中创建中间码的详细步骤,包括函数创建、基本块构建、指令填充及验证优化流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  前面分析了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,然后跳到返回块中,超时块也累似地处理。                                                                                                                                    
                                                                                                                                  
                                                                                                                                         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值