[tree-ssa] Improve removal of statements when removing blocks

law@redhat.com law@redhat.com
Thu Jul 17 01:12:00 GMT 2003


When we delete a basic block, we (correctly) call bsi_remove on each
statement in the block to remove the statements.

bsi_remove has some code which removes the containing COMPOUND_EXPR
if we're removing it's 0th operand and its 1st operand is already
an empty statement.

That's all fine and good, except that when deleting a block we remove
statements from first to last.  Meaning that it's exceedingly rare
that the 1st operand is already an empty statement when we call
bsi_remove on the 0th operand.

Believe it or not, this can cause the optimizers to miss optimization
opportunities.  Consider 20030709-2.c which generates the following
gimple code:



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;
      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
            };
          T.5 = iftmp.4->fld[1].rtmem;
          iftmp.2 = (void *)T.5
        }
      else
        {
          iftmp.2 = 0B
        };
      (void)0;
      return iftmp.2;
    }
  else
    {
      (void)0
    }
}

The dominator optimizations correctly determine that the second test of
T.1 != 0b is always true.  That in turn causes the ELSE block to become
unreachable and we get the following code:

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_8 = t_6->decl.rtl;
  if (T.1_8 != 0B)
    {
      T.1_9 = T.1_8;
      T.3_10 = T.1_8->fld[1].rtmem;
      if (T.3_10 == 0B)
        { 
          T.1_11 = T.1_8;
          if (1)
            {
              iftmp.4_12 = T.1_8
            }
          else
            {
              (void)0;
              (void)0
            };
          T.5_15 = iftmp.4_1->fld[1].rtmem;
          iftmp.2_16 = (void *)T.5_15
        } 
      else    
        {     
          iftmp.2_17 = 0B
        };
      (void)0;
      return iftmp.2_5;
    }
  else
    {
      (void)0
    }
}


Unfortunately, the code which linearizes a COND_EXPR which is always
true requires an ELSE clause which is empty -- a COMPOUND_EXPR with
both operands being empty statements does not qualify as empty, nor does
a chain of COMPOUND_EXPRs where all the leafs are empty statements.
[ Similar problems occur for linearizing an COND_EXPR which is always 
  false -- we require the THEN arm to be empty. ]

Anyway, so we fail to linearize the COND_EXPR, which causes us to leave
a useless COND_EXPR in the IL.

Luckily the solution is pretty simple.  When we're removing all the
statements in a basic block, remove them in reverse order.  This
ensures that the 1st operand of a COMPOUND_EXPR is removed before
the 0th operand is removed.  And thus when the 0th operand is removed
we recognize that the entire COMPOUND_EXPR can be removed.  Resulting
in the following code:


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_8 = t_6->decl.rtl;
  if (T.1_8 != 0B)
    {
      T.1_9 = T.1_8;
      T.3_10 = T.1_8->fld[1].rtmem;
      if (T.3_10 == 0B)
        { 
          T.1_11 = T.1_8;
          iftmp.4_12 = T.1_8;
          T.5_15 = iftmp.4_1->fld[1].rtmem;
          iftmp.2_16 = (void *)T.5_15
        } 
      else
        { 
          iftmp.2_17 = 0B
        };
      (void)0;
      return iftmp.2_5;
    }   
  else  
    {         
      (void)0 
    }
}

Note this is still not fully optimized -- once we start optimizing
PHIs with a single argument we'll realize that the second load of
fld[1].rtmem is redundant.  Even so this is still an improvement
and fixes one of the failures in the recently added tree-ssa tests.

This also happens to slightly simplify the logic in remove_bb to deal
with warnings for unreachable code.


Bootstrapped, regression tested.  Whee.



	* tree-cfg.c (remove_bb): Remove statements in reverse order.
	Simplify code to issue warnings for unreachable code.

Index: tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-cfg.c,v
retrieving revision 1.1.4.128
diff -c -3 -p -r1.1.4.128 tree-cfg.c
*** tree-cfg.c	15 Jul 2003 23:56:23 -0000	1.1.4.128
--- tree-cfg.c	17 Jul 2003 01:10:37 -0000
*************** static void
*** 1777,1783 ****
  remove_bb (basic_block bb, int remove_stmts)
  {
    block_stmt_iterator i;
!   int already_warned;
  
    dump_file = dump_begin (TDI_cfg, &dump_flags);
    if (dump_file)
--- 1777,1784 ----
  remove_bb (basic_block bb, int remove_stmts)
  {
    block_stmt_iterator i;
!   bsi_list_p stack;
!   location_t loc;
  
    dump_file = dump_begin (TDI_cfg, &dump_flags);
    if (dump_file)
*************** remove_bb (basic_block bb, int remove_st
*** 1789,1816 ****
        dump_file = NULL;
      }
  
!   /* Remove all the instructions in the block.  */
!   already_warned = 0;
!   for (i = bsi_start (bb); !bsi_end_p (i); )
      {
        tree stmt = bsi_stmt (i);
  
        set_bb_for_stmt (stmt, NULL);
        if (remove_stmts)
          {
! 	  if (! already_warned && warn_notreached)
! 	    {
! 	      location_t loc;
! 	      loc.file = get_filename (stmt);
! 	      loc.line = get_lineno (stmt);
! 	      warning ("%Hwill never be executed", &loc);
! 	      already_warned = 1;
! 	    }
  	  bsi_remove (&i);
          }
        else
!         bsi_next (&i);
      }
  
    if (bb->head_tree_p)
      set_bb_for_stmt (*bb->head_tree_p, NULL);
--- 1790,1818 ----
        dump_file = NULL;
      }
  
!   /* Remove all the instructions in the block.  Do so in reverse order
!      so that we remove all the containing COMPOUND_EXPRs as well.  */
!   FOR_EACH_BSI_IN_REVERSE (stack, bb, i)
      {
        tree stmt = bsi_stmt (i);
  
        set_bb_for_stmt (stmt, NULL);
        if (remove_stmts)
          {
! 	  loc.file = get_filename (stmt);
! 	  loc.line = get_lineno (stmt);
  	  bsi_remove (&i);
          }
        else
! 	bsi_next (&i);
      }
+ 
+   /* If requested, give a warning that the first statement in the
+      block is unreachable.  We walk statements backwards in the
+      loop above, so the last statement we process is the first statement
+      in the block.  */
+   if (remove_stmts && warn_notreached)
+     warning ("%Hwill never be executed", &loc);
  
    if (bb->head_tree_p)
      set_bb_for_stmt (*bb->head_tree_p, NULL);













More information about the Gcc-patches mailing list