Android / ARM hedefleri için Delphi XEx kod üretimini nasıl etkilerim?


266

Güncelleme 2017-05-17. Artık bu sorunun ortaya çıktığı şirket için çalışmıyorum ve Delphi XEx'e erişimim yok. Ben oradayken, sorun karışık FPC + GCC'ye (Pascal + C) geçerek, fark yarattığı bazı rutinler için NEON intrinsics ile çözüldü. (FPC + GCC ayrıca standart araçların, özellikle Valgrind'in kullanılmasını sağladığı için şiddetle tavsiye edilir.) Birisi güvenilir örneklerle, Delphi XEx'ten nasıl optimize edilmiş ARM kodu üretebildiklerini gösterebilirse, cevabı kabul etmekten mutluluk duyuyorum .


Embarcadero'nun Delphi derleyicileri, Android cihazlar için yerel ARM kodu üretmek için LLVM arka ucu kullanır. Android uygulamalarına derlemem gereken çok miktarda Pascal kodum var ve Delphi'nin daha verimli kod üretmesini nasıl yapacağımı bilmek istiyorum. Şu anda, otomatik SIMD optimizasyonları gibi gelişmiş özelliklerden bile bahsetmiyorum, sadece makul kod üretmekle ilgili. Elbette, parametreleri LLVM tarafına geçirmenin veya bir şekilde sonucu etkilemenin bir yolu olmalı? Genellikle, herhangi bir derleyici kod derleme ve optimizasyonu etkilemek için birçok seçeneğe sahip olacaktır, ancak Delphi'nin ARM hedefleri sadece "optimizasyon açık / kapalı" gibi görünüyor ve hepsi bu.

LLVM'nin makul derecede sıkı ve mantıklı kod üretebileceği düşünülüyor, ancak Delphi'nin tesislerini garip bir şekilde kullanıyor gibi görünüyor. Delphi, yığını çok yoğun bir şekilde kullanmak istiyor ve genellikle işlemcinin r0-r3 kayıtlarını geçici değişkenler olarak kullanıyor. Belki de en çılgını, dört adet 1 baytlık yükleme işlemi olarak normal 32 bit tamsayıları yüklemek gibi görünüyor. Delphi daha iyi ARM kodu üretmek nasıl yapılır ve byte by-byte güçlük olmadan Android için yapıyor?

İlk başta bayt byte bayt yüklemesinin byte sırasını big-endian'dan değiştirmek olduğunu düşündüm, ancak durum böyle değildi, gerçekten sadece 4 bit baytlık yükler ile 32 bitlik bir sayı yüklemek. * hizasız bir kelime boyutlu bellek yükü yapmadan 32 bitin tamamı. (bundan kaçınması gerekip gerekmediği başka bir şeydir, bu da her şeyin bir derleyici hatası olmasını ima eder) *

Bu basit işleve bakalım:

function ReadInteger(APInteger : PInteger) : Integer;
begin
  Result := APInteger^;
end;

Optimizasyonlar açık olsa bile, güncelleme paketi 1'e sahip Delphi XE7 ve XE6, bu işlev için aşağıdaki ARM montaj kodunu üretir:

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}

Sadece Delphi'nin ihtiyaç duyduğu talimatları ve bellek erişimlerini sayın. Ve 4 tek baytlık yüklerden 32 bit tam sayı oluşturmak ... İşlevi biraz değiştirir ve işaretçi yerine var parametresi kullanırsam, biraz daha az kıvrımlıdır:

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}

Sökme işlemini buraya dahil etmeyeceğim, ancak iOS için Delphi, işaretçi ve var parametre sürümleri için aynı kodu üretir ve neredeyse ancak Android var parametre sürümü ile tam olarak aynı değildir. Düzenleme: açıklığa kavuşturmak için, bayt bayt yüklemesi yalnızca Android'de yapılır. Ve sadece Android'de, işaretçi ve var parametre sürümleri birbirinden farklıdır. İOS'ta her iki sürüm de tam olarak aynı kodu oluşturur.

Karşılaştırma için, FPC 2.7.1 (Mart 2014'ten itibaren SVN trunk versiyonu) -O2 optimizasyon seviyesi ile işlevi düşünüyor. İşaretçi ve var parametre sürümleri tamamen aynıdır.

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

Ayrıca Android NDK ile birlikte gelen C derleyicisi ile eşdeğer bir C fonksiyonunu test ettim.

int ReadInteger(int *APInteger)
{
    return *APInteger;
}

Ve bu aslında FPC'nin yaptığı aynı şeyi derler:

Disassembly of section .text._Z11ReadIntegerPi:

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

14
Btw içinde Google+ tartışma bu konuda Sam Shaw debug C ++ gösterileri uzun form kod oluşturur olduğu notlar ve sürümde optimize kodu. Delphi her ikisinde de yapıyor. Bundan dolayı LLVM gönderdikleri bayraklarda basit bir hata olabilir ve eğer öyleyse bir hata raporu dosyalamaya değerse, çok yakında düzeltilebilir.
David

9
Oh, tamam, yanlış okudum. Notlikethat'ın dediği gibi, işaretçi yükünün hizalanmamış olacağını (veya hizalamayı garanti edemeyeceğini) ve daha eski ARM platformlarının hizalanmamış yükleri yapamayacağını varsayar gibi görünüyor. Hizalanmamış yüklerin ARMv6'dan beri desteklenmesi gerektiğinden (ARMv5 olduğu varsayılırken) armeabi-v7abunun yerine hedefleme oluşturduğunuzdan armeabiemin olun (bu derleyicide bu tür seçeneklerin olup olmadığından emin olun armeabi). (Gösterilen demontaj, büyük bir değer okuyor gibi görünmüyor, sadece bir kerede bir bayt değeri okuyor.)
mstorsjo

6
Aynı hata gibi görünen RSP-9922'yi buldum .
David

6
Birisi embarcadero.public.delphi.platformspecific.ios haber grubunda, "ARM Derleyici optimizasyonu bozuk mu?" devsuperpage.com/search/…
Side S. Fresh

6
@Johan: Hangi yürütülebilir dosya? Delphi derleyici yürütülebilir içinde bir şekilde pişmiş olduğu izlenimi vardı. Bir deneyin ve sonuçları bize bildirin.
Side S. Fresh

Yanıtlar:


8

Sorunu araştırıyoruz. Kısacası, bir işaretçi tarafından belirtilen Tamsayı'nın olası yanlış hizalamasına (32 sınıra) bağlıdır. Tüm cevapları almak için biraz daha zamana ve buna yönelik bir plana ihtiyacım var.

Marco Cantù, Delphi Geliştiricileri moderatörü

Ayrıca referans Delphi zlib ve zip kütüphaneleri neden 64 bit'in altında bu kadar yavaş? Win64 kitaplıkları optimizasyon olmadan oluşturulmuş olarak gönderilir.


QP Raporunda: Derleyici tarafından üretilen RSP-9922 Kötü ARM kodu, $ O yönergesi yoksayıldı mı? , Marco aşağıdaki açıklamayı ekledi:

Burada birden fazla sorun var:

  • Belirtildiği gibi, optimizasyon ayarları tek tek işlevler için değil, yalnızca tüm birim dosyaları için geçerlidir. Basitçe söylemek gerekirse, optimizasyonun aynı dosyada açılıp kapatılmasının hiçbir etkisi olmayacaktır.
  • Ayrıca, "Hata ayıklama bilgileri" ni etkinleştirmek optimizasyonu kapatır. Bu nedenle, hata ayıklama yapılırken, optimizasyonların açıkça açılmasının bir etkisi olmaz. Sonuç olarak, IDE'deki CPU görünümü, optimize edilmiş kodun demonte görünümünü görüntüleyemez.
  • Üçüncü olarak, hizalanmamış 64 bit veri yüklemek güvenli değildir ve hatalara yol açar, bu nedenle belirli senaryolarda ihtiyaç duyulan ayrı bir 4 baytlık işlemler.

Marco Cantù, Ocak 2015'te "Sorunu araştırıyoruz" notunu yayınladı ve ilgili hata raporu RSP-9922, Ocak 2016'da "Beklendiği gibi Çalışıyor" kararı ile çözüldü ve bir söz var "2 Mart'ta kapatıldı" 2015" . Onların açıklamalarını anlamıyorum.
Side S. Fresh

1
Sorun çözümüne bir yorum ekledim.
Marco Cantù
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.