Sign in to follow this  

Unexpectedly horrible optimization. VS2015. x64 target.

This topic is 482 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am experiencing a perplexing registry spill in a function that uses ONLY (spill inclusive) the following registers: rax, rbx, rcx, rdx, rsp.

 

rsp: for spill (and one call - cannot remember whether it is required ... should be pointless in regards to unwind, dunno)

rbx: for spill

 

Why would that happen? What could cause that? It does not even use all the volatile registers up ( https://msdn.microsoft.com/en-us/library/9z1stfyw.aspx ) - at least R8 and R9 are free (i think R10 and R11 are also with standard Win x64 ABI).

 

Excluding code for rbx spill itself, rbx ends up used exactly 4 times:

 

Twice on fast path:

movzx       ebx,byte ptr [rcx+rax]
lea         rdx,[rax+rbx*4]

 

Twice on slow path, which i do not really care about:
lock cmpxchg dword ptr [rbx],ecx
lock cmpxchg dword ptr [rbx],ecx

 

None of them look suspicious (pretty sure LEA can take whatever as its index register). It is hard to believe VS2015 fails that badly with the basics ... umm ... ???

 

I guess the function responsible for the call (also on the slow path) is the cause - if i comment it out then rsp and rbx will be unused.

If i allow inlining the function then the fast path will be filled with spill code (quadrupled).

The function, from callers perspective, is a simple one - equivalent to "static int foo(int)".

The slow path ends with another function call, but it is tail-call optimized ("jmp". so, does not care about caller stack frame at all - irrelevant to the issue).

 

Fun times.

Share this post


Link to post
Share on other sites

Not really familiar with ASM, but codegen bugs/shortcomings are not unheard of. Which VS 2015 version BTW? FYI, Update 3 introduced a completely new optimizer which is on by default.

Edited by Stinkfist

Share this post


Link to post
Share on other sites

Why are you looking at the asm code, because it seems clear that you are not coding in that code? Can you post your source code so we can have a look at that and potentially spot the mistake in that.

Remeber that the compiler also has to be able to generate 32bit code where you only have eax, ebx, ecx and edx as general purpose registers, so some paths in the optimiser might still be using that code.

Edited by NightCreature83

Share this post


Link to post
Share on other sites

Not really familiar with ASM, but codegen bugs/shortcomings are not unheard of. Which VS 2015 version BTW? FYI, Update 3 introduced a completely new optimizer which is on by default.

 

Still on Update1. New optimizer? Did not know that, thanks. Well, then i need to update - will do that.

 

However, before i revisited the forums today i noticed (since all that code was on slow path i did not care much what happened on that path before) that i can easily convince the compiler to tail-call optimize out the, somehow offending, function call:

 

----------------- before:

        auto res = man.get(at);
        man.lock.put();
        return res;
----------------- after:

        return man.getAndUnlock(at);
 

... and the compiler happily obliged - all of the spill nonsense is gone (ebx gone and all the frame junk also gone [since all the function calls got tail-call optimized causing the caller to be essentially leaf function enabling all relevant optimizations]).

 

So, unfortunately can not tell whether U3 would have made a difference (would be interesting to know, so might un-fix when i update).

Share this post


Link to post
Share on other sites

Followup:

 

Tried the old code with U3 - same behavior.

Which at this point i am confident enough to call a bug (*) - no compiler should fail at this kind of basics. I have compared both compilations and they are nearly identical (even ends up using the exact same registers with same instructions throughout) - with the sole exception of minor difference at the function call site and the unnecessary spill (non-spilling code just uses R8 [free to trash] instead of RBX [requires spill]) and frame code.

 

Platform Toolset: Visual Studio 2015 (v140). Is this correct? How can i confirm the new compiler is in use (edit: i did notice that the compiler specifically told it had to recompile all functions instead of doing the usual incremental rebuild)?

 

Did i miss something?

 

*) Which it obviously technically is not - since the functional end result is exactly what it should be.

Edited by tanzanite7

Share this post


Link to post
Share on other sites
I have no idea what it is you're looking at or why you think it's wrong, let alone an actual compiler bug.

Post a complete but minimal repro case that demonstrates this, along with the ASM output and a proper description of your reasoning as to why this "obvious" bug is really actually even a problem.

Share this post


Link to post
Share on other sites

This topic is 482 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this