Print Story C99 strict aliasing rears it's ugly head again in the gtk+ codebase
Diary
By ta bu shi da yu (Sun Apr 25, 2004 at 08:07:12 AM EST) (all tags)
Finally got around to generating patches for a large number of warnings I was getting when I compiled gtk+ 2.4.0.


Seems that strict aliasing is done by gcc to comply with the C99 standard:


6.5: Expressions

7. An object shall have it's stored value accessed only by an lvalue expression
that has one of the following types 61):

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

61) the intent of this list is to specify those circumstances in which an object may not be aliased.

It looks like the way to fix these issues are with temporary variables. I explained this better in a post to desktop-devel-list and gnome-bugsquad:

Subject: Deferencing type-punned pointers, and how to stop gcc giving you a warning
Date: Sun, 25 Apr 2004 19:04:34 +1000
From: xxx (find me on the lists!) <deepcover@nospam.mpx.com.au>
Newsgroups: gmane.comp.gnome.desktop

Hi all,

I've been looking at a few warnings that gcc have been spitting out at
me lately about "Derefencing type-punned pointer will break
strict-aliasing rules".

Here's what the gcc manual has to say about it:

> -fstrict-aliasing Allows the compiler to assume the strictest
> aliasing rules applicable to the language being compiled.  For C (and
> C++), this activates optimizations based on the type of expressions.
> In particular, an object of one type is assumed never to reside at
> the same address as an object of a different type, unless the types
> are almost the same.  For example, an "unsigned int" can alias an
> "int", but not a "void*" or a "double".  A character type may alias
> any other type.
>
> Pay special attention to code like this:
>
> union a_union { int i; double d; };
>
> int f() { a_union t; t.d = 3.0; return t.i; }
>
> The practice of reading from a different union member than the one
> most recently written to (called ``type-punning'') is common.  Even
> with -fstrict-aliasing, type-punning is allowed, provided the memory
> is accessed through the union type.  So, the code above will work as
> expected.  However, this code might not:
>
> int f() { a_union t; int* ip; t.d = 3.0; ip = &t.i; return *ip; }
>
> Every language that wishes to perform language-specific alias
> analysis should define a function that computes, given an "tree"
> node, an alias set for the node.  Nodes in different alias sets are
> not allowed to alias.  For an example, see the C front-end function
> "c_get_alias_set".
>
> Enabled at levels -O2, -O3, -Os."
>

and

> -Wstrict-aliasing This option is only active when -fstrict-aliasing
> is active.  It warns about code which might break the strict aliasing
> rules that the compiler is using for optimization. The warning does
> not catch all cases, but does attempt to catch the more common
> pitfalls. It is included in -Wall.

When I compiled gtk+2.4 (and gtk+2.2) I found that I was getting quite a
few of these warnings. During my discovery of the problem - see
http://bugzilla.gnome.org/show_bug.cgi?id=140722 - I've found some
general rules to try to prevent these warnings:

* if you use g_module_symbol, and you need to use a void* function
pointer with multiple parameters to get the module's symbol pointer,
then *also* declare a gpointer pfunc_name, and declare the function
pointer itself to be "void (func_name) (int x, int y)=NULL;" Then, call
g_module_symbol and pass the third parameter as &pfunc_name. As soon as
it's been called, then assign func_name the address of pfunc_name.

Example (from gdk-pixbuf/queryloaders.c):

static void
query_module (const char *dir, const char *file)
{
        char *path;
        GModule *module;

        // needed or gcc will give a warning that dereferencing
        // type-punned pointer will break strict-aliasing rules
        gpointer pfill_info;
        gpointer pfill_vtable;

        void   (*fill_info)     (GdkPixbufFormat *info)=NULL;
        void   (*fill_vtable)   (GdkPixbufModule *module)=NULL;

        if (g_path_is_absolute (file))
                path = g_strdup (file);
        else
                path = g_build_filename (dir, file, NULL);

        module = g_module_open (path, 0);
        if (module &&
            g_module_symbol (module, "fill_info", &pfill_info) &&
            g_module_symbol (module, "fill_vtable", &pfill_vtable)) {
                GdkPixbufFormat *info;
                GdkPixbufModule *vtable;

                fill_info = pfill_info;
                fill_vtable = pfill_vtable;
.
.
.
etc

As far as I can tell, gcc doesn't like casting a function pointer with a
specific parameter signature to a regular gpointer. So you have to pass
an actual gpointer variable's address to g_module_symbol and then copy
over the the gpointer variable's address to the actual function pointer
and THEN call the function pointer.

* if you use gdk_window_get_user_data, parameter 2 of this function is a
gpointer*, so again you can't just do something like:

> GtkWidget *widget;
> GdkWindow *window;
>
> gdk_window_get_user_data(window, (gpointer*)&widget);

This is because a gpointer * is really void **, and GtkWidget is a
typedef to the structure _GtkWidget. As types, these are obviously
incompatible, however as pointers they're the "same" type (ie and
address to an area of memory)... however gcc it breaks gcc's
strict-aliasing rules. You can't cast a struct pointer to a gpointer*
without the warning (though your program will compile OK), but you can
cast a gpointer* to a GtkWidget*. Therefore, you need to do the following:

> GtkWidget *widget;
> GdkWindow *window;
>
> gpointer pwidget;
>
> gdk_window_get_user_data(window, &pwidget);
> widget = (GtkWidget*) &pwidget;

Obviously, these examples can be applied to other areas where you get
this warning.

Anyway, I hope this helps somewhat.

Chris

So far, the bugs I've submitted patches for are:


  • Bug 140346: libmoduletestplugin_a.c gives type-punned warning when compiling glibc-2.2.3
  • Bug 141011: Unused variable i in gtk_cell_view_cell_layout_clear of gtkcellview.c
  • Bug 141020: gtkmain.c: multiple warnings about deferencing type-punned pointer breaks strict aliasing rules
  • Bug 141024: gtkselection.c: gtk_selection_convert - warning about deferencing type-punned pointer breaking strict aliasing rules
  • Bug 141027: gtktipsquery.c: gtk_tips_query_event - warning about deferencing type-punned pointer breaks strict aliasing rules
  • Bug 141030: gtkwidget.c: gtk_widget_reparent_subwindows - warning about deferencing type-punned pointer breaking strict aliasing rules
  • Bug 141066: queryimmodules.c: query_module - warning: dereferencing type-punned pointer will break strict-aliasing rules
  • Bug 141069: demos/gtk-demo/textview.c: easter_egg_callback -
    warning: dereferencing type-punned pointer will break strict-aliasing rules
  • Bug 141072: tests/testgtk.c: many warnings fixed
  • Bug 141080: Patch to remove multiple warnings in gdk/x11/gdkdnd-x11.c
  • Bug 141010: When compiling queryloaders.c gcc generates warning that dereferencing type-punned pointer breaks strict-aliasing
  • Bug 141011: Unused variable i in gtk_cell_view_cell_layout_clear of gtkcellview.c
  • Bug 141020: gtkmain.c: multiple warnings about deferencing type-punned pointer breaks strict aliasing rules
  • Bug 141024: gtkselection.c: gtk_selection_convert - warning about deferencing type-punned pointer breaking strict aliasing rules
  • Bug 141027: gtktipsquery.c: gtk_tips_query_event - warning about deferencing type-punned pointer breaks strict aliasing rules
  • Bug 141030: gtkwidget.c: gtk_widget_reparent_subwindows - warning about deferencing type-punned pointer breaking strict aliasing rules

And last, but not least:

  • Bug 141080: Patch to remove multiple warnings in gdk/x11/gdkdnd-x11.c

This last one deserves a comment. It's probably the ugliest hack job I've seen or done so far. What part of the code tries to do is to cast from a unsigned char ** to a MotifTargetTableHeader*! This, of course, breaks gcc's C99 strict aliasing rules (in fact I wouldn't wonder it breaks a whole lot more too, but let's not go there). To get around this, you need to convert a guchar** (unsigned char**) to a MotifTargetTableHeader*. The only way I can see that is valid is to create a guchar**, use this in parameter 12 of XGetWindowProperty, cast this to a temporary gpointer, and then cast the temporary gpointer to an XGetWindowProperty* variable.

I suspect that Owen Taylor from RedHat won't be too happy with the fix... *sigh* Well, I did at least tell them it was a hack - just check out the diff with my comments!

< Contest is the enemy | BBC White season: 'Rivers of Blood' >
C99 strict aliasing rears it's ugly head again in the gtk+ codebase | 9 comments (9 topical, 0 hidden) | Trackback
I must wonder, by ksandstr (3.00 / 0) #1 Sun Apr 25, 2004 at 11:01:18 AM EST
What the main point is in making an existing body of C89 -style code into C99 code if GCC will continue to support C89 in its future revisions? It seems to me that the last bug you mention is directly due to the fact that the C89 code is written so that it complies with only the less strict aliasing rules, and that the eventual emergence of hacks like this at the seams where old meets new would be inevitable (in cast-heavy code anyway).



Good point... by ta bu shi da yu (3.00 / 0) #8 Sun Apr 25, 2004 at 10:22:10 PM EST
... and it's a query I have about how they are building GNOME and GTK+. Should the target be C89 or C99?

[ Parent ]

Answer: by ta bu shi da yu (3.00 / 0) #9 Mon Apr 26, 2004 at 02:10:06 AM EST
According to jdub on irc (nice guy, I went to school with him), he reckons the answer is "undefined"  - such a great programming term for "we don't know", btw, or C89 if possible.

[ Parent ]

posting a big/technical diary during dead time.. by infinitera (3.00 / 0) #2 Sun Apr 25, 2004 at 11:02:19 AM EST
Is like doubly ensuring limited response.

Who cares what you think? — Son of the 41st King


Yeah, but you responded, right? (nt) by ta bu shi da yu (6.00 / 1) #4 Sun Apr 25, 2004 at 05:41:05 PM EST


[ Parent ]

i don't think metaresponses count [nt] by infinitera (3.00 / 0) #6 Sun Apr 25, 2004 at 06:57:52 PM EST


Who cares what you think? — Son of the 41st King
[ Parent ]

Hey :-) by ta bu shi da yu (6.00 / 1) #7 Sun Apr 25, 2004 at 07:52:09 PM EST
I put it up for my own reference, as well as others. It's not about popularity! It's about... well... getting it out there.

[ Parent ]

What is your day job? by lb008d (3.00 / 0) #3 Sun Apr 25, 2004 at 04:56:52 PM EST
Believe me after a day at work the last thing I'd want to do is even more coding.... :-)



:-) definitely not programming! by ta bu shi da yu (3.00 / 0) #5 Sun Apr 25, 2004 at 05:59:45 PM EST
I enjoy hacking, and the glory of recognition.

[ Parent ]

C99 strict aliasing rears it's ugly head again in the gtk+ codebase | 9 comments (9 topical, 0 hidden) | Trackback