/* Subroutines used for code generation on IBM RS/6000.
- Copyright (C) 1991, 1993 Free Software Foundation, Inc.
- Contributed by Richard Kenner (kenner@nyu.edu)
+ Copyright (C) 1991, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+ Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GNU CC.
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 <stdio.h>
+#include <ctype.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "obstack.h"
#include "tree.h"
+#ifndef TARGET_NO_PROTOTYPE
+#define TARGET_NO_PROTOTYPE 0
+#endif
+
extern char *language_string;
+extern int profile_block_flag;
#define min(A,B) ((A) < (B) ? (A) : (B))
#define max(A,B) ((A) > (B) ? (A) : (B))
+/* Target cpu type */
+
+enum processor_type rs6000_cpu;
+struct rs6000_cpu_select rs6000_select[3] =
+{
+ /* switch name, tune arch */
+ { (char *)0, "--with-cpu=", 1, 1 },
+ { (char *)0, "-mcpu=", 1, 1 },
+ { (char *)0, "-mtune=", 1, 0 },
+};
+
/* Set to non-zero by "fix" operation to indicate that itrunc and
uitrunc must be defined. */
static int trunc_defined;
+/* Set to non-zero once AIX common-mode calls have been defined. */
+static int common_mode_defined;
/* Save information from a "cmpxx" operation until the branch or scc is
emitted. */
rtx rs6000_compare_op0, rs6000_compare_op1;
int rs6000_compare_fp_p;
+
+#ifdef USING_SVR4_H
+/* Label number of label created for -mrelocatable, to call to so we can
+ get the address of the GOT section */
+int rs6000_pic_labelno;
+#endif
+
+/* Whether a System V.4 varargs area was created. */
+int rs6000_sysv_varargs_p;
+
+/* Whether we need to save the TOC register. */
+int rs6000_save_toc_p;
+
+/* ABI enumeration available for subtarget to use. */
+enum rs6000_abi rs6000_current_abi;
+
+/* Temporary memory used to convert integer -> float */
+static rtx stack_temps[NUM_MACHINE_MODES];
+
+\f
+/* Print the options used in the assembly file. */
+
+extern char *version_string, *language_string;
+
+struct asm_option
+{
+ char *string;
+ int *variable;
+ int on_value;
+};
+
+#define MAX_LINE 79
+
+static int
+output_option (file, type, name, pos)
+ FILE *file;
+ char *type;
+ char *name;
+ int pos;
+{
+ int type_len = strlen (type);
+ int name_len = strlen (name);
+
+ if (1 + type_len + name_len + pos > MAX_LINE)
+ {
+ fprintf (file, "\n # %s%s", type, name);
+ return 3 + type_len + name_len;
+ }
+ fprintf (file, " %s%s", type, name);
+ return pos + 1 + type_len + name_len;
+}
+
+static struct { char *name; int value; } m_options[] = TARGET_SWITCHES;
+
+void
+output_options (file, f_options, f_len, W_options, W_len)
+ FILE *file;
+ struct asm_option *f_options;
+ int f_len;
+ struct asm_option *W_options;
+ int W_len;
+{
+ int j;
+ int flags = target_flags;
+ int pos = 32767;
+
+ fprintf (file, " # %s %s", language_string, version_string);
+
+ if (optimize)
+ {
+ char opt_string[20];
+ sprintf (opt_string, "%d", optimize);
+ pos = output_option (file, "-O", opt_string, pos);
+ }
+
+ if (profile_flag)
+ pos = output_option (file, "-p", "", pos);
+
+ if (profile_block_flag)
+ pos = output_option (file, "-a", "", pos);
+
+ if (inhibit_warnings)
+ pos = output_option (file, "-w", "", pos);
+
+ for (j = 0; j < f_len; j++)
+ {
+ if (*f_options[j].variable == f_options[j].on_value)
+ pos = output_option (file, "-f", f_options[j].string, pos);
+ }
+
+ for (j = 0; j < W_len; j++)
+ {
+ if (*W_options[j].variable == W_options[j].on_value)
+ pos = output_option (file, "-W", W_options[j].string, pos);
+ }
+
+ for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
+ {
+ if (m_options[j].name[0] != '\0'
+ && m_options[j].value > 0
+ && ((m_options[j].value & flags) == m_options[j].value))
+ {
+ pos = output_option (file, "-m", m_options[j].name, pos);
+ flags &= ~ m_options[j].value;
+ }
+ }
+
+ for (j = 0; j < sizeof (rs6000_select) / sizeof(rs6000_select[0]); j++)
+ if (rs6000_select[j].string != (char *)0)
+ pos = output_option (file, rs6000_select[j].name, rs6000_select[j].string, pos);
+
+ fputs ("\n\n", file);
+}
+
+\f
+/* Override command line options. Mostly we process the processor
+ type and sometimes adjust other TARGET_ options. */
+
+void
+rs6000_override_options (default_cpu)
+ char *default_cpu;
+{
+ int i, j;
+ struct rs6000_cpu_select *ptr;
+
+ /* Simplify the entries below by making a mask for any POWER
+ variant and any PowerPC variant. */
+
+#define POWER_MASKS (MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING)
+#define POWERPC_MASKS (MASK_POWERPC | MASK_PPC_GPOPT \
+ | MASK_PPC_GFXOPT | MASK_POWERPC64)
+#define POWERPC_OPT_MASKS (MASK_PPC_GPOPT | MASK_PPC_GFXOPT)
+
+ static struct ptt
+ {
+ char *name; /* Canonical processor name. */
+ enum processor_type processor; /* Processor type enum value. */
+ int target_enable; /* Target flags to enable. */
+ int target_disable; /* Target flags to disable. */
+ } processor_target_table[]
+ = {{"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_MASKS},
+ {"power", PROCESSOR_POWER,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"power2", PROCESSOR_POWER,
+ MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
+ POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"powerpc", PROCESSOR_POWERPC,
+ MASK_POWERPC | MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+ {"rios", PROCESSOR_RIOS1,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"rios1", PROCESSOR_RIOS1,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"rsc", PROCESSOR_PPC601,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"rsc1", PROCESSOR_PPC601,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"rios2", PROCESSOR_RIOS2,
+ MASK_POWER | MASK_MULTIPLE | MASK_STRING | MASK_POWER2,
+ POWERPC_MASKS | MASK_NEW_MNEMONICS},
+ {"403", PROCESSOR_PPC403,
+ MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+ {"505", PROCESSOR_MPCCORE,
+ MASK_POWERPC | MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+ {"601", PROCESSOR_PPC601,
+ MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS | MASK_MULTIPLE | MASK_STRING,
+ MASK_POWER2 | POWERPC_OPT_MASKS | MASK_POWERPC64},
+ {"602", PROCESSOR_PPC602,
+ MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+ {"603", PROCESSOR_PPC603,
+ MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+ {"603e", PROCESSOR_PPC603,
+ MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+ {"604", PROCESSOR_PPC604,
+ MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+ {"620", PROCESSOR_PPC620,
+ MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+ {"821", PROCESSOR_MPCCORE,
+ MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+ {"860", PROCESSOR_MPCCORE,
+ MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+ POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}};
+
+ int ptt_size = sizeof (processor_target_table) / sizeof (struct ptt);
+
+ int multiple = TARGET_MULTIPLE; /* save current -mmultiple/-mno-multiple status */
+ int string = TARGET_STRING; /* save current -mstring/-mno-string status */
+
+ profile_block_flag = 0;
+
+ /* Identify the processor type */
+ rs6000_select[0].string = default_cpu;
+ rs6000_cpu = PROCESSOR_DEFAULT;
+
+ for (i = 0; i < sizeof (rs6000_select) / sizeof (rs6000_select[0]); i++)
+ {
+ ptr = &rs6000_select[i];
+ if (ptr->string != (char *)0 && ptr->string[0] != '\0')
+ {
+ for (j = 0; j < ptt_size; j++)
+ if (! strcmp (ptr->string, processor_target_table[j].name))
+ {
+ if (ptr->set_tune_p)
+ rs6000_cpu = processor_target_table[j].processor;
+
+ if (ptr->set_arch_p)
+ {
+ target_flags |= processor_target_table[j].target_enable;
+ target_flags &= ~processor_target_table[j].target_disable;
+ }
+ break;
+ }
+
+ if (i == ptt_size)
+ error ("bad value (%s) for %s switch", ptr->string, ptr->name);
+ }
+ }
+
+ /* If -mmultiple or -mno-multiple was explicitly used, don't
+ override with the processor default */
+ if (TARGET_MULTIPLE_SET)
+ target_flags = (target_flags & ~MASK_MULTIPLE) | multiple;
+
+ /* If -mstring or -mno-string was explicitly used, don't
+ override with the processor default */
+ if (TARGET_STRING_SET)
+ target_flags = (target_flags & ~MASK_STRING) | string;
+
+ /* Don't allow -mmultiple or -mstring on little endian systems, because the
+ hardware doesn't support the instructions used in little endian mode */
+ if (!BYTES_BIG_ENDIAN)
+ {
+ if (TARGET_MULTIPLE)
+ {
+ target_flags &= ~MASK_MULTIPLE;
+ if (TARGET_MULTIPLE_SET)
+ warning ("-mmultiple is not supported on little endian systems");
+ }
+
+ if (TARGET_STRING)
+ {
+ target_flags &= ~MASK_STRING;
+ if (TARGET_STRING_SET)
+ warning ("-mstring is not supported on little endian systems");
+ }
+ }
+
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+ SUBTARGET_OVERRIDE_OPTIONS;
+#endif
+}
+\f
+/* Create a CONST_DOUBLE from a string. */
+
+struct rtx_def *
+rs6000_float_const (string, mode)
+ char *string;
+ enum machine_mode mode;
+{
+ REAL_VALUE_TYPE value = REAL_VALUE_ATOF (string, mode);
+ return immed_real_const_1 (value, mode);
+}
+
+\f
+/* Create a CONST_DOUBLE like immed_double_const, except reverse the
+ two parts of the constant if the target is little endian. */
+
+struct rtx_def *
+rs6000_immed_double_const (i0, i1, mode)
+ HOST_WIDE_INT i0, i1;
+ enum machine_mode mode;
+{
+ if (! WORDS_BIG_ENDIAN)
+ return immed_double_const (i1, i0, mode);
+
+ return immed_double_const (i0, i1, mode);
+}
+
\f
/* Return non-zero if this function is known to have a null epilogue. */
int
direct_return ()
{
- return (reload_completed
- && first_reg_to_save () == 32
- && first_fp_reg_to_save () == 64
- && ! regs_ever_live[65]
- && ! rs6000_pushes_stack ());
+ if (reload_completed)
+ {
+ rs6000_stack_t *info = rs6000_stack_info ();
+
+ if (info->first_gp_reg_save == 32
+ && info->first_fp_reg_save == 64
+ && !info->lr_save_p
+ && !info->cr_save_p
+ && !info->push_p)
+ return 1;
+ }
+
+ return 0;
}
/* Returns 1 always. */
return 1;
}
+/* Returns 1 if op is the count register */
+int count_register_operand(op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != REG)
+ return 0;
+
+ if (REGNO (op) == COUNT_REGISTER_REGNUM)
+ return 1;
+
+ if (REGNO (op) > FIRST_PSEUDO_REGISTER)
+ return 1;
+
+ return 0;
+}
+
/* Return 1 if OP is a constant that can fit in a D field. */
int
register rtx op;
enum machine_mode mode;
{
- if (GET_CODE (op) == CONST_INT)
- return short_cint_operand (op, mode);
-
- return gpc_reg_operand (op, mode);
+ return short_cint_operand (op, mode) || gpc_reg_operand (op, mode);
}
/* Similar, except check if the negation of the constant would be valid for
return GET_CODE (op) == CONST_INT || gpc_reg_operand (op, mode);
}
-/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a
- register with one instruction per word. For SFmode, this means that
- the low 16-bits are zero. For DFmode, it means the low 16-bits of
- the first word are zero and the high 16 bits of the second word
- are zero (usually all bits in the low-order word will be zero).
-
- We only do this if we can safely read CONST_DOUBLE_{LOW,HIGH}. */
+/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a register
+ with one instruction per word. We only do this if we can safely read
+ CONST_DOUBLE_{LOW,HIGH}. */
int
easy_fp_constant (op, mode)
register rtx op;
register enum machine_mode mode;
{
- rtx low, high;
-
if (GET_CODE (op) != CONST_DOUBLE
|| GET_MODE (op) != mode
|| GET_MODE_CLASS (mode) != MODE_FLOAT)
return 0;
- high = operand_subword (op, 0, 0, mode);
- low = operand_subword (op, 1, 0, mode);
+ /* Consider all constants with -msoft-float to be easy */
+ if (TARGET_SOFT_FLOAT)
+ return 1;
+
+ if (mode == DFmode)
+ {
+ long k[2];
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
+
+ return (((unsigned) (k[0] + 0x8000) < 0x10000 || (k[0] & 0xffff) == 0)
+ && ((unsigned) (k[1] + 0x8000) < 0x10000 || (k[1] & 0xffff) == 0));
+ }
+ else
+ {
+ long l;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+
+ return ((unsigned) (l + 0x8000) < 0x10000 || (l & 0xffff) == 0);
+ }
+}
+
+/* Return 1 if the operand is in volatile memory. Note that during the
+ RTL generation phase, memory_operand does not return TRUE for
+ volatile memory references. So this function allows us to
+ recognize volatile references where its safe. */
+
+int
+volatile_mem_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ if (!MEM_VOLATILE_P (op))
+ return 0;
- if (high == 0 || GET_CODE (high) != CONST_INT || (INTVAL (high) & 0xffff))
+ if (mode != GET_MODE (op))
return 0;
- return (mode == SFmode
- || (low != 0 && GET_CODE (low) == CONST_INT
- && (INTVAL (low) & 0xffff0000) == 0));
+ if (reload_completed)
+ return memory_operand (op, mode);
+
+ if (reload_in_progress)
+ return strict_memory_address_p (mode, XEXP (op, 0));
+
+ return memory_address_p (mode, XEXP (op, 0));
}
-
+
+/* Return 1 if the operand is an offsettable memory address. */
+
+int
+offsettable_addr_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return offsettable_address_p (reload_completed | reload_in_progress,
+ mode, op);
+}
+
/* Return 1 if the operand is either a floating-point register, a pseudo
register, or memory. */
enum machine_mode mode;
{
return (memory_operand (op, mode)
+ || volatile_mem_operand (op, mode)
|| (register_operand (op, mode)
&& (GET_CODE (op) != REG
|| REGNO (op) >= FIRST_PSEUDO_REGISTER
register rtx op;
register enum machine_mode mode;
{
- return gpc_reg_operand (op, mode) || memory_operand (op, mode);
+ return (gpc_reg_operand (op, mode)
+ || memory_operand (op, mode)
+ || volatile_mem_operand (op, mode));
+}
+
+/* Return 1 if the operand is a general register or memory operand without
+ pre-inc or pre_dec which produces invalid form of PowerPC lwa
+ instruction. */
+
+int
+lwa_operand (op, mode)
+ register rtx op;
+ register enum machine_mode mode;
+{
+ rtx inner = op;
+
+ if (reload_completed && GET_CODE (inner) == SUBREG)
+ inner = SUBREG_REG (inner);
+
+ return gpc_reg_operand (inner, mode)
+ || (memory_operand (inner, mode)
+ && GET_CODE (XEXP (inner, 0)) != PRE_INC
+ && GET_CODE (XEXP (inner, 0)) != PRE_DEC);
}
/* Return 1 if the operand, used inside a MEM, is a valid first argument
register rtx op;
enum machine_mode mode;
{
+ /* Memory is always valid. */
if (memory_operand (op, mode))
return 1;
- /* For floating-point or multi-word mode, only register or memory
- is valid. */
+ /* For floating-point, easy constants are valid. */
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && CONSTANT_P (op)
+ && easy_fp_constant (op, mode))
+ return 1;
+
+ /* For floating-point or multi-word mode, the only remaining valid type
+ is a register. */
if (GET_MODE_CLASS (mode) == MODE_FLOAT
|| GET_MODE_SIZE (mode) > UNITS_PER_WORD)
- return gpc_reg_operand (op, mode);
+ return register_operand (op, mode);
/* The only cases left are integral modes one word or smaller (we
do not get called for MODE_CC values). These can be in any
if (register_operand (op, mode))
return 1;
- /* For HImode and QImode, any constant is valid. */
- if ((mode == HImode || mode == QImode)
- && GET_CODE (op) == CONST_INT)
+ /* For integer modes, any constant is ok. */
+ if (GET_CODE (op) == CONST_INT)
+ return 1;
+
+ /* A SYMBOL_REF referring to the TOC is valid. */
+ if (LEGITIMATE_CONSTANT_POOL_ADDRESS_P (op))
+ return 1;
+
+ /* Windows NT allows SYMBOL_REFs and LABEL_REFs against the TOC
+ directly in the instruction stream */
+ if (DEFAULT_ABI == ABI_NT
+ && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF))
+ return 1;
+
+ /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region
+ to be valid. */
+ if (DEFAULT_ABI == ABI_V4
+ && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST)
+ && small_data_operand (op, Pmode))
return 1;
- /* Otherwise, we will be doing this SET with an add, so anything valid
- for an add will be valid. */
- return add_operand (op, mode);
+ return 0;
}
-\f
-/* Return 1 if OP is a load multiple operation. It is known to be a
- PARALLEL and the first section will be tested. */
+
+/* Return 1 for an operand in small memory on V.4/eabi */
int
-load_multiple_operation (op, mode)
+small_data_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- int count = XVECLEN (op, 0);
- int dest_regno;
- rtx src_addr;
- int i;
+ rtx sym_ref, const_part;
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
+#ifdef TARGET_SDATA
+ if (!TARGET_SDATA)
return 0;
+#endif
- dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
- src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+ if (DEFAULT_ABI != ABI_V4)
+ return 0;
- for (i = 1; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i);
+ if (GET_CODE (op) == SYMBOL_REF)
+ sym_ref = op;
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != SImode
- || REGNO (SET_DEST (elt)) != dest_regno + i
- || GET_CODE (SET_SRC (elt)) != MEM
- || GET_MODE (SET_SRC (elt)) != SImode
- || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
- || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
- || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
- return 0;
- }
+ else if (GET_CODE (op) != CONST
+ || GET_CODE (XEXP (op, 0)) != PLUS
+ || GET_CODE (XEXP (XEXP (op, 0), 0)) != SYMBOL_REF
+ || GET_CODE (XEXP (XEXP (op, 0), 1)) != CONST_INT)
+ return 0;
+
+ else
+ sym_ref = XEXP (XEXP (op, 0), 0);
+
+ if (*XSTR (sym_ref, 0) != '@')
+ return 0;
return 1;
}
-/* Similar, but tests for store multiple. Here, the second vector element
- is a CLOBBER. It will be tested later. */
+\f
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0.
-int
-store_multiple_operation (op, mode)
- rtx op;
- enum machine_mode mode;
+ For incoming args we set the number of arguments in the prototype large
+ so we never return an EXPR_LIST. */
+
+void
+init_cumulative_args (cum, fntype, libname, incoming)
+ CUMULATIVE_ARGS *cum;
+ tree fntype;
+ rtx libname;
+ int incoming;
{
- int count = XVECLEN (op, 0) - 1;
- int src_regno;
- rtx dest_addr;
- int i;
+ static CUMULATIVE_ARGS zero_cumulative;
+ enum rs6000_abi abi = DEFAULT_ABI;
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
- return 0;
+ *cum = zero_cumulative;
+ cum->words = 0;
+ cum->fregno = FP_ARG_MIN_REG;
+ cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+ cum->call_cookie = CALL_NORMAL;
- src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
- dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+ if (incoming)
+ {
+ cum->nargs_prototype = 1000; /* don't return an EXPR_LIST */
+ if (abi == ABI_V4)
+ cum->varargs_offset = RS6000_VARARGS_OFFSET;
+ }
- for (i = 1; i < count; i++)
+ else if (cum->prototype)
+ cum->nargs_prototype = (list_length (TYPE_ARG_TYPES (fntype)) - 1
+ + (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode
+ || RETURN_IN_MEMORY (TREE_TYPE (fntype))));
+
+ else
+ cum->nargs_prototype = 0;
+
+ cum->orig_nargs = cum->nargs_prototype;
+
+ /* Check for DLL import functions */
+ if (abi == ABI_NT
+ && fntype
+ && lookup_attribute ("dllimport", TYPE_ATTRIBUTES (fntype)))
+ cum->call_cookie = CALL_NT_DLLIMPORT;
+
+ /* Also check for longcall's */
+ else if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype)))
+ cum->call_cookie = CALL_LONG;
+
+ if (TARGET_DEBUG_ARG)
{
- rtx elt = XVECEXP (op, 0, i + 1);
+ fprintf (stderr, "\ninit_cumulative_args:");
+ if (fntype)
+ {
+ tree ret_type = TREE_TYPE (fntype);
+ fprintf (stderr, " ret code = %s,",
+ tree_code_name[ (int)TREE_CODE (ret_type) ]);
+ }
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != SImode
- || REGNO (SET_SRC (elt)) != src_regno + i
- || GET_CODE (SET_DEST (elt)) != MEM
- || GET_MODE (SET_DEST (elt)) != SImode
- || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
- || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
- || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
- return 0;
- }
+ if (abi == ABI_V4 && incoming)
+ fprintf (stderr, " varargs = %d, ", cum->varargs_offset);
- return 1;
+ if (cum->call_cookie & CALL_NT_DLLIMPORT)
+ fprintf (stderr, " dllimport,");
+
+ if (cum->call_cookie & CALL_LONG)
+ fprintf (stderr, " longcall,");
+
+ fprintf (stderr, " proto = %d, nargs = %d\n",
+ cum->prototype, cum->nargs_prototype);
+ }
}
\f
-/* Return 1 if OP is a comparison operation that is valid for a branch insn.
- We only check the opcode against the mode of the CC value here. */
+/* If defined, a C expression that gives the alignment boundary, in bits,
+ of an argument with the specified mode and type. If it is not defined,
+ PARM_BOUNDARY is used for all arguments.
+
+ Windows NT wants anything >= 8 bytes to be double word aligned.
+
+ V.4 wants long longs to be double word aligned. */
int
-branch_comparison_operator (op, mode)
- register rtx op;
+function_arg_boundary (mode, type)
enum machine_mode mode;
+ tree type;
{
- enum rtx_code code = GET_CODE (op);
- enum machine_mode cc_mode;
+ if (DEFAULT_ABI == ABI_V4 && mode == DImode)
+ return 64;
- if (GET_RTX_CLASS (code) != '<')
- return 0;
+ if (DEFAULT_ABI != ABI_NT || TARGET_64BIT)
+ return PARM_BOUNDARY;
- cc_mode = GET_MODE (XEXP (op, 0));
+ if (mode != BLKmode)
+ return (GET_MODE_SIZE (mode)) >= 8 ? 64 : 32;
+
+ return (int_size_in_bytes (type) >= 8) ? 64 : 32;
+}
+\f
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+
+void
+function_arg_advance (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ int align = ((cum->words & 1) != 0 && function_arg_boundary (mode, type) == 64) ? 1 : 0;
+ cum->words += align;
+ cum->nargs_prototype--;
+
+ if (DEFAULT_ABI == ABI_V4)
+ {
+ /* Long longs must not be split between registers and stack */
+ if ((GET_MODE_CLASS (mode) != MODE_FLOAT || TARGET_SOFT_FLOAT)
+ && type && !AGGREGATE_TYPE_P (type)
+ && cum->words < GP_ARG_NUM_REG
+ && cum->words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
+ {
+ cum->words = GP_ARG_NUM_REG;
+ }
+
+ /* Aggregates get passed as pointers */
+ if (type && AGGREGATE_TYPE_P (type))
+ cum->words++;
+
+ /* Floats go in registers, & don't occupy space in the GP registers
+ like they do for AIX unless software floating point. */
+ else if (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && TARGET_HARD_FLOAT
+ && cum->fregno <= FP_ARG_V4_MAX_REG)
+ cum->fregno++;
+
+ else
+ cum->words += RS6000_ARG_SIZE (mode, type, 1);
+ }
+ else
+ if (named)
+ {
+ cum->words += RS6000_ARG_SIZE (mode, type, named);
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT)
+ cum->fregno++;
+ }
+
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr,
+ "function_adv: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n",
+ cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align);
+}
+\f
+/* Determine where to put an argument to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis).
+
+ On RS/6000 the first eight words of non-FP are normally in registers
+ and the rest are pushed. Under AIX, the first 13 FP args are in registers.
+ Under V.4, the first 8 FP args are in registers.
+
+ If this is floating-point and no prototype is specified, we use
+ both an FP and integer register (or possibly FP reg and stack). Library
+ functions (when TYPE is zero) always have the proper types for args,
+ so we can pass the FP value just in one register. emit_library_function
+ doesn't support EXPR_LIST anyway. */
+
+struct rtx_def *
+function_arg (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ int align = ((cum->words & 1) != 0 && function_arg_boundary (mode, type) == 64) ? 1 : 0;
+ int align_words = cum->words + align;
+
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr,
+ "function_arg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n",
+ cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align);
+
+ /* Return a marker to indicate whether CR1 needs to set or clear the bit that V.4
+ uses to say fp args were passed in registers. Assume that we don't need the
+ marker for software floating point, or compiler generated library calls. */
+ if (mode == VOIDmode)
+ {
+ enum rs6000_abi abi = DEFAULT_ABI;
+
+ if (abi == ABI_V4
+ && TARGET_HARD_FLOAT
+ && cum->nargs_prototype < 0
+ && type && (cum->prototype || TARGET_NO_PROTOTYPE))
+ {
+ return GEN_INT (cum->call_cookie
+ | ((cum->fregno == FP_ARG_MIN_REG)
+ ? CALL_V4_SET_FP_ARGS
+ : CALL_V4_CLEAR_FP_ARGS));
+ }
+
+ return GEN_INT (cum->call_cookie);
+ }
+
+ if (!named)
+ {
+ if (DEFAULT_ABI != ABI_V4)
+ return NULL_RTX;
+ }
+
+ if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ return NULL_RTX;
+
+ if (USE_FP_FOR_ARG_P (*cum, mode, type))
+ {
+ if ((cum->nargs_prototype > 0)
+ || (DEFAULT_ABI == ABI_V4) /* V.4 never passes FP values in GP registers */
+ || !type)
+ return gen_rtx (REG, mode, cum->fregno);
+
+ return gen_rtx (EXPR_LIST, VOIDmode,
+ ((align_words < GP_ARG_NUM_REG)
+ ? gen_rtx (REG, mode, GP_ARG_MIN_REG + align_words)
+ : NULL_RTX),
+ gen_rtx (REG, mode, cum->fregno));
+ }
+
+ /* Long longs won't be split between register and stack */
+ else if (DEFAULT_ABI == ABI_V4 &&
+ align_words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
+ {
+ return NULL_RTX;
+ }
+
+ else if (align_words < GP_ARG_NUM_REG)
+ return gen_rtx (REG, mode, GP_ARG_MIN_REG + align_words);
+
+ return NULL_RTX;
+}
+\f
+/* For an arg passed partly in registers and partly in memory,
+ this is the number of registers used.
+ For args passed entirely in registers or entirely in memory, zero. */
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ if (! named)
+ return 0;
+
+ if (DEFAULT_ABI == ABI_V4)
+ return 0;
+
+ if (USE_FP_FOR_ARG_P (*cum, mode, type))
+ {
+ if (cum->nargs_prototype >= 0)
+ return 0;
+ }
+
+ if (cum->words < GP_ARG_NUM_REG
+ && GP_ARG_NUM_REG < (cum->words + RS6000_ARG_SIZE (mode, type, named)))
+ {
+ int ret = GP_ARG_NUM_REG - cum->words;
+ if (ret && TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
+
+ return ret;
+ }
+
+ return 0;
+}
+\f
+/* A C expression that indicates when an argument must be passed by
+ reference. If nonzero for an argument, a copy of that argument is
+ made in memory and a pointer to the argument is passed instead of
+ the argument itself. The pointer is passed in whatever way is
+ appropriate for passing a pointer to that type.
+
+ Under V.4, structures and unions are passed by reference. */
+
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ if (DEFAULT_ABI == ABI_V4 && type && AGGREGATE_TYPE_P (type))
+ {
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: aggregate\n");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+\f
+/* 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
+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;
+
+{
+ rtx save_area = virtual_incoming_args_rtx;
+ int reg_size = (TARGET_64BIT) ? 8 : 4;
+
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr,
+ "setup_vararg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, no_rtl= %d\n",
+ cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), no_rtl);
+
+ if (DEFAULT_ABI == ABI_V4 && !no_rtl)
+ {
+ rs6000_sysv_varargs_p = 1;
+ save_area = plus_constant (frame_pointer_rtx, RS6000_VARARGS_OFFSET);
+ }
+
+ if (cum->words < 8)
+ {
+ int first_reg_offset = cum->words;
+
+ if (MUST_PASS_IN_STACK (mode, type))
+ first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1);
+
+ if (first_reg_offset > GP_ARG_NUM_REG)
+ first_reg_offset = GP_ARG_NUM_REG;
+
+ if (!no_rtl && first_reg_offset != GP_ARG_NUM_REG)
+ 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_NUM_REG - first_reg_offset,
+ (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD);
+
+ *pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD;
+ }
+
+ /* Save FP registers if needed. */
+ if (DEFAULT_ABI == ABI_V4 && TARGET_HARD_FLOAT && !no_rtl)
+ {
+ int fregno = cum->fregno;
+ int num_fp_reg = FP_ARG_V4_MAX_REG + 1 - fregno;
+
+ if (num_fp_reg >= 0)
+ {
+ rtx cr1 = gen_rtx (REG, CCmode, 69);
+ rtx lab = gen_label_rtx ();
+ int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
+
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (NE, VOIDmode, cr1, const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode, lab),
+ pc_rtx)));
+
+ while ( num_fp_reg-- >= 0)
+ {
+ emit_move_insn (gen_rtx (MEM, DFmode, plus_constant (save_area, off)),
+ gen_rtx (REG, DFmode, fregno++));
+ off += 8;
+ }
+
+ emit_label (lab);
+ }
+ }
+}
+\f
+/* 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'.
+
+ The argument ARGS is a `tree_list' containing the arguments that
+ were passed to `__builtin_saveregs'.
+
+ If this macro is not defined, the compiler will output an ordinary
+ call to the library function `__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. Under V.4 this does not. */
+
+struct rtx_def *
+expand_builtin_saveregs (args)
+ tree args;
+{
+ return virtual_incoming_args_rtx;
+}
+
+\f
+/* Allocate a stack temp. Only allocate one stack temp per type for a
+ function. */
+
+struct rtx_def *
+rs6000_stack_temp (mode, size)
+ enum machine_mode mode;
+ int size;
+{
+ rtx temp = stack_temps[ (int)mode ];
+ rtx addr;
+
+ if (temp == NULL_RTX)
+ {
+ temp = assign_stack_local (mode, size, 0);
+ addr = XEXP (temp, 0);
+
+ if ((size > 4 && !offsettable_address_p (0, mode, addr))
+ || (size <= 4 && !memory_address_p (mode, addr)))
+ {
+ XEXP (temp, 0) = copy_addr_to_reg (addr);
+ }
+
+ stack_temps[ (int)mode ] = temp;
+ }
+
+ return temp;
+}
+
+\f
+/* Generate a memory reference for expand_block_move, copying volatile,
+ and other bits from an original memory reference. */
+
+static rtx
+expand_block_move_mem (mode, addr, orig_mem)
+ enum machine_mode mode;
+ rtx addr;
+ rtx orig_mem;
+{
+ rtx mem = gen_rtx (MEM, mode, addr);
+
+ RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (orig_mem);
+ MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem);
+ MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (orig_mem);
+#ifdef MEM_UNALIGNED_P
+ MEM_UNALIGNED_P (mem) = MEM_UNALIGNED_P (orig_mem);
+#endif
+ return mem;
+}
+
+/* Expand a block move operation, and return 1 if successful. Return 0
+ if we should let the compiler generate normal code.
+
+ operands[0] is the destination
+ operands[1] is the source
+ operands[2] is the length
+ operands[3] is the alignment */
+
+#define MAX_MOVE_REG 4
+
+int
+expand_block_move (operands)
+ rtx operands[];
+{
+ rtx orig_dest = operands[0];
+ rtx orig_src = operands[1];
+ rtx bytes_rtx = operands[2];
+ rtx align_rtx = operands[3];
+ int constp = (GET_CODE (bytes_rtx) == CONST_INT);
+ int align = XINT (align_rtx, 0);
+ int bytes;
+ int offset;
+ int num_reg;
+ int i;
+ rtx src_reg;
+ rtx dest_reg;
+ rtx src_addr;
+ rtx dest_addr;
+ rtx tmp_reg;
+ rtx stores[MAX_MOVE_REG];
+ int move_bytes;
+
+ /* If this is not a fixed size move, just call memcpy */
+ if (!constp)
+ return 0;
+
+ /* Anything to move? */
+ bytes = INTVAL (bytes_rtx);
+ if (bytes <= 0)
+ return 1;
+
+ /* Don't support real large moves. If string instructions are not used,
+ then don't generate more than 8 loads. */
+ if (TARGET_STRING)
+ {
+ if (bytes > 4*8)
+ return 0;
+ }
+ else if (!STRICT_ALIGNMENT)
+ {
+ if (bytes > 4*8)
+ return 0;
+ }
+ else if (bytes > 8*align)
+ return 0;
+
+ /* Move the address into scratch registers. */
+ dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
+ src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
+
+ if (TARGET_STRING) /* string instructions are available */
+ {
+ for ( ; bytes > 0; bytes -= move_bytes)
+ {
+ if (bytes > 24 /* move up to 32 bytes at a time */
+ && !fixed_regs[5]
+ && !fixed_regs[6]
+ && !fixed_regs[7]
+ && !fixed_regs[8]
+ && !fixed_regs[9]
+ && !fixed_regs[10]
+ && !fixed_regs[11]
+ && !fixed_regs[12])
+ {
+ move_bytes = (bytes > 32) ? 32 : bytes;
+ emit_insn (gen_movstrsi_8reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+ expand_block_move_mem (BLKmode, src_reg, orig_src),
+ GEN_INT ((move_bytes == 32) ? 0 : move_bytes),
+ align_rtx));
+ }
+ else if (bytes > 16 /* move up to 24 bytes at a time */
+ && !fixed_regs[7]
+ && !fixed_regs[8]
+ && !fixed_regs[9]
+ && !fixed_regs[10]
+ && !fixed_regs[11]
+ && !fixed_regs[12])
+ {
+ move_bytes = (bytes > 24) ? 24 : bytes;
+ emit_insn (gen_movstrsi_6reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+ expand_block_move_mem (BLKmode, src_reg, orig_src),
+ GEN_INT (move_bytes),
+ align_rtx));
+ }
+ else if (bytes > 8 /* move up to 16 bytes at a time */
+ && !fixed_regs[9]
+ && !fixed_regs[10]
+ && !fixed_regs[11]
+ && !fixed_regs[12])
+ {
+ move_bytes = (bytes > 16) ? 16 : bytes;
+ emit_insn (gen_movstrsi_4reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+ expand_block_move_mem (BLKmode, src_reg, orig_src),
+ GEN_INT (move_bytes),
+ align_rtx));
+ }
+ else if (bytes > 4 && !TARGET_64BIT)
+ { /* move up to 8 bytes at a time */
+ move_bytes = (bytes > 8) ? 8 : bytes;
+ emit_insn (gen_movstrsi_2reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+ expand_block_move_mem (BLKmode, src_reg, orig_src),
+ GEN_INT (move_bytes),
+ align_rtx));
+ }
+ else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT))
+ { /* move 4 bytes */
+ move_bytes = 4;
+ tmp_reg = gen_reg_rtx (SImode);
+ emit_move_insn (tmp_reg, expand_block_move_mem (SImode, src_reg, orig_src));
+ emit_move_insn (expand_block_move_mem (SImode, dest_reg, orig_dest), tmp_reg);
+ }
+ else if (bytes == 2 && (align >= 2 || !STRICT_ALIGNMENT))
+ { /* move 2 bytes */
+ move_bytes = 2;
+ tmp_reg = gen_reg_rtx (HImode);
+ emit_move_insn (tmp_reg, expand_block_move_mem (HImode, src_reg, orig_src));
+ emit_move_insn (expand_block_move_mem (HImode, dest_reg, orig_dest), tmp_reg);
+ }
+ else if (bytes == 1) /* move 1 byte */
+ {
+ move_bytes = 1;
+ tmp_reg = gen_reg_rtx (QImode);
+ emit_move_insn (tmp_reg, expand_block_move_mem (QImode, src_reg, orig_src));
+ emit_move_insn (expand_block_move_mem (QImode, dest_reg, orig_dest), tmp_reg);
+ }
+ else
+ { /* move up to 4 bytes at a time */
+ move_bytes = (bytes > 4) ? 4 : bytes;
+ emit_insn (gen_movstrsi_1reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+ expand_block_move_mem (BLKmode, src_reg, orig_src),
+ GEN_INT (move_bytes),
+ align_rtx));
+ }
+
+ if (bytes > move_bytes)
+ {
+ emit_insn (gen_addsi3 (src_reg, src_reg, GEN_INT (move_bytes)));
+ emit_insn (gen_addsi3 (dest_reg, dest_reg, GEN_INT (move_bytes)));
+ }
+ }
+ }
+
+ else /* string instructions not available */
+ {
+ num_reg = offset = 0;
+ for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes))
+ {
+ /* Calculate the correct offset for src/dest */
+ if (offset == 0)
+ {
+ src_addr = src_reg;
+ dest_addr = dest_reg;
+ }
+ else
+ {
+ src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset));
+ dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
+ }
+
+ /* Generate the appropriate load and store, saving the stores for later */
+ if (bytes >= 8 && TARGET_64BIT && (align >= 8 || !STRICT_ALIGNMENT))
+ {
+ move_bytes = 8;
+ tmp_reg = gen_reg_rtx (DImode);
+ emit_insn (gen_movdi (tmp_reg, expand_block_move_mem (DImode, src_addr, orig_src)));
+ stores[ num_reg++ ] = gen_movdi (expand_block_move_mem (DImode, dest_addr, orig_dest), tmp_reg);
+ }
+ else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT))
+ {
+ move_bytes = 4;
+ tmp_reg = gen_reg_rtx (SImode);
+ emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (SImode, src_addr, orig_src)));
+ stores[ num_reg++ ] = gen_movsi (expand_block_move_mem (SImode, dest_addr, orig_dest), tmp_reg);
+ }
+ else if (bytes >= 2 && (align >= 2 || !STRICT_ALIGNMENT))
+ {
+ move_bytes = 2;
+ tmp_reg = gen_reg_rtx (HImode);
+ emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (HImode, src_addr, orig_src)));
+ stores[ num_reg++ ] = gen_movhi (expand_block_move_mem (HImode, dest_addr, orig_dest), tmp_reg);
+ }
+ else
+ {
+ move_bytes = 1;
+ tmp_reg = gen_reg_rtx (QImode);
+ emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (QImode, src_addr, orig_src)));
+ stores[ num_reg++ ] = gen_movqi (expand_block_move_mem (QImode, dest_addr, orig_dest), tmp_reg);
+ }
+
+ if (num_reg >= MAX_MOVE_REG)
+ {
+ for (i = 0; i < num_reg; i++)
+ emit_insn (stores[i]);
+ num_reg = 0;
+ }
+ }
+
+ for (i = 0; i < num_reg; i++)
+ emit_insn (stores[i]);
+ }
+
+ return 1;
+}
+
+\f
+/* Return 1 if OP is a load multiple operation. It is known to be a
+ PARALLEL and the first section will be tested. */
+
+int
+load_multiple_operation (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int count = XVECLEN (op, 0);
+ int dest_regno;
+ rtx src_addr;
+ int i;
+
+ /* Perform a quick check so we don't blow up below. */
+ if (count <= 1
+ || GET_CODE (XVECEXP (op, 0, 0)) != SET
+ || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
+ || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
+ return 0;
+
+ dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
+ src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+
+ for (i = 1; i < count; i++)
+ {
+ rtx elt = XVECEXP (op, 0, i);
+
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_DEST (elt)) != REG
+ || GET_MODE (SET_DEST (elt)) != SImode
+ || REGNO (SET_DEST (elt)) != dest_regno + i
+ || GET_CODE (SET_SRC (elt)) != MEM
+ || GET_MODE (SET_SRC (elt)) != SImode
+ || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+ || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+ || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+ || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Similar, but tests for store multiple. Here, the second vector element
+ is a CLOBBER. It will be tested later. */
+
+int
+store_multiple_operation (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int count = XVECLEN (op, 0) - 1;
+ int src_regno;
+ rtx dest_addr;
+ int i;
+
+ /* Perform a quick check so we don't blow up below. */
+ if (count <= 1
+ || GET_CODE (XVECEXP (op, 0, 0)) != SET
+ || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
+ || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
+ return 0;
+
+ src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
+ dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+
+ for (i = 1; i < count; i++)
+ {
+ rtx elt = XVECEXP (op, 0, i + 1);
+
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_SRC (elt)) != REG
+ || GET_MODE (SET_SRC (elt)) != SImode
+ || REGNO (SET_SRC (elt)) != src_regno + i
+ || GET_CODE (SET_DEST (elt)) != MEM
+ || GET_MODE (SET_DEST (elt)) != SImode
+ || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+ || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+ || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+ || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
+ return 0;
+ }
+
+ return 1;
+}
+\f
+/* Return 1 if OP is a comparison operation that is valid for a branch insn.
+ We only check the opcode against the mode of the CC value here. */
+
+int
+branch_comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (op);
+ enum machine_mode cc_mode;
+
+ if (GET_RTX_CLASS (code) != '<')
+ return 0;
+
+ cc_mode = GET_MODE (XEXP (op, 0));
if (GET_MODE_CLASS (cc_mode) != MODE_CC)
return 0;
return (INTVAL (andop) & ~ shift_mask) == 0;
}
+
+/* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates
+ for lfq and stfq insns.
+
+ Note reg1 and reg2 *must* be hard registers. To be sure we will
+ abort if we are passed pseudo registers. */
+
+int
+registers_ok_for_quad_peep (reg1, reg2)
+ rtx reg1, reg2;
+{
+ /* We might have been passed a SUBREG. */
+ if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
+ return 0;
+
+ return (REGNO (reg1) == REGNO (reg2) - 1);
+}
+
+/* Return 1 if addr1 and addr2 are suitable for lfq or stfq insn. addr1 and
+ addr2 must be in consecutive memory locations (addr2 == addr1 + 8). */
+
+int
+addrs_ok_for_quad_peep (addr1, addr2)
+ register rtx addr1;
+ register rtx addr2;
+{
+ int reg1;
+ int offset1;
+
+ /* Extract an offset (if used) from the first addr. */
+ if (GET_CODE (addr1) == PLUS)
+ {
+ /* If not a REG, return zero. */
+ if (GET_CODE (XEXP (addr1, 0)) != REG)
+ return 0;
+ else
+ {
+ reg1 = REGNO (XEXP (addr1, 0));
+ /* The offset must be constant! */
+ if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
+ return 0;
+ offset1 = INTVAL (XEXP (addr1, 1));
+ }
+ }
+ else if (GET_CODE (addr1) != REG)
+ return 0;
+ else
+ {
+ reg1 = REGNO (addr1);
+ /* This was a simple (mem (reg)) expression. Offset is 0. */
+ offset1 = 0;
+ }
+
+ /* Make sure the second address is a (mem (plus (reg) (const_int). */
+ if (GET_CODE (addr2) != PLUS)
+ return 0;
+
+ if (GET_CODE (XEXP (addr2, 0)) != REG
+ || GET_CODE (XEXP (addr2, 1)) != CONST_INT)
+ return 0;
+
+ if (reg1 != REGNO (XEXP (addr2, 0)))
+ return 0;
+
+ /* The offset for the second addr must be 8 more than the first addr. */
+ if (INTVAL (XEXP (addr2, 1)) != offset1 + 8)
+ return 0;
+
+ /* All the tests passed. addr1 and addr2 are valid for lfq or stfq
+ instructions. */
+ return 1;
+}
\f
/* Return the register class of a scratch register needed to copy IN into
or out of a register in CLASS in MODE. If it can be done directly,
switch (code)
{
case '.':
- /* Write out the bit number for "cror" after a call. This differs
- between AIX 3.2 and earlier versions. */
- fprintf (file, "%d", RS6000_CROR_BIT_NUMBER);
+ /* Write out an instruction after the call which may be replaced
+ with glue code by the loader. This depends on the AIX version. */
+ asm_fprintf (file, RS6000_CALL_GLUE);
+ return;
+
+ case '*':
+ /* Write the register number of the TOC register. */
+ fputs (TARGET_MINIMAL_TOC ? reg_names[30] : reg_names[2], file);
return;
case 'A':
/* If X is a constant integer whose low-order 5 bits are zero,
write 'l'. Otherwise, write 'r'. This is a kludge to fix a bug
- in the RS/6000 assembler where "sri" with a zero shift count
+ in the AIX assembler where "sri" with a zero shift count
write a trash instruction. */
if (GET_CODE (x) == CONST_INT && (INTVAL (x) & 31) == 0)
- fprintf (file, "l");
+ putc ('l', file);
else
- fprintf (file, "r");
+ putc ('r', file);
return;
case 'b':
output_operand_lossage ("invalid %%E value");
fprintf(file, "%d", 4 * (REGNO (x) - 68) + 3);
- break;
+ return;
case 'f':
/* X is a CR register. Print the shift count needed to move it
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%G value");
else if (INTVAL (x) >= 0)
- fprintf (file, "z");
+ putc ('z', file);
else
- fprintf (file, "m");
+ putc ('m', file);
return;
case 'h':
return;
case 'H':
- /* X must be a constant. Output the low order 5 bits plus 24. */
- if (! INT_P (x))
- output_operand_lossage ("invalid %%H value");
-
- fprintf (file, "%d", (INT_LOWPART (x) + 24) & 31);
+ /* If constant, output low-order six bits. Otherwise,
+ write normally. */
+ if (INT_P (x))
+ fprintf (file, "%d", INT_LOWPART (x) & 63);
+ else
+ print_operand (file, x, 0);
return;
case 'I':
/* Print `i' if this is a constant, else nothing. */
if (INT_P (x))
- fprintf (file, "i");
+ putc ('i', file);
return;
case 'j':
output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
else
output_address (plus_constant (XEXP (x, 0), 4));
+ if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+ fprintf (file, "@sda21(%s)", reg_names[0]);
}
return;
the left. */
if (val < 0 && (val & 1) == 0)
{
- fprintf (file, "0");
+ putc ('0', file);
return;
}
else if (val >= 0)
the right. */
if ((val & 1) && val >= 0)
{
- fprintf (file, "31");
+ fputs ("31", file);
return;
}
else if ((val & 1) == 0)
fprintf (file, "%d", (32 - INT_LOWPART (x)) & 31);
return;
- case 'S':
- /* Low 5 bits of 31 - value */
- if (! INT_P (x))
- output_operand_lossage ("invalid %%S value");
-
- fprintf (file, "%d", (31 - INT_LOWPART (x)) & 31);
- return;
-
case 't':
/* Write 12 if this jump operation will branch if true, 4 otherwise.
All floating-point operations except NE branch true and integer
|| GET_CODE (x) == EQ
|| GET_CODE (x) == LT || GET_CODE (x) == GT
|| GET_CODE (x) == LTU || GET_CODE (x) == GTU)
- fprintf (file, "12");
+ fputs ("12", file);
else
- fprintf (file, "4");
+ putc ('4', file);
return;
case 'T':
|| GET_CODE (x) == EQ
|| GET_CODE (x) == LT || GET_CODE (x) == GT
|| GET_CODE (x) == LTU || GET_CODE (x) == GTU)
- fprintf (file, "4");
+ putc ('4', file);
else
- fprintf (file, "12");
+ fputs ("12", file);
return;
case 'u':
if (! INT_P (x))
output_operand_lossage ("invalid %%u value");
- fprintf (file, "%d", (INT_LOWPART (x) >> 16) & 0xffff);
+ fprintf (file, "0x%x", (INT_LOWPART (x) >> 16) & 0xffff);
return;
case 'U':
if (GET_CODE (x) == MEM
&& (GET_CODE (XEXP (x, 0)) == PRE_INC
|| GET_CODE (XEXP (x, 0)) == PRE_DEC))
- fprintf (file, "u");
+ putc ('u', file);
return;
case 'w':
case 'X':
if (GET_CODE (x) == MEM
&& LEGITIMATE_INDEXED_ADDRESS_P (XEXP (x, 0)))
- fprintf (file, "x");
+ putc ('x', file);
return;
case 'Y':
output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8));
else
output_address (plus_constant (XEXP (x, 0), 8));
+ if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+ fprintf (file, "@sda21(%s)", reg_names[0]);
}
return;
case 'z':
/* X is a SYMBOL_REF. Write out the name preceded by a
period and without any trailing data in brackets. Used for function
- names. */
+ names. If we are configured for System V (or the embedded ABI) on
+ the PowerPC, do not emit the period, since those systems do not use
+ TOCs and the like. */
if (GET_CODE (x) != SYMBOL_REF)
abort ();
- fprintf (file, ".");
+ if (XSTR (x, 0)[0] != '.')
+ {
+ switch (DEFAULT_ABI)
+ {
+ default:
+ abort ();
+
+ case ABI_AIX:
+ putc ('.', file);
+ break;
+
+ case ABI_V4:
+ case ABI_AIX_NODESC:
+ break;
+
+ case ABI_NT:
+ fputs ("..", file);
+ break;
+ }
+ }
RS6000_OUTPUT_BASENAME (file, XSTR (x, 0));
return;
output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12));
else
output_address (plus_constant (XEXP (x, 0), 12));
+ if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+ fprintf (file, "@sda21(%s)", reg_names[0]);
}
return;
}
else
output_addr_const (file, x);
- break;
+ return;
default:
output_operand_lossage ("invalid %%xn code");
register rtx x;
{
if (GET_CODE (x) == REG)
- fprintf (file, "0(%d)", REGNO (x));
+ fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST)
{
output_addr_const (file, x);
- fprintf (file, "(2)");
+ if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+ fprintf (file, "@sda21(%s)", reg_names[0]);
+
+#ifdef TARGET_NO_TOC
+ else if (TARGET_NO_TOC)
+ ;
+#endif
+ else
+ fprintf (file, "(%s)", reg_names[ TARGET_MINIMAL_TOC ? 30 : 2 ]);
}
else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG)
{
if (REGNO (XEXP (x, 0)) == 0)
- fprintf (file, "%d,%d", REGNO (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+ fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ],
+ reg_names[ REGNO (XEXP (x, 0)) ]);
else
- fprintf (file, "%d,%d", REGNO (XEXP (x, 0)), REGNO (XEXP (x, 1)));
+ fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 0)) ],
+ reg_names[ REGNO (XEXP (x, 1)) ]);
}
else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
- fprintf (file, "%d(%d)", INTVAL (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+ fprintf (file, "%d(%s)", INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
+ else if (TARGET_ELF && !TARGET_64BIT && GET_CODE (x) == LO_SUM
+ && GET_CODE (XEXP (x, 0)) == REG && CONSTANT_P (XEXP (x, 1)))
+ {
+ output_addr_const (file, XEXP (x, 1));
+ fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+ }
else
abort ();
}
to 23 to do this. Don't use the frame pointer in reg 31.
For now, save enough room for all of the parameter registers. */
- if (profile_flag)
+ if (DEFAULT_ABI == ABI_AIX && profile_flag)
if (first_reg > 23)
first_reg = 23;
return first_reg;
}
-/* Return 1 if we need to save CR. */
-
-int
-must_save_cr ()
-{
- return regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72];
-}
-
-/* Compute the size of the save area in the stack, including the space for
- the fixed area. */
-
-int
-rs6000_sa_size ()
-{
- int size;
-
- /* We have the six fixed words, plus the size of the register save
- areas, rounded to a double-word. */
- size = 6 + (32 - first_reg_to_save ()) + (64 - first_fp_reg_to_save ()) * 2;
- if (size & 1)
- size++;
-
- return size * 4;
-}
-
/* Return non-zero if this function makes calls. */
int
return 0;
}
-/* Return non-zero if this function needs to push space on the stack. */
+\f
+/* Calculate the stack information for the current function. This is
+ complicated by having two separate calling sequences, the AIX calling
+ sequence and the V.4 calling sequence.
+
+ AIX stack frames look like:
+
+ SP----> +---------------------------------------+
+ | back chain to caller | 0
+ +---------------------------------------+
+ | saved CR | 4
+ +---------------------------------------+
+ | saved LR | 8
+ +---------------------------------------+
+ | reserved for compilers | 12
+ +---------------------------------------+
+ | reserved for binders | 16
+ +---------------------------------------+
+ | saved TOC pointer | 20
+ +---------------------------------------+
+ | Parameter save area (P) | 24
+ +---------------------------------------+
+ | Alloca space (A) | 24+P
+ +---------------------------------------+
+ | Local variable space (L) | 24+P+A
+ +---------------------------------------+
+ | Save area for GP registers (G) | 24+P+A+L
+ +---------------------------------------+
+ | Save area for FP registers (F) | 24+P+A+L+G
+ +---------------------------------------+
+ old SP->| back chain to caller's caller |
+ +---------------------------------------+
+
+ V.4 stack frames look like:
+
+ SP----> +---------------------------------------+
+ | back chain to caller | 0
+ +---------------------------------------+
+ | caller's saved LR | 4
+ +---------------------------------------+
+ | Parameter save area (P) | 8
+ +---------------------------------------+
+ | Alloca space (A) | 8+P
+ +---------------------------------------+
+ | Varargs save area (V) | 8+P+A
+ +---------------------------------------+
+ | Local variable space (L) | 8+P+A+V
+ +---------------------------------------+
+ | saved CR (C) | 8+P+A+V+L
+ +---------------------------------------+
+ | Save area for GP registers (G) | 8+P+A+V+L+C
+ +---------------------------------------+
+ | Save area for FP registers (F) | 8+P+A+V+L+C+G
+ +---------------------------------------+
+ old SP->| back chain to caller's caller |
+ +---------------------------------------+
+
+
+ A PowerPC Windows/NT frame looks like:
+
+ SP----> +---------------------------------------+
+ | back chain to caller | 0
+ +---------------------------------------+
+ | reserved | 4
+ +---------------------------------------+
+ | reserved | 8
+ +---------------------------------------+
+ | reserved | 12
+ +---------------------------------------+
+ | reserved | 16
+ +---------------------------------------+
+ | reserved | 20
+ +---------------------------------------+
+ | Parameter save area (P) | 24
+ +---------------------------------------+
+ | Alloca space (A) | 24+P
+ +---------------------------------------+
+ | Local variable space (L) | 24+P+A
+ +---------------------------------------+
+ | Save area for FP registers (F) | 24+P+A+L
+ +---------------------------------------+
+ | Possible alignment area (X) | 24+P+A+L+F
+ +---------------------------------------+
+ | Save area for GP registers (G) | 24+P+A+L+F+X
+ +---------------------------------------+
+ | Save area for CR (C) | 24+P+A+L+F+X+G
+ +---------------------------------------+
+ | Save area for TOC (T) | 24+P+A+L+F+X+G+C
+ +---------------------------------------+
+ | Save area for LR (R) | 24+P+A+L+F+X+G+C+T
+ +---------------------------------------+
+ old SP->| back chain to caller's caller |
+ +---------------------------------------+
+
+ For NT, there is no specific order to save the registers, but in
+ order to support __builtin_return_address, the save area for the
+ link register needs to be in a known place, so we use -4 off of the
+ old SP. To support calls through pointers, we also allocate a
+ fixed slot to store the TOC, -8 off the old SP. */
+
+rs6000_stack_t *
+rs6000_stack_info ()
+{
+ static rs6000_stack_t info, zero_info;
+ rs6000_stack_t *info_ptr = &info;
+ int reg_size = TARGET_64BIT ? 8 : 4;
+ enum rs6000_abi abi;
+ int total_raw_size;
+
+ /* Zero all fields portably */
+ info = zero_info;
-int
-rs6000_pushes_stack ()
+ /* Select which calling sequence */
+ info_ptr->abi = abi = DEFAULT_ABI;
+
+ /* Calculate which registers need to be saved & save area size */
+ info_ptr->first_gp_reg_save = first_reg_to_save ();
+ info_ptr->gp_size = reg_size * (32 - info_ptr->first_gp_reg_save);
+
+ info_ptr->first_fp_reg_save = first_fp_reg_to_save ();
+ info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save);
+
+ /* Does this function call anything? */
+ info_ptr->calls_p = rs6000_makes_calls ();
+
+ /* Do we need to allocate space to save the toc? */
+ if (rs6000_save_toc_p)
+ {
+ info_ptr->toc_save_p = 1;
+ info_ptr->toc_size = reg_size;
+ }
+
+ /* If this is main and we need to call a function to set things up,
+ save main's arguments around the call. */
+ if (strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), "main") == 0)
+ {
+ info_ptr->main_p = 1;
+
+#ifdef NAME__MAIN
+ info_ptr->calls_p = 1;
+
+ if (DECL_ARGUMENTS (current_function_decl))
+ {
+ int i;
+ tree arg;
+
+ info_ptr->main_save_p = 1;
+ info_ptr->main_size = 0;
+
+ for ((i = 0), (arg = DECL_ARGUMENTS (current_function_decl));
+ arg != NULL_TREE && i < 8;
+ (arg = TREE_CHAIN (arg)), i++)
+ {
+ info_ptr->main_size += reg_size;
+ }
+ }
+#endif
+ }
+
+ /* Determine if we need to save the link register */
+ if (regs_ever_live[65] || profile_flag
+#ifdef TARGET_RELOCATABLE
+ || (TARGET_RELOCATABLE && (get_pool_size () != 0))
+#endif
+ || (info_ptr->first_fp_reg_save != 64
+ && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
+ || (abi == ABI_V4 && current_function_calls_alloca)
+ || info_ptr->calls_p)
+ {
+ info_ptr->lr_save_p = 1;
+ regs_ever_live[65] = 1;
+ if (abi == ABI_NT)
+ info_ptr->lr_size = reg_size;
+ }
+
+ /* Determine if we need to save the condition code registers */
+ if (regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72])
+ {
+ info_ptr->cr_save_p = 1;
+ if (abi == ABI_V4 || abi == ABI_NT)
+ info_ptr->cr_size = reg_size;
+ }
+
+ /* Determine various sizes */
+ info_ptr->reg_size = reg_size;
+ info_ptr->fixed_size = RS6000_SAVE_AREA;
+ info_ptr->varargs_size = RS6000_VARARGS_AREA;
+ info_ptr->vars_size = ALIGN (get_frame_size (), 8);
+ info_ptr->parm_size = ALIGN (current_function_outgoing_args_size, 8);
+ info_ptr->save_size = ALIGN (info_ptr->fp_size
+ + info_ptr->gp_size
+ + info_ptr->cr_size
+ + info_ptr->lr_size
+ + info_ptr->toc_size
+ + info_ptr->main_size, 8);
+
+ total_raw_size = (info_ptr->vars_size
+ + info_ptr->parm_size
+ + info_ptr->save_size
+ + info_ptr->varargs_size
+ + info_ptr->fixed_size);
+
+ info_ptr->total_size = ALIGN (total_raw_size, STACK_BOUNDARY / BITS_PER_UNIT);
+
+ /* Determine if we need to allocate any stack frame.
+ For AIX We need to push the stack if a frame pointer is needed (because
+ the stack might be dynamically adjusted), if we are debugging, if the
+ total stack size is more than 220 bytes, or if we make calls.
+
+ For V.4 we don't have the stack cushion that AIX uses, but assume that
+ the debugger can handle stackless frames. */
+
+ if (info_ptr->calls_p)
+ info_ptr->push_p = 1;
+
+ else if (abi == ABI_V4 || abi == ABI_NT)
+ info_ptr->push_p = (total_raw_size > info_ptr->fixed_size
+ || info_ptr->lr_save_p);
+
+ else
+ info_ptr->push_p = (frame_pointer_needed
+ || write_symbols != NO_DEBUG
+ || info_ptr->total_size > 220);
+
+ /* Calculate the offsets */
+ switch (abi)
+ {
+ case ABI_NONE:
+ default:
+ abort ();
+
+ case ABI_AIX:
+ case ABI_AIX_NODESC:
+ info_ptr->fp_save_offset = - info_ptr->fp_size;
+ info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size;
+ info_ptr->main_save_offset = info_ptr->gp_save_offset - info_ptr->main_size;
+ info_ptr->cr_save_offset = 4;
+ info_ptr->lr_save_offset = 8;
+ break;
+
+ case ABI_V4:
+ info_ptr->fp_save_offset = - info_ptr->fp_size;
+ info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size;
+ info_ptr->cr_save_offset = info_ptr->gp_save_offset - reg_size;
+ info_ptr->toc_save_offset = info_ptr->cr_save_offset - info_ptr->cr_size;
+ info_ptr->main_save_offset = info_ptr->toc_save_offset - info_ptr->toc_size;
+ info_ptr->lr_save_offset = reg_size;
+ break;
+
+ case ABI_NT:
+ info_ptr->lr_save_offset = -4;
+ info_ptr->toc_save_offset = info_ptr->lr_save_offset - info_ptr->lr_size;
+ info_ptr->cr_save_offset = info_ptr->toc_save_offset - info_ptr->toc_size;
+ info_ptr->gp_save_offset = info_ptr->cr_save_offset - info_ptr->cr_size - info_ptr->gp_size + reg_size;
+ info_ptr->fp_save_offset = info_ptr->gp_save_offset - info_ptr->fp_size;
+ if (info_ptr->fp_size && ((- info_ptr->fp_save_offset) % 8) != 0)
+ info_ptr->fp_save_offset -= 4;
+
+ info_ptr->main_save_offset = info_ptr->fp_save_offset - info_ptr->main_size;
+ break;
+ }
+
+ /* Zero offsets if we're not saving those registers */
+ if (!info_ptr->fp_size)
+ info_ptr->fp_save_offset = 0;
+
+ if (!info_ptr->gp_size)
+ info_ptr->gp_save_offset = 0;
+
+ if (!info_ptr->lr_save_p)
+ info_ptr->lr_save_offset = 0;
+
+ if (!info_ptr->cr_save_p)
+ info_ptr->cr_save_offset = 0;
+
+ if (!info_ptr->toc_save_p)
+ info_ptr->toc_save_offset = 0;
+
+ if (!info_ptr->main_save_p)
+ info_ptr->main_save_offset = 0;
+
+ return info_ptr;
+}
+
+void
+debug_stack_info (info)
+ rs6000_stack_t *info;
{
- int total_size = (rs6000_sa_size () + get_frame_size ()
- + current_function_outgoing_args_size);
+ char *abi_string;
+
+ if (!info)
+ info = rs6000_stack_info ();
+
+ fprintf (stderr, "\nStack information for function %s:\n",
+ ((current_function_decl && DECL_NAME (current_function_decl))
+ ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
+ : "<unknown>"));
+
+ switch (info->abi)
+ {
+ default: abi_string = "Unknown"; break;
+ case ABI_NONE: abi_string = "NONE"; break;
+ case ABI_AIX: abi_string = "AIX"; break;
+ case ABI_AIX_NODESC: abi_string = "AIX"; break;
+ case ABI_V4: abi_string = "V.4"; break;
+ case ABI_NT: abi_string = "NT"; break;
+ }
+
+ fprintf (stderr, "\tABI = %5s\n", abi_string);
+
+ if (info->first_gp_reg_save != 32)
+ fprintf (stderr, "\tfirst_gp_reg_save = %5d\n", info->first_gp_reg_save);
+
+ if (info->first_fp_reg_save != 64)
+ fprintf (stderr, "\tfirst_fp_reg_save = %5d\n", info->first_fp_reg_save);
+
+ if (info->lr_save_p)
+ fprintf (stderr, "\tlr_save_p = %5d\n", info->lr_save_p);
+
+ if (info->cr_save_p)
+ fprintf (stderr, "\tcr_save_p = %5d\n", info->cr_save_p);
+
+ if (info->toc_save_p)
+ fprintf (stderr, "\ttoc_save_p = %5d\n", info->toc_save_p);
+
+ if (info->push_p)
+ fprintf (stderr, "\tpush_p = %5d\n", info->push_p);
+
+ if (info->calls_p)
+ fprintf (stderr, "\tcalls_p = %5d\n", info->calls_p);
+
+ if (info->main_p)
+ fprintf (stderr, "\tmain_p = %5d\n", info->main_p);
+
+ if (info->main_save_p)
+ fprintf (stderr, "\tmain_save_p = %5d\n", info->main_save_p);
+
+ if (info->gp_save_offset)
+ fprintf (stderr, "\tgp_save_offset = %5d\n", info->gp_save_offset);
+
+ if (info->fp_save_offset)
+ fprintf (stderr, "\tfp_save_offset = %5d\n", info->fp_save_offset);
+
+ if (info->lr_save_offset)
+ fprintf (stderr, "\tlr_save_offset = %5d\n", info->lr_save_offset);
+
+ if (info->cr_save_offset)
+ fprintf (stderr, "\tcr_save_offset = %5d\n", info->cr_save_offset);
+
+ if (info->toc_save_offset)
+ fprintf (stderr, "\ttoc_save_offset = %5d\n", info->toc_save_offset);
+
+ if (info->varargs_save_offset)
+ fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset);
+
+ if (info->main_save_offset)
+ fprintf (stderr, "\tmain_save_offset = %5d\n", info->main_save_offset);
- /* We need to push the stack if a frame pointer is needed (because the
- stack might be dynamically adjusted), if we are debugging, if the
- total stack size is more than 220 bytes, or if we make calls. */
+ if (info->total_size)
+ fprintf (stderr, "\ttotal_size = %5d\n", info->total_size);
- return (frame_pointer_needed || write_symbols != NO_DEBUG
- || total_size > 220
- || rs6000_makes_calls ());
+ if (info->varargs_size)
+ fprintf (stderr, "\tvarargs_size = %5d\n", info->varargs_size);
+
+ if (info->vars_size)
+ fprintf (stderr, "\tvars_size = %5d\n", info->vars_size);
+
+ if (info->parm_size)
+ fprintf (stderr, "\tparm_size = %5d\n", info->parm_size);
+
+ if (info->fixed_size)
+ fprintf (stderr, "\tfixed_size = %5d\n", info->fixed_size);
+
+ if (info->gp_size)
+ fprintf (stderr, "\tgp_size = %5d\n", info->gp_size);
+
+ if (info->fp_size)
+ fprintf (stderr, "\tfp_size = %5d\n", info->fp_size);
+
+ if (info->lr_size)
+ fprintf (stderr, "\tlr_size = %5d\n", info->cr_size);
+
+ if (info->cr_size)
+ fprintf (stderr, "\tcr_size = %5d\n", info->cr_size);
+
+ if (info->toc_size)
+ fprintf (stderr, "\ttoc_size = %5d\n", info->toc_size);
+
+ if (info->main_size)
+ fprintf (stderr, "\tmain_size = %5d\n", info->main_size);
+
+ if (info->save_size)
+ fprintf (stderr, "\tsave_size = %5d\n", info->save_size);
+
+ if (info->reg_size != 4)
+ fprintf (stderr, "\treg_size = %5d\n", info->reg_size);
+
+ fprintf (stderr, "\n");
}
+\f
/* Write function prologue. */
-
void
output_prolog (file, size)
FILE *file;
int size;
{
- int first_reg = first_reg_to_save ();
- int must_push = rs6000_pushes_stack ();
- int first_fp_reg = first_fp_reg_to_save ();
- int basic_size = rs6000_sa_size ();
- int total_size = (basic_size + size + current_function_outgoing_args_size);
+ rs6000_stack_t *info = rs6000_stack_info ();
+ int reg_size = info->reg_size;
+ char *store_reg;
+ char *load_reg;
+ int sp_reg = 1;
+ int sp_offset = 0;
+
+ if (TARGET_32BIT)
+ {
+ store_reg = "\t{st|stw} %s,%d(%s)\n";
+ load_reg = "\t{l|lwz} %s,%d(%s)\n";
+ }
+ else
+ {
+ store_reg = "\tstd %s,%d(%s)\n";
+ load_reg = "\tlld %s,%d(%s)\n";
+ }
- /* Round size to multiple of 8 bytes. */
- total_size = (total_size + 7) & ~7;
+ if (TARGET_DEBUG_STACK)
+ debug_stack_info (info);
/* Write .extern for any function we will call to save and restore fp
values. */
- if (first_fp_reg < 62)
- fprintf (file, "\t.extern ._savef%d\n\t.extern ._restf%d\n",
- first_fp_reg - 32, first_fp_reg - 32);
+ if (info->first_fp_reg_save < 64 && !FP_SAVE_INLINE (info->first_fp_reg_save))
+ fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n",
+ SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX,
+ RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
/* Write .extern for truncation routines, if needed. */
if (rs6000_trunc_used && ! trunc_defined)
{
- fprintf (file, "\t.extern .itrunc\n\t.extern .uitrunc\n");
+ fprintf (file, "\t.extern .%s\n\t.extern .%s\n",
+ RS6000_ITRUNC, RS6000_UITRUNC);
trunc_defined = 1;
}
- /* If we have to call a function to save fpr's, or if we are doing profiling,
- then we will be using LR. */
- if (first_fp_reg < 62 || profile_flag)
- regs_ever_live[65] = 1;
+ /* Write .extern for AIX common mode routines, if needed. */
+ if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
+ {
+ fputs ("\t.extern __mulh\n", file);
+ fputs ("\t.extern __mull\n", file);
+ fputs ("\t.extern __divss\n", file);
+ fputs ("\t.extern __divus\n", file);
+ fputs ("\t.extern __quoss\n", file);
+ fputs ("\t.extern __quous\n", file);
+ common_mode_defined = 1;
+ }
+
+ /* For V.4, update stack before we do any saving and set back pointer. */
+ if (info->push_p && DEFAULT_ABI == ABI_V4)
+ {
+ if (info->total_size < 32767)
+ {
+ asm_fprintf (file,
+ (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n",
+ reg_names[1], - info->total_size, reg_names[1]);
+ sp_offset = info->total_size;
+ }
+ else
+ {
+ int neg_size = - info->total_size;
+ sp_reg = 12;
+ asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]);
+ asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+ reg_names[0], (neg_size >> 16) & 0xffff,
+ reg_names[0], reg_names[0], neg_size & 0xffff);
+ asm_fprintf (file,
+ (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n",
+ reg_names[1], reg_names[1], reg_names[0]);
+ }
+ }
/* If we use the link register, get it into r0. */
- if (regs_ever_live[65])
- fprintf (file, "\tmflr 0\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, "\tmflr %s\n", reg_names[0]);
/* If we need to save CR, put it into r12. */
- if (must_save_cr ())
- fprintf (file, "\tmfcr 12\n");
+ if (info->cr_save_p && sp_reg != 12)
+ asm_fprintf (file, "\tmfcr %s\n", reg_names[12]);
/* Do any required saving of fpr's. If only one or two to save, do it
ourself. Otherwise, call function. Note that since they are statically
linked, we do not need a nop following them. */
- if (first_fp_reg == 62)
- fprintf (file, "\tstfd 30,-16(1)\n\tstfd 31,-8(1)\n");
- else if (first_fp_reg == 63)
- fprintf (file, "\tstfd 31,-8(1)\n");
- else if (first_fp_reg != 64)
- fprintf (file, "\tbl ._savef%d\n", first_fp_reg - 32);
+ if (FP_SAVE_INLINE (info->first_fp_reg_save))
+ {
+ int regno = info->first_fp_reg_save;
+ int loc = info->fp_save_offset + sp_offset;
+
+ for ( ; regno < 64; regno++, loc += 8)
+ asm_fprintf (file, "\tstfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]);
+ }
+ else if (info->first_fp_reg_save != 64)
+ asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX,
+ info->first_fp_reg_save - 32, SAVE_FP_SUFFIX);
/* Now save gpr's. */
- if (first_reg == 31)
- fprintf (file, "\tst 31,%d(1)\n", -4 - (64 - first_fp_reg) * 8);
- else if (first_reg != 32)
- fprintf (file, "\tstm %d,%d(1)\n", first_reg,
- - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+ if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
+ {
+ int regno = info->first_gp_reg_save;
+ int loc = info->gp_save_offset + sp_offset;
+
+ for ( ; regno < 32; regno++, loc += reg_size)
+ asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]);
+ }
+
+ else if (info->first_gp_reg_save != 32)
+ asm_fprintf (file, "\t{stm|stmw} %s,%d(%s)\n",
+ reg_names[info->first_gp_reg_save],
+ info->gp_save_offset + sp_offset,
+ reg_names[sp_reg]);
+
+ /* Save main's arguments if we need to call a function */
+#ifdef NAME__MAIN
+ if (info->main_save_p)
+ {
+ int regno;
+ int loc = info->main_save_offset;
+ int size = info->main_size;
+
+ for (regno = 3; size > 0; regno++, loc -= reg_size, size -= reg_size)
+ asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]);
+ }
+#endif
/* Save lr if we used it. */
- if (regs_ever_live[65])
- fprintf (file, "\tst 0,8(1)\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, store_reg, reg_names[0], info->lr_save_offset + sp_offset,
+ reg_names[sp_reg]);
+
+ /* Save CR if we use any that must be preserved. */
+ if (info->cr_save_p)
+ {
+ if (sp_reg == 12) /* If r12 is used to hold the original sp, copy cr now */
+ {
+ asm_fprintf (file, "\tmfcr %s\n", reg_names[0]);
+ asm_fprintf (file, store_reg, reg_names[0],
+ info->cr_save_offset + sp_offset,
+ reg_names[sp_reg]);
+ }
+ else
+ asm_fprintf (file, store_reg, reg_names[12], info->cr_save_offset + sp_offset,
+ reg_names[sp_reg]);
+ }
+
+ if (info->toc_save_p)
+ asm_fprintf (file, store_reg, reg_names[2], info->toc_save_offset + sp_offset,
+ reg_names[sp_reg]);
+
+ /* NT needs us to probe the stack frame every 4k pages for large frames, so
+ do it here. */
+ if (DEFAULT_ABI == ABI_NT && info->total_size > 4096)
+ {
+ if (info->total_size < 32768)
+ {
+ int probe_offset = 4096;
+ while (probe_offset < info->total_size)
+ {
+ asm_fprintf (file, "\t{l|lwz} %s,%d(%s)\n", reg_names[0], -probe_offset, reg_names[1]);
+ probe_offset += 4096;
+ }
+ }
+ else
+ {
+ int probe_iterations = info->total_size / 4096;
+ static int probe_labelno = 0;
+ char buf[256];
+
+ if (probe_iterations < 32768)
+ asm_fprintf (file, "\tli %s,%d\n", reg_names[12], probe_iterations);
+ else
+ {
+ asm_fprintf (file, "\tlis %s,%d\n", reg_names[12], probe_iterations >> 16);
+ if (probe_iterations & 0xffff)
+ asm_fprintf (file, "\tori %s,%s,%d\n", reg_names[12], reg_names[12],
+ probe_iterations & 0xffff);
+ }
+ asm_fprintf (file, "\tmtctr %s\n", reg_names[12]);
+ asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]);
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LCprobe", probe_labelno);
+ asm_fprintf (file, "\t{lu|lwzu} %s,-4096(%s)\n", reg_names[0], reg_names[12]);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCprobe", probe_labelno);
+ fputs ("\tbdnz ", file);
+ assemble_name (file, buf);
+ fputs ("\n", file);
+ }
+ }
+
+ /* Update stack and set back pointer and we have already done so for V.4. */
+ if (info->push_p && DEFAULT_ABI != ABI_V4)
+ {
+ if (info->total_size < 32767)
+ asm_fprintf (file,
+ (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n",
+ reg_names[1], - info->total_size, reg_names[1]);
+ else
+ {
+ int neg_size = - info->total_size;
+ asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+ reg_names[0], (neg_size >> 16) & 0xffff,
+ reg_names[0], reg_names[0], neg_size & 0xffff);
+ asm_fprintf (file,
+ (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n",
+ reg_names[1], reg_names[1], reg_names[0]);
+ }
+ }
+
+ /* Set frame pointer, if needed. */
+ if (frame_pointer_needed)
+ asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]);
+
+#ifdef NAME__MAIN
+ /* If we need to call a function to set things up for main, do so now
+ before dealing with the TOC. */
+ if (info->main_p)
+ {
+ char *prefix = "";
+
+ switch (DEFAULT_ABI)
+ {
+ case ABI_AIX: prefix = "."; break;
+ case ABI_NT: prefix = ".."; break;
+ }
+
+ fprintf (file, "\tbl %s%s\n", prefix, NAME__MAIN);
+#ifdef RS6000_CALL_GLUE2
+ fprintf (file, "\t%s%s%s\n", RS6000_CALL_GLUE2, prefix, NAME_MAIN);
+#else
+#ifdef RS6000_CALL_GLUE
+ if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT)
+ fprintf (file, "\t%s\n", RS6000_CALL_GLUE);
+#endif
+#endif
+
+ if (info->main_save_p)
+ {
+ int regno;
+ int loc;
+ int size = info->main_size;
+
+ if (info->total_size < 32767)
+ {
+ loc = info->total_size + info->main_save_offset;
+ for (regno = 3; size > 0; regno++, size -= reg_size, loc -= reg_size)
+ asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[1]);
+ }
+ else
+ { /* for large AIX/NT frames, reg 0 above contains -frame size */
+ /* for V.4, we need to reload -frame size */
+ loc = info->main_save_offset;
+ if (DEFAULT_ABI == ABI_V4 && info->total_size > 32767)
+ {
+ int neg_size = - info->total_size;
+ asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+ reg_names[0], (neg_size >> 16) & 0xffff,
+ reg_names[0], reg_names[0], neg_size & 0xffff);
+ }
+
+ asm_fprintf (file, "\t{sf|subf} %s,%s,%s\n", reg_names[0], reg_names[0],
+ reg_names[1]);
+
+ for (regno = 3; size > 0; regno++, size -= reg_size, loc -= reg_size)
+ asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[0]);
+ }
+ }
+ }
+#endif
+
+
+ /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the
+ TOC_TABLE address into register 30. */
+ if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
+ {
+ char buf[256];
+
+#ifdef TARGET_RELOCATABLE
+ if (TARGET_RELOCATABLE)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+ fputs ("\tbl ", file);
+ assemble_name (file, buf);
+ putc ('\n', file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno);
+ fprintf (file, "\tmflr %s\n", reg_names[30]);
+
+ asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz}" : "\tld");
+ fprintf (file, " %s,(", reg_names[0]);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno);
+ assemble_name (file, buf);
+ putc ('-', file);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+ assemble_name (file, buf);
+ fprintf (file, ")(%s)\n", reg_names[30]);
+ asm_fprintf (file, "\t{cax|add} %s,%s,%s\n",
+ reg_names[30], reg_names[0], reg_names[30]);
+ rs6000_pic_labelno++;
+ }
+ else
+#endif
+
+ switch (DEFAULT_ABI)
+ {
+ case ABI_V4:
+ case ABI_AIX_NODESC:
+ if (TARGET_32BIT)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1);
+ asm_fprintf (file, "\t{cau|addis} %s,%s,", reg_names[30], reg_names[0]);
+ assemble_name (file, buf);
+ asm_fprintf (file, "@ha\n");
+ if (TARGET_NEW_MNEMONICS)
+ {
+ asm_fprintf (file, "\taddi %s,%s,", reg_names[30], reg_names[30]);
+ assemble_name (file, buf);
+ asm_fprintf (file, "@l\n");
+ }
+ else
+ {
+ asm_fprintf (file, "\tcal %s,", reg_names[30]);
+ assemble_name (file, buf);
+ asm_fprintf (file, "@l(%s)\n", reg_names[30]);
+ }
+ }
+ else
+ abort ();
- /* Save CR if we use any that must be preserved. */
- if (must_save_cr ())
- fprintf (file, "\tst 12,4(1)\n");
+ break;
- /* Update stack and set back pointer. */
- if (must_push)
- {
- if (total_size < 32767)
- fprintf (file, "\tstu 1,%d(1)\n", - total_size);
- else
- {
- fprintf (file, "\tcau 0,0,%d\n\toril 0,0,%d\n",
- (total_size >> 16) & 0xffff, total_size & 0xffff);
- fprintf (file, "\tsf 12,0,1\n\tst 1,0(12)\n\toril 1,12,0\n");
+ case ABI_NT:
+ case ABI_AIX:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0);
+ asm_fprintf (file, "\t{l|lwz} %s,", reg_names[30]);
+ assemble_name (file, buf);
+ asm_fprintf (file, "(%s)\n", reg_names[2]);
+ break;
}
}
- /* Set frame pointer, if needed. */
- if (frame_pointer_needed)
- fprintf (file, "\toril 31,1,0\n");
+ if (DEFAULT_ABI == ABI_NT)
+ {
+ assemble_name (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+ fputs (".b:\n", file);
+ }
}
/* Write function epilogue. */
FILE *file;
int size;
{
- int first_reg = first_reg_to_save ();
- int must_push = rs6000_pushes_stack ();
- int first_fp_reg = first_fp_reg_to_save ();
- int basic_size = rs6000_sa_size ();
- int total_size = (basic_size + size + current_function_outgoing_args_size);
+ rs6000_stack_t *info = rs6000_stack_info ();
+ char *load_reg = (TARGET_32BIT) ? "\t{l|lwz} %s,%d(%s)\n" : "\tld %s,%d(%s)\n";
rtx insn = get_last_insn ();
+ int sp_reg = 1;
+ int sp_offset = 0;
+ int i;
- /* Round size to multiple of 8 bytes. */
- total_size = (total_size + 7) & ~7;
+ /* Forget about any temporaries created */
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ stack_temps[i] = NULL_RTX;
/* If the last insn was a BARRIER, we don't have to write anything except
the trace table. */
frame, restore the old stack pointer using the backchain. Otherwise,
we know what size to update it with. */
if (frame_pointer_needed || current_function_calls_alloca
- || total_size > 32767)
- fprintf (file, "\tl 1,0(1)\n");
- else if (must_push)
- fprintf (file, "\tai 1,1,%d\n", total_size);
+ || info->total_size > 32767)
+ {
+ /* Under V.4, don't reset the stack pointer until after we're done
+ loading the saved registers. */
+ if (DEFAULT_ABI == ABI_V4)
+ sp_reg = 11;
+
+ asm_fprintf (file, load_reg, reg_names[sp_reg], 0, reg_names[1]);
+ }
+ else if (info->push_p)
+ {
+ if (DEFAULT_ABI == ABI_V4)
+ sp_offset = info->total_size;
+ else if (TARGET_NEW_MNEMONICS)
+ asm_fprintf (file, "\taddi %s,%s,%d\n", reg_names[1], reg_names[1], info->total_size);
+ else
+ asm_fprintf (file, "\tcal %s,%d(%s)\n", reg_names[1], info->total_size, reg_names[1]);
+ }
/* Get the old lr if we saved it. */
- if (regs_ever_live[65])
- fprintf (file, "\tl 0,8(1)\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, load_reg, reg_names[0], info->lr_save_offset + sp_offset, reg_names[sp_reg]);
/* Get the old cr if we saved it. */
- if (must_save_cr ())
- fprintf (file, "\tl 12,4(1)\n");
+ if (info->cr_save_p)
+ asm_fprintf (file, load_reg, reg_names[12], info->cr_save_offset + sp_offset, reg_names[sp_reg]);
/* Set LR here to try to overlap restores below. */
- if (regs_ever_live[65])
- fprintf (file, "\tmtlr 0\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, "\tmtlr %s\n", reg_names[0]);
/* Restore gpr's. */
- if (first_reg == 31)
- fprintf (file, "\tl 31,%d(1)\n", -4 - (64 - first_fp_reg) * 8);
- else if (first_reg != 32)
- fprintf (file, "\tlm %d,%d(1)\n", first_reg,
- - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+ if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
+ {
+ int regno = info->first_gp_reg_save;
+ int loc = info->gp_save_offset + sp_offset;
+ int reg_size = (TARGET_32BIT) ? 4 : 8;
+
+ for ( ; regno < 32; regno++, loc += reg_size)
+ asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[sp_reg]);
+ }
+
+ else if (info->first_gp_reg_save != 32)
+ asm_fprintf (file, "\t{lm|lmw} %s,%d(%s)\n",
+ reg_names[info->first_gp_reg_save],
+ info->gp_save_offset + sp_offset,
+ reg_names[sp_reg]);
/* Restore fpr's if we can do it without calling a function. */
- if (first_fp_reg == 62)
- fprintf (file, "\tlfd 30,-16(1)\n\tlfd 31,-8(1)\n");
- else if (first_fp_reg == 63)
- fprintf (file, "\tlfd 31,-8(1)\n");
+ if (FP_SAVE_INLINE (info->first_fp_reg_save))
+ {
+ int regno = info->first_fp_reg_save;
+ int loc = info->fp_save_offset + sp_offset;
+
+ for ( ; regno < 64; regno++, loc += 8)
+ asm_fprintf (file, "\tlfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]);
+ }
/* If we saved cr, restore it here. Just those of cr2, cr3, and cr4
that were used. */
- if (must_save_cr ())
- fprintf (file, "\tmtcrf %d,12\n",
- (regs_ever_live[70] != 0) * 0x20
- + (regs_ever_live[71] != 0) * 0x10
- + (regs_ever_live[72] != 0) * 0x8);
+ if (info->cr_save_p)
+ asm_fprintf (file, "\tmtcrf %d,%s\n",
+ (regs_ever_live[70] != 0) * 0x20
+ + (regs_ever_live[71] != 0) * 0x10
+ + (regs_ever_live[72] != 0) * 0x8, reg_names[12]);
+
+ /* If this is V.4, unwind the stack pointer after all of the loads have been done */
+ if (sp_offset)
+ {
+ if (TARGET_NEW_MNEMONICS)
+ asm_fprintf (file, "\taddi %s,%s,%d\n", reg_names[1], reg_names[1], sp_offset);
+ else
+ asm_fprintf (file, "\tcal %s,%d(%s)\n", reg_names[1], sp_offset, reg_names[1]);
+ }
+ else if (sp_reg != 1)
+ asm_fprintf (file, "\tmr %s,%s\n", reg_names[1], reg_names[sp_reg]);
/* If we have to restore more than two FP registers, branch to the
restore function. It will return to our caller. */
- if (first_fp_reg < 62)
- fprintf (file, "\tb ._restf%d\n", first_fp_reg - 32);
+ if (info->first_fp_reg_save != 64 && !FP_SAVE_INLINE (info->first_fp_reg_save))
+ asm_fprintf (file, "\tb %s%d%s\n", RESTORE_FP_PREFIX,
+ info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
else
- fprintf (file, "\tbr\n");
+ asm_fprintf (file, "\t{br|blr}\n");
}
/* Output a traceback table here. See /usr/include/sys/debug.h for info
- on its format. */
- {
- char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
- int fixed_parms, float_parms, parm_info;
- int i;
-
- /* Need label immediately before tbtab, so we can compute its offset
- from the function start. */
- if (*fname == '*')
- ++fname;
- fprintf (file, "LT..");
- ASM_OUTPUT_LABEL (file, fname);
-
- /* The .tbtab pseudo-op can only be used for the first eight
- expressions, since it can't handle the possibly variable length
- fields that follow. However, if you omit the optional fields,
- the assembler outputs zeros for all optional fields anyways, giving each
- variable length field is minimum length (as defined in sys/debug.h).
- Thus we can not use the .tbtab pseudo-op at all. */
-
- /* An all-zero word flags the start of the tbtab, for debuggers that have
- to find it by searching forward from the entry point or from the
- current pc. */
- fprintf (file, "\t.long 0\n");
-
- /* Tbtab format type. Use format type 0. */
- fprintf (file, "\t.byte 0,");
-
- /* Language type. Unfortunately, there doesn't seem to be any official way
- to get this info, so we use language_string. C is 0. C++ is 9.
- No number defined for Obj-C, so use the value for C for now. */
- if (! strcmp (language_string, "GNU C")
- || ! strcmp (language_string, "GNU Obj-C"))
- i = 0;
- else if (! strcmp (language_string, "GNU F77"))
- i = 1;
- else if (! strcmp (language_string, "GNU Ada"))
- i = 3;
- else if (! strcmp (language_string, "GNU PASCAL"))
- i = 2;
- else if (! strcmp (language_string, "GNU C++"))
- i = 9;
- else
- abort ();
- fprintf (file, "%d,", i);
-
- /* 8 single bit fields: global linkage (not set for C extern linkage,
- apparently a PL/I convention?), out-of-line epilogue/prologue, offset
- from start of procedure stored in tbtab, internal function, function
- has controlled storage, function has no toc, function uses fp,
- function logs/aborts fp operations. */
- /* Assume that fp operations are used if any fp reg must be saved. */
- fprintf (file, "%d,", (1 << 5) | ((first_fp_reg != 64) << 1));
-
- /* 6 bitfields: function is interrupt handler, name present in proc table,
- function calls alloca, on condition directives (controls stack walks,
- 3 bits), saves condition reg, saves link reg. */
- /* The `function calls alloca' bit seems to be set whenever reg 31 is
- set up as a frame pointer, even when there is no alloca call. */
- fprintf (file, "%d,",
- ((1 << 6) | (frame_pointer_needed << 5)
- | (must_save_cr () << 1) | (regs_ever_live[65])));
-
- /* 3 bitfields: saves backchain, spare bit, number of fpr saved
- (6 bits). */
- fprintf (file, "%d,",
- (must_push << 7) | (64 - first_fp_reg_to_save ()));
-
- /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits). */
- fprintf (file, "%d,", (32 - first_reg_to_save ()));
-
- {
- /* Compute the parameter info from the function decl argument list. */
- tree decl;
- int next_parm_info_bit;
-
- next_parm_info_bit = 31;
- parm_info = 0;
- fixed_parms = 0;
- float_parms = 0;
-
- for (decl = DECL_ARGUMENTS (current_function_decl);
- decl; decl = TREE_CHAIN (decl))
- {
- rtx parameter = DECL_INCOMING_RTL (decl);
- enum machine_mode mode = GET_MODE (parameter);
-
- if (GET_CODE (parameter) == REG)
- {
- if (GET_MODE_CLASS (mode) == MODE_FLOAT)
- {
- int bits;
-
- float_parms++;
+ on its format.
+
+ We don't output a traceback table if -finhibit-size-directive was
+ used. The documentation for -finhibit-size-directive reads
+ ``don't output a @code{.size} assembler directive, or anything
+ else that would cause trouble if the function is split in the
+ middle, and the two halves are placed at locations far apart in
+ memory.'' The traceback table has this property, since it
+ includes the offset from the start of the function to the
+ traceback table itself.
+
+ System V.4 Powerpc's (and the embedded ABI derived from it) use a
+ different traceback table. */
+ if (DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive)
+ {
+ char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ int fixed_parms, float_parms, parm_info;
+ int i;
+
+ while (*fname == '.') /* V.4 encodes . in the name */
+ fname++;
+
+ /* Need label immediately before tbtab, so we can compute its offset
+ from the function start. */
+ if (*fname == '*')
+ ++fname;
+ ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT");
+ ASM_OUTPUT_LABEL (file, fname);
+
+ /* The .tbtab pseudo-op can only be used for the first eight
+ expressions, since it can't handle the possibly variable
+ length fields that follow. However, if you omit the optional
+ fields, the assembler outputs zeros for all optional fields
+ anyways, giving each variable length field is minimum length
+ (as defined in sys/debug.h). Thus we can not use the .tbtab
+ pseudo-op at all. */
+
+ /* An all-zero word flags the start of the tbtab, for debuggers
+ that have to find it by searching forward from the entry
+ point or from the current pc. */
+ fputs ("\t.long 0\n", file);
+
+ /* Tbtab format type. Use format type 0. */
+ fputs ("\t.byte 0,", file);
+
+ /* Language type. Unfortunately, there doesn't seem to be any
+ official way to get this info, so we use language_string. C
+ is 0. C++ is 9. No number defined for Obj-C, so use the
+ value for C for now. */
+ if (! strcmp (language_string, "GNU C")
+ || ! strcmp (language_string, "GNU Obj-C"))
+ i = 0;
+ else if (! strcmp (language_string, "GNU F77"))
+ i = 1;
+ else if (! strcmp (language_string, "GNU Ada"))
+ i = 3;
+ else if (! strcmp (language_string, "GNU PASCAL"))
+ i = 2;
+ else if (! strcmp (language_string, "GNU C++"))
+ i = 9;
+ else
+ abort ();
+ fprintf (file, "%d,", i);
+
+ /* 8 single bit fields: global linkage (not set for C extern linkage,
+ apparently a PL/I convention?), out-of-line epilogue/prologue, offset
+ from start of procedure stored in tbtab, internal function, function
+ has controlled storage, function has no toc, function uses fp,
+ function logs/aborts fp operations. */
+ /* Assume that fp operations are used if any fp reg must be saved. */
+ fprintf (file, "%d,", (1 << 5) | ((info->first_fp_reg_save != 64) << 1));
+
+ /* 6 bitfields: function is interrupt handler, name present in
+ proc table, function calls alloca, on condition directives
+ (controls stack walks, 3 bits), saves condition reg, saves
+ link reg. */
+ /* The `function calls alloca' bit seems to be set whenever reg 31 is
+ set up as a frame pointer, even when there is no alloca call. */
+ fprintf (file, "%d,",
+ ((1 << 6) | (frame_pointer_needed << 5)
+ | (info->cr_save_p << 1) | (info->lr_save_p)));
+
+ /* 3 bitfields: saves backchain, spare bit, number of fpr saved
+ (6 bits). */
+ fprintf (file, "%d,",
+ (info->push_p << 7) | (64 - info->first_fp_reg_save));
+
+ /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits). */
+ fprintf (file, "%d,", (32 - first_reg_to_save ()));
+
+ {
+ /* Compute the parameter info from the function decl argument
+ list. */
+ tree decl;
+ int next_parm_info_bit;
+
+ next_parm_info_bit = 31;
+ parm_info = 0;
+ fixed_parms = 0;
+ float_parms = 0;
+
+ for (decl = DECL_ARGUMENTS (current_function_decl);
+ decl; decl = TREE_CHAIN (decl))
+ {
+ rtx parameter = DECL_INCOMING_RTL (decl);
+ enum machine_mode mode = GET_MODE (parameter);
+
+ if (GET_CODE (parameter) == REG)
+ {
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ int bits;
+
+ float_parms++;
+
+ if (mode == SFmode)
+ bits = 0x2;
+ else if (mode == DFmode)
+ bits = 0x3;
+ else
+ abort ();
+
+ /* If only one bit will fit, don't or in this entry. */
+ if (next_parm_info_bit > 0)
+ parm_info |= (bits << (next_parm_info_bit - 1));
+ next_parm_info_bit -= 2;
+ }
+ else
+ {
+ fixed_parms += ((GET_MODE_SIZE (mode)
+ + (UNITS_PER_WORD - 1))
+ / UNITS_PER_WORD);
+ next_parm_info_bit -= 1;
+ }
+ }
+ }
+ }
+
+ /* Number of fixed point parameters. */
+ /* This is actually the number of words of fixed point parameters; thus
+ an 8 byte struct counts as 2; and thus the maximum value is 8. */
+ fprintf (file, "%d,", fixed_parms);
+
+ /* 2 bitfields: number of floating point parameters (7 bits), parameters
+ all on stack. */
+ /* This is actually the number of fp registers that hold parameters;
+ and thus the maximum value is 13. */
+ /* Set parameters on stack bit if parameters are not in their original
+ registers, regardless of whether they are on the stack? Xlc
+ seems to set the bit when not optimizing. */
+ fprintf (file, "%d\n", ((float_parms << 1) | (! optimize)));
+
+ /* Optional fields follow. Some are variable length. */
+
+ /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float,
+ 11 double float. */
+ /* There is an entry for each parameter in a register, in the order that
+ they occur in the parameter list. Any intervening arguments on the
+ stack are ignored. If the list overflows a long (max possible length
+ 34 bits) then completely leave off all elements that don't fit. */
+ /* Only emit this long if there was at least one parameter. */
+ if (fixed_parms || float_parms)
+ fprintf (file, "\t.long %d\n", parm_info);
+
+ /* Offset from start of code to tb table. */
+ fputs ("\t.long ", file);
+ ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT");
+ RS6000_OUTPUT_BASENAME (file, fname);
+ fputs ("-.", file);
+ RS6000_OUTPUT_BASENAME (file, fname);
+ putc ('\n', file);
+
+ /* Interrupt handler mask. */
+ /* Omit this long, since we never set the interrupt handler bit
+ above. */
+
+ /* Number of CTL (controlled storage) anchors. */
+ /* Omit this long, since the has_ctl bit is never set above. */
+
+ /* Displacement into stack of each CTL anchor. */
+ /* Omit this list of longs, because there are no CTL anchors. */
+
+ /* Length of function name. */
+ fprintf (file, "\t.short %d\n", strlen (fname));
+
+ /* Function name. */
+ assemble_string (fname, strlen (fname));
+
+ /* Register for alloca automatic storage; this is always reg 31.
+ Only emit this if the alloca bit was set above. */
+ if (frame_pointer_needed)
+ fputs ("\t.byte 31\n", file);
+ }
- if (mode == SFmode)
- bits = 0x2;
- else if (mode == DFmode)
- bits = 0x3;
- else
- abort ();
+ /* Reset varargs and save TOC indicator */
+ rs6000_sysv_varargs_p = 0;
+ rs6000_save_toc_p = 0;
- /* If only one bit will fit, don't or in this entry. */
- if (next_parm_info_bit > 0)
- parm_info |= (bits << (next_parm_info_bit - 1));
- next_parm_info_bit -= 2;
- }
- else
- {
- fixed_parms += ((GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1))
- / UNITS_PER_WORD);
- next_parm_info_bit -= 1;
- }
- }
- }
+ if (DEFAULT_ABI == ABI_NT)
+ {
+ RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+ fputs (".e:\nFE_MOT_RESVD..", file);
+ RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+ fputs (":\n", file);
}
-
- /* Number of fixed point parameters. */
- /* This is actually the number of words of fixed point parameters; thus
- an 8 byte struct counts as 2; and thus the maximum value is 8. */
- fprintf (file, "%d,", fixed_parms);
-
- /* 2 bitfields: number of floating point parameters (7 bits), parameters
- all on stack. */
- /* This is actually the number of fp registers that hold parameters;
- and thus the maximum value is 13. */
- /* Set parameters on stack bit if parameters are not in their original
- registers, regardless of whether they are on the stack? Xlc
- seems to set the bit when not optimizing. */
- fprintf (file, "%d\n", ((float_parms << 1) | (! optimize)));
-
- /* Optional fields follow. Some are variable length. */
-
- /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float,
- 11 double float. */
- /* There is an entry for each parameter in a register, in the order that
- they occur in the parameter list. Any intervening arguments on the
- stack are ignored. If the list overflows a long (max possible length
- 34 bits) then completely leave off all elements that don't fit. */
- /* Only emit this long if there was at least one parameter. */
- if (fixed_parms || float_parms)
- fprintf (file, "\t.long %d\n", parm_info);
-
- /* Offset from start of code to tb table. */
- fprintf (file, "\t.long LT..");
- RS6000_OUTPUT_BASENAME (file, fname);
- fprintf (file, "-.");
- RS6000_OUTPUT_BASENAME (file, fname);
- fprintf (file, "\n");
-
- /* Interrupt handler mask. */
- /* Omit this long, since we never set the interrupt handler bit above. */
-
- /* Number of CTL (controlled storage) anchors. */
- /* Omit this long, since the has_ctl bit is never set above. */
-
- /* Displacement into stack of each CTL anchor. */
- /* Omit this list of longs, because there are no CTL anchors. */
-
- /* Length of function name. */
- fprintf (file, "\t.short %d\n", strlen (fname));
-
- /* Function name. */
- assemble_string (fname, strlen (fname));
-
- /* Register for alloca automatic storage; this is always reg 31.
- Only emit this if the alloca bit was set above. */
- if (frame_pointer_needed)
- fprintf (file, "\t.byte 31\n");
- }
}
\f
/* Output a TOC entry. We derive the entry name from what is
{
char buf[256];
char *name = buf;
+ char *real_name;
rtx base = x;
int offset = 0;
- ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno);
+ if (TARGET_NO_TOC)
+ abort ();
- /* Handle FP constants specially. */
+ /* if we're going to put a double constant in the TOC, make sure it's
+ aligned properly when strict alignment is on. */
if (GET_CODE (x) == CONST_DOUBLE
+ && STRICT_ALIGNMENT
&& GET_MODE (x) == DFmode
- && TARGET_FLOAT_FORMAT == HOST_FLOAT_FORMAT
- && BITS_PER_WORD == HOST_BITS_PER_INT
- && TARGET_FP_IN_TOC)
+ && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) {
+ ASM_OUTPUT_ALIGN (file, 3);
+ }
+
+
+ if (TARGET_ELF && TARGET_MINIMAL_TOC)
+ {
+ ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC");
+ fprintf (file, "%d = .-", labelno);
+ ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCTOC");
+ fputs ("1\n", file);
+ }
+ else
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno);
+
+ /* Handle FP constants specially. Note that if we have a minimal
+ TOC, things we put here aren't actually in the TOC, so we can allow
+ FP constants. */
+ if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode
+ && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
{
- fprintf (file, "\t.tc FD_%x_%x[TC],%d,%d\n",
- CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),
- CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
+ REAL_VALUE_TYPE rv;
+ long k[2];
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
+ if (TARGET_MINIMAL_TOC)
+ fprintf (file, "\t.long %ld\n\t.long %ld\n", k[0], k[1]);
+ else
+ fprintf (file, "\t.tc FD_%lx_%lx[TC],%ld,%ld\n",
+ k[0], k[1], k[0], k[1]);
return;
}
else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode
- && TARGET_FP_IN_TOC)
+ && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
{
- rtx val = operand_subword (x, 0, 0, SFmode);
+ REAL_VALUE_TYPE rv;
+ long l;
- if (val == 0 || GET_CODE (val) != CONST_INT)
- abort ();
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+
+ if (TARGET_MINIMAL_TOC)
+ fprintf (file, "\t.long %d\n", l);
+ else
+ fprintf (file, "\t.tc FS_%x[TC],%d\n", l, l);
+ return;
+ }
+ else if (GET_MODE (x) == DImode
+ && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
+ {
+ HOST_WIDE_INT low;
+ HOST_WIDE_INT high;
+
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ low = CONST_DOUBLE_LOW (x);
+ high = CONST_DOUBLE_HIGH (x);
+ }
+ else
+#if HOST_BITS_PER_WIDE_INT == 32
+ {
+ low = INTVAL (x);
+ high = (low < 0) ? ~0 : 0;
+ }
+#else
+ {
+ low = INTVAL (x) & 0xffffffff;
+ high = (HOST_WIDE_INT) INTVAL (x) >> 32;
+ }
+#endif
- fprintf (file, "\t.tc FS_%x[TC],%d\n", INTVAL (val), INTVAL (val));
+ if (TARGET_MINIMAL_TOC)
+ fprintf (file, "\t.long %ld\n\t.long %ld\n", high, low);
+ else
+ fprintf (file, "\t.tc ID_%lx_%lx[TC],%ld,%ld\n",
+ high, low, high, low);
return;
}
else
abort ();
- fprintf (file, "\t.tc ");
- RS6000_OUTPUT_BASENAME (file, name);
+ if (TARGET_MINIMAL_TOC)
+ fputs ("\t.long ", file);
+ else
+ {
+ STRIP_NAME_ENCODING (real_name, name);
+ fprintf (file, "\t.tc %s", real_name);
- if (offset < 0)
- fprintf (file, ".N%d", - offset);
- else if (offset)
- fprintf (file, ".P%d", offset);
+ if (offset < 0)
+ fprintf (file, ".N%d", - offset);
+ else if (offset)
+ fprintf (file, ".P%d", offset);
- fprintf (file, "[TC],");
+ fputs ("[TC],", file);
+ }
output_addr_const (file, x);
- fprintf (file, "\n");
+ putc ('\n', file);
}
\f
/* Output an assembler pseudo-op to write an ASCII string of N characters
/* The last used parameter register. */
int last_parm_reg;
int i, j;
+ char buf[100];
+
+ if (DEFAULT_ABI != ABI_AIX)
+ abort ();
/* Set up a TOC entry for the profiler label. */
toc_section ();
- fprintf (file, "LPC..%d:\n\t.tc\tLP..%d[TC],LP..%d\n",
- labelno, labelno, labelno);
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LPC", labelno);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
+ if (TARGET_MINIMAL_TOC)
+ {
+ fputs ("\t.long ", file);
+ assemble_name (file, buf);
+ putc ('\n', file);
+ }
+ else
+ {
+ fputs ("\t.tc\t", file);
+ assemble_name (file, buf);
+ fputs ("[TC],", file);
+ assemble_name (file, buf);
+ putc ('\n', file);
+ }
text_section ();
/* Figure out last used parameter register. The proper thing to do is
it might be set up as the frame pointer. */
for (i = 3, j = 30; i <= last_parm_reg; i++, j--)
- fprintf (file, "\tai %d,%d,0\n", j, i);
+ asm_fprintf (file, "\tmr %d,%d\n", j, i);
/* Load location address into r3, and call mcount. */
- fprintf (file, "\tl 3,LPC..%d(2)\n\tbl .mcount\n", labelno);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LPC", labelno);
+ asm_fprintf (file, "\t{l|lwz} %s,", reg_names[3]);
+ assemble_name (file, buf);
+ asm_fprintf (file, "(%s)\n\tbl .mcount\n", reg_names[2]);
/* Restore parameter registers. */
for (i = 3, j = 30; i <= last_parm_reg; i++, j--)
- fprintf (file, "\tai %d,%d,0\n", i, j);
+ asm_fprintf (file, "\tmr %d,%d\n", i, j);
+}
+
+/* Adjust the cost of a scheduling dependency. Return the new cost of
+ a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
+
+int
+rs6000_adjust_cost (insn, link, dep_insn, cost)
+ rtx insn;
+ rtx link;
+ rtx dep_insn;
+ int cost;
+{
+ if (! recog_memoized (insn))
+ return 0;
+
+ if (REG_NOTE_KIND (link) != 0)
+ return 0;
+
+ if (REG_NOTE_KIND (link) == 0)
+ {
+ /* Data dependency; DEP_INSN writes a register that INSN reads some
+ cycles later. */
+
+ /* Tell the first scheduling pass about the latency between a mtctr
+ and bctr (and mtlr and br/blr). The first scheduling pass will not
+ know about this latency since the mtctr instruction, which has the
+ latency associated to it, will be generated by reload. */
+ if (get_attr_type (insn) == TYPE_JMPREG)
+ return TARGET_POWER ? 5 : 4;
+
+ /* Fall out to return default cost. */
+ }
+
+ return cost;
+}
+
+/* Return how many instructions the machine can issue per cycle */
+int get_issue_rate()
+{
+ switch (rs6000_cpu_attr) {
+ case CPU_RIOS1:
+ return 3; /* ? */
+ case CPU_RIOS2:
+ return 4;
+ case CPU_PPC601:
+ return 3; /* ? */
+ case CPU_PPC602:
+ return 1;
+ case CPU_PPC603:
+ return 2;
+ case CPU_PPC604:
+ return 4;
+ case CPU_PPC620:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+\f
+/* Output insns to flush the {data|instruction} caches after building a
+ trampoline. */
+
+static void
+rs6000_sync_trampoline (addr)
+ rtx addr;
+{
+ enum machine_mode pmode = Pmode;
+ rtx reg = gen_reg_rtx (pmode);
+ rtx mem2;
+ rtx mem1;
+ int size = rs6000_trampoline_size ();
+ rtx (*sub_fcn) PROTO ((rtx, rtx, rtx));
+ rtx (*cmp_fcn) PROTO ((rtx, rtx));
+ rtx label;
+
+ if (TARGET_32BIT)
+ {
+ sub_fcn = gen_subsi3;
+ cmp_fcn = gen_cmpsi;
+ }
+ else
+ {
+ sub_fcn = gen_subdi3;
+ cmp_fcn = gen_cmpdi;
+ }
+
+ addr = force_reg (pmode, addr);
+ mem2 = gen_rtx (MEM, pmode, gen_rtx (PLUS, pmode, addr, reg));
+ mem1 = gen_rtx (MEM, pmode, addr);
+
+ /* Issue a loop of dcbst's to flush the data cache */
+ emit_move_insn (reg, GEN_INT (size-4));
+ label = gen_label_rtx ();
+ emit_label (label);
+ emit_insn (gen_dcbst (mem2, addr, reg));
+ emit_insn ((*sub_fcn) (reg, reg, GEN_INT (4)));
+ emit_insn ((*cmp_fcn) (reg, const0_rtx));
+ emit_jump_insn (gen_bgt (label));
+
+ /* Issue a sync after the dcbst's to let things settle down */
+ emit_insn (gen_sync (mem1));
+
+ /* Issue a loop of icbi's to flush the instruction cache */
+ emit_move_insn (reg, GEN_INT (size-4));
+ label = gen_label_rtx ();
+ emit_label (label);
+ emit_insn (gen_icbi (mem2, addr, reg));
+ emit_insn ((*sub_fcn) (reg, reg, GEN_INT (4)));
+ emit_insn ((*cmp_fcn) (reg, const0_rtx));
+ emit_jump_insn (gen_bgt (label));
+
+ /* Issue a sync after the icbi's to let things settle down */
+ emit_insn (gen_sync (mem1));
+
+ /* Finally issue an isync to synchronize the icache */
+ emit_insn (gen_isync (mem1));
+}
+
+\f
+/* Output assembler code for a block containing the constant parts
+ of a trampoline, leaving space for the variable parts.
+
+ The trampoline should set the static chain pointer to value placed
+ into the trampoline and should branch to the specified routine. */
+
+void
+rs6000_trampoline_template (file)
+ FILE *file;
+{
+ char *sc = reg_names[STATIC_CHAIN_REGNUM];
+ char *r0 = reg_names[0];
+ char *r2 = reg_names[2];
+
+ switch (DEFAULT_ABI)
+ {
+ default:
+ abort ();
+
+ /* Under AIX, this is not code at all, but merely a data area,
+ since that is the way all functions are called. The first word is
+ the address of the function, the second word is the TOC pointer (r2),
+ and the third word is the static chain value. */
+ case ABI_AIX:
+ fprintf (file, "\t.long %s\n", (TARGET_32BIT) ? "0,0,0" : "0,0,0,0,0,0");
+ break;
+
+
+ /* V.4/eabi function pointers are just a single pointer, so we need to
+ do the full gory code to load up the static chain. */
+ case ABI_V4:
+ case ABI_AIX_NODESC:
+ if (STATIC_CHAIN_REGNUM == 0 || !TARGET_NEW_MNEMONICS)
+ abort ();
+
+ if (TARGET_32BIT)
+ {
+ fprintf (file, "\tmflr %s\n", r0); /* offset 0 */
+ fprintf (file, "\tbl .LTRAMP1\n"); /* offset 4 */
+ fprintf (file, "\t.long 0,0\n"); /* offset 8 */
+ fprintf (file, ".LTRAMP1:\n");
+ fprintf (file, "\tmflr %s\n", sc); /* offset 20 */
+ fprintf (file, "\tmtlr %s\n", r0); /* offset 24 */
+ fprintf (file, "\tlwz %s,0(%s)\n", r0, sc); /* offset 28 */
+ fprintf (file, "\tlwz %s,4(%s)\n", sc, sc); /* offset 32 */
+ fprintf (file, "\tmtctr %s\n", r0); /* offset 36 */
+ fprintf (file, "\tbctr\n"); /* offset 40 */
+ }
+ else
+ {
+ fprintf (file, "\tmflr %s\n", r0); /* offset 0 */
+ fprintf (file, "\tbl .LTRAMP1\n"); /* offset 4 */
+ fprintf (file, "\t.long 0,0,0,0\n"); /* offset 8 */
+ fprintf (file, ".LTRAMP1:\n");
+ fprintf (file, "\tmflr %s\n", sc); /* offset 28 */
+ fprintf (file, "\tmtlr %s\n", r0); /* offset 32 */
+ fprintf (file, "\tld %s,0(%s)\n", r0, sc); /* offset 36 */
+ fprintf (file, "\tld %s,8(%s)\n", sc, sc); /* offset 40 */
+ fprintf (file, "\tmtctr %s\n", r0); /* offset 44 */
+ fprintf (file, "\tbctr\n"); /* offset 48 */
+ }
+ break;
+
+ /* NT function pointers point to a two word area (real address, TOC)
+ which unfortunately does not include a static chain field. So we
+ use the function field to point to ..LTRAMP1 and the toc field
+ to point to the whole table. */
+ case ABI_NT:
+ if (STATIC_CHAIN_REGNUM == 0
+ || STATIC_CHAIN_REGNUM == 2
+ || TARGET_64BIT
+ || !TARGET_NEW_MNEMONICS)
+ abort ();
+
+ fprintf (file, "\t.ualong 0\n"); /* offset 0 */
+ fprintf (file, "\t.ualong 0\n"); /* offset 4 */
+ fprintf (file, "\t.ualong 0\n"); /* offset 8 */
+ fprintf (file, "\t.ualong 0\n"); /* offset 12 */
+ fprintf (file, "\t.ualong 0\n"); /* offset 16 */
+ fprintf (file, "..LTRAMP1..0:\n"); /* offset 20 */
+ fprintf (file, "\tlwz %s,8(%s)\n", r0, r2); /* offset 24 */
+ fprintf (file, "\tlwz %s,12(%s)\n", sc, r2); /* offset 28 */
+ fprintf (file, "\tmtctr %s\n", r0); /* offset 32 */
+ fprintf (file, "\tlwz %s,16(%s)\n", r2, r2); /* offset 36 */
+ fprintf (file, "\tbctr\n"); /* offset 40 */
+ break;
+ }
+
+ return;
+}
+
+/* Length in units of the trampoline for entering a nested function. */
+
+int
+rs6000_trampoline_size ()
+{
+ int ret = 0;
+
+ switch (DEFAULT_ABI)
+ {
+ default:
+ abort ();
+
+ case ABI_AIX:
+ ret = (TARGET_32BIT) ? 12 : 24;
+ break;
+
+ case ABI_V4:
+ case ABI_AIX_NODESC:
+ ret = (TARGET_32BIT) ? 40 : 48;
+ break;
+
+ case ABI_NT:
+ ret = 20;
+ break;
+ }
+
+ return ret;
+}
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+ FNADDR is an RTX for the address of the function's pure code.
+ CXT is an RTX for the static chain value for the function. */
+
+void
+rs6000_initialize_trampoline (addr, fnaddr, cxt)
+ rtx addr;
+ rtx fnaddr;
+ rtx cxt;
+{
+ enum machine_mode pmode = Pmode;
+ int regsize = (TARGET_32BIT) ? 4 : 8;
+ rtx ctx_reg = force_reg (pmode, cxt);
+
+ switch (DEFAULT_ABI)
+ {
+ default:
+ abort ();
+
+/* Macros to shorten the code expansions below. */
+#define MEM_DEREF(addr) gen_rtx (MEM, pmode, memory_address (pmode, addr))
+#define MEM_PLUS(addr,offset) gen_rtx (MEM, pmode, memory_address (pmode, plus_constant (addr, offset)))
+
+ /* Under AIX, just build the 3 word function descriptor */
+ case ABI_AIX:
+ {
+ rtx fn_reg = gen_reg_rtx (pmode);
+ rtx toc_reg = gen_reg_rtx (pmode);
+ emit_move_insn (fn_reg, MEM_DEREF (fnaddr));
+ emit_move_insn (toc_reg, MEM_PLUS (fnaddr, 4));
+ emit_move_insn (MEM_DEREF (addr), fn_reg);
+ emit_move_insn (MEM_PLUS (addr, regsize), toc_reg);
+ emit_move_insn (MEM_PLUS (addr, 2*regsize), ctx_reg);
+ }
+ break;
+
+ /* Under V.4/eabi, update the two words after the bl to have the real
+ function address and the static chain. */
+ case ABI_V4:
+ case ABI_AIX_NODESC:
+ {
+ rtx reg = gen_reg_rtx (pmode);
+ emit_move_insn (reg, fnaddr);
+ emit_move_insn (MEM_PLUS (addr, 8), reg);
+ emit_move_insn (MEM_PLUS (addr, 8 + regsize), ctx_reg);
+ rs6000_sync_trampoline (addr);
+ }
+ break;
+
+ /* Under NT, update the first word to point to the ..LTRAMP1..0 header,
+ the second word will point to the whole trampoline, third-fifth words
+ will then have the real address, static chain, and toc value. */
+ case ABI_NT:
+ {
+ rtx tramp_reg = gen_reg_rtx (pmode);
+ rtx fn_reg = gen_reg_rtx (pmode);
+ rtx toc_reg = gen_reg_rtx (pmode);
+
+ emit_move_insn (tramp_reg, gen_rtx (SYMBOL_REF, pmode, "..LTRAMP1..0"));
+ addr = force_reg (pmode, addr);
+ emit_move_insn (fn_reg, MEM_DEREF (fnaddr));
+ emit_move_insn (toc_reg, MEM_PLUS (fnaddr, regsize));
+ emit_move_insn (MEM_DEREF (addr), tramp_reg);
+ emit_move_insn (MEM_PLUS (addr, regsize), addr);
+ emit_move_insn (MEM_PLUS (addr, 2*regsize), fn_reg);
+ emit_move_insn (MEM_PLUS (addr, 3*regsize), ctx_reg);
+ emit_move_insn (MEM_PLUS (addr, 4*regsize), gen_rtx (REG, pmode, 2));
+ }
+ break;
+ }
+
+ return;
+}
+
+\f
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+ with arguments ARGS is a valid machine specific attribute for DECL.
+ The attributes in ATTRIBUTES have previously been assigned to DECL. */
+
+int
+rs6000_valid_decl_attribute_p (decl, attributes, identifier, args)
+ tree decl;
+ tree attributes;
+ tree identifier;
+ tree args;
+{
+ return 0;
+}
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+ with arguments ARGS is a valid machine specific attribute for TYPE.
+ The attributes in ATTRIBUTES have previously been assigned to TYPE. */
+
+int
+rs6000_valid_type_attribute_p (type, attributes, identifier, args)
+ tree type;
+ tree attributes;
+ tree identifier;
+ tree args;
+{
+ if (TREE_CODE (type) != FUNCTION_TYPE
+ && TREE_CODE (type) != FIELD_DECL
+ && TREE_CODE (type) != TYPE_DECL)
+ return 0;
+
+ /* Longcall attribute says that the function is not within 2**26 bytes
+ of the current function, and to do an indirect call. */
+ if (is_attribute_p ("longcall", identifier))
+ return (args == NULL_TREE);
+
+ if (DEFAULT_ABI == ABI_NT)
+ {
+ /* Stdcall attribute says callee is responsible for popping arguments
+ if they are not variable. */
+ if (is_attribute_p ("stdcall", identifier))
+ return (args == NULL_TREE);
+
+ /* Cdecl attribute says the callee is a normal C declaration */
+ if (is_attribute_p ("cdecl", identifier))
+ return (args == NULL_TREE);
+
+ /* Dllimport attribute says says the caller is to call the function
+ indirectly through a __imp_<name> pointer. */
+ if (is_attribute_p ("dllimport", identifier))
+ return (args == NULL_TREE);
+
+ /* Dllexport attribute says says the callee is to create a __imp_<name>
+ pointer. */
+ if (is_attribute_p ("dllexport", identifier))
+ return (args == NULL_TREE);
+ }
+
+ return 0;
+}
+
+/* If defined, a C expression whose value is zero if the attributes on
+ TYPE1 and TYPE2 are incompatible, one if they are compatible, and
+ two if they are nearly compatible (which causes a warning to be
+ generated). */
+
+int
+rs6000_comp_type_attributes (type1, type2)
+ tree type1;
+ tree type2;
+{
+ return 1;
+}
+
+/* If defined, a C statement that assigns default attributes to newly
+ defined TYPE. */
+
+void
+rs6000_set_default_type_attributes (type)
+ tree type;
+{
+}
+
+/* Return a dll import reference corresponding to to a call's SYMBOL_REF */
+struct rtx_def *
+rs6000_dll_import_ref (call_ref)
+ rtx call_ref;
+{
+ char *call_name;
+ int len;
+ char *p;
+ rtx reg1, reg2;
+ tree node;
+
+ if (GET_CODE (call_ref) != SYMBOL_REF)
+ abort ();
+
+ call_name = XSTR (call_ref, 0);
+ len = sizeof ("__imp_") + strlen (call_name);
+ p = alloca (len);
+ reg2 = gen_reg_rtx (Pmode);
+
+ strcpy (p, "__imp_");
+ strcat (p, call_name);
+ node = get_identifier (p);
+
+ reg1 = force_reg (Pmode, gen_rtx (SYMBOL_REF, VOIDmode, IDENTIFIER_POINTER (node)));
+ emit_move_insn (reg2, gen_rtx (MEM, Pmode, reg1));
+
+ return reg2;
+}
+
+/* Return a reference suitable for calling a function with the longcall attribute. */
+struct rtx_def *
+rs6000_longcall_ref (call_ref)
+ rtx call_ref;
+{
+ char *call_name;
+ int len;
+ char *p;
+ rtx reg1, reg2;
+ tree node;
+
+ if (GET_CODE (call_ref) != SYMBOL_REF)
+ return call_ref;
+
+ /* System V adds '.' to the internal name, so skip them. */
+ call_name = XSTR (call_ref, 0);
+ if (*call_name == '.')
+ {
+ while (*call_name == '.')
+ call_name++;
+
+ node = get_identifier (call_name);
+ call_ref = gen_rtx (SYMBOL_REF, VOIDmode, IDENTIFIER_POINTER (node));
+ }
+
+ return force_reg (Pmode, call_ref);
+}
+
+\f
+/* A C statement or statements to switch to the appropriate section
+ for output of RTX in mode MODE. You can assume that RTX is some
+ kind of constant in RTL. The argument MODE is redundant except in
+ the case of a `const_int' rtx. Select the section by calling
+ `text_section' or one of the alternatives for other sections.
+
+ Do not define this macro if you put all constants in the read-only
+ data section. */
+
+#ifdef USING_SVR4_H
+
+void
+rs6000_select_rtx_section (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x))
+ toc_section ();
+ else
+ const_section ();
+}
+
+/* A C statement or statements to switch to the appropriate
+ section for output of DECL. DECL is either a `VAR_DECL' node
+ or a constant of some sort. RELOC indicates whether forming
+ the initial value of DECL requires link-time relocations. */
+
+void
+rs6000_select_section (decl, reloc)
+ tree decl;
+ int reloc;
+{
+ int size = int_size_in_bytes (TREE_TYPE (decl));
+
+ if (TREE_CODE (decl) == STRING_CST)
+ {
+ if (! flag_writable_strings)
+ const_section ();
+ else
+ data_section ();
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ if ((flag_pic && reloc)
+ || !TREE_READONLY (decl)
+ || TREE_SIDE_EFFECTS (decl)
+ || !DECL_INITIAL (decl)
+ || (DECL_INITIAL (decl) != error_mark_node
+ && !TREE_CONSTANT (DECL_INITIAL (decl))))
+ {
+ if (TARGET_SDATA && (size > 0) && (size <= g_switch_value))
+ sdata_section ();
+ else
+ data_section ();
+ }
+ else
+ {
+ if (TARGET_SDATA && (size > 0) && (size <= g_switch_value))
+ sdata2_section ();
+ else
+ const_section ();
+ }
+ }
+ else
+ const_section ();
}
+#endif /* USING_SVR4_H */