如何确保 Delphi 程序的 16 字节对齐?

4

背景:

我有一套优化的 Delphi/BASM 程序,主要用于重型计算。其中一些程序包含内部循环,如果循环开始对齐到 DQWORD(16 字节)边界,我可以实现显著的加速。只要我知道程序入口点的对齐方式,我就可以确保所涉及的循环按照期望的方式对齐。

据我所见,Delphi 编译器将过程/函数对齐到 DWORD 边界,例如添加函数到单元可能会更改后续函数的对齐方式。但是,只要我将程序的结尾填充为 16 的倍数,我就可以确保后续程序也是对齐的,或者根据第一个程序的对齐方式而不对齐。因此,我尝试将关键程序放在单元实现部分的开头,并在它们之前放置一些填充代码,以便第一个过程对齐到 DQWORD。

具体做法如下:

interface

procedure FirstProcInUnit;

implementation

procedure __PadFirstProcTo16;
asm
    // variable number of NOP instructions here to get the desired code length
end;

procedure FirstProcInUnit;
asm //should start at DQWORD boundary
    //do something
    //padding to align the following label to DQWORD boundary
    @Some16BAlignedLabel:
        //code, looping back to @Some16BAlignedLabel
    //do something else
    ret #params
    //padding to get code length to multiple of 16
end;

initialization

__PadFirstProcTo16; //call this here so that it isn't optimised out
ASSERT ((NativeUInt(Pointer(@FirstProcInUnit)) AND $0F) = 0, 'FirstProcInUnit not DQWORD aligned');

end.

这有点麻烦,但是当必要时我可以让这种事情起作用。问题在于,当我在不同的项目中使用这样的单元,或对同一项目中的其他单元进行一些更改时,这仍可能会破坏__PadFirstProcTo16本身的对齐方式。同样,使用不同的编译器版本(例如D2009与D2010)重新编译同一项目通常也会破坏对齐方式。因此,在所有其他项目都处于最终形式时,我发现手动完成这种事情是唯一的方法。 问题1: 是否有其他方法来实现确保(至少某些特定的)例程为DQWORD对齐的期望效果? 问题2: 哪些因素确切影响编译器代码的对齐方式,我如何利用这些具体知识克服这里概述的问题?
假设出于这个问题的考虑,“不要担心代码对齐/相关的大概小速度优势”不是一个可允许的答案。

1
注意:我也在Embarcadero的BASM论坛上发布了这篇文章: http://forums.codegear.com/thread.jspa?threadID=29333 - PhiS
2个回答

7

从Delphi XE开始,使用$CODEALIGN编译器指令(请参见此Delphi文档页面),现在可以轻松解决代码对齐问题:

{$CODEALIGN 16}
procedure MyAlignedProc;
begin
..
end;

6

您可以做的一件事是,在每个例程的结尾添加一个“神奇”的签名,紧随明确的ret指令之后:

asm
  ...
  ret
  db <magic signature bytes>
end;

现在,您可以创建一个包含指向每个例程的指针的数组,在运行时扫描例程以查找魔术签名以找到每个例程的结尾和长度。然后,您可以将它们复制到一个新的内存块中,该内存块使用PAGE_EXECUTE_READWRITE进行分配,这次确保每个例程从16字节边界开始。


那似乎也是一个选项,谢谢。我会进一步调查这种方法。 - PhiS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接