Does -O3 enable more warnings than just -Wall -Wextra with gcc?

K

Keith Thompson

Malcolm McLean said:
There are two common situations. One is that you need to read from or write to
a memory-mapped address. So typically that's written in C. The result of
*123 = 42 is probably not just to set memory location 123 to 42, there's
usually a side effect, like turning on an LED. Otherwise you'd just use
regular memory on the heap or the stack.

I presume you mean something like

*(int*)123 = 42;

since `*123` violates a constraint and will probably be rejected at
compile time.
 
M

Malcolm McLean

I presume you mean something like

*(int*)123 = 42;

since `*123` violates a constraint and will probably be rejected at
compile time.
Yes, C needs the type. You might need to write a single byte or a word to
manipulate the memory mapped device. Probably a byte since 123 is obviously
not going to be word aligned.
In actual use the whole thing is usually wrapped up in macros, so the code will
read something like

led_on();

led_on will be defined as

SETLOWMAP(LED1, LED_ON)

SETLOWMAP as

SETLOWMAP(loc, val) (BASE[loc] = ((RAW_BYTE) val))

and so on, until you eventually get to the bits.
 
L

Les Cargill

Keith said:
I presume you mean something like

*(int*)123 = 42;

Why would you presume that?
since `*123` violates a constraint and will probably be rejected at
compile time.

It could also be something much more convoluted but compiler-chain-
politically-correct - like declaring a struct-or-array
memory-mapped to 123, then writing to that.
 
K

Keith Thompson

Les Cargill said:
Why would you presume that?

The answer to your question is immediately above this line.
It could also be something much more convoluted but compiler-chain-
politically-correct - like declaring a struct-or-array
memory-mapped to 123, then writing to that.

Which would still be "something like" what I wrote -- but then I'd
be rather surprised if you could assign 42 to it (that would also
be a constraint violation which would *probably* be rejected at
compile time).

If your point is that a compiler could permit `*123` to be treated
as an lvalue, then you're right -- but I don't know of any compilers
that do so. In particular, such a compiler would have to make an
arbitrary decision about the type of the object. (Probably B or
early C compilers would have allowed it.)

If you're making a different point, could you elaborate?
 
K

Keith Thompson

David Brown said:
This means that a compiler implementer could theoretical produce nasal
daemons when the programmer calls waitpid(), just because the writer
thought that is "most appropriate" ? He would not even have to document
it (as would be the case for "implementation-defined behaviour")?
Obviously no sane compiler implementer /would/ do such a thing, but
there seems to me to be a loophole there. Maybe the final bit of
"legalese" is just too subtle for me here - I'll ignore it for now, and
simply assume compiler writers write sensible tools!

A *compiler* implementer presumably would not implement waitpid();
that's part of the runtime library.

Assuming that nasal demons are a physical possibility, there's certainly
nothing *in the C standard* that says a call to waitpid() cannot emit
them.

POSIX does define waitpid() with the following declaration:

pid_t waitpid(pid_t pid, int *stat_loc, int options);

(the type pid_t is defined elsewhere). Passing a misaligned pointer as
the second argument does have undefined behavior -- though you're likely
to run into more explicit undefined behavior before such a call.

I'm sure there are other POSIX functions that, for example, take a
pointer argument that must be non-null; passing a null pointer to such a
function would have behavior that is not defined by POSIX. (I'm too
lazy to search for an example.) waitpid(), on the other hand, has well
defined (by POSIX) behavior if you pass it a null pointer. The C
standard says nothing about which POSIX functions require non-null
pointers and which don't.

The "nasal demons" joke may be leading you a bit astray.

"Undefined behavior", as the term is used by the C standard, means
simply behavior that is not defined by the C standard. Some things are
explicitly stated to have undefined behavior; others have behavior that
is undefined due to the Standard's omission of any definition of the
behavior. Both cases are simply *undefined behavior*.

If your program's behavior is undefined, it means that if your program
produces nasal demons (or, more realistically, crashes or produces
unexpected output), then you can't complain that it's because the C
implementation has failed to conform to the C standard. If waitpid()
produces nasal demons, it's a bug, but it's not a C conformance failure.

If waitpid() doesn't work properly, then you *can* complain about the
implementation's non-conformance to the POSIX standard.
 
J

James Kuyper

On 04/12/13 13:08, James Kuyper wrote: ....

This means that a compiler implementer could theoretical produce nasal
daemons when the programmer calls waitpid(), just because the writer
thought that is "most appropriate" ?

Well, yes. In fact, it's quite likely to fail to link (and probably
won't even compile), on operating systems for which waitpid() is
meaningless.

An implementor is constrained not only by the C standard, but also by
whatever other standards the implementor chooses to support. Even that
choice is constrained, to an extent, by the demands of the implementor's
customers. An implementation supposedly targeting a POSIX environment
which implemented waitpid() in a way inconsistent with the requirements
of the POSIX standard would have, first, very angry customers, and
later, very few customers - but would not be violating any requirement
of the C standard in the process of losing those customers.

He would not even have to document
it (as would be the case for "implementation-defined behaviour")?
Obviously no sane compiler implementer /would/ do such a thing, but
there seems to me to be a loophole there. Maybe the final bit of
"legalese" is just too subtle for me here - I'll ignore it for now, and
simply assume compiler writers write sensible tools!

You're putting too much responsibility on the C standard, and not enough
responsibility on other standards.
 
M

Malcolm McLean

On 04/12/13 13:08, James Kuyper wrote:


This means that a compiler implementer could theoretical produce nasal
daemons when the programmer calls waitpid(), just because the writer
thought that is "most appropriate" ? He would not even have to document
it (as would be the case for "implementation-defined behaviour")?
Yes. He provides a library with a C-callable interface binding to the symbol
waitpid. But he doesn't document what the function does. So if you call it,
anything could happen, constrained only by the physical capabilities of
the machine and, maybe, protections offered by the operating system.
 
S

Stephen Sprunk

It's likely that your code has undefined behavior regardless of the
optimization level (in fact, UB is always independent of the
optimization level), and it just happened to "work" at "-O0" and not
at "-O3". Which is why compiling with "-O3", even if you don't
intend to run the optimized code, can be a good way to flush out
bugs.

I'm sure that was the case, but I didn't know that at the time. What I
did know is that GCC did what I _expected_ my (UB-ridden) code to do at
-O0 but not at -O3, and that it only warned me of the latter.

IMHO, if an implementation is going to warn about questionable code, it
should do so regardless of the optimization level selected.
I don't recall any such proposal.

You're the one that made it!

S
 
K

Keith Thompson

Stephen Sprunk said:
I'm sure that was the case, but I didn't know that at the time. What I
did know is that GCC did what I _expected_ my (UB-ridden) code to do at
-O0 but not at -O3, and that it only warned me of the latter.

IMHO, if an implementation is going to warn about questionable code, it
should do so regardless of the optimization level selected.

The problem is that doing so would require the compiler to do all
the additional analysis at -O0 that it does at -O3.

If you want additional warnings, compile with optimization to enable
the extra analysis that's required before the warnings can be issued.

If you want the compiler doing that analysis unconditionally (thus
slowing down -O0 compilations), you can certainly take it up with the
gcc maintainers. But they'd likely respond that typing "-O3" isn't
enough of a burden to justify changing the compiler's design.
You're the one that made it!

Not that I recall. Can you cite the article where I said that?
 
K

Keith Thompson

Stephen Sprunk said:
I'm sure that was the case, but I didn't know that at the time. What I
did know is that GCC did what I _expected_ my (UB-ridden) code to do at
-O0 but not at -O3, and that it only warned me of the latter.

IMHO, if an implementation is going to warn about questionable code, it
should do so regardless of the optimization level selected.

The problem is that doing so would require the compiler to do all
the additional analysis at -O0 that it does at -O3.

If you want additional warnings, compile with optimization to enable
the extra analysis that's required before the warnings can be issued.

If you want the compiler doing that analysis unconditionally (thus
slowing down -O0 compilations), you can certainly take it up with the
gcc maintainers. But they'd likely respond that typing "-O3" isn't
enough of a burden to justify changing the compiler's design.
You're the one that made it!

Not that I recall. Can you cite the article where I said that?
 
T

Tim Rentsch

Keith Thompson said:
[discussing choice of gcc compiler options]

"-Werror" is often a good idea; on the other hand, it makes gcc
non-conforming, since it causes it to reject some conforming code.

(Presumably you meant "some strictly conforming code" there.)

I believe what you say is true, but only barely. In most cases
the culprit is not -Werror but not giving the right settings for
other warning options. In particular, the combination of

-Wno-unused-result
-Wno-div-by-zero
-Wno-deprecated
-Wno-deprecated-declarations
-Wno-overflow
-Wno-int-to-pointer-cast
-Wno-pointer-to-int-cast

gets gcc pretty close to being conforming even with -Werror.

There are some warnings (which ones?) that gcc gives that
cannot be separately turned off, and these interfere with
the behavior of -Werror being conforming. It would be nice
to file bug reports for gcc, to get compiler options for those
offending conditions put in.

On the practical side, do you know of any examples of "normal
code" (eg, as opposed to something written purposely to show
gcc's erroneous behavior) that gives a warning message under gcc
in one of its nominally conforming mode, with the above compiler
options in force?
 
T

Tim Rentsch

David Brown said:
On 12/04/2013 02:44 AM, David Brown wrote:
...

That's not quite the right way to think about it. In general,
the reason why the committee chose to leave the behavior of a C
construct undefined was that doing so gives the implementation
the freedom needed to do whatever it considers is most
appropriate. In the particular, if the behavior of a call to
waitpid() were well-defined, that would force the implementor to
make sure that the call had precisely the behavior defined for it
by the C standard (whatever that might be), whether or not the OS
provided a function of the same name, and it would have to do so
even if the behavior specified by the C standard were different
from the behavior provided by the OS function of the same name.

Because the behavior is undefined, an implementation of C is free
to implement the call as simply conforming to the calling
conventions of the corresponding platform, and leaving the details
of what happens when it is called up to the library containing the
corresponding function.

This means that a compiler implementer could theoretical produce
nasal daemons when the programmer calls waitpid(), just because the
writer thought that is "most appropriate" ? He would not even have
to document it (as would be the case for "implementation-defined
behaviour")? Obviously no sane compiler implementer /would/ do such
a thing, but there seems to me to be a loophole there. [snip
elaboration]

The previous responses here haven't been very illuminating, and I
believe have steered the conclusions in a bad direction. First,
when something is "undefined behavior" (as with the "unspecified"
or "implementation-defined" labels) that is not a description of
behavior but rather a specification of what behaviors are
permissible. Also, something people often forget, what's being
addressed is not the behavior of /programs/ but the behavior of
/implementations/. Of course it is common to talk about how a
certain program construct must behave, but that's only a shorthand
for saying the implementation must produce an executable which
when run will act according to the semantic descriptions given in
the Standard. That may be a subtle difference, but here it is an
important one.

Upon encountering a call to waitpid(), what is an implementation
obliged to do? The answer is mostly found in 5.1.1.1 p1 and
5.1.1.2 p1. Each translation unit must be processed, linked with
library functions and also other TU's, and ultimately an executable
produced. During translation a call to waitpid() must be
processed just like any other function call. Similarly during
link time the linkage must be done just the way it would for
any other link-time connection. What does this mean for how
waitpid() behaves? There are two possibilities.

One is that there is a "magic" object file (magic in the sense
that its origins are unknown, but accepted by the linker even
though the compiler didn't produce it) that defines an external
reference for waitpid(), so the hookup is complete. A call to
waitpid() happens in the same way that other function calls do
(ie, the implementation is obliged to do that), but what
waitpid() does is outside the domain of the implementation.
Whatever happens is not defined behavior, unspecified behavior,
implementation-defined behavior, or undefined behavior, because
these terms make sense only in the context of a C implementation
processing (all or part of) a C program. Here that hasn't
happened. Probably the best term for this situation is
"extra-linguistic behavior" - it is simply outside the realm of
what the C standard considers. The call must be done normally;
after that some other set of rules is in charge.

The other possibility is that waitpid() is supplied by one of
the implementation's library components, ie, a component the
implementation knows about. In this case such a function would
count as an extension in the sense of 4 p6. This in turn says
something about the behavior - in section 4 p8, the Standard
requires implementations to defined (among other things) all
extensions. So what waitpid() does must be defined explicitly
(under this scenario) by the implementation, even though it does
not fall under the heading of 'implementation-defined'.

So perhaps you can rest easier now, knowing that in neither case
is the implementation off the hook for what happens when making
a call to waitpid() - after the call is made, maybe, but not
before. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top