关于IA64平台上的另一个误用

关于IA64平台上的另一个误用

作者:BlogUpdater |  时间:2020-10-22 |  浏览:76 |  评论已关闭 条评论

蝎子
在之前的文章中,我说过IA64架构是一个十分热门的处理器架构(本文写于2004年)。那,今天我们就再来讲讲在IA64上容易出现的另一个误用。这个误用会生动地讲述:如果你欺骗了编译器,它会回过头来狠狠地咬上你一口。

在IA64处理器上,没有一个所谓的”绝对寻址”模式,而是通过r1寄存器来访问全局变量,这个寄存器还有另外一个别名”gp”(global pointer)。它始终指向程序中的全局变量,举个例子:如果你定义了三个全局变量,则第一个可以通过[gp + 0]来访问,第二个则是[gp + 8],以此类推,第三个是[gp + 16]。(我相信,Win32 MIPS上的调用约定也是使用这个技术)

在IA64架构上,”addl”指令有一个使用限制,也即:你只能使用最多22位,换句话说,就是4MB。所以你只能拥有4MB的全局变量。

和你所想的一样,有些开发者希望能使用多于4MB的全局变量。幸运的是,这些人没有定义100万个DWORD变量,而是使用了一些特别大型的全局数组。

IA64处理器通过将全局变量分为两类来解决这个问题,将它们分为”small”和”large”这两类。(small和large之间使用一个编译器标志来进行分界。我相信编译器默认将大于8个字节的变量视作large)

下面的代码演示了如何访问一个”small”变量:

在上面的代码中,gp寄存器实际上是指向了变量地址的中间,所以可以同时使用正向和负向偏移来访问。在这个例子中,变量恰好位于相对于gp寄存器的负向偏移中。

作为对比,large全局变量通过一个所谓的”二步操作”来实现访问。第一,变量位于一个文件的单独扇区,第二,指向这个变量的指针会保存到一个small全局变量中。结果是,如果需要访问一个large全局变量,则需要添加一个额外间接访问指令,如下图所示:

如果你没有为一个对象设置一个大小,如下面的代码:
extern BYTE b[];

接下来,编译器会假定这个变量为large。如果实际上,这个变量是small类型,则前面的指针依然会在那里,代码会做一次双重间接寻址来访问这个变量,实际上只需要做一次间接寻址操作就够了。
虽然代码看起来没有那么高效,但是,至少它能正常工作。

但是,如果相反地,你将一个实际上是一个large的变量声明为一个small类型,则你就会陷入麻烦。例如,你在一个文件中写了如下的代码:
extern BYTE b;

然后在另外一个文件中这样写:
extern BYTE b[256];

则编译器从第一个文件中的代码会认为对象是small类型并为它生成small版本的代码。但是当编译器看到第二个文件,它觉得这个变量是large类型。如果变量实际上是large类型,则生成的用于第一个文件的代码将会出现意想不到的错误。

总结
所以,最好,不要犯上面的错误。当你声明一个变量的时候,请确保使用正确的变量声明,否则IA64处理器会发现:你在说谎。
结果就是:你得为此付出代价。

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

评论已关闭。