The MASM Forum

General => The Campus => Topic started by: satpro on June 09, 2012, 02:20:00 AM

Title: Preservation of esi, edi, ebx in general code
Post by: satpro on June 09, 2012, 02:20:00 AM
Hi all,


Could someone explain if (and why) preserving ESI, EDI, and EBX is necessary--if they are used--in ordinary procedures (excluding WndProc)?  My understanding is that Windows preserves them, so we should, too?  I'm not sure.  What does Windows do with them--exactly?  When I read text about it I only get more confused.  I've tried it both ways with no adverse effects--YET.  Haven't won the lottery, either... helps, though, if you play.


From Kip R. Irvine:

"Always save and restore registers that are modified by a procedure so the calling program can be
sure that none of its own register values will be overwritten. The exception to this rule pertains to
registers used as return values, usually EAX. Do not push and pop them."


I get the eax sentence--that makes sense.  The rest really doesn't.  It sounds like fear-mongering.  In your own code, let's assume you know what's going on  ::) .  I'm guessing you really don't have to preserve those three in ordinary code, and I'd rather not if I don't have to, but everyone always does.  They aren't holding some value that can't be changed, are they?


Just being "safe" and saving registers unnecessarily doesn't really seem efficient or logical, unless there is good reason (such as preserving across a call).  It seems like C/C++.


Thanks,
Bert
Title: Re: Preservation of esi, edi, ebx in general code
Post by: dedndave on June 09, 2012, 02:39:01 AM
Kip may be refering to "old-style" DOS code - check the date of the book

for windows programming, we tend to follow the ABI
in functions that may be used in call-back (as in WndProc, etc), preserve EBX, ESI, EDI, and EBP
also - the direction flag should be cleared whenever calling an API function
typically, if we set it (STD), we clear it (CLD) when done
we also generally assume it to be cleared when we start a function
EAX, ECX, EDX are considered fair game to be overwritten
EAX is quite often used to return a result or status

windows API functions follow those same rules

while not all procedures are used in call-back situations, it is considered good practice to write functions so that they may be used that way

i write code in a variety of ways, which may be a little confusing - lol
but - some functions i may write "know" the preservation rules are followed by the calling operation

WinMain is not a call-back function, so the register rules may be ignored (not the direction flag, though)
you can use that to your advantage, too
at the beginning of WinMain, i might zero the EDI register
i know it will be 0 all the way through to ExitProcess   :P
Title: Re: Preservation of esi, edi, ebx in general code
Post by: clive on June 09, 2012, 02:42:03 AM
If others call your code they are going to expect it to conform to the ABI (Application Binary Interface) defined for the architecture.

What you do in your own code is up to you, you can use the registers to hold anything you want, and use different registers to pass parameters.

By pushing registers on the stack it makes upstream code development easier as you don't need to be painfully aware of what everything you call is doing, and the side effects different code flows many exhibit.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 09, 2012, 02:44:19 AM
Good question. Ask three members here, and get four different answers. Ask Microsoft, and get no clear answer.
The "preserve in callbacks" seems to be widely accepted, and "in console mode apps do what ever you want", too, but wait for more replies... have a look at The "register gets trashed" trap (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm), too.

I tend to use
WndProc proc uses esi edi ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
It's pretty safe imho.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: satpro on June 09, 2012, 02:52:16 AM
Thanks dedndave, clive, jj2007.

dave--6th edition--believe that?  You really have to sift his books (all editions) to get what you want sometimes.  But there's some good in there, too.
jj-That's pretty much what I do, too.

Thanks again, guys.  I feel better now.  It's been driving me nuts for a while.
Bert
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 11:30:52 AM
satpro,

Stick to the Intel ABI and you are safe. Windows complies with it and it means your code can live with Windows API functions. You preserve registers on the basis of needs, IF you modify EBX ESI or EDI within a procedure, restore them before you exit the procedure, if not then you don't bother as you arew only adding extra code that is not needed. Also remember the inverse, if you call other procedures, they can also modify EAX ECX and EDX so if you are using any of them and you make a procedure call, preserve and restore them as well.

Don't be lead astray here, if you do not write ABI complaint code it may crash on other Wndows versions.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: Ryan on June 09, 2012, 11:41:35 AM
I'm also a bit confused when it comes to register preservation, mainly because if Windows expects registers to be preserved, wouldn't you need to restore the registers before making an API call?  Right now I'm only doing a push/pop around my entire procedure or using the 'uses' keyword.  I know it hasn't been an issue in anything I've written in the last couple months that I've been working with assembly, but is it a potential issue?
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 12:11:50 PM
YES !!!!

> Don't be lead astray here, if you do not write ABI complaint code it may crash on other Wndows versions.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: dedndave on June 09, 2012, 12:15:54 PM
as i mentioned earlier...
windows expects the registers to be preserved in functions used during call-back
i.e., WndProc should preserve whatever values the OS has in EBX, ESI, EDI, and EBP

this is not the same as calling an API function
they follow the same rules as we do   :P
if you put something in EBX, ESI, or EDI and call an API function, those registers will be preserved

most if us generally follow these rules when we write functions so that it may be used the same way
for example, nearly all, if not all, of the functions Hutch has written for the masm32 library preserve the registers
if you want to call one of the masm32 functions inside WndProc, you do not need to worry about EBX, ESI, EDI, or EBP
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 09, 2012, 04:03:00 PM
Quote from: hutch-- on June 09, 2012, 11:30:52 AM
Stick to the Intel ABI and you are safe.

Or test yourself:

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL rc:RECT, whatever:DWORD
mov esi, 123
mov edi, 456
mov ebx, 789


Hutch, can you post a link to the page where Microsoft explains which kind of callback functions need preservation, and what to do for console mode apps? Thanks :icon14:
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 04:09:16 PM
 :biggrin:

No, just look up Intel Application Binary Interface.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 09, 2012, 04:29:55 PM
Quote from: hutch-- on June 09, 2012, 04:09:16 PM
:biggrin:

No, just look up Intel Application Binary Interface.

What does Intel have to do with Microsoft???

Ok, let's launch a little challenge (source and executable attached):

.data?
ghWnd dd ?
guMsg dd ?
gwParam dd ?
glParam dd ?
.code

WndProc proc a1, a2, a3, a4
m2m ghWnd, a1
m2m guMsg, a2
m2m gwParam, a3
m2m glParam, a4
mov ebp, 666   ; the number of the devil ######
mov esi, 123
mov edi, 456
mov ebx, 789


Crash it if you can :biggrin:

P.S.: And no, I do not recommend that, and I do not use that in any code of mine.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 04:54:48 PM
 :biggrin:

Q. What does Microsoft have to do with Intel ????
A. Not much but they use the Intel ABI since WinNT3.5.  :P
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 09, 2012, 05:21:50 PM
Unless you can point me to a page where an authoritative source (aka Microsoft) writes, in detail, what exactly Windows does, and why exactly it does NOT crash even if you trash the most precious registers like ebp in the most important callback (aka WndProc), it remains all hearsay and anecdotes of grumpy old men like you & me.

It is one of these grey areas where Microsoft maintains a tiny comparative advantage by not releasing its little secrets, that's all.

For the noobs here: Use ...
WndProc proc uses esi edi ebx hWin:DWORD, uMsg:DWORD, wParam :DWORD, lParam :DWORD
... otherwise Hutch & me & a few others will eat you alive :greensml:
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 06:15:49 PM
 :biggrin:

This is actually BAD advice.

Quote
WndProc proc uses esi edi ebx hWin:DWORD, uMsg:DWORD, wParam :DWORD, lParam :DWORD
... otherwise Hutch & me & a few others will eat you alive

Taking the lazy way out by using "uses esi edi ebx" saddles your message processing with 6 extra operations (3 pushes and 3 pops) for every message processed, even when its not needed and it limits where you can exit the proc unless you want all of the extra  pops at every exit point. Far better architecture wise to preserve registers on the basis of need within each message processed then exit any way you like.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 09, 2012, 06:52:15 PM
6 cycles, 0.2% slower. Show me one WM_nnn that needs less than 3000 cycles...

(actually, there is one: WM_MOUSEMOVE takes only 600 cycles... which means that with the uses esi edi ebx, your WM_MOUSEMOVE processing will take approx. 0.00408 milliseconds instead of only 0.004 milliseconds... ::))
Title: Re: Preservation of esi, edi, ebx in general code
Post by: hutch-- on June 09, 2012, 09:08:59 PM
Its all of the extra clutter that is the problem and the added problem associated with multiple exits, the much cleaner and simpler technique is to preserve required registers on a needs basis for each message processed. A WndProc is a messy enough affair to start with, essentially a single collector procedure with multiple functions based on the number of processed messages being handled, different messages having different needs, this is why a catch all is bad design.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: dedndave on June 10, 2012, 12:39:54 AM
it's really true
a top-level window with a mouse moving over it gets hundreds, if not thousands of messages
they usually go to DefWindowProc - but only after passing through the epilogue
Title: Re: Preservation of esi, edi, ebx in general code
Post by: jj2007 on June 10, 2012, 03:23:28 AM
Quote from: dedndave on June 10, 2012, 12:39:54 AM
a top-level window with a mouse moving over it gets hundreds, if not thousands of messages

Most of them are WM_MOUSEMOVE - a particularly fast one with only 600 cycles. 3 push/pop pairs delay WM_MOUSEMOVE by 0.00008 milliseconds - how significant is that delay?

To put it differently: Launch a simple window, then Task Manager, and try to get CPU usage above 0% by frenetically moving the mouse over the window. Have fun.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: satpro on June 10, 2012, 04:20:09 AM
Sorry, guys...  didn't mean to shake the tree.


So if I:


-- PUSH (or USES)  ESI or EDI or EBX   (in a procedure IF modified in the procedure)
-- PUSH  EAX or ECX or EDX                 (before entering a procedure IF the value held is needed afterwards)
-- Assume other programmers assume esi, edi, and ebx will be preserved; eax, ecx, and edx will be trashed in procedures.


...it should be okay.  Does that sound about right?
Title: Re: Preservation of esi, edi, ebx in general code
Post by: dedndave on June 10, 2012, 07:11:28 AM
yes - and let's not forget EBP - it should be preserved also
typically, we do not use EBP because the default epilogue for masm PROC's uses it for parms
the assembler takes care of preserving it for you
Title: Re: Preservation of esi, edi, ebx in general code
Post by: BogdanOntanu on June 10, 2012, 08:33:48 AM
Do not PUSH EAX because it is normally used for the return value...
Title: Re: Preservation of esi, edi, ebx in general code
Post by: Ryan on June 10, 2012, 09:54:03 AM
Quote from: BogdanOntanu on June 10, 2012, 08:33:48 AM
Do not PUSH EAX because it is normally used for the return value...
What do you mean?  If the value in EAX is still needed, it has to be pushed, else it will be lost.
Title: Re: Preservation of esi, edi, ebx in general code
Post by: dedndave on June 10, 2012, 10:02:10 AM
you are right, Ryan
most functions do return a result in EAX, however
but - you could do something like...
        push    eax
        INVOKE  SomeFunction
        or      eax,eax                ;test returned value
        pop     eax                    ;restore saved value
        jz      SomeLabel              ;branch on result of test

another way to go might be to restore it into EDX (or ECX)
        push    eax
        INVOKE  SomeFunction
        or      eax,eax                ;test returned value
        pop     edx                    ;restore saved value
        jz      SomeLabel              ;branch on result of test

that way, the returned value is not destroyed