This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] lower-subreg: Decompose multiword shifts
- From: Andreas Krebbel <Andreas dot Krebbel at de dot ibm dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 2 Aug 2007 17:34:34 +0200
- Subject: [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;
+ }