Hey people, I am curerntly working on a linked list project but i must follow all calling conventions regarding tmp and saved regs. Now, ive read previous posts about them in this sub and in others yet i still do not understand some things.
What i do know (correct me if im wrong):
* S regs are callee-saved , so we store smth in them when we want that to be preserved across function calls or when that register/variable has a long lifetime.
* T regs are used for short-term operations, and its the caller's responsibility to save to the stack **if** whatever is in there is important.
Now here are my questions:
1. When calling a child function that uses an S register, do i have to **always** save/restore at the start and end of that child function, or only when the caller, or the caller's caller etc need that value? Lets say for example , that i do know ( as a programmer of my program) that the value of S is not needed by the caller function after child function returns ( i know a tmp reg would be better here, but lets say im forced to have an S register). After that, our S register is "dead" (as the caller does not need its value anymore), so when some other child is changing its contents, do i still have to save/restore the contents to adhere to the calling convention ?
2. In a loop that calls a ***leaf*** function where would the counter be saved, if the register of the counter is:
1. used by callee
2. not used by callee
3. If i have some arguments in a0,a1 in a child procedure but these are needed after the call , why save/restore to stack and not just assign them to some temp registers that **I KNOW** are not going to be destroyed by the child (i know as my program's programmer)?\[Assuming child is a leaf procedure here\]
4. In general, **when** is it needed to save/restore to the **stack** whatever is in S and T registers, when its possible to bypass this for certain, small programs that you know that some registers go unused by all procedures?
5. When first putting a value in an S register, do i have to worry about some other program being linked to mine will be using that so i need to save/restore S even in the beginning? When I know my program will not be linked with smth else, do i still save/restore? What happens to S if Indeed its being linked to some other program that i do not know anything about?
Thank you for your time and sorry for the lengthy post. I just had a lot on my mind and wanted to share it with you as my professor and all his TA's keep telling me the same thing over and over (the two bullets above), with no real world examples and a lot of abstraction. Apologies for any grammar/spelling mistakes, english is not my native language.
NR.
Wellll ... I think most of us understand pretty well the concept of what you're calling "S registers" and "T registers". Thing is, that terminology is pretty much Greek to us here, since we (most of us, anyhow) use assembly language on Intel/AMD processors (X86/X64), and not the RISC processor that you're apparently using. Other than that, yes, there are what we refer to as "volatile" (can be changed arbitrarily) and "non-volatile" (must be preserved) registers.
In the MASM32 world, at least on the Win32 platform, these registers are considered non-volatile and must be preserved across function calls: EBX, ESI, EDI, EBP. The other ones can all be trashed at will.
And of course you understand that this convention is enforced by the operating system, not the processor, right? The processor doesn't care particularly what you do with registers, but it's on account of the ABI (application binary iterface) of the OS. In other words, the OS makes the rules of the game here.
Quote from: n1k0s on March 18, 2024, 12:47:37 PMwith no real world examples and a lot of abstraction
mov esi, 123 ; a non-volatile register (S)
mov ecx, 456 ; a temporary, volatile register (T)
mov eax, 789 ; another T register
push eax ; save a register that will get trashed by Windows
invoke CreateWindowEx, ... a call to Windows
pop eax
print str$(eax), " is safe, hooray", 13, 10
print str$(ecx), " has been trashed by the evil OS", 13, 10
print str$(esi), " is still intact", 13, 10
This is all you need to know about
your code:
the OS will trash eax, ecx, edx. So if the OS is not involved, do whatever you need.
Let's turn to
the OS' point of view. From time to time, the OS calls
your code ("callback function"), and expects that
you don't change its precious non-volatile registers.
The main callback function you have to care about is WndProc:
WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
SWITCH uMsg
CASE WM_CREATE
...
CASE WM_DESTROY ; quit after WM_CLOSE
invoke PostQuitMessage, NULL
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp
The
uses esi edi ebx creates a series of push reg ... pop reg pairs to ensure that Windows finds its own precious registers unchanged.
Same code but explicit push & pop:
WndProc proc hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
push esi
push edi
push ebx
SWITCH uMsg
CASE WM_CREATE
...
CASE WM_DESTROY ; quit after WM_CLOSE
invoke PostQuitMessage, NULL
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
pop esi
pop edi
pop ebx
ret
WndProc endp
See also The "register gets trashed" trap (https://www.jj2007.eu/Masm32_Tips_Tricks_and_Traps.htm), and note that after the "hooray" line above, eax is, of course, trashed - for a simple
print, you need to call Windows under the hood.
Everything NoCForMe writes is correct, of course.
P.S., our tests show that recent versions of Windows couldn't care less if you respect the ABI or not:
mov esi, 123
mov edi, 456
mov ebx, 789
ret
WndProc endp
Finally, Microsh*t has realised that there are too many incompetent programmers around, so they decided to let the OS save esi edi ebx.
However, if you propose that "solution" to your profs, they will eat you alive: it's not documented and therefore tabooooooo!
Quote from: jj2007 on March 18, 2024, 09:18:16 PMP.S., our tests show that recent versions of Windows couldn't care less if you respect the ABI or not:
mov esi, 123
mov edi, 456
mov ebx, 789
ret
WndProc endp
Finally, Microsh*t has realised that there are too many incompetent programmers around, so they decided to let the OS save esi edi ebx.
However, if you propose that "solution" to your profs, they will eat you alive: it's not documented and therefore tabooooooo!
Just to be a little more clear here: Yes, the consensus 'round here seems to be that you do
not need to worry about any non-volatile registers in your code that is called by Windows (i.e., your "WinMain" function, no matter what it's called).
However, though it may be obvious, it bears remembering that Windows
itself absolutely does respect the ABI when you call any Windows function, meaning that the non-volatile registers (EBX, ESI, EDI) will be preserved across the function call. And it's good programming practice to follow this in your own code: unless it's known that saving these registers in a function isn't required, you should save and restore them in your functions as well.
Example: I often use EBX as a pointer to a list of structures to be processed sequentially. So I rely on both Windows and my own private functions to not disturb this register:
MOV EBX, OFFSET SomeList
doloop: MOV EAX, [EBX].SOMELIST.somePtr
CALL DoSomePtr
MOV EAX, [EBX].SOMELIST.someHandle
INVOKE BringWindowToTop, EAX ;Win32 function
. . .
ADD EBX, SIZEOF SOMELIST ;Next array element
CMP someSignalValue, 0
JNE doloop
Basically, if you use a non-volatile register (your S register) your subroutine should save it and restore it on exit.
Any non-volatile register (T) is fair game and can/will be clobbered.
Looking further into it, this (warning: pdf file) (https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf) document might help
I fully agree with the previous reply to the question of the original poster, but will still try to answer myself too below:
Quote from: n1k0s on March 18, 2024, 12:47:37 PM1. When calling a child function that uses an S register, do i have to **always** save/restore at the start and end of that child function, or only when the caller, or the caller's caller etc need that value? Lets say for example , that i do know ( as a programmer of my program) that the value of S is not needed by the caller function after child function returns ( i know a tmp reg would be better here, but lets say im forced to have an S register). After that, our S register is "dead" (as the caller does not need its value anymore), so when some other child is changing its contents, do i still have to save/restore the contents to adhere to the calling convention ?
The best practice is to follow the ABI strictly, i.e. ALWAYS save the callee-saved registers, even if you know that the function you call is a leaf function. Because one day it may CEASE to be a leaf function.
Quote2. In a loop that calls a ***leaf*** function where would the counter be saved, if the register of the counter is:
1. used by callee
2. not used by callee
Again, even if (2) is valid NOW, how can you be sure that it will always be? So, save the counter in the caller function.
Quote3. If i have some arguments in a0,a1 in a child procedure but these are needed after the call , why save/restore to stack and not just assign them to some temp registers that **I KNOW** are not going to be destroyed by the child (i know as my program's programmer)?\[Assuming child is a leaf procedure here\]
The argument registers are by definition ( http://five-embeddev.com/quickref/regs_abi.html ) caller-saved, so if you need to use their old values after you call the function (leaf or not), you have to save them too.
Quote4. In general, **when** is it needed to save/restore to the **stack** whatever is in S and T registers, when its possible to bypass this for certain, small programs that you know that some registers go unused by all procedures?
Always save in the stack the calle-saved registers when you're called, and the caller-saved ones when you call.
Quote5. When first putting a value in an S register, do i have to worry about some other program being linked to mine will be using that so i need to save/restore S even in the beginning? When I know my program will not be linked with smth else, do i still save/restore? What happens to S if Indeed its being linked to some other program that i do not know anything about?
If by "linked" you mean "called", how do you know that it won't be called? Don't you write it exactly in order for it to be called by other functions? In general, follow strictly the ABI and don't worry. It's like the traffic code. If everybody follows it strictly and there are not technical problems with any vehicle and no health or mental problems with any people, there will be no traffic incidents.
Quote from: n1k0s on March 18, 2024, 12:47:37 PMWhat i do know (correct me if im wrong):
* S regs are callee-saved , so we store smth in them when we want that to be preserved across function calls or when that register/variable has a long lifetime.
* T regs are used for short-term operations, and its the caller's responsibility to save to the stack **if** whatever is in there is important.
If you're calling function F, F will not change a callee-saved register. This can be faked by F, saving the register at entry and restoring it at exit. The preservation property is a guarantee. It is also known as call-preserved (or nonvolatile).
The other kind of register is caller-saved. No guarantee that F will change or not change the register. The preservation property is also known as call-clobbered (or volatile) because you must assume it can be changed by F.