更新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位而不进行未对齐的字大小的内存负载 . (是否应该避免这是另一件事,这将暗示整个事情是编译器错误)
我们来看看这个简单的函数:
function ReadInteger(APInteger : PInteger) : Integer;
begin
Result := APInteger^;
end;
即使启用了优化,带有更新包1的Delphi XE7以及XE6也会为该功能生成以下ARM汇编代码:
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参数而不是指针,那么它会稍微复杂化:
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参数版本完全相同 .
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函数 .
int ReadInteger(int *APInteger)
{
return *APInteger;
}
这与FPC制作的基本相同:
Disassembly of section .text._Z11ReadIntegerPi:
00000000 <_Z11ReadIntegerPi>:
0: 6800 ldr r0, [r0, #0]
2: 4770 bx lr
1 回答
另外引用Why are the Delphi zlib and zip libraries so slow under 64 bit?,因为Win64库是在没有优化的情况下构建的 .
在QP报告中:RSP-9922 Bad ARM code produced by the compiler, $O directive ignored?,Marco补充说明如下: