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

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

作者:BlogUpdater |  时间:2020-09-28 |  浏览:596 |  评论已关闭 条评论

预告
今天的文章主要是延续之前有关调用约定的主题,接下来我们要讲述的内容,对于我们后面的一些讨论议题会比较有帮助。
有个需要注意的地方是:在众多的硬件平台上,只有8086和x86平台存在多个调用约定,其他的平台都只有一个。
我们接下来会深入到有关32位调用约定的细节,也许很多人都不太关心这个,但是还是值得讲一讲。

All
在这篇文章里列出的所有的处理器都是RISC类型,这意味着:处理器上有搭载很多寄存器,这些寄存器都提供通用的功能,而不是某些定制化的功能。这里有个例外:零值寄存器是直接硬件连线实现的。挂接到寄存器上的功能都受到调用约定的影响。

在处理器的早期阶段,调用指令call会保存返回地址到一个寄存器,而不是保存在堆栈上。这其实也是一件好事,因为处理器根本不需要了解堆栈的细节信息,这个信息由调用约定来指明。

和之前一样,寄存器或者栈空间被用来传递参数和返回值。

你可以注意到,所有针对RISC处理器的调用约定基本都是差不多的。但是对于8086和x86平台来说,它的调用约定确实是比较奇怪。

Alpha AXP
Alpha AXP(“AXP”是一个人造的首字母缩写单词,实际上它没有什么特别的意思),这款处理器搭载了一系列的32位寄存器,其中有一个硬连接的零值寄存器。根据惯例,这些寄存器中有一个是”栈帧”寄存器,还有一个是”返回地址”寄存器,还有另外两个是和参数传递无关的特殊功能寄存器。

当传递参数的时候,前面6个参数使用寄存器来传递,而剩下的则是通过堆栈来传递。如果是一个可变参数,则所有的参数都会使用堆栈来传递,这样它们就可以使用数组的方式来访问。

剩下的几个寄存器都是被保留的,其中一个用来返回值,剩下的13个Scratch寄存器。
总的来说,32个寄存器被分类为:1个零值寄存器,1个栈帧寄存器,1个返回值寄存器,2个特殊功能寄存器,6个参数传递寄存器,7个保留寄存器,1个返回值寄存器和13个Scratch寄存器。

另外请注意:在Alpha AXP处理器上的函数名称都是完全未修饰(undecorated)的。

MIPS R4000
前4个参数都通过寄存器a0, a1, a2和a3来传递,剩下的则通过堆栈传递。更进一步地,在堆栈上还有4个所谓的”死区”,这这个区域可以用来模拟这4个寄存器参数从堆栈来传递。它们主要被用在被调用函数中,用来将寄存器参数传递到堆栈上,这个对于可变参数十分方便。

在MIPS R4000处理器上的函数名称都是完全未修饰(undecorated)的。

PowerPC
前8个参数通过r3-R10寄存器来传递,返回值则通过手动的方式进行管理。
至于其他的参数如何,我就记不太清了。
在PowerPC处理器上的函数名称都是修饰(decorated)的。

总结
关于MIPS和PPC这两种处理器,我不是很熟悉,所以上面有关它们的讨论可能不是特别准确,但是我想基本原理的讲述应该还是没啥问题的。

最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《The history of calling conventions, part 2》

评论已关闭。