如何确保Delphi例程的16byte代码对齐?

Phi*_*hiS 4 delphi memory-alignment

背景:

我有一个优化的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.
Run Code Online (Sandbox Code Playgroud)

颈部有点疼,但我可以在必要的时候使用这种东西.问题在于,当我在不同的项目中使用这样的单元,或者对同一项目中的其他单元进行一些更改时,这仍然可能会破坏__PadFirstProcTo16它自身的一致性.同样,使用不同的编译器版本(例如D2009与D2010)重新编译相同的项目通常也会破坏对齐.所以,我发现做这种事情的唯一方法是手工作为当项目的其余部分都处于最终形式时要做的最后一件事.

问题1:

有没有其他方法可以达到确保(至少某些特定的)例程与DQWORD对齐的预期效果?

问题2:

哪些是影响编译器代码对齐的确切因素,以及(如何)我可以使用这些特定知识来克服此处概述的问题?

假设为了这个问题"不担心代码对齐/相关的可能小的速度益处" 不是允许的答案.

Phi*_*hiS 7

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

{$CODEALIGN 16}
procedure MyAlignedProc;
begin
..
end;
Run Code Online (Sandbox Code Playgroud)


Fre*_*man 6

你可以做的一件事是在一个明确的ret指令之后,在每个例程的末尾添加一个"魔术"签名:

asm
  ...
  ret
  db <magic signature bytes>
end;
Run Code Online (Sandbox Code Playgroud)

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