Fix subreg fallout in simplify_unary_operation()

Richard Sandiford rsandifo@redhat.com
Thu Jan 8 21:57:00 GMT 2004


This patch fixes what appears to be a latent bug in
simplify_unary_operation().  It was exposed by the
recent simplify_immed_subreg patch and is causing
mips64vrel-elf builds of newlib to fail.

A reduced test case is:

    unsigned long long foo ()
    {
      union { double d; unsigned int i[2]; } u;
      u.d = 1.0;
      return u.i[0];
    }

compiled with -meb -mips16 -mabi=o64.

We enter combine with:

    (set (subreg:DF (reg:DI u) 0)
         (mem:DF (symbol_ref "$LC0")))

    (set (reg:DI r1)
         (lshiftrt:DI (reg:DI u)
                      (const_int 32)))

and combine.c:subst() is called on to simplify:

    (lshiftrt:DI (mem:DI (symbol_ref "$LC0"))
                 (const_int 32))

Specifically, the loop is:

  /* Try to simplify X.  If the simplification changed the code, it is likely
     that further simplification will help, so loop, but limit the number
     of repetitions that will be performed.  */

  for (i = 0; i < 4; i++)
    {
      /* If X is sufficiently simple, don't bother trying to do anything
         with it.  */
      if (code != CONST_INT && code != REG && code != CLOBBER)
        x = combine_simplify_rtx (x, op0_mode, i == 3, in_dest);

      if (GET_CODE (x) == code)
        break;

      code = GET_CODE (x);

      /* We no longer know the original mode of operand 0 since we
         have changed the form of X)  */
      op0_mode = VOIDmode;
    }

First time around, the expression is correctly simplified to:

    (zero_extend:DI (mem:SI (symbol_ref "$LC0")))

and the next iteration tries to simplify it again.  This time, because
of the code at the end of the loop, op0_mode will be VOIDmode.  This
results in a call to:

    simplify_unary_operation (ZERO_EXTEND, DImode,
                              (mem:SI (symbol_ref "$LC0")), VOIDmode)

and the first thing it does is:

    rtx trueop = avoid_constant_pool_reference (op);

Here, OP is an SImode reference to a DFmode constant pool entry.  Before
the simplify_immed_subreg change, the subreg code couldn't simplify this
reference, but now it can.  trueop therefore ends up being a CONST_INT
for the high 32 bits of the double constant.  This leads to an abort in
the code that handles CONST_INTs:

  if (GET_CODE (trueop) == CONST_INT
      && width <= HOST_BITS_PER_WIDE_INT && width > 0)
    {
      [...]
        case ZERO_EXTEND:
          /* When zero-extending a CONST_INT, we need to know its
             original mode.  */
          if (op_mode == VOIDmode)
            abort ();

Perhaps it could be argued that the caller shouldn't pass VOIDmode here.
But the function _was_ given a correct mode by means of the original
MEM operand.  The interface to the function seems more robust if it
falls back on GET_MODE (op) when op0_mode is VOIDmode.  That's what
the caller would have to do anyway.

Oh well.  Long story for a short patch...

Tested on mips64vrel-elf, fixes the build failure.  Since this was my
first build this year, I don't have fully-up-to-date test results to
compare against, but the failures are in line with what I was seeing
a few weeks ago.

OK to install?

Richard


	* simplify-rtx.c (simplify_unary_operation): Use OP's mode instead
	of OP_MODE when the latter is VOIDmode.

Index: simplify-rtx.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/simplify-rtx.c,v
retrieving revision 1.168
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.168 simplify-rtx.c
--- simplify-rtx.c	6 Jan 2004 22:51:00 -0000	1.168
+++ simplify-rtx.c	7 Jan 2004 22:26:54 -0000
@@ -375,6 +375,8 @@ simplify_unary_operation (enum rtx_code 
   unsigned int width = GET_MODE_BITSIZE (mode);
   rtx trueop = avoid_constant_pool_reference (op);
 
+  if (op_mode == VOIDmode)
+    op_mode = GET_MODE (op);
   if (code == VEC_DUPLICATE)
     {
       if (!VECTOR_MODE_P (mode))



More information about the Gcc-patches mailing list