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 pomocí specifického modifikátoru.

__declspec(naked) int foo() {  // zamezí vložení prologu a epilogu
    
    _asm {
       mov eax, 10
       ret;                     // je potreba uvest instrukci ret
    }
}     

Příklady

// funkce jednoho argumentu, ktera vraci hodnotu o jedna vetsi
// pouzita konvence CDECL
__declspec(naked) int incf1(int arg) {
   _asm {
       push ebp;		// prolog funkce
       mov ebp, esp;

       mov eax, [ebp + 8];
       inc eax;

       mov esp, ebp;		// epilog funkce
       pop ebp;
       ret;
   }
}

// funkce jednoho argumentu, ktera vraci hodnotu o jedna vetsi
// pouzita konvence FASTCALL (argument je predan pomoci registru ecx)
__declspec(naked) int incf2(int arg) {
    _asm {
       push ebp;		// prolog funkce
       mov ebp, esp;

       mov eax, ecx
       inc eax

       mov esp, ebp;		// epilog funkce
       pop ebp;
       ret;
    }
}

// funkce jednoho argumentu, ktera 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
// a staci jen instrukce ret
__declspec(naked) int incf3(int arg) {
    _asm {
       mov eax, ecx
       inc eax

       ret;
    }
}
// priklady volani funkci s ruznymi konvencemi
int foo() {  
    
    _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
    }
}


Poznámka

Použitou volací konvenci lze pro jednotlivé funkce určit pomocí specifických modifikátorů, např. __fastcall, __cdecl, atd. V průběhu tohoto cvičení je ale nepoužívejte.

Úkoly

  1. 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.
  2. 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.
  3. 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.
  4. 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. Tyto funkce zavolejte a výsledek vypište na standardní výstup.
  5. 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.

Pro získání bodového hodnocení je potřeba splnit úkoly 1, 2, 3 a 4.


Last update on 12. 4. 2017 13:12
Powered by Schemik.

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