From 4697a36cef9b136c6eccca9c0e23f3e653f0bd99 Mon Sep 17 00:00:00 2001 From: Michael Meissner Date: Fri, 21 Jul 1995 18:15:38 +0000 Subject: [PATCH] V.4 support. From-SVN: r10152 --- gcc/config/rs6000/aix3newas.h | 23 + gcc/config/rs6000/eabi.asm | 34 +- gcc/config/rs6000/eabi.h | 45 ++ gcc/config/rs6000/eabiaix.h | 53 +++ gcc/config/rs6000/eabile.h | 2 + gcc/config/rs6000/milli.exp | 7 + gcc/config/rs6000/rs6000.c | 1046 ++++++++++++++++++++++++++++++++--------- gcc/config/rs6000/rs6000.h | 387 +++++++++------ gcc/config/rs6000/rs6000.md | 174 ++++++- gcc/config/rs6000/sysv4.h | 172 +++++-- gcc/config/rs6000/sysv4le.h | 2 + gcc/config/rs6000/t-ppcgas | 7 +- gcc/config/rs6000/t-rs6000 | 4 +- 13 files changed, 1507 insertions(+), 449 deletions(-) create mode 100644 gcc/config/rs6000/eabiaix.h create mode 100644 gcc/config/rs6000/milli.exp diff --git a/gcc/config/rs6000/aix3newas.h b/gcc/config/rs6000/aix3newas.h index 100d4da..31c0fd3 100644 --- a/gcc/config/rs6000/aix3newas.h +++ b/gcc/config/rs6000/aix3newas.h @@ -47,6 +47,29 @@ Boston, MA 02111-1307, USA. */ %{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. */ diff --git a/gcc/config/rs6000/eabi.asm b/gcc/config/rs6000/eabi.asm index a26c730..d775a21 100644 --- a/gcc/config/rs6000/eabi.asm +++ b/gcc/config/rs6000/eabi.asm @@ -27,6 +27,12 @@ .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 @@ -64,16 +70,36 @@ __eabi: mflr 0 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: diff --git a/gcc/config/rs6000/eabi.h b/gcc/config/rs6000/eabi.h index 88a9b5d..829a180 100644 --- a/gcc/config/rs6000/eabi.h +++ b/gcc/config/rs6000/eabi.h @@ -50,6 +50,11 @@ Boston, MA 02111-1307, USA. */ #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 @@ -74,3 +79,43 @@ Boston, MA 02111-1307, USA. */ #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) + diff --git a/gcc/config/rs6000/eabiaix.h b/gcc/config/rs6000/eabiaix.h new file mode 100644 index 0000000..6e92d17 --- /dev/null +++ b/gcc/config/rs6000/eabiaix.h @@ -0,0 +1,53 @@ +/* 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}" diff --git a/gcc/config/rs6000/eabile.h b/gcc/config/rs6000/eabile.h index 79d043a..11ff65e 100644 --- a/gcc/config/rs6000/eabile.h +++ b/gcc/config/rs6000/eabile.h @@ -29,6 +29,8 @@ Boston, MA 02111-1307, USA. */ #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)}} \ diff --git a/gcc/config/rs6000/milli.exp b/gcc/config/rs6000/milli.exp new file mode 100644 index 0000000..ea3a2b7 --- /dev/null +++ b/gcc/config/rs6000/milli.exp @@ -0,0 +1,7 @@ +#! +__mulh 0x3100 +__mull 0x3180 +__divss 0x3200 +__divus 0x3280 +__quoss 0x3300 +__quous 0x3380 diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 47a005b..424b025 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -70,6 +70,9 @@ int rs6000_compare_fp_p; 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; /* Override command line options. Mostly we process the processor type and sometimes adjust other TARGET_ options. */ @@ -212,11 +215,18 @@ rs6000_immed_double_const (i0, i1, mode) 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. */ @@ -622,6 +632,394 @@ input_operand (op, mode) return add_operand (op, mode); } +/* 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); + } +} + +/* 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); +} + +/* 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; +} + +/* 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; +} + +/* 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; +} + + +/* 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 +} + +/* 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; +} + + /* Expand a block move operation, and return 1 if successful. Return 0 if we should let the compiler generate normal code. @@ -1205,7 +1603,7 @@ print_operand (file, x, 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': @@ -1633,26 +2031,36 @@ print_operand_address (file, x) 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 (); } @@ -1678,9 +2086,11 @@ first_reg_to_save () 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; } @@ -1700,31 +2110,6 @@ first_fp_reg_to_save () 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 @@ -1743,23 +2128,259 @@ rs6000_makes_calls () return 0; } -/* Return non-zero if this function needs to push space on the stack. */ + +/* 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)) + : "")); + + 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"); +} + + + #ifdef USING_SVR4_H /* Write out a System V.4 style traceback table before the prologue @@ -1781,77 +2402,62 @@ svr4_traceback (file, name, decl) 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 */ - + /* 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. */ @@ -1861,6 +2467,7 @@ output_prolog (file, size) RS6000_ITRUNC, RS6000_UITRUNC); trunc_defined = 1; } + /* Write .extern for AIX common mode routines, if needed. */ if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined) { @@ -1873,100 +2480,79 @@ output_prolog (file, size) 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]; @@ -1979,43 +2565,54 @@ output_prolog (file, size) 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"); - } } } @@ -2026,16 +2623,10 @@ output_epilog (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 (); + 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) @@ -2046,71 +2637,68 @@ output_epilog (file, size) 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"); } @@ -2184,7 +2772,7 @@ output_epilog (file, size) 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 @@ -2194,12 +2782,12 @@ output_epilog (file, size) 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 ())); @@ -2308,6 +2896,9 @@ output_epilog (file, size) fprintf (file, "\t.byte 31\n"); } #endif /* !USING_SVR4_H */ + + /* Reset varargs indicator */ + rs6000_sysv_varargs_p = 0; } /* Output a TOC entry. We derive the entry name from what is @@ -2324,6 +2915,9 @@ output_toc (file, x, labelno) rtx base = x; int offset = 0; + if (TARGET_NO_TOC) + abort (); + #ifdef USING_SVR4_H if (TARGET_MINIMAL_TOC) { diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index 59f60a6..9bc64c3 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -150,6 +150,10 @@ extern int target_flags; #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) @@ -166,9 +170,23 @@ extern int target_flags; #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. @@ -215,6 +233,8 @@ extern int target_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}} @@ -833,6 +853,32 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, /* 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 @@ -846,6 +892,29 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, 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 @@ -855,8 +924,9 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, 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. @@ -866,12 +936,12 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, /* 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. */ @@ -880,7 +950,7 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, /* 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 @@ -922,18 +992,35 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, #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)) /* Define a data type for recording info about an argument list during the scan of that argument list. This data type should @@ -944,10 +1031,21 @@ enum reg_class { NO_REGS, BASE_REGS, GENERAL_REGS, FLOAT_REGS, 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. */ @@ -962,40 +1060,27 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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, @@ -1019,30 +1104,24 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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. @@ -1058,27 +1137,23 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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. @@ -1210,34 +1285,21 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; #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 \ @@ -1341,12 +1403,14 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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)))) @@ -1374,6 +1438,16 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; #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; \ @@ -1391,6 +1465,8 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; && (TARGET_HARD_FLOAT || (MODE) != DFmode) \ && LEGITIMATE_INDEXED_ADDRESS_P (X)) \ goto ADDR; \ + if (LEGITIMATE_LO_SUM_ADDRESS_P (MODE, X)) \ + goto ADDR; \ } /* Try machine-dependent ways of modifying an illegitimate address @@ -1416,32 +1492,42 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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) @@ -1462,6 +1548,8 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; goto LABEL; \ if (GET_CODE (ADDR) == PRE_DEC) \ goto LABEL; \ + if (GET_CODE (ADDR) == LO_SUM) \ + goto LABEL; \ } /* Define this if some processing needs to be done immediately before @@ -1561,7 +1649,7 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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. */ @@ -1575,12 +1663,13 @@ struct rs6000_args {int words, fregno, nargs_prototype; }; 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 @@ -1894,7 +1983,7 @@ toc_section () \ 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) \ @@ -1909,15 +1998,16 @@ toc_section () \ 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. @@ -2230,15 +2320,23 @@ toc_section () \ /* 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 @@ -2396,6 +2494,13 @@ extern int lwa_operand (); 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 (); @@ -2411,10 +2516,8 @@ extern void print_operand (); 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 (); diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 2899ecb..06de488 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -3487,6 +3487,7 @@ (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))] @@ -3832,7 +3833,24 @@ [(set_attr "type" "delayed_compare")]) ;; 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 @@ -3850,7 +3868,22 @@ 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 @@ -5014,7 +5047,7 @@ "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")) @@ -5037,7 +5070,7 @@ {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")) @@ -5215,12 +5248,48 @@ (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; }") @@ -5310,6 +5379,7 @@ (define_expand "call" [(parallel [(call (mem:SI (match_operand:SI 0 "address_operand" "")) (match_operand 1 "" "")) + (use (match_operand 2 "" "")) (clobber (scratch:SI))])] "" " @@ -5334,6 +5404,7 @@ [(parallel [(set (match_operand 0 "" "") (call (mem:SI (match_operand:SI 1 "address_operand" "")) (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) (clobber (scratch:SI))])] "" " @@ -5355,24 +5426,50 @@ }") ;; 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)\"; @@ -5386,24 +5483,42 @@ 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)\"; @@ -5417,7 +5532,7 @@ return \"bl %z1\"; #endif }" - [(set_attr "length" "8")]) + [(set_attr "length" "8,8,12,12")]) ;; Call subroutine returning any type. @@ -5431,7 +5546,7 @@ { 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++) { @@ -5455,6 +5570,13 @@ [(unspec_volatile [(const_int 0)] 0)] "" "") + +;; Sync instruction used for V.4 trampolines +(define_insn "sync" + [(unspec [(match_operand 0 "" "")] 1)] + "" + "sync") + ;; Compare insns are next. Note that the RS/6000 has two types of compares, ;; signed & unsigned, and one type of branch. diff --git a/gcc/config/rs6000/sysv4.h b/gcc/config/rs6000/sysv4.h index bf0cf34..d79cc94 100644 --- a/gcc/config/rs6000/sysv4.h +++ b/gcc/config/rs6000/sysv4.h @@ -27,20 +27,35 @@ Boston, MA 02111-1307, USA. */ #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 }, \ @@ -55,8 +70,13 @@ Boston, MA 02111-1307, USA. */ { "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 @@ -69,23 +89,11 @@ Boston, MA 02111-1307, USA. */ #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) #include "rs6000/powerpc.h" @@ -96,12 +104,38 @@ do { \ #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 @@ -192,37 +226,14 @@ toc_section () \ \ 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; \ } \ } @@ -342,7 +353,78 @@ while (0) #undef TARGET_VERSION #define TARGET_VERSION fprintf (stderr, " (PowerPC System V.4)"); + + +/* 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))); \ +} + #undef CPP_PREDEFINES #define CPP_PREDEFINES \ "-DPPC -Dunix -D__svr4__ -Asystem(unix) -Asystem(svr4) -Acpu(powerpc) -Amachine(powerpc)" @@ -382,6 +464,8 @@ while (0) #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)}} \ diff --git a/gcc/config/rs6000/sysv4le.h b/gcc/config/rs6000/sysv4le.h index c72bdb0..59bd1b7 100644 --- a/gcc/config/rs6000/sysv4le.h +++ b/gcc/config/rs6000/sysv4le.h @@ -29,6 +29,8 @@ Boston, MA 02111-1307, USA. */ #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)}} \ diff --git a/gcc/config/rs6000/t-ppcgas b/gcc/config/rs6000/t-ppcgas index aa87b5e..db4b936 100644 --- a/gcc/config/rs6000/t-ppcgas +++ b/gcc/config/rs6000/t-ppcgas @@ -16,15 +16,12 @@ fp-bit.c: $(srcdir)/config/fp-bit.c # 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 diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000 index ee40677..382d15b 100644 --- a/gcc/config/rs6000/t-rs6000 +++ b/gcc/config/rs6000/t-rs6000 @@ -15,8 +15,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c # 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 -- 2.7.4