Як вплинути на генерацію коду Delphi XEx для цілей Android / ARM?


266

Оновлення 2017-05-17. Я більше не працюю в компанії, де виникло це питання, і не маю доступу до Delphi XEx. Поки я там був, проблему було вирішено шляхом переходу на змішаний FPC + GCC (Pascal + C), з нетиповими елементами NEON для деяких процедур, де це змінило значення. (FPC + GCC настійно рекомендується ще й тому, що він дозволяє використовувати стандартні інструменти, зокрема Valgrind.) Якщо хтось може продемонструвати, з надійними прикладами, як вони насправді здатні виробляти оптимізований код ARM від Delphi XEx, я радий прийняти відповідь .


Компілятори Delphi Embarcadero використовують вихідний LLVM для створення нативного ARM-коду для пристроїв Android. У мене є велика кількість коду Pascal, який мені потрібно зібрати в додатки для Android, і я хотів би знати, як зробити Delphi генерувати більш ефективний код. Зараз я навіть не говорю про розширені функції, такі як автоматична оптимізація SIMD, просто про створення розумного коду. Напевно, повинен бути спосіб передати параметри стороні LLVM чи якимось чином вплинути на результат? Зазвичай будь-який компілятор матиме багато варіантів впливу на компіляцію та оптимізацію коду, але цілі ARM Delphi здаються просто "оптимізацією вмикання / виключення", і все.

LLVM, як передбачається, здатний створювати досить чіткий та розумний код, але, схоже, Delphi використовує свої засоби дивно. Delphi хоче дуже стек використовувати стек, і він, як правило, використовує лише регістри процесора r0-r3 як тимчасові змінні. Мабуть, самий божевільний з усіх, здається, завантажує нормальні 32-бітові цілі числа, як чотири 1-байтові операції навантаження. Як змусити Delphi виробляти кращий код ARM, і без бай-байтових клопотів це робить для Android?

Спочатку я подумав, що завантаження байтів байтом призначене для заміни порядку байтів з big-endian, але це було не так, це дійсно просто завантаження 32-бітного числа з 4 однобайтовими навантаженнями. * Це може бути завантаження повних 32 біт, не роблячи завантаження пам'яті розміром у розмірі слова. (чи варто ВІДХОДИТИ це інша річ, яка натякає на всю помилку компілятора) *

Давайте розглянемо цю просту функцію:

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

Навіть із увімкненими оптимізаціями, Delphi XE7 з пакетом оновлення 1, а також 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. І побудова 32-бітного цілого числа з 4 однобайтових навантажень ... Якщо я трохи змінити функцію і використати параметр 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, і вони майже, але не зовсім такі, як версії для параметра var для Android. Редагувати: для уточнення завантаження байта за байтом відбувається лише на Android. І лише на Android версії вказівників та параметрів var відрізняються одна від одної. На iOS обидві версії генерують абсолютно однаковий код.

Для порівняння, ось що FPC 2.7.1 (версія магістралі SVN від березня 2014 року) думає про функцію з оптимізаційним рівнем -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

Я також перевірив еквівалентну функцію C за допомогою компілятора C, який поставляється з Android NDK.

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

І це компілює фактично те саме, що зробив FPC:

Disassembly of section .text._Z11ReadIntegerPi:

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

14
Btw в дискусії Google+ про це, Сем Шоу зазначає, що C ++ показує код довгострокової форми у складах налагодження та оптимізований код у випуску. Wheres Delphi робить це в обох. Тож із цього могло б бути простою помилкою у прапорах, які вони надсилають LLVM, і якщо так, повідомлення про помилку дуже варте подання, воно може виправитись зовсім скоро.
Девід

9
О, гаразд, я неправильно прочитав. Тоді, як сказав Notlikethat, це здається, що він передбачає, що навантаження вказівника буде нерівномірною (або не може гарантувати вирівнювання), а старші платформи ARM не можуть обов'язково виконувати нерівномірні навантаження. Переконайтеся, що у вас встановлено націлювання armeabi-v7aзамість armeabi(не впевнений, чи є такі параметри в цьому компіляторі), оскільки нерівномірні навантаження повинні підтримуватися з ARMv6 (при цьому armeabiпередбачається ARMv5). (Показана розборка не схожа на те, що вона читає бігендіальне значення, вона просто читає трохи ендіанське значення один байт за один раз.)
mstorsjo

6
Я знайшов RSP-9922, який, здається, цей самий помилка.
Давид

6
Хтось запитав про порушення оптимізації між XE4 та XE5 в групі новин embarcadero.public.delphi.platformspecific.ios, "Оптимізація ARM-компілятора зламана?" devsuperpage.com/search/…
Side S. Fresh

6
@Johan: що це виконується? У мене склалося враження, що воно якось запечене у виконанні компілятора Delphi. Спробуйте, і дайте нам знати результати.
Сіде С. Фреш

Відповіді:


8

Ми досліджуємо це питання. Коротше кажучи, це залежить від потенційного неправильного вирівнювання (до 32 меж) цілого числа, на яке посилається вказівник. Потрібно трохи більше часу, щоб мати всі відповіді ... і план вирішення цього питання.

Марко Кантù, модератор розробників Delphi

Також довідка Чому бібліотеки Delphi zlib та zip настільки повільні під 64-бітним? оскільки бібліотеки Win64 поставляються без оптимізації.


У звіті QP: RSP-9922 Неправильний код ARM, створений компілятором, директива $ O ігнорується? , Марко додав наступне пояснення:

Тут є кілька питань:

  • Як зазначено, налаштування оптимізації застосовуються лише до файлів цілого блоку, а не до окремих функцій. Простіше кажучи, включення та вимкнення оптимізації в одному файлі не матиме ефекту.
  • Крім того, просто включення "Інформація про налагодження" вимикає оптимізацію. Таким чином, при налагодженні явне включення оптимізації не матиме ефекту. Отже, подання процесора в IDE не зможе відобразити розібраний вигляд оптимізованого коду.
  • По-третє, завантаження 64-бітових даних, що не вирівнюються, не є безпечним і призводить до помилок, отже, окремі 4-байтові операції, необхідні в заданих сценаріях.

У січні 2015 року Марко Канто розмістив цю замітку "Ми досліджуємо проблему", і відповідний звіт про помилки RSP-9922 був відзначений вирішеним резолюцією "Працює як очікувалося" в січні 2016 року, і згадка "внутрішня проблема закрита 2 березня, 2015 ». Я не розумію їх пояснень.
Сіде С. Свіжий

1
Я додав коментар до вирішення проблеми.
Марко Кантù
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.