After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 776028 - gobject declare macros cause alignment warnings on armhf/armhf/mipsel
gobject declare macros cause alignment warnings on armhf/armhf/mipsel
Status: RESOLVED OBSOLETE
Product: glib
Classification: Platform
Component: gobject
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
: 712370 (view as bug list)
Depends on:
Blocks: 775909
 
 
Reported: 2016-12-13 00:22 UTC by Christian Hergert
Modified: 2018-05-24 19:19 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
gobject: Assert that GObjects are at least as aligned as basic types (4.92 KB, patch)
2017-12-08 15:04 UTC, Philip Withnall
reviewed Details | Review
gtype: Eliminate -Wcast-align warnings with G_TYPE_CHECK_INSTANCE_CAST (1.59 KB, patch)
2017-12-08 15:04 UTC, Philip Withnall
reviewed Details | Review
gobject: Add advice on larger alignment requirements for GObject members (2.47 KB, patch)
2017-12-08 15:04 UTC, Philip Withnall
reviewed Details | Review

Description Christian Hergert 2016-12-13 00:22:36 UTC
This is also likely related to bug #712370

If you use the G_DECLARE_FINAL_TYPE() macro, you can have a situation where the header cast causes an alignment warning if after the GObject we have something that is 8 bytes (like a gint64 or gdouble).

This is breaking sysprof builds on armhf/armhf/mipsel on Debian.

See also sysprof bug #775909 for some build logs.

Like bug 712370, we probably need to just add an extra (gpointer)/(void*) cast in the type cast macros to silence the warning and should be safe (since we are guaranteed to get a 2-pointer aligned allocation from GSlice anyway).
Comment 1 Philip Withnall 2017-10-11 10:28:19 UTC
(In reply to Christian Hergert from comment #0)
> (since we are guaranteed to get a 2-pointer aligned allocation from GSlice
> anyway).

Really? The documentation for g_slice_alloc() says that the returned address:
> can be expected to be aligned to at least 1 * sizeof (void*), though in general slices are 2 * sizeof (void*) bytes aligned, if a malloc() fallback implementation is used instead, the alignment may be reduced in a libc dependent fashion.

I haven’t checked the GSlice code, but if that’s true then our alignment might potentially not be correct for objects like this.
Comment 2 Philip Withnall 2017-10-11 10:34:26 UTC
*** Bug 712370 has been marked as a duplicate of this bug. ***
Comment 3 Christian Hergert 2017-10-11 19:55:07 UTC
If it's not 2*sizeof(void*), it should at least be >=sizeof(gdouble) since malloc is guaranteed to return a pointer that can store the basic types.

So we should at least mark the alignment as 8 instead of 4. This will fix situations like:

/* in header */
G_DECLARE_FINAL_TYPE (FooBar, fooBar, FOO, BAR, GObject)

/* in source */
struct _FooBar {
  GObject parent;
  gdouble bar;
};

Currently, on 32-bit there is a strong chance you'll get a warning for this, because the gdouble increases the alignment requirement to 8, but the ref_count field from GObject is only 4 (and the header forward-declaration of the type will not know about the double).
Comment 4 Philip Withnall 2017-10-12 10:03:56 UTC
(In reply to Christian Hergert from comment #3)
> If it's not 2*sizeof(void*), it should at least be >=sizeof(gdouble) since
> malloc is guaranteed to return a pointer that can store the basic types.

Right, but sizeof(double) is undefined in the C standard, iirc. It’s almost always 8 bytes (guaranteed to be so on x86/x86_64 platforms, since they’re guaranteed to use IEEE 754 floating point; but might differ on other platforms).

> So we should at least mark the alignment as 8 instead of 4. This will fix
> situations like:
> 
> /* in header */
> G_DECLARE_FINAL_TYPE (FooBar, fooBar, FOO, BAR, GObject)
> 
> /* in source */
> struct _FooBar {
>   GObject parent;
>   gdouble bar;
> };
> 
> Currently, on 32-bit there is a strong chance you'll get a warning for this,
> because the gdouble increases the alignment requirement to 8, but the
> ref_count field from GObject is only 4 (and the header forward-declaration
> of the type will not know about the double).

Yeah. If we marked the alignment of GObject as 8, that wouldn’t be a significant performance hit anywhere, would it? It might mean an extra 4 bytes of allocation padding on some platforms (but would mean no difference on most platforms). It would easily solve all the problems like this. I’d be in favour of that if there really are no downsides.
Comment 5 Christian Hergert 2017-10-12 19:57:20 UTC
(In reply to Philip Withnall from comment #4)
> Yeah. If we marked the alignment of GObject as 8, that wouldn’t be a
> significant performance hit anywhere, would it? It might mean an extra 4
> bytes of allocation padding on some platforms (but would mean no difference
> on most platforms). It would easily solve all the problems like this. I’d be
> in favour of that if there really are no downsides.

Performance-wise, I'd expect it to only ever be better since you reduce your chances of reading data across alignment boundaries (which could be a SIGBUS on some architectures like SPARC anyway).

GSlice first categorizes the allocation into a few categories:

 1 - allocate from magazine
 2 - allocate from slab
 3 - fallback to malloc

For 3, we don't need to do anything, since we're guaranteed to get something back that can store a double.

For 1 and 2, they use the chunk_size, which is the nearest ^2 from the requested allocation size (gslice.c:1005). Any power of two that is >=8 will have a natural alignment that is safe for double (and thereby, anything containing a double would be safe using natural packing alignments).

Since the fallback for gslice (G_SLICE=always-malloc) will always be aligned for double storage, I believe this is safe to just add an alignment == sizeof(double) (and that might require a configure time check instead of 8, but I doubt it anywhere we run today).
Comment 6 Philip Withnall 2017-10-13 13:04:09 UTC
(In reply to Christian Hergert from comment #5)
> GSlice first categorizes the allocation into a few categories:
> 
>  1 - allocate from magazine
>  2 - allocate from slab
>  3 - fallback to malloc
> 
> For 3, we don't need to do anything, since we're guaranteed to get something
> back that can store a double.

That would give sufficient alignment to fix the original bug here, but it doesn’t strictly translate into a guarantee that everything is at least 8-byte aligned.

Do we want to give a guarantee that everything is 8-byte aligned, or a guarantee that everything is aligned to the largest basic type?
Comment 7 Christian Hergert 2017-10-13 20:13:14 UTC
(In reply to Philip Withnall from comment #6)
> That would give sufficient alignment to fix the original bug here, but it
> doesn’t strictly translate into a guarantee that everything is at least
> 8-byte aligned.

But it does, because if we look at the GObject struct:

  struct  _GObject
  {
    GTypeInstance  g_type_instance;

    /*< private >*/
    volatile guint ref_count;
    GData         *qdata;
  };

On 32-bit systems, that will be 12-bytes, meaning we use the 16-byte slab/magazine in GSlice. So it will always have a natural alignment of at least a 16-byte boundary.

For malloc based allocations, we are guaranteed to have something that can store a long long/uint64_t/etc. So we maintain at least an 8-byte alignment.

> Do we want to give a guarantee that everything is 8-byte aligned, or a
> guarantee that everything is aligned to the largest basic type?

Largest basic type probably.
Comment 8 Christian Hergert 2017-10-13 20:16:52 UTC
(In reply to Christian Hergert from comment #7)
> Largest basic type probably.

As long as that basic type is <= 16 bytes.
Comment 9 Philip Withnall 2017-12-08 15:04:28 UTC
Created attachment 365250 [details] [review]
gobject: Assert that GObjects are at least as aligned as basic types

See the reasoning in the patch for why we believe GObjects *are*
(already) as aligned as the basic types.

We want to make this guarantee so that it’s guaranteed to be safe for
people to ignore -Wcast-align warnings for GObjects which contain basic
types. This typically happens with gdouble on 32-bit ARM platforms.

The checks are slightly complicated by the need to support GObjects with
custom constructors. We should expect that a custom construction
function will chain up to g_object_constructor (which calls
g_type_create_instance() as normal), but it’s possible that someone has
done something crazy and uses a custom allocator which doesn’t return
with the same alignment as GSlice. Hand them a warning in that case. If
that is true, the code which uses their custom-constructed GObject can
presumably already deal with the alignment it gets given.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Comment 10 Philip Withnall 2017-12-08 15:04:35 UTC
Created attachment 365251 [details] [review]
gtype: Eliminate -Wcast-align warnings with G_TYPE_CHECK_INSTANCE_CAST

Regardless of the actual alignment of the GTypeInstance in question,
these do a runtime check on the type, so if the type was originally
aligned correctly when allocated, it should be aligned correctly if the
type check succeeds. -Wcast-align is meant to warn about casts between
types, which this isn’t (if the check succeeds).

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Comment 11 Philip Withnall 2017-12-08 15:04:41 UTC
Created attachment 365252 [details] [review]
gobject: Add advice on larger alignment requirements for GObject members

We now guarantee that GObjects will always be allocated at least as
aligned as the basic types. If you want to put an element in your
GObject which has higher alignment requirements, we can’t guarantee it
will be aligned*. If you need it to be aligned, you’ll need to put it on
the heap (aligned appropriately), or add appropriate padding in your
GObject struct.

*Actually, GSlice will guarantee that the whole GObject is aligned to at
least the power of 2 greater than or equal to the size of the GObject,
which means any element in the GObject struct should always be
appropriate aligned if the compiler pads it appropriately. If malloc()
is used, however, it doesn’t make that guarantee, so we can’t make that
guarantee overall.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Comment 12 Philip Withnall 2017-12-08 15:17:11 UTC
OK, here’s my take on it, in three parts:
 • Add a runtime assert that GObject is at least as aligned as gdouble/guint64.

 • Cast through (void*) in G_TYPE_CHECK_INSTANCE_CAST. Since we’re doing a runtime type check, it doesn’t matter what the alignment actually is: if the object’s been aligned correctly on allocation, any successful cast to it is guaranteed to be aligned. I haven’t made the corresponding change on the optimised version of G_TYPE_CHECK_INSTANCE_CAST which doesn’t do the runtime check, since it doesn’t cast through GTypeInstance*. Maybe I should, for consistency with the first version? I could see there being warnings emitted when building against a non-debug GLib which aren’t emitted with debug GLib then.

 • Document what people are supposed to do if they want a more-aligned member in a GObject struct: allocate the member on the heap, or ensure their struct is appropriately padded.

Writing the last patch made me wonder whether perhaps we should instead be making stronger guarantees about the alignments of GObjects: for example, the GSlice guarantee that they’re aligned to the power of 2 greater than or equal to the struct size; or a guarantee that they’re always as aligned as their member with the largest alignment.

I was also wondering about how to expose this runtime alignment information to the compiler (to tell it that GObject*s are guaranteed to be aligned to at least the alignment of the largest basic type). Would adding an architecture-dependent __attribute__((aligned(x))) to struct GObject do that (where x is calculated at configure time as the maximum alignof() all the basic types)? I can’t test it (no suitable ARM box at the moment), so I haven’t made the change. (On that note, I haven’t tested any of these patches on ARM.)

Christian, what do you think?
Comment 13 Christian Hergert 2017-12-09 06:50:11 UTC
Review of attachment 365250 [details] [review]:

::: gobject/gobject.c
@@ +1664,3 @@
+{
+  return ((((guintptr) (void *) object) %
+             MAX (_g_alignof (gdouble),

I wonder if it makes sense to have this in glib-config.h? I assume this is still completely resolvable at compile time at least.

Also, have you checked to see if the compiler is smart enough to convert this into:

  (objectptr & (maxval-1)) == 0
Comment 14 Christian Hergert 2017-12-09 06:51:49 UTC
Review of attachment 365251 [details] [review]:

Cool
Comment 15 Christian Hergert 2017-12-09 06:53:07 UTC
Review of attachment 365252 [details] [review]:

Cool
Comment 16 Philip Withnall 2017-12-11 11:58:28 UTC
(In reply to Christian Hergert from comment #13)
> Review of attachment 365250 [details] [review] [review]:
> 
> ::: gobject/gobject.c
> @@ +1664,3 @@
> +{
> +  return ((((guintptr) (void *) object) %
> +             MAX (_g_alignof (gdouble),
> 
> I wonder if it makes sense to have this in glib-config.h? I assume this is
> still completely resolvable at compile time at least.

It probably would, though we could do that as a separate follow-up.

> Also, have you checked to see if the compiler is smart enough to convert
> this into:
> 
>   (objectptr & (maxval-1)) == 0

No, but I should do.

Have you thought about the questions in my last two paragraphs in comment #12?
Comment 17 Christian Hergert 2017-12-11 22:30:53 UTC
(In reply to Philip Withnall from comment #12)
> Writing the last patch made me wonder whether perhaps we should instead be
> making stronger guarantees about the alignments of GObjects: for example,
> the GSlice guarantee that they’re aligned to the power of 2 greater than or
> equal to the struct size; or a guarantee that they’re always as aligned as
> their member with the largest alignment.
> 
> I was also wondering about how to expose this runtime alignment information
> to the compiler (to tell it that GObject*s are guaranteed to be aligned to
> at least the alignment of the largest basic type). Would adding an
> architecture-dependent __attribute__((aligned(x))) to struct GObject do that
> (where x is calculated at configure time as the maximum alignof() all the
> basic types)? I can’t test it (no suitable ARM box at the moment), so I
> haven’t made the change. (On that note, I haven’t tested any of these
> patches on ARM.)

Sorry, forgot to respond here.

For attribute alignment, __attribute__((aligned(8)) (or whatever our max aligned type) isn't enough, because that will only work for GCC/Clang/Mingw. We need both an alignment prefix and suffix so we can support MSVC.

G_ALIGNED_BEGIN(8)
typedef struct {
  ...
} GObject
G_ALIGNED_END(8);

MSVC uses a prefix like: __declspec(align(8))

Anyway, here is what we use in libdazzle:

#if defined(_MSC_VER)
# define DZL_ALIGNED_BEGIN(_N) __declspec(align(_N))
# define DZL_ALIGNED_END(_N)
#else
# define DZL_ALIGNED_BEGIN(_N)
# define DZL_ALIGNED_END(_N) __attribute__((aligned(_N)))
#endif
Comment 18 Philip Withnall 2017-12-12 11:34:42 UTC
(In reply to Philip Withnall from comment #12)
> Writing the last patch made me wonder whether perhaps we should instead be
> making stronger guarantees about the alignments of GObjects: for example,
> the GSlice guarantee that they’re aligned to the power of 2 greater than or
> equal to the struct size; or a guarantee that they’re always as aligned as
> their member with the largest alignment.

I’d also like Colin’s and Matthias’ opinions on this.
Comment 19 Philip Withnall 2018-02-12 14:47:08 UTC
Colin? Matthias?
Comment 20 GNOME Infrastructure Team 2018-05-24 19:19:55 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/glib/issues/1231.