public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Question about changing {machine,type} modes during LTO
@ 2020-01-28  9:35 Erick Ochoa
  2020-01-29  9:52 ` Richard Biener
  0 siblings, 1 reply; 3+ messages in thread
From: Erick Ochoa @ 2020-01-28  9:35 UTC (permalink / raw)
  To: GCC Development, Christoph Müllner, Dr. Philipp Tomsich

Hello,

I have a problem with a transformation I'm working on and I would appreciate
some help. The transformation I am working on removes fields in structs early
during link-time. For the purposes of development and this example, my
transformation deletes the field identified as "delete_me" from the struct
identified as "astruct_s". These identifiers are hard coded in the
transformation at the moment.

For example:

```c
int
main()
{
   struct astruct_s { _Bool a; _Bool delete_me; _Bool c;};
   // more
}
```

should be equivalent to

```c
int
main()
{
   struct astruct_s { _Bool a; _Bool c;};
   // more
}
```

as long as no instruction accesses field "delete me".

I have succeeded in eliminating field "delete_me" from struct "astruct_s" and
at the same time successfully calculating field offsets and array offsets for
a subset of the C syntax. I am working on expanding the allowed syntax and at
the same time creating tests to verify my assumptions/work is still producing
correct results.

I was starting work on supporting arrays of multiple dimensions, when I found
an interesting edge case in my transformation. I was able to transform structs
of size 2, 3, (but not 4), 5, 6, 7, (but not 8), 9, 10... This was the stack
trace when the error was triggered:

```
a.c: In function ‘main’:
a.c:11:19: internal compiler error: in convert_move, at expr.c:219
   11 |  struct astruct_s b = a[argc][argc];
      |                   ^
0xb8bac3 convert_move(rtx_def*, rtx_def*, int)
        /home/eochoa/code/gcc/gcc/expr.c:219
0xb9f5cf store_expr(tree_node*, rtx_def*, int, bool, bool)
        /home/eochoa/code/gcc/gcc/expr.c:5825
0xb9d913 expand_assignment(tree_node*, tree_node*, bool)
        /home/eochoa/code/gcc/gcc/expr.c:5509
0xa08bfb expand_gimple_stmt_1
        /home/eochoa/code/gcc/gcc/cfgexpand.c:3746
0xa09047 expand_gimple_stmt
        /home/eochoa/code/gcc/gcc/cfgexpand.c:3844
0xa1170f expand_gimple_basic_block
        /home/eochoa/code/gcc/gcc/cfgexpand.c:5884
0xa134b7 execute
        /home/eochoa/code/gcc/gcc/cfgexpand.c:6539
Please submit a full bug report,
```

Looking at expr.c:219 I found the following assertions

```c
/* Copy data from FROM to TO, where the machine modes are not the same.
   Both modes may be integer, or both may be floating, or both may be
   fixed-point.
   UNSIGNEDP should be nonzero if FROM is an unsigned type.
   This causes zero-extension instead of sign-extension.  */

void
convert_move (rtx to, rtx from, int unsignedp)
{
  machine_mode to_mode = GET_MODE (to);
  machine_mode from_mode = GET_MODE (from);

  gcc_assert (to_mode != BLKmode);
  gcc_assert (from_mode != BLKmode); <-- crashes here
```

I started reading the gcc internals around machine modes:
https://gcc.gnu.org/onlinedocs/gccint/Machine-Modes.html
and tried the experiment where I first compiled a struct of size 2 (and delete
field "delete_me"), then of size 3 and so on, and so on. I noticed that the
TYPE_MODE for matches the machine mode. And that it varies with the size of the
struct. (Which agrees with the definition of machine mode.)

I originally thought that I needed to set TYPE_MODE myself, but if layout_type
is called after deleting the field (which it is), then TYPE_MODE is correctly
set somewhere within layout_type:
https://github.com/gcc-mirror/gcc/blob/68697710fdd35077e8617f493044b0ea717fc01a/gcc/stor-layout.c#L2203
I verified that layout_type is setting the correct values for TYPE_MODE when
transforming struct "astruct_s" by comparing the TYPE_MODE of different sizes
without the transformation applied. When transforming structs, layout_type
always returned a TYPE_MODE which matched the TYPE_MODE for unmodified structs
with the same size as the transformed struct (post transformation).

In other words:

For variable "struct not_transformed b" without transformation I obtain
the following relationship. Without transformation:

| size | typemode |
|------|----------|
| 1    | 13       |
| 2    | 14       |
| 3    | 1        |
| 4    | 15       |
| 5    | 1        |
| 6    | 1        |
| 7    | 1        |
| 8    | 16       |
| 9    | 1        |

With transformation (i.e. astruct_s b with a field named "delete_me")

| size before | size after | typemode |
|-------------|------------|----------|
| 2           | 1          | 13       |
| 3           | 2          | 14       |
| 4           | 3          | 1        |
| 5           | 4          | 15       |
| 6           | 5          | 1        |
| 7           | 6          | 1        |
| 8           | 7          | 1        |
| 9           | 8          | 16       |

I have a similar result for variable 
"struct astructs b[]". Without modifications:

| size | type_mode |
|------|-----------|
| 1    | 14       |
| 2    | 15       |
| 3    | 1        |
| 4    | 16       |
| 5    | 1        |
| 6    | 1        |

With deletion of a field:

| old size | size | type_mode|
|----------|------|----------|
| 2        | 1    | 14       |
| 3        | 2    | 15       |
| 4        | 3    | 1        | 
| 5        | 4    | 16       |
|6         | 5    | 1        |
| 8        | 7    | 1        |
| 9        | 8    | 17       |
| 10       | 9    | 1        |



So, going back to the error and the information that I had collected, I found
out that for structs of size 3 (and arrays holding structs of size 3) the
assigned TYPE_MODE for my machine should be BLKmode. E.g.

```c
int
main()
{
  struct untransformed { _Bool a; _Bool c; _Bool d;};
  struct untransformed b; // TYPE_MODE == BLKmode
  struct untransformed a[2]; // TYPE_MODE == BLKmode
  b = a[0];
}
```

So, when transforming structs of size 4, initially:

```c
int
main()
{
  struct astruct_s { _Bool a; _Bool c; _Bool delete_me; _Bool d;};
  struct astruct_s b; // TYPE_MODE != BLKmode
  struct astruct_s a[2]; // TYPE_MODE != BLKmode
  b = a[0];
}
```

However, after the struct is transformed, the TYPE_MODE becomes BLKmode.
This means, that the assertion that gets triggered is correct. `from_mode` is
indeed BLKmode and therefore the assertion gets triggered. "from_mode" should
be BLKmode, that's something I want and expect. And the assertion that is not triggered
`to_mode` is incorrect and should be triggered. This means to me that somehow we are
triggering a different execution path and hitting an assertion that we should
not have encountered in the first place.

This leads me to believe that I have not changed a TYPE_MODE somewhere in the
gimple code. Maybe specifically the variable "b" (since this is where the "to"
of the expression `b = a[0]` should be. However, printing the gimple code after
the transformation, shows that b is the new variable type with the correct
TYPE_MODE:

Before transformation
```
Executing structreorg
main (int argc, char * * argv)
{
  struct astruct_s a[2];
  struct astruct_s b;
  int D.10221;

  <bb 2> :
  b = a[0];
  b ={v} {CLOBBER};
  a ={v} {CLOBBER};
  _5 = 0;

  <bb 3> :
<L0>:
  return _5;

}
```

Some output of my pass:
```
modifying,astruct_s
offset,astruct_reorged,a,0
offset,astruct_reorged,c,1
offset,astruct_reorged,d,2
old type_mode 15
new type_mode 1 // This is BLKmode
new type,astruct_reorged

modifying,astruct_s[]
old type_mode 16
new type_mode 1 // This is BLKmode
new type,astruct_reorged[]
```

We can also look at the offending expression more indepth.
The type_mode's are unchanged here, but they are changed at the end.

```
b = a[0];
<rewrite_expr "b">
< type = astruct_s type_mode = 15>
    <rewrite_var_decl "b">
    < type = astruct_s type_mode = 15>
    </ type = astruct_s type_mode = 15>
    </rewrite_var_decl "b">
</ type = astruct_s type_mode = 15>
</rewrite_expr "b">

<rewrite_expr "a[0]">
< type = astruct_s type_mode = 15>
    <rewrite_array_ref "a[0]">
    < type = astruct_s type_mode = 15>

            <rewrite_expr "a">
            < type = astruct_s[] type_mode = 16>
                <rewrite_var_decl "a">
                < type = astruct_s[] type_mode = 16>
                </ type = astruct_s[] type_mode = 16>
            </ type = astruct_s[] type_mode = 16>
            </rewrite_expr "a">

            <rewrite_expr "0">
            < type = integer_cst type_mode = 15>
            </ type = integer_cst type_mode = 15>
            </rewrite_expr "0">

    </ type = astruct_reorged type_mode = 1>
    </rewrite_array_ref "a[0]">

</ type = astruct_reorged type_mode = 1>
</rewrite_expr "a[0]">

// ...SNIP...

<rewrite_expr "{CLOBBER}">
< type = astruct_s type_mode = 15>
    <rewrite_constructor "{CLOBBER}">
    < type = astruct_s type_mode = 15>
    </ type = astruct_reorged type_mode = 1>
    </rewrite_constructor "{CLOBBER}">
</ type = astruct_reorged type_mode = 1>
</rewrite_expr "{CLOBBER}">

// ...SNIP...

// Here is where the type mode are definitely modified for
// local variables
rewriting,local_decl  struct astruct_s a[2];,  struct astruct_reorged a[2]; 
rewriting,local_decl  struct astruct_s b;,  struct astruct_reorged b;

```

After the pass finishes this is the gimple I see.

```

main (int argc, char * * argv)
{
  struct astruct_reorged a[2];
  struct astruct_reorged b;
  int D.10221;

  <bb 2> :
  b = a[0];
  b ={v} {CLOBBER};
  a ={v} {CLOBBER};
  _5 = 0;

  <bb 3> :
<L0>:
  return _5;

}
```

So just to summarize, things changed include:
* Variable's Type b
* Variable's Type a
* Expression's Type a[0]
* {CLOBBER} expression's type

I have also tried using GDB to get a better grasp on how to fix the problem.
I use the following command to explore gcc's run time state in gdb.
$HOME/code/gcc-inst/bin/gcc -flto -fipa-typelist -fdump-ipa-typelist a.c -wrapper gdb,--args
I am able to see that the IPA passes are successfully executed, however, I am
never able to trigger a breakpoint during RTL generation. This is how I use gdb:

* I go to the third gdb instance to look at the linker in gdb
* set catchpoints for fork and vfork
* and look at the inferior process #5 which is where LTO is applied.
* I've tried to set a breakpoint for symbols "execute" and I mostly just see
all IPA passes, but I do not see pass_expand::execute.
* I've also looked other inferior processes but I cannot set a
breakpoint before the assertion is hit. GCC just exists normally.

Can anyone help me understand what could possibly be happening?
Some possibilities:
* Another LTO uses summary information and changes the type back to
non-BLKmode? (However, I also tried passing -flto-partition=none to
avoid summaries.)
* I am missing setting something in gimple which I do not know what that
could be? (Printing gimple doesn't show all information, but I did try
to set everything correctly).
* I am failing to communicate this change to other link time opts?
(I am changing the definition of this function as opposed to creating
a clone and then dropping the previous definition).
* Some other thing?

Any help would be appreciated!
Thanks

-Erick

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

* Re: Question about changing {machine,type} modes during LTO
  2020-01-28  9:35 Question about changing {machine,type} modes during LTO Erick Ochoa
@ 2020-01-29  9:52 ` Richard Biener
  2020-01-30 13:38   ` Erick Ochoa
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Biener @ 2020-01-29  9:52 UTC (permalink / raw)
  To: Erick Ochoa; +Cc: GCC Development, Christoph Müllner, Dr. Philipp Tomsich

On Mon, Jan 27, 2020 at 6:41 PM Erick Ochoa
<erick.ochoa@theobroma-systems.com> wrote:
>
> Hello,
>
> I have a problem with a transformation I'm working on and I would appreciate
> some help. The transformation I am working on removes fields in structs early
> during link-time. For the purposes of development and this example, my
> transformation deletes the field identified as "delete_me" from the struct
> identified as "astruct_s". These identifiers are hard coded in the
> transformation at the moment.
>
> For example:
>
> ```c
> int
> main()
> {
>    struct astruct_s { _Bool a; _Bool delete_me; _Bool c;};
>    // more
> }
> ```
>
> should be equivalent to
>
> ```c
> int
> main()
> {
>    struct astruct_s { _Bool a; _Bool c;};
>    // more
> }
> ```
>
> as long as no instruction accesses field "delete me".
>
> I have succeeded in eliminating field "delete_me" from struct "astruct_s" and
> at the same time successfully calculating field offsets and array offsets for
> a subset of the C syntax. I am working on expanding the allowed syntax and at
> the same time creating tests to verify my assumptions/work is still producing
> correct results.
>
> I was starting work on supporting arrays of multiple dimensions, when I found
> an interesting edge case in my transformation. I was able to transform structs
> of size 2, 3, (but not 4), 5, 6, 7, (but not 8), 9, 10... This was the stack
> trace when the error was triggered:
>
> ```
> a.c: In function ‘main’:
> a.c:11:19: internal compiler error: in convert_move, at expr.c:219
>    11 |  struct astruct_s b = a[argc][argc];
>       |                   ^
> 0xb8bac3 convert_move(rtx_def*, rtx_def*, int)
>         /home/eochoa/code/gcc/gcc/expr.c:219
> 0xb9f5cf store_expr(tree_node*, rtx_def*, int, bool, bool)
>         /home/eochoa/code/gcc/gcc/expr.c:5825
> 0xb9d913 expand_assignment(tree_node*, tree_node*, bool)
>         /home/eochoa/code/gcc/gcc/expr.c:5509
> 0xa08bfb expand_gimple_stmt_1
>         /home/eochoa/code/gcc/gcc/cfgexpand.c:3746
> 0xa09047 expand_gimple_stmt
>         /home/eochoa/code/gcc/gcc/cfgexpand.c:3844
> 0xa1170f expand_gimple_basic_block
>         /home/eochoa/code/gcc/gcc/cfgexpand.c:5884
> 0xa134b7 execute
>         /home/eochoa/code/gcc/gcc/cfgexpand.c:6539
> Please submit a full bug report,
> ```
>
> Looking at expr.c:219 I found the following assertions
>
> ```c
> /* Copy data from FROM to TO, where the machine modes are not the same.
>    Both modes may be integer, or both may be floating, or both may be
>    fixed-point.
>    UNSIGNEDP should be nonzero if FROM is an unsigned type.
>    This causes zero-extension instead of sign-extension.  */
>
> void
> convert_move (rtx to, rtx from, int unsignedp)
> {
>   machine_mode to_mode = GET_MODE (to);
>   machine_mode from_mode = GET_MODE (from);
>
>   gcc_assert (to_mode != BLKmode);
>   gcc_assert (from_mode != BLKmode); <-- crashes here
> ```
>
> I started reading the gcc internals around machine modes:
> https://gcc.gnu.org/onlinedocs/gccint/Machine-Modes.html
> and tried the experiment where I first compiled a struct of size 2 (and delete
> field "delete_me"), then of size 3 and so on, and so on. I noticed that the
> TYPE_MODE for matches the machine mode. And that it varies with the size of the
> struct. (Which agrees with the definition of machine mode.)
>
> I originally thought that I needed to set TYPE_MODE myself, but if layout_type
> is called after deleting the field (which it is), then TYPE_MODE is correctly
> set somewhere within layout_type:
> https://github.com/gcc-mirror/gcc/blob/68697710fdd35077e8617f493044b0ea717fc01a/gcc/stor-layout.c#L2203
> I verified that layout_type is setting the correct values for TYPE_MODE when
> transforming struct "astruct_s" by comparing the TYPE_MODE of different sizes
> without the transformation applied. When transforming structs, layout_type
> always returned a TYPE_MODE which matched the TYPE_MODE for unmodified structs
> with the same size as the transformed struct (post transformation).
>
> In other words:
>
> For variable "struct not_transformed b" without transformation I obtain
> the following relationship. Without transformation:
>
> | size | typemode |
> |------|----------|
> | 1    | 13       |
> | 2    | 14       |
> | 3    | 1        |
> | 4    | 15       |
> | 5    | 1        |
> | 6    | 1        |
> | 7    | 1        |
> | 8    | 16       |
> | 9    | 1        |
>
> With transformation (i.e. astruct_s b with a field named "delete_me")
>
> | size before | size after | typemode |
> |-------------|------------|----------|
> | 2           | 1          | 13       |
> | 3           | 2          | 14       |
> | 4           | 3          | 1        |
> | 5           | 4          | 15       |
> | 6           | 5          | 1        |
> | 7           | 6          | 1        |
> | 8           | 7          | 1        |
> | 9           | 8          | 16       |
>
> I have a similar result for variable
> "struct astructs b[]". Without modifications:
>
> | size | type_mode |
> |------|-----------|
> | 1    | 14       |
> | 2    | 15       |
> | 3    | 1        |
> | 4    | 16       |
> | 5    | 1        |
> | 6    | 1        |
>
> With deletion of a field:
>
> | old size | size | type_mode|
> |----------|------|----------|
> | 2        | 1    | 14       |
> | 3        | 2    | 15       |
> | 4        | 3    | 1        |
> | 5        | 4    | 16       |
> |6         | 5    | 1        |
> | 8        | 7    | 1        |
> | 9        | 8    | 17       |
> | 10       | 9    | 1        |
>
>
>
> So, going back to the error and the information that I had collected, I found
> out that for structs of size 3 (and arrays holding structs of size 3) the
> assigned TYPE_MODE for my machine should be BLKmode. E.g.
>
> ```c
> int
> main()
> {
>   struct untransformed { _Bool a; _Bool c; _Bool d;};
>   struct untransformed b; // TYPE_MODE == BLKmode
>   struct untransformed a[2]; // TYPE_MODE == BLKmode
>   b = a[0];
> }
> ```
>
> So, when transforming structs of size 4, initially:
>
> ```c
> int
> main()
> {
>   struct astruct_s { _Bool a; _Bool c; _Bool delete_me; _Bool d;};
>   struct astruct_s b; // TYPE_MODE != BLKmode
>   struct astruct_s a[2]; // TYPE_MODE != BLKmode
>   b = a[0];
> }
> ```
>
> However, after the struct is transformed, the TYPE_MODE becomes BLKmode.
> This means, that the assertion that gets triggered is correct. `from_mode` is
> indeed BLKmode and therefore the assertion gets triggered. "from_mode" should
> be BLKmode, that's something I want and expect. And the assertion that is not triggered
> `to_mode` is incorrect and should be triggered. This means to me that somehow we are
> triggering a different execution path and hitting an assertion that we should
> not have encountered in the first place.
>
> This leads me to believe that I have not changed a TYPE_MODE somewhere in the
> gimple code. Maybe specifically the variable "b" (since this is where the "to"
> of the expression `b = a[0]` should be. However, printing the gimple code after
> the transformation, shows that b is the new variable type with the correct
> TYPE_MODE:
>
> Before transformation
> ```
> Executing structreorg
> main (int argc, char * * argv)
> {
>   struct astruct_s a[2];
>   struct astruct_s b;
>   int D.10221;
>
>   <bb 2> :
>   b = a[0];
>   b ={v} {CLOBBER};
>   a ={v} {CLOBBER};
>   _5 = 0;
>
>   <bb 3> :
> <L0>:
>   return _5;
>
> }
> ```
>
> Some output of my pass:
> ```
> modifying,astruct_s
> offset,astruct_reorged,a,0
> offset,astruct_reorged,c,1
> offset,astruct_reorged,d,2
> old type_mode 15
> new type_mode 1 // This is BLKmode
> new type,astruct_reorged
>
> modifying,astruct_s[]
> old type_mode 16
> new type_mode 1 // This is BLKmode
> new type,astruct_reorged[]
> ```
>
> We can also look at the offending expression more indepth.
> The type_mode's are unchanged here, but they are changed at the end.
>
> ```
> b = a[0];
> <rewrite_expr "b">
> < type = astruct_s type_mode = 15>
>     <rewrite_var_decl "b">
>     < type = astruct_s type_mode = 15>
>     </ type = astruct_s type_mode = 15>
>     </rewrite_var_decl "b">
> </ type = astruct_s type_mode = 15>
> </rewrite_expr "b">
>
> <rewrite_expr "a[0]">
> < type = astruct_s type_mode = 15>
>     <rewrite_array_ref "a[0]">
>     < type = astruct_s type_mode = 15>
>
>             <rewrite_expr "a">
>             < type = astruct_s[] type_mode = 16>
>                 <rewrite_var_decl "a">
>                 < type = astruct_s[] type_mode = 16>
>                 </ type = astruct_s[] type_mode = 16>
>             </ type = astruct_s[] type_mode = 16>
>             </rewrite_expr "a">
>
>             <rewrite_expr "0">
>             < type = integer_cst type_mode = 15>
>             </ type = integer_cst type_mode = 15>
>             </rewrite_expr "0">
>
>     </ type = astruct_reorged type_mode = 1>
>     </rewrite_array_ref "a[0]">
>
> </ type = astruct_reorged type_mode = 1>
> </rewrite_expr "a[0]">
>
> // ...SNIP...
>
> <rewrite_expr "{CLOBBER}">
> < type = astruct_s type_mode = 15>
>     <rewrite_constructor "{CLOBBER}">
>     < type = astruct_s type_mode = 15>
>     </ type = astruct_reorged type_mode = 1>
>     </rewrite_constructor "{CLOBBER}">
> </ type = astruct_reorged type_mode = 1>
> </rewrite_expr "{CLOBBER}">
>
> // ...SNIP...
>
> // Here is where the type mode are definitely modified for
> // local variables
> rewriting,local_decl  struct astruct_s a[2];,  struct astruct_reorged a[2];
> rewriting,local_decl  struct astruct_s b;,  struct astruct_reorged b;
>
> ```
>
> After the pass finishes this is the gimple I see.
>
> ```
>
> main (int argc, char * * argv)
> {
>   struct astruct_reorged a[2];
>   struct astruct_reorged b;
>   int D.10221;
>
>   <bb 2> :
>   b = a[0];
>   b ={v} {CLOBBER};
>   a ={v} {CLOBBER};
>   _5 = 0;
>
>   <bb 3> :
> <L0>:
>   return _5;
>
> }
> ```
>
> So just to summarize, things changed include:
> * Variable's Type b
> * Variable's Type a
> * Expression's Type a[0]
> * {CLOBBER} expression's type
>
> I have also tried using GDB to get a better grasp on how to fix the problem.
> I use the following command to explore gcc's run time state in gdb.
> $HOME/code/gcc-inst/bin/gcc -flto -fipa-typelist -fdump-ipa-typelist a.c -wrapper gdb,--args
> I am able to see that the IPA passes are successfully executed, however, I am
> never able to trigger a breakpoint during RTL generation. This is how I use gdb:
>
> * I go to the third gdb instance to look at the linker in gdb
> * set catchpoints for fork and vfork
> * and look at the inferior process #5 which is where LTO is applied.
> * I've tried to set a breakpoint for symbols "execute" and I mostly just see
> all IPA passes, but I do not see pass_expand::execute.
> * I've also looked other inferior processes but I cannot set a
> breakpoint before the assertion is hit. GCC just exists normally.
>
> Can anyone help me understand what could possibly be happening?
> Some possibilities:
> * Another LTO uses summary information and changes the type back to
> non-BLKmode? (However, I also tried passing -flto-partition=none to
> avoid summaries.)
> * I am missing setting something in gimple which I do not know what that
> could be? (Printing gimple doesn't show all information, but I did try
> to set everything correctly).
> * I am failing to communicate this change to other link time opts?
> (I am changing the definition of this function as opposed to creating
> a clone and then dropping the previous definition).
> * Some other thing?

My first guess would be that you need to re-layout all decls that refer to the
re-layouted type.

Richard.

> Any help would be appreciated!
> Thanks
>
> -Erick

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

* Re: Question about changing {machine,type} modes during LTO
  2020-01-29  9:52 ` Richard Biener
@ 2020-01-30 13:38   ` Erick Ochoa
  0 siblings, 0 replies; 3+ messages in thread
From: Erick Ochoa @ 2020-01-30 13:38 UTC (permalink / raw)
  To: Richard Biener
  Cc: GCC Development, Christoph Müllner, Dr. Philipp Tomsich

Hi Richard,

Thanks! I can confirm that changing VAR_DECL's type
and calling relayout_decl fixes my problem.

On 2020-01-28 4:35 a.m., Richard Biener wrote:
> On Mon, Jan 27, 2020 at 6:41 PM Erick Ochoa
> <erick.ochoa@theobroma-systems.com> wrote:
>>
>> Hello,
>>
>> I have a problem with a transformation I'm working on and I would appreciate
>> some help. The transformation I am working on removes fields in structs early
>> during link-time. For the purposes of development and this example, my
>> transformation deletes the field identified as "delete_me" from the struct
>> identified as "astruct_s". These identifiers are hard coded in the
>> transformation at the moment.
>>
>> For example:
>>
>> ```c
>> int
>> main()
>> {
>>    struct astruct_s { _Bool a; _Bool delete_me; _Bool c;};
>>    // more
>> }
>> ```
>>
>> should be equivalent to
>>
>> ```c
>> int
>> main()
>> {
>>    struct astruct_s { _Bool a; _Bool c;};
>>    // more
>> }
>> ```
>>
>> as long as no instruction accesses field "delete me".
>>
>> I have succeeded in eliminating field "delete_me" from struct "astruct_s" and
>> at the same time successfully calculating field offsets and array offsets for
>> a subset of the C syntax. I am working on expanding the allowed syntax and at
>> the same time creating tests to verify my assumptions/work is still producing
>> correct results.
>>
>> I was starting work on supporting arrays of multiple dimensions, when I found
>> an interesting edge case in my transformation. I was able to transform structs
>> of size 2, 3, (but not 4), 5, 6, 7, (but not 8), 9, 10... This was the stack
>> trace when the error was triggered:
>>
>> ```
>> a.c: In function ‘main’:
>> a.c:11:19: internal compiler error: in convert_move, at expr.c:219
>>    11 |  struct astruct_s b = a[argc][argc];
>>       |                   ^
>> 0xb8bac3 convert_move(rtx_def*, rtx_def*, int)
>>         /home/eochoa/code/gcc/gcc/expr.c:219
>> 0xb9f5cf store_expr(tree_node*, rtx_def*, int, bool, bool)
>>         /home/eochoa/code/gcc/gcc/expr.c:5825
>> 0xb9d913 expand_assignment(tree_node*, tree_node*, bool)
>>         /home/eochoa/code/gcc/gcc/expr.c:5509
>> 0xa08bfb expand_gimple_stmt_1
>>         /home/eochoa/code/gcc/gcc/cfgexpand.c:3746
>> 0xa09047 expand_gimple_stmt
>>         /home/eochoa/code/gcc/gcc/cfgexpand.c:3844
>> 0xa1170f expand_gimple_basic_block
>>         /home/eochoa/code/gcc/gcc/cfgexpand.c:5884
>> 0xa134b7 execute
>>         /home/eochoa/code/gcc/gcc/cfgexpand.c:6539
>> Please submit a full bug report,
>> ```
>>
>> Looking at expr.c:219 I found the following assertions
>>
>> ```c
>> /* Copy data from FROM to TO, where the machine modes are not the same.
>>    Both modes may be integer, or both may be floating, or both may be
>>    fixed-point.
>>    UNSIGNEDP should be nonzero if FROM is an unsigned type.
>>    This causes zero-extension instead of sign-extension.  */
>>
>> void
>> convert_move (rtx to, rtx from, int unsignedp)
>> {
>>   machine_mode to_mode = GET_MODE (to);
>>   machine_mode from_mode = GET_MODE (from);
>>
>>   gcc_assert (to_mode != BLKmode);
>>   gcc_assert (from_mode != BLKmode); <-- crashes here
>> ```
>>
>> I started reading the gcc internals around machine modes:
>> https://gcc.gnu.org/onlinedocs/gccint/Machine-Modes.html
>> and tried the experiment where I first compiled a struct of size 2 (and delete
>> field "delete_me"), then of size 3 and so on, and so on. I noticed that the
>> TYPE_MODE for matches the machine mode. And that it varies with the size of the
>> struct. (Which agrees with the definition of machine mode.)
>>
>> I originally thought that I needed to set TYPE_MODE myself, but if layout_type
>> is called after deleting the field (which it is), then TYPE_MODE is correctly
>> set somewhere within layout_type:
>> https://github.com/gcc-mirror/gcc/blob/68697710fdd35077e8617f493044b0ea717fc01a/gcc/stor-layout.c#L2203
>> I verified that layout_type is setting the correct values for TYPE_MODE when
>> transforming struct "astruct_s" by comparing the TYPE_MODE of different sizes
>> without the transformation applied. When transforming structs, layout_type
>> always returned a TYPE_MODE which matched the TYPE_MODE for unmodified structs
>> with the same size as the transformed struct (post transformation).
>>
>> In other words:
>>
>> For variable "struct not_transformed b" without transformation I obtain
>> the following relationship. Without transformation:
>>
>> | size | typemode |
>> |------|----------|
>> | 1    | 13       |
>> | 2    | 14       |
>> | 3    | 1        |
>> | 4    | 15       |
>> | 5    | 1        |
>> | 6    | 1        |
>> | 7    | 1        |
>> | 8    | 16       |
>> | 9    | 1        |
>>
>> With transformation (i.e. astruct_s b with a field named "delete_me")
>>
>> | size before | size after | typemode |
>> |-------------|------------|----------|
>> | 2           | 1          | 13       |
>> | 3           | 2          | 14       |
>> | 4           | 3          | 1        |
>> | 5           | 4          | 15       |
>> | 6           | 5          | 1        |
>> | 7           | 6          | 1        |
>> | 8           | 7          | 1        |
>> | 9           | 8          | 16       |
>>
>> I have a similar result for variable
>> "struct astructs b[]". Without modifications:
>>
>> | size | type_mode |
>> |------|-----------|
>> | 1    | 14       |
>> | 2    | 15       |
>> | 3    | 1        |
>> | 4    | 16       |
>> | 5    | 1        |
>> | 6    | 1        |
>>
>> With deletion of a field:
>>
>> | old size | size | type_mode|
>> |----------|------|----------|
>> | 2        | 1    | 14       |
>> | 3        | 2    | 15       |
>> | 4        | 3    | 1        |
>> | 5        | 4    | 16       |
>> |6         | 5    | 1        |
>> | 8        | 7    | 1        |
>> | 9        | 8    | 17       |
>> | 10       | 9    | 1        |
>>
>>
>>
>> So, going back to the error and the information that I had collected, I found
>> out that for structs of size 3 (and arrays holding structs of size 3) the
>> assigned TYPE_MODE for my machine should be BLKmode. E.g.
>>
>> ```c
>> int
>> main()
>> {
>>   struct untransformed { _Bool a; _Bool c; _Bool d;};
>>   struct untransformed b; // TYPE_MODE == BLKmode
>>   struct untransformed a[2]; // TYPE_MODE == BLKmode
>>   b = a[0];
>> }
>> ```
>>
>> So, when transforming structs of size 4, initially:
>>
>> ```c
>> int
>> main()
>> {
>>   struct astruct_s { _Bool a; _Bool c; _Bool delete_me; _Bool d;};
>>   struct astruct_s b; // TYPE_MODE != BLKmode
>>   struct astruct_s a[2]; // TYPE_MODE != BLKmode
>>   b = a[0];
>> }
>> ```
>>
>> However, after the struct is transformed, the TYPE_MODE becomes BLKmode.
>> This means, that the assertion that gets triggered is correct. `from_mode` is
>> indeed BLKmode and therefore the assertion gets triggered. "from_mode" should
>> be BLKmode, that's something I want and expect. And the assertion that is not triggered
>> `to_mode` is incorrect and should be triggered. This means to me that somehow we are
>> triggering a different execution path and hitting an assertion that we should
>> not have encountered in the first place.
>>
>> This leads me to believe that I have not changed a TYPE_MODE somewhere in the
>> gimple code. Maybe specifically the variable "b" (since this is where the "to"
>> of the expression `b = a[0]` should be. However, printing the gimple code after
>> the transformation, shows that b is the new variable type with the correct
>> TYPE_MODE:
>>
>> Before transformation
>> ```
>> Executing structreorg
>> main (int argc, char * * argv)
>> {
>>   struct astruct_s a[2];
>>   struct astruct_s b;
>>   int D.10221;
>>
>>   <bb 2> :
>>   b = a[0];
>>   b ={v} {CLOBBER};
>>   a ={v} {CLOBBER};
>>   _5 = 0;
>>
>>   <bb 3> :
>> <L0>:
>>   return _5;
>>
>> }
>> ```
>>
>> Some output of my pass:
>> ```
>> modifying,astruct_s
>> offset,astruct_reorged,a,0
>> offset,astruct_reorged,c,1
>> offset,astruct_reorged,d,2
>> old type_mode 15
>> new type_mode 1 // This is BLKmode
>> new type,astruct_reorged
>>
>> modifying,astruct_s[]
>> old type_mode 16
>> new type_mode 1 // This is BLKmode
>> new type,astruct_reorged[]
>> ```
>>
>> We can also look at the offending expression more indepth.
>> The type_mode's are unchanged here, but they are changed at the end.
>>
>> ```
>> b = a[0];
>> <rewrite_expr "b">
>> < type = astruct_s type_mode = 15>
>>     <rewrite_var_decl "b">
>>     < type = astruct_s type_mode = 15>
>>     </ type = astruct_s type_mode = 15>
>>     </rewrite_var_decl "b">
>> </ type = astruct_s type_mode = 15>
>> </rewrite_expr "b">
>>
>> <rewrite_expr "a[0]">
>> < type = astruct_s type_mode = 15>
>>     <rewrite_array_ref "a[0]">
>>     < type = astruct_s type_mode = 15>
>>
>>             <rewrite_expr "a">
>>             < type = astruct_s[] type_mode = 16>
>>                 <rewrite_var_decl "a">
>>                 < type = astruct_s[] type_mode = 16>
>>                 </ type = astruct_s[] type_mode = 16>
>>             </ type = astruct_s[] type_mode = 16>
>>             </rewrite_expr "a">
>>
>>             <rewrite_expr "0">
>>             < type = integer_cst type_mode = 15>
>>             </ type = integer_cst type_mode = 15>
>>             </rewrite_expr "0">
>>
>>     </ type = astruct_reorged type_mode = 1>
>>     </rewrite_array_ref "a[0]">
>>
>> </ type = astruct_reorged type_mode = 1>
>> </rewrite_expr "a[0]">
>>
>> // ...SNIP...
>>
>> <rewrite_expr "{CLOBBER}">
>> < type = astruct_s type_mode = 15>
>>     <rewrite_constructor "{CLOBBER}">
>>     < type = astruct_s type_mode = 15>
>>     </ type = astruct_reorged type_mode = 1>
>>     </rewrite_constructor "{CLOBBER}">
>> </ type = astruct_reorged type_mode = 1>
>> </rewrite_expr "{CLOBBER}">
>>
>> // ...SNIP...
>>
>> // Here is where the type mode are definitely modified for
>> // local variables
>> rewriting,local_decl  struct astruct_s a[2];,  struct astruct_reorged a[2];
>> rewriting,local_decl  struct astruct_s b;,  struct astruct_reorged b;
>>
>> ```
>>
>> After the pass finishes this is the gimple I see.
>>
>> ```
>>
>> main (int argc, char * * argv)
>> {
>>   struct astruct_reorged a[2];
>>   struct astruct_reorged b;
>>   int D.10221;
>>
>>   <bb 2> :
>>   b = a[0];
>>   b ={v} {CLOBBER};
>>   a ={v} {CLOBBER};
>>   _5 = 0;
>>
>>   <bb 3> :
>> <L0>:
>>   return _5;
>>
>> }
>> ```
>>
>> So just to summarize, things changed include:
>> * Variable's Type b
>> * Variable's Type a
>> * Expression's Type a[0]
>> * {CLOBBER} expression's type
>>
>> I have also tried using GDB to get a better grasp on how to fix the problem.
>> I use the following command to explore gcc's run time state in gdb.
>> $HOME/code/gcc-inst/bin/gcc -flto -fipa-typelist -fdump-ipa-typelist a.c -wrapper gdb,--args
>> I am able to see that the IPA passes are successfully executed, however, I am
>> never able to trigger a breakpoint during RTL generation. This is how I use gdb:
>>
>> * I go to the third gdb instance to look at the linker in gdb
>> * set catchpoints for fork and vfork
>> * and look at the inferior process #5 which is where LTO is applied.
>> * I've tried to set a breakpoint for symbols "execute" and I mostly just see
>> all IPA passes, but I do not see pass_expand::execute.
>> * I've also looked other inferior processes but I cannot set a
>> breakpoint before the assertion is hit. GCC just exists normally.
>>
>> Can anyone help me understand what could possibly be happening?
>> Some possibilities:
>> * Another LTO uses summary information and changes the type back to
>> non-BLKmode? (However, I also tried passing -flto-partition=none to
>> avoid summaries.)
>> * I am missing setting something in gimple which I do not know what that
>> could be? (Printing gimple doesn't show all information, but I did try
>> to set everything correctly).
>> * I am failing to communicate this change to other link time opts?
>> (I am changing the definition of this function as opposed to creating
>> a clone and then dropping the previous definition).
>> * Some other thing?
> 
> My first guess would be that you need to re-layout all decls that refer to the
> re-layouted type.
> 
> Richard.
> 
>> Any help would be appreciated!
>> Thanks
>>
>> -Erick

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

end of thread, other threads:[~2020-01-29 19:12 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-28  9:35 Question about changing {machine,type} modes during LTO Erick Ochoa
2020-01-29  9:52 ` Richard Biener
2020-01-30 13:38   ` Erick Ochoa

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).