0

I am working on some x86 assembly program analysis tasks, and I am trying to figure out the fastcall behavior.

Although on 32-bit x86 platform, the stack is defined to pass the function parameters in the calling conversion. However, I observed that many function calls are indeed leveraging two registers, eax and edx to pass the first two function parameters.

For example, here is a (simplified) example found in libgcrypt 1.6.1:

  mov  0x24(%esp), %eax
  ...
  mov  0x1c(%esp), %edx
  call mul_n
  ...
mul_n:
  ...
  mov  %eax, 0x20(%esp)
  mov  %edx, 0x24(%esp)

As you can see, register eax and edx are used to pass parameters. My observation is that those two registers are always used to pass the first two parameters.

Note that I am using gcc to compile the code. However, I can only find the fastcall definition of the Microsoft compiler, which uses register ecx (not eax!) and edx to pass parameters.

So here is my question: is there any clear definition of such gcc optimization? I just cannot find some informative sources...

lllllllllllll
  • 8,519
  • 9
  • 45
  • 80
  • 1
    Any possibility _GCC_ is being called with `regparm` parameter (or functions are declared with the `regparm` attribute)? See: https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html . Linux kernel in particular uses `regparm(3)` which passes first 3 integer class values in EAX, EDX, and ECX in that order. `regparm(2)` uses `EAX and EDX` and `regparm(1)` uses `EAX`. `regparm(0)` is regular _CDECL_ calling convention. – Michael Petch Sep 22 '16 at 00:00
  • 1
    In my own software that targets 32-bit x86 I'll often use this `#define fastcall __attribute__((regparm(3)))` and then I use `fastcall` on any function declaration that I wish to alter the normal _CDECL_ calling convention by passing first 3 values in registers rather than the stack. – Michael Petch Sep 22 '16 at 00:12
  • FWIW, in Delphi `fastcall`, the 3 registers are `eax`, `edx`, and `ecx` respectively. Any further arguments are passed on the stack. But in this case, I guess there are a few simple runtime routines that simply follow some kind of internal convention. Generally, these (internal) runtime routines are not meant to be called from user code, only to be compiled in by the compiler, to perform tasks that are not possible in a few machine instructions. Since the compiler compiles them in -- i.e. knows about them and which registers they need --, they don't have to follow any known conventions. – Rudy Velthuis Sep 22 '16 at 10:36
  • Is that compiler output or disassembly, or is that asm *source* that's part of libgcrypt (in a .S file)? If it's compiler output, show the function definition (at least the first line, declaring the return type and args, and including the expansion of any CPP macros that set attributes). – Peter Cordes Sep 23 '16 at 02:53
  • 2
    I took a look and it seems to be a static function `static void mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size, mpi_ptr_t tspace )` Nothing special. Could be some form of optimization. I don't see any references to `regparm` being passed as an option to _GCC_. – Michael Petch Sep 23 '16 at 03:26
  • @PeterCordes That's disassembler's output. – lllllllllllll Sep 23 '16 at 14:48
  • @MichaelPetch Yes, I didn't find the `pregparm` option on this function.. – lllllllllllll Sep 23 '16 at 14:48
  • It looks like gcc's IPO (inter-procedural optimization) has made a non-standard ABI for this private helper function. I thought gcc did this sort of thing, but I hadn't investigated and seen it in practice. The other thing gcc can do when it knows about the target function is have the caller take advantage of the fact that a function doesn't clobber some registers. (e.g. keep something live in ECX across a function call.) This is more likely on x86-64, where there are more call-clobbered regs. – Peter Cordes Sep 23 '16 at 21:57

0 Answers0