Assembly language function template - A lazy convention

Programming, for all ages and all languages.
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Assembly language function template - A lazy convention

Post 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
Last edited by smwikipedia on Thu Apr 28, 2011 9:03 am, edited 2 times in total.
User avatar
qw
Member
Member
Posts: 792
Joined: Mon Jan 26, 2009 2:48 am

Re: Assembly language function template - A lazy convention

Post by qw »

This will trash your stack and return to wherever:

Code: Select all

    addl    yyy, %esp
    ret
This is how stdcall does it:

Code: Select all

    ret     yyy
This is a stack thrasher too:

Code: Select all

    pushal
    subl    xxx, %esp
    ....
    popal
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Assembly language function template - A lazy convention

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post by smwikipedia »

Hobbes wrote:This will trash your stack and return to wherever:

Code: Select all

    addl    yyy, %esp
    ret
This is how stdcall does it:

Code: Select all

    ret     yyy
This is a stack thrasher too:

Code: Select all

    pushal
    subl    xxx, %esp
    ....
    popal
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... :)
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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...
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Assembly language function template - A lazy convention

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Assembly language function template - A lazy convention

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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
Last edited by smwikipedia on Thu Sep 02, 2010 11:38 pm, edited 3 times in total.
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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. :)
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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.
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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.
a5498828
Member
Member
Posts: 99
Joined: Thu Aug 12, 2010 7:25 am

Re: Assembly language function template - A lazy convention

Post 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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Assembly language function template - A lazy convention

Post 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..
User avatar
smwikipedia
Member
Member
Posts: 49
Joined: Tue Apr 20, 2010 1:11 am

Re: Assembly language function template - A lazy convention

Post 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.
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: Assembly language function template - A lazy convention

Post 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.
Post Reply