This is the mail archive of the egcs@egcs.cygnus.com mailing list for the EGCS project. See the EGCS home page for more information.
| Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
|---|---|---|
| Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Reading this in tree.def:
/* Assignment expression. Operand 0 is the what to set; 1, the new value. */
DEFTREECODE (MODIFY_EXPR, "modify_expr", 'e', 2)
I don't see any indication that a partial overlap between operand 0 and
the in-memory source of operand 1 is unsupported.
However, my 1999-12-07 draft copy of the C standard says this about
assignments, (section 3.3.16.1):
If the value being stored in an object is accessed from another
object that overlaps in any way the storage of the first object,
then the overlap shall be exact[...]; otherwise the behavior is
undefined.
Further, I've written three test programs in C, essentially stripped-down,
rewritten versions of two Fortran programs I just added to g77.f-torture
the other day, and two of these three C programs abort due to the
results of the partially-overlapped assignments in them. (The third
uses types small enough to fit into registers, amounting to temporaries.)
So, these test programs are (apparently) not standard-conforming in
the specific areas that cause them to crash (when compiled via gcc for
an x86 target).
However, the Fortran programs definitely *are* standard-conforming in
those areas, because the Fortran definition of assignment (non-CHARACTER
in FORTRAN 77, all assignments, IIRC, in Fortran 90 and beyond) is
much more like that of MODIFY_EXPR above.
That is, the definition of an assignment in Fortran is that the rhs
is evaluated, converted if necessary, *then* copied into the lhs.
That definition accommodates all possible cases of overlap, because
the evaluation part connotes the "capture" of the complete value of
the rhs -- as if in a temporary -- prior to any part of its being
stored.
Since MODIFY_EXPR is described in the same fashion, it is reasonable
for users of it to assume it supports partial overlap.
OTOH, since MODIFY_EXPR was defined originally to support the C language,
it is also reasonable for us to explicitly define it the way it already
works -- in terms similar to how the C language standard defines assignment,
which denotes certain special cases (involving partial overlap) as
undefined.
It seems like it might be important to decide this fairly soon, one way
or the other. g77's current problems with it don't strike me as major,
and gcc doesn't, AFAIK, *have* problems with it (because of how the C
standard is worded), but *other* language front ends might be making
unsuspecting use of MODIFY_EXPR in cases where it matters, more frequently
than it does for g77's users.
If we pick the "clean" definition of MODIFY_EXPR, we need to fix it so
it resorts to using a temporary for the ("evaluated") rhs prior to
copying it to the lhs, in any case where it isn't able to prove no
(local) aliasing.
If we pick the "C" definition of MODIFY_EXPR, we need to document that
accordingly (modify the commentary in tree.def, at least), and I need
to fix *g77* to resort to using a temporary, while others might need to
fix their front ends as well.
So, what do back-end tree-interface designers say about the right choice?
A C program that illustrates the problem (using character strings) is
enclosed below. It's inappropriate to add to the test suite as an
executable test, *unless* we decide to "extend" the GNU C language
to support assignments between partially-overlapping lhs/rhs pairs.
(Though, if we pick the "clean" definition of MODIFY_EXPR, and don't
change the gcc front end to somehow notify the back end to not worry
about partial overlaps, fixing MODIFY_EXPR would cause this test, as
well as the recently added g77 tests, to start working. E.g. there
could be two expressions that assign -- one akin to C's `memcpy', the
other to C's `memmove' -- otherwise, identical for cases where actual
evaluation of rhs, or portions thereof, needs to take place.)
tq vm, (burley)
/* Test whether aggregate assignment properly handles
full and (undefined by the C language standard) partial aliasing.
NOTE: this (19990327-2.f) is the large-aggregate version.
See 19990327-0.f for the single-precision version,
and 19990327-1.f for a double-precision version. */
#include <stdio.h>
typedef char T[20];
typedef struct {
T r;
T i;
} S;
static void tryfull (int, int);
static void trypart (int, int);
static void prepare (S *);
static void check (S, S);
int
main()
{
/* Make sure non-aliased cases work. */
tryfull (0, 2);
/* Now check various combinations of aliasing. */
/* Full aliasing. */
tryfull (0, 0);
/* Partial aliasing. */
trypart (1, 0);
trypart (1, 2);
return 0;
}
static void tryfull (int out, int in)
{
S expect;
union {
T array[4];
S carray[2];
} UU;
/* Make sure the indexes can be accommodated by the equivalences above. */
if ((out & 1) != 0)
abort ();
if ((in & 1) != 0)
abort ();
/* Convert the indexes into ones suitable for the COMPLEX array (carray). */
out /= 2;
in /= 2;
prepare (&UU.carray[in]);
expect = UU.carray[in];
UU.carray[out] = UU.carray[in];
check (expect, UU.carray[out]);
}
static void
trypart (int out, int in)
{
S expect;
union {
T array[6];
S carray[3];
struct {
T pad;
S carrayp[2];
} P;
} UU;
/* Make sure the indexes can be accommodated by the equivalences above. */
if ((out & 1) != 1)
abort ();
if ((in & 1) != 0)
abort ();
/* Convert the indexes into ones suitable for the COMPLEX array (carray). */
out /= 2;
in /= 2;
prepare (&UU.carray[in]);
expect = UU.carray[in];
/* Here is the partially overlapping (aliased) assignment. */
UU.P.carrayp[out] = UU.carray[in];
check (expect, UU.P.carrayp[out]);
}
static void
prepare (S *val)
{
strcpy(val->r,"abcdefghijklmnopqrs");
strcpy(val->i,"ABCDEFGHIJKLMNOPQRS");
}
static void
check (S expect, S got)
{
if (strcmp (expect.r, got.r))
abort ();
if (strcmp (expect.i, got.i))
abort ();
}