2014-10-21 3 views
3

나는 32 비트 델파이 BASM 코드를 64 비트 FPC (Win64를 대상 OS)에 포팅하고 다음 명령은 64 비트 FPC에 컴파일되지 않는 이유를 궁금해하고있다 :왜 LEA 명령어가 컴파일되지 않습니까?

{$IFDEF FPC} 
    {$ASMMODE INTEL} 
{$ENDIF} 

procedure DoesNotCompile; 
asm 
     LEA ECX,[ECX + ESI + $265E5A51] 
end; 

// Error: Asm: 16 or 32 Bit references not supported 

가능한 해결 방법은 다음과 같습니다

procedure Compiles1; 
asm 
     ADD ECX,ESI 
     ADD ECX,$265E5A51 
end; 

procedure Compiles2; 
asm 
     LEA ECX,[RCX + RSI + $265E5A51] 
end; 

Win32 대상에서 32 비트 LEA 명령어가 잘못되었음을 이해하지 못합니다 (32 비트 델파이에서는 정상적으로 컴파일되므로 정확한 CPU 명령어입니다).


최적화 비고 :

64 비트 FPC 2.6.2

{$MODE DELPHI} 
    {$ASMMODE INTEL} 

procedure Test; 
asm 
     LEA  ECX,[RCX + RSI + $265E5A51] 
     NOP 
     LEA  RCX,[RCX + RSI + $265E5A51] 
     NOP 
     ADD  ECX,$265E5A51 
     ADD  ECX,ESI 
     NOP 
end; 

의해 컴파일 된 다음 코드는 다음의 어셈블러 출력 생성

00000000004013F0 4883ec08     sub $0x8,%rsp 
         project1.lpr:10 LEA  ECX,[RCX + RSI + $265E5A51] 
00000000004013F4 8d8c31515a5e26   lea 0x265e5a51(%rcx,%rsi,1),%ecx 
         project1.lpr:11 NOP 
00000000004013FB 90      nop 
         project1.lpr:12 LEA  RCX,[RCX + RSI + $265E5A51] 
00000000004013FC 488d8c31515a5e26   lea 0x265e5a51(%rcx,%rsi,1),%rcx 
         project1.lpr:13 NOP 
0000000000401404 90      nop 
         project1.lpr:14 ADD  ECX,$265E5A51 
0000000000401405 81c1515a5e26    add $0x265e5a51,%ecx 
         project1.lpr:15 ADD  ECX,ESI 
000000000040140B 01f1      add %esi,%ecx 
         project1.lpr:16 NOP 
000000000040140D 90      nop 
         project1.lpr:17 end; 
000000000040140E 4883c408     add $0x8,%rsp 

를 승자 인 (7 바이트 길이) :

LEA  ECX,[RCX + RSI + $265E5A51] 

(모두 64 비트 FPC로 컴파일되지 않는 LEA ECX,[ECX + ESI + $265E5A51] 포함)은 3 바이트입니다.

승자가 가장 빠른 지 확신하지 못합니다.

+1

해결 방법은 더 나은이 (바이트를 저장은)는 0x67로 앞에 덧붙이 물론 – harold

답변

5

나는 FPC 어셈블러에서 버그로 간주합니다 :

는 피연산자 피연산자 요소 좀 더 명확한 구분을 확인하십시오. 여러분이 제시 한 asm 코드는 유효하며, 64 비트 모드에서는 LEA를 32 비트 레지스터와 함께 사용하는 것이 완벽합니다. 인텔 프로세서 문서는 분명히 밝혀졌습니다. Delphi 64 비트 인라인 어셈블러는이 코드를 허용합니다.

 
Project1.dpr.12: DQ $265e5a510e8c8d67 
0000000000424160 678D8C0E515A5E26 lea ecx,[esi+ecx+$265e5a51] 
난에 아주 간단한 벤치마킹을 수행 :이 같이 나오는 델파이 CPU보기에서

DQ $265e5a510e8c8d67 

:

는 조립 손에 코드가 필요합니다를 해결하려면 32 비트와 64 비트 피연산자의 사용을 비교하고 두 개의 ADD를 사용하는 버전을 비교합니다.코드는 다음과 같습니다

 
32 bit 

BenchWithTwoAdds 
Value = -644343429 
Elapsed time = 2615 

BenchWith32bitOperands 
Value = -644343429 
Elapsed time = 3915 

---------------------- 

64 bit 

BenchWithTwoAdds 
Value = -644343429 
Elapsed time = 2612 

BenchWith32bitOperands 
Value = -644343429 
Elapsed time = 3917 

BenchWith64bitOperands 
Value = -644343429 
Elapsed time = 3918 

이에 따라 LEA의 옵션 중 하나를 선택할 수있는 아무것도 볼 수 있듯이 내 인텔 i5-2300에

{$APPTYPE CONSOLE} 

uses 
    System.Diagnostics; 

function BenchWithTwoAdds: Integer; 
asm 
    MOV EDX,ESI 
    XOR EAX,EAX 
    MOV ESI,$98C34 
    MOV ECX,$ffffffff 
@loop: 
    ADD EAX,ESI 
    ADD EAX,$265E5A51 
    DEC ECX 
    CMP ECX,0 
    JNZ @loop 
    MOV ESI,EDX 
end; 

function BenchWith32bitOperands: Integer; 
asm 
    MOV EDX,ESI 
    XOR EAX,EAX 
    MOV ESI,$98C34 
    MOV ECX,$ffffffff 
@loop: 
    LEA EAX,[EAX + ESI + $265E5A51] 
    DEC ECX 
    CMP ECX,0 
    JNZ @loop 
    MOV ESI,EDX 
end; 

{$IFDEF CPUX64} 
function BenchWith64bitOperands: Integer; 
asm 
    MOV EDX,ESI 
    XOR EAX,EAX 
    MOV ESI,$98C34 
    MOV ECX,$ffffffff 
@loop: 
    LEA EAX,[RAX + RSI + $265E5A51] 
    DEC ECX 
    CMP ECX,0 
    JNZ @loop 
    MOV ESI,EDX 
end; 
{$ENDIF} 

var 
    Stopwatch: TStopwatch; 

begin 
{$IFDEF CPUX64} 
    Writeln('64 bit'); 
{$ELSE} 
    Writeln('32 bit'); 
{$ENDIF} 
    Writeln; 

    Writeln('BenchWithTwoAdds'); 
    Stopwatch := TStopwatch.StartNew; 
    Writeln('Value = ', BenchWithTwoAdds); 
    Writeln('Elapsed time = ', Stopwatch.ElapsedMilliseconds); 
    Writeln; 

    Writeln('BenchWith32bitOperands'); 
    Stopwatch := TStopwatch.StartNew; 
    Writeln('Value = ', BenchWith32bitOperands); 
    Writeln('Elapsed time = ', Stopwatch.ElapsedMilliseconds); 
    Writeln; 

{$IFDEF CPUX64} 
    Writeln('BenchWith64bitOperands'); 
    Stopwatch := TStopwatch.StartNew; 
    Writeln('Value = ', BenchWith64bitOperands); 
    Writeln('Elapsed time = ', Stopwatch.ElapsedMilliseconds); 
{$ENDIF} 

    Readln; 
end. 

출력. 시간의 차이는 측정의 다양성 내부에 있습니다. 그러나 ADD을 두 번 사용하는 변형은 손을 놓고 승리합니다.

다른 컴퓨터에서 서로 다른 결과가 나옵니다.

 
64 bit 

BenchWithTwoAdds 
Value = -644343429 
Elapsed time = 3434 

BenchWith32bitOperands 
Value = -644343429 
Elapsed time = 3295 

BenchWith64bitOperands 
Value = -644343429 
Elapsed time = 3279 

그리고 제온 E5-4640 v2의 : 여기 제온 E5530에 출력의

 
64 bit 

BenchWithTwoAdds 
Value = -644343429 
Elapsed time = 4102 

BenchWith32bitOperands 
Value = -644343429 
Elapsed time = 5868 

BenchWith64bitOperands 
Value = -644343429 
Elapsed time = 5868 
+0

버전은 두 개의 ADD를 기반으로합니다. 어느 것이 포괄적으로 이긴다. –

+0

어떤 프로세서에 있습니까? 최근 인텔 하드웨어에서 3 피연산자 'lea'가 처벌됩니다. – gsg

+1

@gsg 해봤습니다. 나는 당신이 현대 하드웨어가 3 개의 경쟁자를 선호하지 않는다고 생각한다. –

2

피연산자 자체의 크기와 별도로 구성 요소의 메모리 피연산자는 기본 크기를 갖습니다. 64 비트 모드에서는 64 비트이므로 특별한 이유가없는 한 메모리 피연산자의 구성 요소에 64 비트 레지스터를 사용해야합니다.

x86 ISA는 접두사 바이트 0x67을 사용하여 주어진 명령어의 크기를 변경할 수 있지만 실제로 그렇게하지 않으려는 것은 분명합니다. 물론 어셈블러에서 지원하지 않을 수도 있습니다.

lea eax, dword ptr [rax + rdx * 4] 

    ^^^ ^^^^^ ^^^     operands: can be any size you like 
        ^^^ ^^^  operand components: usually 64-bit 
+0

의 버그는 아직 아마 여기 정확히 원하는 것일 것입니다. –

+0

@DavidHeffernan 결과가 변경되지 않습니다 (대상은 32 비트 어쨌든)? 그냥 한 바이트의 공간을 차지합니다. AFAIK – harold

+0

@harold 0x67 접두어를 제거하면 다른 명령이 생깁니다. 'lea ecx, [rsi + rcx + $ 265e5a51]' –

관련 문제