2017-05-17を更新。私はこの質問の元となった会社で働いていないため、Delphi XExにアクセスできません。私がそこにいる間、問題はFPC + GCC(Pascal + C)の混合に移行することで解決されました。NEON組み込み関数は、それが違いを生むいくつかのルーチンに使用します。(FPC + GCCは、標準ツール、特にValgrindの使用を可能にするため、強く推奨されます。)信頼できる例を使用して、Delphi XExから最適化されたARMコードを実際に生成できる方法を誰かが示すことができる場合、私は答えを受け入れます。
EmbarcaderoのDelphiコンパイラはLLVMバックエンドを使用して、Androidデバイス用のネイティブARMコードを生成します。Androidアプリケーションにコンパイルする必要があるPascalコードが大量にあり、Delphiでより効率的なコードを生成する方法を知りたいです。現在、私は自動SIMD最適化のような高度な機能についてさえ話していません。合理的なコードを生成することについてだけです。確かに、パラメーターをLLVM側に渡す方法、または何らかの方法で結果に影響を与える方法が必要ですか?通常、どのコンパイラにもコードのコンパイルと最適化に影響を与える多くのオプションがありますが、DelphiのARMターゲットは単に「最適化のオン/オフ」であり、それだけです。
LLVMは適度にタイトで実用的なコードを生成できるはずですが、Delphiはその機能を奇妙な方法で使用しているようです。Delphiはスタックを非常に多用したいと考えており、通常、プロセッサのレジスタr0〜r3を一時変数としてのみ使用します。おそらく最もクレイジーなのは、通常の32ビット整数を4つの1バイトのロード操作としてロードすることです。Delphiがより優れたARMコードを生成するようにするにはどうすればよいですか?
最初は、バイトごとの読み込みはビッグエンディアンからバイト順を交換するためのものだと思っていましたが、そうではありません。実際には、32ビットの数値を4つのシングルバイトの読み込みで読み込むだけです。*アライメントされていないワードサイズのメモリロードを行わずに、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}
Delphiがそのために必要な命令とメモリアクセスの数を数えるだけです。そして、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
armeabi-v7aではなく、armeabi必ずターゲットをビルドしてくださいarmeabi。(示されている逆アセンブリは、ビッグエンディアンの値を読み取るようには見えません。リトルエンディアンの値を一度に1バイトずつ読み取るだけです。)