aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog7
-rw-r--r--gcc/lower-subreg.c169
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.dg/multiword-1.c68
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;
+}