On Wed, Jan 27, 2016 at 1:03 AM, Marc Glisse <marc.gli...@inria.fr> wrote: > On Wed, 27 Jan 2016, Jakub Jelinek wrote: > >> On Wed, Jan 27, 2016 at 09:10:14AM +0100, Marc Glisse wrote: >>>> >>>> Revised: >>>> >>>> /* Returns true if TYPE is POD of one-byte or less in size for the >>>> purpose >>>> of layout and an empty class or an class with empty classes. */ >>>> >>>> static bool >>>> is_empty_record (tree type) >>>> { >>>> if (type == error_mark_node) >>>> return false; >>>> >>>> if (!CLASS_TYPE_P (type)) >>>> return false; >>>> >>>> if (CLASSTYPE_NON_LAYOUT_POD_P (type)) >>>> return false; >>>> >>>> gcc_assert (COMPLETE_TYPE_P (type)); >>>> >>>> if (CLASSTYPE_EMPTY_P (type)) >>>> return true; >>>> >>>> if (int_size_in_bytes (type) > 1) >>>> return false; >>> >>> >>> That's completely arbitrary :-( >> >> >> Yeah. Because (adapted to be compilable with C): >> struct A1 {}; struct A2 {}; >> struct B1 { struct A1 a; struct A2 b; }; struct B2 { struct A1 a; struct >> A2 b; }; >> struct C1 { struct B1 a; struct B2 b; }; struct C2 { struct B1 a; struct >> B2 b; }; >> struct D1 { struct C1 a; struct C2 b; }; struct D2 { struct C1 a; struct >> C2 b; }; >> struct E1 { struct D1 a; struct D2 b; }; struct E2 { struct D1 a; struct >> D2 b; }; >> struct F1 { struct E1 a; struct E2 b; }; struct F2 { struct E1 a; struct >> E2 b; }; >> struct G1 { struct F1 a; struct F2 b; }; struct G2 { struct F1 a; struct >> F2 b; }; >> struct H1 { struct G1 a; struct G2 b; }; struct H2 { struct G1 a; struct >> G2 b; }; >> struct I1 { struct H1 a; struct H2 b; }; struct I2 { struct H1 a; struct >> H2 b; }; >> struct J1 { struct I1 a; struct I2 b; }; struct J2 { struct I1 a; struct >> I2 b; }; >> struct K1 { struct J1 a; struct J2 b; }; >> int v; >> __attribute__((noinline, noclone)) >> struct K1 foo (int a, struct K1 x, int b) >> { >> v = a + b; >> return x; >> } >> struct K1 k, m; >> void >> bar (void) >> { >> m = foo (1, k, 2); >> } >> then would have a different calling convention between C and C++, >> so where is the argument that we change anything just to make the two >> compatible? Though, of course, those two will never be compatible, >> it is enough to add struct L1 { int a; struct K1 b; int c; }; and >> that structure has 1024+8 bytes in C++ and 8 bytes in C. > > > I don't know how empty classes are used in C in practice, but it could make > sense to have ABI compatibility as long as no empty struct is used as a > member of another struct (I also suggested an attribute to let C++ use the > same layout as C here: PR63579). But then the usual definition of empty > would be sufficient. > >> As clang generates different code for the above between C and C++, it >> clearly special cases for some reason just the most common case. >> IMHO it is not worth to change GCC ABI... > > > I was interested in this change because it improves C++, C compatibility was > a convenient excuse ;-) >
I opened a clang bug: https://llvm.org/bugs/show_bug.cgi?id=26337 I propose the following definitions: i. An empty record is: 1. A class without member. Or 2. An array of empty records. Or 3. A class with empty records. ii. An empty record type for parameter passing is POD for the purpose of layout and 1. A class without member. Or 2. A class with empty records. /* An empty record is: 1. A class without member. Or 2. An array of empty records. Or 3. A class with empty records. */ /* Returns true if TYPE is an empty record or an array of empty records. */ static bool is_empty_record_or_array_of_empty_record (tree type) { if (CLASS_TYPE_P (type)) { if (CLASSTYPE_EMPTY_P (type)) return true; tree field; for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field) && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field))) return false; return true; } else if (TREE_CODE (type) == ARRAY_TYPE) return is_empty_record_or_array_of_empty_record (TREE_TYPE (type)); return false; } /* Returns true if TYPE is POD for the purpose of layout and 1. A class without member. Or 2. A class with empty records. */ static bool is_empty_record_for_parm (tree type) { if (type == error_mark_node) return false; if (!CLASS_TYPE_P (type)) return false; if (CLASSTYPE_NON_LAYOUT_POD_P (type)) return false; gcc_assert (COMPLETE_TYPE_P (type)); if (CLASSTYPE_EMPTY_P (type)) return true; tree field; for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field) && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field))) return false; return true; } -- H.J.