mov/add vs lea



  • value = val1 + val2 + val3;
    

    Warum wird da lea verwendet und nicht zweimal add?

    00401326    mov    eax,DWORD PTR [ebp+0xc]   <--- move argument 2 to EAX
    00401329    mov    edx,DWORD PTR [ebp+0x8]   <--- move argument 1 to EDX
    0040132C    lea    eax,[edx+eax*1]           <--- load EDX plus EAX to EAX (LEA = Load Effective Address)
    0040132F    add    eax,DWORD PTR [ebp+0x10]  <--- add  argument 3 to EAX
    00401332    mov    DWORD PTR [ebp-0x4],eax   <--- move EAX to local variable 1
    

    Sind meine Kommentare so ok?

    Sie auch Intel Optimization Manual, 3.5.1.3 Using LEA



  • LEA spart sich den Zugriff auf die flags -> weniger µOps.



  • In einem guten Kommentar steht nie nie nie, was man sehen kann.



  • LEA spart sich den Zugriff auf die flags -> weniger µOps.

    ... und warum nur einmal? warum das zweite mal add?



  • In einem guten Kommentar steht nie nie nie, was man sehen kann.

    argument n kann man aber nicht so einfach erkennen.



  • Erhard Henkes schrieb:

    LEA spart sich den Zugriff auf die flags -> weniger µOps.

    ... und warum nur einmal? warum das zweite mal add?

    weil ADD auf den Speicher zugreifen kann (argument 3) - LEA nicht.



  • Erhard Henkes schrieb:

    Sind meine Kommentare so ok?

    Man bräuchte keine Kommentare, wenn man den Code gleich in AT&T-Syntax ausgeben würde .)



  • weil ADD auf den Speicher zugreifen kann (argument 3) - LEA nicht.

    Das Argument sticht nicht, weil der Vorgang gleich ist, man hätte auch schon beim ersten mal add mit speicher machen können anstelle erst nach EDX und anschließend mit LEA addieren.

    Wo findet eigentlich das statt? [edx+eax*1] wieso *1 ?



  • Man bräuchte keine Kommentare, wenn man den Code gleich in AT&T-Syntax ausgeben würde .)

    Das ist Gewohnheitssache. Die Syntax ist mein geringstes Problem momentan. 😉



  • Erhard Henkes schrieb:

    [edx+eax*1] wieso *1 ?

    Wahrscheinlich, um zu betonen, dass eax in diesem Fall Index-Register ist. Das muss (sollte) man ja bei der Intel-Syntax wahrscheinlich explizit machen...



  • [edx+eax*1] wieso *1 ?

    Nennt sich Skalierung bzw. Skalierungsfaktor. Davon gibt es *1, *2, *4, *8. (Ein Displacement passt auch noch rein.)

    Damit wird das jeweilige Indexregister multipliziert. Sinnvoll bei Feldzugriffen. Finde gerade keinen Link dazu. 😞

    "[edx + eax*4 - 8]" vs. "-8(%edx, %eax, 4)"
    

    Soweit zur Syntax. 🙂



  • merker schrieb:

    ... Soweit zur Syntax.

    Das ist richtig, aber wie würde man in der Intel kompatiblen Syntax folgende Befehle eingeben:

    movl $42, (%eax)
        movl $42, (, %eax)
    

    eax-Register einmal als Basisregister und einmal als Indexregister 😕



  • mov [eax], 42       ; C6 00 2A
    mov [eax*1], 42     ; C6 00 2A
    

    Aber korrekterweise müsste Skalierung *1 bei einem Index- ohne Basisregister via SIB zusammengebaut werden:

    mov [eax*1], 42               ; C6 04 05 00 00 00 00 2A
    mov [eax*1+000000000h], 42    ; C6 04 05 00 00 00 00 2A
    

    👍



  • Hast du dazu einen Assembler benutzt oder manuell assembliert 😕
    Nach den Kommentaren in diesem Code zu urteilen

    merker schrieb:

    mov [eax], 42       ; C6 00 2A
    mov [eax*1], 42     ; C6 00 2A
    

    stimmt was nicht... beide Befehle erzeugen gleichen Code C6 00 2A...
    Der zweite Codeausschnitt scheint richtig zu sein. Ich hab es mit dem GNU as überprüft 😉
    Aus

    .section .text
    myfunc:
        movl $42, (%eax)
        movl $42, (, %eax)
        ret
    

    wird

    00000000 <myfunc>:
       0:	c7 00 2a 00 00 00    	movl   $0x2a,(%eax)
       6:	c7 04 05 00 00 00 00 	movl   $0x2a,0x0(,%eax,1)
       d:	2a 00 00 00 
      11:	c3                   	ret
    

    Übrigens wollte ich die Konstante 42 als 32-bit Konstante. Als 8-Bit Konstante (wie in deinem Beispiel):

    .section .text
    myfunc:
        movb $42, (%eax)
        movb $42, (, %eax)
        ret
    

    disassembliert:

    00000000 <myfunc>:
       0:	c6 00 2a             	movb   $0x2a,(%eax)
       3:	c6 04 05 00 00 00 00 	movb   $0x2a,0x0(,%eax,1)
       a:	2a 
       b:	c3                   	ret
    


  • Mein (Intel-Syntax-)Assembler hält ein Indexregister mit Skalierung *1 ohne Basisregister für sinnfrei. Darum der gleiche Code im ersten Beispiel.

    Es aber via Syntax trotzdem zu erzwingen, könnte auch sinnfrei sein. 🙂



  • Deswegen sage ich, die AT&T Syntax ist am besten durchdacht, siehe hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-275053-and-start-is-40.html



  • movb   $0x2a,(%eax)        ; c6 00 2a
    movb   $0x2a,0x0(,%eax,1)  ; c6 04 05 00 00 00 00 2a
    

    Wußte hier der Assembler nicht, daß beide Befehle die gleiche Wirkung haben?

    Oder wurde das durch die Syntax erzwungen? 🙂



  • Das wurde durch die Syntax erzwungen. Ich bin der Meinung, auch wenn beide Befehle gleiche Wirkung haben, darf der verwendete Assembler diesbezüglich keine Entscheidungen treffen. Der Programmierer entscheidet sich für die eine oder andere Variante und der Assembler muss diese "gehorsam" akzeptieren und entsprechend übersetzen. Wenn der Assembler trotzdem solche Entscheidungen trifft, dann ist das eine nicht zu vernachlässigende Limitierung. Das würde ja bedeuten, der Programmierer denkt, er habe mit dem Assembler volle Kontrolle usw., nutzt aber nur eine Teilmenge von dem, was die CPU wirklich kann...



  • Ein klares Statement von Dir. Woraus aber folgt, daß sich ein ATT-Syntax-Assembler niemals weiterentwickeln kann (oder soll). Neue Befehle aufnehmen, das wars dann schon.



  • Ja, der GNU Assembler hat nur eine rudimentäre Unterstützung für Makros, hat ein Paar bekannte Bugs hinsichtlich FPU Operationen, die wahrscheinlich niemals "gefixt" werden wegen der Abhängigkeit zu älteren Compilern und wenn ich es als Laie beurteilen kann, die Weiterentwicklung kann nur mit der Aufnahme von neuen Befehlen stattfinden.
    Aber was wären die "Features" von anderen Assemblern, bei denen man sagen würde, sie wären "weiterentwickelter"...?


Anmelden zum Antworten