Làm cách nào để ảnh hưởng đến việc tạo mã Delphi XEx cho các mục tiêu Android / ARM?


266

Cập nhật 2017-05-17. Tôi không còn làm việc cho công ty nơi câu hỏi này bắt nguồn và không có quyền truy cập vào Delphi XEx. Trong khi tôi ở đó, vấn đề đã được giải quyết bằng cách di chuyển sang FPC + GCC hỗn hợp (Pascal + C), với nội tại NEON cho một số thói quen tạo ra sự khác biệt. (FPC + GCC cũng rất được khuyến khích vì nó cho phép sử dụng các công cụ tiêu chuẩn, đặc biệt là Valgrind.) Nếu ai đó có thể chứng minh, với các ví dụ đáng tin cậy, làm thế nào họ thực sự có thể tạo mã ARM được tối ưu hóa từ Delphi XEx, tôi rất vui lòng chấp nhận câu trả lời .


Trình biên dịch Delphi của Embarcadero sử dụng phụ trợ LLVM để tạo mã ARM gốc cho các thiết bị Android. Tôi có một lượng lớn mã Pascal mà tôi cần biên dịch vào các ứng dụng Android và tôi muốn biết cách làm cho Delphi tạo mã hiệu quả hơn. Ngay bây giờ, tôi thậm chí không nói về các tính năng nâng cao như tối ưu hóa SIMD tự động, chỉ là về việc sản xuất mã hợp lý. Chắc chắn phải có một cách để truyền tham số cho phía LLVM, hoặc bằng cách nào đó ảnh hưởng đến kết quả? Thông thường, bất kỳ trình biên dịch nào cũng sẽ có nhiều tùy chọn ảnh hưởng đến việc biên dịch và tối ưu hóa mã, nhưng các mục tiêu ARM của Delphi dường như chỉ là "tối ưu hóa bật / tắt" và đó là điều đó.

LLVM được cho là có khả năng tạo ra mã hợp lý chặt chẽ và hợp lý, nhưng có vẻ như Delphi đang sử dụng các cơ sở của nó một cách kỳ lạ. Delphi muốn sử dụng stack rất nhiều và nó thường chỉ sử dụng các thanh ghi r0-r3 của bộ xử lý làm các biến tạm thời. Có lẽ điên rồ nhất trong tất cả, nó dường như đang tải các số nguyên 32 bit bình thường như bốn hoạt động tải 1 byte. Làm cách nào để Delphi tạo mã ARM tốt hơn và không gặp rắc rối theo từng byte mà nó đang tạo cho Android?

Lúc đầu, tôi nghĩ rằng việc tải từng byte là để hoán đổi thứ tự byte từ big-endian, nhưng thực tế không phải vậy, nó thực sự chỉ là tải một số 32 bit với 4 lần tải byte đơn. * Có thể là để tải 32 bit đầy đủ mà không thực hiện tải bộ nhớ có kích thước từ không được phân bổ. (cho dù nó NÊN tránh đó là một điều khác, điều này sẽ gợi ý cho toàn bộ sự việc là lỗi trình biên dịch) *

Hãy xem chức năng đơn giản này:

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

Ngay cả khi tối ưu hóa được bật, Delphi XE7 với gói cập nhật 1, cũng như XE6, tạo ra mã lắp ráp ARM sau cho chức năng đó:

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}

Chỉ cần đếm số lượng hướng dẫn và bộ nhớ truy cập Delphi cần cho điều đó. Và xây dựng một số nguyên 32 bit từ 4 lần tải byte đơn ... Nếu tôi thay đổi hàm một chút và sử dụng tham số var thay vì con trỏ, nó sẽ hơi phức tạp hơn một chút:

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}

Tôi sẽ không bao gồm việc tháo gỡ ở đây, nhưng đối với iOS, Delphi tạo mã giống hệt nhau cho các phiên bản tham số con trỏ và var và chúng gần như không hoàn toàn giống với phiên bản tham số var của Android. Chỉnh sửa: để làm rõ, việc tải từng byte chỉ có trên Android. Và chỉ trên Android, các phiên bản tham số con trỏ và var khác nhau. Trên iOS cả hai phiên bản đều tạo chính xác cùng một mã.

Để so sánh, đây là những gì FPC 2.7.1 (phiên bản trung kế SVN từ tháng 3 năm 2014) nghĩ về chức năng với mức tối ưu hóa -O2. Các phiên bản tham số con trỏ và var hoàn toàn giống nhau.

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

Tôi cũng đã thử nghiệm một chức năng C tương đương với trình biên dịch C đi kèm với NDK của Android.

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

Và điều này tổng hợp thành cơ bản giống như điều FPC đã thực hiện:

Disassembly of section .text._Z11ReadIntegerPi:

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

14
Btw trong cuộc thảo luận trên Google+ về điều này, Sam Shaw lưu ý rằng C ++ hiển thị mã dạng dài trong các bản dựng gỡ lỗi và mã được tối ưu hóa khi phát hành. Trong đó Delphi làm điều đó trong cả hai. Vì vậy, đó có thể là một lỗi đơn giản trong các cờ họ đang gửi LLVM và nếu vậy thì một báo cáo lỗi rất đáng để nộp, nó có thể được sửa chữa khá sớm.
David

9
Ồ, ok, tôi đọc sai. Sau đó, như Notlikethat đã nói, có vẻ như nó giả định rằng tải con trỏ sẽ không được phân bổ (hoặc không thể đảm bảo căn chỉnh) và các nền tảng ARM cũ hơn không nhất thiết phải thực hiện tải không được phân bổ. Hãy chắc chắn rằng bạn đã xây dựng tính năng nhắm mục tiêu armeabi-v7athay vì armeabi(không chắc chắn nếu có các tùy chọn như vậy trong trình biên dịch này), vì tải không được phân bổ sẽ được hỗ trợ kể từ ARMv6 (trong khi armeabigiả sử ARMv5). (Việc phân tách được hiển thị không giống như nó đọc một giá trị lớn, nó chỉ đọc một giá trị endian nhỏ một byte mỗi lần.)
mstorsjo

6
Tôi tìm thấy RSP-9922 có vẻ là lỗi tương tự.
David

6
Ai đó đã hỏi về việc tối ưu hóa bị phá vỡ giữa XE4 và XE5, trong nhóm tin tức embarcadero.public.delphi.pl platformspecific.ios, "Tối ưu hóa trình biên dịch ARM bị hỏng?" devsuperpage.com/search/ từ
Side S. Fresh

6
@Johan: cái gì thực thi được? Tôi có ấn tượng rằng bằng cách nào đó nó đã được nướng trong trình biên dịch của Delphi. Hãy thử và cho chúng tôi biết kết quả.
Side S. Fresh

Câu trả lời:


8

Chúng tôi đang điều tra vấn đề. Nói tóm lại, nó phụ thuộc vào khả năng căn chỉnh sai tiềm năng (đến 32 ranh giới) của Số nguyên được tham chiếu bởi một con trỏ. Cần thêm một chút thời gian để có tất cả các câu trả lời ... và một kế hoạch để giải quyết vấn đề này.

Marco Cantù, người điều hành trên Delphi Developers

Cũng tham khảo Tại sao các thư viện Delphi zlib và zip quá chậm dưới 64 bit? vì các thư viện Win64 được vận chuyển được xây dựng mà không cần tối ưu hóa.


Trong Báo cáo QP: RSP-9922 Mã ARM xấu do trình biên dịch tạo ra, lệnh $ O bị bỏ qua? , Marco thêm lời giải thích sau:

Có nhiều vấn đề ở đây:

  • Như đã chỉ ra, cài đặt tối ưu hóa chỉ áp dụng cho toàn bộ tệp đơn vị và không áp dụng cho các chức năng riêng lẻ. Nói một cách đơn giản, bật và tắt tối ưu hóa trong cùng một tệp sẽ không có hiệu lực.
  • Hơn nữa, chỉ cần bật "Thông tin gỡ lỗi" sẽ tắt tối ưu hóa. Do đó, khi một người đang gỡ lỗi, việc bật tối ưu hóa một cách rõ ràng sẽ không có hiệu lực. Do đó, chế độ xem CPU trong IDE sẽ không thể hiển thị chế độ xem được phân tách của mã được tối ưu hóa.
  • Thứ ba, tải dữ liệu 64 bit không liên kết là không an toàn và không gây ra lỗi, do đó, các hoạt động 4 byte riêng biệt cần thiết trong các tình huống đã cho.

Marco Cantù đã đăng thông báo "Chúng tôi đang điều tra vấn đề" vào tháng 1 năm 2015 và báo cáo lỗi liên quan RSP-9922 đã được đánh dấu giải quyết với độ phân giải "Hoạt động như mong đợi" vào tháng 1 năm 2016 và có một đề cập "vấn đề nội bộ đã đóng vào ngày 2 tháng 3, 2015 ". Tôi không hiểu lời giải thích của họ.
Side S. Fresh

1
Tôi đã thêm một bình luận trong giải quyết vấn đề.
Marco Cantù
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.