summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/gcc
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2012-11-16 22:05:44 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2012-11-16 22:05:44 +0000
commit1fbd71f002e2646b3919903fcdc12497d52918e8 (patch)
treea3cce722a1cf7a70102b9a44768c9a5306221b0b /gnu/usr.bin/gcc
parent2d9ef402c34515c0a59c6ab7b72cf6b989fb35eb (diff)
Rewrite the varags implementation again (third time's the charm), the upstream
varagrs implementation being utterly wrong. This is the same logic as the one I wrote for gcc 2.95, with a few more optimizations whenever possible (such as not outputting instructions to fill fields of the __va_list struct we know won't be accessed, now that we have total control of the struct in the compiler). It should now be possible to mix varags code compiled by gcc 2.95, with code compiled with gcc 3, with the following exception: as I have finally found and fixed the buglet causing all arguments to be passed on the stack if any of the named arguments (before varargs) is a struct of 32 bytes or more passed by value, such a (pretty uncommon) case will not be interoperable. One such case can be found as va-arg-4.c in gcc's testsuite. With these changes, all the following varargs exercizers from gcc's testsuite now pass when compiled with the in-tree gcc 3: gcc.c-torture/execute/920625-1.c gcc.c-torture/execute/931004-10.c gcc.c-torture/execute/931004-12.c gcc.c-torture/execute/931004-14.c gcc.c-torture/execute/931004-4.c gcc.c-torture/execute/931004-6.c gcc.c-torture/execute/931004-8.c gcc.c-torture/execute/strct-varg-2.c gcc.c-torture/execute/va-arg-1.c gcc.c-torture/execute/va-arg-2.c gcc.c-torture/execute/va-arg-4.c gcc.c-torture/execute/va-arg-5.c gcc.c-torture/execute/va-arg-6.c as well as the few others of my own I had been using while working on gcc 2.95 more than 8 years ago.
Diffstat (limited to 'gnu/usr.bin/gcc')
-rw-r--r--gnu/usr.bin/gcc/gcc/config/m88k/m88k.c279
-rw-r--r--gnu/usr.bin/gcc/gcc/config/m88k/m88k.h5
2 files changed, 197 insertions, 87 deletions
diff --git a/gnu/usr.bin/gcc/gcc/config/m88k/m88k.c b/gnu/usr.bin/gcc/gcc/config/m88k/m88k.c
index 4c5dbe03712..d5263f4c6aa 100644
--- a/gnu/usr.bin/gcc/gcc/config/m88k/m88k.c
+++ b/gnu/usr.bin/gcc/gcc/config/m88k/m88k.c
@@ -54,6 +54,7 @@ int m88k_function_number = 0; /* Counter unique to each function */
int m88k_fp_offset = 0; /* offset of frame pointer if used */
int m88k_stack_size = 0; /* size of allocated stack (including frame) */
int m88k_case_index;
+int m88k_first_vararg;
rtx m88k_compare_reg; /* cmp output pseudo register */
rtx m88k_compare_op0; /* cmpsi operand 0 */
@@ -2529,22 +2530,17 @@ m88k_function_arg (args_so_far, mode, type, named)
if (type != 0 && AGGREGATE_TYPE_P (type)) /* undo putting struct in register */
mode = BLKmode;
- if ((args_so_far & 1) != 0
- && (mode == DImode || mode == DFmode
- || (type != 0 && TYPE_ALIGN (type) > BITS_PER_WORD)))
- args_so_far++;
-
-#ifdef ESKIT
- if (no_reg_params)
- return (rtx) 0; /* don't put args in registers */
-#endif
-
if (type == 0 && mode == BLKmode)
abort (); /* m88k_function_arg argument `type' is NULL for BLKmode. */
bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type);
words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if ((args_so_far & 1) != 0
+ && (mode == DImode || mode == DFmode
+ || (type != 0 && TYPE_ALIGN (type) > BITS_PER_WORD)))
+ args_so_far++;
+
if (args_so_far + words > 8)
return (rtx) 0; /* args have exhausted registers */
@@ -2569,22 +2565,76 @@ m88k_function_arg_advance (args_so_far, mode, type, named)
int named ATTRIBUTE_UNUSED;
{
int bytes, words;
+ int asf;
- if ((type != 0) && AGGREGATE_TYPE_P (type))
+ if (type != 0 && AGGREGATE_TYPE_P (type))
mode = BLKmode;
+ bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type);
+ words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ /* Struct and unions which are not exactly the size of a register are to be
+ passed on stack. */
+ if (mode == BLKmode
+ && (TYPE_ALIGN (type) != BITS_PER_WORD || bytes != UNITS_PER_WORD))
+ return;
+
+ asf = *args_so_far;
/* Align arguments requiring more than word alignment to a double-word
boundary (or an even register number if the argument will get passed
in registers). */
- if ((*args_so_far & 1) != 0
+ if ((asf & 1) != 0
&& (mode == DImode || mode == DFmode
|| (type != 0 && TYPE_ALIGN (type) > BITS_PER_WORD)))
- (*args_so_far)++;
+ asf++;
- bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type);
- words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (asf + words > 8)
+ return;
- (*args_so_far) += words;
+ (*args_so_far) = asf + words;
+}
+
+/* Perform any needed actions needed for a function that is receiving a
+ variable number of arguments.
+
+ CUM is as above.
+
+ MODE and TYPE are the mode and type of the current parameter.
+
+ PRETEND_SIZE is a variable that should be set to the amount of stack
+ that must be pushed by the prolog to pretend that our caller pushed
+ it.
+
+ Normally, this macro will push all remaining incoming registers on the
+ stack and set PRETEND_SIZE to the length of the registers pushed. */
+
+void
+m88k_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int *pretend_size;
+ int no_rtl;
+{
+ CUMULATIVE_ARGS next_cum;
+ tree fntype;
+ int stdarg_p;
+
+ if (no_rtl)
+ return;
+
+ fntype = TREE_TYPE (current_function_decl);
+ stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ != void_type_node));
+
+ /* For varargs, we do not want to skip the dummy va_dcl argument.
+ For stdargs, we do want to skip the last named argument. */
+ next_cum = *cum;
+ if (stdarg_p)
+ m88k_function_arg_advance(&next_cum, mode, type, 1);
+
+ m88k_first_vararg = next_cum;
}
/* Do what is necessary for `va_start'. We look at the current function
@@ -2595,34 +2645,34 @@ struct rtx_def *
m88k_builtin_saveregs ()
{
rtx addr;
- tree fntype = TREE_TYPE (current_function_decl);
- int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node)))
- ? -UNITS_PER_WORD : 0) + UNITS_PER_WORD - 1;
- int fixed;
-
- fixed = 0;
- if (GET_CODE (current_function_arg_offset_rtx) == CONST_INT)
- fixed = ((INTVAL (current_function_arg_offset_rtx) + argadj)
- / UNITS_PER_WORD);
-
- /* Allocate the register space, and store it as the __va_reg member. */
- addr = assign_stack_local (BLKmode, 8 * UNITS_PER_WORD, -1);
+ int regcnt, delta;
+
+ if (! CONSTANT_P (current_function_arg_offset_rtx))
+ abort ();
+
+ regcnt = m88k_first_vararg < 8 ? 8 - m88k_first_vararg : 0;
+ delta = regcnt & 1;
+
+ /* Allocate the register space, which will be returned as the __va_reg
+ member. If the number of registers to copy is odd, we add an extra
+ word to align even-numbered registers to a doubleword boundary. */
+ addr = assign_stack_local (BLKmode, (regcnt + delta) * UNITS_PER_WORD, -1);
set_mem_alias_set (addr, get_varargs_alias_set ());
RTX_UNCHANGING_P (addr) = 1;
RTX_UNCHANGING_P (XEXP (addr, 0)) = 1;
/* Now store the incoming registers. */
- if (fixed < 8)
- move_block_from_reg (2 + fixed,
- adjust_address (addr, Pmode, fixed * UNITS_PER_WORD),
- 8 - fixed,
- UNITS_PER_WORD * (8 - fixed));
+ if (regcnt != 0)
+ move_block_from_reg (2 + m88k_first_vararg,
+ adjust_address (addr, Pmode, delta * UNITS_PER_WORD),
+ regcnt, UNITS_PER_WORD * regcnt);
- /* Return the address of the save area, but don't put it in a
+ /* Return the address of the hypothetical save area containing all the
+ argument registers (to help va_arg() computations), but don't put it in a
register. This fails when not optimizing and produces worse code
when optimizing. */
+ addr = adjust_address (addr, Pmode,
+ -(m88k_first_vararg - delta) * UNITS_PER_WORD);
return XEXP (addr, 0);
}
@@ -2665,6 +2715,12 @@ m88k_va_start (valist, nextarg)
{
tree field_reg, field_stk, field_arg;
tree reg, stk, arg, t;
+ tree fntype;
+ int stdarg_p;
+ int offset;
+
+ if (! CONSTANT_P (current_function_arg_offset_rtx))
+ abort ();
field_arg = TYPE_FIELDS (va_list_type_node);
field_stk = TREE_CHAIN (field_arg);
@@ -2674,39 +2730,26 @@ m88k_va_start (valist, nextarg)
stk = build (COMPONENT_REF, TREE_TYPE (field_stk), valist, field_stk);
reg = build (COMPONENT_REF, TREE_TYPE (field_reg), valist, field_reg);
- /* Fill in the ARG member. */
- {
- tree fntype = TREE_TYPE (current_function_decl);
- int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node)))
- ? -UNITS_PER_WORD : 0) + UNITS_PER_WORD - 1;
- tree argsize;
-
- if (CONSTANT_P (current_function_arg_offset_rtx))
- {
- int fixed = (INTVAL (current_function_arg_offset_rtx)
- + argadj) / UNITS_PER_WORD;
-
- argsize = build_int_2 (fixed, 0);
- }
- else
- {
- argsize = make_tree (integer_type_node,
- current_function_arg_offset_rtx);
- argsize = fold (build (PLUS_EXPR, integer_type_node, argsize,
- build_int_2 (argadj, 0)));
- argsize = fold (build (RSHIFT_EXPR, integer_type_node, argsize,
- build_int_2 (2, 0)));
- }
+ fntype = TREE_TYPE (current_function_decl);
+ stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ != void_type_node));
- t = build (MODIFY_EXPR, TREE_TYPE (arg), arg, argsize);
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
+ /* Fill in the __va_arg member if it will ever be needed. */
+ if (m88k_first_vararg < 8)
+ {
+ t = build (MODIFY_EXPR, TREE_TYPE (arg), arg,
+ build_int_2 (m88k_first_vararg, 0));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
/* Store the arg pointer in the __va_stk member. */
+ offset = XINT (current_function_arg_offset_rtx, 0);
+ if (m88k_first_vararg >= 8 && ! stdarg_p)
+ offset -= UNITS_PER_WORD;
t = make_tree (TREE_TYPE (stk), virtual_incoming_args_rtx);
+ t = build (PLUS_EXPR, TREE_TYPE (stk), t, build_int_2 (offset, 0));
t = build (MODIFY_EXPR, TREE_TYPE (stk), stk, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -2725,44 +2768,106 @@ m88k_va_arg (valist, type)
tree valist, type;
{
tree field_reg, field_stk, field_arg;
- tree reg, stk, arg, arg_align, base, t;
int size, wsize, align, reg_p;
rtx addr_rtx;
+ rtx lab_done;
field_arg = TYPE_FIELDS (va_list_type_node);
field_stk = TREE_CHAIN (field_arg);
field_reg = TREE_CHAIN (field_stk);
- arg = build (COMPONENT_REF, TREE_TYPE (field_arg), valist, field_arg);
- stk = build (COMPONENT_REF, TREE_TYPE (field_stk), valist, field_stk);
- reg = build (COMPONENT_REF, TREE_TYPE (field_reg), valist, field_reg);
-
size = int_size_in_bytes (type);
wsize = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
- align = 1 << ((TYPE_ALIGN (type) / BITS_PER_UNIT) >> 3);
reg_p = (AGGREGATE_TYPE_P (type)
? size == UNITS_PER_WORD && TYPE_ALIGN (type) == BITS_PER_WORD
: size <= 2*UNITS_PER_WORD);
- /* Align __va_arg to the (doubleword?) boundary above. */
- t = build (PLUS_EXPR, TREE_TYPE (arg), arg, build_int_2 (align - 1, 0));
- arg_align = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1));
- arg_align = save_expr (arg_align);
+ addr_rtx = gen_reg_rtx (Pmode);
+ lab_done = gen_label_rtx ();
- /* Decide if we should read from stack or regs. */
- t = build (LT_EXPR, integer_type_node, arg_align, build_int_2 (8, 0));
- base = build (COND_EXPR, TREE_TYPE (reg), t, reg, stk);
+ /* Decide if we should read from stack or regs (if the argument could have
+ been passed in registers, and some actually have). */
+ if (reg_p && m88k_first_vararg < 8) {
+ tree arg, arg_align, reg;
+ rtx lab_stack;
+ tree t;
+ rtx r;
- /* Find the final address. */
- t = build (PLUS_EXPR, TREE_TYPE (base), base, arg_align);
- addr_rtx = expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
- addr_rtx = copy_to_reg (addr_rtx);
+ lab_stack = gen_label_rtx ();
- /* Increment __va_arg. */
- t = build (PLUS_EXPR, TREE_TYPE (arg), arg_align, build_int_2 (wsize, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (arg), arg, t);
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ /* Align __va_arg to a doubleword boundary if necessary. */
+ arg = build (COMPONENT_REF, TREE_TYPE (field_arg), valist, field_arg);
+ align = TYPE_ALIGN (type) / BITS_PER_WORD;
+ if (align > 1)
+ {
+ t = build (PLUS_EXPR, TREE_TYPE (arg), arg, build_int_2 (align - 1, 0));
+ arg_align = build (BIT_AND_EXPR, TREE_TYPE (t), t,
+ build_int_2 (-align, -1));
+ arg_align = save_expr (arg_align);
+ }
+ else
+ arg_align = arg;
+
+ /* Make sure the argument fits within the remainder of the saved
+ register area, and branch to the stack logic if not. */
+ r = expand_expr (arg_align, NULL_RTX, TYPE_MODE (TREE_TYPE (arg_align)),
+ EXPAND_NORMAL);
+ /* if (arg_align > 8 - wsize) goto lab_stack */
+ emit_cmp_and_jump_insns (r, GEN_INT (8 - wsize), GTU,
+ GEN_INT (UNITS_PER_WORD), GET_MODE (r), 1,
+ lab_stack);
+
+ /* Compute the argument address. */
+ reg = build (COMPONENT_REF, TREE_TYPE (field_reg), valist, field_reg);
+ t = build (MULT_EXPR, TREE_TYPE (reg), arg_align,
+ build_int_2 (UNITS_PER_WORD, 0));
+ t = build (PLUS_EXPR, TREE_TYPE (reg), reg, t);
+
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
+
+ /* Increment __va_arg. */
+ t = build (PLUS_EXPR, TREE_TYPE (arg), arg_align, build_int_2 (wsize, 0));
+ t = build (MODIFY_EXPR, TREE_TYPE (arg), arg, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ emit_jump_insn (gen_jump (lab_done));
+ emit_barrier ();
+
+ emit_label (lab_stack);
+ }
+
+ {
+ tree stk;
+ tree t;
+ rtx r;
+
+ stk = build (COMPONENT_REF, TREE_TYPE (field_stk), valist, field_stk);
+
+ /* Align __va_stk to the type boundary if necessary. */
+ align = TYPE_ALIGN (type) / BITS_PER_UNIT;
+ if (align > UNITS_PER_WORD)
+ {
+ t = build (PLUS_EXPR, TREE_TYPE (stk), stk, build_int_2 (align - 1, 0));
+ t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1));
+ }
+ else
+ t = stk;
+
+ /* Compute the argument address. */
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
+
+ /* Increment __va_stk. */
+ t = build (PLUS_EXPR, TREE_TYPE (t), t,
+ build_int_2 (wsize * UNITS_PER_WORD, 0));
+ t = build (MODIFY_EXPR, TREE_TYPE (stk), stk, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+
+ emit_label (lab_done);
return addr_rtx;
}
diff --git a/gnu/usr.bin/gcc/gcc/config/m88k/m88k.h b/gnu/usr.bin/gcc/gcc/config/m88k/m88k.h
index 1659305e381..7dce55dd38b 100644
--- a/gnu/usr.bin/gcc/gcc/config/m88k/m88k.h
+++ b/gnu/usr.bin/gcc/gcc/config/m88k/m88k.h
@@ -996,6 +996,11 @@ enum reg_class { NO_REGS, AP_REG, XRF_REGS, GENERAL_REGS, AGRF_REGS,
(((TYPE) ? TYPE_ALIGN (TYPE) : GET_MODE_BITSIZE (MODE)) <= PARM_BOUNDARY \
? PARM_BOUNDARY : 2 * PARM_BOUNDARY)
+/* Perform any actions needed for a function that is receiving a
+ variable number of arguments. */
+#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
+ m88k_setup_incoming_varargs (& (CUM), MODE, TYPE, & (PRETEND_SIZE), NO_RTL)
+
/* Generate necessary RTL for __builtin_saveregs().
ARGLIST is the argument list; see expr.c. */
#define EXPAND_BUILTIN_SAVEREGS() m88k_builtin_saveregs ()