OSDN Git Service

* jump.c (jump_optimize, follow_jumps, mark_jump_label): Disable some
[pf3gnuchains/gcc-fork.git] / gcc / explow.c
index 64431a6..db0fbc8 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines for manipulating rtx's in semantically interesting ways.
-   Copyright (C) 1987, 1991, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1987, 91, 94, 95, 96, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -29,6 +30,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "insn-flags.h"
 #include "insn-codes.h"
 
+static rtx break_out_memory_refs       PROTO((rtx));
+
 /* Return an rtx for the sum of X and the integer C.
 
    This function should be used via the `plus_constant' macro.  */
@@ -274,22 +277,77 @@ break_out_memory_refs (x)
   if (GET_CODE (x) == MEM
       || (CONSTANT_P (x) && CONSTANT_ADDRESS_P (x)
          && GET_MODE (x) != VOIDmode))
-    {
-      register rtx temp = force_reg (GET_MODE (x), x);
-      mark_reg_pointer (temp);
-      x = temp;
-    }
+    x = force_reg (GET_MODE (x), x);
   else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
           || GET_CODE (x) == MULT)
     {
       register rtx op0 = break_out_memory_refs (XEXP (x, 0));
       register rtx op1 = break_out_memory_refs (XEXP (x, 1));
+
       if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
        x = gen_rtx (GET_CODE (x), Pmode, op0, op1);
     }
+
   return x;
 }
 
+#ifdef POINTERS_EXTEND_UNSIGNED
+
+/* Given X, a memory address in ptr_mode, convert it to an address
+   in Pmode, or vice versa (TO_MODE says which way).  We take advantage of
+   the fact that pointers are not allowed to overflow by commuting arithmetic
+   operations over conversions so that address arithmetic insns can be
+   used.  */
+
+rtx
+convert_memory_address (to_mode, x)
+     enum machine_mode to_mode;
+     rtx x;
+{
+  enum machine_mode from_mode = to_mode == ptr_mode ? Pmode : ptr_mode;
+  rtx temp;
+
+  /* Here we handle some special cases.  If none of them apply, fall through
+     to the default case.  */
+  switch (GET_CODE (x))
+    {
+    case CONST_INT:
+    case CONST_DOUBLE:
+      return x;
+
+    case LABEL_REF:
+      return gen_rtx (LABEL_REF, to_mode, XEXP (x, 0));
+
+    case SYMBOL_REF:
+      temp = gen_rtx (SYMBOL_REF, to_mode, XSTR (x, 0));
+      SYMBOL_REF_FLAG (temp) = SYMBOL_REF_FLAG (x);
+      CONSTANT_POOL_ADDRESS_P (temp) = CONSTANT_POOL_ADDRESS_P (x);
+      return temp;
+
+    case CONST:
+      return gen_rtx (CONST, to_mode, 
+                     convert_memory_address (to_mode, XEXP (x, 0)));
+
+    case PLUS:
+    case MULT:
+      /* For addition the second operand is a small constant, we can safely
+        permute the converstion and addition operation.  We can always safely
+        permute them if we are making the address narrower.  In addition,
+        always permute the operations if this is a constant.  */
+      if (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (from_mode)
+         || (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT
+             && (INTVAL (XEXP (x, 1)) + 20000 < 40000
+                 || CONSTANT_P (XEXP (x, 0)))))
+       return gen_rtx (GET_CODE (x), to_mode, 
+                       convert_memory_address (to_mode, XEXP (x, 0)),
+                       convert_memory_address (to_mode, XEXP (x, 1)));
+    }
+
+  return convert_modes (to_mode, from_mode,
+                       x, POINTERS_EXTEND_UNSIGNED);
+}
+#endif
+
 /* Given a memory address or facsimile X, construct a new address,
    currently equivalent, that is stable: future stores won't change it.
 
@@ -340,6 +398,11 @@ memory_address (mode, x)
 {
   register rtx oldx = x;
 
+#ifdef POINTERS_EXTEND_UNSIGNED
+  if (GET_MODE (x) == ptr_mode)
+    x = convert_memory_address (Pmode, x);
+#endif
+
   /* By passing constant addresses thru registers
      we get a chance to cse them.  */
   if (! cse_not_expected && CONSTANT_P (x) && CONSTANT_ADDRESS_P (x))
@@ -402,7 +465,7 @@ memory_address (mode, x)
            }
        }
 
-      if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS)
+      else if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS)
        x = force_operand (x, NULL_RTX);
 
       /* If we have a register that's an invalid address,
@@ -416,25 +479,36 @@ memory_address (mode, x)
        x = force_reg (Pmode, x);
 
       goto done;
-    }
 
- win2:
-  x = oldx;
- win:
-  if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG
-      /* Don't copy an addr via a reg if it is one of our stack slots.  */
-      && ! (GET_CODE (x) == PLUS
-           && (XEXP (x, 0) == virtual_stack_vars_rtx
-               || XEXP (x, 0) == virtual_incoming_args_rtx)))
-    {
-      if (general_operand (x, Pmode))
-       x = force_reg (Pmode, x);
-      else
-       x = force_operand (x, NULL_RTX);
+    win2:
+      x = oldx;
+    win:
+      if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG
+         /* Don't copy an addr via a reg if it is one of our stack slots.  */
+         && ! (GET_CODE (x) == PLUS
+               && (XEXP (x, 0) == virtual_stack_vars_rtx
+                   || XEXP (x, 0) == virtual_incoming_args_rtx)))
+       {
+         if (general_operand (x, Pmode))
+           x = force_reg (Pmode, x);
+         else
+           x = force_operand (x, NULL_RTX);
+       }
     }
 
  done:
 
+  /* If we didn't change the address, we are done.  Otherwise, mark
+     a reg as a pointer if we have REG or REG + CONST_INT.  */
+  if (oldx == x)
+    return x;
+  else if (GET_CODE (x) == REG)
+    mark_reg_pointer (x, 1);
+  else if (GET_CODE (x) == PLUS
+          && GET_CODE (XEXP (x, 0)) == REG
+          && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    mark_reg_pointer (XEXP (x, 0), 1);
+
   /* OLDX may have been the address on a temporary.  Update the address
      to indicate that X is now used.  */
   update_temp_slot_address (oldx, x);
@@ -569,15 +643,19 @@ force_reg (mode, x)
      enum machine_mode mode;
      rtx x;
 {
-  register rtx temp, insn;
+  register rtx temp, insn, set;
 
   if (GET_CODE (x) == REG)
     return x;
   temp = gen_reg_rtx (mode);
   insn = emit_move_insn (temp, x);
+
   /* Let optimizers know that TEMP's value never changes
-     and that X can be substituted for it.  */
-  if (CONSTANT_P (x))
+     and that X can be substituted for it.  Don't get confused
+     if INSN set something else (such as a SUBREG of TEMP).  */
+  if (CONSTANT_P (x)
+      && (set = single_set (insn)) != 0
+      && SET_DEST (set) == temp)
     {
       rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
 
@@ -624,6 +702,49 @@ copy_to_suggested_reg (x, target, mode)
   return temp;
 }
 \f
+/* Return the mode to use to store a scalar of TYPE and MODE.
+   PUNSIGNEDP points to the signedness of the type and may be adjusted
+   to show what signedness to use on extension operations.
+
+   FOR_CALL is non-zero if this call is promoting args for a call.  */
+
+enum machine_mode
+promote_mode (type, mode, punsignedp, for_call)
+     tree type;
+     enum machine_mode mode;
+     int *punsignedp;
+     int for_call;
+{
+  enum tree_code code = TREE_CODE (type);
+  int unsignedp = *punsignedp;
+
+#ifdef PROMOTE_FOR_CALL_ONLY
+  if (! for_call)
+    return mode;
+#endif
+
+  switch (code)
+    {
+#ifdef PROMOTE_MODE
+    case INTEGER_TYPE:   case ENUMERAL_TYPE:   case BOOLEAN_TYPE:
+    case CHAR_TYPE:      case REAL_TYPE:       case OFFSET_TYPE:
+      PROMOTE_MODE (mode, unsignedp, type);
+      break;
+#endif
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+    case REFERENCE_TYPE:
+    case POINTER_TYPE:
+      mode = Pmode;
+      unsignedp = POINTERS_EXTEND_UNSIGNED;
+      break;
+#endif
+    }
+
+  *punsignedp = unsignedp;
+  return mode;
+}
+\f
 /* Adjust the stack pointer by ADJUST (an rtx for a number of bytes).
    This pops when ADJUST is positive.  ADJUST need not be constant.  */
 
@@ -695,7 +816,12 @@ round_push (size)
     }
   else
     {
-      size = expand_divmod (0, CEIL_DIV_EXPR, Pmode, size, GEN_INT (align),
+      /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+        but we know it can't.  So add ourselves and then do
+        TRUNC_DIV_EXPR.  */
+      size = expand_binop (Pmode, add_optab, size, GEN_INT (align - 1),
+                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
+      size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, GEN_INT (align),
                            NULL_RTX, 1);
       size = expand_mult (Pmode, size, GEN_INT (align), NULL_RTX, 1);
     }
@@ -868,6 +994,15 @@ allocate_dynamic_stack_space (size, target, known_align)
      rtx target;
      int known_align;
 {
+  /* If we're asking for zero bytes, it doesn't matter what we point
+     to since we can't dereference it.  But return a reasonable
+     address anyway.  */
+  if (size == const0_rtx)
+    return virtual_stack_dynamic_rtx;
+
+  /* Otherwise, show we're calling alloca or equivalent.  */
+  current_function_calls_alloca = 1;
+
   /* Ensure the size is in the proper mode.  */
   if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
     size = convert_to_mode (Pmode, size, 1);
@@ -885,24 +1020,13 @@ allocate_dynamic_stack_space (size, target, known_align)
      If we have to align, we must leave space in SIZE for the hole
      that might result from the alignment operation.  */
 
-#if defined (STACK_DYNAMIC_OFFSET) || defined(STACK_POINTER_OFFSET) || defined (ALLOCATE_OUTGOING_ARGS)
-#define MUST_ALIGN
-#endif
-
-#if ! defined (MUST_ALIGN) && (!defined(STACK_BOUNDARY) || STACK_BOUNDARY < BIGGEST_ALIGNMENT)
-#define MUST_ALIGN
+#if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET) || ! defined (STACK_BOUNDARY)
+#define MUST_ALIGN 1
+#else
+#define MUST_ALIGN (STACK_BOUNDARY < BIGGEST_ALIGNMENT)
 #endif
 
-#ifdef MUST_ALIGN
-
-#if 0 /* It turns out we must always make extra space, if MUST_ALIGN
-        because we must always round the address up at the end,
-        because we don't know whether the dynamic offset
-        will mess up the desired alignment.  */
-  /* If we have to round the address up regardless of known_align,
-     make extra space regardless, also.  */
-  if (known_align % BIGGEST_ALIGNMENT != 0)
-#endif
+  if (MUST_ALIGN)
     {
       if (GET_CODE (size) == CONST_INT)
        size = GEN_INT (INTVAL (size)
@@ -913,8 +1037,6 @@ allocate_dynamic_stack_space (size, target, known_align)
                             NULL_RTX, 1, OPTAB_LIB_WIDEN);
     }
 
-#endif
-
 #ifdef SETJMP_VIA_SAVE_AREA
   /* If setjmp restores regs from a save area in the stack frame,
      avoid clobbering the reg save area.  Note that the offset of
@@ -947,8 +1069,8 @@ allocate_dynamic_stack_space (size, target, known_align)
 #ifdef STACK_BOUNDARY
   /* If we added a variable amount to SIZE,
      we can no longer assume it is aligned.  */
-#if !defined (SETJMP_VIA_SAVE_AREA) && !defined (MUST_ALIGN)
-  if (known_align % STACK_BOUNDARY != 0)
+#if !defined (SETJMP_VIA_SAVE_AREA)
+  if (MUST_ALIGN || known_align % STACK_BOUNDARY != 0)
 #endif
     size = round_push (size);
 #endif
@@ -960,7 +1082,7 @@ allocate_dynamic_stack_space (size, target, known_align)
       || REGNO (target) < FIRST_PSEUDO_REGISTER)
     target = gen_reg_rtx (Pmode);
 
-  mark_reg_pointer (target);
+  mark_reg_pointer (target, known_align / BITS_PER_UNIT);
 
 #ifndef STACK_GROWS_DOWNWARD
   emit_move_insn (target, virtual_stack_dynamic_rtx);
@@ -975,6 +1097,8 @@ allocate_dynamic_stack_space (size, target, known_align)
       enum machine_mode mode
        = insn_operand_mode[(int) CODE_FOR_allocate_stack][0];
 
+      size = convert_modes (mode, ptr_mode, size, 1);
+
       if (insn_operand_predicate[(int) CODE_FOR_allocate_stack][0]
          && ! ((*insn_operand_predicate[(int) CODE_FOR_allocate_stack][0])
                (size, mode)))
@@ -984,28 +1108,30 @@ allocate_dynamic_stack_space (size, target, known_align)
     }
   else
 #endif
-    anti_adjust_stack (size);
+    {
+      size = convert_modes (Pmode, ptr_mode, size, 1);
+      anti_adjust_stack (size);
+    }
 
 #ifdef STACK_GROWS_DOWNWARD
   emit_move_insn (target, virtual_stack_dynamic_rtx);
 #endif
 
-#ifdef MUST_ALIGN
-#if 0  /* Even if we know the stack pointer has enough alignment,
-         there's no way to tell whether virtual_stack_dynamic_rtx shares that
-         alignment, so we still need to round the address up.  */
-  if (known_align % BIGGEST_ALIGNMENT != 0)
-#endif
+  if (MUST_ALIGN)
     {
-      target = expand_divmod (0, CEIL_DIV_EXPR, Pmode, target,
+      /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+        but we know it can't.  So add ourselves and then do
+        TRUNC_DIV_EXPR.  */
+      target = expand_binop (Pmode, add_optab, target,
+                            GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
+                            NULL_RTX, 1, OPTAB_LIB_WIDEN);
+      target = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, target,
                              GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
                              NULL_RTX, 1);
-
       target = expand_mult (Pmode, target,
                            GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
                            NULL_RTX, 1);
     }
-#endif
   
   /* Some systems require a particular insn to refer to the stack
      to make the pages exist.  */
@@ -1014,6 +1140,10 @@ allocate_dynamic_stack_space (size, target, known_align)
     emit_insn (gen_probe ());
 #endif
 
+  /* Record the new stack level for nonlocal gotos.  */
+  if (nonlocal_goto_handler_slot != 0)
+    emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
+
   return target;
 }
 \f
@@ -1028,7 +1158,28 @@ hard_function_value (valtype, func)
      tree valtype;
      tree func;
 {
-  return FUNCTION_VALUE (valtype, func);
+  rtx val = FUNCTION_VALUE (valtype, func);
+  if (GET_CODE (val) == REG
+      && GET_MODE (val) == BLKmode)
+    {
+      int bytes = int_size_in_bytes (valtype);
+      enum machine_mode tmpmode;
+      for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+           tmpmode != MAX_MACHINE_MODE;
+           tmpmode = GET_MODE_WIDER_MODE (tmpmode))
+        {
+          /* Have we found a large enough mode?  */
+          if (GET_MODE_SIZE (tmpmode) >= bytes)
+            break;
+        }
+
+      /* No suitable mode found.  */
+      if (tmpmode == MAX_MACHINE_MODE)
+        abort ();
+
+      PUT_MODE (val, tmpmode);
+    }      
+  return val;
 }
 
 /* Return an rtx representing the register or memory location