Tausch zweier Variablen
-
Innerhalb C++ (Dev-C++) habe ich folgenden Code für den Tausch zweier globaler Variablen a und b verwendet:
//swap (a,b) __asm("mov _b, %eax"); //AT&T Syntax bei Dev-C++ __asm("mov _a, %ebx"); __asm("mov %eax, _a"); __asm("mov %ebx, _b");
Frage: Wie kann man dies per XCHG schneller abwickeln, man muss dabei doch auch über die Register?
Kann man per Assembler auch auf lokale Variablen in C++ zugreifen. Die befinden sich dort ja im Stack oder auf dem Heap.
-
Erhard Henkes schrieb:
Frage: Wie kann man dies per XCHG schneller abwickeln, man muss dabei doch auch über die Register?
Mindestens über ein Register, das zweite darf ein Speicherzugriff sein. Die Methode wäre dann also (ebenso AT&T):
movl a, %eax xchgl %eax, b xchgl %eax, a
Wenn man den gcc benutzt kann man das noch etwas abkürzen (vom assembelrtext her):
__asm("xchgl %%eax, %1\n\t" : "=a"(a), "=m"(b) : "a"(a));
Mit Glück behält der gcc dann a für die weiterverwendung in eax udn man spart ncoh einen gigantösen Befehl
Kann man per Assembler auch auf lokale Variablen in C++ zugreifen. Die befinden sich dort ja im Stack oder auf dem Heap.
Klar, lokale Variablen liegen auf x86 immer aufm Stack. Unter C/C++ ist es folgendermaßen üblich: auf EBP-4 liegt die erste lokale, EBP-8 die zweite (sofern Typ int) etc. Der Code den ich dafür zum Test genommen habe
#include <stdio.h> int main() { int a = 1; int b = 2; __asm("xchgl %%eax, %1\n\t" : "=a"(a), "=m"(b) : "a"(a)); printf("%i %i", a, b); }
ist z.B. in asm so geworden (AT&T):
main: pushl %ebp movl %esp, %ebp subl $24, %esp andl $-16, %esp movl $0, %eax subl %eax, %esp movl $1, -4(%ebp) movl $2, -8(%ebp) movl -4(%ebp), %eax #APP xchgl %eax, -8(%ebp) #NO_APP movl %eax, -4(%ebp) movl -8(%ebp), %eax movl %eax, 8(%esp) movl -4(%ebp), %eax movl %eax, 4(%esp) movl $.LC0, (%esp) call printf leave ret
Du siehst hier, wie die lokalen Variablen wie -4(%ebp) und -8($bp) (also EBP+4 und EBP+8) angesprochen werden.
NB: Wenn mans optimieren lässt trifft übrigens der erwünschte Effekt ein, dass die Variable a garnicht mehr auf dem Stack rumliegt, sondenr nur noch in eax gehalten wird und der swap so noch kürzer wird:
main: pushl %ebp movl %esp, %ebp subl $24, %esp movl $2, -4(%ebp) movl $1, %eax andl $-16, %esp #APP xchgl %eax, -4(%ebp) #NO_APP movl $.LC0, (%esp) movl -4(%ebp), %ecx movl %eax, 4(%esp) movl %ecx, 8(%esp) call printf movl %ebp, %esp popl %ebp ret
Man kann es sogar so weit treiben, dass beide Variablen im Register bleiben, aber da wirds dann schwer in g rößeren FUnktionen. Generell ists beim gcc imemr die beste Wahl über die sogenannten Constraints (in diesem Fall "=a"(a) z.B.) Auf die Variablen zuzugreifen, dann kann der gcc selbst entscheiden was im Stack ist und was nicht.