如何影响Android / ARM目标的Delphi XEx代码生成?

How to affect Delphi XEx code generation for Android/ARM targets?

更新2017-05-17。我不再为出现此问题的公司工作,也无法访问Delphi XEx。当我在那里时,问题通过迁移到混合FPC + GCC(Pascal + C)来解决,NEON内在函数用于某些例程,它会产生影响。 (强烈建议使用FPC + GCC,因为它可以使用标准工具,特别是Valgrind。)如果有人能够通过可靠的例子展示他们如何实际能够从Delphi XEx生成优化的ARM代码,我很高兴接受答案。

Embarcadero的Delphi编译器使用LLVM后端为Android设备生成本机ARM代码。我有大量的Pascal代码需要编译到Android应用程序中,我想知道如何使Delphi生成更高效的代码。现在,我甚至都没有谈论自动SIMD优化等高级功能,只是关于生成合理的代码。当然必须有一种方法将参数传递给LLVM端,或以某种方式影响结果?通常,任何编译器都会有很多选项来影响代码编译和优化,但Delphi的ARM目标似乎只是"优化开/关",就是这样。

LLVM应该能够生成相当紧凑和合理的代码,但似乎Delphi以一种奇怪的方式使用它的设施。 Delphi希望非常频繁地使用堆栈,它通常只利用处理器的寄存器r0-r3作为临时变量。也许是最疯狂的,似乎是将正常的32位整数加载为四个1字节的加载操作。如何让Delphi产生更好的ARM代码,而且没有逐字节麻烦的Android?

起初我认为逐字节加载是用于从big-endian交换字节顺序,但事实并非如此,它实际上只是加载了一个带有4个单字节加载的32位数字。*它可能是加载完整的32位而不进行未对齐的字大小的内存加载。 (是否应该避免这是另一回事,这将暗示整个事情是编译器错误)*

让我们来看看这个简单的函数:

1
2
3
4
function ReadInteger(APInteger : PInteger) : Integer;
begin
  Result := APInteger^;
end;

即使启用了优化,带有更新包1的Delphi XE7以及XE6也会为该功能生成以下ARM汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Disassembly of section .text._ZN16Uarmcodetestform11ReadIntegerEPi:

00000000 <_ZN16Uarmcodetestform11ReadIntegerEPi>:
   0:   b580        push    {r7, lr}
   2:   466f        mov r7, sp
   4:   b083        sub sp, #12
   6:   9002        str r0, [sp, #8]
   8:   78c1        ldrb    r1, [r0, #3]
   a:   7882        ldrb    r2, [r0, #2]
   c:   ea42 2101   orr.w   r1, r2, r1, lsl #8
  10:   7842        ldrb    r2, [r0, #1]
  12:   7803        ldrb    r3, [r0, #0]
  14:   ea43 2202   orr.w   r2, r3, r2, lsl #8
  18:   ea42 4101   orr.w   r1, r2, r1, lsl #16
  1c:   9101        str r1, [sp, #4]
  1e:   9000        str r0, [sp, #0]
  20:   4608        mov r0, r1
  22:   b003        add sp, #12
  24:   bd80        pop {r7, pc}

只需计算德尔福需要的指令和内存访问次数。并且从4个单字节加载构造一个32位整数...如果我稍微更改一下函数并使用var参数而不是指针,那么它会稍微复杂化:

1
2
3
4
5
6
7
8
9
10
11
12
13
Disassembly of section .text._ZN16Uarmcodetestform14ReadIntegerVarERi:

00000000 <_ZN16Uarmcodetestform14ReadIntegerVarERi>:
   0:   b580        push    {r7, lr}
   2:   466f        mov r7, sp
   4:   b083        sub sp, #12
   6:   9002        str r0, [sp, #8]
   8:   6801        ldr r1, [r0, #0]
   a:   9101        str r1, [sp, #4]
   c:   9000        str r0, [sp, #0]
   e:   4608        mov r0, r1
  10:   b003        add sp, #12
  12:   bd80        pop {r7, pc}

我不会在这里包含反汇编,但对于iOS,Delphi为指针和var参数版本生成相同的代码,它们几乎与Android var参数版本完全相同但不完全相同。
编辑:澄清一下,逐字节加载仅适用于Android。并且仅在Android上,指针和var参数版本彼此不同。在iOS上,两个版本生成完全相同的代码。

为了比较,这里是FPC 2.7.1(2014年3月的SVN中继版本)认为具有优化级别-O2的功能。指针和var参数版本完全相同。

1
2
3
4
5
6
Disassembly of section .text.n_p$armcodetest_$$_readinteger$pinteger$$longint:

00000000 <P$ARMCODETEST_$$_READINTEGER$PINTEGER$$LONGINT>:

   0:   6800        ldr r0, [r0, #0]
   2:   46f7        mov pc, lr

我还使用Android NDK附带的C编译器测试了等效的C函数。

1
2
3
4
int ReadInteger(int *APInteger)
{
    return *APInteger;
}

这与FPC制作的内容基本相同:

1
2
3
4
5
Disassembly of section .text._Z11ReadIntegerPi:

00000000 <_Z11ReadIntegerPi>:
   0:   6800        ldr r0, [r0, #0]
   2:   4770        bx  lr


We are investigating the issue. In short, it depends on the potential mis-alignment (to 32 boundary) of the Integer referenced by a pointer. Need a little more time to have all of the answers... and a plan to address this.

Marco Cantù, moderator on Delphi Developers

另请参考为什么Delphi zlib和zip库在64位下如此之慢? 因为Win64库是在没有优化的情况下构建的。

在QP报告中:RSP-9922
编译器生成的ARM代码错误,$ O指令被忽略了?,Marco补充说明如下:

There are multiple issues here:

  • As indicated, optimization settings apply only to entire unit files and not to individual functions. Simply put, turning optimization on and off in the same file will have no effect.
  • Furthermore, simply having"Debug information" enabled turns off optimization. Thus, when one is debugging, explicitly turning on optimizations will have no effect. Consequently, the CPU view in the IDE will not be able to display a disassembled view of optimized code.
  • Third, loading non-aligned 64bit data is not safe and does result in errors, hence the separate 4 one byte operations that are needed in given scenarios.