2014-04-17 2 views

답변

13

나는이를 컴파일 할 때 디버그 설정에서 생성 된 코드는, 그래서 같다 :

 
    begin 
005A9414 55    push ebp 
005A9415 8BEC    mov ebp,esp 
005A9417 83C4E4   add esp,-$1c 
005A941A 33C9    xor ecx,ecx 
005A941C 894DEC   mov [ebp-$14],ecx 
005A941F 894DE8   mov [ebp-$18],ecx 
005A9422 894DE4   mov [ebp-$1c],ecx 
005A9425 8955F0   mov [ebp-$10],edx 
005A9428 8945F4   mov [ebp-$0c],eax 
005A942B 33C0    xor eax,eax 
005A942D 55    push ebp 
005A942E 6890945A00  push $005a9490 
005A9433 64FF30   push dword ptr fs:[eax] 
005A9436 648920   mov fs:[eax],esp 
    mov X, eax 
005A9439 8945FC   mov [ebp-$04],eax 
    mov Y, edx 
005A943C 8955F8   mov [ebp-$08],edx 

코드가 실행을 시작, eax 참으로 자기의 포인터이다. 그러나 컴파일러는이를 ebp-$0c으로 저장 한 다음 eax을 0으로 선택했습니다. 그건 정말 컴파일러에게 달렸어.

릴리스 설정의 코드는 매우 유사합니다. 컴파일러는 여전히 eax을 0으로 선택합니다. 물론 컴파일러에 의존 할 수는 없습니다.

 
    begin 
005A82A4 55    push ebp 
005A82A5 8BEC    mov ebp,esp 
005A82A7 33C9    xor ecx,ecx 
005A82A9 51    push ecx 
005A82AA 51    push ecx 
005A82AB 51    push ecx 
005A82AC 51    push ecx 
005A82AD 51    push ecx 
005A82AE 33C0    xor eax,eax 
005A82B0 55    push ebp 
005A82B1 6813835A00  push $005a8313 
005A82B6 64FF30   push dword ptr fs:[eax] 
005A82B9 648920   mov fs:[eax],esp 
    mov X, eax 
005A82BC 8945FC   mov [ebp-$04],eax 
    mov Y, edx 
005A82BF 8955F8   mov [ebp-$08],edx 

매개 변수 전달은 함수 실행이 시작될 때 레지스터 및 스택의 상태를 정의합니다. 다음에 어떻게되는지, 함수가 매개 변수를 디코딩하는 방법은 컴파일러에 달려 있습니다. 매개 변수 전달에 사용 된 레지스터 및 스택을 변경하지 않은 상태로 두지 않아도됩니다.

함수 중간에 asm을 삽입하면 eax과 같은 휘발성 레지스터가 특정 값을 가질 것으로 기대할 수 없습니다. 가장 최근에 컴파일러가 무엇이든지 넣었습니다.

함수 실행 초기에 레지스터를 검사하려면 컴파일러가 매개 변수 전달에 사용 된 레지스터를 수정하지 않도록 순수 asm 함수를 사용해야합니다.

var 
    X, Y: Pointer; 
asm 
    mov X, eax 
    mov Y, edx 
    // .... do something with X and Y 
end; 

컴파일러는 함수의 나머지 코드에의 선택이 매우 많이 의존 할 것입니다. 코드의 경우 복잡한 문자열을 ShowMessage으로 전달하면 상당히 큰 전제가됩니다. 대신에이 코드를 고려해 컴파일러 혼자 eax을 떠날만큼이 경우

type 
    TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    private 
    i: Integer; 
    function Sum(j: Integer): Integer; 
    end; 
.... 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
    i := 624; 
    Caption := IntToStr(Sum(42)); 
end; 

function TForm1.Sum(j: Integer): Integer; 
var 
    X: Pointer; 
begin 
    asm 
    mov X, eax 
    end; 
    Result := TForm1(X).i + j; 
end; 

코드는 간단하다. Sum에 대한 최적화 된 릴리스 빌드 코드는 다음과 같습니다

 
    begin 
005A8298 55    push ebp 
005A8299 8BEC    mov ebp,esp 
005A829B 51    push ecx 
    mov X, eax 
005A829C 8945FC   mov [ebp-$04],eax 
    Result := TForm4(X).i + j; 
005A829F 8B45FC   mov eax,[ebp-$04] 
005A82A2 8B80A0030000  mov eax,[eax+$000003a0] 
005A82A8 03C2    add eax,edx 
    end; 
005A82AA 59    pop ecx 
005A82AB 5D    pop ebp 
005A82AC C3    ret 

을 그리고 당신이 코드를 실행하면, 폼의 캡션은 기대 값으로 변경됩니다.


완벽하게 솔직히 말하면, 파스칼 함수 안에 asm 블록으로 배치 된 인라인 어셈블리는별로 유용하지 않습니다. 어셈블리를 작성하는 것은 레지스터와 스택의 상태를 완전히 이해해야한다는 것입니다. 이는 ABI가 정의한 함수의 시작과 끝에서 잘 정의되어 있습니다.

그러나 함수의 중간에서 상태는 컴파일러의 결정에 전적으로 의존합니다. 거기에 asm 블록을 삽입하려면 컴파일러가 내린 결정을 알아야합니다. 또한 컴파일러가 여러분이 내린 결정을 이해할 수 없다는 것을 의미합니다. 이것은 일반적으로 비실용적입니다. 실제로 x64 컴파일러의 경우 Embarcadero는 그러한 인라인 asm 블록을 금지했습니다. 필자는 개인적으로 코드에서 인라인 asm 블록을 사용한 적이 없습니다. 필자가 asm을 쓰는 경우에는 항상 순수한 asm 함수를 작성합니다.

+0

당신의 지식이 의견을 주셔서 감사합니다! – SOUser

0

그냥 SELF의 포인터를 얻을 수있는 푸시/팝을 사용하여 다음과 같이 자유롭게 속성을 사용 :

asm 
     push Self 
     pop edx     //Now, [edx] is the pointer to Self 

     mov ecx, [edx].FItems //ecx = FItems 
     mov eax, [edx].FCount //eax = FCount 
     dec eax    //test zero count! 
     js @Exit    //if count was 0 then exit as -1 
    @Loop:      //and so on... 
     ......