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.