2

As suggested to me in another question i checked the windows ABI and i'm left a little confused about what i can and cannot do if i'm not calling windows API myself.

My scenario is i'm programming .NET and need a small chunk of code in asm targeting a specific processor for a time critical section of code that does heavy multi pass processing on an array.

When checking the register information in the ABI at https://msdn.microsoft.com/en-us/library/9z1stfyw.aspx

I'm left a little confused about what applies to me if i

1) Don't call the windows API from the asm code

2) Don't return a value and take a single parameter.

Here is what i understand, am i getting all of it right?

RAX : i can overwrite this without preserving it as the function doesn't expect a return value

RCX : I need to preserve this as this is where the single int parameter will be passed, then i can overwrite it and not restore it

RDX/R8/R9 : Should not be initialized as there are no such parameters in my method, i can overwrite those and not restore them

R10/R11 : I can overwrite those without saving them, if the caller needs it he is in charge of preserving them

R12/R13/R14/R15/RDI/RSI/RBX : I can overwrite them but i first need to save them (or can i just not save them if i'm not calling the windows API?)

RBP/RSP : I'm assuming i shouldn't touch those?

If so am i correct that this is the right way to handle this (if i don't care about the time taking to preserve data and need as many registers available as possible)? Or is there a way to use even more registers?

; save required registers

push r12
push r13
push r14
push r15
push rdi
push rsi
push rbx

; my own array processing code here, using rax as the memory address passed as the first parameter
; safe to use rax rbx rcx rdx r8 r9 r10 r11 r12 r13 r14 r15 rdi rsi giving me 14 64bit registers
; 1 for the array address 13 for processing
; should not touch rbp rsp

; restore required registers


pop rbx
pop rsi
pop rdi
pop r15
pop r14
pop r13
pop r12
6
  • Well, as an obvious first comment, you need to pop the registers in reverse order. As written, rbx ends up in r12, etc. Do you need rbp? And have you looked at the sse instructions? Cuz there's a lot of array handling stuff there, as well as its own registers. Commented Jun 13, 2016 at 8:21
  • @DavidWohlferd Yes i typed this too fast you're right i need to swap the pop order. I first want to get something simplistic running on the base X86-64 set before deciding on a specific processor and targeting it's instruction set (this is for a program that is only meant to run on a single batch of servers, i'm control of both the code and the hardware decision so i can disregard any form of compatibility with anything else). Going to edit the pop order now Commented Jun 13, 2016 at 8:55
  • R12 et al must be saved if they are being changed whether you call windows or not. RBP is the same. RSP is the stack pointer, so it gets modified with every push/pop. The rest look right. Also, essentially every x64 box supports up to SSE3 (see stackoverflow.com/a/28186601/2189500). By selecting x64, you have already included support for a bunch of instructions. Commented Jun 13, 2016 at 9:45
  • 4
    In addition to register preservation rules, you also need to follow stack alignment rules and unwind codes. Because even though your code may not explicitly call a Windows API, it can do so implicitly: As part of exception handling. Commented Jun 13, 2016 at 14:18
  • 2
    If an exception occurs, the OS will try to unwind your stack in order to dispatch handlers. The lack of unwind codes means that the OS won't be able to find the exception handlers registered by other functions and will most likely terminate the process on the grounds that the situation is unrecoverable. Commented Jun 13, 2016 at 18:19

2 Answers 2

3

TL;DR: if you need registers that are marked preserved, push/pop them in proper order. With your code you can use those 14 registers you mention without issues. You may touch RBP if you preserve it, but don't touch RSP basically ever.

It does matter if you call Windows APIs but not in the way I assume you think. The ABI says what registers you must preserve. The preservation information means that the caller knows that there are registers you will not change. You don't need to call any Windows API functions for that requirement to be there.

The idea as an analogue (yeah, I know...): Here are five different colored stacks of sticky notes. You can use any of them, but if you need the red or the blue ones, could you keep the top one in a safe place and put it back when you stop since I need the phone numbers on them. About the other colors I don't care, they were just scratch paper and I've written the information elsewhere.

So if you call an external function you know that no function will ever change the value of the registers marked as preserved. Any other register may change their values and you have to make sure you don't have anything there that needs to be preserved.

And when your function is called, the caller expects the same: if they put a value in a preserved register, it will have the same value after the call. But any non-preserved registers may be whatever and they will make sure they store those values if they need to keep them.

The return value register you may use however you want. If the function doesn't return a value the caller must not expect it to have any specific value and also will not expect it to preserve its value.

Sign up to request clarification or add additional context in comments.

2 Comments

I already understood all that but this doesn't really answer my overall question (is the sample code i gave enough to guard me as a callee from doing anything wrong to the caller, since i'm always a callee and never a caller that's all i need to worry about which is why i mentioned i don't call any API from asm, and are there ways to get more useful registers or is that about it). While i appreciate the time you took to help TL;DR; doesn't really make for a great answer and this one missed the spot, the only thing that helps is the mention about the return register.
@RonanThibaudau Ok, I added a confirmation of your understanding that you can use those 14 registers this way.
2

You only need to preserve the registers you use. If you don't use all of these, you don't need to preserve all of them.

You can freely use RAX, RCX, RDX, R8, R9, R10 and R11. The latter two must be preserved by the caller, if necessary, not by your function.

Most of the time, these registers (or their subregisters like EAX) are enough for my purposes. I hardly ever need more.

Of course, if any of these (e.g. RCX) contain arguments for your function, it is up to you to preserve them for yourself as long as you need them. How you do that is also up to you. But if you push them, make sure that there is a corresponding pop somewhere.

Use This MSDN page as a guide.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.