This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Serious code generation/optimisation bug (I think)


On Tue, 27 Jan 2009 07:08:51 -0500
Robert Dewar <dewar@adacore.com> wrote:

> James Dennett wrote:
> 
> > I don't know how much work it would be to disable this optimization
> > in gcc.
> 
> To me, it is always troublesome to talk of "disable this optimization"
> in a context like this. The program in question is not C, and its
> semantics cannot be determined from the C standard. If the appropriate
> gcc switch to "disable" the optimization is set, then the status of
> the program does not change, it is still undefined nonsense. Yes, it
> may happen to "work" for some definition of "work" that is in your
> mind, but is not defined or guaranteed by any coherent language
> definition. It seems worrisome to be operating under such
> circumstances, so surely the best advice in a situation like
> this is to fix the program.

I don't mean to complain, but I happen to work with embedded systems. 
I program them in C, or at least in a language that uses the syntactic
elements of C. While it might not be a C program and is utter nonsense
from a linguistic view, in the embedded world dereferencing a NULL
pointer is often legal and actually unavoidable. Many embedded systems
run on CPUs without MMU and I'd dare to state that there are many more
of these lesser beasts out there than big multicore, superscalar CPUs
with paged MMUs and vector processing units and FPUs. Now on many of
these at location 0 you find a vector table or memory mapped registers
or simple memory or even the dreaded zero-page where you can do things
that you can't do anywhere else. On every one of those chips it is
legal to dereference a NULL pointer as long as you have the notion of
a pointer being an address of something. I've been programming in C for
almost 30 years and I neglectfully not followed the language's semantic
development, maybe that's why I am confused to think that C is a
low-level, system programming language and not a highly abstract
language where a "pointer" is actually some sort of a complex
reference to an object that may or may not actually occupy memory.
Assuming, of course, that the notion of "memory" is still a valid one,
in the old sense of collection of addressable data units residing in a
so-called address space. I think the existence of keywords referring to
aliasing is an indication to that, but I am not sure any more.

In that caveman mental domain of mine I would assume that if I
dereference a NULL pointer and on the given environment it is a no-no,
then something nasty is going to happen; an exception is raised on a
micro or I get a sig 11 message on my terminal or the whole gizmo just
resets out of the blue. On the other hand, if the given architecture
treats address 0 as address 0, then it should just fetch whatever value
is at 0 and merrily chug along. In fact, I would assume that since on
every CPU I've ever used the address space included 0, one could do
this:

// 32-bit ints
struct s_addr_space {
  int preg[ 0x100 ];    // 256 memory mapped peripherial register @ 0
  int gap1[ 0x300 ];    // 3K Unused space
  int ether[ 0x400 ];	// 4K Ethernet buffer
  char video[ 0x4000 ]; // 16K video buffer
  int gap2[ 0x800 ];    // 8K unused
  int ram[ 0x1000 ];    // 16K RAM
  int rom[0x1000 ];     // 16K ROM
...
} * const my_micro = (struct s_addr_space *) 0;

...
   if ( my_micro->video[ 3 ] == 'A' ) { ...

Then I would not assume that the compiler simply throws away each and
every statement that refers to any element of the address space just
because it cleary knows that 'my_micro' is a NULL pointer, therefore 
it can, in its superior wisdom, declare that the code dereferencing
it should not and thus will not be executed whatsoever.

It is possible that the above is complete nonsense and should be
punished by public execution of the programmer, but there are *tons* 
of stuff like that out there on embedded systems, working quite nicely.

I openly admit that the test case was sloppy (and admitted it in my OP).
I do accept that due to the elevation of the C language from the low
level system programming language it used to be to the linguisticly
pure high-level object oriented metalanguage that C-99 apparently is,
dereferencing a NULL pointer became meaningless nonsense these days.

However, I'd like to point out one thing: the compiler is there to help
the programmer. Derefencing a NULL pointer might be a bad thing on one
system and perfectly normal on others. The standard declares that the
behaviour is undefined, i.e. it is up to the compiler writer. Now on a
system where NULL dereference is allowed, silently(!) removing an
explicite test for NULL pointer (indicating that the programmer *knew*
that the pointer can indeed be NULL) is the worst possible solution. It
does not save the program from crashing if the pointer was NULL and the
system does not tolerate it. On the other hand, on a NULL-tolerant
system it makes code that, if the compiler hadn't overruled the
programmer, would have worked just fine but due to the compiler's
decision it does not. The compiler optimised something out based on an
assumption, not on actual proof. It can not prove that the pointer is
not NULL, it simply assumes it based on the dereference statement.
I.e., compiling the program with -O0 works, with -O2 it dies, simply
because in the first instance it does what the programmer said and in
the second it does what it thinks the programmer should have said.

From a C language standard purist point of view such code might be
nonsense, but chances are that you own and use some gadget every day
that was programmed by such low-life engineers like me who live in those
by-gone days where a pointer held a memory address and work with chips
where at address 0 there are some useful things. We use C because it
allows us to deal with the hardware efficiently and allows us to
provide the required functionality in heavily constrained hardware
environments. We love gcc because it is a good compiler and (usually)
*helps* us to make those gizmos for your convenience. We use all those
linguistic abominations that info gcc calls "C Language Extensions",
such as embedded assembly, ## deleting the comma before an empty vararg
in a macro and so on, because they help us, even though they are not
standard and as you pointed out, programs using them should be fixed
because they are pure nonsense and not even C.

So, pretty please, when the compiler detects that a language resembling
to, but not really C is used and removes assumedly (albeit unprovenly)
superfluos chunks of code to purify the misguided programmer's intent,
could it please at least send a warning? Just to indicate to those
idiots who are at least aware of their dumbness and thus use -W -Wall
-Wextra -Werror in their CFLAGS that it is now time to purchase the
latest C standard and brush up with the recent changes with regards to
the interpretation semantics of such things as addresses, arrays,
pointers and so on.

Please?

Zoltan


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]