历史工作记录:V8-SH4移植项目到目前为止的经验总结 2010.3

V8-SH4移植项目到目前为止的经验总结 2010.3

  1. 从IA32实现移植,而不是ARM
    1. 尽量在指令级实现移植(Assembler、MacroAssembler),这样大部分代码可不做修改
    2. IA32的通用寄存器名称不变,定义成常量映射为SH4的通用寄存器
  2. 比较及条件跳转
    1. 现实:IA32有FLAG寄存器,而SH4只有一个T位
    2. 从高层概念上把握一个原则:‘条件成立’时,跳或不跳(动作必须固定住)
    3. IA32的ReverseCondition用于交换src,dst,SH4则不需要
    4. 对a<b,IA32翻译为cmp(a,b);j(less,target); 而SH4则翻译为cmp_gt(a,b);bt(target);
      IA32里,a翻译为dst目标操作数;而SH4里,a变成了src源操作数
    5. 如果能够确定IA32的cmp后续只使用j(equal/not_equal, 则可简单翻译为SH4 cmp_eq
      否则,延迟比较操作,将dst、src操作数(j(less,target)即是dst <(less) src)压栈,在碰到j条件跳转时再pop出执行比较
      此情况下,原始IA32 cmp与j中间不能够有push/pop栈操作、或根据sp+offset的数据寻址操作!
  3. 寻址:
    1. 根据两边的ABI约定,EAX应映射为r0,同时r0在SH4中不应作为临时寄存器使用,避免冲突
    2. 所有的寻址包装成XxxOperand函数的形式,可在开始时使用临时寄存器,只翻译为@Rn间接寻址方式
      1. 至于是否需要当offset很小时,使用@(disp,Rn) add(imm8, Rn) @(R0,Rn)待功能实现后优化不迟
  4. CallRuntime:Runtime函数参数为Argument(argc,argv),但有可能返回64位的ObjectPair
    1. IA32里,argc、argv参数被压栈,(edx:eax)用于返回64位ObjectPair
    2. SH4里,argc、argv分别传递给r4、r5,(r1:r0)用于返回64位数据
    3. *推论:EDX EAX最好直接映射为r1,r0?
  5. IA32 Call指令
    1. IA32的call自动将pr(return address)压栈;SH4需要手工加上(在某些情况下,可能不需要)
    2. StackFrame:某些Stub::Generate,SH4栈上没有pr,需要调整offset
    3. fixup(isPCRelative=true/fasle时的不同处理)
    4. C++代码中如何获取当前Frame的pp、fp、sp?(利用pr。。。)
  6. IA32 call/jmp imm32指令的翻译:
    1. 相对地址的call/jmp,使用BSRF BRAF;绝对地址,使用JSR JMP
      1. 注意:要求知道bsrf braf jsr jmp指令的地址,即可得到imm32地址,因此与mov.l的相对位置必须固定
    2. bf/bt/bsr/bra用于同一个Code内部的分支跳转
    3. 不使用bf.s bt.s
  7. SH4 加载32位立即数到寄存器
    1. 统一的调用入口:mov(const Immediate& src, Register dst);
    2. 因重定位信息只保存mov指令的地址,为使得计算公式统一,要求当rmode!=NONE时,mov指令应Align(4,2);
    3. 立即数不管大小,可统一使用mov(const Immediate& src, Register dst);
      1. 后期优化:仅当rmode==NONE且is_int8(src.value())时,可内部替换为mov_imme(imm8, dst);
  8. 下列操作不应用汇编指令实现,而应考虑提供外部C++ builtin函数:
    1. 64位整数的MUL DIV MOD
    2. CheckFloatOperands:+0 -0 NaN
  9. v8原始代码的Smi优化:可暂不考虑
  10. v8翻译js switch语句的快速Jump Table实现:可暂不考虑
  11. CodeSegment算法
    1. EnsureSpace::CheckSpace(Assembler* assm, int ins_bytes_needed=2, int imm_bytes_needed=0/4/...);
    2. 当开始新代码段的第一个立即数加载指令(imm_bytes_needed>0)时,必须确保还有:
      1. ins_bytes_needed(指令本身大小) + 127(第1个disp值)*4+4/2 + 128*4(假设最多可保存128个立即数) + imm_bytes_needed/4*RelocInfo::kMaxSize ==> ins_bytes_needed+1024+3*imm_bytes_needed
  12. DataLabel、ShortLabel
  13. 反汇编:Dissembler::Decode(stdout, start, end);
    1. 应正确输出立即数,因此内部需要记住mov.l指令对应立即数的位置
      1. 这里假设一个mov.l指令加载一个立即数,指令总在操作数前面(实际上disp类型是uint8_t)
  14. Simulator:Jmp外部C++函数时,仿真为先Call外部C++函数,然后执行一个Return
  15. v8 C++ builtin函数:原型总是Object* BUILTIN_FUNCTION(int argc, char** argv);
    1. 也就是说,总是2个参数,SH4下通过r4,r5传递
    2. 但有可能没有返回值,但无论如何,应注意调用builtin后不应立即改掉r0,这有可能导致一个错误
    3. v8的js调用builtin函数实现为:先加载目标函数的绝对地址到Rn;延后、在适当时机执行jsr(Rn)
  16. 测试:首先cctest,然后mjsunit,确保全部通过(0.4.9.3 mjsunit存在已知bug)
  17. 编码实践指南
    1. 不要复制粘贴,而是首先提取出公共函数,然后手工敲入调用代码(特别是注意不同的参数)
    2. 注意临时寄存器的使用,最好是强制要求调用者指定;其次是约定使用规则
    3. 使用C++代码交叉引用生成工具(最好是能够展开宏代码的)
    4. 分离大的文件如codegen-sh4.cc,根据功能变成小的容易理解、管理、分配的,且SCM也不大会出现冲突
    5. 尽快将调试时所做的补丁代码以易于理解维护的方式改写掉
    6. 使用代码覆盖工具,测试MK23测试代码的条件覆盖情况

你可能感兴趣的:(经验,移植)