diff options
-rw-r--r-- | gcc/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/lower-subreg.c | 169 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/multiword-1.c | 68 |
4 files changed, 246 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 50b4bbb..9b40d76 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2007-08-07 Andreas Krebbel <krebbel1@de.ibm.com> + + * lower-subreg.c (resolve_subreg_use): Remove assertion. + (find_decomposable_shift_zext, resolve_shift_zext): New functions. + (decompose_multiword_subregs): Use the functions above to decompose + multiword shifts and zero-extends. + 2007-08-07 Rask Ingemann Lambertsen <rask@sygehus.dk> * doc/sourcebuild.texi (Test Directives): Fix "compile" and diff --git a/gcc/lower-subreg.c b/gcc/lower-subreg.c index 9e207b3..4cb2ca8 100644 --- a/gcc/lower-subreg.c +++ b/gcc/lower-subreg.c @@ -525,8 +525,8 @@ resolve_subreg_use (rtx *px, void *data) { /* 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); + happen inside notes, multiword shift or zero-extend + instructions. */ return 1; } @@ -944,6 +944,155 @@ resolve_use (rtx pat, rtx insn) return false; } +/* Checks if INSN is a decomposable multiword-shift or zero-extend and + sets the decomposable_context bitmap accordingly. A non-zero value + is returned if a decomposable insn has been found. */ + +static int +find_decomposable_shift_zext (rtx insn) +{ + rtx set; + rtx op; + rtx op_operand; + + set = single_set (insn); + if (!set) + return 0; + + op = SET_SRC (set); + if (GET_CODE (op) != ASHIFT + && GET_CODE (op) != LSHIFTRT + && GET_CODE (op) != ZERO_EXTEND) + return 0; + + op_operand = XEXP (op, 0); + if (!REG_P (SET_DEST (set)) || !REG_P (op_operand) + || HARD_REGISTER_NUM_P (REGNO (SET_DEST (set))) + || HARD_REGISTER_NUM_P (REGNO (op_operand)) + || !SCALAR_INT_MODE_P (GET_MODE (op))) + return 0; + + if (GET_CODE (op) == ZERO_EXTEND) + { + if (GET_MODE (op_operand) != word_mode + || GET_MODE_BITSIZE (GET_MODE (op)) != 2 * BITS_PER_WORD) + return 0; + } + else /* left or right shift */ + { + if (GET_CODE (XEXP (op, 1)) != CONST_INT + || INTVAL (XEXP (op, 1)) < BITS_PER_WORD + || GET_MODE_BITSIZE (GET_MODE (op_operand)) != 2 * BITS_PER_WORD) + return 0; + } + + bitmap_set_bit (decomposable_context, REGNO (SET_DEST (set))); + + if (GET_CODE (op) != ZERO_EXTEND) + bitmap_set_bit (decomposable_context, REGNO (op_operand)); + + return 1; +} + +/* Decompose a more than word wide shift (in INSN) of a multiword + pseudo or a multiword zero-extend of a wordmode pseudo into a move + and 'set to zero' insn. Return a pointer to the new insn when a + replacement was done. */ + +static rtx +resolve_shift_zext (rtx insn) +{ + rtx set; + rtx op; + rtx op_operand; + rtx insns; + rtx src_reg, dest_reg, dest_zero; + int src_reg_num, dest_reg_num, offset1, offset2, src_offset; + + set = single_set (insn); + if (!set) + return NULL_RTX; + + op = SET_SRC (set); + if (GET_CODE (op) != ASHIFT + && GET_CODE (op) != LSHIFTRT + && GET_CODE (op) != ZERO_EXTEND) + return NULL_RTX; + + op_operand = XEXP (op, 0); + + if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (op_operand)) + return NULL_RTX; + + /* src_reg_num is the number of the word mode register which we + are operating on. For a left shift and a zero_extend on little + endian machines this is register 0. */ + src_reg_num = GET_CODE (op) == LSHIFTRT ? 1 : 0; + + if (WORDS_BIG_ENDIAN) + src_reg_num = 1 - src_reg_num; + + if (GET_CODE (op) == ZERO_EXTEND) + dest_reg_num = src_reg_num; + else + dest_reg_num = 1 - src_reg_num; + + offset1 = UNITS_PER_WORD * dest_reg_num; + offset2 = UNITS_PER_WORD * (1 - dest_reg_num); + src_offset = UNITS_PER_WORD * src_reg_num; + + if (WORDS_BIG_ENDIAN != BYTES_BIG_ENDIAN) + { + offset1 += UNITS_PER_WORD - 1; + offset2 += UNITS_PER_WORD - 1; + src_offset += UNITS_PER_WORD - 1; + } + + start_sequence (); + + dest_reg = simplify_gen_subreg_concatn (word_mode, SET_DEST (set), + GET_MODE (SET_DEST (set)), + offset1); + dest_zero = simplify_gen_subreg_concatn (word_mode, SET_DEST (set), + GET_MODE (SET_DEST (set)), + offset2); + src_reg = simplify_gen_subreg_concatn (word_mode, op_operand, + GET_MODE (op_operand), + src_offset); + if (GET_CODE (op) != ZERO_EXTEND) + { + int shift_count = INTVAL (XEXP (op, 1)); + if (shift_count > BITS_PER_WORD) + src_reg = expand_shift (GET_CODE (op) == ASHIFT ? + LSHIFT_EXPR : RSHIFT_EXPR, + word_mode, src_reg, + build_int_cst (NULL_TREE, + shift_count - BITS_PER_WORD), + dest_reg, 1); + } + + if (dest_reg != src_reg) + 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) + { + rtx in; + fprintf (dump_file, "; Replacing insn: %d with insns: ", INSN_UID (insn)); + for (in = insns; in != insn; in = NEXT_INSN (in)) + fprintf (dump_file, "%d ", INSN_UID (in)); + fprintf (dump_file, "\n"); + } + + 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. */ @@ -1003,6 +1152,9 @@ decompose_multiword_subregs (void) || GET_CODE (PATTERN (insn)) == USE) continue; + if (find_decomposable_shift_zext (insn)) + continue; + recog_memoized (insn); extract_insn (insn); @@ -1152,6 +1304,19 @@ decompose_multiword_subregs (void) SET_BIT (sub_blocks, bb->index); } } + else + { + rtx decomposed_shift; + + decomposed_shift = resolve_shift_zext (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], diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6d61a77..a20a009 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2007-08-07 Andreas Krebbel <krebbel1@de.ibm.com> + + * gcc.dg/multiword-1.c: New testcase. + 2007-08-07 Daniel Franke <franke.daniel@gmail.com> * gfortran.dg/namelist_33.f90: Improved tests, adjusted error diff --git a/gcc/testsuite/gcc.dg/multiword-1.c b/gcc/testsuite/gcc.dg/multiword-1.c new file mode 100644 index 0000000..c081617 --- /dev/null +++ b/gcc/testsuite/gcc.dg/multiword-1.c @@ -0,0 +1,68 @@ +/* { dg-do run } */ +/* { dg-options "-O3" } */ +/* { dg-require-effective-target ilp32 } */ + +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; +} + +u32 __attribute__((noinline)) +right2 (u64 t) +{ + return (u32)(t >> 40); +} + +u64 __attribute__((noinline)) +left2 (u32 t) +{ + return (u64)t << 40; +} + +u64 __attribute__((noinline)) +zeroextend (u32 t) +{ + return (u64)t; +} + +extern void abort (); + +int +main () +{ + if (foo (13000, 12000) != 55834574860000ULL) + abort (); + + if (right (55834574860000ULL) != 13000) + abort (); + + if (left (13000) != 55834574848000ULL) + abort (); + + if (right2 (55834574860000ULL) != 50) + abort (); + + if (left2 (13000) != 14293651161088000ULL) + abort (); + + if (zeroextend (13000) != 13000ULL) + abort (); + + return 0; +} |