2014-03-19 6 views
5

ich diese C-Funktion zusammengestellt:Warum gibt es drei wichtige Anweisungen für diesen IA32-Assemblercode?

int calc(int x, int y, int z) { 
    return x + 3*y + 19*z; 
} 

Und ich habe dies in calc.s, und ich bin mit Anmerkungen versehen, was geschieht:

 .file "calc.c" 
     .text 
.globl calc 
     .type calc, @function 
calc: 
     pushl %ebp     //Save paramaters 
     movl %esp, %ebp    //Move stack pointer into %ebp 
     movl 12(%ebp), %eax   //Move y into %eax 
     movl 16(%ebp), %ecx   //Move z into %ecx 
     leal (%eax,%eax,2), %eax  //%eax = 3*y 
     addl 8(%ebp), %eax   //%eax = x+3y 
     leal (%ecx,%ecx,8), %edx  // ? 
     leal (%ecx,%edx,2), %edx  // ? 
     addl %edx, %eax    //%eax = (x+3*y)+(19*z) 
     popl %ebp     //Pop the previous pointer 
     ret 
     .size calc, .-calc 
     .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3" 
     .section  .note.GNU-stack,"",@progbits 

Ich verstehe die letzten zwei leal Anweisungen alles auf. Warum benötigen Sie zwei leal Anweisungen für 19 * z, während 3 * y in einer Anweisung ausgeführt wird.

+0

See Die Antwort von Sergey - lea kann nur mit 2, 4 oder 8 multipliziert werden. –

+0

Die allgemeine Antwort ist, dass LEA-Anweisungen wie Multiply-and-Add-Befehle aus kleinen Konstanten 1,2,4,8 agieren. Indem man sie verwendet, kann man in einigen Maschinenbefehlen mit verschiedenen Werten multiplizieren, und das ist schneller als mit einer echten Multiplikationsanweisung. –

Antwort

7

leal ist eine Möglichkeit, eine Multiplikation mit einer kleinen Konstante zu einem billigen durchzuführen, wenn die Konstante eine Potenz von zwei plus eins ist. Die Idee ist, dass leal ohne Offset gleich "Reg1 = Reg2 + Reg3 * Scale" ist. Wenn Reg2 und Reg3 übereinstimmen passieren, das heißt „Reg1 = Reg2 * (Skala + 1).

leal unterstützt nur Skalierungsfaktoren bis zu 8, so um 19 zu multiplizieren, zwei müssen.

Die Wirkung von

leal (%eax,%eax,2), %eax 

ist:

eax = eax + eax*2 

, die Multiplikation mit drei zu sagen ist

. 10

Die zweite zwei leal s zusammen Multiplikation durchführen, indem 19:

leal (%ecx,%ecx,8), %edx  // edx = ecx+ecx*8 
leal (%ecx,%edx,2), %edx  // edx = ecx+edx*2 (but edx is already z*9) 
+0

Macht jetzt vollkommen Sinn, danke – MeesterMarcus

4
leal (%ecx,%ecx,8), %edx # edx = ecx + 8*ecx = 9*ecx = 9 * z 
leal (%ecx,%edx,2), %edx 
# edx = ecx + 2*edx = ecx + 2 * (ecx + 8*ecx) = z + 2 * 9 * z = 19 * z 

Der Grund dafür, dass der lea Befehl verwendet hinzufügen und bitshifts und ist schneller als mul für Integer-Multiplikation verwendet wird. Lea beschränkt sich jedoch auf Multiplikationsfaktoren von 1, 2, 4 und 8 - also zwei Anweisungen.

+1

Dies beantwortet die andere Frage "Wofür wird' LEA' verwendet? "; Dies beantwortet nicht die Frage von OP "Warum brauchen Sie zwei Anweisungen?" – anatolyg

2

lea dient einem doppelten Zweck ist Adressen zu berechnen, aber es kann auch mit einigen Einschränkungen für die Arithmetik verwendet werden, wie Sie mit Ihrem Code zu beobachten. Zwei Anrufe werden benötigt, weil der skalare Multiplikator von lea ist limited to 1, 2, 4 or 8 was bedeutet, Ihre Multiplikation zu erhalten, indem 19 erfordert zwei Anrufe lea:

[...] Der Skalar Multiplikator beschränkt sich auf konstante Werte 1, 2, 4 oder 8 für Byte-, Wort-, Doppelwort- oder Vierfachwort-Offsets. Das allein ermöglicht eine Multiplikation eines allgemeinen Registers durch konstante Werte 2, 3, 4, 5, 8 und 9, [...]

so in Ihrem Fall haben Sie:

leal (%ecx,%ecx,8), %edx  // edx = ecx + ecx*8 which is z*8 + z = z*9 
leal (%ecx,%edx,2), %edx  // edx = ecx + edx*2 which gives us (z*9)*2 + z 
           // for a total of 19z 
Verwandte Themen