newton
-Programm mit einem ordentlichen Formel-Compiler zu versehen, hier zunächst
eine Übersicht und etwas Code zum Aufbau eines Stackframes bzw. dem Stack-Layout beim Aufruf einer
Funktion.
AMD64 ABI Linux/GCC
Wird eine Funktion mit mehr als 2 integralen Argumenten aufgerufen, so erwartet sie einen "shadow"-Space von 32 Byte auf dem Stack. Dies ist ein freier Bereich, der vom Aufrufer zur Verfügung gestellt werden muss, damit die aufgerufene Funktion bei Bedarf die Register RCX, RDX, R8 und R9 dorthin auslagern kann.
Bis zu 6 Ganzzahl-Argumente einer Funktion werden in den Registern RDI, RSI, RDX, RCX, R8 und R9 (Argumente von links nach rechts, Register in dieser Reihenfolge) übergeben, zusätzliche Argumente werden auf dem Stack (hinter dem Shadow-Space) abgelegt.
Der integrale Rückgabewert einer Funktion wird im Register RAX gespeichert.
Alle anderen Register (RBX, R10 - R15, etc.) sind Callee-saved, müssen also von der aufgerufenen Funktion gesichert werden.
Beispiel:
long f1(long a, long b, long c, long d, long e, long f, long g, long h);
Register | Argument | |
---|---|---|
R7 | RDI | a |
R6 | RSI | b |
R2 | RDX | c |
R1 | RCX | d |
R8 | e | |
R9 | f |
Stack, wächst nach unten Breite: 64 Bit | |
---|---|
... alter Stack-Frame | |
-- | 32 Byte Shadow- Space |
-- | |
-- | |
-- | |
h | |
g | ← RSP bevor call <f1> ausgeführt wird |
Return Address (old RIP) | |
Frame Pointer (old RBP) | |
lokale Variablen von f1 ... |
Siehe auch x64 General Purpose Registers auf sandpile.org für eine Übersicht über vorhandene Register.
Floating-Point-Argumente werden in den SSE-Registern XMM0 bis XMM7 gespeichert, weitere Argumente landen wie oben auf dem Stack. Eine Floating-Point-Rückgabe erfolgt in XMM0.
Windows benutzt lediglich die Register RCX, RDX, R8 und R9 für Integer-Argumente und XMM0 bis XMM3 für Floating-Point-Werte.
Implementierung
Hier eine C-Funktion, die die AMD64-ABI implementiert und es erlaubt, Funktionen, die eine beliebige Anzahl integraler Argumente der Breite 64 Bit oder kleiner erwarten, aufzurufen.
long call64(void (*func)(), unsigned k, long *args) { const long k6 = k > 6 ? k - 6 : 0; __asm__( "sub $0x20, %%rsp;" "testq $1, %1;" /* stack re-alignment to 16 bytes */ "jz l64_1;" /* (enforced since GCC 4.5) */ "sub $8, %%rsp;" "add $1, %1;" "l64_1:" "cmp $6, %0;" /* if (k <= 6) */ "jbe a64_6;" /* goto a64_6; */ "sub $1, %0;" /* k--; */ "mov %0, %%eax;" "shl $3, %%rax;" /* %rax = k * 8; */ "add %4, %%rax;" /* %rax = args + k; */ "push (%%rax);" /* push args[k]; */ "jmp l64_1;" /* goto l64_1; */ "a64_6: cmp $6, %0; jb a64_5; mov 0x28(%4), %%r9;" "a64_5: cmp $5, %0; jb a64_4; mov 0x20(%4), %%r8;" "a64_4: cmp $4, %0; jb a64_3; mov 0x18(%4), %%rcx;" "a64_3: cmp $3, %0; jb a64_2; mov 0x10(%4), %%rdx;" "a64_2: cmp $2, %0; jb a64_1; mov 0x08(%4), %%rsi;" "a64_1: cmp $1, %0; jb a64_0; mov 0x00(%4), %%rdi;" "a64_0:" "mov %2, %%rax;" "call *%%rax;" /* %rax = (*func)(...) */ "shlq $3, %1;" "add %1, %%rsp;" /* Cleanup stack */ "add $0x20, %%rsp;" /* Remove shadow space */ : "=g"(k), "=g"(k6) /* asm output */ : "m"(func), "0"(k), /* asm input */ "r"(args), "1"(k6) : "rax", "rdi", "rsi", /* asm clobber */ "rdx", "rcx", "r8", "r9"); }