From dfafc897fe79d12aae145e182301260f0dacb2dd Mon Sep 17 00:00:00 2001 From: Franz Sirl Date: Mon, 2 Aug 1999 20:40:56 +0000 Subject: [PATCH] Franz Sirl * rs6000.c (rs6000_va_list): Type is an array. (rs6000_va_start): Don't doubly adjust for varargs. (rs6000_va_arg): Evaluate long long GPR adjustment. From-SVN: r28408 --- gcc/ChangeLog | 6 + gcc/config/rs6000/rs6000.c | 327 +++++++++++++++++++++++++++---------- gcc/config/rs6000/rs6000.h | 21 ++- 3 files changed, 264 insertions(+), 90 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b5e1157f324..4533f3931ad 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +1999-08-02 Franz Sirl + + * rs6000.c (rs6000_va_list): Type is an array. + (rs6000_va_start): Don't doubly adjust for varargs. + (rs6000_va_arg): Evaluate long long GPR adjustment. + Mon Aug 2 16:15:57 1999 David Edelsohn * rs6000/aix43.h (SUBTARGET_SWITCHES): Use -m64 and -m32 instead of diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index df67031f41c..865ec9254e6 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -31,9 +31,9 @@ Boston, MA 02111-1307, USA. */ #include "insn-attr.h" #include "flags.h" #include "recog.h" -#include "expr.h" #include "obstack.h" #include "tree.h" +#include "expr.h" #include "except.h" #include "function.h" #include "output.h" @@ -1678,8 +1678,8 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) { CUMULATIVE_ARGS next_cum; int reg_size = TARGET_32BIT ? 4 : 8; - rtx save_area; - int first_reg_offset; + rtx save_area, mem; + int first_reg_offset, set; if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) { @@ -1717,12 +1717,16 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1); } + set = get_varargs_alias_set (); if (!no_rtl && first_reg_offset < GP_ARG_NUM_REG) { + mem = gen_rtx_MEM (BLKmode, + plus_constant (save_area, + first_reg_offset * reg_size)), + MEM_ALIAS_SET (mem) = set; + move_block_from_reg - (GP_ARG_MIN_REG + first_reg_offset, - gen_rtx_MEM (BLKmode, - plus_constant (save_area, first_reg_offset * reg_size)), + (GP_ARG_MIN_REG + first_reg_offset, mem, GP_ARG_NUM_REG - first_reg_offset, (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD); @@ -1750,8 +1754,9 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) while (fregno <= FP_ARG_V4_MAX_REG) { - emit_move_insn (gen_rtx_MEM (DFmode, plus_constant (save_area, off)), - gen_rtx_REG (DFmode, fregno)); + mem = gen_rtx_MEM (DFmode, plus_constant (save_area, off)); + MEM_ALIAS_SET (mem) = set; + emit_move_insn (mem, gen_rtx_REG (DFmode, fregno)); fregno++; off += 8; } @@ -1759,97 +1764,255 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) emit_label (lab); } } - -/* If defined, is a C expression that produces the machine-specific - code for a call to `__builtin_saveregs'. This code will be moved - to the very beginning of the function, before any parameter access - are made. The return value of this function should be an RTX that - contains the value to use as the return of `__builtin_saveregs'. - On the Power/PowerPC return the address of the area on the stack - used to hold arguments. Under AIX, this includes the 8 word register - save area. +/* Create the va_list data type. */ - Under V.4, things are more complicated. We do not have access to - all of the virtual registers required for va_start to do its job, - so we construct the va_list in its entirity here, and reduce va_start - to a block copy. This is similar to the way we do things on Alpha. */ +tree +rs6000_build_va_list () +{ + tree f_gpr, f_fpr, f_ovf, f_sav, record; + tree uchar_type_node; -struct rtx_def * -rs6000_expand_builtin_saveregs () + /* Only SVR4 needs something special. */ + if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) + return ptr_type_node; + + record = make_node (RECORD_TYPE); + uchar_type_node = make_unsigned_type (CHAR_TYPE_SIZE); + + f_gpr = build_decl (FIELD_DECL, get_identifier ("gpr"), uchar_type_node); + f_fpr = build_decl (FIELD_DECL, get_identifier ("fpr"), uchar_type_node); + f_ovf = build_decl (FIELD_DECL, get_identifier ("overflow_arg_area"), + ptr_type_node); + f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"), + ptr_type_node); + + DECL_FIELD_CONTEXT (f_gpr) = record; + DECL_FIELD_CONTEXT (f_fpr) = record; + DECL_FIELD_CONTEXT (f_ovf) = record; + DECL_FIELD_CONTEXT (f_sav) = record; + + TYPE_FIELDS (record) = f_gpr; + TREE_CHAIN (f_gpr) = f_fpr; + TREE_CHAIN (f_fpr) = f_ovf; + TREE_CHAIN (f_ovf) = f_sav; + + layout_type (record); + + /* The correct type is an array type of one element. */ + return build_array_type (record, build_index_type (size_zero_node)); +} + +/* Implement va_start. */ + +void +rs6000_va_start (stdarg_p, valist, nextarg) + int stdarg_p; + tree valist; + rtx nextarg; { - rtx block, mem_gpr_fpr, mem_reg_save_area, mem_overflow, tmp; - tree fntype; - int stdarg_p; - HOST_WIDE_INT words, gpr, fpr; + HOST_WIDE_INT words, n_gpr, n_fpr; + tree f_gpr, f_fpr, f_ovf, f_sav; + tree gpr, fpr, ovf, sav, t; + /* Only SVR4 needs something special. */ if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - return virtual_incoming_args_rtx; - - fntype = TREE_TYPE (current_function_decl); - stdarg_p = (TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node)); - - /* Allocate the va_list constructor. */ - block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_WORD); - RTX_UNCHANGING_P (block) = 1; - RTX_UNCHANGING_P (XEXP (block, 0)) = 1; - - mem_gpr_fpr = change_address (block, word_mode, XEXP (block, 0)); - mem_overflow = change_address (block, ptr_mode, - plus_constant (XEXP (block, 0), - UNITS_PER_WORD)); - mem_reg_save_area = change_address (block, ptr_mode, - plus_constant (XEXP (block, 0), - 2 * UNITS_PER_WORD)); - - /* Construct the two characters of `gpr' and `fpr' as a unit. */ + { + std_expand_builtin_va_start (stdarg_p, valist, nextarg); + return; + } + + f_gpr = TYPE_FIELDS (va_list_type_node); + f_fpr = TREE_CHAIN (f_gpr); + f_ovf = TREE_CHAIN (f_fpr); + f_sav = TREE_CHAIN (f_ovf); + + gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr); + fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr); + ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf); + sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav); + + /* Count number of gp and fp argument registers used. */ words = current_function_args_info.words; - gpr = current_function_args_info.sysv_gregno - GP_ARG_MIN_REG; - fpr = current_function_args_info.fregno - FP_ARG_MIN_REG; + n_gpr = current_function_args_info.sysv_gregno - GP_ARG_MIN_REG; + n_fpr = current_function_args_info.fregno - FP_ARG_MIN_REG; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, "va_start: words = %d, n_gpr = %d, n_fpr = %d\n", + words, n_gpr, n_fpr); + + t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0)); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0)); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + /* Find the overflow area. */ + t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); + if (words != 0) + t = build (PLUS_EXPR, TREE_TYPE (ovf), t, + build_int_2 (words * UNITS_PER_WORD, 0)); + t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + /* Find the register save area. */ + t = make_tree (TREE_TYPE (sav), virtual_stack_vars_rtx); + t = build (PLUS_EXPR, TREE_TYPE (sav), t, + build_int_2 (-RS6000_VARARGS_SIZE, -1)); + t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); +} + +/* Implement va_arg. */ + +rtx +rs6000_va_arg (valist, type) + tree valist, type; +{ + tree f_gpr, f_fpr, f_ovf, f_sav; + tree gpr, fpr, ovf, sav, reg, t, u; + int indirect_p, size, rsize, n_reg, sav_ofs, sav_scale; + rtx lab_false, lab_over, addr_rtx, r; + + /* Only SVR4 needs something special. */ + if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) + return std_expand_builtin_va_arg (valist, type); + + f_gpr = TYPE_FIELDS (va_list_type_node); + f_fpr = TREE_CHAIN (f_gpr); + f_ovf = TREE_CHAIN (f_fpr); + f_sav = TREE_CHAIN (f_ovf); + + gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr); + fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr); + ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf); + sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav); - /* Varargs has the va_dcl argument, but we don't count it. */ - if (!stdarg_p) + size = int_size_in_bytes (type); + rsize = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + if (AGGREGATE_TYPE_P (type) || TYPE_MODE (type) == TFmode) { - if (gpr > GP_ARG_NUM_REG) - words -= 1; - else - gpr -= 1; + /* Aggregates and long doubles are passed by reference. */ + indirect_p = 1; + reg = gpr; + n_reg = 1; + sav_ofs = 0; + sav_scale = 4; + size = rsize = UNITS_PER_WORD; } + else if (FLOAT_TYPE_P (type) && ! TARGET_SOFT_FLOAT) + { + /* FP args go in FP registers, if present. */ + indirect_p = 0; + reg = fpr; + n_reg = 1; + sav_ofs = 8*4; + sav_scale = 8; + } + else + { + /* Otherwise into GP registers. */ + indirect_p = 0; + reg = gpr; + n_reg = rsize; + sav_ofs = 0; + sav_scale = 4; + } + + /* + * Pull the value out of the saved registers ... + */ + + lab_false = gen_label_rtx (); + lab_over = gen_label_rtx (); + addr_rtx = gen_reg_rtx (Pmode); - if (BYTES_BIG_ENDIAN) + emit_cmp_and_jump_insns (expand_expr (reg, NULL_RTX, QImode, EXPAND_NORMAL), + GEN_INT (8 - n_reg + 1), + GE, const1_rtx, QImode, 1, 1, lab_false); + + /* Long long is aligned in the registers. */ + if (n_reg > 1) { - HOST_WIDE_INT bits = gpr << 8 | fpr; - if (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD) - tmp = GEN_INT (bits << (BITS_PER_WORD - 16)); - else - { - bits <<= BITS_PER_WORD - HOST_BITS_PER_WIDE_INT - 16; - tmp = immed_double_const (0, bits, word_mode); - } + u = build (BIT_AND_EXPR, TREE_TYPE (reg), reg, + build_int_2 (n_reg - 1, 0)); + u = build (PLUS_EXPR, TREE_TYPE (reg), reg, u); + u = build (MODIFY_EXPR, TREE_TYPE (reg), reg, u); + TREE_SIDE_EFFECTS (u) = 1; + expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL); } + + if (sav_ofs) + t = build (PLUS_EXPR, ptr_type_node, sav, build_int_2 (sav_ofs, 0)); else - tmp = GEN_INT (fpr << 8 | gpr); + t = sav; - emit_move_insn (mem_gpr_fpr, tmp); + u = build (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, build_int_2 (n_reg, 0)); + TREE_SIDE_EFFECTS (u) = 1; - /* Find the overflow area. */ - tmp = expand_binop (Pmode, add_optab, virtual_incoming_args_rtx, - GEN_INT (words * UNITS_PER_WORD), - mem_overflow, 0, OPTAB_WIDEN); - if (tmp != mem_overflow) - emit_move_insn (mem_overflow, tmp); + u = build1 (CONVERT_EXPR, integer_type_node, u); + TREE_SIDE_EFFECTS (u) = 1; - /* Find the register save area. */ - tmp = expand_binop (Pmode, add_optab, virtual_stack_vars_rtx, - GEN_INT (-RS6000_VARARGS_SIZE), - mem_reg_save_area, 0, OPTAB_WIDEN); - if (tmp != mem_reg_save_area) - emit_move_insn (mem_reg_save_area, tmp); - - /* Return the address of the va_list constructor. */ - return XEXP (block, 0); + u = build (MULT_EXPR, integer_type_node, u, build_int_2 (sav_scale, 0)); + TREE_SIDE_EFFECTS (u) = 1; + + t = build (PLUS_EXPR, ptr_type_node, t, u); + TREE_SIDE_EFFECTS (t) = 1; + + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + emit_jump_insn (gen_jump (lab_over)); + emit_barrier (); + emit_label (lab_false); + + /* + * ... otherwise out of the overflow area. + */ + + /* Make sure we don't find reg 7 for the next int arg. */ + if (n_reg > 1) + { + t = build (MODIFY_EXPR, TREE_TYPE (reg), reg, build_int_2 (8, 0)); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + /* Care for on-stack alignment if needed. */ + if (rsize <= 1) + t = ovf; + else + { + t = build (PLUS_EXPR, TREE_TYPE (ovf), ovf, build_int_2 (7, 0)); + t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-8, -1)); + } + t = save_expr (t); + + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (size, 0)); + t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + emit_label (lab_over); + + if (indirect_p) + { + r = gen_rtx_MEM (Pmode, addr_rtx); + MEM_ALIAS_SET (r) = get_varargs_alias_set (); + emit_move_insn (addr_rtx, r); + } + + return addr_rtx; } /* Generate a memory reference for expand_block_move, copying volatile, diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index 9da3c598819..d5eb6f61e56 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -1566,14 +1566,17 @@ typedef struct rs6000_args #define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \ setup_incoming_varargs (&CUM, MODE, TYPE, &PRETEND_SIZE, NO_RTL) -/* If defined, is a C expression that produces the machine-specific - code for a call to `__builtin_saveregs'. This code will be moved - to the very beginning of the function, before any parameter access - are made. The return value of this function should be an RTX that - contains the value to use as the return of `__builtin_saveregs'. */ +/* Define the `__builtin_va_list' type for the ABI. */ +#define BUILD_VA_LIST_TYPE(VALIST) \ + (VALIST) = rs6000_build_va_list () -#define EXPAND_BUILTIN_SAVEREGS() \ - rs6000_expand_builtin_saveregs () +/* Implement `va_start' for varargs and stdarg. */ +#define EXPAND_BUILTIN_VA_START(stdarg, valist, nextarg) \ + rs6000_va_start (stdarg, valist, nextarg) + +/* Implement `va_arg'. */ +#define EXPAND_BUILTIN_VA_ARG(valist, type) \ + rs6000_va_arg (valist, type) /* This macro generates the assembly code for function entry. FILE is a stdio stream to output the code to. @@ -3297,7 +3300,9 @@ extern struct rtx_def *function_arg (); extern int function_arg_partial_nregs (); extern int function_arg_pass_by_reference (); extern void setup_incoming_varargs (); -extern struct rtx_def *rs6000_expand_builtin_saveregs (); +extern union tree_node *rs6000_va_list (); +extern void rs6000_va_start (); +extern struct rtx_def *rs6000_va_arg (); extern struct rtx_def *rs6000_stack_temp (); extern int expand_block_move (); extern int load_multiple_operation (); -- 2.30.2