Submitter: Rich Peterson (US)
Submission Date: 2007-03-09
Source: Rich Peterson <Rich.Peterson@hp.com>
Reference Document: WG14
N1124
Version:1.4
Date: 2008-09-12
Subject: C99 seems to exclude indeterminate value
from being an uninitialized register
Summary
The following function has undefined behavior under C90, but
appears to be
strictly conforming under C99:
int foo(void) { unsigned char uc; return uc + 1 >= 0; }
If that is true, then a C99 compiler for a real-life
architecture like ia64
that supports trap representations in hardware (via NaT values)
cannot in
general just allocate auto variables to registers and
leave initialization
to the source code as it would for most other architectures.
Instead it
would either have to initialize the register or allocate the
variable to
memory. This is because ia64 NaT values only exist in register
representations,
not in memory representations.
Rationale
In C90, 3.16 defines undefined behavior as "behavior,
upon use of a
non-portable or erroneous program construct, of erroneous data,
or of
indeterminately valued objects, for which the standard imposes
no
requirements...". And 6.5.7 says: "If an object that has
automatic storage
duration is not initialized explicitly, its value is
indeterminate." So it
directly follows that the above function has undefined behavior
under C90.
C99 then added a definition for indeterminate value
(3.17.2): "either an
unspecified value or a trap representation". The first problem is
that the
type unsigned char specifically is excluded from having any
trap
representations. This would seem to render non-conforming a
NaT
consumption fault when evaluating uc + 1 in the example
function.
Furthermore, my reading of 6.2.6.2 "Integer types" is that in
order for
any type to have trap representations, there must be padding bits
in the
in-memory representation of the type. This is because there does
not
appear to be any allowance for padding bits that are present only
in the
register representation of a type, but not in memory.
Since ia64 NaT values clearly exhibit the properties intended
for C99
trap representations, offering one of the few hardware
implementations
of those properties, it seems most likely that either my reading
is
faulty, or that the words do not correctly express the intent.
I
believe the intent of excluding type unsigned char from
having trap
representations was to allow it to be used to copy (via
memcpy)
arbitrary memory, in the case that memory might contain trap
representations for some types. I believe it was not the intent
to
require translators to perform run-time initialization of
uninitialized
auto objects of type unsigned char in order to suppress
hardware
detection of programming faults. And I believe it certainly was
not the
intent to require that all trap representations for any type
be
representable in memory, forbidding register-only trap
representations
like NaT values.
Unless someone can find text that supports register-only
trap
representations, I think this deserves a TC.
Suggested Technical Corrigendum
Page 6, 3.17.2, change the definition of "indeterminate
value"
Old:
either an unspecified value or a trap representation
New:
either an unspecified value or a trap representation; or in
the case of an object of automatic storage duration whose
address
is never taken, a value that behaves as if it were a trap
representation, even for types that have no trap
representations
in memory (including type unsigned char)
Committee Discussion (for history only)
On some hardware (e.g. Itanium), an 8-bit value may have as many as 257 different values (0-255 and a "Not a Thing" value). However, c99 explicitly forbids such a value for an unsigned char.
Page 6, 3.17.2, change the definition of indeterminate value to:
either an unspecified value or a trap representation; or in the case of an object of automatic storage duration whose address is never taken, a value that behaves as if it were a trap representation.
5.1.2.3 para 5 second bullet speaks to this also.
See WG14 e-mail SC22WG14.11380
While some agreed with the comments in this email others did not.
Regarding WG14 e-mail SC22WG14.11380, the observations about trap representations are correct, and it is unfortunate that the DR was written in a way that relied on trap representation terminology and behavior. In fact, the problem and solution are not really related to trap representation as defined in the standard at all.
A better way to describe the issue might be to talk about use of an uninitialized object that is eligible to have register storage class (i.e. an object of automatic storage duration whose address is never taken).
Also the original DR's suggested wording change was made in the definition of indeterminate value. The reason for that was to trigger undefined behavior, which would have worked in C90, but in C99 the definition of undefined behavior was changed such that it does not mention indeterminate value.
Change for C1X:
6.3.2.1 paragraph 2, add a sentence to the end:If the lvalue designates an object of automatic storage duration that could have been declared with register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer, and no assignment to it has been performed prior to the use), the behavior is undefined.