Parameter passing methods

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Ready4Dis
Member
Member
Posts: 571
Joined: Sat Nov 18, 2006 9:11 am

Post by Ready4Dis »

Craze Frog wrote:You can pass parameters on the stack and access them easily without creating a stack frame with ebp. (My compiler supports only stdcall, and uses fasm for assembling.)
There are plenty of ways, you could use ESP (assuming your functino doesn't change it in any way shape or form), you could store it in another general purpose register, you could use a pointer, etc. EBP is just very easy, mostly unused variable that is convinient ;).

Sounds good about the compiler, is it C, C++, etc? Do you use it for your OSDev and stuff, or was it just for learning?
Craze Frog
Member
Member
Posts: 368
Joined: Sun Sep 23, 2007 4:52 am

Post by Craze Frog »

It's a BASIC compiler (I didn't finish it), and it uses esp for accessing variables (of course, esp is changed throughout the function, but the compiler keep track of it).
skyking
Member
Member
Posts: 174
Joined: Sun Jan 06, 2008 8:41 am

Post by skyking »

The caller is responsible for removing the parameters (and they are pushed first parameter last) after the call for a simple reason:

What about for a variable number of parameters?


I think the StdCall calling convention is the most effective at the end of the day considering all the factors. You could use the StdCall calling convention also for variable number of parameters. You push the parameters from right to left and then as a DWORD, push the number of parameters onto the stack.

For example, if you have DWORD1 and DWORD2 to push onto the stack by value, then do this:

Code:
PUSH DWORD PTR [DWORD1]
PUSH DWORD PTR [DWORD2]
PUSH DWORD 0x00000002


The last parameter will be the number of parameters that are pushed onto the stack. The good thing about this method is that the procedure that wants to receive these parameters will always be able to find the number of parameters since the number of parameters is the last parameter pushed onto the stack:
Thats not the way it's done. First the number of parameters isn't needed since the caller knows how many arguments it has pushed and how many it have to remove - the called function however uses other means to determine the number of arguments to be examined (printf for example uses the format string to determine that).

And the order is first parameter last - this way the first parameter will be directly under the return address (for intel architectur). This is an easy address to compute without knowledge of the number of parameters then the function just examine parameters further down on the stack as far as it finds required to examine (based for example on the format string in printf).
Which other considerations to pass parameters are there? It's specially because having to handle each parameter with an inverse addressing into the stack seems to be very bloated, even more when one needs to push further elements, which of course will change the position of the passed parameters.
First of all you should always make sure you don't run out of stack space since there is more than just parameters that end up on the stack (local variables for example). One special situation is if you call supervisor mode code from user mode where passing all direct parameters via registers since the function will probably not execute on the same stack. If you need more parameters then using pointer to a struct will make every direct parameter fit in a limited register set.

[/quote]
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Look, I've written a compiler (without lex/yacc/other tools) and I know how calling conventions works.
I notice the tools you list only help with writing a front end (and not even as far as creating the abstract syntax tree - they only help with the parse tree), and have nothing whatsoever to do with your argument which is about calling conventions (deep backend territory).
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

skyking wrote: Thats not the way it's done. First the number of parameters isn't needed since the caller knows how many arguments it has pushed and how many it have to remove - the called function however uses other means to determine the number of arguments to be examined (printf for example uses the format string to determine that).
The number of parameters is NOT needed? If you are following a derivation of StdCall for Variable Arguments where parameters are pushed onto the stack from right to the left, the number of parameters should be the last argument that is passed onto the stack. The reason is that with StdCall, the Caller procedure DOES NOT remove the parameters from the stack. That's CDecl. In StdCall, the CALLEE removes the parameters. The Callee therefore must know how many parameters to remove from the stack by adjusting ESP since you can't have a GPR or a memory reference with the RET instruction. So you will have to increase the ESP.
skyking wrote: And the order is first parameter last - this way the first parameter will be directly under the return address (for intel architectur).
Again, I don't remember talking about CDecl at all. I was talking about StdCall and Variable arguments that can be pushed onto the stack using a calling convention similar to StdCall.
Craze Frog wrote:Sorry, but have obviously no clue what you are talking about. Instead of arguing, why don't you go an look at some actual compiler output? I wouldn't have said these things if I didn't have tested it, I suggest you follow the same policy.
I would never follow the "Same policy" that you have followed since your statements are utterly wrong. You NEVER pass an 80-bit long value onto the stack using 80-bits of the stack space. You ALWAYS pass the pointer to that data structure. I suggest YOU debug some programs and see what I mean. When you pass floating point values and etc amongst normal integral values, an optimized and a sane compiler usually passes the integral values (poiners, etc) using the calling convention and floating points in ST0 to ST7 so that the function will not have to maintain those floating point values from the stack to maintain stack and parameter size consistency.
Craze Frog wrote:That line is WRONG for gcc and vcc. Even for Delphi it's wrong order...
Do you see me discussing the order of these parameters in Delphi? Again to refresh your mind,
My Web Site wrote:If there are more than three parameters, the first three should be placed inside EAX, ECX and EDX registers and the rest should be pushed onto the stack from left to right.
You'll win 2000 USD if you could find me discussing the order in which these parameters are passed to those GPRs :lol:
Craze Frog wrote:You can't pick your own style if you want to interface with compiled code.
Do you see me discussing interfacing with compiled codes about the FastCall calling convention? The Register calling convention is what Delphi uses for example. First parameter (from the left) in EAX, then EDX and then ECX. Why do you think you can't pick your own style? If you are writing an Operating System, you could have a compiler that follows your operating system's default calling convention. Like what Linux does when passing parameters into GPRs. There are no rules to that.

I suggest you don't suggest me what to suggest to people :lol:
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Craze Frog
Member
Member
Posts: 368
Joined: Sun Sep 23, 2007 4:52 am

Post by Craze Frog »

JamesM wrote:
Look, I've written a compiler (without lex/yacc/other tools) and I know how calling conventions works.
I notice the tools you list only help with writing a front end (and not even as far as creating the abstract syntax tree - they only help with the parse tree), and have nothing whatsoever to do with your argument which is about calling conventions (deep backend territory).
So "other tools" does not mean anything to you?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Craze Frog wrote:
JamesM wrote:
Look, I've written a compiler (without lex/yacc/other tools) and I know how calling conventions works.
I notice the tools you list only help with writing a front end (and not even as far as creating the abstract syntax tree - they only help with the parse tree), and have nothing whatsoever to do with your argument which is about calling conventions (deep backend territory).
So "other tools" does not mean anything to you?
It does mean something to me. It means that either you:

* Couldn't name any other tools and put "other tools" as a blanket statement to make your point seem to have more weight OR
* You *could* name other tools but felt that lex/yacc were the most important (thus put them first in your list); in which case I would question what "other tools" we are talking about, given that if they were related to the compiler backend they would probably have scored higher on your "list ranking".
* What am I meant to understand about "other tools" anyway? It's a blanket statement and conveys no information.
Craze Frog
Member
Member
Posts: 368
Joined: Sun Sep 23, 2007 4:52 am

Post by Craze Frog »

Well, it just means I wrote it by hand. Other tools could include LLVM but I assumed no one would know it by it's name so naming it would be useless.
XCHG wrote:
Craze Frog wrote:Sorry, but have obviously no clue what you are talking about. Instead of arguing, why don't you go an look at some actual compiler output? I wouldn't have said these things if I didn't have tested it, I suggest you follow the same policy.
I would never follow the "Same policy" that you have followed since your statements are utterly wrong. You NEVER pass an 80-bit long value onto the stack using 80-bits of the stack space. You ALWAYS pass the pointer to that data structure. I suggest YOU debug some programs and see what I mean. When you pass floating point values and etc amongst normal integral values, an optimized and a sane compiler usually passes the integral values (poiners, etc) using the calling convention and floating points in ST0 to ST7 so that the function will not have to maintain those floating point values from the stack to maintain stack and parameter size consistency.
Sorry, I actually misread 80. However, I am still correct. This is the input program to gcc:

Code: Select all

int kix(long double first) {
    return first;
}

int main(int argc) {
    __asm__("#CODE START");
    return kix(argc*2.0);
    __asm__("#CODE END");
}
This is the output of gcc main.c -S:

Code: Select all

	.file	"main.c"
	.text
.globl _kix
	.def	_kix;	.scl	2;	.type	32;	.endef
_kix:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$8, %esp
	fldt	8(%ebp)
	fnstcw	-2(%ebp)
	movzwl	-2(%ebp), %eax
	orw	$3072, %ax
	movw	%ax, -4(%ebp)
	fldcw	-4(%ebp)
	fistpl	-8(%ebp)
	fldcw	-2(%ebp)
	movl	-8(%ebp), %eax
	leave
	ret
	.def	___main;	.scl	2;	.type	32;	.endef
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$24, %esp
	andl	$-16, %esp
	movl	$0, %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -4(%ebp)
	movl	-4(%ebp), %eax
	call	__alloca
	call	___main
/APP
	#CODE START
/NO_APP
	fildl	8(%ebp)
	fadd	%st(0), %st
	fstpt	(%esp)
	call	_kix
	leave
	ret
See that the 80-bit value is passed on the stack? Passing floating point values in st0-st7 is completely wrong for stdcall and cdecl.


Craze Frog wrote:That line is WRONG for gcc and vcc. Even for Delphi it's wrong order...
Do you see me discussing the order of these parameters in Delphi? Again to refresh your mind,
Yes, the calling convention name "register" is the name of Delphi's register calling convention. I (maybe wrongly) assumed that you knew that, since you were writing a guide on it. GCC and microsoft's similar convention is called fastcall.
Craze Frog wrote: That line is WRONG for gcc and vcc. Even for Delphi it's wrong order... You'll win 2000 USD if you could find me discussing the order in which these parameters are passed to those GPRs :lol:
It's wrong even if the order was correct. If there are three or more parameters, only the first two ones are passed in registers with gcc and vcc. Delphi, however, uses three registers. But your order is the first one in eax, the second in ecx and the third in edx. That is wrong. The second goes in edx, and the third in ecx.
Do you see me discussing interfacing with compiled codes about the FastCall calling convention? The Register calling convention is what Delphi uses for example. First parameter (from the left) in EAX, then EDX and then ECX. Why do you think you can't pick your own style? If you are writing an Operating System, you could have a compiler that follows your operating system's default calling convention. Like what Linux does when passing parameters into GPRs. There are no rules to that.
Of course you can pick your own style, but then you won't be using one of the normal calling conventions any more.

All the correct information can be found at wikipedia:
http://en.wikipedia.org/wiki/X86_calling_conventions
Or here: http://www.programmersheaven.com/2/Calling-conventions
Or here: http://msdn2.microsoft.com/en-us/librar ... S.71).aspx (mentions that all returns are also widened to 32 bits, bytes are NOT returned in AL!)
skyking
Member
Member
Posts: 174
Joined: Sun Jan 06, 2008 8:41 am

Post by skyking »

XCHG wrote:
skyking wrote: Thats not the way it's done. First the number of parameters isn't needed since the caller knows how many arguments it has pushed and how many it have to remove - the called function however uses other means to determine the number of arguments to be examined (printf for example uses the format string to determine that).
The number of parameters is NOT needed? If you are following a derivation of StdCall for Variable Arguments where parameters are pushed onto the stack from right to the left, the number of parameters should be the last argument that is passed onto the stack. The reason is that with StdCall, the Caller procedure DOES NOT remove the parameters from the stack. That's CDecl. In StdCall, the CALLEE removes the parameters. The Callee therefore must know how many parameters to remove from the stack by adjusting ESP since you can't have a GPR or a memory reference with the RET instruction. So you will have to increase the ESP.
No you did not talk about stdcall o cdecl, but thats no excuse to use a calling convention that's obsolete. Normally the caller removes the parameters and in that case the number of parameters don't have to be supplied.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

skyking wrote: No you did not talk about stdcall o cdecl, but thats no excuse to use a calling convention that's obsolete. Normally the caller removes the parameters and in that case the number of parameters don't have to be supplied.
Obsolete? What calling convention is obsolete? StdCall? Are you serious? :roll: The only calling convention that I've seen which is still in use where the caller wipes the parameters off the stack is Cdecl. In the Pascal, Safecall annd the StdCall the caller removes the parameters off the stack.

Parameters being removed by the caller: that has a lot of advantages. You wouldn't have to push and pop a lot especially when using a series of regularly used values that must be pushed onto the stack. You will just adjust ESP for the second and the rest of the procedures and BAM. I had this experience when writing OASML. It was pretty good but I got tired of having to remove the parameters myself every time I wanted to use a procedure/function so I decided to choose StdCall for my OS.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Post Reply