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

Alloy

It seems like an easy idea with little space and performance overhead to have API calls leave registers it uses as they were found.
We all used to be something else. Nature has always recycled.

KeepingRealBusy

Alloy,

You are preaching to the wrong choir around here. Everyone seems to support the ABI and only the ABI (Application Binary Interface). I agree with you, but you and I seem to be a minority opinion.

see http://www.masm32.com/board/index.php?topic=10433.0

Dave.


jdoe

Quote from: KeepingRealBusy on December 04, 2008, 03:28:35 AM
This is what you get when you report this to MS.

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=356547

Now I understand that Microsoft is sometime slow to fix "real bugs", when they have to take care of such report... reproduce the "bug" and reply kindly to the person and all that because of a misunderstanding of someone about a high level syntax.

:snooty:


MichaelW

I thought Microsoft's response was entirely reasonable:
Quote
Posted by Microsoft on 7/18/2008 at 1:10 AM
Thank you for contacting Microsoft.

The behavior of INVOKE potentially modifying (and not restoring) EAX is expected. INVOKE is a high level language construct for an assembly language function call patterned largely after the C/C++ compiler. The Visual C/C++ compiler considers, among a few others, the register EAX free game across a function call. This makes more sense when you consider that EAX is generally used for a function's return value. MASM also chose this behavior.

Thank you,
Visual Studio Product Team

The solution for the problems with passing a byte or word register on a 32-bit stack is to pass the byte or word value in a 32-bit register.
eschew obfuscation

Rockoon

The STDCALL convention makes a lot of sense to me.

The traditional pointer registers EBX, EBP, ESI, and EDI are nonvolatile.
The traditional math registers: EAX, ECX, and EDX are volatile.

Note that many integer and bitwise instructions require using specifically EAX, EDX:EAX, ECX or CL, and also many have shorter encoding when using AL, AX, EAX, CL, or ECX.
Note that many memory instructions require using specifically ESI or EDI, or are encoded shorter using EBP or EBX.

Note that memory is often iterated over, such that the state of one of these traditional pointer registers could be important for thousands or even millions of cycles, with many function calls during the duration.

The convention "preserve all registers" would be very inefficient. Functions would be preserving registers even when it was not important to the calller that it do so.
The convention "preserve no registers" would also be very inefficient. Callers would be preserving their register state even when the callee does not modify them.

The happy medium is when about half of them are preserved. Callers can leverage some nonvolatile registers, and callees can leverage some volatile ones.
When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

BlackVortex

There isn't a shortage of registers, there's plenty to go around when coding correctly.

jj2007

Quote from: Alloy on December 04, 2008, 02:50:13 AM
It seems like an easy idea with little space and performance overhead to have API calls leave registers it uses as they were found.

It seems like. But fast API's would not be possible if Windows had to preserve all registers. Here are cycle counts on a Celeron M:

WinApi SetRect:  14     cycles
WinApi GetTickCount:  3      cycles

The push & pops alone needed to preserve eax, edx and ecx would significantly increase these timings.
The ABI makes perfect sense,  in that it strikes a compromise between your code's performance and the performance of the OS.
The problem is not the ABI; it's that people start hacking together some code without knowing about the ABI, and then spending entire nights to find the "bug".

Rainstorm

rockoon wrote..
QuoteThe traditional pointer registers EBX, EBP, ESI, and EDI are nonvolatile.
..then can EBP be used safely across WinApI calls without preserving it ?

thx
-

jj2007

Quote from: Rainstorm on December 04, 2008, 12:18:05 PM
..then can EBP be used safely across WinApI calls without preserving it ?

You are doing that all the time: Whenever you have local variables in a proc, and you use a WinAPI. A local variable is simply [ebp+X]...


Rockoon

Quote from: Rainstorm on December 04, 2008, 12:18:05 PM
rockoon wrote..
QuoteThe traditional pointer registers EBX, EBP, ESI, and EDI are nonvolatile.
..then can EBP be used safely across WinApI calls without preserving it ?

nonvolatile means they should be preserved by the called function (anything else would violate the stdcall convention)

Most 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.)

In these cases they simply use esp+constant and do not bother setting up a stack frame (push ebp / mov ebp, esp / sub esp, amount) but simply allocate the necessary storage immediately (sub esp, amount) .. even VB6 does this sort of optimization.

This allows ebp to be used as a general purpose register without losing track of the stack context, although it must be saved because its defined as nonvolatile by stdcallL.

I cannot remember the last time I used ebp for a stack frame in my assembler code.. its been a very very long time.. its rare to need to use the stack in such a way that its required.. consequently I also don't use the simplified procedure parameter declarations...

When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

donkey

Quote from: KeepingRealBusy on December 04, 2008, 03:05:22 AMYou are preaching to the wrong choir around here. Everyone seems to support the ABI and only the ABI (Application Binary Interface). I agree with you, but you and I seem to be a minority opinion.

Hi Dave,

Its not a matter of "supporting" the ABI or not, it is simply that these are the rules which govern programming in Windows. Without a set of guidelines for everybody to follow programming would be a nightmare, imagine if some functions returned results in EDI or ESI instead of (EDX:)EAX and others returned them on the stack... it is a recipe for buggy applications. The ABI is not only about register preservation but also dictates how stack frames are built and managed as well as how values are returned from procedures, calling conventions used etc... So, support is not the word here, it is simply a matter of living within the framework Microsoft has built for its OS.
"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

KeepingRealBusy

Donkey,

I was referring only to the register saving conventions. In my Public library procedures, I never return values, I treat them like functions returning VOID. If a value needs to be returned, a parameter is defined which contains the OFFSET to the location to which the value should be returned. I preserve ALL registers, and all flags (especially direction) except for Zero, Sign, and Carry. I return only a status indicating success (Zero) or failure (non-zero). Even for procedures that are defined as returning Nothing, I set Zero on return so that you do not need to check if a procedure returns a status or not, it always will, and you can always safely use JNZ Error or JZ Good following a call. A last status word is maintained and you can either get that last status value, or else test it against a defined status value to classify what kind of error was returned. You can call to get a formatted status message. I wrap all API calls to preserve all user registers and calls, returning any OS data (handle for open file, etc) and always check for an API error and return a status error of "API_ERROR" from my procedure if I get an error from the OS. I also  format error messages for API errors.

Note: In my Private library procedures, many calls use register parameters and return register results, but these are never accessed outside the library (no external prototypes).

None of what I do violates the ABI.

I do not foist my conventions on anyone else. 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.

Rockoon

Quote from: KeepingRealBusy on December 06, 2008, 01:51:55 PM
I do not foist my conventions on anyone else. 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.

Since you do not write in assembler for performance or codesize reasons, may I ask what your reasons are for writing in assembler?

When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.