public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PR c++/78572] handle array self references in intializers
@ 2016-12-20 16:42 Aldy Hernandez
  2016-12-20 17:21 ` Nathan Sidwell
  0 siblings, 1 reply; 6+ messages in thread
From: Aldy Hernandez @ 2016-12-20 16:42 UTC (permalink / raw)
  To: jason merrill, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1563 bytes --]

The problem in this PR is that we're trying to initialize an array with 
members of itself:

	int array[10] = { array[3]=5, array[7]=3 };

The C++ front-end, in store_init_value() via cxx_constant_value() and 
friends will transform:

	{array[3] = 5, array[7] = 3}

into:

	{[3]=5, [0]=5, [7]=3, [1]=3}

which looks invalid to output_constructor*(), presumably because it 
isn't sorted by increasing index.

Jakub has even gone further to show that for the following:

	... = { array[3]=5, array[7]=3, array[7]=8, array[7] = 9 };

things get even worse, because we generate code to write twice into [3]:

	{[3]=5, [0]=5, [7]=9, [1]=3, [2]=8, [3]=9}

I took the easy way in cxx_eval_store_expression() and marked the 
expression as non_constant_p if we're trying to set an array from 
members of itself.  This causes us to to generate the initialization of 
the self-referencing array[sub] elements as a CLEANUP_POINT_EXPR:

<<cleanup_point <<< Unknown tree: expr_stmt
   array[0] = array[3] = 5 >>>>>;
<<cleanup_point <<< Unknown tree: expr_stmt
   array[1] = array[7] = 3 >>>>>;

Ultimately this yields correct code:

array:
         .zero   40
...
...

_GLOBAL__sub_I_array:
.LFB1:
         .cfi_startproc
         movl    $5, array+12(%rip)
         movl    $5, array(%rip)
         movl    $3, array+28(%rip)
         movl    $3, array+4(%rip)
	ret

Is this approach acceptable, or should we be marking this as 
non-constant somewhere else in the chain?  Or perhaps another approach 
would be to handle such constructor holes in the in the varasm code?

Aldy

[-- Attachment #2: curr --]
[-- Type: text/plain, Size: 1248 bytes --]

commit 295d93f60bcbec5b9959a7b3656f10aa0df71c9f
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Tue Dec 20 06:13:26 2016 -0500

            PR c++/78572
            * constexpr.c (cxx_eval_store_expression): Avoid array self
            references in initialization.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index aedd004..ac279ad 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3313,6 +3313,15 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	}
     }
 
+  /* Initializing an array from a variant of itself is a non-constant.
+     This avoids attemps at generating incorrect self references in
+     something like: int foo[10] = { stuff[3]=8 }.  */
+  if (TREE_CODE (target) == ARRAY_REF && object == ctx->object)
+    {
+      *non_constant_p = true;
+      return t;
+    }
+
   /* And then find/build up our initializer for the path to the subobject
      we're initializing.  */
   tree *valp;
diff --git a/gcc/testsuite/g++.dg/pr78572.C b/gcc/testsuite/g++.dg/pr78572.C
new file mode 100644
index 0000000..82ab4e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr78572.C
@@ -0,0 +1,9 @@
+// { dg-do compile } */
+
+static int array[10] = { array[3]=5, array[7]=3 };
+
+int
+main ()
+{
+  return 0;
+}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PR c++/78572] handle array self references in intializers
  2016-12-20 16:42 [PR c++/78572] handle array self references in intializers Aldy Hernandez
@ 2016-12-20 17:21 ` Nathan Sidwell
  2016-12-20 18:58   ` Aldy Hernandez
  0 siblings, 1 reply; 6+ messages in thread
From: Nathan Sidwell @ 2016-12-20 17:21 UTC (permalink / raw)
  To: Aldy Hernandez, jason merrill, gcc-patches

On 12/20/2016 11:25 AM, Aldy Hernandez wrote:
> The problem in this PR is that we're trying to initialize an array with
> members of itself:

> Jakub has even gone further to show that for the following:
>
>     ... = { array[3]=5, array[7]=3, array[7]=8, array[7] = 9 };
>
> things get even worse, because we generate code to write twice into [3]:
>
>     {[3]=5, [0]=5, [7]=9, [1]=3, [2]=8, [3]=9}

I looked at this a couple of weeks ago, and got confused.  It wasn't 
very clear to me how the side-effect assignments interact with any 
implict zero assignments.  (Especially if one then tries to support 
C-style designated initializers).

IIUC the side effects of an initializer are evaluated before the storage 
of the initializer itself (separated by a sequence point).  further it 
seems that default zero initialization of non-explicitly initialized 
elements happens after any side-effected store to that element (and 
hence zaps the sideeffect).  The rules for non-aggregates appear to be 
as-if one builds a temporary object and then copy-constructs it into the 
object -- clearly zapping any and all side-effects.  However for 
aggregate initializations it looked like elements were initialized in 
order -- so the side effect on a later element could overwrite the 
initialization of an earlier element.

It wasn't entirely clear.

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PR c++/78572] handle array self references in intializers
  2016-12-20 17:21 ` Nathan Sidwell
@ 2016-12-20 18:58   ` Aldy Hernandez
  2016-12-20 20:30     ` Nathan Sidwell
  0 siblings, 1 reply; 6+ messages in thread
From: Aldy Hernandez @ 2016-12-20 18:58 UTC (permalink / raw)
  To: Nathan Sidwell, jason merrill, gcc-patches

On 12/20/2016 11:48 AM, Nathan Sidwell wrote:
> On 12/20/2016 11:25 AM, Aldy Hernandez wrote:
>> The problem in this PR is that we're trying to initialize an array with
>> members of itself:
>
>> Jakub has even gone further to show that for the following:
>>
>>     ... = { array[3]=5, array[7]=3, array[7]=8, array[7] = 9 };
>>
>> things get even worse, because we generate code to write twice into [3]:
>>
>>     {[3]=5, [0]=5, [7]=9, [1]=3, [2]=8, [3]=9}
>
> I looked at this a couple of weeks ago, and got confused.  It wasn't
> very clear to me how the side-effect assignments interact with any
> implict zero assignments.  (Especially if one then tries to support
> C-style designated initializers).
>
> IIUC the side effects of an initializer are evaluated before the storage
> of the initializer itself (separated by a sequence point).  further it
> seems that default zero initialization of non-explicitly initialized
> elements happens after any side-effected store to that element (and
> hence zaps the sideeffect).  The rules for non-aggregates appear to be
> as-if one builds a temporary object and then copy-constructs it into the
> object -- clearly zapping any and all side-effects.  However for
> aggregate initializations it looked like elements were initialized in
> order -- so the side effect on a later element could overwrite the
> initialization of an earlier element.
>
> It wasn't entirely clear.

Looks like things are happening in the right order for this testcase. 
That is, the zeroing happens by virtue of it being in the global 
section, and the rest of the runtime initialization happening afterwards:

abulafia:/build/t/gcc$ cat a.cc
int array[10] = { array[3]=5, array[7]=3 };

int main()
{
   return 0;
}

assembly:
---------
         .size   array, 40
array:
         .zero   40

...
...

abulafia:/build/t/gcc$ gdb -n a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x40055b
(gdb) r
Starting program: /home/build/t/gcc/a.out

Breakpoint 1, 0x000000000040055b in main ()
(gdb) x/4x &array
0x601080 <array>:       0x00000005      0x00000003      0x00000000 
0x00000005
(gdb)

The above looks correct.

Now for the next example below I get what I intuitively expect, though I 
don't know if that's how it's defined in the standard:

int array[10] = { array[3]=5, 0x111, 0x222, 0x333 };

(gdb) x/4x &array
0x601040 <array>:       0x00000005      0x00000111      0x00000222 
0x00000005

That is, the array[3]=5 overwrites the last 0x333.  I would expect that...

Is any of this incorrect?

Aldy

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PR c++/78572] handle array self references in intializers
  2016-12-20 18:58   ` Aldy Hernandez
@ 2016-12-20 20:30     ` Nathan Sidwell
  2016-12-21 17:41       ` Aldy Hernandez
  0 siblings, 1 reply; 6+ messages in thread
From: Nathan Sidwell @ 2016-12-20 20:30 UTC (permalink / raw)
  To: Aldy Hernandez, jason merrill, gcc-patches

On 12/20/2016 01:52 PM, Aldy Hernandez wrote:

> int array[10] = { array[3]=5, 0x111, 0x222, 0x333 };
>
> (gdb) x/4x &array
> 0x601040 <array>:       0x00000005      0x00000111      0x00000222
> 0x00000005
>
> That is, the array[3]=5 overwrites the last 0x333.  I would expect that...

That may be wrong. Using the 4431 draft, [8.5.4]/4 says 'Within the 
initializer-list of a braced-init-list, the initializer-clauses, 
including any that result from pack expansions (14.5.3), are evaluated 
in the order in which they appear.'
It goes on to mention that there are sequence points, plus that the 
ordering holds regardless of the semantics of initialization.

So by my reading, the effects of the above list are:
a) assign 5 to array[3]
b) assign 5 to array[0] -- consume the first initializer
c) assign x111 to array[1] -- consume the second initializer
d) assign 0x222 to array[2] -- consume the third initializer
e) assign 0x333 to array[3] -- consume the fourth intializer,
                                overwrite #a

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PR c++/78572] handle array self references in intializers
  2016-12-20 20:30     ` Nathan Sidwell
@ 2016-12-21 17:41       ` Aldy Hernandez
  2016-12-21 17:47         ` Jakub Jelinek
  0 siblings, 1 reply; 6+ messages in thread
From: Aldy Hernandez @ 2016-12-21 17:41 UTC (permalink / raw)
  To: Nathan Sidwell, jason merrill, gcc-patches

On 12/20/2016 03:14 PM, Nathan Sidwell wrote:
> On 12/20/2016 01:52 PM, Aldy Hernandez wrote:
>
>> int array[10] = { array[3]=5, 0x111, 0x222, 0x333 };
>>
>> (gdb) x/4x &array
>> 0x601040 <array>:       0x00000005      0x00000111      0x00000222
>> 0x00000005
>>
>> That is, the array[3]=5 overwrites the last 0x333.  I would expect
>> that...
>
> That may be wrong. Using the 4431 draft, [8.5.4]/4 says 'Within the
> initializer-list of a braced-init-list, the initializer-clauses,
> including any that result from pack expansions (14.5.3), are evaluated
> in the order in which they appear.'
> It goes on to mention that there are sequence points, plus that the
> ordering holds regardless of the semantics of initialization.
>
> So by my reading, the effects of the above list are:
> a) assign 5 to array[3]
> b) assign 5 to array[0] -- consume the first initializer
> c) assign x111 to array[1] -- consume the second initializer
> d) assign 0x222 to array[2] -- consume the third initializer
> e) assign 0x333 to array[3] -- consume the fourth intializer,
>                                overwrite #a

Hmmmm, I see.

Since designated initializers in arrays are not supported in the C++ FE, 
should we sorry() on array initializers using self-reference like above 
the above?  Surely it's better than ICEing.  Or, would you prefer we 
implement the above semantics in the draft for GCC7?

Aldy

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PR c++/78572] handle array self references in intializers
  2016-12-21 17:41       ` Aldy Hernandez
@ 2016-12-21 17:47         ` Jakub Jelinek
  0 siblings, 0 replies; 6+ messages in thread
From: Jakub Jelinek @ 2016-12-21 17:47 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: Nathan Sidwell, jason merrill, gcc-patches

On Wed, Dec 21, 2016 at 12:29:37PM -0500, Aldy Hernandez wrote:
> > > int array[10] = { array[3]=5, 0x111, 0x222, 0x333 };
> > > 
> > > (gdb) x/4x &array
> > > 0x601040 <array>:       0x00000005      0x00000111      0x00000222
> > > 0x00000005
> > > 
> > > That is, the array[3]=5 overwrites the last 0x333.  I would expect
> > > that...
> > 
> > That may be wrong. Using the 4431 draft, [8.5.4]/4 says 'Within the
> > initializer-list of a braced-init-list, the initializer-clauses,
> > including any that result from pack expansions (14.5.3), are evaluated
> > in the order in which they appear.'
> > It goes on to mention that there are sequence points, plus that the
> > ordering holds regardless of the semantics of initialization.
> > 
> > So by my reading, the effects of the above list are:
> > a) assign 5 to array[3]
> > b) assign 5 to array[0] -- consume the first initializer
> > c) assign x111 to array[1] -- consume the second initializer
> > d) assign 0x222 to array[2] -- consume the third initializer
> > e) assign 0x333 to array[3] -- consume the fourth intializer,
> >                                overwrite #a
> 
> Hmmmm, I see.
> 
> Since designated initializers in arrays are not supported in the C++ FE,
> should we sorry() on array initializers using self-reference like above the
> above?  Surely it's better than ICEing.  Or, would you prefer we implement
> the above semantics in the draft for GCC7?

The side-effects are unrelated to designed initializers, only [3] = 5
is designed initializer, array[3] = 5 is not, it is just arbitrary
expression evaluated at that point to compute the corresponding element.
We do support designed initializers in C++, but only in a useless way
(require that the designators appear in order without any gaps).

	Jakub

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2016-12-21 17:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-20 16:42 [PR c++/78572] handle array self references in intializers Aldy Hernandez
2016-12-20 17:21 ` Nathan Sidwell
2016-12-20 18:58   ` Aldy Hernandez
2016-12-20 20:30     ` Nathan Sidwell
2016-12-21 17:41       ` Aldy Hernandez
2016-12-21 17:47         ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).