News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Why doesn't windows preserve registers?

Started by Alloy, December 04, 2008, 02:50:13 AM

Previous topic - Next topic

Rainstorm

rockoon wrote..
QuoteMost compilers no longer use ebp to refer to local variables or passed parameters, when optimizations are turned on, as long as the function has no dynamic storage (storage requirements unknown at compile time.)
don't know anything about compilers, so appreciate that extra info. - its a bit of a downer to hear though that VB6 does this optimization & masm doesn't do it in its regular procs :  /

>> KeepingRealBusy wrote..
QuoteI preserve ALL registers, and all flags (especially direction) except for Zero, Sign, and Carry.
can i ask why especially the direction flag, how does this help ?

jj2007

Quote from: KeepingRealBusy on December 06, 2008, 01:51:55 PM
...
I preserve ALL registers, and all flags (especially direction) except for Zero, Sign, and Carry.
...
If you want to code functions that return values in EAX, and feel the need to trash ECX and EDX, then do so. All I know is that I will not use your functions, I will continue to use my procedures.

Dave,

It would be a pity if you could not use the Masm32 library. It is really rich, and in most cases several times faster than corresponding Windows API's. However, it is true that the gang here is blindly focused on performance, performance, performance....!

However, I might be able to help you. Here is a SafeInvoke macro that does what you require: It preserves all registers and even the flags, doesn't return anything, and yet it provides you with the possibility to check the return values in eax, ecx and edx via specific global variables. I am afraid it may be a bit slower, and it may bloat your code a little bit, but as said earlier, it would be a pity if you could not use the treasures of Masm32.

include \masm32\include\masm32rt.inc

SafeInvoke MACRO LibName:REQ, args:VARARG
LOCAL tmp$
tmp$ CATSTR <invoke >, <LibName>
for arg, <args>
tmp$ CATSTR tmp$, <, >, <arg>
% echo tmp$
ENDM
pushad
pushf
tmp$
popf
ifndef Dave_eax
.data?
Dave_eax dd ?
.code
endif
ifndef Dave_ecx
.data?
Dave_ecx dd ?
.code
endif
ifndef Dave_edx
.data?
Dave_edx dd ?
.code
endif
mov Dave_eax, eax
mov Dave_ecx, ecx
mov Dave_edx, edx
popad
ENDM

.code
AppName db "Masm32", 0

start:
mov edi, 12h ; create some arbitrary values
mov esi, 13h
mov ebx, 14h
xor ecx, ecx
add ecx, 123h ; more arbitrary values
mov edx, ecx
add edx, edx ; 246h
or eax, -1 ; set some flags
; int 3 ; for Olly, in case you want to check if registers and flags are really OK
SafeInvoke StrLen, addr AppName ; reads the length of a zero terminated string and places it in eax
; int 3 ; for Olly
MsgBox 0, str$(Dave_eax), "'Masm32' has this length:", MB_OK
exit

end start


P.S.: Attention - the MsgBox will of course trash everything again. If you don't want to trash registers and flags, don't look at the output.

BlackVortex

Oh, no, he won't use the masm32 library, we're doomed ! We're not elite enough for KeepingReallyCheesy    :lol

Rockoon

Quote from: jj2007 on December 06, 2008, 07:25:11 PM
It would be a pity if you could not use the Masm32 library. It is really rich, and in most cases several times faster than corresponding Windows API's. However, it is true that the gang here is blindly focused on performance, performance, performance....!

I admit that I'm into performance (pretty much exclusively why I continue to code in assembler) but I see just as many size freaks as cycle freaks here.
When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

Mark Jones

Isn't size and/or speed the whole reason any of us are here? :toothy

The particularities of Windows is just another programming constraint or "gotcha"... just simply work around it. There is little to gain in complaining about something we have zero control over. :wink
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

BogdanOntanu

Quote from: Mark Jones on December 07, 2008, 01:22:01 AM
Isn't size and/or speed the whole reason any of us are here? :toothy

Short answer: NO. I am not here for speed or size.

I am here for a much greater joy: creation and simplicity. More exactly creation of huge and well structured applications in ASM.

From my hands on experience I find ASM as 10x up to 100x faster to prototype and develop applications when compared to any HLL. Then I also find ASM huge projects to be very easy to maintain and manage and improve ... not having dependency chains and having a huge "rebuild all" speed are also great features I enjoy.

Then I also like the stability and the simplicity of ASM as a programming language. Instead of learning countless details about countless programming languages that are the "hype of the day" I concentrate on algorithms and creation.

As for preserving registers I usually save them all (but eax or return values) in my own API. This might be "slower" and "larger" but it does help a lot in avoiding errors. Whenever I need to optimize (if ever)  I can always return and change the "inner loop".

In your own procedures you can use whatever rule is more close to your heart. However in interfacing with the OS API you must observe and respect the ABI standards. It is as simple as that.

Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

raymond

I have observed that WinXP doesn't seem to rely anymore on users respecting the ABI standard. Last year I wrote an app which was working perfectly under WinXP but simply froze when tested under Win98. I later found that the cause was an inadvertant restauration of the ESI and EDI registers in the wrong order :red which did not seem to bother WinXP.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

sinsi

Quote from: BogdanOntanu on December 07, 2008, 03:06:54 AM
As for preserving registers I usually save them all (but eax or return values) in my own API. This might be "slower" and "larger" but it does help a lot in avoiding errors. Whenever I need to optimize (if ever)  I can always return and change the "inner loop".

In your own procedures you can use whatever rule is more close to your heart. However in interfacing with the OS API you must observe and respect the ABI standards. It is as simple as that.
In my own code, if I use a register then I save it (except for returning stuff).

I can see microsoft's point though, every api call saving every register was pretty slow in the old win3 days - and that's the programming style most programmers are stuck in nowadays...


Quote from: raymond on December 07, 2008, 04:13:31 AM
I have observed that WinXP doesn't seem to rely anymore on users respecting the ABI standard. Last year I wrote an app which was working perfectly under WinXP but simply froze when tested under Win98. I later found that the cause was an inadvertant restauration of the ESI and EDI registers in the wrong order :red which did not seem to bother WinXP.
I've seen quite a few stories about this sort of thing and it's a bit worrying. Are we going to have to write wrappers around api calls?  :(


I remember the old INT 2F days...only CS,IP and (usually) SS:SP could be relied on.  :bdg
Light travels faster than sound, that's why some people seem bright until you hear them.

Mark Jones

Quote from: BogdanOntanu on December 07, 2008, 03:06:54 AM
Quote from: Mark Jones on December 07, 2008, 01:22:01 AM
Isn't size and/or speed the whole reason any of us are here? :toothy

Short answer: NO. I am not here for speed or size.

I am here for a much greater joy: creation and simplicity. More exactly creation of huge and well structured applications in ASM...

Of course this is true for me also, please see the toothy emoticon. :bg

Those who do not know assembler, perhaps look upon it as "foreign," "difficult," or "impossible," while many of us whom have used it, find it actually much easier to deal with than a HLL like Java (or especially C++.) I have been a long-time opponent of HLL abstraction, even if it means (more) assembly code. However with nice libraries such as Steve's, Donkey's, or Japeth's, this has been greatly mitigated, and today a C++ source can be very similar in length to an equivalent assembler source. So then, why anyone would bother learning all the extra abstraction (which serves absolutely no point in understanding the mechanics of the architecture and OS) is beyond me. (All other arguments aside please.) So yes, I agree with you Bogdan.

But yes, the ABI should be followed, whether we like it or not. Rules are rules -- if we don't like the rules, then perhaps the solution is not to change the rules, but to change those people whom make the rules. :green
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

donkey

Quote from: BlackVortex on December 06, 2008, 07:30:26 PM
Oh, no, he won't use the masm32 library, we're doomed ! We're not elite enough for KeepingReallyCheesy    :lol

Just got back to this thread, that's pretty insulting to KeepingReallyBusy, I'm sure we can all argue the point and make a few jokes without attacking anyone. As for his point of view, can't say I agree with preserving everything but I have always thought the ABI should have preserved ECX, after all it is used for a lot of counting loops and REPs at the processor level. They preserved ESI and EDI which are only really useful when used for sting and counting functions and they decided not to preserve the actual counter, that's about the only complaint I have with the ABI.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

jdoe

Quote from: donkey on December 08, 2008, 04:42:01 AM
Quote from: BlackVortex on December 06, 2008, 07:30:26 PM
Oh, no, he won't use the masm32 library, we're doomed ! We're not elite enough for KeepingReallyCheesy    :lol

Just got back to this thread, that's pretty insulting to KeepingReallyBusy, I'm sure we can all argue the point and make a few jokes without attacking anyone.


I could not post a comment about it as polite as that.


KeepingRealBusy

Donkey,

Quotebut I have always thought the ABI should have preserved ECX, after all it is used for a lot of counting loops and REPs at the processor level. They preserved ESI and EDI which are only really useful when used for sting and counting functions and they decided not to preserve the actual counter,

Another thing to consider is that the string functions are also using eax, as well as using the direction flag. In processing multiple precision arithmetic, comparisons and divisions are always top down (DF set), additions and multiplies are bottom up (DF cleared). I do a lot of this and this is where I got into the habit of preserving everything.

I do have a question about flag preservation. Do the API functions ever use the FPU and possibly change the content or state without saving and restoring the content and flags? What does the ABI say about preserving MMX and XMM registers?

Thank you for the support, but I just let things like that slide by, I just consider the source. If a factual statement was made and was incorrect, then I might correct that error, otherwise I try not to fan the flames.

Dave.

Rockoon

Quote from: donkey on December 08, 2008, 04:42:01 AM
but I have always thought the ABI should have preserved ECX, after all it is used for a lot of counting loops and REPs at the processor level. They preserved ESI and EDI which are only really useful when used for sting and counting functions and they decided not to preserve the actual counter, that's about the only complaint I have with the ABI.

Registers like edi and esi are more often used in expensive heavyweight functions, where saving registers has little impact on performance.

ecx on the other hand is used in the shift and roll instructions, which doesnt scream heavyweight all by itself.


If you step back and were tasked with selecting the ideal registers to be volatile, you would of course take some real world programs and try different permutations and simply measure the overall impact on these real world programs. This is something that compiler writers can do trivialy for all self-contained calls, and I see no reason to believe that something like that hasnt happened and that eax/ecx/edx is optimal or very close to it.
When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

raymond

QuoteI do have a question about flag preservation. Do the API functions ever use the FPU and possibly change the content or state without saving and restoring the content and flags? What does the ABI say about preserving MMX and XMM registers?

The ABI does not cover the FPU (and MMX which uses the same registers). As I have indicated in other threads, at least starting with WinXP (and possibly before), MS programmers "discovered" the use of MMX and used it for most if not all their arithmetic. Whenever 2+2 was required, they used MMX which destroys the content of the FPU registers. All APIs which involve the display, sizing, etc. of child windows (such as a simple MessageBox) will modify the content of the FPU registers.

Run a small app (which includes the display of a MessageBox) in Ollydbg and watch what happens to the FPU content while the MessageBox is being displayed and you simply hover over it with the mouse cursor!!! :boohoo: :boohoo:

When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

donkey

Quote from: KeepingRealBusy on December 08, 2008, 06:24:21 PM
Another thing to consider is that the string functions are also using eax, as well as using the direction flag. In processing multiple precision arithmetic, comparisons and divisions are always top down (DF set), additions and multiplies are bottom up (DF cleared). I do a lot of this and this is where I got into the habit of preserving everything.

Hi KeepingRealBusy,

I don't completely agree about EAX, there is a difference in the need to preserve the registers. ECX may be reduced to an unknown number depending on how far you get along the string (eg when using REPNE SCASB) and that value can be used to indicate your character index while EAX (actually AL in strings) is known (after all you set it at the entry into the loop). Also since ECX is used explicitly in instructions such as LOOP and the shift instructions it has more functionality as a counting/index register and therefore more reason to be preserved across calls than EAX.

AFAIK direction flags are not changed by the API though I recall that there are API functions that will fail if the direction flag is set on entry, I still need an example of an API changing the flag before I can agree that Windows doesn't do it already. For the carry flag there are API functions that use it to return error status (DeviceIoControl for one) so preserving it would be problematic since you may need to know if the flag has changed state on exit from the API.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable