Ehren's Blog

Adding a new function attribute to GCC

Posted in Seneca by ehren on October 7, 2009

I’ve recently made a bit of a breakthrough in my quest to add an alwayszero function attribute to GCC (as described in my last post). Previously, I tried grepping for the windows specific attribute “dllexport” which I believe is never used by GCC (as opposed to “const”, “pure”, etc…). This approach allowed me to add a new bitflag representing my attribute to a FUNCTION_DECL node. Giddy with the thought that I actually found code to modify (tree.h and calls.c), I immediately proceeded with a build which failed for reasons I was never able to identify. After getting some help with working on the CDOT computers remotely from my professors David Humphrey and Chris Tyler on IRC I was ready to try again. As an aside Dave got me started with GNU Screen which has proved indispensable. However, after all the work, I realized a crucial piece of the puzzle was missing: where exactly should GCC recognize the attribute string “alwayszero”? Faced with the sinking feeling that the recognition of an attribute would be harder than the actual optimization I retired for the night.

The next morning I tried a new approach by consulting GCC’s bugzilla. Searching for bugs marked enhancement and containing the word “attribute” I came across Bug 36892 Support __attribute__((deprecated(“text string”))). Reviewing
H. J. Lu’s accepted revision filled in the missing piece: c-common.c contains a table which includes the attribute name as well as a pointer to a call back function intended to handle the attribute. This definitely counts as spamming the planet but here’s my patch for a new do nothing attribute:

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 152509)
+++ gcc/tree.h	(working copy)
@@ -3127,6 +3127,10 @@
    but may have arbitrary side effects).  */
 #define DECL_IS_NOVOPS(NODE) (FUNCTION_DECL_CHECK (NODE)->function_decl.novops_flag)
 
+/* Nonzero in a FUNCTION_DECL means this function should be treated
+   as an "alwayszero" function (always returns 0 regardless of actual return. */
+#define DECL_IS_ALWAYSZERO(NODE) (FUNCTION_DECL_CHECK (NODE)->function_decl.alwayszero_flag)
+
 /* Used in FUNCTION_DECLs to indicate that they should be run automatically
    at the beginning or end of execution.  */
 #define DECL_STATIC_CONSTRUCTOR(NODE) \
@@ -3242,8 +3246,8 @@
   unsigned disregard_inline_limits : 1;
   unsigned pure_flag : 1;
   unsigned looping_const_or_pure_flag : 1;
+  unsigned alwayszero_flag : 1;
 
-
   /* 3 bits left */
 };
 
@@ -5049,7 +5053,10 @@
 /* Function does not read or write memory (but may have side effects, so
    it does not necessarily fit ECF_CONST).  */
 #define ECF_NOVOPS		  (1 << 9)
+/* Function always returns 0 */
+#define ECF_ALWAYSZERO           (1 << 10)
 
+
 extern int flags_from_decl_or_type (const_tree);
 extern int call_expr_flags (const_tree);
 
Index: gcc/calls.c
===================================================================
--- gcc/calls.c	(revision 152509)
+++ gcc/calls.c	(working copy)
@@ -608,6 +608,9 @@
       if (DECL_IS_NOVOPS (exp))
 	flags |= ECF_NOVOPS;
 
+      if (DECL_IS_ALWAYSZERO (exp))
+	flags |= ECF_ALWAYSZERO;
+
       if (TREE_NOTHROW (exp))
 	flags |= ECF_NOTHROW;
 
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 152509)
+++ gcc/c-common.c	(working copy)
@@ -530,6 +530,7 @@
 static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree handle_target_attribute (tree *, tree, tree, int, bool *);
 static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alwayszero_attribute (tree *, tree, tree, int, bool *);
 
 static void check_function_nonnull (tree, int, tree *);
 static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
@@ -824,6 +825,8 @@
 			      handle_target_attribute },
   { "optimize",               1, -1, true, false, false,
 			      handle_optimize_attribute },
+  { "alwayszero",               0, 0, true,  false, false,
+			      handle_alwayszero_attribute },
   { NULL,                     0, 0, false, false, false, NULL }
 };
 
@@ -7808,6 +7811,24 @@
 
   return NULL_TREE;
 }
+
+/* Derived from handle_nothrow_attribute */
+static tree
+handle_alwayszero_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			  int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    DECL_IS_ALWAYSZERO(*node) = 1;
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+
 
 /* Check for valid arguments being passed to a function.
    ATTRS is a list of attributes.  There are NARGS arguments in the array

Once I’ve made more progress on the optimization front it will be quite straightforward to go back and restrict the attribute to, for example, non-void functions, functions of a particular type, etc. It would actually be easy to add an argument to the attribute as well ie __attribute__((alwaysX("0"))) but I don’t want to get ahead of myself.

I should note that the comments I received in my previous post were all extremely helpful as well. I’ve been slightly concerned for the past few days that any sensible use of the alwayszero attribute would be unnecessary in light of existing gcc optimizations. Rereading Taras’ encouraging post I’m left with the impression that, in addition to his proposed devirtualizer, dynamic linking also eliminates the redundancy of an alwayszero attribute.

Taras’ comments also got me on the right track in terms of the ‘optimization’ required. Browsing this page on GCC debugging options, I noticed the compiler flag -fdump-tree-all which dumps a cornucopia of pretty printed intermediate GCC representations to file.

For example this program:

#include <stdio.h>
int blah(void) {
    printf("blah\n");
    return 5;
}
int main(void) {
    int c;
    blah();
    while (blah())
        printf("1");
    if (blah())
        printf("2");
    if (c = blah() == 5)
        printf("3");
    return 0;
}

Results in the following GIMPLE representation:

blah ()
{
  int D.1587;

  __builtin_puts (&"blah"[0]);
  D.1587 = 5;
  return D.1587;
}

main ()
{
  int D.1596;
  int D.1597;
  int D.1598;
  int D.1599;
  int c;

  blah ();
  goto <D.1593>;
  <D.1592>:;
  __builtin_putchar (49);
  <D.1593>:;
  D.1596 = blah ();
  if (D.1596 != 0)
    {
      goto <D.1592>;
    }
  else
    {
      goto <D.1594>;
    }
  <D.1594>:;
  D.1597 = blah ();
  if (D.1597 != 0)
    {
      __builtin_putchar (50);
    }
  else
    {
      
    }
  D.1598 = blah ();
  c = D.1598 == 5;
  if (c != 0)
    {
      __builtin_putchar (51);
    }
  else
    {
      
    }
  D.1599 = 0;
  return D.1599;
}

(can also be obtained directly using -fdump-tree-gimple)

I’m still a bit murky on how all of these representations fit together since, for example, there is low gimple, high gimple (middle high gimple?), etc, but Taras’ described optimization seems straightforward at this level. Viewing this page on SSA form, which I was led to by Benjamin Smedberg’s comment in my previous post, I would think that I should make my change before GCC builds its control flow graph, but this will require more investigation. I’ve actually seen that it’s quite simple to access an attribute tree code from down in the gimple, so this will be my next step.

Also, if I finish Kenneth Louden’s compiler book, Robert Morgan’s Building An Optimizing Compiler might be worth checking out since it’s referenced in the above SSA doc, as well as in several comments within the GCC source (of course the YorkU Library doesn’t have it). One thing at a time though.

Advertisements

3 Responses

Subscribe to comments with RSS.

  1. Taras said, on October 7, 2009 at 5:06 pm

    Looks like you are getting really close.

    Take a look at whatever code eliminates unfeasible branches..for example

    {

    int x;//non-escaping variable
    if (x) {
    …//dont write to x here
    If (!x) {/// this branch will be eliminated.

    }
    }

    So I’m pretty sure you are wrong about doing this optimization before building CFG. It should be somewhere in ssa(ie near the same place as above optimization)

    so essencially it will boil down to in ssa form you’ll have a statement
    var = func() // <– here if func has an alwayszero attribute, var will get a value assigned to it and whatever ssa pass will propagate it futher to root out unfeasible codepaths.

  2. Wiley said, on October 9, 2014 at 10:44 am

    I read a lot of interesting posts here. Probably you spend a lot of
    time writing, i know how to save you a lot of time, there is an online tool that creates
    unique, SEO friendly posts in minutes, just search in google – rewriter creates an unique article in a minute

  3. wohnzimmer renovieren einrichten ideen said, on August 6, 2015 at 1:26 pm

    Wow! After all I got a blog from where I can genuinely obtain valuable data regarding my study and
    knowledge.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: