有关调用约定的历史 第二部分

预告
今天的文章主要是延续之前有关调用约定的主题 , 接下来我们要讲述的内容 , 对于我们后面的一些讨论议题会比较有帮助 。 有个需要注意的地方是:在众多的硬件平台上 , 只有8086和x86平台存在多个调用约定 , 其他的平台都只有一个 。 我们接下来会深入到有关32位调用约定的细节 , 也许很多人都不太关心这个 , 但是还是值得讲一讲 。
All
在这篇文章里列出的所有的处理器都是RISC类型 , 这意味着:处理器上有搭载很多寄存器 , 这些寄存器都提供通用的功能 , 而不是某些定制化的功能 。 这里有个例外:零值寄存器是直接硬件连线实现的 。 挂接到寄存器上的功能都受到调用约定的影响 。
在处理器的早期阶段 , 调用指令call会保存返回地址到一个寄存器 , 而不是保存在堆栈上 。 这其实也是一件好事 , 因为处理器根本不需要了解堆栈的细节信息 , 这个信息由调用约定来指明 。
和之前一样 , 寄存器或者栈空间被用来传递参数和返回值 。
你可以注意到 , 所有针对RISC处理器的调用约定基本都是差不多的 。 但是对于8086和x86平台来说 , 它的调用约定确实是比较奇怪 。
AlphaAXP
AlphaAXP(“AXP”是一个人造的首字母缩写单词 , 实际上它没有什么特别的意思) , 这款处理器搭载了一系列的32位寄存器 , 其中有一个硬连接的零值寄存器 。 根据惯例 , 这些寄存器中有一个是”栈帧”寄存器 , 还有一个是”返回地址”寄存器 , 还有另外两个是和参数传递无关的特殊功能寄存器 。
当传递参数的时候 , 前面6个参数使用寄存器来传递 , 而剩下的则是通过堆栈来传递 。 如果是一个可变参数 , 则所有的参数都会使用堆栈来传递 , 这样它们就可以使用数组的方式来访问 。
剩下的几个寄存器都是被保留的 , 其中一个用来返回值 , 剩下的13个Scratch寄存器 。 总的来说 , 32个寄存器被分类为:1个零值寄存器 , 1个栈帧寄存器 , 1个返回值寄存器 , 2个特殊功能寄存器 , 6个参数传递寄存器 , 7个保留寄存器 , 1个返回值寄存器和13个Scratch寄存器 。
另外请注意:在AlphaAXP处理器上的函数名称都是完全未修饰(undecorated)的 。
MIPSR4000
前4个参数都通过寄存器a0,a1,a2和a3来传递 , 剩下的则通过堆栈传递 。 更进一步地 , 在堆栈上还有4个所谓的”死区” , 这这个区域可以用来模拟这4个寄存器参数从堆栈来传递 。 它们主要被用在被调用函数中 , 用来将寄存器参数传递到堆栈上 , 这个对于可变参数十分方便 。
在MIPSR4000处理器上的函数名称都是完全未修饰(undecorated)的 。
PowerPC
前8个参数通过r3-R10寄存器来传递 , 返回值则通过手动的方式进行管理 。 至于其他的参数如何 , 我就记不太清了 。 在PowerPC处理器上的函数名称都是修饰(decorated)的 。
总结
关于MIPS和PPC这两种处理器 , 我不是很熟悉 , 所以上面有关它们的讨论可能不是特别准确 , 但是我想基本原理的讲述应该还是没啥问题的 。
最后
【有关调用约定的历史 第二部分】RaymondChen的《TheOldNewThing》是我非常喜欢的博客之一 , 里面有很多关于Windows的小知识 , 对于广大Windows平台开发者来说 , 确实十分有帮助 。 本文来自:《Thehistoryofcallingconventions,part2》
有关调用约定的历史 第二部分
文章图片


    推荐阅读