Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Order of Variable

Reply
Thread Tools

Order of Variable

 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-11-2010
* tni, on 11.09.2010 14:40:
> On 2010-09-11 14:14, Alf P. Steinbach /Usenet wrote:
>> Not entirely sure what you're talking about. I vaguely remember g++
>> having some "strict aliasing" bug. Hopefully that's not it?
>>
>> Could you give a concrete, minimal but complete example where you think
>> that access could be "reorder/optimize away"?

>
> I can't give you a simple example, but what I have seen is similar to the
> behavior in this case (in my complex case, the structs started with the same
> sequence):
>
> #include <iostream>
> #include <stdint.h>
>
> struct A {
> uint32_t x;
> uint32_t y;
> };
>
> struct B {
> float x;
> uint32_t y;
> };
>
> int main() {
> A a = A();
> B& b = *reinterpret_cast<B*>(&a);
> b.x = 42;
> std::cout << a.x << " " << b.x << std::endl;
> return 0;
> }
>
> Output (g++ -O2):
> 0 42


OK, this is an optimizer problem/bug. The optimizer "knows" that 'a' has not
been modified, by assuming a little too much, and optimizes away 'a.x'. You can
use '-fno-strict-aliasing' to turn off that silliness, or, you can deny the
optimizer the knowledge about the particular instance 'a'.

E.g. in the OP's example the instance was allocated dynamically.

Like ...


<code>
#include <iostream>
typedef unsigned uint32_t;

struct A {
uint32_t x;
uint32_t y;
};

struct B {
uint32_t x;
uint32_t y;
};

int main() {
A& a = *new A();
B& b = *reinterpret_cast< B* >( &a );
b.x = 42;
std::cout << a.x << " " << b.x << std::endl;
}
</code>


.... which due to the new-ing (as in the OP's article) produces no strict
aliasing warning and the result "42 42".

About how the g++ optimizer's "strict aliasing" assumption is invalid: the Holy
Standard has no notion of strict aliasing.


Cheers & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
 
 
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-11-2010
Alf P. Steinbach /Usenet wrote:

> * tni, on 11.09.2010 14:40:
>> On 2010-09-11 14:14, Alf P. Steinbach /Usenet wrote:
>>> Not entirely sure what you're talking about. I vaguely remember g++
>>> having some "strict aliasing" bug. Hopefully that's not it?
>>>
>>> Could you give a concrete, minimal but complete example where you think
>>> that access could be "reorder/optimize away"?

>>
>> I can't give you a simple example, but what I have seen is similar to the
>> behavior in this case (in my complex case, the structs started with the
>> same sequence):
>>
>> #include <iostream>
>> #include <stdint.h>
>>
>> struct A {
>> uint32_t x;
>> uint32_t y;
>> };
>>
>> struct B {
>> float x;
>> uint32_t y;
>> };
>>
>> int main() {
>> A a = A();
>> B& b = *reinterpret_cast<B*>(&a);
>> b.x = 42;
>> std::cout << a.x << " " << b.x << std::endl;
>> return 0;
>> }
>>
>> Output (g++ -O2):
>> 0 42

>
> OK, this is an optimizer problem/bug. The optimizer "knows" that 'a' has
> not been modified, by assuming a little too much, and optimizes away
> 'a.x'. You can use '-fno-strict-aliasing' to turn off that silliness, or,
> you can deny the optimizer the knowledge about the particular instance
> 'a'.
>


I believe it's completely valid for the compiler to optimize to this output.
"a.x" requires "x" to refer to an uint32_t object by 3.10/15, but you have
no guarantee that this is the case anymore after b.x was assigned the value
42. If storage of b.x and and a.x overlap, lifetime of a.x ends because you
have reused its memory (just like in the union case, you would have switched
the active member). a.x needs to be a char or unsigned char for this to be
valid.
 
Reply With Quote
 
 
 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-11-2010
* Johannes Schaub (litb), on 11.09.2010 16:49:
> Alf P. Steinbach /Usenet wrote:
>
>> * tni, on 11.09.2010 14:40:
>>> On 2010-09-11 14:14, Alf P. Steinbach /Usenet wrote:
>>>> Not entirely sure what you're talking about. I vaguely remember g++
>>>> having some "strict aliasing" bug. Hopefully that's not it?
>>>>
>>>> Could you give a concrete, minimal but complete example where you think
>>>> that access could be "reorder/optimize away"?
>>>
>>> I can't give you a simple example, but what I have seen is similar to the
>>> behavior in this case (in my complex case, the structs started with the
>>> same sequence):
>>>
>>> #include<iostream>
>>> #include<stdint.h>
>>>
>>> struct A {
>>> uint32_t x;
>>> uint32_t y;
>>> };
>>>
>>> struct B {
>>> float x;
>>> uint32_t y;
>>> };
>>>
>>> int main() {
>>> A a = A();
>>> B& b = *reinterpret_cast<B*>(&a);
>>> b.x = 42;
>>> std::cout<< a.x<< " "<< b.x<< std::endl;
>>> return 0;
>>> }
>>>
>>> Output (g++ -O2):
>>> 0 42

>>
>> OK, this is an optimizer problem/bug. The optimizer "knows" that 'a' has
>> not been modified, by assuming a little too much, and optimizes away
>> 'a.x'. You can use '-fno-strict-aliasing' to turn off that silliness, or,
>> you can deny the optimizer the knowledge about the particular instance
>> 'a'.
>>

>
> I believe it's completely valid for the compiler to optimize to this output.


Oh yes, in that particular case, since the two x'es have different type.

But not in the case under discussion where the x'es have the same type.


> "a.x" requires "x" to refer to an uint32_t object by 3.10/15, but you have
> no guarantee that this is the case anymore after b.x was assigned the value
> 42. If storage of b.x and and a.x overlap, lifetime of a.x ends because you
> have reused its memory (just like in the union case, you would have switched
> the active member). a.x needs to be a char or unsigned char for this to be
> valid.


It's valid to access a.x in that case, and in the case of same type. However, I
don't think the g++ optimizer cares. I believe (I don't have time to check it)
that it will produce the 0 no matter the type of a.x as long as it's numeric.

Which goes to show that the assumption it relies on is invalid.

It would be simpler if you had quoted the example in my reply.


Cheers,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-11-2010
tni wrote:

> On 2010-09-11 14:14, Alf P. Steinbach /Usenet wrote:
>> Not entirely sure what you're talking about. I vaguely remember g++
>> having some "strict aliasing" bug. Hopefully that's not it?
>>
>> Could you give a concrete, minimal but complete example where you think
>> that access could be "reorder/optimize away"?

>
> I can't give you a simple example, but what I have seen is similar to
> the behavior in this case (in my complex case, the structs started with
> the same sequence):
>
> #include <iostream>
> #include <stdint.h>
>
> struct A {
> uint32_t x;
> uint32_t y;
> };
>
> struct B {
> float x;
> uint32_t y;
> };
>
> int main() {
> A a = A();
> B& b = *reinterpret_cast<B*>(&a);
> b.x = 42;
> std::cout << a.x << " " << b.x << std::endl;
> return 0;
> }
>
> Output (g++ -O2):
> 0 42


g++ 4.5 does act differently, it seems. It doesn't optimize this anymore.
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      09-11-2010
On Sep 10, 11:14 pm, tni <t...@example.invalid> wrote:
> On 2010-09-10 15:31, Alf P. Steinbach /Usenet wrote:


> > * tni, on 10.09.2010 08:31:
> >> On 2010-09-10 5:51, Alf P. Steinbach /Usenet wrote:
> >>> In C++98 you're formally in UB-land when you try that on
> >>> non-POD types such as above.


> >>> For POD types the standard supports it via the discussion
> >>> of layout-compatible types in §9.2, and in particular
> >>> support of reinterpret_cast from first member in struct to
> >>> struct and vice versa in §9.2/17.


> >> Things aren't quite that rosy. You still violate the
> >> aliasing rules and there enough compilers that will happily
> >> generate wrong code.


> > Nah to both.


> I've seen something like that miscompiled by g++ (IIRC
> somewhere around version 4.0).


The issue is not simple, and formally g++ isn't conformant in
this respect (but it has nothing to do with §9.2/17---I've not
verified, but I suspect that g++ does handle that correctly).
But if I understand correctly, the C committee thinks that it is
the standard which is broken, not g++---the standard guarantees
too much.

The exact issue was something like:

union U { int a; double b; }

int f(int* a, double *b)
{
// g++ reorders the following two statements...
int result = *a;
*b = 3.14159;
}

// ...
U u;
u.a = 42;
int i = f(&u.a, &u.b);

Technically, the above code fulfills the requirements of the
standard; you're reading the last element written in the union,
then writing a different element. IIRC, the opinion of the C
committee was that this *should* only be guaranteed to work when
the actual access is through the union type.

This is, of course, more or less irrelevant here, and I suspect
that g++ will recognize layout compatible prefixes, and take
those into account when doing its aliasing analysis. (I suspect
this because the idiom is so prevelant in C. I seem to recall
having seen something similar in the sources of gcc, in fact.)

> >> The only safe thing to do is memcpy or using a union (if
> >> your C++ compiler supports C99 semantics for the union
> >> access, the C++ standard itself doesn't really allow it).


> > Ouch.


> > It's an old C technique. No compiler will foul it up. Don't
> > add needless complexity.


> What's your point?


> This:
> float f;
> int i = *(int*) &f;


> is an old C technique as well with lots of code using it.


> What makes you think it's not a violation of the aliasing
> rules? Given your reference to the first member, you did
> notice that the OP wanted to access the second one as well,
> right?


That's maybe an issue in C++. The wording of the C standard
guarantees that all elements of a common prefix will work. And
I'm not sure, but I seem to recall that it was guaranteed even
if a union wasn't involved.

> (I can see the argument that access to the first member might
> be safe. But the language in the standard isn't really
> explicit that you are not violating the aliasing rules and
> that's always dangerous.)


The first member of a PODS is guaranteed to be at the same
address as the structure itself. That's one guarantee. There
is another one concerning PODS with layout compatible initial
sequences. In C++, this is only guaranteed if the structs are
in a union; in C, I think it is generally guaranteed (but I
don't have access to a C standard here to verify). In practice,
things like:

struct Node
{
int nodeType;
int childCount;
};

struct BinaryOperatorNode
{
int nodeType;
int childCount;
Node* left;
Node* right;
};

and then:

Node* currentNode;
// ...
if (isBinaryNode(currentNode->nodeType)) {
BinaryOperatorNode* binaryNode =
(BinaryOperatorNode*)currentNode;
// ...
}

is a more or less standard technique in C, used (IIRC) in the
gcc compiler itself. I don't think that g++ would break this.

--
James Kanze
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      09-11-2010
On Sep 11, 3:49 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
> Alf P. Steinbach /Usenet wrote:
> > * tni, on 11.09.2010 14:40:
> >> On 2010-09-11 14:14, Alf P. Steinbach /Usenet wrote:
> >>> Not entirely sure what you're talking about. I vaguely
> >>> remember g++ having some "strict aliasing" bug. Hopefully
> >>> that's not it?


> >>> Could you give a concrete, minimal but complete example
> >>> where you think that access could be "reorder/optimize
> >>> away"?


> >> I can't give you a simple example, but what I have seen is
> >> similar to the behavior in this case (in my complex case,
> >> the structs started with the same sequence):


> >> #include <iostream>
> >> #include <stdint.h>


> >> struct A {
> >> uint32_t x;
> >> uint32_t y;
> >> };


> >> struct B {
> >> float x;
> >> uint32_t y;
> >> };


> >> int main() {
> >> A a = A();
> >> B& b = *reinterpret_cast<B*>(&a);
> >> b.x = 42;
> >> std::cout << a.x << " " << b.x << std::endl;
> >> return 0;
> >> }


> >> Output (g++ -O2):
> >> 0 42


Really!

> > OK, this is an optimizer problem/bug. The optimizer "knows"
> > that 'a' has not been modified, by assuming a little too
> > much, and optimizes away 'a.x'. You can use
> > '-fno-strict-aliasing' to turn off that silliness, or, you
> > can deny the optimizer the knowledge about the particular
> > instance 'a'.


> I believe it's completely valid for the compiler to optimize
> to this output.


The optimization is clearly valid; the above code contains
undefined behavior. (Think of it for a moment. In b.x = 42,
b.x is a float, so this is really b.x = 42.0f. And the bit
pattern for 42.0f coult be a trapping representation for
int.) In general, according to §3.10/15:

If a program attempts to access the stored value of an
object through an lvalue of other than one of the
following types the behavior is undefined:
-- the dynamic type of the object,
-- a cv-qualified version of the dynamic type of the
object,
-- a type that is the signed or unsigned type
corresponding to the dynamic type of the object,
-- a type that is the signed or unsigned type
corresponding to a cv-qualified version of the
dynamic type of the object,
-- an aggregate or union type that includes one of the
aforementioned types among its members (including,
recursively, a member of a subaggregate or contained
union),
-- a type that is a (possibly cv-qualified) base class
type of the dynamic type of the object,
-- a char or unsigned char type.

This paragraph clearly makes the above program undefined
behavior. This paragraph, however, says nothing about
something like:

struct A { int a; int b; };
struct B { int a; int b; int c; };

void f(A* pA, B* pB)
{
pA->b = 42;
pB->b = 0;
std::cout << pA->b << ',' << pB->b << std::endl;
}

int main()
{
union U { A a; B b; } u;
f( &u.a, &u.b );
return 0;
}

which is (according to the current wording of the standard),
required to work. What isn't really clear is something
like:

struct A { int a; int b; };
struct B { int a; int b; int c; };

void f(A* pA, B* pB)
{
pA->b = 42;
pB->b = 0;
std::cout << pA->b << ',' << pB->b << std::endl;
}

int main()
{
B b;
f( reinterpret_cast<A*>( &b ), &b );
return 0;
}

Given that the first is required to work, however, and that
the union and the function call may be in a different
translation unit from the function definition, it is
difficult to see how an implementation cannot make it work.
(Formally, the result of the reinterpret_cast here is
"unspecified". Practically, however, no information can be
lost, since it must be possible to recast it back to a B*,
and use that. So I can't imagine that being a problem in
practice. Formally, in f pA and pB point to different
types, so the compiler can assume that they don't alias (and
that the write through pB->b could not have modified the
value written through pA->b); practically, again, if the
objects pointed to by pA and pB are in a union, the pointers
are allowed to alias, so unless the compiler can prove that
they aren't in a union, it can't assume no aliasing.

--
James Kanze
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      09-11-2010
On Sep 11, 3:49 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
> Alf P. Steinbach /Usenet wrote:
> >> I can't give you a simple example, but what I have seen is similar to the
> >> behavior in this case (in my complex case, the structs started with the
> >> same sequence):


> >> #include <iostream>
> >> #include <stdint.h>


> >> struct A {
> >> uint32_t x;
> >> uint32_t y;
> >> };


> >> struct B {
> >> float x;
> >> uint32_t y;
> >> };


> >> int main() {
> >> A a = A();
> >> B& b = *reinterpret_cast<B*>(&a);
> >> b.x = 42;
> >> std::cout << a.x << " " << b.x << std::endl;
> >> return 0;
> >> }


> >> Output (g++ -O2):
> >> 0 42


> > OK, this is an optimizer problem/bug. The optimizer
> > "knows" that 'a' has not been modified, by assuming a
> > little too much, and optimizes away 'a.x'. You can use
> > '-fno-strict-aliasing' to turn off that silliness, or,
> > you can deny the optimizer the knowledge about the
> > particular instance 'a'.


> I believe it's completely valid for the compiler to
> optimize to this output. "a.x" requires "x" to refer to
> an uint32_t object by 3.10/15, but you have no guarantee
> that this is the case anymore after b.x was assigned the
> value 42. If storage of b.x and and a.x overlap, lifetime
> of a.x ends because you have reused its memory (just like
> in the union case, you would have switched the active
> member). a.x needs to be a char or unsigned char for this
> to be valid.


It's completely valid for the compiler to do anything it
wants in this case, since the code has undefined behavior.
(I would be interested in seeing what happens with an
unoptimized build on a Unisys Libra system, which has a
tagged architecture.) From a quality of implementation
point of view: the reinterpret_cast is clearly visible,
which should tell any reasonable compiler that all bets
concerning aliasing are off. If you really got the output
above from the program above, I'd consider it a bug. If the
assignment and the output was in a separate function,
however, which had been passed references or pointers, it
would be perfectly normal.

--
James Kanze
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-11-2010
Alf P. Steinbach /Usenet wrote:

> * James Kanze, on 11.09.2010 21:10:
>> If, on the other hand, in addition:
>>
>> union U { A a; B b; } u;
>> and call f with:
>> f(&a,&b );
>>
>> this reordering cannot take place, according to §9.2/16.

>
> Assuming for the sake of discussion that there is such a thing as aliasing
> based reordering rule (I haven't found it), this interpretation where the
> /call/ of a function determines the validity of possible code
> transformations /within/ the function seems 100% out of spirit with the
> standard.
>


I think it's certainly within the Spirit. See http://www.open-
std.org/jtc1/sc22/wg21/docs/cwg_active.html#636

 
Reply With Quote
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-11-2010
* Johannes Schaub (litb), on 11.09.2010 22:08:
> Alf P. Steinbach /Usenet wrote:
>
>> * James Kanze, on 11.09.2010 21:10:
>>> If, on the other hand, in addition:
>>>
>>> union U { A a; B b; } u;
>>> and call f with:
>>> f(&a,&b );
>>>
>>> this reordering cannot take place, according to §9.2/16.

>>
>> Assuming for the sake of discussion that there is such a thing as aliasing
>> based reordering rule (I haven't found it), this interpretation where the
>> /call/ of a function determines the validity of possible code
>> transformations /within/ the function seems 100% out of spirit with the
>> standard.
>>

>
> I think it's certainly within the Spirit. See
> http://www.open-std.org/jtc1/sc22/wg...ctive.html#636


It's a different case, and no, it shows no such thing as call-dependent validity
of code transformations; instead it concerns the opposite.

James' example concerns validity of reordering when accessing objects of the
same type (members of a and b), which according to §3.10/15 can be aliased. With
James example the source code transformation is purportedly (I don't agree)
valid or invalid depending on how the function is called.

Gabriel's example, on the other hand, concerns accessing float as int, which
according to the same para yields UB. And he shows that the possibility of
aliasing in a call means that reordering is an invalid code transformation,
because although in general accessing float as int is UB, it's valid in a union.
That is, the types that the function know of are not the full story, and the
reason for the DR is exactly that call-dependent semantics are not in the spirit
of the standard -- it's so outlandish that it's not even considered.

Now consider

#include <stdio.h>

struct A { double d; int x; };

void foo( int* a, A* b )
{
b->x = 1;
*a = 2;
}

int main()
{
A aha;
foo( &aha.x, &aha );
printf( "%d\n", aha.x );
}

Since int* and A* are clearly pointers with different referent types, at least
the simplistic purported aliasing-based reordering rule mentioned so far should
allow reordering of the statements in foo, causing output 1 instead of 2.

I don't believe it -- although I may be forced to if James can hark up some
reference (in which case the standard, IMHO, needs to be fixed).


Cheers, & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-11-2010
Alf P. Steinbach /Usenet wrote:

> * Johannes Schaub (litb), on 11.09.2010 22:08:
>> Alf P. Steinbach /Usenet wrote:
>>
>>> * James Kanze, on 11.09.2010 21:10:
>>>> If, on the other hand, in addition:
>>>>
>>>> union U { A a; B b; } u;
>>>> and call f with:
>>>> f(&a,&b );
>>>>
>>>> this reordering cannot take place, according to §9.2/16.
>>>
>>> Assuming for the sake of discussion that there is such a thing as
>>> aliasing based reordering rule (I haven't found it), this interpretation
>>> where the /call/ of a function determines the validity of possible code
>>> transformations /within/ the function seems 100% out of spirit with the
>>> standard.
>>>

>>
>> I think it's certainly within the Spirit. See
>> http://www.open-std.org/jtc1/sc22/wg...ctive.html#636

>
> It's a different case, and no, it shows no such thing as call-dependent
> validity of code transformations; instead it concerns the opposite.
>
> James' example concerns validity of reordering when accessing objects of
> the same type (members of a and b), which according to §3.10/15 can be
> aliased. With James example the source code transformation is purportedly
> (I don't agree) valid or invalid depending on how the function is called.
>
> Gabriel's example, on the other hand, concerns accessing float as int,
> which according to the same para yields UB. And he shows that the
> possibility of aliasing in a call means that reordering is an invalid code
> transformation, because although in general accessing float as int is UB,
> it's valid in a union. That is, the types that the function know of are
> not the full story, and the reason for the DR is exactly that
> call-dependent semantics are not in the spirit
> of the standard -- it's so outlandish that it's not even considered.
>


The way I understand it, it does not matter whether the reference is by an
union or not. Aliasing an int as a float is UB no matter by an union or not.
I thought that was the whole point of Gabriel's issue report? I.e if the
statements in foo are reordered, then "t.d" accesses an int object by a
double lvalue, violating aliasing.

Example to show what I mean:

union {
float f;
int i;
} u;

// now the object is a float, by 3.8/1.
u.f = 1.f;

// now it is an int, also by 3.8/1
u.i = 10;

// aliasing violation by 3.10/15 - UB, trying
// to access value of int object by float lvalue.
float f = u.f;


> Now consider
>
> #include <stdio.h>
>
> struct A { double d; int x; };
>
> void foo( int* a, A* b )
> {
> b->x = 1;
> *a = 2;
> }
>
> int main()
> {
> A aha;
> foo( &aha.x, &aha );
> printf( "%d\n", aha.x );
> }
>
> Since int* and A* are clearly pointers with different referent types, at
> least the simplistic purported aliasing-based reordering rule mentioned so
> far should allow reordering of the statements in foo, causing output 1
> instead of 2.
>
> I don't believe it -- although I may be forced to if James can hark up
> some reference (in which case the standard, IMHO, needs to be fixed).
>


I don't believe that the Standard allows reordering this either. b->x is int
lvalue and *a is int lvalue too. Both are allowed to alias.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
"Variable variable name" or "variable lvalue" mfglinux Python 11 09-12-2007 03:08 AM
If you get an order # does it mean the order is accepted? =?Utf-8?B?U3RldmUxMDc3?= Windows 64bit 3 05-12-2005 11:46 PM
How do I scope a variable if the variable name contains a variable? David Filmer Perl Misc 19 05-21-2004 03:55 PM
Traversion order cf. output order in XSL Soren Kuula XML 2 02-01-2004 09:10 AM
How to Display DropDownList with preserved order (custom order) cspoh ASP .Net Web Controls 0 07-31-2003 09:19 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57