[comp.sys.hp] C Compiler Optimizer

ahill@bbn.com (Alan R. Hill) (08/28/90)

	We have experienced problems of varying symptoms while using
the optimizer switch of the HP-UX 7.0 C compiler.  Rather than 
extensively analyzing these problems we have opted to compile without
optimizing.  Has anyone else noticed bad behavior with the optimizer?

Thanks,
Alan R. Hill

UUCP: {backbone}!bbn!ahill     USPS: BBN Communications Corporation
ARPA: ahill@bbn.com                  150 CambridgePark Drive
POTS: (617) 873-2786                 Cambridge, MA  02140

jim@tiamat.fsc.com (Jim O'Connor) (08/28/90)

In article <59169@bbn.BBN.COM>, ahill@bbn.com (Alan R. Hill) writes:
> 
> 	We have experienced problems of varying symptoms while using
> the optimizer switch of the HP-UX 7.0 C compiler.  Rather than 
> extensively analyzing these problems we have opted to compile without
> optimizing.  Has anyone else noticed bad behavior with the optimizer?
> 

Yes, we have, too.  One example that comes to mind is:
(this is simplified, the actual program we used was much longer)

example.c
------
main()
{

printf("hello, world\n");

}

$ cc -o example example.c
$ example
hello, world
$ echo $?
0

$ cc -O -o example example.c
$ example
hello, world
$ echo $?
seemingly random numbers, but not once 0

I didn't really worry about this, telling myself that not using "exit(0)"
at the "normal" exit point of the program was probably bad programming
practice.  Using the optimizer also helped to point out another bad
habit of mine (learned while programming on Xenix) which is not correctly
initializing automatic variables.  In one case we had, with the optimizer
off, the automotic variable was being initialized (i.e. set to 0), but then
when we did the "production" compile and test, the program didn't work
because the variable wasn't initialized.  Like the other one, it wasn't
really the compilers fault, but I wish there was more info on what the
optimizer will do so I won't get suprised on things like this.

------------- 
James B. O'Connor			jim@tiamat.fsc.com
Ahlstrom Filtration, Inc.		615/821-4022 x. 651

rer@hpfcdc.HP.COM (Rob Robason) (08/28/90)

ahill>         We have experienced problems of varying symptoms while
ahill> using the optimizer switch of the HP-UX 7.0 C compiler.

It would help draw responses if you indicate which series (300/800).

mlight@hp-ptp.HP.COM (Mike Light) (08/29/90)

>	We have experienced problems of varying symptoms while using
>the optimizer switch of the HP-UX 7.0 C compiler.  Rather than 
>extensively analyzing these problems we have opted to compile without
>optimizing.  Has anyone else noticed bad behavior with the optimizer?


There is one common symptom, particularly with level 2 optimization:

  The larger the function, the much longer it takes to optimize and the more
swap space you need.  Some of my complex yacc C outputs take up to 40 minutes
each to optimize (even on a 9000/850) and consume about 12 Megabytes of
swap space in the process.  On my 360 the compilation went on and on and on
until in exasperation (after 6 hours) I killed it and used the +O1 switch
which took less than 2 minutes.

  There may be no way to "fix" the optimizer, but rather just choose
the most appropriate optimization for the function.  Lex and yacc outputs
are particularly prone to optimization time warps, and I habitually specify
+O1 for them now.

-----------------------------------------------------------------------
 Mike Light  HP Industrial Applications Center - mlight@hpiala9.HP.COM
-----------------------------------------------------------------------

dave@dptechno.UUCP (Dave Lee) (08/29/90)

In article <59169@bbn.BBN.COM> ahill@bbn.com (Alan R. Hill) writes:
>
>	We have experienced problems of varying symptoms while using
>the optimizer switch of the HP-UX 7.0 C compiler.  Rather than 
>extensively analyzing these problems we have opted to compile without
>optimizing.  Has anyone else noticed bad behavior with the optimizer?
>

Alas, I also have had problems. A perfectly fine  program just stopped
working with -O .  Minor inspection showed no ***obvious*** flaws
in the code.  I didnt investigate enough to be able to pinpoint the
problem so it "could" be a program error, but in this case I 
really doubt it.   

It is exactly this type of hard to find infrequent optimization bugs that
make ANY optimization useless.   I am just too afraid of this type of
bug that we CANT afford to chance it.  Much Much Much better a slower
program than a broken one.  (Upps, sorry sir, didn't mean to crash
your $10000 part because of an optimization bug, try it again. 
What you say another $10000 part thrashed, well it's not MY fault ... )

Too bad really, because on the whole, the optimizer does a good job.
Someone please let me know when ALL optimization bugs are fixed 
(or atleast documented with all forms of source code that cause the problem).


-- 
Dave Lee
uunet!dptechno!dave

roger@zuken.co.jp (Roger Meunier) (08/29/90)

In article <59169@bbn.BBN.COM> ahill@bbn.com (Alan R. Hill) writes:

 >	  We have experienced problems of varying symptoms while using
 >the optimizer switch of the HP-UX 7.0 C compiler.  Rather than 
 >extensively analyzing these problems we have opted to compile without
 >optimizing.  Has anyone else noticed bad behavior with the optimizer?

Indeed we have.  The code given below optimizes cleanly under +O1, but
the -O optimizer does manage registers properly.  It doesn't handle
cases where side affects may be introduced by function calls.

---------------------------  Cut Here  ------------------------------

#include <stdio.h>

void	reg_and_stack();
void	doit();
void	doitagain();

main()
{
	int	parms[10];

	parms[0] = -1;
	reg_and_stack(parms);
}

void	reg_and_stack(parms)
int*	parms;
{
	int	id;
	int	args[30];
	short	cnt;

	cnt = 0;

	if (parms) {
		doit(parms,&args[0],&cnt);
		if (parms[0] & 0x0020)	id = parms[1];
	}

	args[cnt] = 1;	cnt++;
	args[cnt] = 2;	cnt++;
	args[cnt] = 3;	cnt++;

	doitagain(&args[0],&cnt);
}

void	doit(parms,args,cnt)
int*	parms;
int	args[];
short*	cnt;
{
	printf("doit: on entry, cnt = %d\n",*cnt);
	*cnt = 2;
	printf("doit: on exit, cnt = %d\n",*cnt);
}

void	doitagain(args,cnt)
int	args[];
short*	cnt;
{
	printf("doitagain: on entry, cnt = %d\n",*cnt);
}
---------------------------  Cut Here  ------------------------------

% cc -o reg_stack +O1 reg_stack.c
% reg_stack
doit: on entry, cnt = 0
doit: on exit, cnt = 2
doitagain: on entry, cnt = 5
                           ^
% cc -o reg_stack -O reg_stack.c
% reg_stack
doit: on entry, cnt = 0
doit: on exit, cnt = 2
doitagain: on entry, cnt = 3
                           ^

A quick look at the assembly listing produced by the -O optimizer shows
that while the stack version of cnt was set to 2 by doit(), the stack
version is overwritten by the register version, which still contained
the pre-doit() value of 0.
--
Roger Meunier @ Zuken, Inc.  Yokohama, Japan	(roger@zuken.co.jp)

wunder@hp-ses.SDE.HP.COM (Walter Underwood) (08/30/90)

   example.c
   ------
   main()
   {
   printf("hello, world\n");
   }

Exiting with an unpredictable number is proper behavior for this 
program.  It is not a defect.  Notice:

   $ lint /tmp/example.c

   example.c
   ==============
   (6)  warning: main() returns random value to invocation environment

   ...

Which is exactly what your code does when optimized.  The repeatable
behavior when not optimized is due to luck, not C.  Here is a fixed
version of "example.c".

   int main()
   {
   printf("hello, world\n");
   return 0;
   }

Now, the main() routine returns a type which matches its declared
return type.  This is correct C.

When program behavior changes under optimization, use lint to find out
whether the code depends on things that C doesn't guarantee.  You can
also use C++, which has much more strict checking and just won't
compile code with type mismatches.

wunder

guy@auspex.auspex.com (Guy Harris) (08/30/90)

>I didn't really worry about this, telling myself that not using "exit(0)"
>at the "normal" exit point of the program was probably bad programming
>practice.

Whether not using "exit(0)" is bad programming practice is a matter of
taste.  Neither using "exit(0)" *nor* "return 0" (or "return (0)" or
whatever) definitely *is* bad programming practice.  Most C
implementation - *all* valid ANSI C implementations - will, if you
return from "main()", exit with the exit status being the value returned
from "main()".  If you just fall off the end, you return a random value.

I believe there exist systems (SunOS 4.1 is one of them, I think) where
turning the optimizer off won't help.  Running the code through "lint"
may help, though; some versions of "lint" will catch attempts to fall
off the end of "main()", and HP's may be one of them.

>Like the other one, it wasn't really the compilers fault,

And, like the other one, "lint" may help, by catching the error...

>but I wish there was more info on what the optimizer will do so I won't get
>suprised on things like this.

...and, like the other one, there exist systems where turning the
optimizer off won't help.  SunOS 4.x is one of them; the run-time linker
uses the stack before "main()" does, and therefore there may be cruft
left on the stack with random values.

It's not really up to HP to enumerate all the cases of
common-but-incorrect practice where some particular release of their
optimizer will surprise you; in fact, it may not be practical for them
to do so.

"lint" really is your friend....

khb@chiba.Eng.Sun.COM (Keith Bierman - SPD Advanced Languages) (08/31/90)

In article <570@dptechno.UUCP> dave@dptechno.UUCP (Dave Lee) writes:

   It is exactly this type of hard to find infrequent optimization bugs that
   make ANY optimization useless.   I am just too afraid of this type of
   bug that we CANT afford to chance it.  Much Much Much better a slower

On some machines (e.g. a Cray) not optimizing the code can cost a
factor of about 50x. There is no point in buying a high performance
computer to run slowly.

The vast majority of "optimizer" bugs eventually lead back to errors
in the application source code. Make sure your code passes lint (or
the equivalent thereof). Then, for large projects:

	1)  compile at low optimization (often the default on 
	    non-unix boxes; unix traditional default is none;
            other platforms default to max; ibm mainframes are
	    site specific, it is governed by the sysadmin)

	2)  profile

	3)  turn on full optimization on the most expensive module(s)

	4)  test and return to step 2 until happy.

This heuristic works well on a wide variety of platforms. I first got
into it working with old CDC machines ... they had a delightful
"unsafe" optimization level (0 debugging, 1, 2, 3, u). When I finally
found the module which wouldn't execute reliably at anything but 0 ...
I found a minor violation of the Fortran standard ..... the code
worked correctly on VAXen, IBM (levels G, H, and VS) DG's, Primes,
PC's, Univacs and a dozen other platforms. Was the CDC broken ? No, it
did the best optimization of the bunch. Getting the heuristic down was
worth the suffering though.

On modern machines, defeating ALL optimization is good for at least a
factor of 2x. As more and more machines get interesting, the 50x cost will
become common rather than the exception. I have seen 200x in extreme
cases. 

Note: It is vital to realize that just because you didn't ask for
      optimization that doesn't mean you didn't get it. Example:
      The LPI compiler on SPARCs default to its max optimization
      level. The Sun compiler defaults to none. This does not
      represent varying degrees of trust, it represent variant
      traditions. The unix tradition is for all "tools" to do
      something simple and to have an array of options ... 
      which users are expected to "softwire" with shell magic. LPI's
      tradition comes from a traditional minicomputerish (nonunix) realm...

When working on machines like the VAX, where optimization only
provided a 30% boost being a neoluddite wasn't so expensive. Nowadays
it can be a very, very expensive vice.
--
----------------------------------------------------------------
Keith H. Bierman    kbierman@Eng.Sun.COM | khb@chiba.Eng.Sun.COM
SMI 2550 Garcia 12-33			 | (415 336 2648)   
    Mountain View, CA 94043

shankar@hpclscu.HP.COM (Shankar Unni) (08/31/90)

> Alas, I also have had problems. A perfectly fine  program just stopped
> working with -O .  Minor inspection showed no ***obvious*** flaws
> in the code.  I didnt investigate enough to be able to pinpoint the
> problem so it "could" be a program error, but in this case I 
> really doubt it.   
> 
> Too bad really, because on the whole, the optimizer does a good job.
> Someone please let me know when ALL optimization bugs are fixed 
> (or atleast documented with all forms of source code that cause the problem).

And how are we to know about the bugs if people don't report them to us?

Please take the time to call HP support and let them know what problem you
are having, so that they can take a crack at isolating the problem.

Also, you'd be surprised at how many "correct" programs are exposed as
incorrect by any self-respecting optimizer.  It may be things as obscure as
variables left uninitialized along certain paths, accessing off the limits
of an array, etc.

One particularly nasty problem is if you have a signal handler that touches
a global variable. Such variables should be declared as "volatile", but
many people who inherit old code don't do so (because some other C
compilers may not have implemented this (ANSI standard) feature).

The problem is that if a function which uses this variable (and possibly
caches it in a register) is interrupted, and the signal handler modifies
this variable and returns back to that function, then it may not know that
the variable has been changed underneath it, and will continue to use the
bad value in the register.

The classic example of this is:

   extern int notavailable;	

   while (notavailable) /* do nothing */ ;
   
And the program expects some signal to set "notavailable". Oops. You should
say

   extern volatile int notavailable;

(True, this is a trivial case that should be caught, but the usage may be
much more obscure than this, and the only workaround would be for the
optimizer never to cache any globals, which is an unacceptable hit).


SUGGESTION:

If you are running HP-UX 7.0, use the optimizer flag "+OV" instead of "-O".
If your program now runs correctly, then the problem is with volatile
globals that are not declared as such.

If even that does not work, try "+O1" (which does a less aggressive level
of optimization).

BUT ABOVE ALL: try to pinpoint which part of your program is not working
after optimization, and tell HP support about it. PLEASE!!!!
-----
Shankar Unni                                   E-Mail: 
Hewlett-Packard California Language Lab.     Internet: shankar@hpda.hp.com
Phone : (408) 447-5797                           UUCP: ...!hplabs!hpda!shankar