-
Notifications
You must be signed in to change notification settings - Fork 552
x86 calling convention
Our Intel x86 output uses a calling convention that's similar to the old pascal calling convention. Method arguments are pushed left-to-right and the callee does stack cleanup.
Arguments are passed on the stack only. Result values are put on the stack as well. Exception state is returned in the ECX
register.
If the total argument size (including the $this
for instance methods) is less than the result size, we push extra dword
values onto the stack.
Inside a method, the method arguments and local variables are access relative to the EBP
register. Because the stack on Intel architecture grows up (towards zero), local variables are EBP - X
and arguments are EBP + X
This section contains a number of assembler samples. The shown assembler code is might differ in spots compared to the actual compiler output, but it's purely to show the calling convention. Besides that, label names used are not valid assembler language labels, so we can better explain what's going on.
The method itself has a single local variable or type Int32
Calling method:
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
push 0x42 // push argument value 0x42 on the stack
call PrintValue(int) // call the actual method
// there's no result on the stack
test dword ECX, 0x2 // if second bit of the ECX register is set, an exception occurred in the method (directly or indirectly)
je NextInstructionLabel
jne MethodFooter_Exception // if the bit was set, jump to method footer, or catch/finally block
NextInstructionLabel:
// Continue method
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret // return to caller
PrintValue(int):
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
mov EAX, [EBP + 8] // the argument value is now in EAX
// do something here.
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret 4 // return to caller, cleaning up 4 bytes from the stack.
Calling method:
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
push 0x42 // push argument value 0x42 on the stack
call CalculateValue(int) // call the actual method
test dword ECX, 0x2 // if second bit of the ECX register is set, an exception occurred in the method (directly or indirectly)
je NextInstructionLabel
add esp, 4 // clean up the return value from the stack.
jne MethodFooter_Exception // if bit was set, jump to method footer, or catch/finally block
NextInstructionLabel:
// Continue method
pop EAX // value was on the stack.
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret // return to caller
CalculateValue(int):
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
mov EAX, [EBP + 8] // the argument value is now in EAX
// do something here.
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret // return to caller. Don't cleanup here.
Calling method:
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
push dword 0 // make room on the stack for the return value
call GetValue() // call the actual method
test dword ECX, 0x2 // if second bit of the ECX register is set, an exception occurred in the method (directly or indirectly)
je NextInstructionLabel
add esp, 4 // clean up the return value from the stack.
jne MethodFooter_Exception // if bit was set, jump to method footer, or catch/finally block
NextInstructionLabel:
// Continue method
pop EAX // value was on the stack.
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret // return to caller
GetValue():
push ebp // Save original EBP
mov ebp, esp // Set new method base pointer
// do something here
push 0x42 // push value 0x42 on the stack, which is our return value.
MethodFooter_Normal:
mov ecx, 0 // explicitly set ECX to 0, meaning no exception happened
pop eax // pop result from stack
mov [EBP + 8], eax // store result on stack, but below the return pointer.
MethodFooter_Exception:
// cleanup our locals
pop EBP
ret // return to caller. Don't clean up here.