This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [tree-ssa] Remove useless null pointer checks


In message <wvloeza8u7t.fsf@prospero.boston.redhat.com>, Jason Merrill writes:
 >> Note that simply removing the typecasts can actually be a step backwards
 >> in terms of exposing stuff to the optimizer.    I found that when I
 >> started looking real closely at stripping out unwanted NOP_EXPRs in
 >> the gimplifier.
 >
 >Can you elaborate?
Yes I can :-)  Though it may not be as relevant given the set of rules
I've come up with for the dominator optimizer which make it largely 
immune to this stuff.

20030709-2.c:

struct rtx_def;
typedef struct rtx_def *rtx;
union tree_node;
typedef union tree_node *tree;
typedef struct mem_attrs
{
  int  foo;

} mem_attrs;
union rtunion_def
{
  mem_attrs *rtmem;
};
typedef union rtunion_def rtunion;
struct rtx_def
{
  rtunion fld[1];
};
struct tree_decl
{
  rtx rtl;
};
union tree_node
{
  struct tree_decl decl;
};
void *
get_alias_set (t)
     tree t;
{
  long set;
  if (t->decl.rtl)
    return (t->decl.rtl->fld[1].rtmem 
            ? 0
            : (((t->decl.rtl ? t->decl.rtl : (make_decl_rtl (t, 0), t->
decl.rtl)
))->fld[1]).rtmem);
}


Which gimplifies into something like this:

get_alias_set (t)
{
  struct rtx_def * T.1;
  void * iftmp.2; 
  struct mem_attrs * T.3;
  struct rtx_def * iftmp.4;
  struct mem_attrs * T.5;
  long int set;
  extern  make_decl_rtl;

  T.1 = t->decl.rtl;
  if (T.1 != 0B)
    {
      T.1 = t->decl.rtl;
XXX   T.3 = T.1->fld[1].rtmem;
      if (T.3 == 0B)
        {
          T.1 = t->decl.rtl;
          if (T.1 != 0B)
            {
              iftmp.4 = t->decl.rtl
            }
          else
            {
              make_decl_rtl (t, 0);
              iftmp.4 = t->decl.rtl
            };
XXX       T.5 = iftmp.4->fld[1].rtmem;
          iftmp.2 = (void *)T.5
        }
      else
        {
          iftmp.2 = 0B
        };
      (void)0;
      return iftmp.2;
    }
  else
    {
      (void)0
    }
}

Notice the two statements marked with XXX.  The dominator optimizer can
prove that iftmp.4 and T.1 are the same value and thus the second load
is redundant because they both load from the same memory location using
the same type.

Note how the second load feeds a typecast in the next statement.


Now let's look at the same function, but with some gimplifier changes
to avoid the unnecessary typecast:

get_alias_set (t)
{
  struct rtx_def * T.1;
  void * iftmp.2;
  struct mem_attrs * T.3;
  struct rtx_def * iftmp.4;
  long int set;
  extern  make_decl_rtl;

  T.1 = t->decl.rtl;
  if (T.1 != 0B)
    {
      T.1 = t->decl.rtl;
XXX   T.3 = T.1->fld[1].rtmem;
      if (T.3 == 0B)
        {
          T.1 = t->decl.rtl;
          if (T.1 != 0B)
            {
              iftmp.4 = t->decl.rtl
            }
          else
            {
              make_decl_rtl (t, 0);
              iftmp.4 = t->decl.rtl
            };
XXX       iftmp.2 = iftmp.4->fld[1].rtmem
        }
      else
        {
          iftmp.2 = 0B
        };
      (void)0;
      return iftmp.2;
    }
  else
    {
      (void)0
    }
}

You can see the annoying typecast is gone.  However, you'll also note that
the LHS of the two marked statements have different types.  One is a 
void *, the other is a struct mem_attrs *.

The type differences will prevent the dominator optimizer from recognizing
that the second marked statement is redundant.


Now as I said, this may all be a non-issue with the rules I've got which
make the dominator optimizer handle this kind of stuff better.  To recap
the underlying problem is that the typecasts get in the way of finding
equivalences between variables.

So the "trick" is to recognize typecasts which are not important from
a code generation standpoint and enter the proper equivalences into 
the const_and_copies table.

So given a statement such as

LHS = (TYPE) RHS;

If the main variant of TYPE and RHS is the same, then enter LHS = RHS into
the const_and_copies table.

If TYPE and RHS's type are both pointers and one is a void *, then enter
LHS = RHS into the const_and_copies table.

For integral types, we can safely call STRIP_SIGN_NOPS to remove any
casts which do not change the underlying mode or sign.

There may be other "rules" we could use for typecasts which are safe to
ignore in the gimplifier and tree-ssa optimizers. 

Thoughts?

Jeff




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]