While reading the Microsoft Docs, I came across the following table about register usage (https://docs.microsoft.com/en-us/cpp/build/register-usage). It says that some of the registers must be preserved. What exactly does it mean? Also when checking the caller/callee saved registers (https://docs.microsoft.com/en-us/cpp/build/caller-callee-saved-registersl) page, there are list of registers that are volatile and nonvolatile registers. And the nonvolatile ones need to be saved or restored. Does it mean I need to push and pop all the nonvolatile registers that I use in my functions?
Yes, in 32-bit code it's push & pop, see here (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm).
In 64-bit code it can be push & pop, but only under certain tricky conditions (->alignment, shadow space). Better define a local variable:
MyTest proc
Local @rsi:QWORD
mov @rsi, rsi
...
mov rsi, @rsi
ret
MyTest endp
QuoteDoes it mean I need to push and pop all the nonvolatile registers that I use in my functions?
The answer to that question could be YES or NO; it all depends on the kind of code you intend to write.
If you intend to
reuse part of your code in other programs, you definitely need to preserve such registers. Your other programs would expect that in the same way that you expect Windows functions to preserve them.
If you intend to use repeatable code within a PROC using local variables, you certainly need to preserve the EBP register at the very least. There may not be any need to preserve other registers (apart from the ESP) as long as you don't have any need for such preservation in the calling code.
In the 'old' days prior to WinXP, it was important to preserve all the nonvolatile registers; otherwise, the program could crash after calling a Windows function. The MS programmers finally woke up and preserved those registers themselves within the OS. If you write code to be used with Win98 or earlier, you then need to preserve them all!
I write many short programs to solve math problems for example. I don't normally use any formal PROC and use the EBX, ESI, EDI and EBP freely without preservation within the code. The only detail I watch is the stack balance (ESP). Such program function perfectly and return to the OS without any glitch.
And, for your information, the FPU registers are all considered
volatile by the OS. If those contain any useful data, make sure to preserve such data before calling any Windows function.
Quote from: raymond on January 02, 2018, 06:49:40 AMIn the 'old' days prior to WinXP, it was important to preserve all the nonvolatile registers; otherwise, the program could crash after calling a Windows function. The MS programmers finally woke up and preserved those registers themselves within the OS.
Dear Raymond,
Do you have any official document regarding this behaviour? Occasionally I forget to preserve esi edi ebx in a WndProc, and it never crashes, but I wonder whether this has ever been documented...
There is an old preservation of esi, edi, ebx in general code (http://masm32.com/board/index.php?topic=250.0) thread, but it didn't come to a conclusion. GCC, for example, preserves them:
00401340 Ú. 55 push ebp
00401341 ³. 57 push edi
00401342 ³. 56 push esi
00401343 ³. 53 push ebx
00401344 ³. 81EC 8C010000 sub esp, 18C
0040134A ³. 8BB424 A0010000 mov esi, [esp+1A0]
00401351 ³. 8B9C24 A4010000 mov ebx, [esp+1A4]
00401358 ³. 83FB 02 cmp ebx, 2 ; WM_DESTROY, cascaded IF (messages 1..111, 4 exits)
0040135B ³. 0F84 20020000 je 00401581
00401361 ³. 81FB 11010000 cmp ebx, 111 ; WM_COMMAND
00401367 ³. 0F84 71010000 je 004014DE
0040136D ³. 83FB 01 cmp ebx, 1 ; WM_CREATE
00401370 ³. 0F85 1A020000 jne 00401590
00401376 ³. E8 1D0C0000 call <jmp.&USER32.CreateMenu> ; [USER32.CreateMenu, case 1 (WM_CREATE) of cascaded IF Tmp.401358
0040137B ³. 89C5 mov ebp, eax
0040137D ³. E8 1E0C0000 call <jmp.&USER32.CreatePopupMenu> ; [USER32.CreatePopupMenu
The same source compiled with VC shows no register preservation; however, VC doesn't use esi edi ebx, either, so that is no proof 8)
With 64 bit Windows code, the important factor is procedure alignment and this has to be correct at the procedure entry point. When you use PUSH / POP you alter the stack pointer RSP and unless you know exactly what you are doing and why, you risk messing up the alignment and by doing so the app will exit with no warning or explanation. Win64 has more registers to start with so there is less pressure to have to deal with the non-volatile registers but if you need more registers, the method that JJ explained is the safe way to do this. Allocate 64 bit LOCAL variables and copy the non-volatile register into it and at the end of the procedure, copy the local back to the register.
The Intel 32 bit ABI was started on Windows platforms with NT3.5 from memory and was standard in WinNT4. It remains current in 32 bit Windows code. If you bother to have a good look at the Intel ABI, you will see how it works and why it is done. The OS controls the non-volatile registers for its own use and if you exit a procedure with a modified non-volatile register, you risk the OS shutting down the application with a general protection fault.
I have always wondered why some programmers play Russian Roulette with register conventions, get something to work on one OS version but it crashes on the next. Code written correctly for Win95 OEM runs correctly on Win10 32 bit subsystem. The general rule is to observe the Intel ABI and you code will run correctly from one OS version to another. Play Russian Roulette and you risk it exploding in you face on another OS version and make a jerk out of you.
Notwithstanding the above, once you know how to do it properly, writing simple test apps lets you get away with sloppy code in a single procedure app and they are often handy when you are writing "write once, run once" code but try it in production code and you are in trouble.
QuoteDo you have any official document regarding this behaviour?
Nothing official, only own experience.
A few years ago, I had written a program to provide a specific slide show for some 600 pictures distributed in about a dozen subfolders. It had been developed on a WinXP OS and ran perfectly. However, I found out later that it would crash when trying to run it on a different computer under Win98.
Upon examination, the one and only cause of the problem was a single instance of preserving the ESI/EDI registers on the stack and restoring them later in the wrong order :icon_redface:. Simply exchanging those two bytes in the .exe file with a hex editor solved the crashing problem when running on that Win98 computer.
I Think for Writing recursive coding, you need to use macros like this for your favourite registers you wanna code with,its for 32bit but can be customized for 64bit to what register usage you need to push/pop or customized to make use of virtual stack for general 64bit regs
its from unfinished try to make procedurally generated tree in masm,inspired by fluxus script
fluxus is what makes all that patterns in your mediaplayer
.data?
statestack dd 0 dup (65536)
dd 0 dup (65536)
.data
ALIGN 16
vectconst0 REAL4 0.0,0.0,0.0,0.0 ;room for constanst used in fluxusprogram
vectconst1 REAL4 0.0,0.0,0.0,0.0
vectconst2 REAL4 0.0,0.0,0.0,0.0
vectconst3 REAL4 0.0,0.0,0.0,0.0
vectconst4 REAL4 0.0,0.0,0.0,0.0
vectconst5 REAL4 0.0,0.0,0.0,0.0
vectconst6 REAL4 0.0,0.0,0.0,0.0
vectconst7 REAL4 0.0,0.0,0.0,0.0
vectconst8 REAL4 0.0,0.0,0.0,0.0
tmpxmm0 REAL4 0.0,0.0,0.0,0.0 ;temporary storage
tmpxmm1 REAL4 0.0,0.0,0.0,0.0
tmpxmm2 REAL4 0.0,0.0,0.0,0.0
tmpxmm3 REAL4 0.0,0.0,0.0,0.0
tmpxmm4 REAL4 0.0,0.0,0.0,0.0
tmpxmm5 REAL4 0.0,0.0,0.0,0.0
tmpxmm6 REAL4 0.0,0.0,0.0,0.0
tmpxmm7 REAL4 0.0,0.0,0.0,0.0
tmpf0 REAL4 0.0,0.0,0.0,0.0
tmpf1 REAL4 0.0,0.0,0.0,0.0
tmpf2 REAL4 0.0,0.0,0.0,0.0
X equ 0
Y equ 4
Z equ 8
.code
; #########################################################################
PUSHSTATE MACRO
FXSAVE [ebx]
add ebx,512
ENDM
POPSTATE MACRO
sub ebx,512
FXRSTOR [ebx]
ENDM
TRANSLATE MACRO vectconst
addps XMM0,vectconst
addps XMM1,vectconst
addps XMM2,vectconst
addps XMM3,vectconst
addps XMM4,vectconst
addps XMM5,vectconst
addps XMM6,vectconst
addps XMM7,vectconst
ENDM
SCALE MACRO vectconst
mulps XMM0,vectconst
mulps XMM1,vectconst
mulps XMM2,vectconst
mulps XMM3,vectconst
mulps XMM4,vectconst
mulps XMM5,vectconst
mulps XMM6,vectconst
mulps XMM7,vectconst
ENDM