This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Combine deletes non noop insns


While working on a patch to pass small structs in their proper location,
I have come up with the following problem.  It appears combine incorrectly
decides the following rtl is a noop:

(insn 41 40 42 0 (nil) (set (reg:QI 125)
        (mem/s:QI (plus:SI (reg/f:SI 3 %r3)
                (const_int -40 [0xffffffd8])) [0+0 S1 A8])) 61 {*pa.md:2805} (nil)
    (nil))

(insn 42 41 43 0 (nil) (set (zero_extract:SI (subreg:SI (reg:QI 125) 0)
            (const_int 8 [0x8])
            (const_int 24 [0x18]))
        (reg:SI 97)) 220 {insv_32} (insn_list 4 (insn_list 41 (nil)))
    (expr_list:REG_DEAD (reg:SI 97)
        (nil)))

(insn 43 42 44 0 (nil) (set (mem/s:QI (plus:SI (reg/f:SI 3 %r3)
                (const_int -40 [0xffffffd8])) [0+0 S1 A8])
        (reg:QI 125)) 61 {*pa.md:2805} (insn_list 42 (nil))
    (expr_list:REG_DEAD (reg:QI 125)
        (nil)))

The rtl is from the life pass.  SI 97 is set from the first argument
register and its value is not known.  While I can see that this
sequence can be simplified, the set in insn 43 can't be eliminated.

This rtl was generated with the following test program:

struct S5 { unsigned char i[5]; };
extern void check5 (struct S5 *, int);
void test5 (struct S5 s5)
{
  check5 (&s5, 64);
}

It is a simplification from struct-by-value-1.c.

struct-by-value-1.c is the only remaining struct problem left on the
32-bit port when the patch below is applied.  It passes at -O0 but not
at higher optimization levels.

Anybody got any ideas why the above rtl confuses combine?

Dave
-- 
J. David Anglin                                  dave.anglin@nrc.ca
National Research Council of Canada              (613) 990-0752 (FAX: 952-6605)

Index: function.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/function.c,v
retrieving revision 1.381
diff -u -3 -p -r1.381 function.c
--- function.c	29 Aug 2002 19:20:00 -0000	1.381
+++ function.c	10 Sep 2002 16:36:45 -0000
@@ -255,10 +255,8 @@ static int instantiate_virtual_regs_1 PA
 static void delete_handlers	PARAMS ((void));
 static void pad_to_arg_alignment PARAMS ((struct args_size *, int,
 					  struct args_size *));
-#ifndef ARGS_GROW_DOWNWARD
 static void pad_below		PARAMS ((struct args_size *, enum machine_mode,
 					 tree));
-#endif
 static rtx round_trampoline_addr PARAMS ((rtx));
 static rtx adjust_trampoline_addr PARAMS ((rtx));
 static tree *identify_blocks_1	PARAMS ((rtx, tree *, tree *, tree *));
@@ -5244,6 +5242,9 @@ locate_and_pad_parm (passed_mode, type, 
     = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
   enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
+#ifdef ARGS_GROW_DOWNWARD
+  tree s2 = sizetree;
+#endif
 
 #ifdef REG_PARM_STACK_SPACE
   /* If we have found a stack parm before we reach the end of the
@@ -5289,13 +5290,20 @@ locate_and_pad_parm (passed_mode, type, 
       offset_ptr->constant = -initial_offset_ptr->constant;
       offset_ptr->var = 0;
     }
+
   if (where_pad != none
       && (!host_integerp (sizetree, 1)
 	  || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % PARM_BOUNDARY))
-    sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
-  SUB_PARM_SIZE (*offset_ptr, sizetree);
-  if (where_pad != downward)
+    s2 = round_up (s2, PARM_BOUNDARY / BITS_PER_UNIT);
+  SUB_PARM_SIZE (*offset_ptr, s2);
+
+  if (!in_regs
+#ifdef REG_PARM_STACK_SPACE
+      || REG_PARM_STACK_SPACE (fndecl) > 0
+#endif
+     )
     pad_to_arg_alignment (offset_ptr, boundary, alignment_pad);
+
   if (initial_offset_ptr->var)
     arg_size_ptr->var = size_binop (MINUS_EXPR,
 				    size_binop (MINUS_EXPR,
@@ -5307,6 +5315,14 @@ locate_and_pad_parm (passed_mode, type, 
     arg_size_ptr->constant = (-initial_offset_ptr->constant
 			      - offset_ptr->constant);
 
+  /* Pad_below needs the pre-rounded size to know how much to pad below.
+     We only pad BLKmode parameters which are not in registers.  BLKmode
+     parameters passed in registers have their padding done elsewhere.  */
+  if (where_pad == downward
+      && passed_mode == BLKmode
+      && !in_regs)
+    pad_below (offset_ptr, passed_mode, sizetree);
+
 #else /* !ARGS_GROW_DOWNWARD */
   if (!in_regs
 #ifdef REG_PARM_STACK_SPACE
@@ -5392,7 +5408,6 @@ pad_to_arg_alignment (offset_ptr, bounda
     }
 }
 
-#ifndef ARGS_GROW_DOWNWARD
 static void
 pad_below (offset_ptr, passed_mode, sizetree)
      struct args_size *offset_ptr;
@@ -5420,7 +5435,6 @@ pad_below (offset_ptr, passed_mode, size
 	}
     }
 }
-#endif
 
 /* Walk the tree of blocks describing the binding levels within a function
    and warn about uninitialized variables.
Index: config/pa/pa-64.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa-64.h,v
retrieving revision 1.11
diff -u -3 -p -r1.11 pa-64.h
--- config/pa/pa-64.h	4 Sep 2002 18:09:32 -0000	1.11
+++ config/pa/pa-64.h	10 Sep 2002 16:36:45 -0000
@@ -88,8 +88,26 @@ Boston, MA 02111-1307, USA.  */
 #undef STATIC_CHAIN_REGNUM
 #define STATIC_CHAIN_REGNUM 31
 
-/* Nonzero if we do not know how to pass TYPE solely in registers.  */
-#define MUST_PASS_IN_STACK(MODE,TYPE) \
-  ((TYPE) != 0							\
-   && (TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST		\
-       || TREE_ADDRESSABLE (TYPE)))
+/* Define to 1 on a big-endian system to pass structures in a little-endian
+   manner.  The HP ABI specifies that structures and other aggregates are to
+   be left-justified (i.e., padded on the right).  The default padding
+   for big-endian is to right-justify structures that have a size smaller
+   than PARM_BOUNDARY and to left-justify larger ones.  Now, the order
+   of the HP argument registers are reversed, so we need to use a PARALLEL
+   to load the registers in the correct order.  This changes the default
+   justification to right-justication because emit_group_{load,store} are
+   now used.  So, we define FUNCTION_ARG_REG_LITTLE_ENDIAN to reverse this
+   behavior and get back to left-justification.  However, we don't use
+   a PARALLEL for structures smaller than PARM_BOUNDARY since this results
+   in incorrect justification.  For these, we return a REG.  Yikes!  */
+
+#define FUNCTION_ARG_REG_LITTLE_ENDIAN 1
+
+/* If defined, a C expression which determines whether the default
+   implementation of va_arg will attempt to pad down before reading the
+   next argument, if that argument is smaller than its aligned space as
+   controlled by PARM_BOUNDARY.  If this macro is not defined, all such
+   arguments are padded down when BYTES_BIG_ENDIAN is true.  We don't
+   want aggregrates padded down.  */
+
+#define PAD_VARARGS_DOWN (!AGGREGATE_TYPE_P (type))
Index: config/pa/pa.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa.c,v
retrieving revision 1.179
diff -u -3 -p -r1.179 pa.c
--- config/pa/pa.c	4 Sep 2002 18:09:32 -0000	1.179
+++ config/pa/pa.c	10 Sep 2002 16:36:46 -0000
@@ -5089,22 +5089,26 @@ function_arg_padding (mode, type)
      enum machine_mode mode;
      tree type;
 {
-  int size;
-
   if (mode == BLKmode)
     {
-      if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
-	size = int_size_in_bytes (type) * BITS_PER_UNIT;
+      /* Return none if left or right justification is not required.  */
+      if (type
+	  && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+	  && (int_size_in_bytes (type) * BITS_PER_UNIT) % PARM_BOUNDARY == 0)
+	return none;
+
+      if (TARGET_64BIT)
+	/* The 64-bit runtime specifies left justification.  */
+        return upward;
       else
-	return upward;		/* Don't know if this is right, but */
-				/* same as old definition.  */
+	/* The 32-bit runtime architecture specifies right justification.
+	   This is highly non standard for blocks larger than one word and
+	   needs special handling.  */
+	return downward;
     }
-  else
-    size = GET_MODE_BITSIZE (mode);
-  if (size < PARM_BOUNDARY)
+
+  if (GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
     return downward;
-  else if (size % PARM_BOUNDARY)
-    return upward;
   else
     return none;
 }
@@ -5201,8 +5205,11 @@ hppa_va_arg (valist, type)
 
   if (TARGET_64BIT)
     {
-      /* Every argument in PA64 is passed by value (including large structs).
-         Arguments with size greater than 8 must be aligned 0 MOD 16.  */
+      /* Every argument in PA64 is supposed to be passed by value (including
+	 large structs).  However, as a GCC extension, we pass zero and
+	 variable sized types by reference.
+
+	 Arguments with a size greater than 8 must be aligned 0 MOD 16.  */
 
       size = int_size_in_bytes (type);
       if (size > UNITS_PER_WORD)
@@ -5215,7 +5222,21 @@ hppa_va_arg (valist, type)
           TREE_SIDE_EFFECTS (t) = 1;
           expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
         }
-      return std_expand_builtin_va_arg (valist, type);
+      else if (size <= 0)
+	{
+	  t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
+		     build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
+	  TREE_SIDE_EFFECTS (t) = 1;
+
+	  pptr = build_pointer_type (ptr);
+	  t = build1 (NOP_EXPR, pptr, t);
+	  TREE_SIDE_EFFECTS (t) = 1;
+
+	  t = build1 (INDIRECT_REF, ptr, t);
+	  TREE_SIDE_EFFECTS (t) = 1;
+	}
+      else
+	return std_expand_builtin_va_arg (valist, type);
     }
 
   /* Compute the rounded size of the type.  */
@@ -5224,8 +5245,8 @@ hppa_va_arg (valist, type)
 
   ptr = build_pointer_type (type);
 
-  /* "Large" types are passed by reference.  */
-  if (size > 8)
+  /* "Large" and variable sized types are passed by reference.  */
+  if (size > 8 || size <= 0)
     {
       t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
 		 build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
@@ -5263,7 +5284,7 @@ hppa_va_arg (valist, type)
     }
 
   /* Calculate!  */
-  return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
+  return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
 }
 
 
@@ -7446,28 +7467,37 @@ function_arg (cum, mode, type, named, in
      int incoming;
 {
   int max_arg_words = (TARGET_64BIT ? 8 : 4);
-  int arg_size = FUNCTION_ARG_SIZE (mode, type);
   int alignment = 0;
+  int arg_size;
   int fpr_reg_base;
   int gpr_reg_base;
   rtx retval;
 
+  if (mode == VOIDmode)
+    return NULL_RTX;
+
+  arg_size = FUNCTION_ARG_SIZE (mode, type);
+
+  /* This might happen if someone tries to pass a variable sized structure
+     or union.  */
+  if (arg_size == 0)
+    abort ();
+
+  /* If this arg would be passed partially or totally on the stack, then
+     this routine should return zero.  FUNCTION_ARG_PARTIAL_NREGS will
+     handle arguments which are split between regs and stack slots if
+     the ABI mandates split arguments.  */
   if (! TARGET_64BIT)
     {
-      /* If this arg would be passed partially or totally on the stack, then
-         this routine should return zero.  FUNCTION_ARG_PARTIAL_NREGS will
-         handle arguments which are split between regs and stack slots if
-         the ABI mandates split arguments.  */
-      if (cum->words + arg_size > max_arg_words
-          || mode == VOIDmode)
+      /* The 32-bit ABI does not split arguments.  */
+      if (cum->words + arg_size > max_arg_words)
 	return NULL_RTX;
     }
   else
     {
       if (arg_size > 1)
 	alignment = cum->words & 1;
-      if (cum->words + alignment >= max_arg_words
-	  || mode == VOIDmode)
+      if (cum->words + alignment >= max_arg_words)
 	return NULL_RTX;
     }
 
@@ -7533,6 +7563,22 @@ function_arg (cum, mode, type, named, in
 	    {
 	      gpr_reg_base = 25;
 	      fpr_reg_base = 34;
+	    }
+
+	  /* Structures 5 to 8 bytes in size are passed in the general
+	     registers in the same manner as other non floating-point
+	     objects.  The data is right-justified and zero-extended
+	     to 64 bits.  We use a PARALLEL instead of a REG to obtain
+	     the correct justifiction.  PARALLELs use emit_group_{load,
+	     store} and these functions right-justify for big-endian.  */
+	  if (mode == BLKmode)
+	    {
+	      rtx loc[1];
+
+	      loc[0] = gen_rtx_EXPR_LIST (VOIDmode,
+					  gen_rtx_REG (DImode, gpr_reg_base),
+					  const0_rtx);
+	      return gen_rtx_PARALLEL (mode, gen_rtvec_v (1, loc));
 	    }
 	}
       else
Index: config/pa/pa.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa.h,v
retrieving revision 1.166
diff -u -3 -p -r1.166 pa.h
--- config/pa/pa.h	21 Aug 2002 02:41:50 -0000	1.166
+++ config/pa/pa.h	10 Sep 2002 16:36:46 -0000
@@ -407,7 +407,7 @@ extern int target_flags;
 
 /* Largest alignment required for any stack parameter, in bits.
    Don't define this if it is equal to PARM_BOUNDARY */
-#define MAX_PARM_BOUNDARY 64
+#define MAX_PARM_BOUNDARY (2 * PARM_BOUNDARY)
 
 /* Boundary (in *bits*) on which stack pointer is always aligned;
    certain optimizations in combine depend on this.
@@ -506,9 +506,14 @@ extern struct rtx_def *hppa_pic_save_rtx
    PA64 ABI says that objects larger than 128 bits are returned in memory.
    Note, int_size_in_bytes can return -1 if the size of the object is
    variable or larger than the maximum value that can be expressed as
-   a HOST_WIDE_INT.  */
+   a HOST_WIDE_INT.   It can also return zero for an empty type.  The
+   simplest way to handle variable and empty types is to pass them in
+   memory.  This avoids problems in defining the boundaries of argument
+   slots, allocating registers, etc.  */
 #define RETURN_IN_MEMORY(TYPE)	\
-  ((unsigned HOST_WIDE_INT) int_size_in_bytes (TYPE) > (TARGET_64BIT ? 16 : 8))
+  ((TYPE) == 0							\
+   || int_size_in_bytes (TYPE) > (TARGET_64BIT ? 16 : 8)	\
+   || int_size_in_bytes (TYPE) <= 0)
 
 /* Register in which address to store a structure value
    is passed to a function.  */
@@ -681,16 +686,18 @@ extern struct rtx_def *hppa_pic_save_rtx
    otherwise, FUNC is 0.  */
 
 /* On the HP-PA the value is found in register(s) 28(-29), unless
-   the mode is SF or DF. Then the value is returned in fr4 (32, ) */
+   the mode is SF or DF. Then the value is returned in fr4 (32).  */
 
 /* This must perform the same promotions as PROMOTE_MODE, else
    PROMOTE_FUNCTION_RETURN will not work correctly.  */
-#define FUNCTION_VALUE(VALTYPE, FUNC)				\
-  gen_rtx_REG (((INTEGRAL_TYPE_P (VALTYPE)			\
-		 && TYPE_PRECISION (VALTYPE) < BITS_PER_WORD)	\
-		|| POINTER_TYPE_P (VALTYPE))			\
-	       ? word_mode : TYPE_MODE (VALTYPE),		\
-	       TREE_CODE (VALTYPE) == REAL_TYPE && !TARGET_SOFT_FLOAT ? 32 : 28)
+#define FUNCTION_VALUE(VALTYPE, FUNC)					\
+  gen_rtx_REG (((INTEGRAL_TYPE_P (VALTYPE)				\
+		 && TYPE_PRECISION (VALTYPE) < BITS_PER_WORD)		\
+		|| POINTER_TYPE_P (VALTYPE))				\
+	        ? word_mode : TYPE_MODE (VALTYPE),			\
+	       (TREE_CODE (VALTYPE) == REAL_TYPE			\
+		&& TYPE_MODE (VALTYPE) != TFmode			\
+		&& !TARGET_SOFT_FLOAT) ? 32 : 28)
 
 /* Define how to find the value returned by a library function
    assuming the value has mode MODE.  */
@@ -745,7 +752,9 @@ struct hppa_args {int words, nargs_proto
   (CUM).indirect = 0,				\
   (CUM).nargs_prototype = 1000
 
-/* Figure out the size in words of the function argument.  */
+/* Figure out the size in words of the function argument.  The size
+   returned by this macro should always be greater than zero because
+   we pass variable and zero sized objects by reference.  */
 
 #define FUNCTION_ARG_SIZE(MODE, TYPE)	\
   ((((MODE) != BLKmode \
@@ -817,6 +826,17 @@ struct hppa_args {int words, nargs_proto
 #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
   function_arg (&CUM, MODE, TYPE, NAMED, 0)
 
+/* This needs to be set to force structure arguments with a single
+   field to be treated as structures and not as the type of their
+   field.  */
+#define MEMBER_TYPE_FORCES_BLK(FIELD, MODE) 1
+
+/* Nonzero if we do not know how to pass TYPE solely in registers.  */
+#define MUST_PASS_IN_STACK(MODE,TYPE) \
+  ((TYPE) != 0							\
+   && (TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST		\
+       || TREE_ADDRESSABLE (TYPE)))
+
 #define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \
   function_arg (&CUM, MODE, TYPE, NAMED, 1)
 
@@ -833,33 +853,37 @@ struct hppa_args {int words, nargs_proto
    bits, of an argument with the specified mode and type.  If it is
    not defined,  `PARM_BOUNDARY' is used for all arguments.  */
 
-#define FUNCTION_ARG_BOUNDARY(MODE, TYPE)			\
-  (((TYPE) != 0)						\
-   ? ((integer_zerop (TYPE_SIZE (TYPE))				\
-       || ! TREE_CONSTANT (TYPE_SIZE (TYPE)))			\
-      ? BITS_PER_UNIT						\
-      : (((int_size_in_bytes (TYPE)) + UNITS_PER_WORD - 1)	\
-	 / UNITS_PER_WORD) * BITS_PER_WORD)			\
-   : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY)		\
-      ? PARM_BOUNDARY : GET_MODE_ALIGNMENT(MODE)))
-
-/* Arguments larger than eight bytes are passed by invisible reference */
+/* Arguments larger than one word are double word aligned.  */
 
-/* PA64 does not pass anything by invisible reference.  */
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE)				\
+  (((TYPE)								\
+    ? (integer_zerop (TYPE_SIZE (TYPE))					\
+       || !TREE_CONSTANT (TYPE_SIZE (TYPE))				\
+       || int_size_in_bytes (TYPE) <= UNITS_PER_WORD)			\
+    : GET_MODE_SIZE(MODE) <= UNITS_PER_WORD)				\
+   ? PARM_BOUNDARY : MAX_PARM_BOUNDARY)
+
+/* In the 32-bit runtime, arguments larger than eight bytes are passed
+   by invisible reference.  As a GCC extension, we also pass anything
+   with a zero or variable size by reference.
+
+   The 64-bit runtime does not describe passing any types by invisible
+   reference.  The internals of GCC can't currently handle passing
+   empty structures, and zero or variable length arrays when they are
+   not passed entirely on the stack or by reference.  Thus, as a GCC
+   extension, we pass these types by reference.  The HP compiler doesn't
+   support these types, so hopefully there shouldn't be any compatibility
+   issues.  This may have to be revisited when HP releases a C99 compiler
+   or updates the ABI.  */
 #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED)		\
   (TARGET_64BIT								\
-   ? 0									\
-   : (((TYPE) && int_size_in_bytes (TYPE) > 8)				\
+   ? ((TYPE) && int_size_in_bytes (TYPE) <= 0)				\
+   : (((TYPE) && (int_size_in_bytes (TYPE) > 8				\
+		  || int_size_in_bytes (TYPE) <= 0))			\
       || ((MODE) && GET_MODE_SIZE (MODE) > 8)))
  
-/* PA64 does not pass anything by invisible reference.
-   This should be undef'ed for 64bit, but we'll see if this works. The
-   problem is that we can't test TARGET_64BIT from the preprocessor.  */
-#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \
-  (TARGET_64BIT							\
-   ? 0								\
-   : (((TYPE) && int_size_in_bytes (TYPE) > 8)			\
-      || ((MODE) && GET_MODE_SIZE (MODE) > 8)))
+#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) 		\
+  FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED)
 
 
 extern GTY(()) rtx hppa_compare_op0;
Index: testsuite/gcc.dg/struct-ret-1.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/testsuite/gcc.dg/struct-ret-1.c,v
retrieving revision 1.2
diff -u -3 -p -r1.2 struct-ret-1.c
--- testsuite/gcc.dg/struct-ret-1.c	16 Dec 1998 22:23:30 -0000	1.2
+++ testsuite/gcc.dg/struct-ret-1.c	10 Sep 2002 16:36:46 -0000
@@ -1,5 +1,6 @@
 /* { dg-do run { target hppa*-*-* } } */
 /* { dg-options { -O2 } { target hppa*-*-* } } */
+extern void exit (int);
 typedef struct {
         int             x;
         int             y;
@@ -16,7 +17,7 @@ main(int argc, char *argv[])
         if (printPoints(toPoint(0, 0), toPoint(1000, 1000)) != 1)
                 abort();
         else
-                exit();
+                exit(0);
 
         return 0;
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]