aboutsummaryrefslogtreecommitdiff
path: root/gcc/shrink-wrap.c
diff options
context:
space:
mode:
authorRichard Henderson <rth@redhat.com>2014-10-10 08:56:07 -0700
committerRichard Henderson <rth@gcc.gnu.org>2014-10-10 08:56:07 -0700
commit069d7fc5a4f28fa0ee577b156e506852f98c557e (patch)
tree98910f50ad06223df3a830870ec1d14aac86e59f /gcc/shrink-wrap.c
parentfb0ab69706097f5e9c6e772e82d4f8e39c7e5955 (diff)
downloadgcc-069d7fc5a4f28fa0ee577b156e506852f98c557e.zip
gcc-069d7fc5a4f28fa0ee577b156e506852f98c557e.tar.gz
gcc-069d7fc5a4f28fa0ee577b156e506852f98c557e.tar.bz2
re PR target/63404 (gcc 5 miscompiles linux block layer)
PR target/63404 * shrink-wrap.c (move_insn_for_shrink_wrap): Don't use single_set. Restrict the set of expressions we're willing to move. From-SVN: r216096
Diffstat (limited to 'gcc/shrink-wrap.c')
-rw-r--r--gcc/shrink-wrap.c89
1 files changed, 66 insertions, 23 deletions
diff --git a/gcc/shrink-wrap.c b/gcc/shrink-wrap.c
index b1ff8a2..257812c 100644
--- a/gcc/shrink-wrap.c
+++ b/gcc/shrink-wrap.c
@@ -176,17 +176,40 @@ move_insn_for_shrink_wrap (basic_block bb, rtx_insn *insn,
basic_block next_block;
edge live_edge;
- /* Look for a simple register copy. */
- set = single_set (insn);
- if (!set)
+ /* Look for a simple register assignment. We don't use single_set here
+ because we can't deal with any CLOBBERs, USEs, or REG_UNUSED secondary
+ destinations. */
+ if (!INSN_P (insn))
+ return false;
+ set = PATTERN (insn);
+ if (GET_CODE (set) != SET)
return false;
src = SET_SRC (set);
dest = SET_DEST (set);
- if (!REG_P (src))
+ /* For the destination, we want only a register. Also disallow STACK
+ or FRAME related adjustments. They are likely part of the prologue,
+ so keep them in the entry block. */
+ if (!REG_P (dest)
+ || dest == stack_pointer_rtx
+ || dest == frame_pointer_rtx
+ || dest == hard_frame_pointer_rtx)
+ return false;
+
+ /* For the source, we want one of:
+ (1) A (non-overlapping) register
+ (2) A constant,
+ (3) An expression involving no more than one register.
+
+ That last point comes from the code following, which was originally
+ written to handle only register move operations, and still only handles
+ a single source register when checking for overlaps. Happily, the
+ same checks can be applied to expressions like (plus reg const). */
+
+ if (CONSTANT_P (src))
+ ;
+ else if (!REG_P (src))
{
- unsigned int reg_num = 0;
- unsigned int nonconstobj_num = 0;
rtx src_inner = NULL_RTX;
if (can_throw_internal (insn))
@@ -196,30 +219,50 @@ move_insn_for_shrink_wrap (basic_block bb, rtx_insn *insn,
FOR_EACH_SUBRTX_VAR (iter, array, src, ALL)
{
rtx x = *iter;
- if (REG_P (x))
+ switch (GET_RTX_CLASS (GET_CODE (x)))
{
- reg_num++;
- src_inner = x;
+ case RTX_CONST_OBJ:
+ case RTX_COMPARE:
+ case RTX_COMM_COMPARE:
+ case RTX_BIN_ARITH:
+ case RTX_COMM_ARITH:
+ case RTX_UNARY:
+ case RTX_TERNARY:
+ /* Constant or expression. Continue. */
+ break;
+
+ case RTX_OBJ:
+ case RTX_EXTRA:
+ switch (GET_CODE (x))
+ {
+ case UNSPEC:
+ case SUBREG:
+ case STRICT_LOW_PART:
+ case PC:
+ /* Ok. Continue. */
+ break;
+
+ case REG:
+ /* Fail if we see a second inner register. */
+ if (src_inner != NULL)
+ return false;
+ src_inner = x;
+ break;
+
+ default:
+ return false;
+ }
+ break;
+
+ default:
+ return false;
}
- else if (!CONSTANT_P (x) && OBJECT_P (x))
- nonconstobj_num++;
}
- if (nonconstobj_num > 0
- || reg_num > 1)
- src = NULL_RTX;
- else if (reg_num == 1)
+ if (src_inner != NULL)
src = src_inner;
}
- if (!REG_P (dest) || src == NULL_RTX
- /* STACK or FRAME related adjustment might be part of prologue.
- So keep them in the entry block. */
- || dest == stack_pointer_rtx
- || dest == frame_pointer_rtx
- || dest == hard_frame_pointer_rtx)
- return false;
-
/* Make sure that the source register isn't defined later in BB. */
if (REG_P (src))
{