CLI与Rotor中JIT(x86)的函数调用协定

CLI Calling Conventions [in ECMA-335]

CLI函数调用协定精确地描述了托管代码函数调用方Caller与被调用方Callee之间是如何利用堆栈来传递调用参数的。由于CLI采用了完全基于堆栈的简易虚拟机模型,因此,托管函数调用中所有的实参均通过CLI堆栈来传递。下面给出了在调用call指令之前,托管堆栈帧的构造步骤及示例:

1.this引用压栈,如果是方法调用的话
2.函数调用实参从左到右进栈
3.如果最后一个实参是可变数量参数的话,仅需其数组引用进栈即可
4.call/calli/callvirt等call指令返回之后,返回值或引用处于栈顶,函数调用参数全部退栈

cc1.png


JIT Calling Conventions of x86
[in Rotor 1.x]

JIT函数调用协定主要适用于JIT生成的本地代码(Jitted Native Code)之间、及其与执行引擎内部函数之间的参数传递过程。事实上,在真实的本地代码执行过程中并不存在着一个优美而又简单的CLI虚拟堆栈。在进行即时编译时,JIT Compiler需要将抽象的CLI函数调用协议转换成为具体的面向x86本地代码的JIT函数调用方式。在Rotor 1.x与Rotor 2.0中采用了不同的JIT调用协定,后者比前者更为简洁、高效。Rotor 1.x中的JIT调用协定不仅饶舌,而且让人看起来非常费解,其设计者David Stutz在公开演讲中曾多次提到了这一点,他指出:之所以采用这种令人困惑的参数传递方式,主要是为了尽可能避免和减少对执行引擎中已有的非托管代码部分(如Stub/JIT Prestub等处)的修改,貌似有偷懒之虞...

Rotor 1.x中的JIT函数调用协定主要分为两种类型,其步骤与示例分别如下:

A.无可变数量参数时:
1.函数调用实参从左到右进栈
2.将栈中的实参进行重排(Reordering)
 2.1挑选出调用参数列表中最左边的两个可放置在4字节寄存器中的实参(如类型为byte/int的变量等)
 2.2将其值分别存进ECX和EDX寄存器
 2.3从堆栈中删除挑选出的实参,并压缩堆栈
 2.4EDX进栈,然后ECX进栈
3.函数返回缓冲区指针(Return buffer ptr)进栈,如果需要的话

B.存在可变数量参数时:
1.函数调用实参从左到右进栈
2.栈中实参无需进行Reordering
3.将可变数量实参的实际参数数量压栈
4.将可变数量实参的元数据类型Token压栈
5.Return buffer ptr进栈,如果需要有的话
6.this指针进栈,如果是对象方法调用的话

cc2.png


JIT Calling Conventions of x86
[in Rotor 2.0]

Rotor 2.0对JIT函数调用协定进行了再次的整理,其参数传递方式类似于__fastcall的改版,同时还利用了寄存器和浮点堆栈来实现整型及浮点类型的返回。相对整洁的流程及寄存器的使用令整个JIT函数调用过程得到了进一步的优化。其基本流程和2个实例如下:

1.this指针进栈(如果是方法调用的话),然后Return buffer ptr进栈(如果需要的话)
2.所有非可变实参以从左至右的次序进栈
3.如果存在可变数量参数,则所有可变实参从左至右进栈,然后再将可变实参的类型Token压栈
4.从上述压栈次序中挑选出最前面两个可放置在4字节寄存器中的参数,分别存进ECX、EDX并将它们从栈中删除,然后压缩堆栈
5.如果返回值为浮点数,该返回值将放置在FP堆栈的栈顶返回(非调用栈栈顶!)
6.如果返回值为32位整数(或者是可扩充为32bit的类型),则通过EAX寄存器返回
7.如果返回值为64位整数,则通过EAX:EDX寄存器返回
8.其他返回类型一律通过Return buffer ptr返回

cc3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值