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]

[PATCH] lower-subreg: Decompose multiword shifts


Hello,

the attached patch enhances the lower subreg pass to be able to
decompose multiword shift instructions.

Currently GCC generates quite ugly code (on s390 31bit) for a function
like the first of the attached testcase:

u64
foo (u32 high, u32 low)
{
  return ((u64)high << 32) | low;
}

Although that should be a nop on s390 GCC comes up with:

foo:
.LFB2:
        lr      %r5,%r2
        lhi     %r4,0
        sldl    %r4,32
        or      %r5,%r3
        lr      %r2,%r4
        lr      %r3,%r5
        br      %r14

With the attached patch the function is optimized to a single
"br %r14".

Besides the fact that a shift decomposed into 2 simple move
instructions can often be merged into other INSNs also the rest of
lower subreg benefits since more multiword regs can be marked as
decomposable.

In spite of the 2 new functions the cc1 executable shrinks by
9345 bytes.

Bootstrapped on s390, s390x and i686. No testsuite regressions.

Ok for mainline?

Bye,

-Andreas-


2007-08-02  Andreas Krebbel  <krebbel1@de.ibm.com>

	* lower-subreg.c (resolve_subreg_use): Remove assertion.
	(find_decomposable_shift, resolve_multiword_shift): New functions.
	(decompose_multiword_subregs): Use the functions above to decompose
	multiword shifts.


2007-08-02  Andreas Krebbel  <krebbel1@de.ibm.com>

	* gcc.dg/multiword-shifts.c: New testcase.


Index: gcc/lower-subreg.c
===================================================================
*** gcc/lower-subreg.c.orig	2007-08-02 11:46:48.000000000 +0200
--- gcc/lower-subreg.c	2007-08-02 11:48:02.000000000 +0200
*************** resolve_subreg_use (rtx *px, void *data)
*** 525,532 ****
      {
        /* Return 1 to the caller to indicate that we found a direct
  	 reference to a register which is being decomposed.  This can
! 	 happen inside notes.  */
!       gcc_assert (!insn);
        return 1;
      }
  
--- 525,531 ----
      {
        /* Return 1 to the caller to indicate that we found a direct
  	 reference to a register which is being decomposed.  This can
! 	 happen inside notes or multiword shift instructions.  */
        return 1;
      }
  
*************** resolve_use (rtx pat, rtx insn)
*** 944,949 ****
--- 943,1069 ----
    return false;
  }
  
+ /* Checks if INSN is a decomposable multiword-shift and sets the
+    decomposable_context bitmap accordingly.  A non-zero value is
+    returned if a decomposable shift was found.  */
+ 
+ static int
+ find_decomposable_shift (rtx insn)
+ {
+   rtx set;
+   rtx shift;
+   rtx shift_operand;
+   rtx shift_count;
+ 
+   set = single_set (insn);
+   if (!set)
+     return 0;
+ 
+   shift = SET_SRC (set);
+   if (GET_CODE (shift) != ASHIFT && GET_CODE (shift) != LSHIFTRT)
+     return 0;
+ 
+   shift_operand = XEXP (shift, 0);
+   shift_count = XEXP (shift, 1);
+   if (!REG_P (SET_DEST (set)) || !REG_P (shift_operand)
+       || HARD_REGISTER_NUM_P (REGNO (SET_DEST (set)))
+       || HARD_REGISTER_NUM_P (REGNO (shift_operand))
+       || GET_CODE (shift_count) != CONST_INT
+       || !SCALAR_INT_MODE_P (GET_MODE (shift)))
+     return 0;
+ 
+   if (INTVAL (shift_count) != BITS_PER_WORD
+       || GET_MODE_BITSIZE (GET_MODE (shift_operand)) != 2 * BITS_PER_WORD)
+     return 0;
+ 
+   bitmap_set_bit (decomposable_context, REGNO (SET_DEST (set)));
+   bitmap_set_bit (decomposable_context, REGNO (shift_operand));
+ 
+   return 1;
+ }
+ 
+ /* Decompose the word wide shift (in INSN) of a multiword pseudo into
+    a move and 'set to zero' insn.  Return a pointer to the new insn
+    when a replacement was done.  */
+ 
+ static rtx
+ resolve_multiword_shift (rtx insn)
+ {
+   rtx set;
+   rtx shift;
+   rtx shift_operand;
+   rtx insns;
+   rtx src_reg, dest_reg, dest_zero;
+   int src_reg_num, offset1, offset2;
+ 
+   set = single_set (insn);
+   if (!set)
+     return NULL_RTX;
+ 
+   shift = SET_SRC (set);
+   if (GET_CODE (shift) != ASHIFT && GET_CODE (shift) != LSHIFTRT)
+     return NULL_RTX;
+ 
+   shift_operand = XEXP (shift, 0);
+ 
+   if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (shift_operand))
+     return NULL_RTX;
+ 
+   /* src_reg_num is the number of the word register to be shifted.  */
+   src_reg_num = GET_CODE (shift) == LSHIFTRT ? 1 : 0;
+ 
+   if (WORDS_BIG_ENDIAN)
+     src_reg_num = 1 - src_reg_num;
+ 
+   offset1 = UNITS_PER_WORD * (1 - src_reg_num);
+   offset2 = UNITS_PER_WORD * src_reg_num;
+ 
+   if (WORDS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+     {
+       offset1 += UNITS_PER_WORD - 1;
+       offset2 += UNITS_PER_WORD - 1;
+     }
+ 
+   start_sequence ();
+ 
+   if (resolve_reg_p (SET_DEST (set)))
+     {
+       gcc_assert (GET_CODE (SET_DEST (set)) == CONCATN);
+ 
+       dest_reg = XVECEXP (SET_DEST (set), 0, 1 - src_reg_num);
+       dest_zero = XVECEXP (SET_DEST (set), 0, src_reg_num);
+     }
+   else
+     {
+       dest_reg = gen_rtx_SUBREG (word_mode, SET_DEST (set), offset1);
+       dest_zero = gen_rtx_SUBREG (word_mode, SET_DEST (set), offset2);
+     }
+ 
+   if (resolve_reg_p (shift_operand))
+     {
+       gcc_assert (GET_CODE (shift_operand) == CONCATN);
+ 
+       src_reg = XVECEXP (shift_operand, 0, src_reg_num);
+     }
+   else
+     src_reg = gen_rtx_SUBREG (word_mode, shift_operand, offset2);
+ 
+   emit_move_insn (dest_reg, src_reg);
+   emit_move_insn (dest_zero, CONST0_RTX (word_mode));
+   insns = get_insns ();
+ 
+   end_sequence ();
+ 
+   emit_insn_before (insns, insn);
+ 
+   if (dump_file)
+     fprintf (dump_file, "; Replacing SHIFT insn: %d with insns: %d and %d\n",
+ 	     INSN_UID (insn), INSN_UID (insns), INSN_UID (NEXT_INSN (insns)));
+ 
+   delete_insn (insn);
+   return insns;
+ }
+ 
  /* Look for registers which are always accessed via word-sized SUBREGs
     or via copies.  Decompose these registers into several word-sized
     pseudo-registers.  */
*************** decompose_multiword_subregs (void)
*** 1003,1008 ****
--- 1123,1131 ----
  	      || GET_CODE (PATTERN (insn)) == USE)
  	    continue;
  
+ 	  if (find_decomposable_shift (insn))
+ 	    continue;
+ 
  	  recog_memoized (insn);
  	  extract_insn (insn);
  
*************** decompose_multiword_subregs (void)
*** 1152,1157 ****
--- 1275,1293 ----
  			    SET_BIT (sub_blocks, bb->index);
  			}
  		    }
+ 		  else
+ 		    {
+ 		      rtx decomposed_shift;
+ 
+ 		      decomposed_shift = resolve_multiword_shift (insn);
+ 		      if (decomposed_shift != NULL_RTX)
+ 			{
+ 			  changed = true;
+ 			  insn = decomposed_shift;
+ 			  recog_memoized (insn);
+ 			  extract_insn (insn);
+ 			}
+ 		    }
  
  		  for (i = recog_data.n_operands - 1; i >= 0; --i)
  		    for_each_rtx (recog_data.operand_loc[i],
Index: gcc/testsuite/gcc.dg/multiword-shifts.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/multiword-shifts.c	2007-08-02 11:48:02.000000000 +0200
***************
*** 0 ****
--- 1,40 ----
+ /* { dg-do run } */
+ /* { dg-options "-O3" } */
+ 
+ typedef unsigned int u32;
+ typedef unsigned long long u64;
+ 
+ u64 __attribute__((noinline))
+ foo (u32 high, u32 low)
+ {
+   return ((u64)high << 32) | low;
+ }
+ 
+ u32 __attribute__((noinline))
+ right (u64 t)
+ {
+   return (u32)(t >> 32);
+ }
+ 
+ u64 __attribute__((noinline))
+ left (u32 t)
+ {
+   return (u64)t << 32;
+ }
+ 
+ extern void abort ();
+ 
+ int
+ main ()
+ {
+   if (foo (13000, 12000) != 55834574860000ULL)
+     abort ();
+ 
+   if (right (55834574860000ULL) != 13000)
+     abort ();
+ 
+   if (left (13000) != 55834574848000ULL)
+     abort ();
+ 
+   return 0;
+ }


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