2017-05-17 업데이트 이 질문이 시작된 회사에서는 더 이상 일하지 않으며 Delphi XEx에 액세스 할 수 없습니다. 내가 거기에 있었을 때 문제를 해결하기 위해 NEON 내장 함수를 사용하여 혼합 FPC + GCC (Pascal + C)로 마이그레이션하여 문제를 해결했습니다. (FPC + GCC는 표준 도구, 특히 Valgrind를 사용할 수 있기 때문에 강력하게 권장됩니다.) 누군가가 믿을만한 예제를 통해 실제로 Delphi XEx에서 최적화 된 ARM 코드를 생성 할 수있는 방법을 시연 할 수 있다면 그 대답을 기꺼이 받아들입니다 .
Embarcadero의 Delphi 컴파일러는 LLVM 백엔드를 사용하여 Android 디바이스 용 기본 ARM 코드를 생성합니다. 안드로이드 응용 프로그램으로 컴파일 해야하는 많은 양의 파스칼 코드가 있으며 델파이가보다 효율적인 코드를 생성하는 방법을 알고 싶습니다. 지금은 자동 SIMD 최적화와 같은 고급 기능에 대해 이야기하지 않고 합리적인 코드를 생성하는 것에 대해서만 이야기하고 있습니다. 분명히 LLVM 측에 매개 변수를 전달하는 방법이 있어야합니까, 아니면 어떻게 든 결과에 영향을 줍니까? 일반적으로 모든 컴파일러에는 코드 컴파일 및 최적화에 영향을주는 많은 옵션이 있지만 Delphi의 ARM 대상은 "최적화 켜기 / 끄기"인 것 같습니다.
LLVM은 상당히 타이트하고 현명한 코드를 생성 할 수 있어야하지만 델파이는 그 기능을 이상한 방식으로 사용하고있는 것 같습니다. 델파이는 스택을 매우 많이 사용하기를 원하며 일반적으로 프로세서의 레지스터 r0-r3 만 임시 변수로 사용합니다. 아마도 가장 열악한 것은 아마도 일반적인 32 비트 정수를 4 개의 1 바이트로드 작업으로로드하는 것 같습니다. 델파이가 더 나은 ARM 코드를 생성하고 바이트 단위 번거 로움없이 안드로이드를 만드는 방법?
처음에는 바이트 단위 로딩이 빅 엔디안에서 바이트 순서를 교체하는 것이라고 생각했지만 실제로는 그렇지 않았습니다. 실제로 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에서만 가능합니다. 그리고 안드로이드에서만 포인터와 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
). (표시된 디스 어셈블리는 bigendian 값을 읽는 것처럼 보이지 않으며 한 번에 1 바이트