views:

140

answers:

2

Hi everybody, I'm having trouble understanding the behavior of the MS VC compiler on this one. This line compiles fine, but the result I get is not what I'd expect at all:

this->Test((char *)&CS2 - (char *)&CS1 == sizeof(void *));

The CS1 and CS2 arguments are declared as follows:

myFunction(tCS1* CS1, tCS2* CS2) {...

tCS1 and tCS2 are structures containing one int and one __int64, resp.

This is meant to check the distance on the stack between my arguments CS1 and CS2, which are both pointers. When I break execution on this line and use the debugger to get the addresses of my two variables, I find that they indeed are 8 bytes away from each other (x64 platform).

However, the result of the comparison is false.

Here is the assembly code generated by the compiler:

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax 

(then it does the comparison using the result stored in rdi, and makes the call)

Yes, the compiler is comparing the values of my pointer arguments, rather than their addresses. I'm missing a level of indirection here, where did it go?

Of course I can't reproduce this in a test environment, and I have no clue where to look anymore. I'm cross-compiling this bit of code on a 32-bits machine to an x64 platform (I have to), that's the only 'odd' thing about it. Any idea, any hint?

A: 

The assembly

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax

indicates CS1 and CS2 are not really stack arguments, but rather some global symbols - if I wanted to produce similar results, I'd do something like this:

int* CS1 = NULL, *CS2 = NULL; /* or any other value...*/
#define CS1 *CS1
#define CS2 *CS2

Of course this is ugly code - but have you checked you haven't such things in your code? Also, dynamic linker might play a role in it.

And last but not least: If you attempt to write code like:

void foo()
{
  int a;
  int b;
  printf("%d", &a-&b);
}

You should be aware that this is actually undefined behaviour, as C (and C++) only permits to subtract pointers pointing inside a single object (eg. array).

jpalecek
CS1 and CS2 really are my stack arguments, their addresses/values match those of my argument variables.
Christophe AGUETTAZ
"You should be aware that this is actually undefined behaviour, as C (and C++) only permits to subtract pointers pointing inside a single object (eg. array)."I always thought it worked the other way around: pointer substraction was allowed in C++ because it makes sense in such cases. But you might be right. However, I wrote the exact same piece of code in a test project and got the expected behavior, so it seems my compiler is tolerating such abuses.
Christophe AGUETTAZ
@Christophe: no, your compiler tolerated it in the specific case when you tested it. There is no guarantee it'll behave sensibly *every* time. Of course, compiler writers aren't malicious, and won't intentionally break your code. But they may sometimes apply optimizations that can change the behavior when you rely on undefined behavior.
jalf
@Christophe AGUETTAZ: Shouldn't taking an address of a stack argument look rather like `lea rax,[rbp+4]` and such?
jpalecek
This was compiled in debug mode, with all optimizations turned off (as was my test project).I don't defend the idea of using undefined behaviors that happen to work of course, and I don't like what this piece of code does, but I find it hard to believe that one level of indirection could get dropped because my pointers don't belong to the same array/object. In this specific case, the compiler can 'see' that they don't, but in the general case there's no way to tell if substracting two pointers should lead to undefined behavior or not, so I imagine that the most practical way to implement
Christophe AGUETTAZ
pointer substractoin is simply to substract the pointer values (and divide the result by the size of the pointed type) no matter what.This doesn't prove anything of course, but that's why I doubt that this really is the issue. Then again, it's the only plausible one that's come up so far.
Christophe AGUETTAZ
@jpalecek: Exactly, and that's the issue. The compiler does not take the argument's address, but its value instead.
Christophe AGUETTAZ
A: 
poolie
Yes, it's strange, and with nothing but the info I've given you to go, it's hardly possible to come up with an idea any better than that, but after all if I knew which other piece of info was relevant, I'd probably have the solution. I still think that something doesn't quite add up here, and that there might be a more satisfactory answer. But maybe not.Thanks everyone for the help.
Christophe AGUETTAZ
Just a guess. Is it possible that optimization is putting this arguments into registers, and, as the address of this arguments is being taken, they are being placed in a local variable? Then, just the compiler isn't placing them in the same order as if they were pushed on the stack, so that's why you get +4 and not -4. Is there something in the standard that grants a particular ordering for arguments addresses ?
asr
The ordering of arguments is defined by the calling convention on the platform. I don't think the C standard says anything about it.
poolie