OSDN Git Service

* config/xtensa/xtensa.c (function_arg_advance): Check for args
[pf3gnuchains/gcc-fork.git] / gcc / config / xtensa / xtensa.c
index 09a2f34..940ba0e 100644 (file)
@@ -48,6 +48,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "target.h"
 #include "target-def.h"
 #include "langhooks.h"
+#include "tree-gimple.h"
+
 
 /* Enumeration for all of the relational tests, so that we can build
    arrays indexed by the test type, and not worry about the order
@@ -200,6 +202,7 @@ static rtx gen_conditional_move (rtx);
 static rtx fixup_subreg_mem (rtx);
 static enum machine_mode xtensa_find_mode_for_size (unsigned);
 static struct machine_function * xtensa_init_machine_status (void);
+static bool xtensa_return_in_msb (tree);
 static void printx (FILE *, signed int);
 static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
 static rtx xtensa_builtin_saveregs (void);
@@ -210,8 +213,8 @@ static void xtensa_select_rtx_section (enum machine_mode, rtx,
 static bool xtensa_rtx_costs (rtx, int, int, int *);
 static tree xtensa_build_builtin_va_list (void);
 static bool xtensa_return_in_memory (tree, tree);
+static tree xtensa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 
-static int current_function_arg_words;
 static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
   REG_ALLOC_ORDER;
 \f
@@ -250,9 +253,21 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
 
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY xtensa_return_in_memory
+#undef TARGET_SPLIT_COMPLEX_ARG
+#define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
 
 #undef TARGET_EXPAND_BUILTIN_SAVEREGS
 #define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR xtensa_gimplify_va_arg_expr
+
+#undef TARGET_RETURN_IN_MSB
+#define TARGET_RETURN_IN_MSB xtensa_return_in_msb
+
+#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
+#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hook_int_void_1
 
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
@@ -567,7 +582,8 @@ call_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
        {
          tree callee, callee_sec, caller_sec;
 
-         if (GET_CODE (op) != SYMBOL_REF || !SYMBOL_REF_LOCAL_P (op))
+         if (GET_CODE (op) != SYMBOL_REF
+             || !SYMBOL_REF_LOCAL_P (op) || SYMBOL_REF_EXTERNAL_P (op))
            return FALSE;
 
          /* Don't attempt a direct call if the callee is known to be in
@@ -621,11 +637,6 @@ move_operand (rtx op, enum machine_mode mode)
 
     case HImode:
     case QImode:
-      /* Accept CONSTANT_P_RTX, since it will be gone by CSE1 and
-        result in 0/1.  */
-      if (GET_CODE (op) == CONSTANT_P_RTX)
-       return TRUE;
-
       if (GET_CODE (op) == CONST_INT && xtensa_simm12b (INTVAL (op)))
        return TRUE;
       break;
@@ -1246,7 +1257,6 @@ int
 xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
 {
   if (CONSTANT_P (operands[1])
-      && GET_CODE (operands[1]) != CONSTANT_P_RTX
       && (GET_CODE (operands[1]) != CONST_INT
          || !xtensa_simm12b (INTVAL (operands[1]))))
     {
@@ -1461,7 +1471,7 @@ xtensa_expand_block_move (rtx *operands)
   operands[0] = validize_mem (dest);
   operands[1] = validize_mem (src);
 
-  emit_insn (gen_movstrsi_internal (operands[0], operands[1],
+  emit_insn (gen_movmemsi_internal (operands[0], operands[1],
                                    operands[2], operands[3]));
   return 1;
 }
@@ -1773,7 +1783,9 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
            ? (int) GET_MODE_SIZE (mode)
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
-  if ((*arg_words + words > max) && (*arg_words < max))
+  if (*arg_words < max
+      && (targetm.calls.must_pass_in_stack (mode, type)
+         || *arg_words + words > max))
     *arg_words = max;
 
   *arg_words += words;
@@ -1801,7 +1813,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
   if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
-    *arg_words += (*arg_words & 1);
+    {
+      int align = TYPE_ALIGN (type) / BITS_PER_WORD;
+      *arg_words = (*arg_words + align - 1) & -align;
+    }
 
   if (*arg_words + words > max)
     return (rtx)0;
@@ -1815,6 +1830,15 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
 }
 
 
+static bool
+xtensa_return_in_msb (tree valtype)
+{
+  return (TARGET_BIG_ENDIAN
+         && AGGREGATE_TYPE_P (valtype)
+         && int_size_in_bytes (valtype) >= UNITS_PER_WORD);
+}
+
+
 void
 override_options (void)
 {
@@ -2211,7 +2235,7 @@ long
 compute_frame_size (int size)
 {
   /* Add space for the incoming static chain value.  */
-  if (current_function_needs_context)
+  if (cfun->static_chain_decl != NULL)
     size += (1 * UNITS_PER_WORD);
 
   xtensa_current_frame_size =
@@ -2322,17 +2346,20 @@ xtensa_return_addr (int count, rtx frame)
 
 
 /* Create the va_list data type.
-   This structure is set up by __builtin_saveregs.  The __va_reg
-   field points to a stack-allocated region holding the contents of the
-   incoming argument registers.  The __va_ndx field is an index initialized
-   to the position of the first unnamed (variable) argument.  This same index
-   is also used to address the arguments passed in memory.  Thus, the
-   __va_stk field is initialized to point to the position of the first
-   argument in memory offset to account for the arguments passed in
-   registers.  E.G., if there are 6 argument registers, and each register is
-   4 bytes, then __va_stk is set to $sp - (6 * 4); then __va_reg[N*4]
-   references argument word N for 0 <= N < 6, and __va_stk[N*4] references
-   argument word N for N >= 6.  */
+
+   This structure is set up by __builtin_saveregs.  The __va_reg field
+   points to a stack-allocated region holding the contents of the
+   incoming argument registers.  The __va_ndx field is an index
+   initialized to the position of the first unnamed (variable)
+   argument.  This same index is also used to address the arguments
+   passed in memory.  Thus, the __va_stk field is initialized to point
+   to the position of the first argument in memory offset to account
+   for the arguments passed in registers and to account for the size
+   of the argument registers not being 16-byte aligned.  E.G., there
+   are 6 argument registers of 4 bytes each, but we want the __va_ndx
+   for the first stack argument to have the maximal alignment of 16
+   bytes, so we offset the __va_stk address by 32 bytes so that
+   __va_stk[32] references the first argument on the stack.  */
 
 static tree
 xtensa_build_builtin_va_list (void)
@@ -2371,7 +2398,7 @@ static rtx
 xtensa_builtin_saveregs (void)
 {
   rtx gp_regs, dest;
-  int arg_words = current_function_arg_words;
+  int arg_words = current_function_args_info.arg_words;
   int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
 
   if (gp_left <= 0)
@@ -2412,26 +2439,28 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
   f_reg = TREE_CHAIN (f_stk);
   f_ndx = TREE_CHAIN (f_reg);
 
-  stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
-  reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
-  ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+  stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
+  reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
+  ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
 
   /* Call __builtin_saveregs; save the result in __va_reg */
-  current_function_arg_words = arg_words;
   u = make_tree (ptr_type_node, expand_builtin_saveregs ());
   t = build (MODIFY_EXPR, ptr_type_node, reg, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  /* Set the __va_stk member to $arg_ptr - (size of __va_reg area) */
+  /* Set the __va_stk member to ($arg_ptr - 32).  */
   u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
-  u = fold (build (PLUS_EXPR, ptr_type_node, u,
-                  build_int_2 (-MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1)));
+  u = fold (build (PLUS_EXPR, ptr_type_node, u, build_int_2 (-32, -1)));
   t = build (MODIFY_EXPR, ptr_type_node, stk, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  /* Set the __va_ndx member.  */
+  /* Set the __va_ndx member.  If the first variable argument is on
+     the stack, adjust __va_ndx by 2 words to account for the extra
+     alignment offset for __va_stk.  */
+  if (arg_words >= MAX_ARGS_IN_REGISTERS)
+    arg_words += 2;
   u = build_int_2 (arg_words * UNITS_PER_WORD, 0);
   t = build (MODIFY_EXPR, integer_type_node, ndx, u);
   TREE_SIDE_EFFECTS (t) = 1;
@@ -2441,150 +2470,144 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 
 /* Implement `va_arg'.  */
 
-rtx
-xtensa_va_arg (tree valist, tree type)
+static tree
+xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
+                            tree *post_p ATTRIBUTE_UNUSED)
 {
   tree f_stk, stk;
   tree f_reg, reg;
   tree f_ndx, ndx;
-  tree tmp, addr_tree, type_size;
-  rtx array, orig_ndx, r, addr, size, va_size;
-  rtx lab_false, lab_over, lab_false2;
+  tree type_size, array, orig_ndx, addr, size, va_size, t;
+  tree lab_false, lab_over, lab_false2;
 
   /* Handle complex values as separate real and imaginary parts.  */
   if (TREE_CODE (type) == COMPLEX_TYPE)
     {
-      rtx real_part, imag_part, concat_val, local_copy;
-
-      real_part = xtensa_va_arg (valist, TREE_TYPE (type));
-      imag_part = xtensa_va_arg (valist, TREE_TYPE (type));
+      tree real_part, imag_part;
 
-      /* Make a copy of the value in case the parts are not contiguous.  */
-      real_part = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (type)), real_part);
-      imag_part = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (type)), imag_part);
-      concat_val = gen_rtx_CONCAT (TYPE_MODE (type), real_part, imag_part);
+      real_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
+                                              pre_p, NULL);
+      real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
 
-      local_copy = assign_temp (type, 0, 1, 0);
-      emit_move_insn (local_copy, concat_val);
+      imag_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
+                                              pre_p, NULL);
+      imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
 
-      return XEXP (local_copy, 0);
+      return build (COMPLEX_EXPR, type, real_part, imag_part);
     }
 
   f_stk = TYPE_FIELDS (va_list_type_node);
   f_reg = TREE_CHAIN (f_stk);
   f_ndx = TREE_CHAIN (f_reg);
 
-  stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
-  reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
-  ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+  stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
+  reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
+  ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
 
-  type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+  type_size = size_in_bytes (type);
+  va_size = round_up (type_size, UNITS_PER_WORD);
+  gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  va_size = gen_reg_rtx (SImode);
-  tmp = fold (build (MULT_EXPR, sizetype,
-                    fold (build (TRUNC_DIV_EXPR, sizetype,
-                                 fold (build (PLUS_EXPR, sizetype,
-                                              type_size,
-                                              size_int (UNITS_PER_WORD - 1))),
-                                 size_int (UNITS_PER_WORD))),
-                    size_int (UNITS_PER_WORD)));
-  r = expand_expr (tmp, va_size, SImode, EXPAND_NORMAL);
-  if (r != va_size)
-    emit_move_insn (va_size, r);
 
+  /* First align __va_ndx if necessary for this arg:
 
-  /* First align __va_ndx to a double word boundary if necessary for this arg:
+     orig_ndx = (AP).__va_ndx;
+     if (__alignof__ (TYPE) > 4 )
+       orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
+                       & -__alignof__ (TYPE)); */
 
-     if (__alignof__ (TYPE) > 4)
-       (AP).__va_ndx = (((AP).__va_ndx + 7) & -8); */
+  orig_ndx = get_initialized_tmp_var (ndx, pre_p, NULL);
 
   if (TYPE_ALIGN (type) > BITS_PER_WORD)
     {
-      tmp = build (PLUS_EXPR, integer_type_node, ndx,
-                  build_int_2 ((2 * UNITS_PER_WORD) - 1, 0));
-      tmp = build (BIT_AND_EXPR, integer_type_node, tmp,
-                  build_int_2 (-2 * UNITS_PER_WORD, -1));
-      tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
-      TREE_SIDE_EFFECTS (tmp) = 1;
-      expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      int align = TYPE_ALIGN (type) / BITS_PER_UNIT;
+
+      t = build (PLUS_EXPR, integer_type_node, orig_ndx,
+                build_int_2 (align - 1, 0));
+      t = build (BIT_AND_EXPR, integer_type_node, t,
+                build_int_2 (-align, -1));
+      t = build (MODIFY_EXPR, integer_type_node, orig_ndx, t);
+      gimplify_and_add (t, pre_p);
     }
 
 
   /* Increment __va_ndx to point past the argument:
 
-     orig_ndx = (AP).__va_ndx;
-     (AP).__va_ndx += __va_size (TYPE); */
-
-  orig_ndx = gen_reg_rtx (SImode);
-  r = expand_expr (ndx, orig_ndx, SImode, EXPAND_NORMAL);
-  if (r != orig_ndx)
-    emit_move_insn (orig_ndx, r);
+     (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
 
-  tmp = build (PLUS_EXPR, integer_type_node, ndx,
-              make_tree (intSI_type_node, va_size));
-  tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
-  TREE_SIDE_EFFECTS (tmp) = 1;
-  expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  t = fold_convert (integer_type_node, va_size);
+  t = build (PLUS_EXPR, integer_type_node, orig_ndx, t);
+  t = build (MODIFY_EXPR, integer_type_node, ndx, t);
+  gimplify_and_add (t, pre_p);
 
 
   /* Check if the argument is in registers:
 
      if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
-         && !MUST_PASS_IN_STACK (type))
+         && !must_pass_in_stack (type))
         __array = (AP).__va_reg; */
 
-  array = gen_reg_rtx (Pmode);
+  array = create_tmp_var (ptr_type_node, NULL);
 
-  lab_over = NULL_RTX;
-  if (!MUST_PASS_IN_STACK (VOIDmode, type))
+  lab_over = NULL;
+  if (!targetm.calls.must_pass_in_stack (TYPE_MODE (type), type))
     {
-      lab_false = gen_label_rtx ();
-      lab_over = gen_label_rtx ();
-
-      emit_cmp_and_jump_insns (expand_expr (ndx, NULL_RTX, SImode,
-                                           EXPAND_NORMAL),
-                              GEN_INT (MAX_ARGS_IN_REGISTERS
-                                       * UNITS_PER_WORD),
-                              GT, const1_rtx, SImode, 0, lab_false);
-
-      r = expand_expr (reg, array, Pmode, EXPAND_NORMAL);
-      if (r != array)
-       emit_move_insn (array, r);
-
-      emit_jump_insn (gen_jump (lab_over));
-      emit_barrier ();
-      emit_label (lab_false);
+      lab_false = create_artificial_label ();
+      lab_over = create_artificial_label ();
+
+      t = build_int_2 (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, 0);
+      t = build (GT_EXPR, boolean_type_node, ndx, t);
+      t = build (COND_EXPR, void_type_node, t,
+                build (GOTO_EXPR, void_type_node, lab_false),
+                NULL);
+      gimplify_and_add (t, pre_p);
+
+      t = build (MODIFY_EXPR, void_type_node, array, reg);
+      gimplify_and_add (t, pre_p);
+
+      t = build (GOTO_EXPR, void_type_node, lab_over);
+      gimplify_and_add (t, pre_p);
+
+      t = build (LABEL_EXPR, void_type_node, lab_false);
+      gimplify_and_add (t, pre_p);
     }
 
+
   /* ...otherwise, the argument is on the stack (never split between
      registers and the stack -- change __va_ndx if necessary):
 
      else
        {
-        if (orig_ndx < __MAX_ARGS_IN_REGISTERS * 4)
-            (AP).__va_ndx = __MAX_ARGS_IN_REGISTERS * 4 + __va_size (TYPE);
+        if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
+            (AP).__va_ndx = 32 + __va_size (TYPE);
         __array = (AP).__va_stk;
        } */
 
-  lab_false2 = gen_label_rtx ();
-  emit_cmp_and_jump_insns (orig_ndx,
-                          GEN_INT (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD),
-                          GE, const1_rtx, SImode, 0, lab_false2);
+  lab_false2 = create_artificial_label ();
 
-  tmp = build (PLUS_EXPR, sizetype, make_tree (intSI_type_node, va_size),
-              build_int_2 (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, 0));
-  tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
-  TREE_SIDE_EFFECTS (tmp) = 1;
-  expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  t = build_int_2 (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, 0);
+  t = build (GT_EXPR, boolean_type_node, orig_ndx, t);
+  t = build (COND_EXPR, void_type_node, t,
+            build (GOTO_EXPR, void_type_node, lab_false2),
+            NULL);
+  gimplify_and_add (t, pre_p);
 
-  emit_label (lab_false2);
+  t = size_binop (PLUS_EXPR, va_size, size_int (32));
+  t = fold_convert (integer_type_node, t);
+  t = build (MODIFY_EXPR, integer_type_node, ndx, t);
+  gimplify_and_add (t, pre_p);
 
-  r = expand_expr (stk, array, Pmode, EXPAND_NORMAL);
-  if (r != array)
-    emit_move_insn (array, r);
+  t = build (LABEL_EXPR, void_type_node, lab_false2);
+  gimplify_and_add (t, pre_p);
 
-  if (lab_over != NULL_RTX)
-    emit_label (lab_over);
+  t = build (MODIFY_EXPR, void_type_node, array, stk);
+  gimplify_and_add (t, pre_p);
+
+  if (lab_over)
+    {
+      t = build (LABEL_EXPR, void_type_node, lab_over);
+      gimplify_and_add (t, pre_p);
+    }
 
 
   /* Given the base array pointer (__array) and index to the subsequent
@@ -2597,33 +2620,24 @@ xtensa_va_arg (tree valist, tree type)
      The results are endian-dependent because values smaller than one word
      are aligned differently.  */
 
-  size = gen_reg_rtx (SImode);
-  emit_move_insn (size, va_size);
 
-  if (BYTES_BIG_ENDIAN)
+  if (BYTES_BIG_ENDIAN && TREE_CODE (type_size) == INTEGER_CST)
     {
-      rtx lab_use_va_size = gen_label_rtx ();
-
-      emit_cmp_and_jump_insns (expand_expr (type_size, NULL_RTX, SImode,
-                                           EXPAND_NORMAL),
-                              GEN_INT (PARM_BOUNDARY / BITS_PER_UNIT),
-                              GE, const1_rtx, SImode, 0, lab_use_va_size);
-
-      r = expand_expr (type_size, size, SImode, EXPAND_NORMAL);
-      if (r != size)
-       emit_move_insn (size, r);
-
-      emit_label (lab_use_va_size);
+      t = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
+      t = fold (build (GE_EXPR, boolean_type_node, type_size, t));
+      t = fold (build (COND_EXPR, sizetype, t, va_size, type_size));
+      size = t;
     }
+  else
+    size = va_size;
+
+  t = fold_convert (ptr_type_node, ndx);
+  addr = build (PLUS_EXPR, ptr_type_node, array, t);
+  t = fold_convert (ptr_type_node, size);
+  addr = build (MINUS_EXPR, ptr_type_node, addr, t);
 
-  addr_tree = build (PLUS_EXPR, ptr_type_node,
-                    make_tree (ptr_type_node, array),
-                    ndx);
-  addr_tree = build (MINUS_EXPR, ptr_type_node, addr_tree,
-                    make_tree (intSI_type_node, size));
-  addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
-  addr = copy_to_reg (addr);
-  return addr;
+  addr = fold_convert (build_pointer_type (type), addr);
+  return build_fold_indirect_ref (addr);
 }