compatibility of structs/unions/enums in the middle end

classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

compatibility of structs/unions/enums in the middle end

Uecker, Martin


Hi,

I have a proposal for making changes to the rules for
compatibility of tagged types in C2X  (N2366). This was
received with interest by WG14, so there is a chance
that this could get accepted into C2X.

In particular, the idea is to make structs (+ unions, enums)
with the same tag and the same members compatible. The
current C standards says that such structs are compatible
between different TUs but not inside the same TU, which
is very strange and - as pointed out by Joseph
in DR314 - this leads to "interesting" scenarios
where types across different TU cannot be partitioned
into equivalence classes in a consistent way.

The new rules would fix these inconsistencies and also
make some useful programming patterns possible: E.g. one
could declare structs/union/enums types in a macro so
that another invocation produces a compatible type.
For example:

#define MAYBE(T) struct foo_##T { _Bool flag; T value }; 

MAYBE(int) x = { true, 0 };
MAYBE(int) y = x;


I am working on a patch for GCC which adds this as an
optional feature. So far, I have a working patch to the
C front end which changes the concept of type compatibility
to match the proposed model. It uses the existing code
for type compatibility, so is relatively simple.

The question is now how this should interact with the
middle end. So far, I have to insert some VIEW_CONVERT_EXPR
to avoid "useless type conversion" errors during gimple
verification. 

I am also wonder how to make TBAA do the right thing
for the new rules. Currently, GCC assumes 's1p' and 's2p'
cannot alias in the following example and outputs '2'
in 'f', but this would not be true anymore according
to the proposal. 


#include <stdio.h>

typedef struct { int i; } st1;
typedef struct { int i; } st2;

void f(void* s1v, void* s2v)
{
  st1 *s1p = s1v;
  st2 *s2p = s2v;
  s1p->i = 2;
  s2p->i = 3;
  printf("f: s1p->i = %i\n", s1p->i);
}

int main()
{
  st1 s = { .i = 1 };
  f(&s, &s);
  printf("s.i = %i\n", s.i);
}

BTW: According to current rules when 'f' is
moved into a different TU, there is no UB.
As both 'st1'
and 'st2' in one TU are compatible
to both 'st1' and 'st2' in the other TU there
is no UB. Still, GCC
incorrectly assumes that
's1p' and 's1p' do not alias.


I would appreciate any information about how to
approach this.

Best,
Martin

Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Tue, Oct 1, 2019 at 7:49 PM Uecker, Martin
<[hidden email]> wrote:

>
>
>
> Hi,
>
> I have a proposal for making changes to the rules for
> compatibility of tagged types in C2X  (N2366). This was
> received with interest by WG14, so there is a chance
> that this could get accepted into C2X.
>
> In particular, the idea is to make structs (+ unions, enums)
> with the same tag and the same members compatible. The
> current C standards says that such structs are compatible
> between different TUs but not inside the same TU, which
> is very strange and - as pointed out by Joseph
> in DR314 - this leads to "interesting" scenarios
> where types across different TU cannot be partitioned
> into equivalence classes in a consistent way.
>
> The new rules would fix these inconsistencies and also
> make some useful programming patterns possible: E.g. one
> could declare structs/union/enums types in a macro so
> that another invocation produces a compatible type.
> For example:
>
> #define MAYBE(T) struct foo_##T { _Bool flag; T value };
>
> MAYBE(int) x = { true, 0 };
> MAYBE(int) y = x;
>
>
> I am working on a patch for GCC which adds this as an
> optional feature. So far, I have a working patch to the
> C front end which changes the concept of type compatibility
> to match the proposed model. It uses the existing code
> for type compatibility, so is relatively simple.
>
> The question is now how this should interact with the
> middle end. So far, I have to insert some VIEW_CONVERT_EXPR
> to avoid "useless type conversion" errors during gimple
> verification.
>
> I am also wonder how to make TBAA do the right thing
> for the new rules. Currently, GCC assumes 's1p' and 's2p'
> cannot alias in the following example and outputs '2'
> in 'f', but this would not be true anymore according
> to the proposal.
>
>
> #include <stdio.h>
>
> typedef struct { int i; } st1;
> typedef struct { int i; } st2;
>
> void f(void* s1v, void* s2v)
> {
>   st1 *s1p = s1v;
>   st2 *s2p = s2v;
>   s1p->i = 2;
>   s2p->i = 3;
>   printf("f: s1p->i = %i\n", s1p->i);
> }
>
> int main()
> {
>   st1 s = { .i = 1 };
>   f(&s, &s);
>   printf("s.i = %i\n", s.i);
> }
>
> BTW: According to current rules when 'f' is
> moved into a different TU, there is no UB.
> As both 'st1'
> and 'st2' in one TU are compatible
> to both 'st1' and 'st2' in the other TU there
> is no UB. Still, GCC
> incorrectly assumes that
> 's1p' and 's1p' do not alias.
>
>
> I would appreciate any information about how to
> approach this.

The frontend either needs to have the same internal
type representation for both or provide the middle-end
with unification of compatible types via the TYPE_CANONICAL
mechanism (that's what the C++ FE does in similar circumstances).

That is, the TBAA machinery relies on TYPE_CANONICAL (TYPE_MAIN_VARIANT (st1))
== TYPE_CANONICAL (TYPE_MAIN_VARIANT (st2))
(or requivalent TYPE_MAIN_VARIANT if that's already the case).

Richard.

>
> Best,
> Martin
>
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Wed, Oct 2, 2019 at 12:46 PM Richard Biener
<[hidden email]> wrote:

>
> On Tue, Oct 1, 2019 at 7:49 PM Uecker, Martin
> <[hidden email]> wrote:
> >
> >
> >
> > Hi,
> >
> > I have a proposal for making changes to the rules for
> > compatibility of tagged types in C2X  (N2366). This was
> > received with interest by WG14, so there is a chance
> > that this could get accepted into C2X.
> >
> > In particular, the idea is to make structs (+ unions, enums)
> > with the same tag and the same members compatible. The
> > current C standards says that such structs are compatible
> > between different TUs but not inside the same TU, which
> > is very strange and - as pointed out by Joseph
> > in DR314 - this leads to "interesting" scenarios
> > where types across different TU cannot be partitioned
> > into equivalence classes in a consistent way.
> >
> > The new rules would fix these inconsistencies and also
> > make some useful programming patterns possible: E.g. one
> > could declare structs/union/enums types in a macro so
> > that another invocation produces a compatible type.
> > For example:
> >
> > #define MAYBE(T) struct foo_##T { _Bool flag; T value };
> >
> > MAYBE(int) x = { true, 0 };
> > MAYBE(int) y = x;
> >
> >
> > I am working on a patch for GCC which adds this as an
> > optional feature. So far, I have a working patch to the
> > C front end which changes the concept of type compatibility
> > to match the proposed model. It uses the existing code
> > for type compatibility, so is relatively simple.
> >
> > The question is now how this should interact with the
> > middle end. So far, I have to insert some VIEW_CONVERT_EXPR
> > to avoid "useless type conversion" errors during gimple
> > verification.
> >
> > I am also wonder how to make TBAA do the right thing
> > for the new rules. Currently, GCC assumes 's1p' and 's2p'
> > cannot alias in the following example and outputs '2'
> > in 'f', but this would not be true anymore according
> > to the proposal.
> >
> >
> > #include <stdio.h>
> >
> > typedef struct { int i; } st1;
> > typedef struct { int i; } st2;
> >
> > void f(void* s1v, void* s2v)
> > {
> >   st1 *s1p = s1v;
> >   st2 *s2p = s2v;
> >   s1p->i = 2;
> >   s2p->i = 3;
> >   printf("f: s1p->i = %i\n", s1p->i);
> > }
> >
> > int main()
> > {
> >   st1 s = { .i = 1 };
> >   f(&s, &s);
> >   printf("s.i = %i\n", s.i);
> > }
> >
> > BTW: According to current rules when 'f' is
> > moved into a different TU, there is no UB.
> > As both 'st1'
> > and 'st2' in one TU are compatible
> > to both 'st1' and 'st2' in the other TU there
> > is no UB. Still, GCC
> > incorrectly assumes that
> > 's1p' and 's1p' do not alias.
> >
> >
> > I would appreciate any information about how to
> > approach this.
>
> The frontend either needs to have the same internal
> type representation for both or provide the middle-end
> with unification of compatible types via the TYPE_CANONICAL
> mechanism (that's what the C++ FE does in similar circumstances).
>
> That is, the TBAA machinery relies on TYPE_CANONICAL (TYPE_MAIN_VARIANT (st1))
> == TYPE_CANONICAL (TYPE_MAIN_VARIANT (st2))
> (or requivalent TYPE_MAIN_VARIANT if that's already the case).

Btw, for you example, how do you expect debug information to look like?
Would there be two type definitions that are not related?

Richard.

> Richard.
>
> >
> > Best,
> > Martin
> >
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Mittwoch, den 02.10.2019, 12:47 +0200 schrieb Richard Biener:
> On Wed, Oct 2, 2019 at 12:46 PM Richard Biener
> <[hidden email]> wrote:
> >
> > On Tue, Oct 1, 2019 at 7:49 PM Uecker, Martin
> > <[hidden email]> wrote:

...
> > >
> > > In particular, the idea is to make structs (+ unions, enums)
> > > with the same tag and the same members compatible. The
> > > current C standards says that such structs are compatible
> > > between different TUs but not inside the same TU, which
> > > is very strange and - as pointed out by Joseph
> > > in DR314 - this leads to "interesting" scenarios
> > > where types across different TU cannot be partitioned
> > > into equivalence classes in a consistent way.
...

> > > I would appreciate any information about how to
> > > approach this.
> >
> > The frontend either needs to have the same internal
> > type representation for both or provide the middle-end
> > with unification of compatible types via the TYPE_CANONICAL
> > mechanism (that's what the C++ FE does in similar circumstances).
> >
> > That is, the TBAA machinery relies on TYPE_CANONICAL (TYPE_MAIN_VARIANT (st1))
> > == TYPE_CANONICAL (TYPE_MAIN_VARIANT (st2))
> > (or requivalent TYPE_MAIN_VARIANT if that's already the case).

Yes, this is what I assumed from looking at the code. The problem
is that the front end would need to go over all types and set
TYPE_CANONICAL. 

This seems easy to do on the fly whenever the front
end needs to compare types anyway, but this would not be enough
as also types which appear unrelated to the front end (e.g. two
types declared in separate local scopes) could be compatible.
To identify these types would require searching a data structure
of all such types in the front end every time a new tagged type
is created. This would not be too difficult to implement.

On the other hand, the situation with this propsoal for such types
is then very similar to any other complex type expressions which
need to compared structurally in the middle end. So what I am
wondering is whether it would be possible to do such comparisons
in the middle end also for tagged types?

Finally, how does LTO does it? It somehow also needs to unify
different tagged types? Could we reuse this mechanism somehow?

> Btw, for you example, how do you expect debug information to look like?
> Would there be two type definitions that are not related?

I don't know yet. This is why I am trying to implement it, to
figure out all these practical issues. How does it work now for
tagged types in different TUs that are compatible?

Best,
Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
<[hidden email]> wrote:

>
> Am Mittwoch, den 02.10.2019, 12:47 +0200 schrieb Richard Biener:
> > On Wed, Oct 2, 2019 at 12:46 PM Richard Biener
> > <[hidden email]> wrote:
> > >
> > > On Tue, Oct 1, 2019 at 7:49 PM Uecker, Martin
> > > <[hidden email]> wrote:
>
> ...
> > > >
> > > > In particular, the idea is to make structs (+ unions, enums)
> > > > with the same tag and the same members compatible. The
> > > > current C standards says that such structs are compatible
> > > > between different TUs but not inside the same TU, which
> > > > is very strange and - as pointed out by Joseph
> > > > in DR314 - this leads to "interesting" scenarios
> > > > where types across different TU cannot be partitioned
> > > > into equivalence classes in a consistent way.
> ...
>
> > > > I would appreciate any information about how to
> > > > approach this.
> > >
> > > The frontend either needs to have the same internal
> > > type representation for both or provide the middle-end
> > > with unification of compatible types via the TYPE_CANONICAL
> > > mechanism (that's what the C++ FE does in similar circumstances).
> > >
> > > That is, the TBAA machinery relies on TYPE_CANONICAL (TYPE_MAIN_VARIANT (st1))
> > > == TYPE_CANONICAL (TYPE_MAIN_VARIANT (st2))
> > > (or requivalent TYPE_MAIN_VARIANT if that's already the case).
>
> Yes, this is what I assumed from looking at the code. The problem
> is that the front end would need to go over all types and set
> TYPE_CANONICAL.

Yes.

> This seems easy to do on the fly whenever the front
> end needs to compare types anyway, but this would not be enough
> as also types which appear unrelated to the front end (e.g. two
> types declared in separate local scopes) could be compatible.
> To identify these types would require searching a data structure
> of all such types in the front end every time a new tagged type
> is created. This would not be too difficult to implement.
>
> On the other hand, the situation with this propsoal for such types
> is then very similar to any other complex type expressions which
> need to compared structurally in the middle end. So what I am
> wondering is whether it would be possible to do such comparisons
> in the middle end also for tagged types?

The middle-end ensures there's only one such type via hashing
types via type_hash_canon.

> Finally, how does LTO does it? It somehow also needs to unify
> different tagged types? Could we reuse this mechanism somehow?

LTO structurally merges types via TYPE_CANONICAL.  But rules
for merging depend on language semantics, too much merging
hinders optimization.

> > Btw, for you example, how do you expect debug information to look like?
> > Would there be two type definitions that are not related?
>
> I don't know yet. This is why I am trying to implement it, to
> figure out all these practical issues. How does it work now for
> tagged types in different TUs that are compatible?

You get two copies.

Richard.

> Best,
> Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Mittwoch, den 02.10.2019, 14:18 +0200 schrieb Richard Biener:
> On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
> <[hidden email]> wrote:
> >

Thank you for your answers.

> > Finally, how does LTO does it? It somehow also needs to unify
> > different tagged types? Could we reuse this mechanism somehow?
>
> LTO structurally merges types via TYPE_CANONICAL.  But rules
> for merging depend on language semantics, too much merging
> hinders optimization.

LTO would need to merge types with identical tag and structure
across TUs anyway as this is needed for C programs to work.
But this implies that it also must merge such types inside a TU
(because merging enforces an equivalence relationship).
So if I am not missing anything important, LTO would already
implement the exact semantics which I propose for C2X.

Best,
Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Wed, Oct 2, 2019 at 2:35 PM Uecker, Martin
<[hidden email]> wrote:

>
> Am Mittwoch, den 02.10.2019, 14:18 +0200 schrieb Richard Biener:
> > On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
> > <[hidden email]> wrote:
> > >
>
> Thank you for your answers.
>
> > > Finally, how does LTO does it? It somehow also needs to unify
> > > different tagged types? Could we reuse this mechanism somehow?
> >
> > LTO structurally merges types via TYPE_CANONICAL.  But rules
> > for merging depend on language semantics, too much merging
> > hinders optimization.
>
> LTO would need to merge types with identical tag and structure
> across TUs anyway as this is needed for C programs to work.
> But this implies that it also must merge such types inside a TU
> (because merging enforces an equivalence relationship).
> So if I am not missing anything important, LTO would already
> implement the exact semantics which I propose for C2X.

Sure LTO handles C2X fine.  The issue is that it creates way
larger equivalence classes than necessary for C2X (it has to
work across language boundaries where compatibility is much
less specified).

Richard.

> Best,
> Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Wed, Oct 2, 2019 at 3:10 PM Richard Biener
<[hidden email]> wrote:

>
> On Wed, Oct 2, 2019 at 2:35 PM Uecker, Martin
> <[hidden email]> wrote:
> >
> > Am Mittwoch, den 02.10.2019, 14:18 +0200 schrieb Richard Biener:
> > > On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
> > > <[hidden email]> wrote:
> > > >
> >
> > Thank you for your answers.
> >
> > > > Finally, how does LTO does it? It somehow also needs to unify
> > > > different tagged types? Could we reuse this mechanism somehow?
> > >
> > > LTO structurally merges types via TYPE_CANONICAL.  But rules
> > > for merging depend on language semantics, too much merging
> > > hinders optimization.
> >
> > LTO would need to merge types with identical tag and structure
> > across TUs anyway as this is needed for C programs to work.
> > But this implies that it also must merge such types inside a TU
> > (because merging enforces an equivalence relationship).
> > So if I am not missing anything important, LTO would already
> > implement the exact semantics which I propose for C2X.
>
> Sure LTO handles C2X fine.  The issue is that it creates way
> larger equivalence classes than necessary for C2X (it has to
> work across language boundaries where compatibility is much
> less specified).

Oh, and LTO does _not_ merge types declared inside a function,
so

void foo () { struct S { int i; }; }
void bar () { struct S { int i; }; }

the two S are distinct and objects of that type do not conflict.

Richard.

> Richard.
>
> > Best,
> > Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Mittwoch, den 02.10.2019, 15:12 +0200 schrieb Richard Biener:

> On Wed, Oct 2, 2019 at 3:10 PM Richard Biener
> <[hidden email]> wrote:
> >
> > On Wed, Oct 2, 2019 at 2:35 PM Uecker, Martin
> > <[hidden email]> wrote:
> > >
> > > Am Mittwoch, den 02.10.2019, 14:18 +0200 schrieb Richard Biener:
> > > > On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
> > > > <[hidden email]> wrote:
> > > > >
> > >
> > > Thank you for your answers.
> > >
> > > > > Finally, how does LTO does it? It somehow also needs to unify
> > > > > different tagged types? Could we reuse this mechanism somehow?
> > > >
> > > > LTO structurally merges types via TYPE_CANONICAL.  But rules
> > > > for merging depend on language semantics, too much merging
> > > > hinders optimization.
> > >
> > > LTO would need to merge types with identical tag and structure
> > > across TUs anyway as this is needed for C programs to work.
> > > But this implies that it also must merge such types inside a TU
> > > (because merging enforces an equivalence relationship).
> > > So if I am not missing anything important, LTO would already
> > > implement the exact semantics which I propose for C2X.
> >
> > Sure LTO handles C2X fine.  The issue is that it creates way
> > larger equivalence classes than necessary for C2X (it has to
> > work across language boundaries where compatibility is much
> > less specified).

Ok, using this would also for our purposes would pessimize things
too much.

> Oh, and LTO does _not_ merge types declared inside a function,
> so
>
> void foo () { struct S { int i; }; }
> void bar () { struct S { int i; }; }
>
> the two S are distinct and objects of that type do not conflict.

This is surprising as these types are compatible across TUs. So
if some pointer is passed between these functions this is
supposed to work.

Best,
Martin

Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On October 2, 2019 3:55:43 PM GMT+02:00, "Uecker, Martin" <[hidden email]> wrote:

>Am Mittwoch, den 02.10.2019, 15:12 +0200 schrieb Richard Biener:
>> On Wed, Oct 2, 2019 at 3:10 PM Richard Biener
>> <[hidden email]> wrote:
>> >
>> > On Wed, Oct 2, 2019 at 2:35 PM Uecker, Martin
>> > <[hidden email]> wrote:
>> > >
>> > > Am Mittwoch, den 02.10.2019, 14:18 +0200 schrieb Richard Biener:
>> > > > On Wed, Oct 2, 2019 at 1:57 PM Uecker, Martin
>> > > > <[hidden email]> wrote:
>> > > > >
>> > >
>> > > Thank you for your answers.
>> > >
>> > > > > Finally, how does LTO does it? It somehow also needs to unify
>> > > > > different tagged types? Could we reuse this mechanism
>somehow?
>> > > >
>> > > > LTO structurally merges types via TYPE_CANONICAL.  But rules
>> > > > for merging depend on language semantics, too much merging
>> > > > hinders optimization.
>> > >
>> > > LTO would need to merge types with identical tag and structure
>> > > across TUs anyway as this is needed for C programs to work.
>> > > But this implies that it also must merge such types inside a TU
>> > > (because merging enforces an equivalence relationship).
>> > > So if I am not missing anything important, LTO would already
>> > > implement the exact semantics which I propose for C2X.
>> >
>> > Sure LTO handles C2X fine.  The issue is that it creates way
>> > larger equivalence classes than necessary for C2X (it has to
>> > work across language boundaries where compatibility is much
>> > less specified).
>
>Ok, using this would also for our purposes would pessimize things
>too much.
>
>> Oh, and LTO does _not_ merge types declared inside a function,
>> so
>>
>> void foo () { struct S { int i; }; }
>> void bar () { struct S { int i; }; }
>>
>> the two S are distinct and objects of that type do not conflict.
>
>This is surprising as these types are compatible across TUs. So
>if some pointer is passed between these functions this is
>supposed to work.

So if they are compatible the frontend needs to mark them so in this case.

Richard.

>Best,
>Martin

Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Mittwoch, den 02.10.2019, 17:37 +0200 schrieb Richard Biener:
> On October 2, 2019 3:55:43 PM GMT+02:00, "Uecker, Martin" <[hidden email]>
> wrote:
> > Am Mittwoch, den 02.10.2019, 15:12 +0200 schrieb Richard Biener:
> > > On Wed, Oct 2, 2019 at 3:10 PM Richard Biener
> > > <[hidden email]> wrote:
> > > >
...

> > > Oh, and LTO does _not_ merge types declared inside a function,
> > > so
> > >
> > > void foo () { struct S { int i; }; }
> > > void bar () { struct S { int i; }; }
> > >
> > > the two S are distinct and objects of that type do not conflict.
> >
> > This is surprising as these types are compatible across TUs. So
> > if some pointer is passed between these functions this is
> > supposed to work.
>
> So if they are compatible the frontend needs to mark them so in this case. 

It can't. The front end never sees the other TU.

Best,
Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Wed, Oct 2, 2019 at 8:24 PM Uecker, Martin
<[hidden email]> wrote:

>
> Am Mittwoch, den 02.10.2019, 17:37 +0200 schrieb Richard Biener:
> > On October 2, 2019 3:55:43 PM GMT+02:00, "Uecker, Martin" <[hidden email]>
> > wrote:
> > > Am Mittwoch, den 02.10.2019, 15:12 +0200 schrieb Richard Biener:
> > > > On Wed, Oct 2, 2019 at 3:10 PM Richard Biener
> > > > <[hidden email]> wrote:
> > > > >
> ...
>
> > > > Oh, and LTO does _not_ merge types declared inside a function,
> > > > so
> > > >
> > > > void foo () { struct S { int i; }; }
> > > > void bar () { struct S { int i; }; }
> > > >
> > > > the two S are distinct and objects of that type do not conflict.
> > >
> > > This is surprising as these types are compatible across TUs. So
> > > if some pointer is passed between these functions this is
> > > supposed to work.
> >
> > So if they are compatible the frontend needs to mark them so in this case.
>
> It can't. The front end never sees the other TU.

If the type "leaves" the CU via a call the called function has a prototype
through which it "sees" the CU.

Richard.

> Best,
> Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Freitag, den 04.10.2019, 12:29 +0200 schrieb Richard Biener:
> On Wed, Oct 2, 2019 at 8:24 PM Uecker, Martin
> <[hidden email]> wrote:
> >
> > Am Mittwoch, den 02.10.2019, 17:37 +0200 schrieb Richard Biener:

> >
> > ...
> >
> > > > > Oh, and LTO does _not_ merge types declared inside a function,
> > > > > so
> > > > >
> > > > > void foo () { struct S { int i; }; }
> > > > > void bar () { struct S { int i; }; }
> > > > >
> > > > > the two S are distinct and objects of that type do not conflict.
> > > >
> > > > This is surprising as these types are compatible across TUs. So
> > > > if some pointer is passed between these functions this is
> > > > supposed to work.
> > >
> > > So if they are compatible the frontend needs to mark them so in this case.
> >
> > It can't. The front end never sees the other TU.
>
> If the type "leaves" the CU via a call the called function has a prototype
> through which it "sees" the CU.

The prototype could be local to the function or it could be a void*
(or other pointer type) argument.



TU1-----------------------
#include <stdio.h>

extern void f(void *p);

int main()
{
        struct foo { int x; } b;
        b.x = 2;
        f(&b);
        printf("%d\n", b.x);
}

TU2----------------------------- 
extern void f(void *p)
{
        struct foo { int x; } *q = p;
        q->x = 3;
}

Best,
Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Richard Biener-2
On Fri, Oct 4, 2019 at 1:55 PM Uecker, Martin
<[hidden email]> wrote:

>
> Am Freitag, den 04.10.2019, 12:29 +0200 schrieb Richard Biener:
> > On Wed, Oct 2, 2019 at 8:24 PM Uecker, Martin
> > <[hidden email]> wrote:
> > >
> > > Am Mittwoch, den 02.10.2019, 17:37 +0200 schrieb Richard Biener:
>
> > >
> > > ...
> > >
> > > > > > Oh, and LTO does _not_ merge types declared inside a function,
> > > > > > so
> > > > > >
> > > > > > void foo () { struct S { int i; }; }
> > > > > > void bar () { struct S { int i; }; }
> > > > > >
> > > > > > the two S are distinct and objects of that type do not conflict.
> > > > >
> > > > > This is surprising as these types are compatible across TUs. So
> > > > > if some pointer is passed between these functions this is
> > > > > supposed to work.
> > > >
> > > > So if they are compatible the frontend needs to mark them so in this case.
> > >
> > > It can't. The front end never sees the other TU.
> >
> > If the type "leaves" the CU via a call the called function has a prototype
> > through which it "sees" the CU.
>
> The prototype could be local to the function or it could be a void*
> (or other pointer type) argument.
>
>
>
> TU1-----------------------
> #include <stdio.h>
>
> extern void f(void *p);
>
> int main()
> {
>         struct foo { int x; } b;
>         b.x = 2;
>         f(&b);
>         printf("%d\n", b.x);
> }
>
> TU2-----------------------------
> extern void f(void *p)
> {
>         struct foo { int x; } *q = p;
>         q->x = 3;
> }

If the frontend puts those structures at local scope
then yes, the above presents a problem to LTO
at the moment.  So, trigger some inlining,
make main() read b.x after f(&b) and assert that
it is 3.  I think that would fail at the moment.

Richard.

> Best,
> Martin
Reply | Threaded
Open this post in threaded view
|

Re: compatibility of structs/unions/enums in the middle end

Uecker, Martin
Am Freitag, den 04.10.2019, 14:28 +0200 schrieb Richard Biener:

> On Fri, Oct 4, 2019 at 1:55 PM Uecker, Martin
> <[hidden email]> wrote:
> >
> > Am Freitag, den 04.10.2019, 12:29 +0200 schrieb Richard Biener:
> > > On Wed, Oct 2, 2019 at 8:24 PM Uecker, Martin
> > > <[hidden email]> wrote:
> > > >
> > > > Am Mittwoch, den 02.10.2019, 17:37 +0200 schrieb Richard Biener:
> > > >
> > > > ...
> > > >
> > > > > > > Oh, and LTO does _not_ merge types declared inside a function,
> > > > > > > so
> > > > > > >
> > > > > > > void foo () { struct S { int i; }; }
> > > > > > > void bar () { struct S { int i; }; }
> > > > > > >
> > > > > > > the two S are distinct and objects of that type do not conflict.
> > > > > >
> > > > > > This is surprising as these types are compatible across TUs. So
> > > > > > if some pointer is passed between these functions this is
> > > > > > supposed to work.
> > > > >
> > > > > So if they are compatible the frontend needs to mark them so in this case.
> > > >
> > > > It can't. The front end never sees the other TU.
> > >
> > > If the type "leaves" the CU via a call the called function has a prototype
> > > through which it "sees" the CU.
> >
> > The prototype could be local to the function or it could be a void*
> > (or other pointer type) argument.
> >
> >
> >
> > TU1-----------------------
> > #include <stdio.h>
> >
> > extern void f(void *p);
> >
> > int main()
> > {
> >         struct foo { int x; } b;
> >         b.x = 2;
> >         f(&b);
> >         printf("%d\n", b.x);
> > }
> >
> > TU2-----------------------------
> > extern void f(void *p)
> > {
> >         struct foo { int x; } *q = p;
> >         q->x = 3;
> > }
>
> If the frontend puts those structures at local scope
> then yes, the above presents a problem to LTO
> at the moment.  So, trigger some inlining,
> make main() read b.x after f(&b) and assert that
> it is 3.  I think that would fail at the moment.

I expected it to fail, but it seems to work (returns 3).
Maybe by chance.

The example which doesn't work is the one I gave
at the beginning in the thread (even without lto).

Best,
Martin