Volací konvence
Způsob jakým jsou předávany argumenty a návratové hodnoty funkcí není u procesorů x86 pevně určen a jedná se o konvence definované v době překladu. Můžeme si tedy definovat ,,vlastní konvence''. Situaci komplikuje překladač (Visual Studio), který automaticky vytváří prolog a epilog funkce a mj. přidává ladící informace. Tento problém lze, ale obejít následovně.
void foo() { // vloží prolog
_asm {
// kod vyuzivajici nase volaci konvence
mov esp, ebp; // nami vytvoreny epilog
pop ebp;
ret;
//
// nami definovane funkce pouzivajici nami zvolene volaci konvence
//
}
} // vloží epilog, který se již neprovede
Příklad
void foo() { // vloží prolog
_asm {
// volani funkce s konvenci CDECL
push dword ptr 10
call incf1
add esp, 4
// volani funkce s konvenci FASTCALL
mov ecx, 10
call incf2 // jde pouzit i incf3
mov esp, ebp; // nami vytvoreny epilog
pop ebp;
ret;
//
// nami definovane funkce pouzivajici ruzne volaci konvence
//
// funkce jednoho argumentu, ktere vraci hodnotu o jedna vetsi
// pouzita konvence CDECL
incf1:
push ebp; // prolog funkce
mov ebp, esp;
mov eax, [ebp + 8];
inc eax;
mov esp, ebp; // epilog funkce
pop ebp;
ret;
// funkce jednoho argumentu, ktere vraci hodnotu o jedna vetsi
// pouzita konvence FASTCALL (argument je predan pomoci registru ecx)
incf2:
push ebp; // prolog funkce
mov ebp, esp;
mov eax, ecx
inc eax
mov esp, ebp; // epilog funkce
pop ebp;
ret;
// funkce jednoho argumentu, ktere vraci hodnotu o jedna vetsi
// pouzita konvence FASTCALL (argument je predan pomoci registru ecx)
// optimalizavana verze -- vsimnete si, ze v tomto pripade neni potreba prolog ani epilog
incf3:
mov eax, ecx
inc eax
ret;
}
} // vloží epilog, který se již neprovede
Poznámka
Použitou volací konvenci lze pro jednotlivé funkce určit pomocí direktiv překladače, např. __fastcall, __cdecl, atd. V průběhu tohoto cvičení je ale nepoužívejte.
Úkoly
- S použitím výše popsaného postupu napište funkci
int min1(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte zásobník. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkci
int min2(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte registr ecx a zásobník. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkci
int min3(int a, int b), která vrací nejmenší hodnotu ze dvou. K předání argumentů použijte registry ecx a edx. Tuto funkci zavolejte. - S použitím výše popsaného postupu napište funkce počítající rekurzivně faktoriál zadaného argumentu jednou s použitím konvence CDECL a jednou s použitím fastcall. Porovnejte rychlost těchto funkcí.
- S použitím výše popsaného postupu napište funkce počítající rekurzivně fibonacciho číslo zadaného argumentu jednou s použitím konvence CDECL a jednou s použitím fastcall. Porovnejte rychlost těchto funkcí.