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í.

Last update on 26. 10. 2012 15:47
Powered by Schemik.

© Petr Krajča, 2010, 2012
petr.krajca (at) upol.cz