-1

According to https://learn.microsoft.com/ru-ru/cpp/build/x64-software-conventions?view=vs-2017 - xmm6:xmm15 are non-volatile. But my program doesn't crash if I don't preserve xmm6, xmm7. I don't call OS from assembly. Do I need to preserve registers in this case ? I am running under Windows 7.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Andrey
  • 5,932
  • 3
  • 17
  • 35
  • 4
    Your program doesn't crash today, but no guarantees about tomorrow. (It could also be behaving incorrectly in a way that doesn't crash.) – Raymond Chen Nov 18 '19 at 18:45
  • 1
    If you never return from a function then that function doesn't need to preserve any registers because you never need to restore them. Otherwise if you do return and the non-volatile registers have changed then you've broken a promise you made to the caller and anything can happen. – Ross Ridge Nov 18 '19 at 18:54
  • I understand, but my program is big enough. It seems strange that no crashes happened - it looks like compiler doesnt use xmm6-xmm7. It would be great to avoid saving these registers because it affects performance (not much but still). – Andrey Nov 18 '19 at 19:00
  • 2
    The compiler makes no promises not to use XMM6 or XMM7 and will use these registers whenever it finds it beneficial to do so, Because of auto-vectorization and other optimizations they can up being used in functions where you would think the compiler would have no call to use them. Also remember that the compiler you're using today may not be same compiler you're using tomorrow. Finally crashing isn't the only possible bad result, more likely your program doesn't crash, but as Raymond Chen said, behaves incorrectly. That can be a very hard bug to track down if you don't know the cause. – Ross Ridge Nov 18 '19 at 19:14
  • The OS that is calling your function expects the registers to be preserved. If you return with different values in those registers, then the OS may behave erratically in ways that cannot be predicted (and which can change at any security update). – Raymond Chen Nov 18 '19 at 19:17
  • My function is a windows application. Can I just save these registers in the start of my app and restore them in the end ? – Andrey Nov 18 '19 at 19:20
  • 2
    Only if your entire Windows application is written in assembly, then you can follow whatever calling convention you want when calling your own assembly functions from your own assembly functions. Otherwise if calling your own assembly function from a C++ function then you need to follow the standard calling convention, otherwise you're breaking a promise to the compiler and anything can happen as a result. – Ross Ridge Nov 18 '19 at 19:27

1 Answers1

3

Breaking the ABI does not guarantee a fault, just like UB in C might happen to work. e.g. perhaps a caller saves/restores (on entry/exit) the XMM reg you destroy, but didn't care about their values across the call to your function. e.g. maybe it wanted to keep an FP value in a register across a printf call, not across the call to your function.

Or perhaps nothing uses them, and main and the CRT startup code don't care.


The way ABI guarantees work is that if you follow them, you guarantee no problems, not the other way around.

IDK if there's a calling-convention "checker" wrapper function that verifies that all call-preserved regs are correctly preserved, and that you didn't step on any stack space outside the shadow space and (if any) your stack args. Probably someone's written something like that. e.g. Writing a thunk to verify SysV ABI compliance


It would be great to avoid saving these registers because it affects performance (not much but still).

If you're compiling your C with GCC or clang, you could declare your asm function's prototype as using the x86-64 System V ABI where all of xmm0..15 are call-clobbered (and arg-passing uses different registers), using a GCC function attribute

__attribute__((sysv_abi))
extern "C" int myfunc(void);

Then the caller will have to save/restore all of xmm6..15 because it has to assume the callee destroyed them all.

So do this for a function high enough up the call tree that this overhead is amortized over many function calls.

(Or better, use intrinsics so your use of XMM regs can inline and optimize away the call/ret overhead as well as the save/restore XMM reg overhead. If save/restore or call overhead matters, the solution is more inlining so functions aren't so small.)

See Work around windows calling convention preserving xmm registers? for that. Beware that ICC is buggy and fails to save/restore XMM6..15 around a call to a System V ABI function, and GCC doesn't properly support AVX on Windows (stack alignment problems).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847