2014-04-17 8 views

Antwort

13

Der Code generiert, wenn ich das Kompilieren unter Debug-Einstellungen ist, etwa so:

 
    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 

Wenn der Code ausgeführt wird, wird in der Tat eax der Zeiger selbst ist. Aber der Compiler hat entschieden, es zu ebp-$0c zu speichern und dann eax auf Null zu setzen. Das liegt wirklich am Compiler.

Der Code unter Freigabeeinstellungen ist ziemlich ähnlich. Der Compiler wählt weiterhin eax. Natürlich können Sie sich nicht darauf verlassen, dass der Compiler das tut. Denken Sie daran,

 
    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 

, dass die Parameterübergabe, die den Zustand der Register definiert und stapeln, wenn die Funktion der Ausführung beginnt. Was als nächstes passiert, wie die Funktion die Parameter dekodiert, liegt beim Compiler. Es ist nicht verpflichtet, die Register und den Stack, die für die Parameterübergabe verwendet wurden, unberührt zu lassen. Wenn Sie Asum in die Mitte einer Funktion injizieren, können Sie nicht erwarten, dass die flüchtigen Register wie eax bestimmte Werte haben. Sie werden halten, was immer der Compiler gerade in sie geschrieben hat.

Wenn Sie die Register am Anfang der Ausführung der Funktion untersuchen möchten, müssen Sie eine reine asm-Funktion verwenden, um sicherzustellen, dass der Compiler die Register ändern zu vermeiden, die für die Parameterübergabe verwendet wurden:

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

der Compiler wird seine Entscheidungen sehr stark abhängig von dem Code in dem Rest der Funktion machen. Für Ihren Code verursacht die Komplexität des Zusammenfügens der Zeichenfolge, die an übergeben wird, eine ziemlich große Präambel. Betrachten Sie stattdessen diesen Code:

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; 

In diesem Fall wird der Code einfach genug für den Compiler eax allein zu lassen. Der optimierte Release-Build-Code für Sum ist:

 
    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 

Und wenn Sie den Code ausführen, wird die Form der Beschriftung auf den erwarteten Wert geändert.


Um ehrlich, Inline-Assembler, als asm-Block in einer Pascal-Funktion gesetzt zu werden, ist nicht sehr nützlich. Die Sache mit dem Schreiben von Assembly ist, dass Sie den Zustand der Register und des Stacks vollständig verstehen müssen. das ist am Anfang und am Ende einer Funktion definiert, die durch die ABI definiert wird.

Aber in der Mitte einer Funktion hängt dieser Zustand vollständig von den Entscheidungen des Compilers ab. Das Eingeben von asm-Blöcken dort erfordert, dass Sie die Entscheidungen kennen, die der Compiler getroffen hat. Es bedeutet auch, dass der Compiler die von Ihnen getroffenen Entscheidungen nicht verstehen kann. Dies ist normalerweise unpraktisch. Tatsächlich verbot Embarcadero für den x64-Compiler solche Inline-asm-Blöcke. Ich persönlich habe nie einen Inline-Asm-Block in meinem Code verwendet. Wenn ich asm schreibe, schreibe ich immer reine asm-Funktionen.

+0

Sie für Ihre sachkundige Kommentare Dank! – SOUser

0

einfach die Push/Pop verwenden Sie den Zeiger von SELF zu bekommen, und Eigenschaften dann frei verwenden, wie folgt aus:

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... 
     ...... 
Verwandte Themen