%{mcpu=603: -mppc} \
%{mcpu=604: -mppc}"
+/* Define the options for the binder: Start text at 512, align all segments
+ to 512 bytes, and warn if there is text relocation.
+
+ The -bhalt:4 option supposedly changes the level at which ld will abort,
+ but it also suppresses warnings about multiply defined symbols and is
+ used by the AIX cc command. So we use it here.
+
+ -bnodelcsect undoes a poor choice of default relating to multiply-defined
+ csects. See AIX documentation for more information about this.
+
+ -bM:SRE tells the linker that the output file is Shared REusable. Note
+ that to actually build a shared library you will also need to specify an
+ export list with the -Wl,-bE option.
+
+ If -mcpu=common, export the architecture dependent multiply/divide routines
+ as per README.RS6000. */
+
+#undef LINK_SPEC
+#define LINK_SPEC "-T512 -H512 %{!r:-btextro} -bhalt:4 -bnodelcsect\
+ %{static:-bnso -bI:/lib/syscalls.exp} \
+ %{mcpu=common: milli.exp%s} \
+ %{!shared:%{g*:-bexport:/usr/lib/libg.exp}} %{shared:-bM:SRE}"
+
/* These are not necessary when we pass -u to the assembler, and undefining
them saves a great deal of space in object files. */
.Lgot2e = .-.LCTOC1
.long _GOT2_END_ # -mrelocatable GOT pointers end
+.Lfixups = .-.LCTOC1
+ .long _FIXUP_START_ # start of .fixup section
+
+.Lfixupe = .-.LCTOC1
+ .long _FIXUP_END_ # end of .fixup section
+
.text
.Lptr:
.long .LCTOC1-.Laddr # PC relative pointer to .got2
add 4,12,4
cmpw 1,3,4 # any pointers to adjust
- bc 12,6,.Ldone
+ bc 12,6,.Lfix
.Lloop:
- lwz 11,0(3) # next pointer
- add 11,11,12 # adjust
- stw 11,0(3)
+ lwz 5,0(3) # next pointer
+ add 5,5,12 # adjust
+ stw 5,0(3)
addi 3,3,4 # bump to next word
cmpw 1,3,4 # more pointers to adjust?
bc 4,6,.Lloop
+# Fixup any user initialized pointers now (the compiler drops pointers to
+# each of the relocs that it does in the .fixup section). Note, the pointers
+# themselves have already been fixed up by the previous loop.
+
+.Lfix:
+ lwz 3,.Lfixups(11) # fixup pointers start
+ lwz 4,.Lfixupe(11) # fixup pointers end
+
+ cmpw 1,3,4 # any user pointers to adjust
+ bc 12,6,.Ldone
+
+.Lfloop:
+ lwz 5,0(3) # next pointer
+ lwz 6,0(5) # get the pointer it points to
+ add 6,6,12 # adjust
+ stw 6,0(5)
+ addi 3,3,4 # bump to next word
+ cmpw 1,3,4 # more pointers to adjust?
+ bc 4,6,.Lfloop
+
# Done adjusting pointers, return
.Ldone:
#define MINIMAL_TOC_SECTION_ASM_OP \
((TARGET_RELOCATABLE) ? "\t.section\t\".got2\",\"aw\"" : "\t.section\t\".got1\",\"aw\"")
+/* Put relocatable data in .data, not .rodata so initialized pointers can be updated */
+#undef CONST_SECTION_ASM_OP
+#define CONST_SECTION_ASM_OP \
+ ((TARGET_RELOCATABLE) ? "\t.section\t\".data\"\t# .rodata" : "\t.section\t\".rodata\"")
+
/* Invoke an initializer function to set up the GOT */
#define NAME__MAIN "__eabi"
#define INVOKE__main 1
#undef ENDFILE_SPEC
#define ENDFILE_SPEC ""
+/* This is how to output an assembler line defining an `int' constant.
+ For -mrelocatable, we mark all addresses that need to be fixed up
+ in the .fixup section. */
+#undef ASM_OUTPUT_INT
+#define ASM_OUTPUT_INT(FILE,VALUE) \
+do { \
+ static int recurse = 0; \
+ if (TARGET_RELOCATABLE \
+ && in_section != in_toc \
+ && in_section != in_text \
+ && in_section != in_ctors \
+ && in_section != in_dtors \
+ && !recurse \
+ && GET_CODE (VALUE) != CONST_INT \
+ && GET_CODE (VALUE) != CONST_DOUBLE) \
+ { \
+ static int labelno = 0; \
+ char buf[256], *p; \
+ \
+ recurse = 1; \
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", labelno++); \
+ STRIP_NAME_ENCODING (p, buf); \
+ fprintf (FILE, "%s:\n", p); \
+ fprintf (FILE, "\t.long ("); \
+ output_addr_const (FILE, (VALUE)); \
+ fprintf (FILE, ")@fixup\n"); \
+ fprintf (FILE, "\t.section\t\".fixup\",\"aw\"\n"); \
+ ASM_OUTPUT_ALIGN (FILE, 2); \
+ fprintf (FILE, "\t.long\t%s\n", p); \
+ fprintf (FILE, "\t.previous\n"); \
+ recurse = 0; \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\t.long "); \
+ output_addr_const (FILE, (VALUE)); \
+ fprintf (FILE, "\n"); \
+ } \
+} while (0)
+
--- /dev/null
+/* Embedded ELF system support, using old AIX based calling sequence.
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "rs6000/eabi.h"
+
+#undef TARGET_DEFAULT
+#define TARGET_DEFAULT (MASK_POWERPC | MASK_NEW_MNEMONICS | MASK_AIX_CALLS)
+
+#undef CPP_SPEC
+#define CPP_SPEC "\
+%{posix: -D_POSIX_SOURCE} \
+%{mrelocatable: -D_RELOCATABLE} \
+%{mcall-sysv: -D_CALL_SYSV} %{mcall-aix: -D_CALL_AIX} %{!mcall-sysv: %{!mcall-aix: -D_CALL_AIX}} \
+%{msoft-float: -D_SOFT_FLOAT} %{mcpu=403: -D_SOFT_FLOAT} \
+%{mlittle: -D_LITTLE_ENDIAN -Amachine(littleendian)} \
+%{mlittle-endian: -D_LITTLE_ENDIAN -Amachine(littleendian)} \
+%{!mlittle: %{!mlittle-endian: -D_BIG_ENDIAN -Amachine(bigendian)}} \
+%{!mcpu*: \
+ %{mpower: %{!mpower2: -D_ARCH_PWR}} \
+ %{mpower2: -D_ARCH_PWR2} \
+ %{mpowerpc*: -D_ARCH_PPC} \
+ %{mno-powerpc: %{!mpower: %{!mpower2: -D_ARCH_COM}}} \
+ %{!mno-powerpc: -D_ARCH_PPC}} \
+%{mcpu=common: -D_ARCH_COM} \
+%{mcpu=power: -D_ARCH_PWR} \
+%{mcpu=powerpc: -D_ARCH_PPC} \
+%{mcpu=rios: -D_ARCH_PWR} \
+%{mcpu=rios1: -D_ARCH_PWR} \
+%{mcpu=rios2: -D_ARCH_PWR2} \
+%{mcpu=rsc: -D_ARCH_PWR} \
+%{mcpu=rsc1: -D_ARCH_PWR} \
+%{mcpu=403: -D_ARCH_PPC} \
+%{mcpu=601: -D_ARCH_PPC -D_ARCH_PWR} \
+%{mcpu=603: -D_ARCH_PPC} \
+%{mcpu=604: -D_ARCH_PPC}"
#define CPP_SPEC "\
%{posix: -D_POSIX_SOURCE} \
%{mrelocatable: -D_RELOCATABLE} \
+%{mcall-sysv: -D_CALL_SYSV} %{mcall-aix: -D_CALL_AIX} %{!mcall-sysv: %{!mcall-aix: -D_CALL_SYSV}} \
+%{msoft-float: -D_SOFT_FLOAT} %{mcpu=403: -D_SOFT_FLOAT} \
%{mbig: -D_BIG_ENDIAN -Amachine(bigendian)} \
%{mbig-endian: -D_BIG_ENDIAN -Amachine(bigendian)} \
%{!mbig: %{!mbig-endian: -D_LITTLE_ENDIAN -Amachine(littleendian)}} \
--- /dev/null
+#!
+__mulh 0x3100
+__mull 0x3180
+__divss 0x3200
+__divus 0x3280
+__quoss 0x3300
+__quous 0x3380
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;
\f
/* Override command line options. Mostly we process the processor
type and sometimes adjust other TARGET_ options. */
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->push_p)
+ return 1;
+ }
+
+ return 0;
}
/* Returns 1 always. */
for an add will be valid. */
return add_operand (op, mode);
}
+\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.
+
+ 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;
+{
+ static CUMULATIVE_ARGS zero_cumulative;
+
+ *cum = zero_cumulative;
+ cum->words = 0;
+ cum->fregno = FP_ARG_MIN_REG;
+ cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+
+ if (incoming)
+ {
+ cum->nargs_prototype = 1000; /* don't return an EXPR_LIST */
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ cum->varargs_offset = RS6000_VARARGS_OFFSET;
+#endif
+ }
+
+ 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;
+ if (TARGET_DEBUG_ARG)
+ {
+ 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) ]);
+ }
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS && incoming)
+ fprintf (stderr, " varargs = %d, ", cum->varargs_offset);
+#endif
+
+ fprintf (stderr, " proto = %d, nargs = %d\n",
+ cum->prototype, cum->nargs_prototype);
+ }
+}
+\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;
+{
+ cum->nargs_prototype--;
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ {
+ /* 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
+#endif
+ 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\n",
+ cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named);
+}
+\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;
+{
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr,
+ "function_arg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d\n",
+ cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named);
+
+ /* 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)
+ {
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS && TARGET_HARD_FLOAT && cum->nargs_prototype < 0
+ && type && (cum->prototype || TARGET_NO_PROTOTYPE))
+ return GEN_INT ((cum->fregno == FP_ARG_MIN_REG) ? -1 : 1);
+#endif
+
+ return GEN_INT (0);
+ }
+
+ if (!named)
+ {
+#ifdef TARGET_V4_CALLS
+ if (!TARGET_V4_CALLS)
+#endif
+ 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)
+#ifdef TARGET_V4_CALLS
+ || TARGET_V4_CALLS /* V.4 never passes FP values in GP registers */
+#endif
+ || !type)
+ return gen_rtx (REG, mode, cum->fregno);
+
+ return gen_rtx (EXPR_LIST, VOIDmode,
+ ((cum->words < GP_ARG_NUM_REG)
+ ? gen_rtx (REG, mode, GP_ARG_MIN_REG + cum->words)
+ : NULL_RTX),
+ gen_rtx (REG, mode, cum->fregno));
+ }
+
+#ifdef TARGET_V4_CALLS
+ /* Long longs won't be split between register and stack */
+ else if (TARGET_V4_CALLS &&
+ cum->words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
+ {
+ return NULL_RTX;
+ }
+#endif
+
+ else if (cum->words < GP_ARG_NUM_REG)
+ return gen_rtx (REG, mode, GP_ARG_MIN_REG + cum->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;
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ return 0;
+#endif
+
+ 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;
+{
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS && type && AGGREGATE_TYPE_P (type))
+ {
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: aggregate\n");
+
+ return 1;
+ }
+#endif
+
+ 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);
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS && !no_rtl)
+ {
+ rs6000_sysv_varargs_p = 1;
+ save_area = plus_constant (frame_pointer_rtx, RS6000_VARARGS_OFFSET);
+ }
+#endif
+
+ 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;
+ }
+
+#ifdef TARGET_V4_CALLS
+ /* Save FP registers if needed. */
+ if (TARGET_V4_CALLS && 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);
+ }
+ }
+#endif
+}
+\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
/* Expand a block move operation, and return 1 if successful. Return 0
if we should let the compiler generate normal code.
case '*':
/* Write the register number of the TOC register. */
- fputs (TARGET_MINIMAL_TOC ? "30" : "2", file);
+ fputs (TARGET_MINIMAL_TOC ? reg_names[30] : reg_names[2], file);
return;
case 'A':
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);
/* When TARGET_MINIMAL_TOC, use the indirected toc table pointer instead
of the toc pointer. */
- if (TARGET_MINIMAL_TOC)
- fprintf (file, "(30)");
+#ifdef TARGET_NO_TOC
+ if (TARGET_NO_TOC)
+ ;
else
- fprintf (file, "(2)");
+#endif
+ 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. */
+#ifndef USING_SVR4_H
if (profile_flag)
if (first_reg > 23)
first_reg = 23;
+#endif
return first_reg;
}
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
+ +---------------------------------------+
+ | 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 |
+ +---------------------------------------+
+*/
-int
-rs6000_pushes_stack ()
+rs6000_stack_t *
+rs6000_stack_info ()
{
- int total_size = (rs6000_sa_size () + get_frame_size ()
- + current_function_outgoing_args_size);
+ static rs6000_stack_t info, zero_info;
+ rs6000_stack_t *info_ptr = &info;
+ int reg_size = TARGET_64BIT ? 8 : 4;
+ int v4_call_p = 0;
+
+ /* Zero all fields portably */
+ info = zero_info;
+
+ /* Select which calling sequence */
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ info_ptr->v4_call_p = v4_call_p = 1;
+#endif
+
+ /* 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);
- /* 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. */
+ /* Does this function call anything? */
+ info_ptr->calls_p = rs6000_makes_calls ();
- return (frame_pointer_needed || write_symbols != NO_DEBUG
- || total_size > 220
- || rs6000_makes_calls ());
+ /* 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))
+ || (v4_call_p && current_function_calls_alloca)
+ || info_ptr->calls_p)
+ {
+ info_ptr->lr_save_p = 1;
+ regs_ever_live[65] = 1;
+ }
+
+ /* 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 (v4_call_p)
+ 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, 8);
+ info_ptr->total_size = ALIGN (info_ptr->vars_size
+ + info_ptr->parm_size
+ + info_ptr->save_size
+ + info_ptr->varargs_size
+ + info_ptr->fixed_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 (v4_call_p)
+ info_ptr->push_p = (info_ptr->total_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 */
+ info_ptr->fp_save_offset = - info_ptr->fp_size;
+ info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size;
+ if (v4_call_p)
+ {
+ info_ptr->cr_save_offset = info_ptr->gp_save_offset - reg_size;
+ info_ptr->lr_save_offset = - info_ptr->total_size + reg_size;
+ }
+ else
+ {
+ info_ptr->cr_save_offset = 4;
+ info_ptr->lr_save_offset = 8;
+ }
+
+ /* 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;
+
+ return info_ptr;
}
+void
+debug_stack_info (info)
+ rs6000_stack_t *info;
+{
+ 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>"));
+
+ 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->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->v4_call_p)
+ fprintf (stderr, "\tv4_call_p = %5d\n", info->v4_call_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->varargs_save_offset)
+ fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset);
+
+ if (info->total_size)
+ fprintf (stderr, "\ttotal_size = %5d\n", info->total_size);
+
+ 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->cr_size)
+ fprintf (stderr, "\tcr_size = %5d\n", info->cr_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
+
#ifdef USING_SVR4_H
/* Write out a System V.4 style traceback table before the prologue
FILE *file;
tree name, decl;
{
-
- int first_reg = first_reg_to_save ();
- int first_fp_reg = first_fp_reg_to_save ();
- int pushes_stack = rs6000_pushes_stack ();
+ rs6000_stack_t *info = rs6000_stack_info ();
long tag;
- long version = 0; /* version number */
- long tag_type = 0; /* function type */
- long extended_tag = 0; /* additional tag words needed */
- long spare = 0; /* reserved for future use */
- long alloca_reg; /* stack/frame register */
- long fpr_max = 64 - first_fp_reg; /* # of floating point registers saved */
- long gpr_max = 32 - first_reg; /* # of general purpose registers saved */
- long sp_max; /* 1 if the function acquires a stack frame */
- long lr_max; /* 1 if the function stores the link register */
- long cr_max; /* 1 if the function has a CR save word */
- long fpscr_max = 0; /* 1 if the function has a FPSCR save word */
+ long version = 0; /* version number */
+ long tag_type = 0; /* function type */
+ long extended_tag = 0; /* additional tag words needed */
+ long spare = 0; /* reserved for future use */
+ long fpscr_max = 0; /* 1 if the function has a FPSCR save word */
+ long fpr_max = 64 - info->first_fp_reg_save; /* # of floating point registers saved */
+ long gpr_max = 32 - info->first_gp_reg_save; /* # of general purpose registers saved */
+ long alloca_reg; /* stack/frame register */
if (frame_pointer_needed)
alloca_reg = 31;
- else if (pushes_stack != 0)
+ else if (info->push_p != 0)
alloca_reg = 1;
else
alloca_reg = 0;
- lr_max = (regs_ever_live[65] || first_fp_reg < 62 || profile_flag);
- cr_max = (must_save_cr () != 0);
- sp_max = (pushes_stack != 0);
-
- tag = (((version & 3) << 24)
- | ((tag_type & 7) << 21)
- | ((extended_tag & 1) << 20)
- | ((spare & 1) << 19)
- | ((alloca_reg & 0x1f) << 14)
- | ((fpr_max & 0x1f) << 9)
- | ((gpr_max & 0x1f) << 4)
- | ((sp_max & 1) << 3)
- | ((lr_max & 1) << 2)
- | ((cr_max & 1) << 1)
- | ((fpscr_max & 1) << 0));
+ tag = ((version << 24)
+ | (tag_type << 21)
+ | (extended_tag << 20)
+ | (spare << 19)
+ | (alloca_reg << 14)
+ | (fpr_max << 9)
+ | (gpr_max << 4)
+ | (info->push_p << 3)
+ | (info->lr_save_p << 2)
+ | (info->cr_save_p << 1)
+ | (fpscr_max << 0));
fprintf (file, "\t.long 0x%lx\n", tag);
}
#endif /* USING_SVR4_H */
-
+\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);
- char buf[256];
+ rs6000_stack_t *info = rs6000_stack_info ();
+ char *store_reg = (TARGET_64BIT) ? "\tstd %s,%d(%s)" : "\t{st|stw} %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. */
#ifndef USING_SVR4_H
- if (first_fp_reg < 62)
+ if (info->first_fp_reg_save < 62)
fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n",
- SAVE_FP_PREFIX, first_fp_reg - 32, SAVE_FP_SUFFIX,
- RESTORE_FP_PREFIX, first_fp_reg - 32, RESTORE_FP_SUFFIX);
+ SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX,
+ RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
#endif
/* Write .extern for truncation routines, if needed. */
RS6000_ITRUNC, RS6000_UITRUNC);
trunc_defined = 1;
}
+
/* Write .extern for AIX common mode routines, if needed. */
if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
{
common_mode_defined = 1;
}
-#ifdef USING_SVR4_H
- /* If we have a relocatable GOT section, we need to save the LR. */
- if (TARGET_RELOCATABLE && get_pool_size () != 0)
- regs_ever_live[65] = 1;
-#endif
-
- /* If we have to call a function to save fpr's, or if we are doing profiling,
- then we will be using LR. */
- if (profile_flag)
- regs_ever_live[65] = 1;
-
-#ifndef USING_SVR4_H
- if (first_fp_reg < 62)
- regs_ever_live[65] = 1;
-#endif
-
/* If we use the link register, get it into r0. */
- if (regs_ever_live[65])
- asm_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 ())
- asm_fprintf (file, "\tmfcr 12\n");
+ if (info->cr_save_p)
+ 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)
- asm_fprintf (file, "\tstfd 30,-16(1)\n\tstfd 31,-8(1)\n");
- else if (first_fp_reg == 63)
- asm_fprintf (file, "\tstfd 31,-8(1)\n");
- else if (first_fp_reg != 64)
+ if (FP_SAVE_INLINE (info->first_fp_reg_save))
{
-#ifndef USING_SVR4_H
- asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX, first_fp_reg - 32, SAVE_FP_SUFFIX);
-#else
- int regno, loc;
+ int regno = info->first_fp_reg_save;
+ int loc = info->fp_save_offset;
- for (regno = first_fp_reg,
- loc = - (64 - first_fp_reg) * 8;
- regno < 64;
- regno++, loc += 8)
- asm_fprintf (file, "\tstfd %d,%d(1)\n", regno - 32, loc);
-#endif
+ for ( ; regno < 64; regno++, loc += 8)
+ asm_fprintf (file, "\tstfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[1]);
}
+ 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 (! TARGET_MULTIPLE || first_reg == 31)
+ if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
{
- int regno, loc;
+ int regno = info->first_gp_reg_save;
+ int loc = info->gp_save_offset;
+ int reg_size = (TARGET_64BIT) ? 8 : 4;
- for (regno = first_reg,
- loc = - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8;
- regno < 32;
- regno++, loc += 4)
- asm_fprintf (file, "\t{st|stw} %d,%d(1)\n", regno, loc);
+ for ( ; regno < 32; regno++, loc += reg_size)
+ asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[1]);
}
- else if (first_reg != 32)
- asm_fprintf (file, "\t{stm|stmw} %d,%d(1)\n", first_reg,
- - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+ 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,
+ reg_names[1]);
/* Save lr if we used it. */
- if (regs_ever_live[65])
- asm_fprintf (file, "\t{st|stw} 0,8(1)\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, store_reg, reg_names[0], info->lr_save_offset, reg_names[1]);
/* Save CR if we use any that must be preserved. */
- if (must_save_cr ())
- asm_fprintf (file, "\t{st|stw} 12,4(1)\n");
+ if (info->cr_save_p)
+ asm_fprintf (file, store_reg, reg_names[12], info->cr_save_offset, reg_names[1]);
/* Update stack and set back pointer. */
- if (must_push)
+ if (info->push_p)
{
- if (total_size < 32767)
- asm_fprintf (file, "\t{stu|stwu} 1,%d(1)\n", - total_size);
+ if (info->total_size < 32767)
+ asm_fprintf (file,
+ (TARGET_64BIT) ? "\tstdu %s,%d(%s)\n" : "\t{stu|stwu} %s,%d(%s)\n",
+ reg_names[1], - info->total_size, reg_names[1]);
else
{
- asm_fprintf (file, "\t{liu|lis} 0,%d\n\t{oril|ori} 0,0,%d\n",
- (total_size >> 16) & 0xffff, total_size & 0xffff);
- if (TARGET_POWERPC)
- asm_fprintf (file, "\tsubf 12,0,1\n");
- else
- asm_fprintf (file, "\t{sf|subfc} 12,0,1\n");
- asm_fprintf (file, "\t{st|stw} 1,0(12)\n\tmr 1,12\n");
+ asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+ reg_names[0], (info->total_size >> 16) & 0xffff,
+ reg_names[0], reg_names[0], info->total_size & 0xffff);
+ asm_fprintf (file,
+ (TARGET_64BIT) ? "\tstdux %s,%s,%s\n" : "\tstwux %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 31,1\n");
+ asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]);
/* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the
TOC_TABLE address into register 30. */
- if (TARGET_MINIMAL_TOC && get_pool_size () != 0)
+ if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
{
char buf[256];
fprintf (file, "\n");
ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno);
- fprintf (file, "\tmflr 30\n");
+ fprintf (file, "\tmflr %s\n", reg_names[30]);
if (TARGET_POWERPC64)
- fprintf (file, "\tld 0,");
+ fprintf (file, "\tld");
else if (TARGET_NEW_MNEMONICS)
- fprintf (file, "\tlwz 0,");
+ fprintf (file, "\tlwz");
else
- fprintf (file, "\tl 0,");
+ fprintf (file, "\tl");
- fprintf (file, "(");
+ fprintf (file, " %s,(", reg_names[0]);
ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno);
assemble_name (file, buf);
fprintf (file, "-");
ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
assemble_name (file, buf);
- fprintf (file, ")(30)\n");
- asm_fprintf (file, "\t{cax|add} 30,0,30\n");
+ 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 if (TARGET_NO_TOC)
+ else if (!TARGET_64BIT)
{
ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1);
- asm_fprintf (file, "\t{cau|addis} 30,0,");
+ asm_fprintf (file, "\t{cau|addis} %s,%s,", reg_names[30], reg_names[0]);
assemble_name (file, buf);
asm_fprintf (file, "@ha\n");
- asm_fprintf (file, "\t{cal|addi} 30,30,");
- assemble_name (file, buf);
- asm_fprintf (file, "@l\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 ();
+
+#else /* !USING_SVR4_H */
+ 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]);
#endif /* USING_SVR4_H */
- {
- ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0);
- asm_fprintf (file, "\t{l|lwz} 30,");
- assemble_name (file, buf);
- asm_fprintf (file, "(2)\n");
- }
}
}
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_64BIT) ? "\tld %s,%d(%s)" : "\t{l|lwz} %s,%d(%s)\n";
rtx insn = get_last_insn ();
- /* Round size to multiple of 8 bytes. */
- total_size = (total_size + 7) & ~7;
-
/* If the last insn was a BARRIER, we don't have to write anything except
the trace table. */
if (GET_CODE (insn) == NOTE)
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)
- asm_fprintf (file, "\t{l|lwz} 1,0(1)\n");
- else if (must_push)
- asm_fprintf (file, "\t{cal 1,%d(1)|addi 1,1,%d}\n", total_size);
+ || info->total_size > 32767)
+ asm_fprintf (file, load_reg, reg_names[1], 0, reg_names[1]);
+ else if (info->push_p)
+ {
+ 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])
- asm_fprintf (file, "\t{l|lwz} 0,8(1)\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, load_reg, reg_names[0], info->lr_save_offset, reg_names[1]);
/* Get the old cr if we saved it. */
- if (must_save_cr ())
- asm_fprintf (file, "\t{l|lwz} 12,4(1)\n");
+ if (info->cr_save_p)
+ asm_fprintf (file, load_reg, reg_names[12], info->cr_save_offset, reg_names[1]);
/* Set LR here to try to overlap restores below. */
- if (regs_ever_live[65])
- asm_fprintf (file, "\tmtlr 0\n");
+ if (info->lr_save_p)
+ asm_fprintf (file, "\tmtlr %s\n", reg_names[0]);
/* Restore gpr's. */
- if (! TARGET_MULTIPLE || first_reg == 31)
+ if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
{
- int regno, loc;
+ int regno = info->first_gp_reg_save;
+ int loc = info->gp_save_offset;
+ int reg_size = (TARGET_64BIT) ? 8 : 4;
- for (regno = first_reg,
- loc = - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8;
- regno < 32;
- regno++, loc += 4)
- asm_fprintf (file, "\t{l|lwz} %d,%d(1)\n", regno, loc);
+ for ( ; regno < 32; regno++, loc += reg_size)
+ asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[1]);
}
- else if (first_reg != 32)
- asm_fprintf (file, "\t{lm|lmw} %d,%d(1)\n", first_reg,
- - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+ 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,
+ reg_names[1]);
/* Restore fpr's if we can do it without calling a function. */
- if (first_fp_reg == 62)
- asm_fprintf (file, "\tlfd 30,-16(1)\n\tlfd 31,-8(1)\n");
- else if (first_fp_reg == 63)
- asm_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;
+
+ for ( ; regno < 64; regno++, loc += 8)
+ asm_fprintf (file, "\tlfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[1]);
+ }
/* If we saved cr, restore it here. Just those of cr2, cr3, and cr4
that were used. */
- if (must_save_cr ())
- asm_fprintf (file, "\tmtcrf %d,12\n",
+ 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);
+ + (regs_ever_live[72] != 0) * 0x8, reg_names[12]);
/* 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)
- {
-#ifndef USING_SVR4_H
- asm_fprintf (file, "\tb %s%d%s\n", RESTORE_FP_PREFIX, first_fp_reg - 32, RESTORE_FP_SUFFIX);
-#else
- int regno, loc;
-
- for (regno = first_fp_reg,
- loc = - (64 - first_fp_reg) * 8;
- regno < 64;
- regno++, loc += 8)
- asm_fprintf (file, "\tlfd %d,%d(1)\n", regno - 32, loc);
-
- asm_fprintf (file, "\t{br|blr}\n");
-#endif
- }
+ 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
asm_fprintf (file, "\t{br|blr}\n");
}
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));
+ 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
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])));
+ | (info->cr_save_p << 1) | (info->lr_save_p)));
/* 3 bitfields: saves backchain, spare bit, number of fpr saved
(6 bits). */
fprintf (file, "%d,",
- (must_push << 7) | (64 - first_fp_reg_to_save ()));
+ (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 ()));
fprintf (file, "\t.byte 31\n");
}
#endif /* !USING_SVR4_H */
+
+ /* Reset varargs indicator */
+ rs6000_sysv_varargs_p = 0;
}
\f
/* Output a TOC entry. We derive the entry name from what is
rtx base = x;
int offset = 0;
+ if (TARGET_NO_TOC)
+ abort ();
+
#ifdef USING_SVR4_H
if (TARGET_MINIMAL_TOC)
{
#define MASK_STRING 0x4000
#define MASK_STRING_SET 0x8000
+/* Temporary debug switches */
+#define MASK_DEBUG_STACK 0x10000
+#define MASK_DEBUG_ARG 0x20000
+
#define TARGET_POWER (target_flags & MASK_POWER)
#define TARGET_POWER2 (target_flags & MASK_POWER2)
#define TARGET_POWERPC (target_flags & MASK_POWERPC)
#define TARGET_MULTIPLE_SET (target_flags & MASK_MULTIPLE_SET)
#define TARGET_STRING (target_flags & MASK_STRING)
#define TARGET_STRING_SET (target_flags & MASK_STRING_SET)
+#define TARGET_DEBUG_STACK (target_flags & MASK_DEBUG_STACK)
+#define TARGET_DEBUG_ARG (target_flags & MASK_DEBUG_ARG)
#define TARGET_HARD_FLOAT (! TARGET_SOFT_FLOAT)
+/* Pseudo target to indicate whether the object format is ELF
+ (to get around not having conditional compilation in the md file) */
+#ifndef TARGET_ELF
+#define TARGET_ELF 0
+#endif
+
+/* If this isn't V.4, don't support -mno-toc. */
+#ifndef TARGET_NO_TOC
+#define TARGET_NO_TOC 0
+#define TARGET_TOC 1
+#endif
+
/* Run-time compilation parameters selecting different hardware subsets.
Macro to define tables used to set the flags.
{"string", MASK_STRING | MASK_STRING_SET}, \
{"no-string", - MASK_STRING}, \
{"no-string", MASK_STRING_SET}, \
+ {"debug-stack", MASK_DEBUG_STACK}, \
+ {"debug-arg", MASK_DEBUG_ARG}, \
SUBTARGET_SWITCHES \
{"", TARGET_DEFAULT}}
\f
/* Stack layout; function entry, exit and calling. */
+/* Structure used to define the rs6000 stack */
+typedef struct rs6000_stack {
+ int first_gp_reg_save; /* first callee saved GP register used */
+ int first_fp_reg_save; /* first callee saved FP register used */
+ int lr_save_p; /* true if the link reg needs to be saved */
+ int cr_save_p; /* true if the CR reg needs to be saved */
+ int push_p; /* true if we need to allocate stack space */
+ int calls_p; /* true if the function makes any calls */
+ int v4_call_p; /* true if V.4 calling sequence used */
+ int gp_save_offset; /* offset to save GP regs from inital SP */
+ int fp_save_offset; /* offset to save FP regs from inital SP */
+ int lr_save_offset; /* offset to save LR from initial SP */
+ int cr_save_offset; /* offset to save CR from initial SP */
+ int varargs_save_offset; /* offset to save the varargs registers */
+ int reg_size; /* register size (4 or 8) */
+ int varargs_size; /* size to hold V.4 args passed in regs */
+ int vars_size; /* variable save area size */
+ int parm_size; /* outgoing parameter size */
+ int save_size; /* save area size */
+ int fixed_size; /* fixed size of stack frame */
+ int gp_size; /* size of saved GP registers */
+ int fp_size; /* size of saved FP registers */
+ int cr_size; /* size to hold CR if not in save_size */
+ int total_size; /* total bytes allocated for stack */
+} rs6000_stack_t;
+
/* Define this if pushing a word on the stack
makes the stack pointer a smaller address. */
#define STACK_GROWS_DOWNWARD
arguments. */
/* #define FRAME_GROWS_DOWNWARD */
+/* Size of the outgoing register save area */
+#define RS6000_REG_SAVE (TARGET_64BIT ? 64 : 32)
+
+/* Size of the fixed area on the stack */
+#define RS6000_SAVE_AREA (TARGET_64BIT ? 48 : 24)
+
+/* Size of the V.4 varargs area if needed */
+#define RS6000_VARARGS_AREA 0
+
+/* Whether a V.4 varargs area is needed */
+extern int rs6000_sysv_varargs_p;
+
+/* Align an address */
+#define ALIGN(n,a) (((n) + (a) - 1) & ~((a) - 1))
+
+/* Size of V.4 varargs area in bytes */
+#define RS6000_VARARGS_SIZE \
+ ((GP_ARG_NUM_REG * (TARGET_64BIT ? 8 : 4)) + (FP_ARG_NUM_REG * 8) + 8)
+
+/* Offset of V.4 varargs area */
+#define RS6000_VARARGS_OFFSET \
+ (ALIGN (current_function_outgoing_args_size, 8) + RS6000_SAVE_AREA)
+
/* Offset within stack frame to start allocating local variables at.
If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
first local allocated. Otherwise, it is the offset to the BEGINNING
except for dynamic allocations. So we start after the fixed area and
outgoing parameter area. */
-#define STARTING_FRAME_OFFSET (current_function_outgoing_args_size \
- + (TARGET_64BIT ? 48 : 24))
+#define STARTING_FRAME_OFFSET (ALIGN (current_function_outgoing_args_size, 8) \
+ + RS6000_VARARGS_AREA \
+ + RS6000_SAVE_AREA)
/* If we generate an insn to push BYTES bytes,
this says how many the stack pointer really advances by.
/* Offset of first parameter from the argument pointer register value.
On the RS/6000, we define the argument pointer to the start of the fixed
area. */
-#define FIRST_PARM_OFFSET(FNDECL) (TARGET_64BIT ? 48 : 24)
+#define FIRST_PARM_OFFSET(FNDECL) RS6000_SAVE_AREA
/* Define this if stack space is still allocated for a parameter passed
in a register. The value is the number of bytes allocated to this
area. */
-#define REG_PARM_STACK_SPACE(FNDECL) (TARGET_64BIT ? 64 : 32)
+#define REG_PARM_STACK_SPACE(FNDECL) RS6000_REG_SAVE
/* Define this if the above stack space is to be considered part of the
space allocated by the caller. */
/* This is the difference between the logical top of stack and the actual sp.
For the RS/6000, sp points past the fixed area. */
-#define STACK_POINTER_OFFSET (TARGET_64BIT ? 48 : 24)
+#define STACK_POINTER_OFFSET RS6000_SAVE_AREA
/* Define this if the maximum size of all the outgoing args is to be
accumulated and pushed during the prologue. The amount can be
#define RETURN_IN_MEMORY(TYPE) \
(TYPE_MODE (TYPE) == BLKmode)
+/* Minimum and maximum general purpose registers used to hold arguments. */
+#define GP_ARG_MIN_REG 3
+#define GP_ARG_MAX_REG 10
+#define GP_ARG_NUM_REG (GP_ARG_MAX_REG - GP_ARG_MIN_REG + 1)
+
+/* Minimum and maximum floating point registers used to hold arguments. */
+#define FP_ARG_MIN_REG 33
+#define FP_ARG_MAX_REG 45
+#define FP_ARG_NUM_REG (FP_ARG_MAX_REG - FP_ARG_MIN_REG + 1)
+
+/* Return registers */
+#define GP_ARG_RETURN GP_ARG_MIN_REG
+#define FP_ARG_RETURN FP_ARG_MIN_REG
+
+/* Define cutoff for using external functions to save floating point */
+#define FP_SAVE_INLINE(FIRST_REG) ((FIRST_REG) == 62 || (FIRST_REG) == 63)
+
/* 1 if N is a possible register number for a function value
as seen by the caller.
On RS/6000, this is r3 and fp1. */
-
-#define FUNCTION_VALUE_REGNO_P(N) ((N) == 3 || ((N) == 33))
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_ARG_RETURN || ((N) == FP_ARG_RETURN))
/* 1 if N is a possible register number for function argument passing.
On RS/6000, these are r3-r10 and fp1-fp13. */
+#define FUNCTION_ARG_REGNO_P(N) \
+ (((unsigned)((N) - GP_ARG_MIN_REG) < (unsigned)(GP_ARG_NUM_REG)) \
+ || ((unsigned)((N) - FP_ARG_MIN_REG) < (unsigned)(FP_ARG_NUM_REG)))
-#define FUNCTION_ARG_REGNO_P(N) \
- (((N) <= 10 && (N) >= 3) || ((N) >= 33 && (N) <= 45))
\f
/* Define a data type for recording info about an argument list
during the scan of that argument list. This data type should
On the RS/6000, this is a structure. The first element is the number of
total argument words, the second is used to store the next
floating-point register number, and the third says how many more args we
- have prototype types for. */
-
-struct rs6000_args {int words, fregno, nargs_prototype; };
-#define CUMULATIVE_ARGS struct rs6000_args
+ have prototype types for.
+
+ The System V.4 varargs/stdarg support requires that this structure's size
+ be a multiple of sizeof(int), and that WORDS, FREGNO, NARGS_PROTOTYPE,
+ ORIG_NARGS, and VARARGS_OFFSET be the first five ints. */
+
+typedef struct rs6000_args
+{
+ int words; /* # words uses for passing GP registers */
+ int fregno; /* next available FP register */
+ int nargs_prototype; /* # args left in the current prototype */
+ int orig_nargs; /* Original value of nargs_prototype */
+ int varargs_offset; /* offset of the varargs save area */
+ int prototype; /* Whether a prototype was defined */
+} CUMULATIVE_ARGS;
/* Define intermediate macro to compute the size (in registers) of an argument
for the RS/6000. */
for a call to a function whose data type is FNTYPE.
For a library call, FNTYPE is 0. */
-#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) \
- (CUM).words = 0, \
- (CUM).fregno = 33, \
- (CUM).nargs_prototype = (FNTYPE && TYPE_ARG_TYPES (FNTYPE) \
- ? (list_length (TYPE_ARG_TYPES (FNTYPE)) - 1 \
- + (TYPE_MODE (TREE_TYPE (FNTYPE)) == BLKmode \
- || RETURN_IN_MEMORY (TREE_TYPE (FNTYPE)))) \
- : 0)
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) \
+ init_cumulative_args (&CUM, FNTYPE, LIBNAME, FALSE)
/* Similar, but when scanning the definition of a procedure. We always
set NARGS_PROTOTYPE large so we never return an EXPR_LIST. */
-#define INIT_CUMULATIVE_INCOMING_ARGS(CUM,FNTYPE,IGNORE) \
- (CUM).words = 0, \
- (CUM).fregno = 33, \
- (CUM).nargs_prototype = 1000
+#define INIT_CUMULATIVE_INCOMING_ARGS(CUM,FNTYPE,LIBNAME) \
+ init_cumulative_args (&CUM, FNTYPE, LIBNAME, TRUE)
/* 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.) */
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
-{ (CUM).nargs_prototype--; \
- if (NAMED) \
- { \
- (CUM).words += RS6000_ARG_SIZE (MODE, TYPE, NAMED); \
- if (GET_MODE_CLASS (MODE) == MODE_FLOAT) \
- (CUM).fregno++; \
- } \
-}
+ function_arg_advance (&CUM, MODE, TYPE, NAMED)
/* Non-zero if we can use a floating-point register to pass this arg. */
-#define USE_FP_FOR_ARG_P(CUM,MODE,TYPE) \
- (GET_MODE_CLASS (MODE) == MODE_FLOAT && (CUM).fregno < 46 && TARGET_HARD_FLOAT)
+#define USE_FP_FOR_ARG_P(CUM,MODE,TYPE) \
+ (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ && (CUM).fregno <= FP_ARG_MAX_REG \
+ && TARGET_HARD_FLOAT)
/* Determine where to put an argument to a function.
Value is zero to push the argument on the stack,
so we can pass the FP value just in one register. emit_library_function
doesn't support EXPR_LIST anyway. */
-#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
- (! (NAMED) ? 0 \
- : ((TYPE) != 0 && TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST) ? 0 \
- : USE_FP_FOR_ARG_P (CUM, MODE, TYPE) \
- ? ((CUM).nargs_prototype > 0 || (TYPE) == 0 \
- ? gen_rtx (REG, MODE, (CUM).fregno) \
- : ((CUM).words < 8 \
- ? gen_rtx (EXPR_LIST, VOIDmode, \
- gen_rtx (REG, (MODE), 3 + (CUM).words), \
- gen_rtx (REG, (MODE), (CUM).fregno)) \
- : gen_rtx (EXPR_LIST, VOIDmode, 0, \
- gen_rtx (REG, (MODE), (CUM).fregno)))) \
- : (CUM).words < 8 ? gen_rtx(REG, (MODE), 3 + (CUM).words) : 0)
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+ function_arg (&CUM, MODE, TYPE, NAMED)
/* 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. */
-#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
- (! (NAMED) ? 0 \
- : USE_FP_FOR_ARG_P (CUM, MODE, TYPE) && (CUM).nargs_prototype >= 0 ? 0 \
- : (((CUM).words < 8 \
- && 8 < ((CUM).words + RS6000_ARG_SIZE (MODE, TYPE, NAMED))) \
- ? 8 - (CUM).words : 0))
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+ function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED)
+
+/* 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. */
+
+#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \
+ function_arg_pass_by_reference(&CUM, MODE, TYPE, NAMED)
/* Perform any needed actions needed for a function that is receiving a
variable number of arguments.
Normally, this macro will push all remaining incoming registers on the
stack and set PRETEND_SIZE to the length of the registers pushed. */
-#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
-{ 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 > 8) \
- first_reg_offset = 8; \
- \
- if (! (NO_RTL) && first_reg_offset != 8) \
- move_block_from_reg \
- (3 + first_reg_offset, \
- gen_rtx (MEM, BLKmode, \
- plus_constant (virtual_incoming_args_rtx, \
- first_reg_offset * 4)), \
- 8 - first_reg_offset, (8 - first_reg_offset) * UNITS_PER_WORD); \
- PRETEND_SIZE = (8 - first_reg_offset) * UNITS_PER_WORD; \
- } \
-}
+#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
+ setup_incoming_varargs (&CUM, MODE, TYPE, &PRETEND_SIZE, NO_RTL)
+
+/* If defined, is a C expression that produces the machine-specific
+ code for a call to `__builtin_saveregs'. This code will be moved
+ to the very beginning of the function, before any parameter access
+ are made. The return value of this function should be an RTX that
+ contains the value to use as the return of `__builtin_saveregs'.
+
+ 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'. */
+
+#define EXPAND_BUILTIN_SAVEREGS(ARGS) \
+ expand_builtin_saveregs (ARGS)
/* This macro generates the assembly code for function entry.
FILE is a stdio stream to output the code to.
#define CAN_ELIMINATE(FROM, TO) \
((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
? ! frame_pointer_needed \
- : (FROM) == 30 ? ! TARGET_MINIMAL_TOC || get_pool_size () == 0 \
+ : (FROM) == 30 ? ! TARGET_MINIMAL_TOC || TARGET_NO_TOC || get_pool_size () == 0 \
: 1)
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \
- int total_stack_size = (rs6000_sa_size () + get_frame_size () \
- + current_function_outgoing_args_size); \
- \
- total_stack_size = (total_stack_size + 7) & ~7; \
+ rs6000_stack_t *info = rs6000_stack_info (); \
\
if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
- { \
- if (rs6000_pushes_stack ()) \
- (OFFSET) = 0; \
- else \
- (OFFSET) = - total_stack_size; \
- } \
- else if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) \
- (OFFSET) = total_stack_size; \
- else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
- { \
- if (rs6000_pushes_stack ()) \
- (OFFSET) = total_stack_size; \
- else \
- (OFFSET) = 0; \
- } \
+ (OFFSET) = (info->push_p) ? 0 : - info->total_size; \
+ else if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) \
+ (OFFSET) = info->total_size; \
+ else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
+ (OFFSET) = (info->push_p) ? info->total_size : 0; \
else if ((FROM) == 30) \
(OFFSET) = 0; \
else \
we must ensure that both words are addressable. */
#define LEGITIMATE_CONSTANT_POOL_BASE_P(X) \
- (GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X) \
+ (TARGET_TOC && GET_CODE (X) == SYMBOL_REF \
+ && CONSTANT_POOL_ADDRESS_P (X) \
&& ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (X)))
#define LEGITIMATE_CONSTANT_POOL_ADDRESS_P(X) \
(LEGITIMATE_CONSTANT_POOL_BASE_P (X) \
- || (GET_CODE (X) == CONST && GET_CODE (XEXP (X, 0)) == PLUS \
+ || (TARGET_TOC \
+ && GET_CODE (X) == CONST && GET_CODE (XEXP (X, 0)) == PLUS \
&& GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT \
&& LEGITIMATE_CONSTANT_POOL_BASE_P (XEXP (XEXP (X, 0), 0))))
#define LEGITIMATE_INDIRECT_ADDRESS_P(X) \
(GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X))
+#define LEGITIMATE_LO_SUM_ADDRESS_P(MODE, X) \
+ (TARGET_ELF \
+ && (MODE) != DImode \
+ && (MODE) != TImode \
+ && (TARGET_HARD_FLOAT || (MODE) != DFmode) \
+ && GET_CODE (X) == LO_SUM \
+ && GET_CODE (XEXP (X, 0)) == REG \
+ && REG_OK_FOR_BASE_P (XEXP (X, 0)) \
+ && CONSTANT_P (XEXP (X, 1)))
+
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ if (LEGITIMATE_INDIRECT_ADDRESS_P (X)) \
goto ADDR; \
&& (TARGET_HARD_FLOAT || (MODE) != DFmode) \
&& LEGITIMATE_INDEXED_ADDRESS_P (X)) \
goto ADDR; \
+ if (LEGITIMATE_LO_SUM_ADDRESS_P (MODE, X)) \
+ goto ADDR; \
}
\f
/* Try machine-dependent ways of modifying an illegitimate address
Then check for the sum of a register and something not constant, try to
load the other things into a register and return the sum. */
-#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
-{ if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
- && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (unsigned) (INTVAL (XEXP (X, 1)) + 0x8000) >= 0x10000) \
- { int high_int, low_int; \
- high_int = INTVAL (XEXP (X, 1)) >> 16; \
- low_int = INTVAL (XEXP (X, 1)) & 0xffff; \
- if (low_int & 0x8000) \
- high_int += 1, low_int |= 0xffff0000; \
- (X) = gen_rtx (PLUS, SImode, \
- force_operand \
- (gen_rtx (PLUS, SImode, XEXP (X, 0), \
- gen_rtx (CONST_INT, VOIDmode, \
- high_int << 16)), 0),\
- gen_rtx (CONST_INT, VOIDmode, low_int)); \
- goto WIN; \
- } \
- else if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
- && GET_CODE (XEXP (X, 1)) != CONST_INT \
- && (TARGET_HARD_FLOAT || (MODE) != DFmode) \
- && (MODE) != DImode && (MODE) != TImode) \
- { \
- (X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
+{ if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
+ && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && (unsigned) (INTVAL (XEXP (X, 1)) + 0x8000) >= 0x10000) \
+ { int high_int, low_int; \
+ high_int = INTVAL (XEXP (X, 1)) >> 16; \
+ low_int = INTVAL (XEXP (X, 1)) & 0xffff; \
+ if (low_int & 0x8000) \
+ high_int += 1, low_int |= 0xffff0000; \
+ (X) = gen_rtx (PLUS, SImode, \
+ force_operand \
+ (gen_rtx (PLUS, SImode, XEXP (X, 0), \
+ gen_rtx (CONST_INT, VOIDmode, \
+ high_int << 16)), 0), \
+ gen_rtx (CONST_INT, VOIDmode, low_int)); \
+ goto WIN; \
+ } \
+ else if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
+ && GET_CODE (XEXP (X, 1)) != CONST_INT \
+ && (TARGET_HARD_FLOAT || (MODE) != DFmode) \
+ && (MODE) != DImode && (MODE) != TImode) \
+ { \
+ (X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \
force_reg (SImode, force_operand (XEXP (X, 1), 0))); \
- goto WIN; \
- } \
+ goto WIN; \
+ } \
+ else if (TARGET_ELF && !TARGET_64BIT && TARGET_NO_TOC \
+ && GET_CODE (X) != CONST_INT \
+ && GET_CODE (X) != CONST_DOUBLE && CONSTANT_P (X) \
+ && (TARGET_HARD_FLOAT || (MODE) != DFmode) \
+ && (MODE) != DImode && (MODE) != TImode) \
+ { \
+ rtx reg = gen_reg_rtx (Pmode); \
+ emit_insn (gen_elf_high (reg, (X))); \
+ (X) = gen_rtx (LO_SUM, Pmode, reg, (X)); \
+ } \
}
/* Go to LABEL if ADDR (a legitimate address expression)
goto LABEL; \
if (GET_CODE (ADDR) == PRE_DEC) \
goto LABEL; \
+ if (GET_CODE (ADDR) == LO_SUM) \
+ goto LABEL; \
}
\f
/* Define this if some processing needs to be done immediately before
The sle and sre instructions which allow SHIFT_COUNT_TRUNCATED
have been dropped from the PowerPC architecture. */
-#define SHIFT_COUNT_TRUNCATED TARGET_POWER ? 1 : 0
+#define SHIFT_COUNT_TRUNCATED (TARGET_POWER ? 1 : 0)
/* Use atexit for static constructors/destructors, instead of defining
our own exit function. */
On the RS/6000, if it is valid in the insn, it is free. So this
always returns 0. */
-#define CONST_COSTS(RTX,CODE,OUTER_CODE) \
+#define CONST_COSTS(RTX,CODE,OUTER_CODE) \
case CONST_INT: \
case CONST: \
case LABEL_REF: \
case SYMBOL_REF: \
case CONST_DOUBLE: \
+ case HIGH: \
return 0;
/* Provide the costs of a rtl expression. This is in the body of a
fprintf (FILE, "\t.long ."); \
RS6000_OUTPUT_BASENAME (FILE, NAME); \
fprintf (FILE, ", TOC[tc0], 0\n"); \
- fprintf (FILE, ".csect .text[PR]\n."); \
+ fprintf (FILE, ".csect .text[PR]\n."); \
RS6000_OUTPUT_BASENAME (FILE, NAME); \
fprintf (FILE, ":\n"); \
if (write_symbols == XCOFF_DEBUG) \
we can't check that since not every file that uses
GO_IF_LEGITIMATE_ADDRESS_P includes real.h. */
-#define ASM_OUTPUT_SPECIAL_POOL_ENTRY_P(X) \
- (GET_CODE (X) == SYMBOL_REF \
- || (GET_CODE (X) == CONST && GET_CODE (XEXP (X, 0)) == PLUS \
- && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF) \
- || GET_CODE (X) == LABEL_REF \
- || (! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC) \
- && GET_CODE (X) == CONST_DOUBLE \
- && GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \
- && BITS_PER_WORD == HOST_BITS_PER_INT))
+#define ASM_OUTPUT_SPECIAL_POOL_ENTRY_P(X) \
+ (TARGET_TOC \
+ && (GET_CODE (X) == SYMBOL_REF \
+ || (GET_CODE (X) == CONST && GET_CODE (XEXP (X, 0)) == PLUS \
+ && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF) \
+ || GET_CODE (X) == LABEL_REF \
+ || (! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC) \
+ && GET_CODE (X) == CONST_DOUBLE \
+ && GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \
+ && BITS_PER_WORD == HOST_BITS_PER_INT)))
/* Select section for constant in constant pool.
/* This is how to output code to push a register on the stack.
It need not be very fast code. */
-#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \
- asm_fprintf (FILE, "\{tstu|stwu} %s,-4(r1)\n", reg_names[REGNO]);
+#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \
+do { \
+ extern char *reg_names[]; \
+ asm_fprintf (FILE, "\{tstu|stwu} %s,-4(%s)\n", reg_names[REGNO], \
+ reg_names[1]); \
+} while (0)
/* This is how to output an insn to pop a register from the stack.
It need not be very fast code. */
-#define ASM_OUTPUT_REG_POP(FILE,REGNO) \
- asm_fprintf (FILE, "\t{l|lwz} %s,0(r1)\n\t{ai|addic} r1,r1,4\n", \
- reg_names[REGNO])
+#define ASM_OUTPUT_REG_POP(FILE,REGNO) \
+do { \
+ extern char *reg_names[]; \
+ asm_fprintf (FILE, "\t{l|lwz} %s,0(%s)\n\t{ai|addic} %s,%s,4\n", \
+ reg_names[REGNO], reg_names[1], reg_names[1], \
+ reg_names[1]); \
+} while (0)
/* This is how to output an element of a case-vector that is absolute.
(RS/6000 does not use such vectors, but we must define this macro
extern int call_operand ();
extern int current_file_function_operand ();
extern int input_operand ();
+extern void init_cumulative_args ();
+extern void function_arg_advance ();
+extern struct rtx_def *function_arg ();
+extern int function_arg_partial_nregs ();
+extern int function_arg_pass_by_reference ();
+extern void setup_incoming_varargs ();
+extern struct rtx_def *expand_builtin_saveregs ();
extern int expand_block_move ();
extern int load_multiple_operation ();
extern int store_multiple_operation ();
extern void print_operand_address ();
extern int first_reg_to_save ();
extern int first_fp_reg_to_save ();
-extern int must_save_cr ();
-extern int rs6000_sa_size ();
extern int rs6000_makes_calls ();
-extern int rs6000_pushes_stack ();
+extern rs6000_stack_t *rs6000_stack_info ();
extern void svr4_traceback ();
extern void output_prolog ();
extern void output_epilog ();
(use (reg:DF 33))
(parallel [(set (reg:SI 3)
(call (mem:SI (match_operand 2 "" "")) (const_int 0)))
+ (use (const_int 0))
(clobber (scratch:SI))])
(set (match_operand:SI 0 "gpc_reg_operand" "")
(reg:SI 3))]
[(set_attr "type" "delayed_compare")])
\f
;; Now define ways of moving data around.
-;;
+
+;; Elf specific ways of loading addresses for non-PIC code.
+;; The output of this could be r0, but we limit it to base
+;; registers, since almost all uses of this will need it
+;; in a base register shortly.
+(define_insn "elf_high"
+ [(set (match_operand:SI 0 "register_operand" "=b")
+ (high:SI (match_operand 1 "" "")))]
+ "TARGET_ELF && !TARGET_64BIT"
+ "{cau|addis} %0,0,%1@ha")
+
+(define_insn "elf_low"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "b")
+ (match_operand 2 "" "")))]
+ "TARGET_ELF && !TARGET_64BIT"
+ "{cal %0,%a2@l(%1)|addi %0,%1,%2@l}")
+
;; For SI, we special-case integers that can't be loaded in one insn. We
;; do the load 16-bits at a time. We could do this by loading from memory,
;; and this is even supposed to be faster, but it is simpler not to get
if (GET_CODE (operands[1]) == CONST_DOUBLE)
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
- if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT
+ if (TARGET_ELF && TARGET_NO_TOC && !TARGET_64BIT
+ && CONSTANT_P (operands[1])
+ && GET_CODE (operands[1]) != HIGH
+ && GET_CODE (operands[1]) != CONST_INT)
+ {
+ rtx target = (reload_completed || reload_in_progress)
+ ? operands[0] : gen_reg_rtx (SImode);
+
+ emit_insn (gen_elf_high (target, operands[1]));
+ emit_insn (gen_elf_low (operands[0], target, operands[1]));
+ DONE;
+ }
+
+ if (CONSTANT_P (operands[1])
+ && GET_CODE (operands[1]) != CONST_INT
+ && GET_CODE (operands[1]) != HIGH
&& ! LEGITIMATE_CONSTANT_POOL_ADDRESS_P (operands[1]))
{
/* If we are to limit the number of things we put in the TOC and
"lwaux %3,%0,%2"
[(set_attr "type" "load")])
-(define_insn ""
+(define_insn "movdi_update"
[(set (mem:DI (plus:DI (match_operand:DI 1 "gpc_reg_operand" "0,0")
(match_operand:DI 2 "reg_or_short_operand" "r,I")))
(match_operand:DI 3 "gpc_reg_operand" "r,r"))
{lu|lwzu} %3,%2(%0)"
[(set_attr "type" "load")])
-(define_insn ""
+(define_insn "movsi_update"
[(set (mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0")
(match_operand:SI 2 "reg_or_short_operand" "r,I")))
(match_operand:SI 3 "gpc_reg_operand" "r,r"))
(minus:SI (reg:SI 1) (match_operand:SI 0 "reg_or_short_operand" "")))]
""
"
-{ rtx chain = gen_reg_rtx (SImode);
+{ rtx chain = gen_reg_rtx (Pmode);
rtx stack_bot = gen_rtx (MEM, Pmode, stack_pointer_rtx);
+ rtx neg_op0;
+ rtx lr_addr = NULL_RTX;
+ rtx lr = NULL_RTX;
emit_move_insn (chain, stack_bot);
- emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, operands[0]));
- emit_move_insn (stack_bot, chain);
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ {
+ lr = gen_reg_rtx (Pmode);
+ lr_addr = gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ GEN_INT (4)));
+ emit_move_insn (lr, lr_addr);
+ }
+#endif
+
+ if (GET_CODE (operands[0]) != CONST_INT
+ || INTVAL (operands[0]) < -32767
+ || INTVAL (operands[0]) > 32768)
+ {
+ neg_op0 = gen_reg_rtx (Pmode);
+ if (TARGET_POWERPC64)
+ emit_insn (gen_negdi2 (neg_op0, operands[0]));
+ else
+ emit_insn (gen_negsi2 (neg_op0, operands[0]));
+ }
+ else
+ neg_op0 = GEN_INT (- INTVAL (operands[0]));
+
+ if (TARGET_POWERPC64)
+ emit_insn (gen_movdi_update (stack_pointer_rtx, stack_pointer_rtx, neg_op0, chain));
+ else
+ emit_insn (gen_movsi_update (stack_pointer_rtx, stack_pointer_rtx, neg_op0, chain));
+
+#ifdef TARGET_V4_CALLS
+ if (TARGET_V4_CALLS)
+ emit_move_insn (lr_addr, lr);
+#endif
+
DONE;
}")
(define_expand "call"
[(parallel [(call (mem:SI (match_operand:SI 0 "address_operand" ""))
(match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))
(clobber (scratch:SI))])]
""
"
[(parallel [(set (match_operand 0 "" "")
(call (mem:SI (match_operand:SI 1 "address_operand" ""))
(match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))
(clobber (scratch:SI))])]
""
"
}")
;; Call to function in current module. No TOC pointer reload needed.
+;; Operand2 is non-zero if we are using the V.4 calling sequence and
+;; either the function was not prototyped, or it was prototyped as a
+;; variable argument function. It is > 0 if FP registers were passed
+;; and < 0 if they were not.
(define_insn ""
- [(call (mem:SI (match_operand:SI 0 "current_file_function_operand" "s"))
- (match_operand 1 "" "g"))
- (clobber (match_scratch:SI 2 "=l"))]
+ [(call (mem:SI (match_operand:SI 0 "current_file_function_operand" "s,s"))
+ (match_operand 1 "" "g,g"))
+ (use (match_operand:SI 2 "immediate_operand" "O,n"))
+ (clobber (match_scratch:SI 3 "=l,l"))]
""
- "bl %z0")
+ "*
+{
+ if (INTVAL (operands[2]) > 0)
+ return \"creqv 6,6,6\;bl %z0\";
+
+ else if (INTVAL (operands[2]) < 0)
+ return \"crxor 6,6,6\;bl %z0\";
+
+ return \"bl %z0\";
+}"
+ [(set_attr "length" "4,8")])
;; Call to function which may be in another module. Restore the TOC
;; pointer (r2) after the call unless this is System V.
+;; Operand2 is non-zero if we are using the V.4 calling sequence and
+;; either the function was not prototyped, or it was prototyped as a
+;; variable argument function. It is > 0 if FP registers were passed
+;; and < 0 if they were not.
(define_insn ""
- [(call (mem:SI (match_operand:SI 0 "call_operand" "l,s"))
- (match_operand 1 "" "fg,fg"))
- (clobber (match_scratch:SI 2 "=l,l"))]
+ [(call (mem:SI (match_operand:SI 0 "call_operand" "l,s,l,s"))
+ (match_operand 1 "" "fg,fg,fg,fg"))
+ (use (match_operand:SI 2 "immediate_operand" "O,O,n,n"))
+ (clobber (match_scratch:SI 3 "=l,l,l,l"))]
""
"*
{
+ if (INTVAL (operands[2]) > 0)
+ output_asm_insn (\"creqv 6,6,6\", operands);
+
+ else if (INTVAL (operands[2]) < 0)
+ output_asm_insn (\"crxor 6,6,6\", operands);
+
#ifndef USING_SVR4_H
if (GET_CODE (operands[0]) == REG)
return \"{brl|blrl}\;{l|lwz} 2,20(1)\";
return \"bl %z0\";
#endif
}"
- [(set_attr "length" "8")])
+ [(set_attr "length" "8,8,12,12")])
(define_insn ""
- [(set (match_operand 0 "" "=fg")
- (call (mem:SI (match_operand:SI 1 "current_file_function_operand" "s"))
- (match_operand 2 "" "g")))
- (clobber (match_scratch:SI 3 "=l"))]
+ [(set (match_operand 0 "" "=fg,fg")
+ (call (mem:SI (match_operand:SI 1 "current_file_function_operand" "s,s"))
+ (match_operand 2 "" "g,g")))
+ (use (match_operand:SI 3 "immediate_operand" "O,n"))
+ (clobber (match_scratch:SI 4 "=l,l"))]
""
- "bl %z1")
+ "*
+{
+ if (INTVAL (operands[3]) > 0)
+ return \"creqv 6,6,6\;bl %z1\";
+
+ else if (INTVAL (operands[3]) < 0)
+ return \"crxor 6,6,6\;bl %z1\";
+
+ return \"bl %z1\";
+}"
+ [(set_attr "length" "4,8")])
(define_insn ""
- [(set (match_operand 0 "" "=fg,fg")
- (call (mem:SI (match_operand:SI 1 "call_operand" "l,s"))
- (match_operand 2 "" "fg,fg")))
- (clobber (match_scratch:SI 3 "=l,l"))]
+ [(set (match_operand 0 "" "=fg,fg,fg,fg")
+ (call (mem:SI (match_operand:SI 1 "call_operand" "l,s,l,s"))
+ (match_operand 2 "" "fg,fg,fg,fg")))
+ (use (match_operand:SI 3 "immediate_operand" "O,O,n,n"))
+ (clobber (match_scratch:SI 4 "=l,l,l,l"))]
""
"*
{
+ if (INTVAL (operands[3]) > 0)
+ output_asm_insn (\"creqv 6,6,6\", operands);
+
+ else if (INTVAL (operands[3]) < 0)
+ output_asm_insn (\"crxor 6,6,6\", operands);
+
#ifndef USING_SVR4_H
if (GET_CODE (operands[1]) == REG)
return \"{brl|blrl}\;{l|lwz} 2,20(1)\";
return \"bl %z1\";
#endif
}"
- [(set_attr "length" "8")])
+ [(set_attr "length" "8,8,12,12")])
;; Call subroutine returning any type.
{
int i;
- emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx));
+ emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx, const0_rtx));
for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
[(unspec_volatile [(const_int 0)] 0)]
""
"")
+
+;; Sync instruction used for V.4 trampolines
+(define_insn "sync"
+ [(unspec [(match_operand 0 "" "")] 1)]
+ ""
+ "sync")
+
\f
;; Compare insns are next. Note that the RS/6000 has two types of compares,
;; signed & unsigned, and one type of branch.
#define MASK_RELOCATABLE 0x10000000 /* GOT pointers are PC relative */
#define MASK_NO_TRACEBACK 0x08000000 /* eliminate traceback words */
#define MASK_LITTLE_ENDIAN 0x04000000 /* target is little endian */
-#define MASK_NO_TOC 0x02000000 /* do not use TOC for loading addresses */
+#define MASK_AIX_CALLS 0x02000000 /* Use AIX calling sequence */
+#define MASK_PROTOTYPE 0x01000000 /* Only prototyped fcns pass variable args */
#define TARGET_NO_BITFIELD_TYPE (target_flags & MASK_NO_BITFIELD_TYPE)
#define TARGET_STRICT_ALIGN (target_flags & MASK_STRICT_ALIGN)
#define TARGET_RELOCATABLE (target_flags & MASK_RELOCATABLE)
#define TARGET_NO_TRACEBACK (target_flags & MASK_NO_TRACEBACK)
#define TARGET_LITTLE_ENDIAN (target_flags & MASK_LITTLE_ENDIAN)
-#define TARGET_NO_TOC (target_flags & MASK_NO_TOC)
+#define TARGET_AIX_CALLS (target_flags & MASK_AIX_CALLS)
+#define TARGET_PROTOTYPE (target_flags & MASK_PROTOTYPE)
+#define TARGET_TOC (target_flags & (MASK_64BIT \
+ | MASK_RELOCATABLE \
+ | MASK_MINIMAL_TOC))
#define TARGET_BITFIELD_TYPE (! TARGET_NO_BITFIELD_TYPE)
#define TARGET_TRACEBACK (! TARGET_NO_TRACEBACK)
#define TARGET_BIG_ENDIAN (! TARGET_LITTLE_ENDIAN)
-#define TARGET_TOC (! TARGET_NO_TOC)
+#define TARGET_NO_AIX_CALLS (! TARGET_AIX_CALLS)
+#define TARGET_NO_PROTOTYPE (! TARGET_PROTOTYPE)
+#define TARGET_NO_TOC (! TARGET_TOC)
+#define TARGET_V4_CALLS TARGET_NO_AIX_CALLS
+
+/* Pseudo target to indicate whether the object format is ELF
+ (to get around not having conditional compilation in the md file) */
+#define TARGET_ELF 1
+
+/* Note, V.4 no longer uses a normal TOC, so make -mfull-toc, be just
+ the same as -mminimal-toc. */
#undef SUBTARGET_SWITCHES
#define SUBTARGET_SWITCHES \
{ "bit-align", -MASK_NO_BITFIELD_TYPE }, \
{ "little", MASK_LITTLE_ENDIAN }, \
{ "big-endian", -MASK_LITTLE_ENDIAN }, \
{ "big", -MASK_LITTLE_ENDIAN }, \
- { "no-toc", MASK_NO_TOC | MASK_MINIMAL_TOC }, \
- { "toc", -MASK_NO_TOC },
+ { "no-toc", 0 }, \
+ { "toc", MASK_MINIMAL_TOC }, \
+ { "full-toc", MASK_MINIMAL_TOC }, \
+ { "call-aix", MASK_AIX_CALLS }, \
+ { "call-sysv", -MASK_AIX_CALLS }, \
+ { "prototype", MASK_PROTOTYPE }, \
+ { "no-prototype", -MASK_PROTOTYPE },
/* Sometimes certain combinations of command options do not make sense
on a particular target machine. You can define a macro
#define SUBTARGET_OVERRIDE_OPTIONS \
do { \
- if (TARGET_RELOCATABLE && TARGET_NO_TOC) \
- { \
- target_flags &= ~ MASK_NO_TOC; \
- error ("-mrelocatable and -mno-toc are incompatible."); \
- } \
- \
if (TARGET_RELOCATABLE && !TARGET_MINIMAL_TOC) \
{ \
target_flags |= MASK_MINIMAL_TOC; \
error ("-mrelocatable and -mno-minimal-toc are incompatible."); \
- } \
- \
- if (TARGET_NO_TOC && !TARGET_MINIMAL_TOC) \
- { \
- target_flags |= MASK_MINIMAL_TOC; \
- error ("-mno-toc and -mno-minimal-toc are incompatible."); \
} \
} while (0)
#undef FIXED_R13
#define FIXED_R13 1
+/* System V.4 passes the first 8 floating arguments in registers,
+ instead of the first 13 like AIX does. */
+#undef FP_ARG_MAX_REG
+#define FP_ARG_AIX_MAX_REG 45
+#define FP_ARG_V4_MAX_REG 40
+#define FP_ARG_MAX_REG ((TARGET_AIX_CALLS) ? FP_ARG_AIX_MAX_REG : FP_ARG_V4_MAX_REG)
+
+/* Size of the V.4 varargs area if needed */
+#undef RS6000_VARARGS_AREA
+#define RS6000_VARARGS_AREA ((rs6000_sysv_varargs_p) ? RS6000_VARARGS_SIZE : 0)
+
/* Override default big endianism */
#undef BYTES_BIG_ENDIAN
#undef WORDS_BIG_ENDIAN
#define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN)
#define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN)
+/* Size of the outgoing register save area */
+#undef RS6000_REG_SAVE
+#define RS6000_REG_SAVE (TARGET_AIX_CALLS ? (TARGET_64BIT ? 64 : 32) : 0)
+
+/* Size of the fixed area on the stack. For AIX, use the standard 6 word
+ area, otherwise use 2 words to store back chain & LR. */
+#undef RS6000_SAVE_AREA
+#define RS6000_SAVE_AREA \
+ ((TARGET_AIX_CALLS ? 24 : 8) << (TARGET_64BIT ? 1 : 0))
+
+/* Define cutoff for using external functions to save floating point.
+ Currently on V.4, always use inline stores */
+#undef FP_SAVE_INLINE
+#define FP_SAVE_INLINE(FIRST_REG) ((FIRST_REG) < 64)
+
/* Don't generate XCOFF debugging information. */
#undef XCOFF_DEBUGGING_INFO
\
if (in_section != in_toc) \
{ \
+ in_section = in_toc; \
+ fprintf (asm_out_file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); \
if (! toc_initialized) \
{ \
- if (!TARGET_RELOCATABLE && !TARGET_NO_TOC) \
- fprintf (asm_out_file, "%s\n", TOC_SECTION_ASM_OP); \
- \
- if (TARGET_MINIMAL_TOC) \
- { \
- if (!TARGET_RELOCATABLE && !TARGET_NO_TOC) \
- { \
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LCTOC", 0); \
- fprintf (asm_out_file, "\t.tc "); \
- ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1[TC],"); \
- ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); \
- fprintf (asm_out_file, "\n"); \
- } \
- \
- fprintf (asm_out_file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); \
- ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); \
- fprintf (asm_out_file, " = .+32768\n"); \
- } \
- \
+ ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); \
+ fprintf (asm_out_file, " = .+32768\n"); \
toc_initialized = 1; \
} \
- \
- else \
- fprintf (asm_out_file, "%s\n", \
- (TARGET_MINIMAL_TOC \
- ? MINIMAL_TOC_SECTION_ASM_OP \
- : TOC_SECTION_ASM_OP)); \
- \
- in_section = in_toc; \
} \
}
#undef TARGET_VERSION
#define TARGET_VERSION fprintf (stderr, " (PowerPC System V.4)");
+\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.
+
+ Unlike AIX, this needs real code. */
+
+#undef TRAMPOLINE_TEMPLATE
+#define TRAMPOLINE_TEMPLATE(FILE) \
+do { \
+ char *sc = reg_names[STATIC_CHAIN_REGNUM]; \
+ char *r0 = reg_names[0]; \
+ \
+ if (STATIC_CHAIN_REGNUM == 0 || !TARGET_NEW_MNEMONICS) \
+ abort (); \
+ \
+ if (TARGET_64BIT) \
+ { \
+ 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 */ \
+ } \
+ else \
+ { \
+ 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 */ \
+ } \
+} while (0)
+
+/* Length in units of the trampoline for entering a nested function. */
+
+#undef TRAMPOLINE_SIZE
+#define TRAMPOLINE_SIZE (TARGET_64BIT ? 52 : 44)
+
+/* 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. */
+
+#undef INITIALIZE_TRAMPOLINE
+#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, CXT) \
+{ \
+ rtx reg = gen_reg_rtx (Pmode); \
+ \
+ emit_move_insn (reg, FNADDR); \
+ emit_move_insn (gen_rtx (MEM, Pmode, \
+ plus_constant (ADDR, 8)), \
+ reg); \
+ emit_move_insn (gen_rtx (MEM, Pmode, \
+ plus_constant (ADDR, (TARGET_64BIT ? 16 : 12))), \
+ CXT); \
+ emit_insn (gen_sync (gen_rtx (MEM, BLKmode, ADDR))); \
+}
+\f
#undef CPP_PREDEFINES
#define CPP_PREDEFINES \
"-DPPC -Dunix -D__svr4__ -Asystem(unix) -Asystem(svr4) -Acpu(powerpc) -Amachine(powerpc)"
#define CPP_SPEC "\
%{posix: -D_POSIX_SOURCE} \
%{mrelocatable: -D_RELOCATABLE} \
+%{mcall-sysv: -D_CALL_SYSV} %{mcall-aix: -D_CALL_AIX} %{!mcall-sysv: %{!mcall-aix: -D_CALL_SYSV}} \
+%{msoft-float: -D_SOFT_FLOAT} %{mcpu=403: -D_SOFT_FLOAT} \
%{mlittle: -D_LITTLE_ENDIAN -Amachine(littleendian)} \
%{mlittle-endian: -D_LITTLE_ENDIAN -Amachine(littleendian)} \
%{!mlittle: %{!mlittle-endian: -D_BIG_ENDIAN -Amachine(bigendian)}} \
#define CPP_SPEC "\
%{posix: -D_POSIX_SOURCE} \
%{mrelocatable: -D_RELOCATABLE} \
+%{mcall-sysv: -D_CALL_SYSV} %{mcall-aix: -D_CALL_AIX} %{!mcall-sysv: %{!mcall-aix: -D_CALL_SYSV}} \
+%{msoft-float: -D_SOFT_FLOAT} %{mcpu=403: -D_SOFT_FLOAT} \
%{mbig: -D_BIG_ENDIAN -Amachine(bigendian)} \
%{mbig-endian: -D_BIG_ENDIAN -Amachine(bigendian)} \
%{!mbig: %{!mbig-endian: -D_LITTLE_ENDIAN -Amachine(littleendian)}} \
# Build libgcc.a with different options.
MULTILIB_OPTIONS = msoft-float \
- mno-toc \
- mlittle/mbig
+ mlittle
MULTILIB_DIRNAMES = soft-float \
- no-toc \
- little-endian big-endian
+ little-endian
MULTILIB_MATCHES = mlittle=mlittle-endian \
- mbig=mbig-endian \
msoft-float=mcpu?403 \
msoft-float=mcpu?mpc403 \
msoft-float=mcpu?ppc403
# Build the libraries for both hard and soft floating point
-MULTILIB_OPTIONS = msoft-float
-MULTILIB_DIRNAMES = soft-float
+MULTILIB_OPTIONS = msoft-float mcpu=common
+MULTILIB_DIRNAMES = soft-float common
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib