banshee@ucscb.ucsc.edu (Wailin' Through The Nets) (08/18/90)
This fragment of code breaks when the following function is inlined and the code is compiled with -O on a RS/6000. Question, why? Note that without -O everything works fine, even with the function inlined. But -O kills it. tradd(b) union g {long xx; struct half yy;} *b; { b->yy.high &= 077777; /* WHY CAN'T I INLINE THIS? */ } m_mult(a,b,c) struct mint *a,*b,*c; { long x; union {long xx; struct half yy;} sum; int carry; int i,j; c->val=xalloc(a->len+b->len,"m_mult"); sum.xx=0; for(i=0;i<b->len;i++) { carry=0; for(j=0;j<i+1;j++) { sum.xx += (a->val[j])*(b->val[i-j]); if (sum.yy.high & 0100000) { tradd(&sum); /* sum.yy.high &= 077777; */ carry += 1; } c->val[i]=sum.yy.low & 077777; sum.xx=sum.xx >> 15; sum.yy.high=carry; etc etc etc [Sounds to me like an optimizer bug. Have you reported it to IBM? -John] -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
jar@florida.eng.ileaf.com (Jim Roskind x5570) (08/21/90)
> This fragment of code breaks when the following function is inlined and > the code is compiled with -O on a RS/6000. Question, why? Note that > without -O everything works fine, even with the function inlined. As with many optimizer related problems, you were simply lucky it worked without optimization! You were slightly justified in your expectation, but still luck was key! > But -O kills it. > > tradd(b) > union g {long xx; struct half yy;} *b; Note that you define a tag "g" here. Note that any other union or struct that "happens" to look like it does not have to *really* be implemented identically, *UNLESS* you reuse the tag! (I assume you have defined "half" somewhere else). If you have severely different expectations, you should take them up with the ANSI C Committee, as I believe this is in keeping with the standard. > { > b->yy.high &= 077777; /* WHY CAN'T I INLINE THIS? */ > } > > m_mult(a,b,c) > struct mint *a,*b,*c; > { > long x; > union {long xx; struct half yy;} sum; Here you define another union, but you did *NOT* use the tag "g". As a result, the compiler has the freedom to "optimize" the layout of these distinct unions. *IF* you had simply said "union g sum;" you probably would be safe (unless there was a real bug in the compiler). In the form given here, all bets are off in terms of guarantees of compatibility of the two union types. My guess is that the optimizing compiler realized that you had declared a local union, and placed the elements individually in a convenient place on the stack or in registers. A lot of folks use unions to sneakily cast from one data type to another, but there are very few guarantees that such attempts will work! > int carry; > int i,j; > c->val=xalloc(a->len+b->len,"m_mult"); > sum.xx=0; > for(i=0;i<b->len;i++) > { carry=0; > for(j=0;j<i+1;j++) > { > sum.xx += (a->val[j])*(b->val[i-j]); > if (sum.yy.high & 0100000) { > tradd(&sum); The above statement could have given you a warning that the type of the argument that you passed to "tradd" was different from the "expected" type for an argument. However, since you did *NOT* prototype the function (and the above old style definition for "tradd" does *NOT* constitute a prototype) the compiler simply looked the other way. In fact, you could have said "tradd(3.14259)" and it would not have complained :-(. > /* sum.yy.high &= 077777; */ > carry += 1; > } > c->val[i]=sum.yy.low & 077777; > sum.xx=sum.xx >> 15; > sum.yy.high=carry; > etc etc etc Moral of the story: 1) Use function prototypes and the compiler would have given you an error message, rather than a subtle bug. 2) Avoid the use of casts. Avoid the use of unions to hide the fact that you were wanted to use casts. 3) Optimizers are pretty clever. 4) If you define the same value twice in a program, you are asking for trouble. Generally such redefinitions get out of sync. Even if they "look the same", they might not be. It is better to work to reuse a single definition. This was an interesting extreme example of two definitions "looking the same". 5) This was really a comp.lang.c question, but it shows just how far good optimizers can go. It is their job to assume as much as the law (standard) allows, and not one bit more. In this case, they followed the law, and your code, which lived outside the law, broke. Jim Roskind Independent Consultant (407)729-4348 or (617)577-9813 x5570 jar@hq.ileaf.com or ...!uunet!leafusa!jar [Actually, my reading of the standard implies pretty strongly that all similar definitions of a union be equivalent, but it explicitly says that the behavior when you store one element in a union and read back another is in almost all cases undefined. Jim's point that an optimizer often breaks programs that depend on semantics not guaranteed by the language is a good one. -John] -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
worley@compass.com (Dale Worley) (08/25/90)
[In reagard to two similar unions, only one of which has a tag, and which don't do what the author expected.] Note that you define a tag "g" here. Note that any other union or struct that "happens" to look like it does not have to *really* be implemented identically, *UNLESS* you reuse the tag! (I assume you have defined "half" somewhere else). If you have severely different expectations, you should take them up with the ANSI C Committee, as I believe this is in keeping with the standard. Here you define another union, but you did *NOT* use the tag "g". As a result, the compiler has the freedom to "optimize" the layout of these distinct unions. Strictly speaking, you're correct. However, for any sane implementation, there are some tight constraints: a pointer to a union must point to each of its members (ANSI 3.5.2.1) -- therefore, all members start at the same place, and so unions of the same types of fields pretty will have to be laid out the same. Again, if two structs are members of a union, and they have an initial set of members of the same type, then those members have to be laid out the same way (ANSI 3.3.2.3). How this applies in this particular example, I don't know precisely, but it's likely that the optimizer is disregarding one or the other of these rules. Dale Worley Compass, Inc. worley@compass.com -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
daveg@near.cs.caltech.edu (Dave Gillespie) (08/26/90)
>>>>> On 24 Aug 90 18:36:06 GMT, worley@compass.com (Dale Worley) said: >> Here you define another union, but you did *NOT* use the tag "g". As a >> result, the compiler has the freedom to "optimize" the layout of these >> distinct unions. > Strictly speaking, you're correct. However, for any sane implementation, > there are some tight constraints: a pointer to a union must point to each > of its members (ANSI 3.5.2.1) -- therefore, all members start at the same > place, and so unions of the same types of fields pretty will have to be > laid out the same. > Again, if two structs are members of a union, and they have an initial set > of members of the same type, then those members have to be laid out the > same way (ANSI 3.3.2.3). > ... it's likely that the optimizer is disregarding one or the other of these > rules. I don't have the original code handy, but as I recall it did not take the addresses of both the unions in question, nor did the unions contain at least two different structs. So the optimizer is probably right to disregard these rules---they don't apply in this particular example. In fact, if the C standard mentions the things you refer to above, but also explicitly makes no guarantees about general relationships among the fields of a union, then probably allowing this optimization was exactly what the standard-writers had in mind. But, having said all that, I actually agree with your assertion that the implentation is not "sane", but only because the situations where it's safe to rearrange a union are probably rare, and more often than not will occur because the programmer was trying to use the union to fake up a type cast. So the optimization is more likely to hurt than to help. -- Dave -- Dave Gillespie 256-80 Caltech Pasadena CA USA 91125 daveg@csvax.cs.caltech.edu, ...!cit-vax!daveg [Can someone run the original code through the RS/6000 compiler, look at the object code, and report whether the compiler is in fact doing something strange? IBM is usually pretty good at reading standards. -John] -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
johnv@metaware.uucp (John Vinopal) (08/27/90)
I have recieved several responses to my query about the RS/6000 optimizer breaking code. As it turns out (I generated a much better test case) the optimizer does have a bug when dealing with unions in the way I was -- and IBM promises a fix soon. Thanks for the suggstions and help. -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
johnv@metaware.com (John Vinopal) (08/28/90)
[Commentary deleted] >But, having said all that, I actually agree with your assertion that the >implentation is not "sane", but only because the situations where it's >safe to rearrange a union are probably rare, and more often than not will >occur because the programmer was trying to use the union to fake up a >type cast. So the optimization is more likely to hurt than to help. > >[Can someone run the original code through the RS/6000 compiler, look at >the object code, and report whether the compiler is in fact doing something >strange? IBM is usually pretty good at reading standards. -John] The union in this case was in fact used as a type cast of sorts; a method to prevent a short from overflowing. Bad style and not portable begins to sum up this approach. However... This has been submitted to IBM tech and verified as a real bug. If anyone would like a 'clean' copy of the code in question, please mail requests to banshee@ucscb.UCSC.EDU. -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.
worley@compass.com (Dale Worley) (08/29/90)
From: johnv@metaware.com (John Vinopal) The union in this case was in fact used as a type cast of sorts; a method to prevent a short from overflowing. Bad style and not portable begins to sum up this approach. However... Yes, since the results of using a union for type punning are undefined (ANSI 3.3, 3.5.2.1), one might say that the code is bad style. One might also say that it is incorrect, and that the user can't expect *anything* from it. Dale Worley Compass, Inc. worley@compass.com -- Send compilers articles to compilers@esegue.segue.boston.ma.us {ima | spdcc | world}!esegue. Meta-mail to compilers-request@esegue.