Page 1 of 2
Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 1:20 am
by smwikipedia
Hi geeks~
I am developing some functions in assembly language, at the beginning, my functions passing the parameters in the following 3 manners:
- registers
- global data in .data section
- stack
Now I found that the mix of the above 3 manners is making things complicated. And I always fall into the situation where I have to scratch my head to make sure whether certain register is polluted. So I decide to pass the parameters only through stack. And use the following function template as a lazy once-for-all solution:
Code: Select all
pushl %ebp
movl %esp, %ebp
pushal <--- save all the registers, this is kind of a lazy solution
subl xxx, %esp <--- allocate space for local variables
....
popal <--- restore all the registers
movl %ebp, %esp
popl %ebp
(addl yyy, %esp)<--- if it is __stdcall convention, the callee will clear the stack
ret
(xxx is the size of local variables for
callee, yyy is the size of paramters pushed by
caller.)
The caller is responsible for push parameters and clear the stack (like the C call convention). Of course, if the number of parameters is fixed, I can make the callee to clear the stack (like the __stdcall convention on Windows).
I am hoping this template could relieve me from the confusions of registers usage. Could it achieve that? If it is low efficiency, is there some better approach? I'd like to hear your comment.
Many thanks.
ADD:
PUSHA instruction details:
http://pdos.csail.mit.edu/6.858/2010/re ... /PUSHA.htm
RET instruction details:
http://pdos.csail.mit.edu/6.858/2010/re ... 86/RET.htm
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 6:06 am
by qw
This will trash your stack and return to wherever:
This is how stdcall does it:
This is a stack thrasher too:
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 9:49 am
by Brendan
Hi,
smwikipedia wrote:Now I found that the mix of the above 3 manners is making things complicated. And I always fall into the situation where I have to scratch my head to make sure whether certain register is polluted. So I decide to pass the parameters only through stack. And use the following function template as a lazy once-for-all solution:
In general, people who are used to one language that learn a different language tend to try to do things the same way they did in the first language, even when it's not really appropriate for the second language. An example of this is a C programmer who learns C++ (and doesn't use many of the OOP features of C++), or a C programmer who learns Python (and keeps putting semicolons at the end of statements), or a C programmer that learns assembly (and continues thinking that using the stack for passing parameters and/or only returning a maximum of one value from a routine is sane).
Cheers,
Brendan
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 10:04 am
by smwikipedia
Hobbes wrote:This will trash your stack and return to wherever:
This is how stdcall does it:
This is a stack thrasher too:
Thanks Hobbes.
- After some time of debugging, I found that the
addl yyy, %esp trashed my stack. And thanks for confirming it.
- Thanks for telling me how to implement
__stdcall
- I am still wondering how could the
subl xxx, %esp could trash the stack? Isn't that the standard way to allocate space on the stack for local variables?
Thanks for any comments...
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 10:09 am
by smwikipedia
Brendan wrote:Hi,
smwikipedia wrote:Now I found that the mix of the above 3 manners is making things complicated. And I always fall into the situation where I have to scratch my head to make sure whether certain register is polluted. So I decide to pass the parameters only through stack. And use the following function template as a lazy once-for-all solution:
In general, people who are used to one language that learn a different language tend to try to do things the same way they did in the first language, even when it's not really appropriate for the second language. An example of this is a C programmer who learns C++ (and doesn't use many of the OOP features of C++), or a C programmer who learns Python (and keeps putting semicolons at the end of statements), or a C programmer that learns assembly (and continues thinking that using the stack for passing parameters and/or only returning a maximum of one value from a routine is sane).
Cheers,
Brendan
Thanks Brendan for your comment.
I am still a newbie. Could you tell me what's on your mind when you see my code? It seems I missed something...
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 12:40 pm
by Brendan
Hi,
smwikipedia wrote:Thanks Brendan for your comment.
I am still a newbie. Could you tell me what's on your mind when you see my code? It seems I missed something...
For assembly language:
- you rarely need to pass more arguments than there are registers. If this is the case you've probably failed to split code up into routines of manageable size (e.g. you've got a massive routine that does "everything" rather than a set of routines), or you should be using a pointer to a structure instead.
- you rarely need to allocate space for local variables on the stack. Again, split code up into routines of manageable size. There are rare cases where local variables can't be avoided (for some reason, graphics code seems to be the main culprit); but it's easy enough to handle these rare cases manually (e.g. "sub esp,8", "%define myFirstVar [esp]", "%define mySecondVar [esp+4], etc").
- use EBP as just another general register (it helps to avoid the need for local variables). You can use it for input and output parameters too. There's no need to use it for a stack frame pointer.
- don't forget that you can use flags as output parameters. This avoids the need for the caller to test the result (for example, "call foo" then "jc .failed" is more efficient than "call foo", "test eax,eax", "jne .failed").
- you rarely (never) need to pass a variable number of arguments. For example, the actual code for something like "printf()" is ugly and inefficient (it only exists for convenience) - consider separate routines to print a character, print a string, print a value in hex, print a value in decimal, etc so you don't have to waste CPU time by parsing a format string (and don't have to bother with variable arguments).
- if the output of one routine may be used as the input to another routine, try to make the output match the input. For example, imagine you've already got a routine named "get_colour" that returns "red" in EAX, "green" in EBX and "blue" in ECX; and you're writing a routine named "combine_colour" to combine separate red/green/blue values into a single integer (maybe for 16-bpp). In this case, the registers used should match, so people can do "call get_colour" then "call combine_colour" with no instructions inbetween at all.
- for registers that are trashed (e.g. used for local variables), in general it's more efficient (for space) for the routine to save/restore them than to expect every caller to save/restore them. It also makes it easier to maintain the code if you know that you can call any routine and the only registers that will be modified are registers used for outputs (otherwise, changing a routine so it uses an extra register would mean finding every place where the routine is called). You'll also probably find situations where a register is only used sometimes (e.g. a conditional branch might cause the CPU to skip a block of code that uses the register). In this case you can optimise a little (e.g. avoid saving/restoring the register when it's not used).
For high level languages the inputs and outputs are (partially) described by the function's definition/declaration (for example, "bool move_pages(int count, void *source, void *dest);" gives you a fair idea of what each parameter is for). For assembly, register names don't tell you anything (e.g. "EAX" could be an integer or a pointer or a colour or...). To avoid confusion you should use comments to document the inputs and outputs. For example:
Code: Select all
;Move pages from one virtual address to another
;
;Input
; ecx Number of pages to move
; edx Virtual address to put first page moved
; esi Virtual address of first page to move
;
;Output
; carry Set if not enough memory error
MEM_movePages:
;**stuff**
ret
Cheers,
Brendan
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 12:49 pm
by Brendan
Hi again!
One last thing I forgot.
If you need to call a routine written in assembly from C; then create a wrapper (e.g. in header file) that uses inline assembly to call your routine (instead of writing the routine to use C calling conventions). This gives the compiler the freedom to optimise register usage (and avoids the need to pollute the assembly routine).
Cheers,
Brendan
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 6:56 pm
by smwikipedia
smwikipedia wrote:Hi geeks~
I am developing some functions in assembly language, at the beginning, my functions passing the parameters in the following 3 manners:
- registers
- global data in .data section
- stack
Now I found that the mix of the above 3 manners is making things complicated. And I always fall into the situation where I have to scratch my head to make sure whether certain register is polluted. So I decide to pass the parameters only through stack. And use the following function template as a lazy once-for-all solution:
Code: Select all
pushl %ebp
movl %esp, %ebp
pushal <--- save all the registers, this is kind of a lazy solution
subl xxx, %esp <--- allocate space for local variables
....(function body)
popal <--- restore all the registers
movl %ebp, %esp
popl %ebp
(addl yyy, %esp)<--- if it is __stdcall convention, the callee will clear the stack
ret
(xxx is the size of local variables for
callee, yyy is the size of paramters pushed by
caller.)
The caller is responsible for push parameters and clear the stack (like the C call convention). Of course, if the number of parameters is fixed, I can make the callee to clear the stack (like the __stdcall convention on Windows).
I am hoping this template could relieve me from the confusions of registers usage. Could it achieve that? If it is low efficiency, is there some better approach? I'd like to hear your comment.
Many thanks.
Thanks berkus. It seems I need some sleep now and have a clean mind.
Now I modified it like this, I think this should be ok.
Code: Select all
pushl %ebp
movl %esp, %ebp
pushal <--- save all the registers, this is kind of a lazy solution
subl xxx, %esp <--- allocate space for local variables
....
addl xxx, %esp <--- reclaim the space for local variables
popal <--- restore all the registers
movl %ebp, %esp
popl %ebp
ret yyy <--- for __stdcall convention, the callee will clear the parameters pushed on stack by caller
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 7:11 pm
by smwikipedia
Brendan wrote:Hi again!
One last thing I forgot.
If you need to call a routine written in assembly from C; then create a wrapper (e.g. in header file) that uses inline assembly to call your routine (instead of writing the routine to use C calling conventions). This gives the compiler the freedom to optimise register usage (and avoids the need to pollute the assembly routine).
Cheers,
Brendan
Hi Brendan,
Your comment is a treasure to me. Many thanks.
I have some similar but quite vague feeling to your words. Thanks for making them clear. I will spend more time to digest it.
Again, many thanks.
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 02, 2010 11:41 pm
by smwikipedia
Brendan wrote:
- if the output of one routine may be used as the input to another routine, try to make the output match the input. For example, imagine you've already got a routine named "get_colour" that returns "red" in EAX, "green" in EBX and "blue" in ECX; and you're writing a routine named "combine_colour" to combine separate red/green/blue values into a single integer (maybe for 16-bpp). In this case, the registers used should match, so people can do "call get_colour" then "call combine_colour" with no instructions inbetween at all.
No offense but I don't quite agree with you on this point. This will lead to a tight coupling between functions, which will put constraints on how the functions should be used. I think it's better to make a function self-contained.
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 09, 2010 2:04 am
by smwikipedia
Just a sum up.
I finally come to the following code template as a simple & lazy convention:
Code: Select all
pushl %ebp
movl %esp, %ebp
pushal <--- save all the registers, this is kind of a lazy solution
subl xxx, %esp <--- allocate space for local variables
....
addl xxx, %esp <--- reclaim the space for local variables
popal <--- restore all the registers
(we can set return value in general purpose register here)
movl %ebp, %esp
popl %ebp
ret yyy <--- for __stdcall convention, the callee will clear the parameters pushed on stack by caller
And a couple of rules as below:
1- pusha/popa deals with 7 general purpose registers, if registers other than those 7 are involved in the body of the function, enclose it as below whereever the register is used in the function body.
Code: Select all
push xxx_register
...use it
pop xxx_register
2- If we want to return some value in a general purpose regiseter, such as eax, put that after the final popal
The aim of all above is to ensure that after the execution of this function, the value of all the registers are not affected/polluted. This convention is kind of like an
insulation between the inside and outside of the function. And thus save our efforts of carefully arranging for the register usage.
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 09, 2010 2:37 pm
by a5498828
you cant just add/sub esp.
stack has fixed size, and it can expand it by using guard pages. if you substract too much, you will skip guard page overwriting another data or code.
use enter instruction, wich is safe.
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 09, 2010 3:17 pm
by Owen
a5498828 wrote:you cant just add/sub esp.
stack has fixed size, and it can expand it by using guard pages. if you substract too much, you will skip guard page overwriting another data or code.
use enter instruction, wich is safe.
a) Enter is directly equivalent to a series of simpler instructions
b) Enter will not stop you from jumping guard pages
c) Many systems guard down the entire reserved lenth of the stack
d) Enter is very slow
Your ignorance is showing..
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 09, 2010 8:42 pm
by smwikipedia
I have just finished my bootloader code with my lazy convention function template, but it is noticeably slower than my original implementation. My original implementation use register to pass parameters and didn't use the pusha/popa instructions. The algorithm is quite the same. What could be the reason? I will post my code if necessary.
Re: Assembly language function template - A lazy convention
Posted: Thu Sep 09, 2010 8:51 pm
by NickJohnson
Well, pusha/popa take a decent amount of time to work (lots of memory accesses and such), but register passing has zero overhead. Of course the former is slower.