]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR middle-end/48660 (ARM ICE in expand_expr_real_1)
authorRichard Sandiford <richard.sandiford@linaro.org>
Fri, 6 Jan 2012 14:59:47 +0000 (14:59 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 6 Jan 2012 14:59:47 +0000 (14:59 +0000)
gcc/
PR middle-end/48660
* expr.h (copy_blkmode_to_reg): Declare.
* expr.c (copy_blkmode_to_reg): New function.
(expand_assignment): Don't expand register RESULT_DECLs before
the lhs.  Use copy_blkmode_to_reg to copy BLKmode values into a
RESULT_DECL register.
(expand_expr_real_1): Handle BLKmode decls when looking for promotion.

gcc/testsuite/
PR middle-end/48660
* g++.dg/pr48660.C: New test.

From-SVN: r182956

gcc/ChangeLog
gcc/expr.c
gcc/expr.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/pr48660.C [new file with mode: 0644]

index 4a1a3e04f2f684e2b5326c291b9a369577e63124..6a8595c1d5f23138e768438409960585bb9bd917 100644 (file)
@@ -1,3 +1,13 @@
+2012-01-06  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       PR middle-end/48660
+       * expr.h (copy_blkmode_to_reg): Declare.
+       * expr.c (copy_blkmode_to_reg): New function.
+       (expand_assignment): Don't expand register RESULT_DECLs before
+       the lhs.  Use copy_blkmode_to_reg to copy BLKmode values into a
+       RESULT_DECL register.
+       (expand_expr_real_1): Handle BLKmode decls when looking for promotion.
+
 2012-01-04  Richard Guenther  <rguenther@suse.de>
 
        Backport from mainline
index 4d05c617116295f058091fffc8008ab740790c15..65a1c6066a8f5cc41fad23a018e1c491c9e940fb 100644 (file)
@@ -2220,6 +2220,111 @@ copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type)
   return tgtblk;
 }
 
+/* Copy BLKmode value SRC into a register of mode MODE.  Return the
+   register if it contains any data, otherwise return null.
+
+   This is used on targets that return BLKmode values in registers.  */
+
+rtx
+copy_blkmode_to_reg (enum machine_mode mode, tree src)
+{
+  int i, n_regs;
+  unsigned HOST_WIDE_INT bitpos, xbitpos, padding_correction = 0, bytes;
+  unsigned int bitsize;
+  rtx *dst_words, dst, x, src_word = NULL_RTX, dst_word = NULL_RTX;
+  enum machine_mode dst_mode;
+
+  gcc_assert (TYPE_MODE (TREE_TYPE (src)) == BLKmode);
+
+  x = expand_normal (src);
+
+  bytes = int_size_in_bytes (TREE_TYPE (src));
+  if (bytes == 0)
+    return NULL_RTX;
+
+  /* If the structure doesn't take up a whole number of words, see
+     whether the register value should be padded on the left or on
+     the right.  Set PADDING_CORRECTION to the number of padding
+     bits needed on the left side.
+
+     In most ABIs, the structure will be returned at the least end of
+     the register, which translates to right padding on little-endian
+     targets and left padding on big-endian targets.  The opposite
+     holds if the structure is returned at the most significant
+     end of the register.  */
+  if (bytes % UNITS_PER_WORD != 0
+      && (targetm.calls.return_in_msb (TREE_TYPE (src))
+         ? !BYTES_BIG_ENDIAN
+         : BYTES_BIG_ENDIAN))
+    padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
+                                          * BITS_PER_UNIT));
+
+  n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  dst_words = XALLOCAVEC (rtx, n_regs);
+  bitsize = MIN (TYPE_ALIGN (TREE_TYPE (src)), BITS_PER_WORD);
+
+  /* Copy the structure BITSIZE bits at a time.  */
+  for (bitpos = 0, xbitpos = padding_correction;
+       bitpos < bytes * BITS_PER_UNIT;
+       bitpos += bitsize, xbitpos += bitsize)
+    {
+      /* We need a new destination pseudo each time xbitpos is
+        on a word boundary and when xbitpos == padding_correction
+        (the first time through).  */
+      if (xbitpos % BITS_PER_WORD == 0
+         || xbitpos == padding_correction)
+       {
+         /* Generate an appropriate register.  */
+         dst_word = gen_reg_rtx (word_mode);
+         dst_words[xbitpos / BITS_PER_WORD] = dst_word;
+
+         /* Clear the destination before we move anything into it.  */
+         emit_move_insn (dst_word, CONST0_RTX (word_mode));
+       }
+
+      /* We need a new source operand each time bitpos is on a word
+        boundary.  */
+      if (bitpos % BITS_PER_WORD == 0)
+       src_word = operand_subword_force (x, bitpos / BITS_PER_WORD, BLKmode);
+
+      /* Use bitpos for the source extraction (left justified) and
+        xbitpos for the destination store (right justified).  */
+      store_bit_field (dst_word, bitsize, xbitpos % BITS_PER_WORD, word_mode,
+                      extract_bit_field (src_word, bitsize,
+                                         bitpos % BITS_PER_WORD, 1,
+                                         NULL_RTX, word_mode, word_mode));
+    }
+
+  if (mode == BLKmode)
+    {
+      /* Find the smallest integer mode large enough to hold the
+        entire structure.  */
+      for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+          mode != VOIDmode;
+          mode = GET_MODE_WIDER_MODE (mode))
+       /* Have we found a large enough mode?  */
+       if (GET_MODE_SIZE (mode) >= bytes)
+         break;
+
+      /* A suitable mode should have been found.  */
+      gcc_assert (mode != VOIDmode);
+    }
+
+  if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode))
+    dst_mode = word_mode;
+  else
+    dst_mode = mode;
+  dst = gen_reg_rtx (dst_mode);
+
+  for (i = 0; i < n_regs; i++)
+    emit_move_insn (operand_subword (dst, i, 0, dst_mode), dst_words[i]);
+
+  if (mode != dst_mode)
+    dst = gen_lowpart (mode, dst);
+
+  return dst;
+}
+
 /* Add a USE expression for REG to the (possibly empty) list pointed
    to by CALL_FUSAGE.  REG must denote a hard register.  */
 
@@ -4380,7 +4485,9 @@ expand_assignment (tree to, tree from, bool nontemporal)
   if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from, from)
       && COMPLETE_TYPE_P (TREE_TYPE (from))
       && TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) == INTEGER_CST
-      && ! (((TREE_CODE (to) == VAR_DECL || TREE_CODE (to) == PARM_DECL)
+      && ! (((TREE_CODE (to) == VAR_DECL
+             || TREE_CODE (to) == PARM_DECL
+             || TREE_CODE (to) == RESULT_DECL)
             && REG_P (DECL_RTL (to)))
            || TREE_CODE (to) == SSA_NAME))
     {
@@ -4426,12 +4533,15 @@ expand_assignment (tree to, tree from, bool nontemporal)
       rtx temp;
 
       push_temp_slots ();
-      temp = expand_expr (from, NULL_RTX, GET_MODE (to_rtx), EXPAND_NORMAL);
+      if (REG_P (to_rtx) && TYPE_MODE (TREE_TYPE (from)) == BLKmode)
+       temp = copy_blkmode_to_reg (GET_MODE (to_rtx), from);
+      else
+       temp = expand_expr (from, NULL_RTX, GET_MODE (to_rtx), EXPAND_NORMAL);
 
       if (GET_CODE (to_rtx) == PARALLEL)
        emit_group_load (to_rtx, temp, TREE_TYPE (from),
                         int_size_in_bytes (TREE_TYPE (from)));
-      else
+      else if (temp)
        emit_move_insn (to_rtx, temp);
 
       preserve_temp_slots (to_rtx);
@@ -8528,11 +8638,15 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
          return temp;
        }
 
-      /* If the mode of DECL_RTL does not match that of the decl, it
-        must be a promoted value.  We return a SUBREG of the wanted mode,
-        but mark it so that we know that it was already extended.  */
+      /* If the mode of DECL_RTL does not match that of the decl,
+        there are two cases: we are dealing with a BLKmode value
+        that is returned in a register, or we are dealing with
+        a promoted value.  In the latter case, return a SUBREG
+        of the wanted mode, but mark it so that we know that it
+        was already extended.  */
 
       if (REG_P (decl_rtl)
+         && DECL_MODE (exp) != BLKmode
          && GET_MODE (decl_rtl) != DECL_MODE (exp))
        {
          enum machine_mode pmode;
index 4fddde6006ef6650d00ec69bf0af51aa80bb1986..c1f2396745cacf1da8dc29adb6d053919b1de90d 100644 (file)
@@ -460,6 +460,8 @@ extern void emit_group_store (rtx, rtx, tree, int);
 /* Copy BLKmode object from a set of registers.  */
 extern rtx copy_blkmode_from_reg (rtx, rtx, tree);
 
+extern rtx copy_blkmode_to_reg (enum machine_mode, tree);
+
 /* Mark REG as holding a parameter for the next CALL_INSN.  */
 extern void use_reg (rtx *, rtx);
 
index d5a0134200e048052315193c5bd4d920fb1284a0..4b780419d55358f9e9d1e0bc921e48db23d8015e 100644 (file)
@@ -1,3 +1,8 @@
+2012-01-06  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       PR middle-end/48660
+       * g++.dg/pr48660.C: New test.
+
 2012-01-06  Eric Botcazou  <ebotcazou@adacore.com>
 
        * ada/acats/overflow.lst: Add cb20004.
diff --git a/gcc/testsuite/g++.dg/pr48660.C b/gcc/testsuite/g++.dg/pr48660.C
new file mode 100644 (file)
index 0000000..37b6174
--- /dev/null
@@ -0,0 +1,22 @@
+template<int N> struct val { char a[N]; };
+
+class Base
+{
+public:
+  virtual val<1> get1() const = 0;
+  virtual val<2> get2() const = 0;
+  virtual val<3> get3() const = 0;
+  virtual val<4> get4() const = 0;
+};
+
+class Derived : public virtual Base
+{
+public:
+  virtual val<1> get1() const { return foo->get1(); }
+  virtual val<2> get2() const { return foo->get2(); }
+  virtual val<3> get3() const { return foo->get3(); }
+  virtual val<4> get4() const { return foo->get4(); }
+  Base *foo;
+};
+
+Base* make() { return new Derived; }