diff options
Diffstat (limited to 'gcc/config/rs6000/rs6000.c')
-rwxr-xr-x | gcc/config/rs6000/rs6000.c | 7156 |
1 files changed, 0 insertions, 7156 deletions
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c deleted file mode 100755 index 53ef3f1..0000000 --- a/gcc/config/rs6000/rs6000.c +++ /dev/null @@ -1,7156 +0,0 @@ -/* Subroutines used for code generation on IBM RS/6000. - Copyright (C) 1991, 93-8, 1999 Free Software Foundation, Inc. - Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) - -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 "config.h" -#include "system.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" -#include "insn-config.h" -#include "conditions.h" -#include "insn-flags.h" -#include "insn-attr.h" -#include "flags.h" -#include "recog.h" -#include "expr.h" -#include "obstack.h" -#include "tree.h" -#include "except.h" -#include "function.h" -#include "output.h" -#include "toplev.h" - -#ifndef TARGET_NO_PROTOTYPE -#define TARGET_NO_PROTOTYPE 0 -#endif - -extern char *language_string; -extern int profile_block_flag; - -#define min(A,B) ((A) < (B) ? (A) : (B)) -#define max(A,B) ((A) > (B) ? (A) : (B)) - -/* Target cpu type */ - -enum processor_type rs6000_cpu; -struct rs6000_cpu_select rs6000_select[3] = -{ - /* switch name, tune arch */ - { (char *)0, "--with-cpu=", 1, 1 }, - { (char *)0, "-mcpu=", 1, 1 }, - { (char *)0, "-mtune=", 1, 0 }, -}; - -/* Set to non-zero by "fix" operation to indicate that itrunc and - uitrunc must be defined. */ - -int rs6000_trunc_used; - -/* Set to non-zero once they have been defined. */ - -static int trunc_defined; - -/* Set to non-zero once AIX common-mode calls have been defined. */ -static int common_mode_defined; - -/* Save information from a "cmpxx" operation until the branch or scc is - emitted. */ -rtx rs6000_compare_op0, rs6000_compare_op1; -int rs6000_compare_fp_p; - -/* CYGNUS LOCAL -- vmakarov */ -/* Override for BRANCH_COST */ -char *rs6000_branch_cost_string; -int rs6000_branch_cost = BRANCH_COST_DEFAULT; -/* END CYGNUS LOCAL */ - -#ifdef USING_SVR4_H -/* Label number of label created for -mrelocatable, to call to so we can - get the address of the GOT section */ -int rs6000_pic_labelno; -int rs6000_pic_func_labelno; - -/* Which abi to adhere to */ -char *rs6000_abi_name = RS6000_ABI_NAME; - -/* Semantics of the small data area */ -enum rs6000_sdata_type rs6000_sdata = SDATA_DATA; - -/* Which small data model to use */ -char *rs6000_sdata_name = (char *)0; -#endif - -/* Whether a System V.4 varargs area was created. */ -int rs6000_sysv_varargs_p; - -/* ABI enumeration available for subtarget to use. */ -enum rs6000_abi rs6000_current_abi; - -/* Offset & size for fpmem stack locations used for converting between - float and integral types. */ -int rs6000_fpmem_offset; -int rs6000_fpmem_size; - -/* Debug flags */ -char *rs6000_debug_name; -int rs6000_debug_stack; /* debug stack applications */ -int rs6000_debug_arg; /* debug argument handling */ - -/* Flag to say the TOC is initialized */ -int toc_initialized; - - -/* Default register names. */ -char rs6000_reg_names[][8] = -{ - "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", "10", "11", "12", "13", "14", "15", - "16", "17", "18", "19", "20", "21", "22", "23", - "24", "25", "26", "27", "28", "29", "30", "31", - "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", "10", "11", "12", "13", "14", "15", - "16", "17", "18", "19", "20", "21", "22", "23", - "24", "25", "26", "27", "28", "29", "30", "31", - "mq", "lr", "ctr","ap", - "0", "1", "2", "3", "4", "5", "6", "7", - "fpmem" -}; - -#ifdef TARGET_REGNAMES -static char alt_reg_names[][8] = -{ - "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", - "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", - "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", - "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31", - "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", - "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", - "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", - "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", - "mq", "lr", "ctr", "ap", - "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7", - "fpmem" -}; -#endif - -#ifndef MASK_STRICT_ALIGN -#define MASK_STRICT_ALIGN 0 -#endif - -/* Override command line options. Mostly we process the processor - type and sometimes adjust other TARGET_ options. */ - -void -rs6000_override_options (default_cpu) - char *default_cpu; -{ - size_t i, j; - struct rs6000_cpu_select *ptr; - - /* Simplify the entries below by making a mask for any POWER - variant and any PowerPC variant. */ - -#define POWER_MASKS (MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING) -#define POWERPC_MASKS (MASK_POWERPC | MASK_PPC_GPOPT \ - | MASK_PPC_GFXOPT | MASK_POWERPC64) -#define POWERPC_OPT_MASKS (MASK_PPC_GPOPT | MASK_PPC_GFXOPT) - - static struct ptt - { - char *name; /* Canonical processor name. */ - enum processor_type processor; /* Processor type enum value. */ - int target_enable; /* Target flags to enable. */ - int target_disable; /* Target flags to disable. */ - } processor_target_table[] - = {{"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_MASKS}, - {"power", PROCESSOR_POWER, - MASK_POWER | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"power2", PROCESSOR_POWER, - MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING, - POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"powerpc", PROCESSOR_POWERPC, - MASK_POWERPC | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"rios", PROCESSOR_RIOS1, - MASK_POWER | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"rios1", PROCESSOR_RIOS1, - MASK_POWER | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"rsc", PROCESSOR_PPC601, - MASK_POWER | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"rsc1", PROCESSOR_PPC601, - MASK_POWER | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"rios2", PROCESSOR_RIOS2, - MASK_POWER | MASK_MULTIPLE | MASK_STRING | MASK_POWER2, - POWERPC_MASKS | MASK_NEW_MNEMONICS}, - {"401", PROCESSOR_PPC403, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"403", PROCESSOR_PPC403, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS | MASK_STRICT_ALIGN, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"505", PROCESSOR_MPCCORE, - MASK_POWERPC | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"601", PROCESSOR_PPC601, - MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS | MASK_MULTIPLE | MASK_STRING, - MASK_POWER2 | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"602", PROCESSOR_PPC603, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"603", PROCESSOR_PPC603, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"603e", PROCESSOR_PPC603, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"ec603e", PROCESSOR_PPC603, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"604", PROCESSOR_PPC604, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"604e", PROCESSOR_PPC604e, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"620", PROCESSOR_PPC620, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT}, - {"740", PROCESSOR_PPC750, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"750", PROCESSOR_PPC750, - MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, - POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, - {"801", PROCESSOR_MPCCORE, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"821", PROCESSOR_MPCCORE, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"823", PROCESSOR_MPCCORE, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, - {"860", PROCESSOR_MPCCORE, - MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS, - POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}}; - - size_t ptt_size = sizeof (processor_target_table) / sizeof (struct ptt); - - int multiple = TARGET_MULTIPLE; /* save current -mmultiple/-mno-multiple status */ - int string = TARGET_STRING; /* save current -mstring/-mno-string status */ - - profile_block_flag = 0; - - /* Identify the processor type */ - rs6000_select[0].string = default_cpu; - rs6000_cpu = PROCESSOR_DEFAULT; - - for (i = 0; i < sizeof (rs6000_select) / sizeof (rs6000_select[0]); i++) - { - ptr = &rs6000_select[i]; - if (ptr->string != (char *)0 && ptr->string[0] != '\0') - { - for (j = 0; j < ptt_size; j++) - if (! strcmp (ptr->string, processor_target_table[j].name)) - { - if (ptr->set_tune_p) - rs6000_cpu = processor_target_table[j].processor; - - if (ptr->set_arch_p) - { - target_flags |= processor_target_table[j].target_enable; - target_flags &= ~processor_target_table[j].target_disable; - } - break; - } - - if (i == ptt_size) - error ("bad value (%s) for %s switch", ptr->string, ptr->name); - } - } - - /* If we are optimizing big endian systems for space, use the - store multiple instructions. */ - if (BYTES_BIG_ENDIAN && optimize_size) - target_flags |= MASK_MULTIPLE; - - /* If -mmultiple or -mno-multiple was explicitly used, don't - override with the processor default */ - if (TARGET_MULTIPLE_SET) - target_flags = (target_flags & ~MASK_MULTIPLE) | multiple; - - /* If -mstring or -mno-string was explicitly used, don't - override with the processor default */ - if (TARGET_STRING_SET) - target_flags = (target_flags & ~MASK_STRING) | string; - - /* Don't allow -mmultiple or -mstring on little endian systems unless the cpu - is a 750, because the hardware doesn't support the instructions used in - little endian mode, and causes an alignment trap. The 750 does not cause - an alignment trap (except when the target is unaligned). */ - - if (!BYTES_BIG_ENDIAN && rs6000_cpu != PROCESSOR_PPC750) - { - if (TARGET_MULTIPLE) - { - target_flags &= ~MASK_MULTIPLE; - if (TARGET_MULTIPLE_SET) - warning ("-mmultiple is not supported on little endian systems"); - } - - if (TARGET_STRING) - { - target_flags &= ~MASK_STRING; - if (TARGET_STRING_SET) - warning ("-mstring is not supported on little endian systems"); - } - } - - if (flag_pic && (DEFAULT_ABI == ABI_AIX)) - { - warning ("-f%s ignored for AIX (all code is position independent)", - (flag_pic > 1) ? "PIC" : "pic"); - flag_pic = 0; - } - - /* Set debug flags */ - if (rs6000_debug_name) - { - if (!strcmp (rs6000_debug_name, "all")) - rs6000_debug_stack = rs6000_debug_arg = 1; - else if (!strcmp (rs6000_debug_name, "stack")) - rs6000_debug_stack = 1; - else if (!strcmp (rs6000_debug_name, "arg")) - rs6000_debug_arg = 1; - else - error ("Unknown -mdebug-%s switch", rs6000_debug_name); - } - -#ifdef TARGET_REGNAMES - /* If the user desires alternate register names, copy in the alternate names - now. */ - if (TARGET_REGNAMES) - bcopy ((char *)alt_reg_names, (char *)rs6000_reg_names, sizeof (rs6000_reg_names)); -#endif - - /* CYGNUS LOCAL -- vmakarov */ - /* Override BRANCH_COST if -mbranch-cost= */ - if (rs6000_branch_cost_string) - rs6000_branch_cost = atoi (rs6000_branch_cost_string); - /* END CYGNUS LOCAL */ - -#ifdef SUBTARGET_OVERRIDE_OPTIONS - SUBTARGET_OVERRIDE_OPTIONS; -#endif -} - -void -optimization_options (level, size) - int level; - int size ATTRIBUTE_UNUSED; -{ -#ifdef HAVE_decrement_and_branch_on_count - /* When optimizing, enable use of BCT instruction. */ - if (level >= 1) - flag_branch_on_count_reg = 1; -#endif -} - -/* Do anything needed at the start of the asm file. */ - -void -rs6000_file_start (file, default_cpu) - FILE *file; - char *default_cpu; -{ - size_t i; - char buffer[80]; - char *start = buffer; - struct rs6000_cpu_select *ptr; - - if (flag_verbose_asm) - { - sprintf (buffer, "\n%s rs6000/powerpc options:", ASM_COMMENT_START); - rs6000_select[0].string = default_cpu; - - for (i = 0; i < sizeof (rs6000_select) / sizeof (rs6000_select[0]); i++) - { - ptr = &rs6000_select[i]; - if (ptr->string != (char *)0 && ptr->string[0] != '\0') - { - fprintf (file, "%s %s%s", start, ptr->name, ptr->string); - start = ""; - } - } - -#ifdef USING_SVR4_H - switch (rs6000_sdata) - { - case SDATA_NONE: fprintf (file, "%s -msdata=none", start); start = ""; break; - case SDATA_DATA: fprintf (file, "%s -msdata=data", start); start = ""; break; - case SDATA_SYSV: fprintf (file, "%s -msdata=sysv", start); start = ""; break; - case SDATA_EABI: fprintf (file, "%s -msdata=eabi", start); start = ""; break; - } - - if (rs6000_sdata && g_switch_value) - { - fprintf (file, "%s -G %d", start, g_switch_value); - start = ""; - } -#endif - - if (*start == '\0') - fputs ("\n", file); - } -} - - -/* Create a CONST_DOUBLE from a string. */ - -struct rtx_def * -rs6000_float_const (string, mode) - char *string; - enum machine_mode mode; -{ - REAL_VALUE_TYPE value = REAL_VALUE_ATOF (string, mode); - return immed_real_const_1 (value, mode); -} - -/* Return non-zero if this function is known to have a null epilogue. */ - -int -direct_return () -{ - if (reload_completed) - { - rs6000_stack_t *info = rs6000_stack_info (); - - if (info->first_gp_reg_save == 32 - && info->first_fp_reg_save == 64 - && !info->lr_save_p - && !info->cr_save_p - && !info->push_p) - return 1; - } - - return 0; -} - -/* Returns 1 always. */ - -int -any_operand (op, mode) - register rtx op ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return 1; -} - -/* Returns 1 if op is the count register */ -int -count_register_operand(op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (GET_CODE (op) != REG) - return 0; - - if (REGNO (op) == COUNT_REGISTER_REGNUM) - return 1; - - if (REGNO (op) > FIRST_PSEUDO_REGISTER) - return 1; - - return 0; -} - -/* Returns 1 if op is memory location for float/int conversions that masquerades - as a register. */ -int -fpmem_operand(op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (GET_CODE (op) != REG) - return 0; - - if (FPMEM_REGNO_P (REGNO (op))) - return 1; - -#if 0 - if (REGNO (op) > FIRST_PSEUDO_REGISTER) - return 1; -#endif - - return 0; -} - -/* Return 1 if OP is a constant that can fit in a D field. */ - -int -short_cint_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return ((GET_CODE (op) == CONST_INT - && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x8000) < 0x10000) - || GET_CODE (op) == CONSTANT_P_RTX); -} - -/* Similar for a unsigned D field. */ - -int -u_short_cint_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return ((GET_CODE (op) == CONST_INT - && (INTVAL (op) & (~ (HOST_WIDE_INT) 0xffff)) == 0) - || GET_CODE (op) == CONSTANT_P_RTX); -} - -/* Return 1 if OP is a CONST_INT that cannot fit in a signed D field. */ - -int -non_short_cint_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == CONST_INT - && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x8000) >= 0x10000); -} - -/* Returns 1 if OP is a register that is not special (i.e., not MQ, - ctr, or lr). */ - -int -gpc_reg_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) - && (GET_CODE (op) != REG - || (REGNO (op) >= 67 && !FPMEM_REGNO_P (REGNO (op))) - || REGNO (op) < 64)); -} - -/* Returns 1 if OP is either a pseudo-register or a register denoting a - CR field. */ - -int -cc_reg_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) - && (GET_CODE (op) != REG - || REGNO (op) >= FIRST_PSEUDO_REGISTER - || CR_REGNO_P (REGNO (op)))); -} - -/* Returns 1 if OP is either a pseudo-register or a register denoting a - CR field that isn't CR0. */ - -int -cc_reg_not_cr0_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) - && (GET_CODE (op) != REG - || REGNO (op) >= FIRST_PSEUDO_REGISTER - || CR_REGNO_NOT_CR0_P (REGNO (op)))); -} - -/* Returns 1 if OP is either a constant integer valid for a D-field or a - non-special register. If a register, it must be in the proper mode unless - MODE is VOIDmode. */ - -int -reg_or_short_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return short_cint_operand (op, mode) || gpc_reg_operand (op, mode); -} - -/* Similar, except check if the negation of the constant would be valid for - a D-field. */ - -int -reg_or_neg_short_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == CONST_INT) - return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P'); - - return gpc_reg_operand (op, mode); -} - -/* Return 1 if the operand is either a register or an integer whose high-order - 16 bits are zero. */ - -int -reg_or_u_short_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return u_short_cint_operand (op, mode) || gpc_reg_operand (op, mode); -} - -/* Return 1 is the operand is either a non-special register or ANY - constant integer. */ - -int -reg_or_cint_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (GET_CODE (op) == CONST_INT - || GET_CODE (op) == CONSTANT_P_RTX - || gpc_reg_operand (op, mode)); -} - -/* Return 1 if the operand is an operand that can be loaded via the GOT */ - -int -got_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == SYMBOL_REF - || GET_CODE (op) == CONST - || GET_CODE (op) == LABEL_REF); -} - -/* Return 1 if the operand is a simple references that can be loaded via - the GOT (labels involving addition aren't allowed). */ - -int -got_no_const_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF); -} - -/* Return the number of instructions it takes to form a constant in an - integer register. */ - -static int -num_insns_constant_wide (value) - HOST_WIDE_INT value; -{ - /* signed constant loadable with {cal|addi} */ - if (((unsigned HOST_WIDE_INT)value + 0x8000) < 0x10000) - return 1; - -#if HOST_BITS_PER_WIDE_INT == 32 - /* constant loadable with {cau|addis} */ - else if ((value & 0xffff) == 0) - return 1; - -#else - /* constant loadable with {cau|addis} */ - else if ((value & 0xffff) == 0 && (value & ~0xffffffff) == 0) - return 1; - - else if (TARGET_64BIT) - { - HOST_WIDE_INT low = value & 0xffffffff; - HOST_WIDE_INT high = value >> 32; - - if (high == 0 && (low & 0x80000000) == 0) - return 2; - - else if (high == 0xffffffff && (low & 0x80000000) != 0) - return 2; - - else if (!low) - return num_insns_constant_wide (high) + 1; - - else - return (num_insns_constant_wide (high) - + num_insns_constant_wide (low) + 1); - } -#endif - - else - return 2; -} - -int -num_insns_constant (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == CONST_INT) - return num_insns_constant_wide (INTVAL (op)); - - else if (GET_CODE (op) == CONST_DOUBLE && mode == SFmode) - { - long l; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_SINGLE (rv, l); - return num_insns_constant_wide ((HOST_WIDE_INT)l); - } - - else if (GET_CODE (op) == CONST_DOUBLE) - { - HOST_WIDE_INT low; - HOST_WIDE_INT high; - long l[2]; - REAL_VALUE_TYPE rv; - int endian = (WORDS_BIG_ENDIAN == 0); - - if (mode == VOIDmode || mode == DImode) - { - high = CONST_DOUBLE_HIGH (op); - low = CONST_DOUBLE_LOW (op); - } - else - { - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_DOUBLE (rv, l); - high = l[endian]; - low = l[1 - endian]; - } - - if (TARGET_32BIT) - return (num_insns_constant_wide (low) - + num_insns_constant_wide (high)); - - else - { - if (high == 0 && (low & 0x80000000) == 0) - return num_insns_constant_wide (low); - - else if (((high & 0xffffffff) == 0xffffffff) - && ((low & 0x80000000) != 0)) - return num_insns_constant_wide (low); - - else if (mask64_operand (op, mode)) - return 2; - - else if (low == 0) - return num_insns_constant_wide (high) + 1; - - else - return (num_insns_constant_wide (high) - + num_insns_constant_wide (low) + 1); - } - } - - else - abort (); -} - -/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a register - with one instruction per word. We only do this if we can safely read - CONST_DOUBLE_{LOW,HIGH}. */ - -int -easy_fp_constant (op, mode) - register rtx op; - register enum machine_mode mode; -{ - if (GET_CODE (op) != CONST_DOUBLE - || GET_MODE (op) != mode - || (GET_MODE_CLASS (mode) != MODE_FLOAT && mode != DImode)) - return 0; - - /* Consider all constants with -msoft-float to be easy */ - if (TARGET_SOFT_FLOAT && mode != DImode) - return 1; - - /* If we are using V.4 style PIC, consider all constants to be hard */ - if (flag_pic && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - return 0; - -#ifdef TARGET_RELOCATABLE - /* Similarly if we are using -mrelocatable, consider all constants to be hard */ - if (TARGET_RELOCATABLE) - return 0; -#endif - - if (mode == DFmode) - { - long k[2]; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_DOUBLE (rv, k); - - return (num_insns_constant_wide ((HOST_WIDE_INT)k[0]) == 1 - && num_insns_constant_wide ((HOST_WIDE_INT)k[1]) == 1); - } - - else if (mode == SFmode) - { - long l; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_SINGLE (rv, l); - - return num_insns_constant_wide (l) == 1; - } - - else if (mode == DImode) - return ((TARGET_64BIT - && GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_LOW (op) == 0) - || (num_insns_constant (op, DImode) <= 2)); - - else - abort (); -} - -/* Return 1 if the operand is in volatile memory. Note that during the - RTL generation phase, memory_operand does not return TRUE for - volatile memory references. So this function allows us to - recognize volatile references where its safe. */ - -int -volatile_mem_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return 0; - - if (!MEM_VOLATILE_P (op)) - return 0; - - if (mode != GET_MODE (op)) - return 0; - - if (reload_completed) - return memory_operand (op, mode); - - if (reload_in_progress) - return strict_memory_address_p (mode, XEXP (op, 0)); - - return memory_address_p (mode, XEXP (op, 0)); -} - -/* Return 1 if the operand is an offsettable memory address. */ - -int -offsettable_addr_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return offsettable_address_p (reload_completed | reload_in_progress, - mode, op); -} - -/* Return 1 if the operand is either an easy FP constant (see above) or - memory. */ - -int -mem_or_easy_const_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return memory_operand (op, mode) || easy_fp_constant (op, mode); -} - -/* Return 1 if the operand is either a non-special register or an item - that can be used as the operand of an SI add insn. */ - -int -add_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (reg_or_short_operand (op, mode) - || (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0)); -} - -/* Return 1 if OP is a constant but not a valid add_operand. */ - -int -non_add_cint_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == CONST_INT - && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x8000) >= 0x10000 - && (INTVAL (op) & 0xffff) != 0); -} - -/* Return 1 if the operand is a non-special register or a constant that - can be used as the operand of an OR or XOR insn on the RS/6000. */ - -int -logical_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (gpc_reg_operand (op, mode) - || (GET_CODE (op) == CONST_INT - && ((INTVAL (op) & (~ (HOST_WIDE_INT) 0xffff)) == 0 - || (INTVAL (op) & 0xffff) == 0)) - || GET_CODE (op) == CONSTANT_P_RTX); -} - -/* Return 1 if C is a constant that is not a logical operand (as - above). */ - -int -non_logical_cint_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == CONST_INT - && (INTVAL (op) & (~ (HOST_WIDE_INT) 0xffff)) != 0 - && (INTVAL (op) & 0xffff) != 0); -} - -/* Return 1 if C is a constant that can be encoded in a mask on the - RS/6000. It is if there are no more than two 1->0 or 0->1 transitions. - Reject all ones and all zeros, since these should have been optimized - away and confuse the making of MB and ME. */ - -int -mask_constant (c) - register HOST_WIDE_INT c; -{ - int i; - int last_bit_value; - int transitions = 0; - - if (c == 0 || c == ~0) - return 0; - - last_bit_value = c & 1; - - for (i = 1; i < 32; i++) - if (((c >>= 1) & 1) != last_bit_value) - last_bit_value ^= 1, transitions++; - - return transitions <= 2; -} - -/* Return 1 if the operand is a constant that is a mask on the RS/6000. */ - -int -mask_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return GET_CODE (op) == CONST_INT && mask_constant (INTVAL (op)); -} - -/* Return 1 if the operand is a constant that is a PowerPC64 mask. - It is if there are no more than one 1->0 or 0->1 transitions. - Reject all ones and all zeros, since these should have been optimized - away and confuse the making of MB and ME. */ - -int -mask64_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == CONST_INT) - { - HOST_WIDE_INT c = INTVAL (op); - int i; - int last_bit_value; - int transitions = 0; - - if (c == 0 || c == ~0) - return 0; - - last_bit_value = c & 1; - - for (i = 1; i < HOST_BITS_PER_WIDE_INT; i++) - if (((c >>= 1) & 1) != last_bit_value) - last_bit_value ^= 1, transitions++; - -#if HOST_BITS_PER_WIDE_INT == 32 - /* Consider CONST_INT sign-extended. */ - transitions += (last_bit_value != 1); -#endif - - return transitions <= 1; - } - else if (GET_CODE (op) == CONST_DOUBLE - && (mode == VOIDmode || mode == DImode)) - { - HOST_WIDE_INT low = CONST_DOUBLE_LOW (op); -#if HOST_BITS_PER_WIDE_INT == 32 - HOST_WIDE_INT high = CONST_DOUBLE_HIGH (op); -#endif - int i; - int last_bit_value; - int transitions = 0; - - if ((low == 0 -#if HOST_BITS_PER_WIDE_INT == 32 - && high == 0 -#endif - ) - || (low == ~0 -#if HOST_BITS_PER_WIDE_INT == 32 - && high == ~0 -#endif - )) - return 0; - - last_bit_value = low & 1; - - for (i = 1; i < HOST_BITS_PER_WIDE_INT; i++) - if (((low >>= 1) & 1) != last_bit_value) - last_bit_value ^= 1, transitions++; - -#if HOST_BITS_PER_WIDE_INT == 32 - if ((high & 1) != last_bit_value) - last_bit_value ^= 1, transitions++; - - for (i = 1; i < HOST_BITS_PER_WIDE_INT; i++) - if (((high >>= 1) & 1) != last_bit_value) - last_bit_value ^= 1, transitions++; -#endif - - return transitions <= 1; - } - else - return 0; -} - -/* Return 1 if the operand is either a non-special register or a constant - that can be used as the operand of a PowerPC64 logical AND insn. */ - -int -and64_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (logical_operand (op, mode) - || mask64_operand (op, mode)); -} - -/* Return 1 if the operand is either a non-special register or a - constant that can be used as the operand of an RS/6000 logical AND insn. */ - -int -and_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (logical_operand (op, mode) - || mask_operand (op, mode)); -} - -/* Return 1 if the operand is a general register or memory operand. */ - -int -reg_or_mem_operand (op, mode) - register rtx op; - register enum machine_mode mode; -{ - return (gpc_reg_operand (op, mode) - || memory_operand (op, mode) - || volatile_mem_operand (op, mode)); -} - -/* Return 1 if the operand is a general register or memory operand without - pre-inc or pre_dec which produces invalid form of PowerPC lwa - instruction. */ - -int -lwa_operand (op, mode) - register rtx op; - register enum machine_mode mode; -{ - rtx inner = op; - - if (reload_completed && GET_CODE (inner) == SUBREG) - inner = SUBREG_REG (inner); - - return gpc_reg_operand (inner, mode) - || (memory_operand (inner, mode) - && GET_CODE (XEXP (inner, 0)) != PRE_INC - && GET_CODE (XEXP (inner, 0)) != PRE_DEC); -} - -/* Return 1 if the operand, used inside a MEM, is a valid first argument - to CALL. This is a SYMBOL_REF or a pseudo-register, which will be - forced to lr. */ - -int -call_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && GET_MODE (op) != mode) - return 0; - - return (GET_CODE (op) == SYMBOL_REF - || (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER)); -} - - -/* Return 1 if the operand is a SYMBOL_REF for a function known to be in - this file. */ - -int -current_file_function_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == SYMBOL_REF - && (SYMBOL_REF_FLAG (op) - || op == XEXP (DECL_RTL (current_function_decl), 0))); -} - - -/* Return 1 if this operand is a valid input for a move insn. */ - -int -input_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - /* Memory is always valid. */ - if (memory_operand (op, mode)) - return 1; - - /* For floating-point, easy constants are valid. */ - if (GET_MODE_CLASS (mode) == MODE_FLOAT - && CONSTANT_P (op) - && easy_fp_constant (op, mode)) - return 1; - - /* Allow any integer constant. */ - if (GET_MODE_CLASS (mode) == MODE_INT - && (GET_CODE (op) == CONST_INT - || GET_CODE (op) == CONSTANT_P_RTX - || GET_CODE (op) == CONST_DOUBLE)) - return 1; - - /* For floating-point or multi-word mode, the only remaining valid type - is a register. */ - if (GET_MODE_CLASS (mode) == MODE_FLOAT - || GET_MODE_SIZE (mode) > UNITS_PER_WORD) - return register_operand (op, mode); - - /* The only cases left are integral modes one word or smaller (we - do not get called for MODE_CC values). These can be in any - register. */ - if (register_operand (op, mode)) - return 1; - - /* A SYMBOL_REF referring to the TOC is valid. */ - if (LEGITIMATE_CONSTANT_POOL_ADDRESS_P (op)) - return 1; - - /* Windows NT allows SYMBOL_REFs and LABEL_REFs against the TOC - directly in the instruction stream */ - if (DEFAULT_ABI == ABI_NT - && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)) - return 1; - - /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region - to be valid. */ - if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST) - && small_data_operand (op, Pmode)) - return 1; - - return 0; -} - -/* Return 1 for an operand in small memory on V.4/eabi */ - -int -small_data_operand (op, mode) - rtx op ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ -#if TARGET_ELF - rtx sym_ref, const_part; - - if (rs6000_sdata == SDATA_NONE || rs6000_sdata == SDATA_DATA) - return 0; - - if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - return 0; - - if (GET_CODE (op) == SYMBOL_REF) - sym_ref = op; - - else if (GET_CODE (op) != CONST - || GET_CODE (XEXP (op, 0)) != PLUS - || GET_CODE (XEXP (XEXP (op, 0), 0)) != SYMBOL_REF - || GET_CODE (XEXP (XEXP (op, 0), 1)) != CONST_INT) - return 0; - - else - { - rtx sum = XEXP (op, 0); - HOST_WIDE_INT summand; - - /* We have to be careful here, because it is the referenced address - that must be 32k from _SDA_BASE_, not just the symbol. */ - summand = INTVAL (XEXP (sum, 1)); - if (summand < 0 || summand > g_switch_value) - return 0; - - sym_ref = XEXP (sum, 0); - } - - if (*XSTR (sym_ref, 0) != '@') - return 0; - - return 1; - -#else - return 0; -#endif -} - - -/* 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 a PARALLEL. */ - -void -init_cumulative_args (cum, fntype, libname, incoming) - CUMULATIVE_ARGS *cum; - tree fntype; - rtx libname ATTRIBUTE_UNUSED; - int incoming; -{ - static CUMULATIVE_ARGS zero_cumulative; - enum rs6000_abi abi = DEFAULT_ABI; - - *cum = zero_cumulative; - cum->words = 0; - cum->fregno = FP_ARG_MIN_REG; - cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); - cum->call_cookie = CALL_NORMAL; - - if (incoming) - { - cum->nargs_prototype = 1000; /* don't return a PARALLEL */ - if (abi == ABI_V4 || abi == ABI_SOLARIS) - cum->varargs_offset = RS6000_VARARGS_OFFSET; - } - - else if (cum->prototype) - cum->nargs_prototype = (list_length (TYPE_ARG_TYPES (fntype)) - 1 - + (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode - || RETURN_IN_MEMORY (TREE_TYPE (fntype)))); - - else - cum->nargs_prototype = 0; - - cum->orig_nargs = cum->nargs_prototype; - - /* Check for DLL import functions */ - if (abi == ABI_NT - && fntype - && lookup_attribute ("dllimport", TYPE_ATTRIBUTES (fntype))) - cum->call_cookie = CALL_NT_DLLIMPORT; - - /* Also check for longcall's */ - else if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))) - cum->call_cookie = CALL_LONG; - - if (TARGET_DEBUG_ARG) - { - fprintf (stderr, "\ninit_cumulative_args:"); - if (fntype) - { - tree ret_type = TREE_TYPE (fntype); - fprintf (stderr, " ret code = %s,", - tree_code_name[ (int)TREE_CODE (ret_type) ]); - } - - if ((abi == ABI_V4 || abi == ABI_SOLARIS) && incoming) - fprintf (stderr, " varargs = %d, ", cum->varargs_offset); - - if (cum->call_cookie & CALL_NT_DLLIMPORT) - fprintf (stderr, " dllimport,"); - - if (cum->call_cookie & CALL_LONG) - fprintf (stderr, " longcall,"); - - fprintf (stderr, " proto = %d, nargs = %d\n", - cum->prototype, cum->nargs_prototype); - } -} - -/* If defined, a C expression which determines whether, and in which - direction, to pad out an argument with extra space. The value - should be of type `enum direction': either `upward' to pad above - the argument, `downward' to pad below, or `none' to inhibit - padding. - - For the AIX ABI structs are always stored left shifted in their - argument slot. */ - -int -function_arg_padding (mode, type) - enum machine_mode mode; - tree type; -{ - if (type != 0 && AGGREGATE_TYPE_P (type)) - return (int)upward; - - /* This is the default definition. */ - return (! BYTES_BIG_ENDIAN - ? (int)upward - : ((mode == BLKmode - ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST - && int_size_in_bytes (type) < (PARM_BOUNDARY / BITS_PER_UNIT)) - : GET_MODE_BITSIZE (mode) < PARM_BOUNDARY) - ? (int)downward : (int)upward)); -} - -/* If defined, a C expression that gives the alignment boundary, in bits, - of an argument with the specified mode and type. If it is not defined, - PARM_BOUNDARY is used for all arguments. - - Windows NT wants anything >= 8 bytes to be double word aligned. - - V.4 wants long longs to be double word aligned. */ - -int -function_arg_boundary (mode, type) - enum machine_mode mode; - tree type; -{ - if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && mode == DImode) - return 64; - - if (DEFAULT_ABI != ABI_NT || TARGET_64BIT) - return PARM_BOUNDARY; - - if (mode != BLKmode) - return (GET_MODE_SIZE (mode)) >= 8 ? 64 : 32; - - return (int_size_in_bytes (type) >= 8) ? 64 : 32; -} - -/* Update the data in CUM to advance over an argument - of mode MODE and data type TYPE. - (TYPE is null for libcalls where that information may not be available.) */ - -void -function_arg_advance (cum, mode, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - int align = (TARGET_32BIT && (cum->words & 1) != 0 - && function_arg_boundary (mode, type) == 64) ? 1 : 0; - cum->words += align; - cum->nargs_prototype--; - - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - { - /* Long longs must not be split between registers and stack */ - if ((GET_MODE_CLASS (mode) != MODE_FLOAT || TARGET_SOFT_FLOAT) - && type && !AGGREGATE_TYPE_P (type) - && cum->words < GP_ARG_NUM_REG - && cum->words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG) - { - cum->words = GP_ARG_NUM_REG; - } - - /* Aggregates get passed as pointers */ - if (type && AGGREGATE_TYPE_P (type)) - cum->words++; - - /* Floats go in registers, & don't occupy space in the GP registers - like they do for AIX unless software floating point. */ - else if (GET_MODE_CLASS (mode) == MODE_FLOAT - && TARGET_HARD_FLOAT - && cum->fregno <= FP_ARG_V4_MAX_REG) - cum->fregno++; - - else - cum->words += RS6000_ARG_SIZE (mode, type, 1); - } - else - if (named) - { - cum->words += RS6000_ARG_SIZE (mode, type, named); - if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT) - cum->fregno++; - } - - if (TARGET_DEBUG_ARG) - fprintf (stderr, - "function_adv: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n", - cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align); -} - -/* 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 PARALLEL anyway. */ - -struct rtx_def * -function_arg (cum, mode, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - int align = (TARGET_32BIT && (cum->words & 1) != 0 - && function_arg_boundary (mode, type) == 64) ? 1 : 0; - int align_words = cum->words + align; - - if (TARGET_DEBUG_ARG) - fprintf (stderr, - "function_arg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n", - cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align); - - /* Return a marker to indicate whether CR1 needs to set or clear the bit that V.4 - uses to say fp args were passed in registers. Assume that we don't need the - marker for software floating point, or compiler generated library calls. */ - if (mode == VOIDmode) - { - enum rs6000_abi abi = DEFAULT_ABI; - - if ((abi == ABI_V4 || abi == ABI_SOLARIS) - && TARGET_HARD_FLOAT - && cum->nargs_prototype < 0 - && type && (cum->prototype || TARGET_NO_PROTOTYPE)) - { - return GEN_INT (cum->call_cookie - | ((cum->fregno == FP_ARG_MIN_REG) - ? CALL_V4_SET_FP_ARGS - : CALL_V4_CLEAR_FP_ARGS)); - } - - return GEN_INT (cum->call_cookie); - } - - if (!named) - { - if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - 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 (DEFAULT_ABI == ABI_V4 /* V.4 never passes FP values in GP registers */ - || DEFAULT_ABI == ABI_SOLARIS - || ! type - || ((cum->nargs_prototype > 0) - /* IBM AIX extended its linkage convention definition always to - require FP args after register save area hole on the stack. */ - && (DEFAULT_ABI != ABI_AIX - || ! TARGET_XL_CALL - || (align_words < GP_ARG_NUM_REG)))) - return gen_rtx_REG (mode, cum->fregno); - - return gen_rtx_PARALLEL (mode, - gen_rtvec - (2, - gen_rtx_EXPR_LIST (VOIDmode, - ((align_words >= GP_ARG_NUM_REG) - ? NULL_RTX - : (align_words - + RS6000_ARG_SIZE (mode, type, named) - > GP_ARG_NUM_REG - /* If this is partially on the stack, then - we only include the portion actually - in registers here. */ - ? gen_rtx_REG (SImode, - GP_ARG_MIN_REG + align_words) - : gen_rtx_REG (mode, - GP_ARG_MIN_REG + align_words))), - const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (mode, cum->fregno), - const0_rtx))); - } - - /* Long longs won't be split between register and stack; - FP arguments get passed on the stack if they didn't get a register. */ - else if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && - (align_words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG - || (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT))) - { - return NULL_RTX; - } - - else if (align_words < GP_ARG_NUM_REG) - return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); - - return NULL_RTX; -} - -/* For an arg passed partly in registers and partly in memory, - this is the number of registers used. - For args passed entirely in registers or entirely in memory, zero. */ - -int -function_arg_partial_nregs (cum, mode, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - if (! named) - return 0; - - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - return 0; - - if (USE_FP_FOR_ARG_P (*cum, mode, type)) - { - if (cum->nargs_prototype >= 0) - return 0; - } - - if (cum->words < GP_ARG_NUM_REG - && GP_ARG_NUM_REG < (cum->words + RS6000_ARG_SIZE (mode, type, named))) - { - int ret = GP_ARG_NUM_REG - cum->words; - if (ret && TARGET_DEBUG_ARG) - fprintf (stderr, "function_arg_partial_nregs: %d\n", ret); - - return ret; - } - - return 0; -} - -/* 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 ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; - tree type; - int named ATTRIBUTE_UNUSED; -{ - if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - && type && AGGREGATE_TYPE_P (type)) - { - if (TARGET_DEBUG_ARG) - fprintf (stderr, "function_arg_pass_by_reference: aggregate\n"); - - return 1; - } - - 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_32BIT ? 4 : 8; - - if (TARGET_DEBUG_ARG) - fprintf (stderr, - "setup_vararg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, no_rtl= %d\n", - cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), no_rtl); - - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - { - rs6000_sysv_varargs_p = 1; - if (! no_rtl) - save_area = plus_constant (frame_pointer_rtx, RS6000_VARARGS_OFFSET); - } - else - rs6000_sysv_varargs_p = 0; - - if (cum->words < 8) - { - int first_reg_offset = cum->words; - - if (MUST_PASS_IN_STACK (mode, type)) - first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1); - - if (first_reg_offset > GP_ARG_NUM_REG) - first_reg_offset = GP_ARG_NUM_REG; - - if (!no_rtl && first_reg_offset != GP_ARG_NUM_REG) - move_block_from_reg - (GP_ARG_MIN_REG + first_reg_offset, - gen_rtx_MEM (BLKmode, - plus_constant (save_area, first_reg_offset * reg_size)), - GP_ARG_NUM_REG - first_reg_offset, - (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD); - - *pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD; - } - - /* Save FP registers if needed. */ - if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && 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); - } - } -} - -/* 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 ATTRIBUTE_UNUSED; -{ - return virtual_incoming_args_rtx; -} - - -/* Generate a memory reference for expand_block_move, copying volatile, - and other bits from an original memory reference. */ - -static rtx -expand_block_move_mem (mode, addr, orig_mem) - enum machine_mode mode; - rtx addr; - rtx orig_mem; -{ - rtx mem = gen_rtx_MEM (mode, addr); - - RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (orig_mem); - MEM_COPY_ATTRIBUTES (mem, orig_mem); -#ifdef MEM_UNALIGNED_P - MEM_UNALIGNED_P (mem) = MEM_UNALIGNED_P (orig_mem); -#endif - return mem; -} - -/* Expand a block move operation, and return 1 if successful. Return 0 - if we should let the compiler generate normal code. - - operands[0] is the destination - operands[1] is the source - operands[2] is the length - operands[3] is the alignment */ - -#define MAX_MOVE_REG 4 - -int -expand_block_move (operands) - rtx operands[]; -{ - rtx orig_dest = operands[0]; - rtx orig_src = operands[1]; - rtx bytes_rtx = operands[2]; - rtx align_rtx = operands[3]; - int constp = (GET_CODE (bytes_rtx) == CONST_INT); - int align = XINT (align_rtx, 0); - int bytes; - int offset; - int num_reg; - int i; - rtx src_reg; - rtx dest_reg; - rtx src_addr; - rtx dest_addr; - rtx tmp_reg; - rtx stores[MAX_MOVE_REG]; - int move_bytes; - - /* If this is not a fixed size move, just call memcpy */ - if (!constp) - return 0; - - /* Anything to move? */ - bytes = INTVAL (bytes_rtx); - if (bytes <= 0) - return 1; - - /* Don't support real large moves. If string instructions are not used, - then don't generate more than 8 loads. */ - if (TARGET_STRING) - { - if (bytes > 4*8) - return 0; - } - else if (!STRICT_ALIGNMENT) - { - if (bytes > 4*8) - return 0; - } - else if (bytes > 8*align) - return 0; - - /* Move the address into scratch registers. */ - dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0)); - src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); - - if (TARGET_STRING) /* string instructions are available */ - { - for ( ; bytes > 0; bytes -= move_bytes) - { - if (bytes > 24 /* move up to 32 bytes at a time */ - && !fixed_regs[5] - && !fixed_regs[6] - && !fixed_regs[7] - && !fixed_regs[8] - && !fixed_regs[9] - && !fixed_regs[10] - && !fixed_regs[11] - && !fixed_regs[12]) - { - move_bytes = (bytes > 32) ? 32 : bytes; - emit_insn (gen_movstrsi_8reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), - expand_block_move_mem (BLKmode, src_reg, orig_src), - GEN_INT ((move_bytes == 32) ? 0 : move_bytes), - align_rtx)); - } - else if (bytes > 16 /* move up to 24 bytes at a time */ - && !fixed_regs[7] - && !fixed_regs[8] - && !fixed_regs[9] - && !fixed_regs[10] - && !fixed_regs[11] - && !fixed_regs[12]) - { - move_bytes = (bytes > 24) ? 24 : bytes; - emit_insn (gen_movstrsi_6reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), - expand_block_move_mem (BLKmode, src_reg, orig_src), - GEN_INT (move_bytes), - align_rtx)); - } - else if (bytes > 8 /* move up to 16 bytes at a time */ - && !fixed_regs[9] - && !fixed_regs[10] - && !fixed_regs[11] - && !fixed_regs[12]) - { - move_bytes = (bytes > 16) ? 16 : bytes; - emit_insn (gen_movstrsi_4reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), - expand_block_move_mem (BLKmode, src_reg, orig_src), - GEN_INT (move_bytes), - align_rtx)); - } - else if (bytes > 4 && !TARGET_64BIT) - { /* move up to 8 bytes at a time */ - move_bytes = (bytes > 8) ? 8 : bytes; - emit_insn (gen_movstrsi_2reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), - expand_block_move_mem (BLKmode, src_reg, orig_src), - GEN_INT (move_bytes), - align_rtx)); - } - else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT)) - { /* move 4 bytes */ - move_bytes = 4; - tmp_reg = gen_reg_rtx (SImode); - emit_move_insn (tmp_reg, expand_block_move_mem (SImode, src_reg, orig_src)); - emit_move_insn (expand_block_move_mem (SImode, dest_reg, orig_dest), tmp_reg); - } - else if (bytes == 2 && (align >= 2 || !STRICT_ALIGNMENT)) - { /* move 2 bytes */ - move_bytes = 2; - tmp_reg = gen_reg_rtx (HImode); - emit_move_insn (tmp_reg, expand_block_move_mem (HImode, src_reg, orig_src)); - emit_move_insn (expand_block_move_mem (HImode, dest_reg, orig_dest), tmp_reg); - } - else if (bytes == 1) /* move 1 byte */ - { - move_bytes = 1; - tmp_reg = gen_reg_rtx (QImode); - emit_move_insn (tmp_reg, expand_block_move_mem (QImode, src_reg, orig_src)); - emit_move_insn (expand_block_move_mem (QImode, dest_reg, orig_dest), tmp_reg); - } - else - { /* move up to 4 bytes at a time */ - move_bytes = (bytes > 4) ? 4 : bytes; - emit_insn (gen_movstrsi_1reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), - expand_block_move_mem (BLKmode, src_reg, orig_src), - GEN_INT (move_bytes), - align_rtx)); - } - - if (bytes > move_bytes) - { - emit_insn (gen_addsi3 (src_reg, src_reg, GEN_INT (move_bytes))); - emit_insn (gen_addsi3 (dest_reg, dest_reg, GEN_INT (move_bytes))); - } - } - } - - else /* string instructions not available */ - { - num_reg = offset = 0; - for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes)) - { - /* Calculate the correct offset for src/dest */ - if (offset == 0) - { - src_addr = src_reg; - dest_addr = dest_reg; - } - else - { - src_addr = gen_rtx_PLUS (Pmode, src_reg, GEN_INT (offset)); - dest_addr = gen_rtx_PLUS (Pmode, dest_reg, GEN_INT (offset)); - } - - /* Generate the appropriate load and store, saving the stores for later */ - if (bytes >= 8 && TARGET_64BIT && (align >= 8 || !STRICT_ALIGNMENT)) - { - move_bytes = 8; - tmp_reg = gen_reg_rtx (DImode); - emit_insn (gen_movdi (tmp_reg, expand_block_move_mem (DImode, src_addr, orig_src))); - stores[ num_reg++ ] = gen_movdi (expand_block_move_mem (DImode, dest_addr, orig_dest), tmp_reg); - } - else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT)) - { - move_bytes = 4; - tmp_reg = gen_reg_rtx (SImode); - emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (SImode, src_addr, orig_src))); - stores[ num_reg++ ] = gen_movsi (expand_block_move_mem (SImode, dest_addr, orig_dest), tmp_reg); - } - else if (bytes >= 2 && (align >= 2 || !STRICT_ALIGNMENT)) - { - move_bytes = 2; - tmp_reg = gen_reg_rtx (HImode); - emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (HImode, src_addr, orig_src))); - stores[ num_reg++ ] = gen_movhi (expand_block_move_mem (HImode, dest_addr, orig_dest), tmp_reg); - } - else - { - move_bytes = 1; - tmp_reg = gen_reg_rtx (QImode); - emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (QImode, src_addr, orig_src))); - stores[ num_reg++ ] = gen_movqi (expand_block_move_mem (QImode, dest_addr, orig_dest), tmp_reg); - } - - if (num_reg >= MAX_MOVE_REG) - { - for (i = 0; i < num_reg; i++) - emit_insn (stores[i]); - num_reg = 0; - } - } - - for (i = 0; i < num_reg; i++) - emit_insn (stores[i]); - } - - return 1; -} - - -/* Return 1 if OP is a load multiple operation. It is known to be a - PARALLEL and the first section will be tested. */ - -int -load_multiple_operation (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - int count = XVECLEN (op, 0); - int dest_regno; - rtx src_addr; - int i; - - /* Perform a quick check so we don't blow up below. */ - if (count <= 1 - || GET_CODE (XVECEXP (op, 0, 0)) != SET - || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG - || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) - return 0; - - dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); - src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); - - for (i = 1; i < count; i++) - { - rtx elt = XVECEXP (op, 0, i); - - if (GET_CODE (elt) != SET - || GET_CODE (SET_DEST (elt)) != REG - || GET_MODE (SET_DEST (elt)) != SImode - || REGNO (SET_DEST (elt)) != dest_regno + i - || GET_CODE (SET_SRC (elt)) != MEM - || GET_MODE (SET_SRC (elt)) != SImode - || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS - || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) - || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT - || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4) - return 0; - } - - return 1; -} - -/* Similar, but tests for store multiple. Here, the second vector element - is a CLOBBER. It will be tested later. */ - -int -store_multiple_operation (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - int count = XVECLEN (op, 0) - 1; - int src_regno; - rtx dest_addr; - int i; - - /* Perform a quick check so we don't blow up below. */ - if (count <= 1 - || GET_CODE (XVECEXP (op, 0, 0)) != SET - || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM - || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) - return 0; - - src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); - dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); - - for (i = 1; i < count; i++) - { - rtx elt = XVECEXP (op, 0, i + 1); - - if (GET_CODE (elt) != SET - || GET_CODE (SET_SRC (elt)) != REG - || GET_MODE (SET_SRC (elt)) != SImode - || REGNO (SET_SRC (elt)) != src_regno + i - || GET_CODE (SET_DEST (elt)) != MEM - || GET_MODE (SET_DEST (elt)) != SImode - || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS - || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) - || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT - || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4) - return 0; - } - - return 1; -} - -/* Return 1 if OP is a comparison operation that is valid for a branch insn. - We only check the opcode against the mode of the CC value here. */ - -int -branch_comparison_operator (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - enum rtx_code code = GET_CODE (op); - enum machine_mode cc_mode; - - if (GET_RTX_CLASS (code) != '<') - return 0; - - cc_mode = GET_MODE (XEXP (op, 0)); - if (GET_MODE_CLASS (cc_mode) != MODE_CC) - return 0; - - if ((code == GT || code == LT || code == GE || code == LE) - && cc_mode == CCUNSmode) - return 0; - - if ((code == GTU || code == LTU || code == GEU || code == LEU) - && (cc_mode != CCUNSmode)) - return 0; - - return 1; -} - -/* Return 1 if OP is a comparison operation that is valid for an scc insn. - We check the opcode against the mode of the CC value and disallow EQ or - NE comparisons for integers. */ - -int -scc_comparison_operator (op, mode) - register rtx op; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (op); - enum machine_mode cc_mode; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_RTX_CLASS (code) != '<') - return 0; - - cc_mode = GET_MODE (XEXP (op, 0)); - if (GET_MODE_CLASS (cc_mode) != MODE_CC) - return 0; - - if (code == NE && cc_mode != CCFPmode) - return 0; - - if ((code == GT || code == LT || code == GE || code == LE) - && cc_mode == CCUNSmode) - return 0; - - if ((code == GTU || code == LTU || code == GEU || code == LEU) - && (cc_mode != CCUNSmode)) - return 0; - - if (cc_mode == CCEQmode && code != EQ && code != NE) - return 0; - - return 1; -} - -int -trap_comparison_operator (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - return (GET_RTX_CLASS (GET_CODE (op)) == '<' - || GET_CODE (op) == EQ || GET_CODE (op) == NE); -} - -/* Return 1 if ANDOP is a mask that has no bits on that are not in the - mask required to convert the result of a rotate insn into a shift - left insn of SHIFTOP bits. Both are known to be CONST_INT. */ - -int -includes_lshift_p (shiftop, andop) - register rtx shiftop; - register rtx andop; -{ - int shift_mask = (~0 << INTVAL (shiftop)); - - return (INTVAL (andop) & ~shift_mask) == 0; -} - -/* Similar, but for right shift. */ - -int -includes_rshift_p (shiftop, andop) - register rtx shiftop; - register rtx andop; -{ - unsigned HOST_WIDE_INT shift_mask = ~(unsigned HOST_WIDE_INT) 0; - - shift_mask >>= INTVAL (shiftop); - - return (INTVAL (andop) & ~ shift_mask) == 0; -} - -/* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates - for lfq and stfq insns. - - Note reg1 and reg2 *must* be hard registers. To be sure we will - abort if we are passed pseudo registers. */ - -int -registers_ok_for_quad_peep (reg1, reg2) - rtx reg1, reg2; -{ - /* We might have been passed a SUBREG. */ - if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) - return 0; - - return (REGNO (reg1) == REGNO (reg2) - 1); -} - -/* Return 1 if addr1 and addr2 are suitable for lfq or stfq insn. addr1 and - addr2 must be in consecutive memory locations (addr2 == addr1 + 8). */ - -int -addrs_ok_for_quad_peep (addr1, addr2) - register rtx addr1; - register rtx addr2; -{ - int reg1; - int offset1; - - /* Extract an offset (if used) from the first addr. */ - if (GET_CODE (addr1) == PLUS) - { - /* If not a REG, return zero. */ - if (GET_CODE (XEXP (addr1, 0)) != REG) - return 0; - else - { - reg1 = REGNO (XEXP (addr1, 0)); - /* The offset must be constant! */ - if (GET_CODE (XEXP (addr1, 1)) != CONST_INT) - return 0; - offset1 = INTVAL (XEXP (addr1, 1)); - } - } - else if (GET_CODE (addr1) != REG) - return 0; - else - { - reg1 = REGNO (addr1); - /* This was a simple (mem (reg)) expression. Offset is 0. */ - offset1 = 0; - } - - /* Make sure the second address is a (mem (plus (reg) (const_int). */ - if (GET_CODE (addr2) != PLUS) - return 0; - - if (GET_CODE (XEXP (addr2, 0)) != REG - || GET_CODE (XEXP (addr2, 1)) != CONST_INT) - return 0; - - if (reg1 != REGNO (XEXP (addr2, 0))) - return 0; - - /* The offset for the second addr must be 8 more than the first addr. */ - if (INTVAL (XEXP (addr2, 1)) != offset1 + 8) - return 0; - - /* All the tests passed. addr1 and addr2 are valid for lfq or stfq - instructions. */ - return 1; -} - -/* Return the register class of a scratch register needed to copy IN into - or out of a register in CLASS in MODE. If it can be done directly, - NO_REGS is returned. */ - -enum reg_class -secondary_reload_class (class, mode, in) - enum reg_class class; - enum machine_mode mode ATTRIBUTE_UNUSED; - rtx in; -{ - int regno; - - /* We can not copy a symbolic operand directly into anything other than - BASE_REGS for TARGET_ELF. So indicate that a register from BASE_REGS - is needed as an intermediate register. */ - if (TARGET_ELF - && class != BASE_REGS - && (GET_CODE (in) == SYMBOL_REF - || GET_CODE (in) == HIGH - || GET_CODE (in) == LABEL_REF - || GET_CODE (in) == CONST)) - return BASE_REGS; - - if (GET_CODE (in) == REG) - { - regno = REGNO (in); - if (regno >= FIRST_PSEUDO_REGISTER) - { - regno = true_regnum (in); - if (regno >= FIRST_PSEUDO_REGISTER) - regno = -1; - } - } - else if (GET_CODE (in) == SUBREG) - { - regno = true_regnum (in); - if (regno >= FIRST_PSEUDO_REGISTER) - regno = -1; - } - else - regno = -1; - - /* We can place anything into GENERAL_REGS and can put GENERAL_REGS - into anything. */ - if (class == GENERAL_REGS || class == BASE_REGS - || (regno >= 0 && INT_REGNO_P (regno))) - return NO_REGS; - - /* Constants, memory, and FP registers can go into FP registers. */ - if ((regno == -1 || FP_REGNO_P (regno)) - && (class == FLOAT_REGS || class == NON_SPECIAL_REGS)) - return NO_REGS; - - /* We can copy among the CR registers. */ - if ((class == CR_REGS || class == CR0_REGS) - && regno >= 0 && CR_REGNO_P (regno)) - return NO_REGS; - - /* Otherwise, we need GENERAL_REGS. */ - return GENERAL_REGS; -} - -/* Given a comparison operation, return the bit number in CCR to test. We - know this is a valid comparison. - - SCC_P is 1 if this is for an scc. That means that %D will have been - used instead of %C, so the bits will be in different places. - - Return -1 if OP isn't a valid comparison for some reason. */ - -int -ccr_bit (op, scc_p) - register rtx op; - int scc_p; -{ - /* CYGNUS LOCAL -- meissner/branch prediction */ - enum rtx_code code = GET_CODE (op); - enum machine_mode cc_mode; - int cc_regnum; - int base_bit; - rtx reg; - - if (GET_RTX_CLASS (code) != '<') - return -1; - - reg = XEXP (op, 0); - - if (GET_CODE (reg) == EXPECT) - reg = XEXP (reg, 0); - - if (GET_CODE (reg) != REG - || REGNO (reg) < 68 - || REGNO (reg) > 75) - abort (); - - cc_mode = GET_MODE (reg); - cc_regnum = REGNO (reg); - base_bit = 4 * (cc_regnum - 68); - /* END CYGNUS LOCAL -- meissner/branch prediction */ - - /* In CCEQmode cases we have made sure that the result is always in the - third bit of the CR field. */ - - if (cc_mode == CCEQmode) - return base_bit + 3; - - switch (code) - { - case NE: - return scc_p ? base_bit + 3 : base_bit + 2; - case EQ: - return base_bit + 2; - case GT: case GTU: - return base_bit + 1; - case LT: case LTU: - return base_bit; - - case GE: case GEU: - /* If floating-point, we will have done a cror to put the bit in the - unordered position. So test that bit. For integer, this is ! LT - unless this is an scc insn. */ - return cc_mode == CCFPmode || scc_p ? base_bit + 3 : base_bit; - - case LE: case LEU: - return cc_mode == CCFPmode || scc_p ? base_bit + 3 : base_bit + 1; - - default: - abort (); - } -} - -/* Return the GOT register, creating it if needed. */ - -struct rtx_def * -rs6000_got_register (value) - rtx value; -{ - if (!current_function_uses_pic_offset_table || !pic_offset_table_rtx) - { - if (reload_in_progress || reload_completed) - fatal_insn ("internal error -- needed new GOT register during reload phase to load:", value); - - current_function_uses_pic_offset_table = 1; - pic_offset_table_rtx = gen_rtx_REG (Pmode, GOT_TOC_REGNUM); - } - - return pic_offset_table_rtx; -} - - -/* Replace all occurrences of register FROM with an new pseudo register in an insn X. - Store the pseudo register used in REG. - This is only safe during FINALIZE_PIC, since the registers haven't been setup - yet. */ - -static rtx -rs6000_replace_regno (x, from, reg) - rtx x; - int from; - rtx *reg; -{ - register int i, j; - register char *fmt; - - /* Allow this function to make replacements in EXPR_LISTs. */ - if (!x) - return x; - - switch (GET_CODE (x)) - { - case SCRATCH: - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return x; - - case REG: - if (REGNO (x) == from) - { - if (! *reg) - *reg = pic_offset_table_rtx = gen_reg_rtx (Pmode); - - return *reg; - } - - return x; - - default: - break; - } - - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = rs6000_replace_regno (XEXP (x, i), from, reg); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - XVECEXP (x, i, j) = rs6000_replace_regno (XVECEXP (x, i, j), from, reg); - } - - return x; -} - - -/* By generating position-independent code, when two different - programs (A and B) share a common library (libC.a), the text of - the library can be shared whether or not the library is linked at - the same address for both programs. In some of these - environments, position-independent code requires not only the use - of different addressing modes, but also special code to enable the - use of these addressing modes. - - The `FINALIZE_PIC' macro serves as a hook to emit these special - codes once the function is being compiled into assembly code, but - not before. (It is not done before, because in the case of - compiling an inline function, it would lead to multiple PIC - prologues being included in functions which used inline functions - and were compiled to assembly language.) */ - -void -rs6000_finalize_pic () -{ - /* Loop through all of the insns, replacing the special GOT_TOC_REGNUM - with an appropriate pseudo register. If we find we need GOT/TOC, - add the appropriate init code. */ - if (flag_pic && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - { - rtx insn = get_insns (); - rtx reg = NULL_RTX; - rtx first_insn; - rtx last_insn = NULL_RTX; - - if (GET_CODE (insn) == NOTE) - insn = next_nonnote_insn (insn); - - first_insn = insn; - for ( ; insn != NULL_RTX; insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - PATTERN (insn) = rs6000_replace_regno (PATTERN (insn), - GOT_TOC_REGNUM, - ®); - - if (REG_NOTES (insn)) - REG_NOTES (insn) = rs6000_replace_regno (REG_NOTES (insn), - GOT_TOC_REGNUM, - ®); - } - - if (GET_CODE (insn) != NOTE) - last_insn = insn; - } - - if (reg) - { - rtx init = gen_init_v4_pic (reg); - emit_insn_before (init, first_insn); - if (!optimize && last_insn) - emit_insn_after (gen_rtx_USE (VOIDmode, reg), last_insn); - } - } -} - - -/* Search for any occurrence of the GOT_TOC register marker that should - have been eliminated, but may have crept back in. */ - -void -rs6000_reorg (insn) - rtx insn; -{ - if (flag_pic && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - { - rtx got_reg = gen_rtx_REG (Pmode, GOT_TOC_REGNUM); - for ( ; insn != NULL_RTX; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (got_reg, PATTERN (insn))) - fatal_insn ("GOT/TOC register marker not removed:", PATTERN (insn)); - } -} - - -/* Define the structure for the machine field in struct function. */ -struct machine_function -{ - int sysv_varargs_p; - int save_toc_p; - int fpmem_size; - int fpmem_offset; - rtx pic_offset_table_rtx; -}; - -/* Functions to save and restore rs6000_fpmem_size. - These will be called, via pointer variables, - from push_function_context and pop_function_context. */ - -void -rs6000_save_machine_status (p) - struct function *p; -{ - struct machine_function *machine = - (struct machine_function *) xmalloc (sizeof (struct machine_function)); - - p->machine = machine; - machine->sysv_varargs_p = rs6000_sysv_varargs_p; - machine->fpmem_size = rs6000_fpmem_size; - machine->fpmem_offset = rs6000_fpmem_offset; - machine->pic_offset_table_rtx = pic_offset_table_rtx; -} - -void -rs6000_restore_machine_status (p) - struct function *p; -{ - struct machine_function *machine = p->machine; - - rs6000_sysv_varargs_p = machine->sysv_varargs_p; - rs6000_fpmem_size = machine->fpmem_size; - rs6000_fpmem_offset = machine->fpmem_offset; - pic_offset_table_rtx = machine->pic_offset_table_rtx; - - free (machine); - p->machine = (struct machine_function *)0; -} - -/* Do anything needed before RTL is emitted for each function. */ - -void -rs6000_init_expanders () -{ - /* Reset varargs and save TOC indicator */ - rs6000_sysv_varargs_p = 0; - rs6000_fpmem_size = 0; - rs6000_fpmem_offset = 0; - pic_offset_table_rtx = (rtx)0; - - /* Arrange to save and restore machine status around nested functions. */ - save_machine_status = rs6000_save_machine_status; - restore_machine_status = rs6000_restore_machine_status; -} - - -/* Print an operand. Recognize special options, documented below. */ - -#if TARGET_ELF -#define SMALL_DATA_RELOC ((rs6000_sdata == SDATA_EABI) ? "sda21" : "sdarel") -#define SMALL_DATA_REG ((rs6000_sdata == SDATA_EABI) ? 0 : 13) -#else -#define SMALL_DATA_RELOC "sda21" -#define SMALL_DATA_REG 0 -#endif - -void -print_operand (file, x, code) - FILE *file; - rtx x; - char code; -{ - int i; - /* CYGNUS LOCAL -- meissner/branch prediction */ - int succeed; - /* END CYGNUS LOCAL -- meissner/branch prediction */ - HOST_WIDE_INT val; - - /* These macros test for integers and extract the low-order bits. */ -#define INT_P(X) \ -((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \ - && GET_MODE (X) == VOIDmode) - -#define INT_LOWPART(X) \ - (GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X)) - - switch (code) - { - case '.': - /* Write out an instruction after the call which may be replaced - with glue code by the loader. This depends on the AIX version. */ - asm_fprintf (file, RS6000_CALL_GLUE); - return; - - case '*': - /* Write the register number of the TOC register. */ - fputs (TARGET_MINIMAL_TOC ? reg_names[30] : reg_names[2], file); - return; - - case '$': - /* Write out either a '.' or '$' for the current location, depending - on whether this is Solaris or not. */ - putc ((DEFAULT_ABI == ABI_SOLARIS) ? '.' : '$', file); - return; - - case 'A': - /* If X is a constant integer whose low-order 5 bits are zero, - write 'l'. Otherwise, write 'r'. This is a kludge to fix a bug - in the AIX assembler where "sri" with a zero shift count - write a trash instruction. */ - if (GET_CODE (x) == CONST_INT && (INTVAL (x) & 31) == 0) - putc ('l', file); - else - putc ('r', file); - return; - - case 'b': - /* Low-order 16 bits of constant, unsigned. */ - if (! INT_P (x)) - output_operand_lossage ("invalid %%b value"); - - fprintf (file, "%d", INT_LOWPART (x) & 0xffff); - return; - - case 'B': - /* If the low-order bit is zero, write 'r'; otherwise, write 'l' - for 64-bit mask direction. */ - putc (((INT_LOWPART(x) & 1) == 0 ? 'r' : 'l'), file); - return; - - case 'C': - /* This is an optional cror needed for LE or GE floating-point - comparisons. Otherwise write nothing. */ - if ((GET_CODE (x) == LE || GET_CODE (x) == GE) - && GET_MODE (XEXP (x, 0)) == CCFPmode) - { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); - - fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3, - base_bit + 2, base_bit + (GET_CODE (x) == GE)); - } - return; - - case 'D': - /* Similar, except that this is for an scc, so we must be able to - encode the test in a single bit that is one. We do the above - for any LE, GE, GEU, or LEU and invert the bit for NE. */ - if (GET_CODE (x) == LE || GET_CODE (x) == GE - || GET_CODE (x) == LEU || GET_CODE (x) == GEU) - { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); - - fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3, - base_bit + 2, - base_bit + (GET_CODE (x) == GE || GET_CODE (x) == GEU)); - } - - else if (GET_CODE (x) == NE) - { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); - - fprintf (file, "crnor %d,%d,%d\n\t", base_bit + 3, - base_bit + 2, base_bit + 2); - } - return; - - case 'E': - /* X is a CR register. Print the number of the third bit of the CR */ - if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) - output_operand_lossage ("invalid %%E value"); - - fprintf(file, "%d", 4 * (REGNO (x) - 68) + 3); - return; - - case 'f': - /* X is a CR register. Print the shift count needed to move it - to the high-order four bits. */ - if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) - output_operand_lossage ("invalid %%f value"); - else - fprintf (file, "%d", 4 * (REGNO (x) - 68)); - return; - - case 'F': - /* Similar, but print the count for the rotate in the opposite - direction. */ - if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) - output_operand_lossage ("invalid %%F value"); - else - fprintf (file, "%d", 32 - 4 * (REGNO (x) - 68)); - return; - - case 'G': - /* X is a constant integer. If it is negative, print "m", - otherwise print "z". This is to make a aze or ame insn. */ - if (GET_CODE (x) != CONST_INT) - output_operand_lossage ("invalid %%G value"); - else if (INTVAL (x) >= 0) - putc ('z', file); - else - putc ('m', file); - return; - - case 'h': - /* If constant, output low-order five bits. Otherwise, - write normally. */ - if (INT_P (x)) - fprintf (file, "%d", INT_LOWPART (x) & 31); - else - print_operand (file, x, 0); - return; - - case 'H': - /* If constant, output low-order six bits. Otherwise, - write normally. */ - if (INT_P (x)) - fprintf (file, "%d", INT_LOWPART (x) & 63); - else - print_operand (file, x, 0); - return; - - case 'I': - /* Print `i' if this is a constant, else nothing. */ - if (INT_P (x)) - putc ('i', file); - return; - - case 'j': - /* Write the bit number in CCR for jump. */ - i = ccr_bit (x, 0); - if (i == -1) - output_operand_lossage ("invalid %%j code"); - else - fprintf (file, "%d", i); - return; - - case 'J': - /* Similar, but add one for shift count in rlinm for scc and pass - scc flag to `ccr_bit'. */ - i = ccr_bit (x, 1); - if (i == -1) - output_operand_lossage ("invalid %%J code"); - else - /* If we want bit 31, write a shift count of zero, not 32. */ - fprintf (file, "%d", i == 31 ? 0 : i + 1); - return; - - case 'k': - /* X must be a constant. Write the 1's complement of the - constant. */ - if (! INT_P (x)) - output_operand_lossage ("invalid %%k value"); - - fprintf (file, "%d", ~ INT_LOWPART (x)); - return; - - case 'L': - /* Write second word of DImode or DFmode reference. Works on register - or non-indexed memory only. */ - if (GET_CODE (x) == REG) - fprintf (file, "%s", reg_names[REGNO (x) + 1]); - else if (GET_CODE (x) == MEM) - { - /* Handle possible auto-increment. Since it is pre-increment and - we have already done it, we can just use an offset of word. */ - if (GET_CODE (XEXP (x, 0)) == PRE_INC - || GET_CODE (XEXP (x, 0)) == PRE_DEC) - output_address (plus_constant (XEXP (XEXP (x, 0), 0), - UNITS_PER_WORD)); - else - output_address (plus_constant (XEXP (x, 0), UNITS_PER_WORD)); - if (small_data_operand (x, GET_MODE (x))) - fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, - reg_names[SMALL_DATA_REG]); - } - return; - - case 'm': - /* MB value for a mask operand. */ - if (! mask_operand (x, VOIDmode)) - output_operand_lossage ("invalid %%m value"); - - val = INT_LOWPART (x); - - /* If the high bit is set and the low bit is not, the value is zero. - If the high bit is zero, the value is the first 1 bit we find from - the left. */ - if ((val & 0x80000000) && ((val & 1) == 0)) - { - putc ('0', file); - return; - } - else if ((val & 0x80000000) == 0) - { - for (i = 1; i < 32; i++) - if ((val <<= 1) & 0x80000000) - break; - fprintf (file, "%d", i); - return; - } - - /* Otherwise, look for the first 0 bit from the right. The result is its - number plus 1. We know the low-order bit is one. */ - for (i = 0; i < 32; i++) - if (((val >>= 1) & 1) == 0) - break; - - /* If we ended in ...01, i would be 0. The correct value is 31, so - we want 31 - i. */ - fprintf (file, "%d", 31 - i); - return; - - case 'M': - /* ME value for a mask operand. */ - if (! mask_operand (x, VOIDmode)) - output_operand_lossage ("invalid %%M value"); - - val = INT_LOWPART (x); - - /* If the low bit is set and the high bit is not, the value is 31. - If the low bit is zero, the value is the first 1 bit we find from - the right. */ - if ((val & 1) && ((val & 0x80000000) == 0)) - { - fputs ("31", file); - return; - } - else if ((val & 1) == 0) - { - for (i = 0; i < 32; i++) - if ((val >>= 1) & 1) - break; - - /* If we had ....10, i would be 0. The result should be - 30, so we need 30 - i. */ - fprintf (file, "%d", 30 - i); - return; - } - - /* Otherwise, look for the first 0 bit from the left. The result is its - number minus 1. We know the high-order bit is one. */ - for (i = 0; i < 32; i++) - if (((val <<= 1) & 0x80000000) == 0) - break; - - fprintf (file, "%d", i); - return; - - case 'N': - /* Write the number of elements in the vector times 4. */ - if (GET_CODE (x) != PARALLEL) - output_operand_lossage ("invalid %%N value"); - - fprintf (file, "%d", XVECLEN (x, 0) * 4); - return; - - case 'O': - /* Similar, but subtract 1 first. */ - if (GET_CODE (x) != PARALLEL) - output_operand_lossage ("invalid %%O value"); - - fprintf (file, "%d", (XVECLEN (x, 0) - 1) * 4); - return; - - case 'p': - /* X is a CONST_INT that is a power of two. Output the logarithm. */ - if (! INT_P (x) - || (i = exact_log2 (INT_LOWPART (x))) < 0) - output_operand_lossage ("invalid %%p value"); - - fprintf (file, "%d", i); - return; - - case 'P': - /* The operand must be an indirect memory reference. The result - is the register number. */ - if (GET_CODE (x) != MEM || GET_CODE (XEXP (x, 0)) != REG - || REGNO (XEXP (x, 0)) >= 32) - output_operand_lossage ("invalid %%P value"); - - fprintf (file, "%d", REGNO (XEXP (x, 0))); - return; - - /* CYGNUS LOCAL -- meissner/branch prediction */ - case 'q': - case 'Q': - /* Write -/+ depending on whether a branch is expected to succeed */ - succeed = (code == 'Q'); - if (x != const0_rtx) - succeed = !succeed; - - putc ((succeed) ? '+' : '-', file); - return; - /* END CYGNUS LOCAL -- meissner/branch prediction */ - - case 'R': - /* X is a CR register. Print the mask for `mtcrf'. */ - if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) - output_operand_lossage ("invalid %%R value"); - else - fprintf (file, "%d", 128 >> (REGNO (x) - 68)); - return; - - case 's': - /* Low 5 bits of 32 - value */ - if (! INT_P (x)) - output_operand_lossage ("invalid %%s value"); - - fprintf (file, "%d", (32 - INT_LOWPART (x)) & 31); - return; - - case 'S': - /* PowerPC64 mask position. All 0's and all 1's are excluded. - CONST_INT 32-bit mask is considered sign-extended so any - transition must occur within the CONST_INT, not on the boundary. */ - if (! mask64_operand (x, VOIDmode)) - output_operand_lossage ("invalid %%S value"); - - val = INT_LOWPART (x); - - if (val & 1) /* Clear Left */ - { - for (i = 0; i < HOST_BITS_PER_WIDE_INT; i++) - if (!((val >>= 1) & 1)) - break; - -#if HOST_BITS_PER_WIDE_INT == 32 - if (GET_CODE (x) == CONST_DOUBLE && i == 32) - { - val = CONST_DOUBLE_HIGH (x); - - if (val == 0) - --i; - else - for (i = 32; i < 64; i++) - if (!((val >>= 1) & 1)) - break; - } -#endif - /* i = index of last set bit from right - mask begins at 63 - i from left */ - if (i > 63) - output_operand_lossage ("%%S computed all 1's mask"); - fprintf (file, "%d", 63 - i); - return; - } - else /* Clear Right */ - { - for (i = 0; i < HOST_BITS_PER_WIDE_INT; i++) - if ((val >>= 1) & 1) - break; - -#if HOST_BITS_PER_WIDE_INT == 32 - if (GET_CODE (x) == CONST_DOUBLE && i == 32) - { - val = CONST_DOUBLE_HIGH (x); - - if (val == (HOST_WIDE_INT) -1) - --i; - else - for (i = 32; i < 64; i++) - if ((val >>= 1) & 1) - break; - } -#endif - /* i = index of last clear bit from right - mask ends at 62 - i from left */ - if (i > 62) - output_operand_lossage ("%%S computed all 0's mask"); - fprintf (file, "%d", 62 - i); - return; - } - - case 't': - /* Write 12 if this jump operation will branch if true, 4 otherwise. - All floating-point operations except NE branch true and integer - EQ, LT, GT, LTU and GTU also branch true. */ - if (GET_RTX_CLASS (GET_CODE (x)) != '<') - output_operand_lossage ("invalid %%t value"); - - else if ((GET_MODE (XEXP (x, 0)) == CCFPmode - && GET_CODE (x) != NE) - || GET_CODE (x) == EQ - || GET_CODE (x) == LT || GET_CODE (x) == GT - || GET_CODE (x) == LTU || GET_CODE (x) == GTU) - fputs ("12", file); - else - putc ('4', file); - return; - - case 'T': - /* Opposite of 't': write 4 if this jump operation will branch if true, - 12 otherwise. */ - if (GET_RTX_CLASS (GET_CODE (x)) != '<') - output_operand_lossage ("invalid %%T value"); - - else if ((GET_MODE (XEXP (x, 0)) == CCFPmode - && GET_CODE (x) != NE) - || GET_CODE (x) == EQ - || GET_CODE (x) == LT || GET_CODE (x) == GT - || GET_CODE (x) == LTU || GET_CODE (x) == GTU) - putc ('4', file); - else - fputs ("12", file); - return; - - case 'u': - /* High-order 16 bits of constant for use in unsigned operand. */ - if (! INT_P (x)) - output_operand_lossage ("invalid %%u value"); - - fprintf (file, "0x%x", (INT_LOWPART (x) >> 16) & 0xffff); - return; - - case 'v': - /* High-order 16 bits of constant for use in signed operand. */ - if (! INT_P (x)) - output_operand_lossage ("invalid %%v value"); - - { - int value = (INT_LOWPART (x) >> 16) & 0xffff; - - /* Solaris assembler doesn't like lis 0,0x80000 */ - if (DEFAULT_ABI == ABI_SOLARIS && (value & 0x8000) != 0) - fprintf (file, "%d", value | (~0 << 16)); - else - fprintf (file, "0x%x", value); - return; - } - - case 'U': - /* Print `u' if this has an auto-increment or auto-decrement. */ - if (GET_CODE (x) == MEM - && (GET_CODE (XEXP (x, 0)) == PRE_INC - || GET_CODE (XEXP (x, 0)) == PRE_DEC)) - putc ('u', file); - return; - - case 'V': - /* Print the trap code for this operand. */ - switch (GET_CODE (x)) - { - case EQ: - fputs ("eq", file); /* 4 */ - break; - case NE: - fputs ("ne", file); /* 24 */ - break; - case LT: - fputs ("lt", file); /* 16 */ - break; - case LE: - fputs ("le", file); /* 20 */ - break; - case GT: - fputs ("gt", file); /* 8 */ - break; - case GE: - fputs ("ge", file); /* 12 */ - break; - case LTU: - fputs ("llt", file); /* 2 */ - break; - case LEU: - fputs ("lle", file); /* 6 */ - break; - case GTU: - fputs ("lgt", file); /* 1 */ - break; - case GEU: - fputs ("lge", file); /* 5 */ - break; - default: - abort (); - } - break; - - case 'w': - /* If constant, low-order 16 bits of constant, signed. Otherwise, write - normally. */ - if (INT_P (x)) - fprintf (file, "%d", - (INT_LOWPART (x) & 0xffff) - 2 * (INT_LOWPART (x) & 0x8000)); - else - print_operand (file, x, 0); - return; - - case 'W': - /* If constant, low-order 16 bits of constant, unsigned. - Otherwise, write normally. */ - if (INT_P (x)) - fprintf (file, "%d", INT_LOWPART (x) & 0xffff); - else - print_operand (file, x, 0); - return; - - case 'X': - if (GET_CODE (x) == MEM - && LEGITIMATE_INDEXED_ADDRESS_P (XEXP (x, 0))) - putc ('x', file); - return; - - case 'Y': - /* Like 'L', for third word of TImode */ - if (GET_CODE (x) == REG) - fprintf (file, "%s", reg_names[REGNO (x) + 2]); - else if (GET_CODE (x) == MEM) - { - if (GET_CODE (XEXP (x, 0)) == PRE_INC - || GET_CODE (XEXP (x, 0)) == PRE_DEC) - output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8)); - else - output_address (plus_constant (XEXP (x, 0), 8)); - if (small_data_operand (x, GET_MODE (x))) - fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, - reg_names[SMALL_DATA_REG]); - } - return; - - case 'z': - /* X is a SYMBOL_REF. Write out the name preceded by a - period and without any trailing data in brackets. Used for function - names. If we are configured for System V (or the embedded ABI) on - the PowerPC, do not emit the period, since those systems do not use - TOCs and the like. */ - if (GET_CODE (x) != SYMBOL_REF) - abort (); - - if (XSTR (x, 0)[0] != '.') - { - switch (DEFAULT_ABI) - { - default: - abort (); - - case ABI_AIX: - putc ('.', file); - break; - - case ABI_V4: - case ABI_AIX_NODESC: - case ABI_SOLARIS: - break; - - case ABI_NT: - fputs ("..", file); - break; - } - } - RS6000_OUTPUT_BASENAME (file, XSTR (x, 0)); - return; - - case 'Z': - /* Like 'L', for last word of TImode. */ - if (GET_CODE (x) == REG) - fprintf (file, "%s", reg_names[REGNO (x) + 3]); - else if (GET_CODE (x) == MEM) - { - if (GET_CODE (XEXP (x, 0)) == PRE_INC - || GET_CODE (XEXP (x, 0)) == PRE_DEC) - output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12)); - else - output_address (plus_constant (XEXP (x, 0), 12)); - if (small_data_operand (x, GET_MODE (x))) - fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, - reg_names[SMALL_DATA_REG]); - } - return; - - case 0: - if (GET_CODE (x) == REG) - fprintf (file, "%s", reg_names[REGNO (x)]); - else if (GET_CODE (x) == MEM) - { - /* We need to handle PRE_INC and PRE_DEC here, since we need to - know the width from the mode. */ - if (GET_CODE (XEXP (x, 0)) == PRE_INC) - fprintf (file, "%d(%d)", GET_MODE_SIZE (GET_MODE (x)), - REGNO (XEXP (XEXP (x, 0), 0))); - else if (GET_CODE (XEXP (x, 0)) == PRE_DEC) - fprintf (file, "%d(%d)", - GET_MODE_SIZE (GET_MODE (x)), - REGNO (XEXP (XEXP (x, 0), 0))); - else - output_address (XEXP (x, 0)); - } - else - output_addr_const (file, x); - return; - - default: - output_operand_lossage ("invalid %%xn code"); - } -} - -/* Print the address of an operand. */ - -void -print_operand_address (file, x) - FILE *file; - register rtx x; -{ - if (GET_CODE (x) == REG) - fprintf (file, "0(%s)", reg_names[ REGNO (x) ]); - else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST || GET_CODE (x) == LABEL_REF) - { - output_addr_const (file, x); - if (small_data_operand (x, GET_MODE (x))) - fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, - reg_names[SMALL_DATA_REG]); - -#ifdef TARGET_NO_TOC - else if (TARGET_NO_TOC) - ; -#endif - else - fprintf (file, "(%s)", reg_names[ TARGET_MINIMAL_TOC ? 30 : 2 ]); - } - else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG) - { - if (REGNO (XEXP (x, 0)) == 0) - fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ], - reg_names[ REGNO (XEXP (x, 0)) ]); - else - 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(%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 (); -} - -/* This page contains routines that are used to determine what the function - prologue and epilogue code will do and write them out. */ - -/* Return the first fixed-point register that is required to be saved. 32 if - none. */ - -int -first_reg_to_save () -{ - int first_reg; - - /* Find lowest numbered live register. */ - for (first_reg = 13; first_reg <= 31; first_reg++) - if (regs_ever_live[first_reg]) - break; - - /* If profiling, then we must save/restore every register that contains - a parameter before/after the .__mcount call. Use registers from 30 down - to 23 to do this. Don't use the frame pointer in reg 31. - - For now, save enough room for all of the parameter registers. */ - if (DEFAULT_ABI == ABI_AIX && profile_flag) - if (first_reg > 23) - first_reg = 23; - - return first_reg; -} - -/* Similar, for FP regs. */ - -int -first_fp_reg_to_save () -{ - int first_reg; - - /* Find lowest numbered live register. */ - for (first_reg = 14 + 32; first_reg <= 63; first_reg++) - if (regs_ever_live[first_reg]) - break; - - return first_reg; -} - -/* Return non-zero if this function makes calls. */ - -int -rs6000_makes_calls () -{ - rtx insn; - - /* If we are profiling, we will be making a call to __mcount. - Under the System V ABI's, we store the LR directly, so - we don't need to do it here. */ - if (DEFAULT_ABI == ABI_AIX && profile_flag) - return 1; - - for (insn = get_insns (); insn; insn = next_insn (insn)) - if (GET_CODE (insn) == CALL_INSN) - return 1; - - return 0; -} - - -/* 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: - 32-bit 64-bit - SP----> +---------------------------------------+ - | back chain to caller | 0 0 - +---------------------------------------+ - | saved CR | 4 8 (8-11) - +---------------------------------------+ - | saved LR | 8 16 - +---------------------------------------+ - | reserved for compilers | 12 24 - +---------------------------------------+ - | reserved for binders | 16 32 - +---------------------------------------+ - | saved TOC pointer | 20 40 - +---------------------------------------+ - | Parameter save area (P) | 24 48 - +---------------------------------------+ - | Alloca space (A) | 24+P etc. - +---------------------------------------+ - | Local variable space (L) | 24+P+A - +---------------------------------------+ - | Float/int conversion temporary (X) | 24+P+A+L - +---------------------------------------+ - | Save area for GP registers (G) | 24+P+A+X+L - +---------------------------------------+ - | Save area for FP registers (F) | 24+P+A+X+L+G - +---------------------------------------+ - old SP->| back chain to caller's caller | - +---------------------------------------+ - - The required alignment for AIX configurations is two words (i.e., 8 - or 16 bytes). - - - V.4 stack frames look like: - - SP----> +---------------------------------------+ - | back chain to caller | 0 - +---------------------------------------+ - | caller's saved LR | 4 - +---------------------------------------+ - | Parameter save area (P) | 8 - +---------------------------------------+ - | Alloca space (A) | 8+P - +---------------------------------------+ - | Varargs save area (V) | 8+P+A - +---------------------------------------+ - | Local variable space (L) | 8+P+A+V - +---------------------------------------+ - | Float/int conversion temporary (X) | 8+P+A+V+L - +---------------------------------------+ - | saved CR (C) | 8+P+A+V+L+X - +---------------------------------------+ - | Save area for GP registers (G) | 8+P+A+V+L+X+C - +---------------------------------------+ - | Save area for FP registers (F) | 8+P+A+V+L+X+C+G - +---------------------------------------+ - old SP->| back chain to caller's caller | - +---------------------------------------+ - - The required alignment for V.4 is 16 bytes, or 8 bytes if -meabi is - given. (But note below and in sysv4.h that we require only 8 and - may round up the size of our stack frame anyways. The historical - reason is early versions of powerpc-linux which didn't properly - align the stack at program startup. A happy side-effect is that - -mno-eabi libraries can be used with -meabi programs.) - - - A PowerPC Windows/NT frame looks like: - - SP----> +---------------------------------------+ - | back chain to caller | 0 - +---------------------------------------+ - | reserved | 4 - +---------------------------------------+ - | reserved | 8 - +---------------------------------------+ - | reserved | 12 - +---------------------------------------+ - | reserved | 16 - +---------------------------------------+ - | reserved | 20 - +---------------------------------------+ - | Parameter save area (P) | 24 - +---------------------------------------+ - | Alloca space (A) | 24+P - +---------------------------------------+ - | Local variable space (L) | 24+P+A - +---------------------------------------+ - | Float/int conversion temporary (X) | 24+P+A+L - +---------------------------------------+ - | Save area for FP registers (F) | 24+P+A+L+X - +---------------------------------------+ - | Possible alignment area (Y) | 24+P+A+L+X+F - +---------------------------------------+ - | Save area for GP registers (G) | 24+P+A+L+X+F+Y - +---------------------------------------+ - | Save area for CR (C) | 24+P+A+L+X+F+Y+G - +---------------------------------------+ - | Save area for TOC (T) | 24+P+A+L+X+F+Y+G+C - +---------------------------------------+ - | Save area for LR (R) | 24+P+A+L+X+F+Y+G+C+T - +---------------------------------------+ - old SP->| back chain to caller's caller | - +---------------------------------------+ - - For NT, there is no specific order to save the registers, but in - order to support __builtin_return_address, the save area for the - link register needs to be in a known place, so we use -4 off of the - old SP. To support calls through pointers, we also allocate a - fixed slot to store the TOC, -8 off the old SP. - - The required alignment for NT is 16 bytes. - - - The EABI configuration defaults to the V.4 layout, unless - -mcall-aix is used, in which case the AIX layout is used. However, - the stack alignment requirements may differ. If -mno-eabi is not - given, the required stack alignment is 8 bytes; if -mno-eabi is - given, the required alignment is 16 bytes. (But see V.4 comment - above.) */ - -#ifndef ABI_STACK_BOUNDARY -#define ABI_STACK_BOUNDARY STACK_BOUNDARY -#endif - -rs6000_stack_t * -rs6000_stack_info () -{ - static rs6000_stack_t info, zero_info; - rs6000_stack_t *info_ptr = &info; - int reg_size = TARGET_32BIT ? 4 : 8; - enum rs6000_abi abi; - int total_raw_size; - - /* Zero all fields portably */ - info = zero_info; - - /* Select which calling sequence */ - info_ptr->abi = abi = DEFAULT_ABI; - - /* Calculate which registers need to be saved & save area size */ - info_ptr->first_gp_reg_save = first_reg_to_save (); - info_ptr->gp_size = reg_size * (32 - info_ptr->first_gp_reg_save); - - info_ptr->first_fp_reg_save = first_fp_reg_to_save (); - info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save); - - /* Does this function call anything? */ - info_ptr->calls_p = rs6000_makes_calls (); - - /* Allocate space to save the toc. */ - if (abi == ABI_NT && info_ptr->calls_p) - { - info_ptr->toc_save_p = 1; - info_ptr->toc_size = reg_size; - } - - /* Does this machine need the float/int conversion area? */ - info_ptr->fpmem_p = regs_ever_live[FPMEM_REGNUM]; - - /* If this is main and we need to call a function to set things up, - save main's arguments around the call. */ -#ifdef TARGET_EABI - if (TARGET_EABI) -#endif - { - if (strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), "main") == 0 - && DECL_CONTEXT (current_function_decl) == NULL_TREE) - { - info_ptr->main_p = 1; - -#ifdef NAME__MAIN - info_ptr->calls_p = 1; - - if (DECL_ARGUMENTS (current_function_decl)) - { - int i; - tree arg; - - info_ptr->main_save_p = 1; - info_ptr->main_size = 0; - - for ((i = 0), (arg = DECL_ARGUMENTS (current_function_decl)); - arg != NULL_TREE && i < 8; - (arg = TREE_CHAIN (arg)), i++) - { - info_ptr->main_size += reg_size; - } - } -#endif - } - } - - /* Determine if we need to save the link register */ - if (regs_ever_live[65] - || (DEFAULT_ABI == ABI_AIX && profile_flag) -#ifdef TARGET_RELOCATABLE - || (TARGET_RELOCATABLE && (get_pool_size () != 0)) -#endif - || (info_ptr->first_fp_reg_save != 64 - && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save)) - || (abi == ABI_V4 && current_function_calls_alloca) - || (abi == ABI_SOLARIS && current_function_calls_alloca) - || info_ptr->calls_p) - { - info_ptr->lr_save_p = 1; - regs_ever_live[65] = 1; - if (abi == ABI_NT) - info_ptr->lr_size = reg_size; - } - - /* Determine if we need to save the condition code registers */ - if (regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72]) - { - info_ptr->cr_save_p = 1; - if (abi == ABI_V4 || abi == ABI_NT || abi == ABI_SOLARIS) - 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 = RS6000_ALIGN (get_frame_size (), 8); - info_ptr->parm_size = RS6000_ALIGN (current_function_outgoing_args_size, 8); - info_ptr->fpmem_size = (info_ptr->fpmem_p) ? 8 : 0; - info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size - + info_ptr->gp_size - + info_ptr->cr_size - + info_ptr->lr_size - + info_ptr->toc_size - + info_ptr->main_size, 8); - - /* Calculate the offsets */ - switch (abi) - { - case ABI_NONE: - default: - abort (); - - case ABI_AIX: - case ABI_AIX_NODESC: - info_ptr->fp_save_offset = - info_ptr->fp_size; - info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; - info_ptr->main_save_offset = info_ptr->gp_save_offset - info_ptr->main_size; - info_ptr->cr_save_offset = reg_size; /* first word when 64-bit. */ - info_ptr->lr_save_offset = 2*reg_size; - break; - - case ABI_V4: - case ABI_SOLARIS: - info_ptr->fp_save_offset = - info_ptr->fp_size; - info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; - info_ptr->cr_save_offset = info_ptr->gp_save_offset - info_ptr->cr_size; - info_ptr->toc_save_offset = info_ptr->cr_save_offset - info_ptr->toc_size; - info_ptr->main_save_offset = info_ptr->toc_save_offset - info_ptr->main_size; - info_ptr->lr_save_offset = reg_size; - break; - - case ABI_NT: - info_ptr->lr_save_offset = -reg_size; - info_ptr->toc_save_offset = info_ptr->lr_save_offset - info_ptr->lr_size; - info_ptr->cr_save_offset = info_ptr->toc_save_offset - info_ptr->toc_size; - info_ptr->gp_save_offset = info_ptr->cr_save_offset - info_ptr->cr_size - info_ptr->gp_size + reg_size; - info_ptr->fp_save_offset = info_ptr->gp_save_offset - info_ptr->fp_size; - if (info_ptr->fp_size && ((- info_ptr->fp_save_offset) % 8) != 0) - info_ptr->fp_save_offset -= reg_size; - - info_ptr->main_save_offset = info_ptr->fp_save_offset - info_ptr->main_size; - break; - } - - /* Ensure that fpmem_offset will be aligned to an 8-byte boundary. */ - if (info_ptr->fpmem_p - && (info_ptr->main_save_offset - info_ptr->fpmem_size) % 8) - info_ptr->fpmem_size += reg_size; - - total_raw_size = (info_ptr->vars_size - + info_ptr->parm_size - + info_ptr->fpmem_size - + info_ptr->save_size - + info_ptr->varargs_size - + info_ptr->fixed_size); - - info_ptr->total_size = RS6000_ALIGN (total_raw_size, ABI_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 we - make calls, or if the sum of fp_save, gp_save, fpmem, and local variables - are more than the space needed to save all non-volatile registers: - 32-bit: 18*8 + 19*4 = 220 or 64-bit: 18*8 + 19*8 = 296 - - For V.4 we don't have the stack cushion that AIX uses, but assume that - the debugger can handle stackless frames. */ - - if (info_ptr->calls_p) - info_ptr->push_p = 1; - - else if (abi == ABI_V4 || abi == ABI_NT || abi == ABI_SOLARIS) - info_ptr->push_p = (total_raw_size > info_ptr->fixed_size - || (abi == ABI_NT ? info_ptr->lr_save_p - : info_ptr->calls_p)); - - else - info_ptr->push_p = (frame_pointer_needed - || write_symbols != NO_DEBUG - || ((total_raw_size - info_ptr->fixed_size) - > (TARGET_32BIT ? 220 : 296))); - - if (info_ptr->fpmem_p) - { - info_ptr->fpmem_offset = info_ptr->main_save_offset - info_ptr->fpmem_size; - rs6000_fpmem_size = info_ptr->fpmem_size; - rs6000_fpmem_offset = (info_ptr->push_p - ? info_ptr->total_size + info_ptr->fpmem_offset - : info_ptr->fpmem_offset); - } - else - info_ptr->fpmem_offset = 0; - - /* Zero offsets if we're not saving those registers */ - if (info_ptr->fp_size == 0) - info_ptr->fp_save_offset = 0; - - if (info_ptr->gp_size == 0) - info_ptr->gp_save_offset = 0; - - if (!info_ptr->lr_save_p) - info_ptr->lr_save_offset = 0; - - if (!info_ptr->cr_save_p) - info_ptr->cr_save_offset = 0; - - if (!info_ptr->toc_save_p) - info_ptr->toc_save_offset = 0; - - if (!info_ptr->main_save_p) - info_ptr->main_save_offset = 0; - - return info_ptr; -} - -void -debug_stack_info (info) - rs6000_stack_t *info; -{ - char *abi_string; - - if (!info) - info = rs6000_stack_info (); - - fprintf (stderr, "\nStack information for function %s:\n", - ((current_function_decl && DECL_NAME (current_function_decl)) - ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl)) - : "<unknown>")); - - switch (info->abi) - { - default: abi_string = "Unknown"; break; - case ABI_NONE: abi_string = "NONE"; break; - case ABI_AIX: abi_string = "AIX"; break; - case ABI_AIX_NODESC: abi_string = "AIX"; break; - case ABI_V4: abi_string = "V.4"; break; - case ABI_SOLARIS: abi_string = "Solaris"; break; - case ABI_NT: abi_string = "NT"; break; - } - - fprintf (stderr, "\tABI = %5s\n", abi_string); - - if (info->first_gp_reg_save != 32) - fprintf (stderr, "\tfirst_gp_reg_save = %5d\n", info->first_gp_reg_save); - - if (info->first_fp_reg_save != 64) - fprintf (stderr, "\tfirst_fp_reg_save = %5d\n", info->first_fp_reg_save); - - if (info->lr_save_p) - fprintf (stderr, "\tlr_save_p = %5d\n", info->lr_save_p); - - if (info->cr_save_p) - fprintf (stderr, "\tcr_save_p = %5d\n", info->cr_save_p); - - if (info->toc_save_p) - fprintf (stderr, "\ttoc_save_p = %5d\n", info->toc_save_p); - - if (info->push_p) - fprintf (stderr, "\tpush_p = %5d\n", info->push_p); - - if (info->calls_p) - fprintf (stderr, "\tcalls_p = %5d\n", info->calls_p); - - if (info->main_p) - fprintf (stderr, "\tmain_p = %5d\n", info->main_p); - - if (info->main_save_p) - fprintf (stderr, "\tmain_save_p = %5d\n", info->main_save_p); - - if (info->fpmem_p) - fprintf (stderr, "\tfpmem_p = %5d\n", info->fpmem_p); - - if (info->gp_save_offset) - fprintf (stderr, "\tgp_save_offset = %5d\n", info->gp_save_offset); - - if (info->fp_save_offset) - fprintf (stderr, "\tfp_save_offset = %5d\n", info->fp_save_offset); - - if (info->lr_save_offset) - fprintf (stderr, "\tlr_save_offset = %5d\n", info->lr_save_offset); - - if (info->cr_save_offset) - fprintf (stderr, "\tcr_save_offset = %5d\n", info->cr_save_offset); - - if (info->toc_save_offset) - fprintf (stderr, "\ttoc_save_offset = %5d\n", info->toc_save_offset); - - if (info->varargs_save_offset) - fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset); - - if (info->main_save_offset) - fprintf (stderr, "\tmain_save_offset = %5d\n", info->main_save_offset); - - if (info->fpmem_offset) - fprintf (stderr, "\tfpmem_offset = %5d\n", info->fpmem_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->fpmem_size) - fprintf (stderr, "\tfpmem_size = %5d\n", info->fpmem_size); - - if (info->fixed_size) - fprintf (stderr, "\tfixed_size = %5d\n", info->fixed_size); - - if (info->gp_size) - fprintf (stderr, "\tgp_size = %5d\n", info->gp_size); - - if (info->fp_size) - fprintf (stderr, "\tfp_size = %5d\n", info->fp_size); - - if (info->lr_size) - fprintf (stderr, "\tlr_size = %5d\n", info->cr_size); - - if (info->cr_size) - fprintf (stderr, "\tcr_size = %5d\n", info->cr_size); - - if (info->toc_size) - fprintf (stderr, "\ttoc_size = %5d\n", info->toc_size); - - if (info->main_size) - fprintf (stderr, "\tmain_size = %5d\n", info->main_size); - - if (info->save_size) - fprintf (stderr, "\tsave_size = %5d\n", info->save_size); - - if (info->reg_size != 4) - fprintf (stderr, "\treg_size = %5d\n", info->reg_size); - - fprintf (stderr, "\n"); -} - - -/* CYGNUS LOCAL -- vmakarov/prolog-epilog instruction scheduling */ -#if 0 -/* END CYGNUS LOCAL */ - -/* Write out an instruction to load the TOC_TABLE address into register 30. - This is only needed when TARGET_TOC, TARGET_MINIMAL_TOC, and there is - a constant pool. */ - -void -rs6000_output_load_toc_table (file, reg) - FILE *file; - int reg; -{ - char buf[256]; - -#ifdef USING_SVR4_H - if (TARGET_RELOCATABLE) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "\tbl "); - assemble_name (file, buf); - fprintf (file, "\n"); - - /* possibly create the toc section */ - if (!toc_initialized) - { - toc_section (); - function_section (current_function_decl); - } - - /* If not first call in this function, we need to put the - different between .LCTOC1 and the address we get to right - after the bl. It will mess up disassembling the instructions - but that can't be helped. We will later need to bias the - address before loading. */ - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - { - char *init_ptr = TARGET_32BIT ? ".long" : ".quad"; - char *buf_ptr; - - ASM_OUTPUT_INTERNAL_LABEL (file, "LCL", rs6000_pic_labelno); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - STRIP_NAME_ENCODING (buf_ptr, buf); - fprintf (file, "\t%s %s-", init_ptr, buf_ptr); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "%s\n", buf_ptr); - } - - ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno); - fprintf (file, "\tmflr %s\n", reg_names[reg]); - - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - asm_fprintf(file, "\t{cal|la} %s,%d(%s)\n", reg_names[reg], - (TARGET_32BIT ? 4 : 8), reg_names[reg]); - - asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz} %s,(" : "\tld %s,(", - reg_names[0]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); - assemble_name (file, buf); - fputs ("-", file); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - assemble_name (file, buf); - fprintf (file, ")(%s)\n", reg_names[reg]); - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", - reg_names[reg], reg_names[0], reg_names[reg]); - rs6000_pic_labelno++; - } - else if (!TARGET_64BIT) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - asm_fprintf (file, "\t{liu|lis} %s,", reg_names[reg]); - assemble_name (file, buf); - fputs ("@ha\n", file); - asm_fprintf (file, "\t{cal|la} %s,", reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "@l(%s)\n", reg_names[reg]); - } - else - abort (); - -#else /* !USING_SVR4_H */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0); - asm_fprintf (file, TARGET_32BIT ? "\t{l|lwz} %s," : "\tld %s,", - reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", reg_names[2]); -#endif /* USING_SVR4_H */ -} - - -/* Emit the correct code for allocating stack space. If COPY_R12, make sure a copy - of the old frame is left in r12. */ - -void -rs6000_allocate_stack_space (file, size, copy_r12) - FILE *file; - int size; - int copy_r12; -{ - int neg_size = -size; - if (TARGET_UPDATE) - { - if (size < 32767) - asm_fprintf (file, - (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); - else - { - if (copy_r12) - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - - asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, - (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n", - reg_names[1], reg_names[1], reg_names[0]); - } - } - else - { - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - if (size < 32767) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); - else - { - asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", reg_names[1], - reg_names[0], reg_names[1]); - } - - asm_fprintf (file, - (TARGET_32BIT) ? "\t{st|stw} %s,0(%s)\n" : "\tstd %s,0(%s)\n", - reg_names[12], reg_names[1]); - } -} - - -/* Write function prologue. */ -void -output_prolog (file, size) - FILE *file; - int size ATTRIBUTE_UNUSED; -{ - rs6000_stack_t *info = rs6000_stack_info (); - int reg_size = info->reg_size; - char *store_reg; - char *load_reg; - int sp_reg = 1; - int sp_offset = 0; - - if (TARGET_32BIT) - { - store_reg = "\t{st|stw} %s,%d(%s)\n"; - load_reg = "\t{l|lwz} %s,%d(%s)\n"; - } - else - { - store_reg = "\tstd %s,%d(%s)\n"; - load_reg = "\tlld %s,%d(%s)\n"; - } - - if (TARGET_DEBUG_STACK) - debug_stack_info (info); - - /* Write .extern for any function we will call to save and restore fp - values. */ - if (info->first_fp_reg_save < 64 && !FP_SAVE_INLINE (info->first_fp_reg_save)) - fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n", - SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX, - RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); - - /* Write .extern for truncation routines, if needed. */ - if (rs6000_trunc_used && ! trunc_defined) - { - fprintf (file, "\t.extern .%s\n\t.extern .%s\n", - RS6000_ITRUNC, RS6000_UITRUNC); - trunc_defined = 1; - } - - /* Write .extern for AIX common mode routines, if needed. */ - if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined) - { - fputs ("\t.extern __mulh\n", file); - fputs ("\t.extern __mull\n", file); - fputs ("\t.extern __divss\n", file); - fputs ("\t.extern __divus\n", file); - fputs ("\t.extern __quoss\n", file); - fputs ("\t.extern __quous\n", file); - common_mode_defined = 1; - } - - /* For V.4, update stack before we do any saving and set back pointer. */ - if (info->push_p && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - { - if (info->total_size < 32767) - sp_offset = info->total_size; - else - sp_reg = 12; - rs6000_allocate_stack_space (file, info->total_size, sp_reg == 12); - } - - /* If we use the link register, get it into r0. */ - 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 (info->cr_save_p && sp_reg != 12) - asm_fprintf (file, "\tmfcr %s\n", reg_names[12]); - - /* Do any required saving of fpr's. If only one or two to save, do it - ourself. Otherwise, call function. Note that since they are statically - linked, we do not need a nop following them. */ - if (FP_SAVE_INLINE (info->first_fp_reg_save)) - { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; - - for ( ; regno < 64; regno++, loc += 8) - asm_fprintf (file, "\tstfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]); - } - else if (info->first_fp_reg_save != 64) - asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX, - info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); - - /* Now save gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) - { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - - for ( ; regno < 32; regno++, loc += reg_size) - asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]); - } - - else if (info->first_gp_reg_save != 32) - asm_fprintf (file, "\t{stm|stmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Save main's arguments if we need to call a function */ -#ifdef NAME__MAIN - if (info->main_save_p) - { - int regno; - int loc = info->main_save_offset + sp_offset; - int size = info->main_size; - - for (regno = 3; size > 0; regno++, loc += reg_size, size -= reg_size) - asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]); - } -#endif - - /* Save lr if we used it. */ - if (info->lr_save_p) - asm_fprintf (file, store_reg, reg_names[0], info->lr_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Save CR if we use any that must be preserved. */ - if (info->cr_save_p) - { - if (sp_reg == 12) /* If r12 is used to hold the original sp, copy cr now */ - { - asm_fprintf (file, "\tmfcr %s\n", reg_names[0]); - asm_fprintf (file, store_reg, reg_names[0], - info->cr_save_offset + sp_offset, - reg_names[sp_reg]); - } - else - asm_fprintf (file, store_reg, reg_names[12], info->cr_save_offset + sp_offset, - reg_names[sp_reg]); - } - - /* NT needs us to probe the stack frame every 4k pages for large frames, so - do it here. */ - if (DEFAULT_ABI == ABI_NT && info->total_size > 4096) - { - if (info->total_size < 32768) - { - int probe_offset = 4096; - while (probe_offset < info->total_size) - { - asm_fprintf (file, "\t{l|lwz} %s,%d(%s)\n", reg_names[0], -probe_offset, reg_names[1]); - probe_offset += 4096; - } - } - else - { - int probe_iterations = info->total_size / 4096; - static int probe_labelno = 0; - char buf[256]; - - if (probe_iterations < 32768) - asm_fprintf (file, "\tli %s,%d\n", reg_names[12], probe_iterations); - else - { - asm_fprintf (file, "\tlis %s,%d\n", reg_names[12], probe_iterations >> 16); - if (probe_iterations & 0xffff) - asm_fprintf (file, "\tori %s,%s,%d\n", reg_names[12], reg_names[12], - probe_iterations & 0xffff); - } - asm_fprintf (file, "\tmtctr %s\n", reg_names[12]); - asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - ASM_OUTPUT_INTERNAL_LABEL (file, "LCprobe", probe_labelno); - asm_fprintf (file, "\t{lu|lwzu} %s,-4096(%s)\n", reg_names[0], reg_names[12]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCprobe", probe_labelno++); - fputs ("\tbdnz ", file); - assemble_name (file, buf); - fputs ("\n", file); - } - } - - /* Update stack and set back pointer unless this is V.4, which was done previously */ - if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - rs6000_allocate_stack_space (file, info->total_size, FALSE); - - /* Set frame pointer, if needed. */ - if (frame_pointer_needed) - asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]); - -#ifdef NAME__MAIN - /* If we need to call a function to set things up for main, do so now - before dealing with the TOC. */ - if (info->main_p) - { - char *prefix = ""; - - switch (DEFAULT_ABI) - { - case ABI_AIX: prefix = "."; break; - case ABI_NT: prefix = ".."; break; - } - - fprintf (file, "\tbl %s%s\n", prefix, NAME__MAIN); -#ifdef RS6000_CALL_GLUE2 - fprintf (file, "\t%s%s%s\n", RS6000_CALL_GLUE2, prefix, NAME_MAIN); -#else -#ifdef RS6000_CALL_GLUE - if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT) - fprintf (file, "\t%s\n", RS6000_CALL_GLUE); -#endif -#endif - - if (info->main_save_p) - { - int regno; - int loc; - int size = info->main_size; - - if (info->total_size < 32767) - { - loc = info->total_size + info->main_save_offset; - for (regno = 3; size > 0; regno++, size -= reg_size, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[1]); - } - else - { - int neg_size = info->main_save_offset - info->total_size; - loc = 0; - asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - - asm_fprintf (file, "\t{sf|subf} %s,%s,%s\n", reg_names[0], reg_names[0], - reg_names[1]); - - for (regno = 3; size > 0; regno++, size -= reg_size, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[0]); - } - } - } -#endif - - - /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the - TOC_TABLE address into register 30. */ - if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) - { -#ifdef USING_SVR4_H - if (!profile_flag) - rs6000_pic_func_labelno = rs6000_pic_labelno; -#endif - rs6000_output_load_toc_table (file, 30); - } - - if (DEFAULT_ABI == ABI_NT) - { - assemble_name (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".b:\n", file); - } -} - -/* Write function epilogue. */ - -void -output_epilog (file, size) - FILE *file; - int size ATTRIBUTE_UNUSED; -{ - rs6000_stack_t *info = rs6000_stack_info (); - char *load_reg = (TARGET_32BIT) ? "\t{l|lwz} %s,%d(%s)\n" : "\tld %s,%d(%s)\n"; - rtx insn = get_last_insn (); - int sp_reg = 1; - int sp_offset = 0; - - /* If the last insn was a BARRIER, we don't have to write anything except - the trace table. */ - if (GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn == 0 || GET_CODE (insn) != BARRIER) - { - /* If we have a frame pointer, a call to alloca, or a large stack - 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 - || info->total_size > 32767) - { - /* Under V.4, don't reset the stack pointer until after we're done - loading the saved registers. */ - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - sp_reg = 11; - - asm_fprintf (file, load_reg, reg_names[sp_reg], 0, reg_names[1]); - } - else if (info->push_p) - { - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - sp_offset = info->total_size; - else - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], info->total_size, reg_names[1]); - } - - /* Get the old lr if we saved it. */ - if (info->lr_save_p) - asm_fprintf (file, load_reg, reg_names[0], info->lr_save_offset + sp_offset, reg_names[sp_reg]); - - /* Get the old cr if we saved it. */ - if (info->cr_save_p) - asm_fprintf (file, load_reg, reg_names[12], info->cr_save_offset + sp_offset, reg_names[sp_reg]); - - /* Set LR here to try to overlap restores below. */ - if (info->lr_save_p) - asm_fprintf (file, "\tmtlr %s\n", reg_names[0]); - - /* Restore gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) - { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - int reg_size = (TARGET_32BIT) ? 4 : 8; - - for ( ; regno < 32; regno++, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[sp_reg]); - } - - else if (info->first_gp_reg_save != 32) - asm_fprintf (file, "\t{lm|lmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Restore fpr's if we can do it without calling a function. */ - if (FP_SAVE_INLINE (info->first_fp_reg_save)) - { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; - - for ( ; regno < 64; regno++, loc += 8) - asm_fprintf (file, "\tlfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]); - } - - /* If we saved cr, restore it here. Just those of cr2, cr3, and cr4 - that were used. */ - if (info->cr_save_p) - asm_fprintf (file, "\tmtcrf %d,%s\n", - (regs_ever_live[70] != 0) * 0x20 - + (regs_ever_live[71] != 0) * 0x10 - + (regs_ever_live[72] != 0) * 0x8, reg_names[12]); - - /* If this is V.4, unwind the stack pointer after all of the loads - have been done */ - if (sp_offset != 0) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], sp_offset, reg_names[1]); - else if (sp_reg != 1) - asm_fprintf (file, "\tmr %s,%s\n", reg_names[1], reg_names[sp_reg]); - - /* If we have to restore more than two FP registers, branch to the - restore function. It will return to our caller. */ - if (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"); - } - - /* Output a traceback table here. See /usr/include/sys/debug.h for info - on its format. - - We don't output a traceback table if -finhibit-size-directive was - used. The documentation for -finhibit-size-directive reads - ``don't output a @code{.size} assembler directive, or anything - else that would cause trouble if the function is split in the - middle, and the two halves are placed at locations far apart in - memory.'' The traceback table has this property, since it - includes the offset from the start of the function to the - traceback table itself. - - System V.4 Powerpc's (and the embedded ABI derived from it) use a - different traceback table. */ - if (DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive) - { - char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); - int fixed_parms, float_parms, parm_info; - int i; - - while (*fname == '.') /* V.4 encodes . in the name */ - fname++; - - /* Need label immediately before tbtab, so we can compute its offset - from the function start. */ - if (*fname == '*') - ++fname; - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); - ASM_OUTPUT_LABEL (file, fname); - - /* The .tbtab pseudo-op can only be used for the first eight - expressions, since it can't handle the possibly variable - length fields that follow. However, if you omit the optional - fields, the assembler outputs zeros for all optional fields - anyways, giving each variable length field is minimum length - (as defined in sys/debug.h). Thus we can not use the .tbtab - pseudo-op at all. */ - - /* An all-zero word flags the start of the tbtab, for debuggers - that have to find it by searching forward from the entry - point or from the current pc. */ - fputs ("\t.long 0\n", file); - - /* Tbtab format type. Use format type 0. */ - fputs ("\t.byte 0,", file); - - /* Language type. Unfortunately, there doesn't seem to be any - official way to get this info, so we use language_string. C - is 0. C++ is 9. No number defined for Obj-C, so use the - value for C for now. There is no official value for Java, - although IBM appears to be using 13. There is no official value - for Chill, so we've choosen 44 pseudo-randomly. */ - if (! strcmp (language_string, "GNU C") - || ! strcmp (language_string, "GNU Obj-C")) - i = 0; - else if (! strcmp (language_string, "GNU F77")) - i = 1; - else if (! strcmp (language_string, "GNU Ada")) - i = 3; - else if (! strcmp (language_string, "GNU Pascal")) - i = 2; - else if (! strcmp (language_string, "GNU C++")) - i = 9; - else if (! strcmp (language_string, "GNU Java")) - i = 13; - else if (! strcmp (language_string, "GNU CHILL")) - i = 44; - else - abort (); - fprintf (file, "%d,", i); - - /* 8 single bit fields: global linkage (not set for C extern linkage, - apparently a PL/I convention?), out-of-line epilogue/prologue, offset - from start of procedure stored in tbtab, internal function, function - has controlled storage, function has no toc, function uses fp, - function logs/aborts fp operations. */ - /* Assume that fp operations are used if any fp reg must be saved. */ - fprintf (file, "%d,", (1 << 5) | ((info->first_fp_reg_save != 64) << 1)); - - /* 6 bitfields: function is interrupt handler, name present in - proc table, function calls alloca, on condition directives - (controls stack walks, 3 bits), saves condition reg, saves - link reg. */ - /* The `function calls alloca' bit seems to be set whenever reg 31 is - set up as a frame pointer, even when there is no alloca call. */ - fprintf (file, "%d,", - ((1 << 6) | (frame_pointer_needed << 5) - | (info->cr_save_p << 1) | (info->lr_save_p))); - - /* 3 bitfields: saves backchain, spare bit, number of fpr saved - (6 bits). */ - fprintf (file, "%d,", - (info->push_p << 7) | (64 - info->first_fp_reg_save)); - - /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits). */ - fprintf (file, "%d,", (32 - first_reg_to_save ())); - - { - /* Compute the parameter info from the function decl argument - list. */ - tree decl; - int next_parm_info_bit; - - next_parm_info_bit = 31; - parm_info = 0; - fixed_parms = 0; - float_parms = 0; - - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - { - rtx parameter = DECL_INCOMING_RTL (decl); - enum machine_mode mode = GET_MODE (parameter); - - if (GET_CODE (parameter) == REG) - { - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - int bits; - - float_parms++; - - if (mode == SFmode) - bits = 0x2; - else if (mode == DFmode) - bits = 0x3; - else - abort (); - - /* If only one bit will fit, don't or in this entry. */ - if (next_parm_info_bit > 0) - parm_info |= (bits << (next_parm_info_bit - 1)); - next_parm_info_bit -= 2; - } - else - { - fixed_parms += ((GET_MODE_SIZE (mode) - + (UNITS_PER_WORD - 1)) - / UNITS_PER_WORD); - next_parm_info_bit -= 1; - } - } - } - } - - /* Number of fixed point parameters. */ - /* This is actually the number of words of fixed point parameters; thus - an 8 byte struct counts as 2; and thus the maximum value is 8. */ - fprintf (file, "%d,", fixed_parms); - - /* 2 bitfields: number of floating point parameters (7 bits), parameters - all on stack. */ - /* This is actually the number of fp registers that hold parameters; - and thus the maximum value is 13. */ - /* Set parameters on stack bit if parameters are not in their original - registers, regardless of whether they are on the stack? Xlc - seems to set the bit when not optimizing. */ - fprintf (file, "%d\n", ((float_parms << 1) | (! optimize))); - - /* Optional fields follow. Some are variable length. */ - - /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float, - 11 double float. */ - /* There is an entry for each parameter in a register, in the order that - they occur in the parameter list. Any intervening arguments on the - stack are ignored. If the list overflows a long (max possible length - 34 bits) then completely leave off all elements that don't fit. */ - /* Only emit this long if there was at least one parameter. */ - if (fixed_parms || float_parms) - fprintf (file, "\t.long %d\n", parm_info); - - /* Offset from start of code to tb table. */ - fputs ("\t.long ", file); - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); - RS6000_OUTPUT_BASENAME (file, fname); - fputs ("-.", file); - RS6000_OUTPUT_BASENAME (file, fname); - putc ('\n', file); - - /* Interrupt handler mask. */ - /* Omit this long, since we never set the interrupt handler bit - above. */ - - /* Number of CTL (controlled storage) anchors. */ - /* Omit this long, since the has_ctl bit is never set above. */ - - /* Displacement into stack of each CTL anchor. */ - /* Omit this list of longs, because there are no CTL anchors. */ - - /* Length of function name. */ - fprintf (file, "\t.short %d\n", (int) strlen (fname)); - - /* Function name. */ - assemble_string (fname, strlen (fname)); - - /* Register for alloca automatic storage; this is always reg 31. - Only emit this if the alloca bit was set above. */ - if (frame_pointer_needed) - fputs ("\t.byte 31\n", file); - } - - if (DEFAULT_ABI == ABI_NT) - { - RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".e:\nFE_MOT_RESVD..", file); - RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (":\n", file); - } -} - -/* CYGNUS LOCAL -- vmakarov/prolog-epilog instruction scheduling */ -#endif - -/* This part (from CYGNUS LOCAL to CYGNUS local) contains code needed - for scheduling of prologue and epilogue. The part is based on the - analogous functions above. Now it works only for EABI. The code - contains new code for RTL generation and old code for assembler - output (for output prologue/epilogue for other ABI besides EABI). - So now this implementation is different from MIPS one. When RTL - generation for all other ABI is written, the assembler code output - (and code above between #if 0 and #endif) can be removed and the - implementation will become analogous to MIPS/ARM one. The code for - RTL generation for the following ABI must be finished and tested - for this (please pay attention onto 64 bit targets too): - - ABI_AIX, ABI_AIX_NODESC - ABI_NT, ABI_SOLARIS (did they die???) - -*/ - -/* Write out an assembler instruction (if !RTX_FLAG) otherwise RTL - insns to load the TOC_TABLE address into register 30. This is only - necessary when TARGET_TOC, TARGET_MINIMAL_TOC, and there is a - constant pool. */ - -void -rs6000_output_load_toc_table (file, reg, rtx_flag) - FILE *file; - int reg; - int rtx_flag; -{ - char buf[256]; - rtx insn; - enum machine_mode reg_mode = (TARGET_32BIT) ? SImode : DImode; - -#ifdef USING_SVR4_H - if (TARGET_RELOCATABLE) - { - if (rtx_flag) - { - if (reg != 30) - abort; - insn = emit_call_insn - (TARGET_32BIT - ? gen_loadsi_svr4_relocatable_toc (gen_rtx - (REG, reg_mode, 0)) - : gen_loaddi_svr4_relocatable_toc (gen_rtx - (REG, reg_mode, 0))); - RTX_FRAME_RELATED_P (insn) = 1; - return; - } - - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "\tbl "); - assemble_name (file, buf); - fprintf (file, "\n"); - - /* possibly create the toc section */ - if (!toc_initialized) - { - toc_section (); - function_section (current_function_decl); - } - - /* If not first call in this function, we need to put the - different between .LCTOC1 and the address we get to right - after the bl. It will mess up disassembling the instructions - but that can't be helped. We will later need to bias the - address before loading. */ - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - { - char *init_ptr = TARGET_32BIT ? ".long" : ".quad"; - char *buf_ptr; - - ASM_OUTPUT_INTERNAL_LABEL (file, "LCL", rs6000_pic_labelno); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - STRIP_NAME_ENCODING (buf_ptr, buf); - fprintf (file, "\t%s %s-", init_ptr, buf_ptr); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "%s\n", buf_ptr); - } - - ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno); - fprintf (file, "\tmflr %s\n", reg_names[reg]); - - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - asm_fprintf(file, "\t{cal|la} %s,%d(%s)\n", reg_names[reg], - (TARGET_32BIT ? 4 : 8), reg_names[reg]); - - asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz} %s,(" : "\tld %s,(", - reg_names[0]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); - assemble_name (file, buf); - fputs ("-", file); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - assemble_name (file, buf); - fprintf (file, ")(%s)\n", reg_names[reg]); - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", - reg_names[reg], reg_names[0], reg_names[reg]); - rs6000_pic_labelno++; - } - else if (!TARGET_64BIT) - { - if (rtx_flag) - { - if (reg != 30) - abort; - insn = emit_insn (gen_loadsi_svr4_toc ()); - RTX_FRAME_RELATED_P (insn) = 1; - return; - } - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - asm_fprintf (file, "\t{liu|lis} %s,", reg_names[reg]); - assemble_name (file, buf); - fputs ("@ha\n", file); - asm_fprintf (file, "\t{cal|la} %s,", reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "@l(%s)\n", reg_names[reg]); - } - else - abort (); - -#else /* !USING_SVR4_H */ - if (rtx_flag) - { - if (reg != 30) - abort (); - insn - = emit_insn (TARGET_32BIT - ? gen_loadsi_nonsvr4_toc (gen_rtx (REG, reg_mode, 2)) - : gen_loaddi_nonsvr4_toc (gen_rtx (REG, reg_mode, 2))); - RTX_FRAME_RELATED_P (insn) = 1; - return; - } - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0); - asm_fprintf (file, TARGET_32BIT ? "\t{l|lwz} %s," : "\tld %s,", - reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", reg_names[2]); -#endif /* USING_SVR4_H */ -} - - - -/* Emit the correct code for allocating stack space. If COPY_R12, - make sure a copy of the old frame is left in r12. We generate RTX - instead of assembler code when RTX_FLAG is true. */ -static void -rs6000_allocate_stack_space (file, size, copy_r12, rtx_flag) - FILE *file; - int size; - int copy_r12; - int rtx_flag; -{ - int neg_size = -size; - rtx insn; - enum machine_mode reg_mode = (TARGET_32BIT) ? SImode : DImode; - - if (TARGET_UPDATE) - { - if (size < 32767) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, - (TARGET_32BIT) - ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); - else if (rtx_flag) - { - rtx incr = GEN_INT (neg_size); - - if (TARGET_32BIT) - insn = emit_insn (gen_movsi_update (stack_pointer_rtx, - stack_pointer_rtx, incr, - stack_pointer_rtx)); - else - insn = emit_insn (gen_movdi_update (stack_pointer_rtx, - stack_pointer_rtx, incr, - stack_pointer_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (copy_r12) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - else if (rtx_flag) - { - rtx r12_rtx = gen_rtx (REG, Pmode, 12); - - insn = emit_move_insn (r12_rtx, stack_pointer_rtx); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, - (TARGET_32BIT) - ? "\t{stux|stwux} %s,%s,%s\n" - : "\tstdux %s,%s,%s\n", - reg_names[1], reg_names[1], reg_names[0]); - } - else if (rtx_flag) - { - rtx r0_rtx = gen_rtx (REG, reg_mode, 0); - rtx const_int_rtx; - - const_int_rtx = GEN_INT (((neg_size >> 16) & 0xffff) << 16); - insn = emit_insn (gen_rtx (SET, VOIDmode, - r0_rtx, const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - const_int_rtx = GEN_INT (neg_size & 0xffff); - insn = emit_insn (TARGET_32BIT - ? gen_iorsi3 (r0_rtx, r0_rtx, const_int_rtx) - : gen_iordi3 (r0_rtx, r0_rtx, const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (TARGET_32BIT - ? gen_movsi_update (stack_pointer_rtx, - stack_pointer_rtx, r0_rtx, - stack_pointer_rtx) - : gen_movdi_update (stack_pointer_rtx, - stack_pointer_rtx, r0_rtx, - stack_pointer_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - else - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - else if (rtx_flag) - { - rtx r12_rtx = gen_rtx (REG, Pmode, 12); - - insn = emit_move_insn (r12_rtx, stack_pointer_rtx); - RTX_FRAME_RELATED_P (insn) = 1; - } - if (size < 32767) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); - else if (rtx_flag) - { - rtx reg_rtx = gen_rtx (REG, reg_mode, 1); - - insn = emit_insn (gen_addsi3 (reg_rtx, reg_rtx, - GEN_INT (neg_size))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - asm_fprintf (file, - "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - } - else if (rtx_flag) - { - rtx r0_rtx = gen_rtx (REG, reg_mode, 0); - rtx const_int_rtx; - - const_int_rtx = GEN_INT (((neg_size >> 16) & 0xffff) << 16); - insn = emit_insn (gen_rtx (SET, VOIDmode, r0_rtx, - const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - const_int_rtx = GEN_INT (neg_size & 0xffff); - insn = emit_insn (TARGET_32BIT - ? gen_iorsi3 (r0_rtx, r0_rtx, const_int_rtx) - : gen_iordi3 (r0_rtx, r0_rtx, const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", reg_names[1], - reg_names[0], reg_names[1]); - else if (rtx_flag) - { - rtx r0_rtx = gen_rtx (REG, reg_mode, 0); - rtx r1_rtx = gen_rtx (REG, reg_mode, 1); - - insn = emit_insn (TARGET_32BIT - ? gen_addsi3 (r1_rtx, r0_rtx, r1_rtx) - : gen_adddi3 (r1_rtx, r0_rtx, r1_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, - (TARGET_32BIT) - ? "\t{st|stw} %s,0(%s)\n" : "\tstd %s,0(%s)\n", - reg_names[12], reg_names[1]); - else if (rtx_flag) - { - rtx r12_rtx = gen_rtx (REG, reg_mode, 12); - rtx r1_rtx = gen_rtx (REG, Pmode, 1); - - insn = emit_move_insn (gen_rtx (MEM, reg_mode, r1_rtx), r12_rtx); - RTX_FRAME_RELATED_P (insn) = 1; - } - } -} - - -/* Write function prologue. The function is called twice with two - different values of RTX_FLAG. Generate or not rtl or assembler - code is depended from value of TARGET_SCHED_PROLOG. */ - -static void -rs6000_prolog (file, rtx_flag, info) - FILE *file; - int rtx_flag; - rs6000_stack_t *info; -{ - int reg_size = info->reg_size; - char *store_reg; - char *load_reg; - int sp_reg = 1; - int sp_offset = 0; - enum machine_mode reg_mode; - rtx insn; - rtx sp_reg_rtx = stack_pointer_rtx; - - if (TARGET_32BIT) - { - store_reg = "\t{st|stw} %s,%d(%s)\n"; - load_reg = "\t{l|lwz} %s,%d(%s)\n"; - reg_mode = SImode; - } - else - { - store_reg = "\tstd %s,%d(%s)\n"; - load_reg = "\tlld %s,%d(%s)\n"; - reg_mode = DImode; - } - - if (TARGET_DEBUG_STACK) - debug_stack_info (info); - - /* Write .extern for any function we will call to save and restore fp - values. */ - if (!rtx_flag && info->first_fp_reg_save < 64 - && !FP_SAVE_INLINE (info->first_fp_reg_save)) - fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n", - SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX, - RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, - RESTORE_FP_SUFFIX); - - /* Write .extern for truncation routines, if needed. */ - if (!rtx_flag && rs6000_trunc_used && ! trunc_defined) - { - fprintf (file, "\t.extern .%s\n\t.extern .%s\n", - RS6000_ITRUNC, RS6000_UITRUNC); - trunc_defined = 1; - } - - /* Write .extern for AIX common mode routines, if needed. */ - if (!rtx_flag && !TARGET_POWER && !TARGET_POWERPC && !common_mode_defined) - { - fputs ("\t.extern __mulh\n", file); - fputs ("\t.extern __mull\n", file); - fputs ("\t.extern __divss\n", file); - fputs ("\t.extern __divus\n", file); - fputs ("\t.extern __quoss\n", file); - fputs ("\t.extern __quous\n", file); - common_mode_defined = 1; - } - - /* For V.4, update stack before we do any saving and set back pointer. */ - if (info->push_p && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - { - if (info->total_size < 32767) - sp_offset = info->total_size; - else - { - sp_reg = 12; - sp_reg_rtx = gen_rtx (REG, Pmode, 12); - } - rs6000_allocate_stack_space (file, info->total_size, sp_reg == 12, - rtx_flag); - } - - /* If we use the link register, get it into r0. */ - if (info->lr_save_p) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tmflr %s\n", reg_names[0]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, Pmode, 0), - gen_rtx (REG, Pmode, 65)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - /* If we need to save CR, put it into r12. */ - if (info->cr_save_p && sp_reg != 12) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tmfcr %s\n", reg_names[12]); - else if (rtx_flag) - { - insn = emit_insn (TARGET_32BIT - ? gen_movesi_from_cr (gen_rtx (REG, reg_mode, 12)) - : gen_movedi_from_cr (gen_rtx - (REG, reg_mode, 12))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* 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 (FP_SAVE_INLINE (info->first_fp_reg_save)) - { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; - - for ( ; regno < 64; regno++, loc += 8) - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tstfd %s,%d(%s)\n", - reg_names[regno], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx - (MEM, DFmode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, GEN_INT (loc))), - gen_rtx (REG, DFmode, regno)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else if (info->first_fp_reg_save != 64) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX, - info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); - else if (rtx_flag) - { - static char label [100]; - - sprintf (label, "%s%d%s", SAVE_FP_PREFIX, - info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); - insn = emit_jump_insn (gen_jump (gen_rtx (SYMBOL_REF, Pmode, - label))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Now save gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) - { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - - for ( ; regno < 32; regno++, loc += reg_size) - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, store_reg, - reg_names[regno], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, regno)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else if (info->first_gp_reg_save != 32) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\t{stm|stmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - else if (rtx_flag) - { - int loc; - int regno; - - for (loc = info->gp_save_offset + sp_offset, - regno = info->first_gp_reg_save; - regno <= 31; - regno++, loc += reg_size) - { - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, regno)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - /* Save main's arguments if we need to call a function */ -#ifdef NAME__MAIN - if (info->main_save_p) - { - int regno; - int loc = info->main_save_offset + sp_offset; - int size = info->main_size; - - for (regno = 3; size > 0; regno++, loc += reg_size, size -= reg_size) - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, store_reg, - reg_names[regno], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, regno)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } -#endif - - /* Save lr if we used it. */ - if (info->lr_save_p) - { - int loc = info->lr_save_offset + sp_offset; - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, store_reg, reg_names[0], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, 0)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Save CR if we use any that must be preserved. */ - if (info->cr_save_p) - { - int loc = info->cr_save_offset + sp_offset; - - if (sp_reg == 12) - /* If r12 is used to hold the original sp, copy cr now */ - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - asm_fprintf (file, "\tmfcr %s\n", reg_names[0]); - asm_fprintf (file, store_reg, reg_names[0], - loc, reg_names[sp_reg]); - } - else if (rtx_flag) - { - insn = emit_insn (TARGET_32BIT - ? gen_movesi_from_cr (gen_rtx - (REG, reg_mode, 0)) - : gen_movedi_from_cr (gen_rtx - (REG, reg_mode, 0))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, 0)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - - asm_fprintf (file, store_reg, reg_names[12], - loc, reg_names[sp_reg]); - - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (loc))), - gen_rtx (REG, reg_mode, 12)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - /* NT needs us to probe the stack frame every 4k pages for large frames, so - do it here. */ - if (DEFAULT_ABI == ABI_NT && info->total_size > 4096) - { - if (info->total_size < 32768) - { - int probe_offset = 4096; - while (probe_offset < info->total_size) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\t{l|lwz} %s,%d(%s)\n", - reg_names[0], -probe_offset, reg_names[1]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 0), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - stack_pointer_rtx, - GEN_INT (-probe_offset)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - probe_offset += 4096; - } - } - else - { - int probe_iterations = info->total_size / 4096; - static int probe_labelno = 0; - char buf[256]; - rtx r12_rtx = gen_rtx (REG, reg_mode, 12); - rtx r0_rtx = gen_rtx (REG, reg_mode, 0); - - if (probe_iterations < 32768) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tli %s,%d\n", - reg_names[12], probe_iterations); - else if (rtx_flag) - { - insn - = emit_insn (TARGET_32BIT - ? gen_addsi3 (r12_rtx, r0_rtx, - GEN_INT (probe_iterations)) - : gen_adddi3 (r12_rtx, r0_rtx, - GEN_INT (probe_iterations))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tlis %s,%d\n", - reg_names[12], probe_iterations >> 16); - else if (rtx_flag) - { - insn = emit_insn (TARGET_32BIT - ? gen_addsi3 (r12_rtx, r0_rtx, - GEN_INT ((probe_iterations - >> 16) << 16)) - : gen_adddi3 (r12_rtx, r0_rtx, - GEN_INT ((probe_iterations - >> 16) << 16))); - RTX_FRAME_RELATED_P (insn) = 1; - } - if (probe_iterations & 0xffff) - { - rtx const_int_rtx; - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tori %s,%s,%d\n", reg_names[12], - reg_names[12], probe_iterations & 0xffff); - else if (rtx_flag) - { - const_int_rtx = GEN_INT (probe_iterations & 0xffff); - - insn = emit_insn (TARGET_32BIT - ? gen_iorsi3 (r12_rtx, r12_rtx, - const_int_rtx) - : gen_iordi3 (r12_rtx, r12_rtx, - const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - asm_fprintf (file, "\tmtctr %s\n", reg_names[12]); - asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - ASM_OUTPUT_INTERNAL_LABEL (file, "LCprobe", probe_labelno); - asm_fprintf (file, "\t{lu|lwzu} %s,-4096(%s)\n", - reg_names[0], reg_names[12]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCprobe", probe_labelno++); - fputs ("\tbdnz ", file); - assemble_name (file, buf); - fputs ("\n", file); - } - else if (rtx_flag) - { - abort (); /* ??? vmakarov: Not implemented yet (ABI_NT). */ - } - } - } - - /* Update stack and set back pointer unless this is V.4, which was - done previously */ - if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - rs6000_allocate_stack_space (file, info->total_size, FALSE, rtx_flag); - - /* Set frame pointer, if needed. */ - if (frame_pointer_needed) - { - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 31), - gen_rtx (REG, reg_mode, 1)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - -#ifdef NAME__MAIN - /* If we need to call a function to set things up for main, do so now - before dealing with the TOC. */ - if (info->main_p) - { - char *prefix = ""; - - switch (DEFAULT_ABI) - { - case ABI_AIX: prefix = "."; break; - case ABI_NT: prefix = ".."; break; - } - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - fprintf (file, "\tbl %s%s\n", prefix, NAME__MAIN); -#ifdef RS6000_CALL_GLUE2 - fprintf (file, "\t%s%s%s\n", RS6000_CALL_GLUE2, prefix, NAME_MAIN); -#else -#ifdef RS6000_CALL_GLUE - if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT) - fprintf (file, "\t%s\n", RS6000_CALL_GLUE); -#endif -#endif - } - else if (rtx_flag) - { - static char name [100]; - - sprintf (name, "%s%s", prefix, NAME__MAIN); - insn = emit_call_insn (gen_rtx - (PARALLEL, VOIDmode, - gen_rtvec - (3, - gen_rtx (CALL, VOIDmode, - gen_rtx (MEM, FUNCTION_MODE, - gen_rtx (SYMBOL_REF, - Pmode, name)), - const0_rtx), - gen_rtx (USE, VOIDmode, const0_rtx), - gen_rtx (CLOBBER, VOIDmode, - gen_rtx (REG, reg_mode, 65))))); - RTX_FRAME_RELATED_P (insn) = 1; - } - - if (info->main_save_p) - { - int regno; - int loc; - int size = info->main_size; - - if (info->total_size < 32767) - { - loc = info->total_size + info->main_save_offset; - for (regno = 3; - size > 0; - regno++, size -= reg_size, loc += reg_size) - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, load_reg, - reg_names[regno], loc, reg_names[1]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, regno), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - stack_pointer_rtx, - GEN_INT (loc)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - int neg_size = info->main_save_offset - info->total_size; - loc = 0; - - if (!rtx_flag && !TARGET_SCHED_PROLOG) - { - asm_fprintf (file, - "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, "\t{sf|subf} %s,%s,%s\n", - reg_names[0], reg_names[0], reg_names[1]); - } - else if (rtx_flag) - { - rtx r0_rtx = gen_rtx (REG, reg_mode, 0); - rtx const_int_rtx; - - const_int_rtx = GEN_INT (((neg_size >> 16) & 0xffff) << 16); - insn = emit_insn (gen_rtx (SET, VOIDmode, r0_rtx, - const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - const_int_rtx = GEN_INT (neg_size & 0xffff); - insn = emit_insn (TARGET_32BIT - ? gen_iorsi3 (r0_rtx, r0_rtx, - const_int_rtx) - : gen_iordi3 (r0_rtx, r0_rtx, - const_int_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_rtx (MINUS, reg_mode, - r0_rtx, - gen_rtx (REG, reg_mode, 1), - r0_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - - for (regno = 3; - size > 0; - regno++, size -= reg_size, loc += reg_size) - if (!rtx_flag && !TARGET_SCHED_PROLOG) - asm_fprintf (file, load_reg, - reg_names[regno], loc, reg_names[0]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, regno), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - gen_rtx (REG, reg_mode, - 0), - GEN_INT (loc)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - } -#endif - - - /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the - TOC_TABLE address into register 30. */ - if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) - { -#ifdef USING_SVR4_H - if (!profile_flag) - rs6000_pic_func_labelno = rs6000_pic_labelno; -#endif - /* There is no oportuntity to optimize loading toc table -- - linear dependecies. */ - if (!rtx_flag && !TARGET_SCHED_PROLOG) - rs6000_output_load_toc_table (file, 30, FALSE); - else if (rtx_flag) - rs6000_output_load_toc_table (file, 30, TRUE); - } - - if (DEFAULT_ABI == ABI_NT) - { - assemble_name (file, - XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".b:\n", file); - } -} - -/* Write function epilogue. The function is called twice with two - different values of RTX_FLAG. Generate or not rtl or assembler - code is depended from value of TARGET_SCHED_PROLOG. */ - -static void -rs6000_epilog (file, rtx_flag, info) - FILE *file; - int rtx_flag; - rs6000_stack_t *info; -{ - char *load_reg = ((TARGET_32BIT) ? - "\t{l|lwz} %s,%d(%s)\n" : "\tld %s,%d(%s)\n"); - enum machine_mode reg_mode = (TARGET_32BIT) ? SImode : DImode; - rtx insn = get_last_insn (); - int sp_reg = 1; - rtx sp_reg_rtx = gen_rtx (REG, reg_mode, 1); - int sp_offset = 0; - - /* If the last insn was a BARRIER, we don't have to write anything except - the trace table. */ - if (insn != 0 && GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn == 0 || GET_CODE (insn) != BARRIER) - { - /* If we have a frame pointer, a call to alloca, or a large stack - 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 - || info->total_size > 32767) - { - /* Under V.4, don't reset the stack pointer until after we're done - loading the saved registers. */ - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - { - sp_reg = 11; - sp_reg_rtx = gen_rtx (REG, reg_mode, 11); - } - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, load_reg, reg_names[sp_reg], 0, reg_names[1]); - else if (rtx_flag) - { - insn = emit_move_insn (sp_reg_rtx, - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - gen_rtx (REG, reg_mode, 1), - const0_rtx))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else if (info->push_p) - { - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - sp_offset = info->total_size; - else - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], info->total_size, reg_names[1]); - else if (rtx_flag) - { - rtx reg_rtx = gen_rtx (REG, reg_mode, 1); - - insn - = emit_insn (TARGET_32BIT - ? gen_addsi3 (reg_rtx, reg_rtx, - GEN_INT (info->total_size)) - : gen_adddi3 (reg_rtx, reg_rtx, - GEN_INT (info->total_size))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - /* Get the old lr if we saved it. */ - if (info->lr_save_p) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, load_reg, reg_names[0], - info->lr_save_offset + sp_offset, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 0), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (info->lr_save_offset - + sp_offset)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Get the old cr if we saved it. */ - if (info->cr_save_p) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, load_reg, reg_names[12], - info->cr_save_offset + sp_offset, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 12), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (info->cr_save_offset - + sp_offset)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Set LR here to try to overlap restores below. */ - if (info->lr_save_p) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\tmtlr %s\n", reg_names[0]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 65), - gen_rtx (REG, reg_mode, 0)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Restore gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) - { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - int reg_size = (TARGET_32BIT) ? 4 : 8; - - for ( ; regno < 32; regno++, loc += reg_size) - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, load_reg, - reg_names[regno], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, regno), - gen_rtx - (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, GEN_INT (loc)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else if (info->first_gp_reg_save != 32) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\t{lm|lmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - else if (rtx_flag) - { - int loc; - int regno; - int reg_size = (TARGET_32BIT) ? 4 : 8; - - for (loc = info->gp_save_offset + sp_offset, - regno = info->first_gp_reg_save; - regno <= 31; - regno++, loc += reg_size) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, regno), - gen_rtx (MEM, reg_mode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, - GEN_INT (loc)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - /* Restore fpr's if we can do it without calling a function. */ - if (FP_SAVE_INLINE (info->first_fp_reg_save)) - { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; - - for ( ; regno < 64; regno++, loc += 8) - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\tlfd %s,%d(%s)\n", - reg_names[regno], loc, reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, DFmode, regno), - gen_rtx - (MEM, DFmode, - gen_rtx (PLUS, Pmode, - sp_reg_rtx, GEN_INT (loc)))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* If we saved cr, restore it here. Just those of cr2, cr3, and cr4 - that were used. */ - if (info->cr_save_p) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\tmtcrf %d,%s\n", - (regs_ever_live[70] != 0) * 0x20 - + (regs_ever_live[71] != 0) * 0x10 - + (regs_ever_live[72] != 0) * 0x8, reg_names[12]); - else if (rtx_flag) - { - rtx r12_rtx = gen_rtx (REG, reg_mode, 12); - - insn = emit_insn (TARGET_32BIT - ? gen_movesi_to_cr (r12_rtx) - : gen_movedi_to_cr (r12_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - - /* If this is V.4, unwind the stack pointer after all of the loads - have been done */ - if (sp_offset != 0) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], sp_offset, reg_names[1]); - else if (rtx_flag) - { - rtx reg_rtx = gen_rtx (REG, reg_mode, 1); - - insn = emit_insn (TARGET_32BIT - ? gen_addsi3 (reg_rtx, reg_rtx, - GEN_INT (sp_offset)) - : gen_adddi3 (reg_rtx, reg_rtx, - GEN_INT (sp_offset))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else if (sp_reg != 1) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\tmr %s,%s\n", - reg_names[1], reg_names[sp_reg]); - else if (rtx_flag) - { - insn = emit_move_insn (gen_rtx (REG, reg_mode, 1), sp_reg_rtx); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - /* If we have to restore more than two FP registers, branch to the - restore function. It will return to our caller. */ - if (info->first_fp_reg_save != 64 - && !FP_SAVE_INLINE (info->first_fp_reg_save)) - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\tb %s%d%s\n", RESTORE_FP_PREFIX, - info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); - else if (rtx_flag) - { - static char label [100]; - - sprintf (label, "%s%d%s", RESTORE_FP_PREFIX, - info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); - insn = emit_jump_insn (gen_jump (gen_rtx (SYMBOL_REF, Pmode, - label))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (!rtx_flag && !TARGET_SCHED_EPILOG) - asm_fprintf (file, "\t{br|blr}\n"); - else if (rtx_flag) - { - insn = emit_jump_insn (gen_indirect_jump - (gen_rtx (REG, reg_mode, 65))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - /* Output a traceback table here. See /usr/include/sys/debug.h for info - on its format. - - We don't output a traceback table if -finhibit-size-directive was - used. The documentation for -finhibit-size-directive reads - ``don't output a @code{.size} assembler directive, or anything - else that would cause trouble if the function is split in the - middle, and the two halves are placed at locations far apart in - memory.'' The traceback table has this property, since it - includes the offset from the start of the function to the - traceback table itself. - - System V.4 Powerpc's (and the embedded ABI derived from it) use a - different traceback table. */ - if (!rtx_flag && DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive) - { - char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); - int fixed_parms, float_parms, parm_info; - int i; - - while (*fname == '.') /* V.4 encodes . in the name */ - fname++; - - /* Need label immediately before tbtab, so we can compute its offset - from the function start. */ - if (*fname == '*') - ++fname; - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); - ASM_OUTPUT_LABEL (file, fname); - - /* The .tbtab pseudo-op can only be used for the first eight - expressions, since it can't handle the possibly variable - length fields that follow. However, if you omit the optional - fields, the assembler outputs zeros for all optional fields - anyways, giving each variable length field is minimum length - (as defined in sys/debug.h). Thus we can not use the .tbtab - pseudo-op at all. */ - - /* An all-zero word flags the start of the tbtab, for debuggers - that have to find it by searching forward from the entry - point or from the current pc. */ - fputs ("\t.long 0\n", file); - - /* Tbtab format type. Use format type 0. */ - fputs ("\t.byte 0,", file); - - /* Language type. Unfortunately, there doesn't seem to be any - official way to get this info, so we use language_string. C - is 0. C++ is 9. No number defined for Obj-C, so use the - value for C for now. There is no official value for Java, - although IBM appears to be using 13. There is no official value - for Chill, so we've choosen 44 pseudo-randomly. */ - if (! strcmp (language_string, "GNU C") - || ! strcmp (language_string, "GNU Obj-C")) - i = 0; - else if (! strcmp (language_string, "GNU F77")) - i = 1; - else if (! strcmp (language_string, "GNU Ada")) - i = 3; - else if (! strcmp (language_string, "GNU Pascal")) - i = 2; - else if (! strcmp (language_string, "GNU C++")) - i = 9; - else if (! strcmp (language_string, "GNU Java")) - i = 13; - else if (! strcmp (language_string, "GNU CHILL")) - i = 44; - else - abort (); - fprintf (file, "%d,", i); - - /* 8 single bit fields: global linkage (not set for C extern linkage, - apparently a PL/I convention?), out-of-line epilogue/prologue, offset - from start of procedure stored in tbtab, internal function, function - has controlled storage, function has no toc, function uses fp, - function logs/aborts fp operations. */ - /* Assume that fp operations are used if any fp reg must be saved. */ - fprintf (file, "%d,", - (1 << 5) | ((info->first_fp_reg_save != 64) << 1)); - - /* 6 bitfields: function is interrupt handler, name present in - proc table, function calls alloca, on condition directives - (controls stack walks, 3 bits), saves condition reg, saves - link reg. */ - /* The `function calls alloca' bit seems to be set whenever reg 31 is - set up as a frame pointer, even when there is no alloca call. */ - fprintf (file, "%d,", - ((1 << 6) | (frame_pointer_needed << 5) - | (info->cr_save_p << 1) | (info->lr_save_p))); - - /* 3 bitfields: saves backchain, spare bit, number of fpr saved - (6 bits). */ - fprintf (file, "%d,", - (info->push_p << 7) | (64 - info->first_fp_reg_save)); - - /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits). */ - fprintf (file, "%d,", (32 - first_reg_to_save ())); - - { - /* Compute the parameter info from the function decl argument - list. */ - tree decl; - int next_parm_info_bit; - - next_parm_info_bit = 31; - parm_info = 0; - fixed_parms = 0; - float_parms = 0; - - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - { - rtx parameter = DECL_INCOMING_RTL (decl); - enum machine_mode mode = GET_MODE (parameter); - - if (GET_CODE (parameter) == REG) - { - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - int bits; - - float_parms++; - - if (mode == SFmode) - bits = 0x2; - else if (mode == DFmode) - bits = 0x3; - else - abort (); - - /* If only one bit will fit, don't or in this entry. */ - if (next_parm_info_bit > 0) - parm_info |= (bits << (next_parm_info_bit - 1)); - next_parm_info_bit -= 2; - } - else - { - fixed_parms += ((GET_MODE_SIZE (mode) - + (UNITS_PER_WORD - 1)) - / UNITS_PER_WORD); - next_parm_info_bit -= 1; - } - } - } - } - - /* Number of fixed point parameters. */ - /* This is actually the number of words of fixed point parameters; thus - an 8 byte struct counts as 2; and thus the maximum value is 8. */ - fprintf (file, "%d,", fixed_parms); - - /* 2 bitfields: number of floating point parameters (7 bits), parameters - all on stack. */ - /* This is actually the number of fp registers that hold parameters; - and thus the maximum value is 13. */ - /* Set parameters on stack bit if parameters are not in their original - registers, regardless of whether they are on the stack? Xlc - seems to set the bit when not optimizing. */ - fprintf (file, "%d\n", ((float_parms << 1) | (! optimize))); - - /* Optional fields follow. Some are variable length. */ - - /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float, - 11 double float. */ - /* There is an entry for each parameter in a register, in the order that - they occur in the parameter list. Any intervening arguments on the - stack are ignored. If the list overflows a long (max possible length - 34 bits) then completely leave off all elements that don't fit. */ - /* Only emit this long if there was at least one parameter. */ - if (fixed_parms || float_parms) - fprintf (file, "\t.long %d\n", parm_info); - - /* Offset from start of code to tb table. */ - fputs ("\t.long ", file); - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); - RS6000_OUTPUT_BASENAME (file, fname); - fputs ("-.", file); - RS6000_OUTPUT_BASENAME (file, fname); - putc ('\n', file); - - /* Interrupt handler mask. */ - /* Omit this long, since we never set the interrupt handler bit - above. */ - - /* Number of CTL (controlled storage) anchors. */ - /* Omit this long, since the has_ctl bit is never set above. */ - - /* Displacement into stack of each CTL anchor. */ - /* Omit this list of longs, because there are no CTL anchors. */ - - /* Length of function name. */ - fprintf (file, "\t.short %d\n", (int) strlen (fname)); - - /* Function name. */ - assemble_string (fname, strlen (fname)); - - /* Register for alloca automatic storage; this is always reg 31. - Only emit this if the alloca bit was set above. */ - if (frame_pointer_needed) - fputs ("\t.byte 31\n", file); - } - - if (!rtx_flag && DEFAULT_ABI == ABI_NT) - { - RS6000_OUTPUT_BASENAME - (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".e:\nFE_MOT_RESVD..", file); - RS6000_OUTPUT_BASENAME - (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (":\n", file); - } -} - - - - -/* Write function prologue. */ -void -output_prolog (file, size) - FILE *file; - int size; -{ - rs6000_prolog (file, FALSE, rs6000_stack_info ()); -} - -/* Write function epilogue. */ - -void -output_epilog (file, size) - FILE *file; - int size; -{ - rs6000_epilog (file, FALSE, rs6000_stack_info ()); -} - -/* END CYGNUS LOCAL */ - - -/* A C compound statement that outputs the assembler code for a thunk function, - used to implement C++ virtual function calls with multiple inheritance. The - thunk acts as a wrapper around a virtual function, adjusting the implicit - object parameter before handing control off to the real function. - - First, emit code to add the integer DELTA to the location that contains the - incoming first argument. Assume that this argument contains a pointer, and - is the one used to pass the `this' pointer in C++. This is the incoming - argument *before* the function prologue, e.g. `%o0' on a sparc. The - addition must preserve the values of all other incoming arguments. - - After the addition, emit code to jump to FUNCTION, which is a - `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch - the return address. Hence returning from FUNCTION will return to whoever - called the current `thunk'. - - The effect must be as if FUNCTION had been called directly with the adjusted - first argument. This macro is responsible for emitting all of the code for - a thunk function; `FUNCTION_PROLOGUE' and `FUNCTION_EPILOGUE' are not - invoked. - - The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been - extracted from it.) It might possibly be useful on some targets, but - probably not. - - If you do not define this macro, the target-independent code in the C++ - frontend will generate a less efficient heavyweight thunk that calls - FUNCTION instead of jumping to it. The generic approach does not support - varargs. */ - -void -output_mi_thunk (file, thunk_fndecl, delta, function) - FILE *file; - tree thunk_fndecl; - int delta; - tree function; -{ - char *this_reg = reg_names[ aggregate_value_p (TREE_TYPE (TREE_TYPE (function))) ? 4 : 3 ]; - char *r0 = reg_names[0]; - char *sp = reg_names[1]; - char *toc = reg_names[2]; - char *schain = reg_names[11]; - char *r12 = reg_names[12]; - char *prefix; - char *fname; - char buf[512]; - static int labelno = 0; - - /* Small constants that can be done by one add instruction */ - if (delta >= -32768 && delta <= 32767) - { - if (!TARGET_NEW_MNEMONICS) - fprintf (file, "\tcal %s,%d(%s)\n", this_reg, delta, this_reg); - else - fprintf (file, "\taddi %s,%s,%d\n", this_reg, this_reg, delta); - } - - /* Large constants that can be done by one addis instruction */ - else if ((delta & 0xffff) == 0 && num_insns_constant_wide (delta) == 1) - asm_fprintf (file, "\t{cau|addis} %s,%s,%d\n", this_reg, this_reg, - delta >> 16); - - /* 32-bit constants that can be done by an add and addis instruction. */ - else if (TARGET_32BIT || num_insns_constant_wide (delta) == 1) - { - /* Break into two pieces, propigating the sign bit from the low word to - the upper word. */ - int delta_high = delta >> 16; - int delta_low = delta & 0xffff; - if ((delta_low & 0x8000) != 0) - { - delta_high++; - delta_low = (delta_low ^ 0x8000) - 0x8000; /* sign extend */ - } - - asm_fprintf (file, "\t{cau|addis} %s,%s,%d\n", this_reg, this_reg, - delta_high); - - if (!TARGET_NEW_MNEMONICS) - fprintf (file, "\tcal %s,%d(%s)\n", this_reg, delta_low, this_reg); - else - fprintf (file, "\taddi %s,%s,%d\n", this_reg, this_reg, delta_low); - } - - /* 64-bit constants, fixme */ - else - abort (); - - /* Get the prefix in front of the names. */ - switch (DEFAULT_ABI) - { - default: - abort (); - - case ABI_AIX: - prefix = "."; - break; - - case ABI_V4: - case ABI_AIX_NODESC: - case ABI_SOLARIS: - prefix = ""; - break; - - case ABI_NT: - prefix = ".."; - break; - } - - /* If the function is compiled in this module, jump to it directly. - Otherwise, load up its address and jump to it. */ - - fname = XSTR (XEXP (DECL_RTL (function), 0), 0); -#if 1 - /* For now, just emit a branch always, until we can figure out better when we - need to load the address into the count register and emit the slower bctr - instruction. */ - fprintf (file, "\tb %s", prefix); - assemble_name (file, fname); - fprintf (file, "\n"); - -#else - if (current_file_function_operand (XEXP (DECL_RTL (function), 0)) - && !lookup_attribute ("longcall", TYPE_ATTRIBUTES (TREE_TYPE (function)))) - { - fprintf (file, "\tb %s", prefix); - assemble_name (file, fname); - fprintf (file, "\n"); - } - - else - { - switch (DEFAULT_ABI) - { - default: - case ABI_NT: - abort (); - - case ABI_AIX: - /* Set up a TOC entry for the function. */ - ASM_GENERATE_INTERNAL_LABEL (buf, "Lthunk", labelno); - toc_section (); - ASM_OUTPUT_INTERNAL_LABEL (file, "Lthunk", labelno); - labelno++; - - /* Note, MINIMAL_TOC doesn't make sense in the case of a thunk, since - there will be only one TOC entry for this function. */ - fputs ("\t.tc\t", file); - assemble_name (file, buf); - fputs ("[TC],", file); - assemble_name (file, buf); - putc ('\n', file); - text_section (); - asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz} %s," : "\tld %s", r12); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", reg_names[2]); - asm_fprintf (file, - (TARGET_32BIT) ? "\t{l|lwz} %s,0(%s)\n" : "\tld %s,0(%s)\n", - r0, r12); - - asm_fprintf (file, - (TARGET_32BIT) ? "\t{l|lwz} %s,4(%s)\n" : "\tld %s,8(%s)\n", - toc, r12); - - asm_fprintf (file, "\tmtctr %s\n", r0); - asm_fprintf (file, - (TARGET_32BIT) ? "\t{l|lwz} %s,8(%s)\n" : "\tld %s,16(%s)\n", - schain, r12); - - asm_fprintf (file, "\tbctr\n"); - break; - - /* Don't use r11, that contains the static chain, just use r0/r12. */ - case ABI_V4: - case ABI_AIX_NODESC: - case ABI_SOLARIS: - if (flag_pic == 1) - { - fprintf (file, "\tmflr %s\n", r0); - fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file); - asm_fprintf (file, "\tmflr %s\n", r12); - asm_fprintf (file, "\tmtlr %s\n", r0); - asm_fprintf (file, "\t{l|lwz} %s,", r0); - assemble_name (file, fname); - asm_fprintf (file, "@got(%s)\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r0); - asm_fprintf (file, "\tbctr\n"); - } -#if TARGET_ELF - else if (flag_pic > 1 || TARGET_RELOCATABLE) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "Lthunk", labelno); - labelno++; - fprintf (file, "\tmflr %s\n", r0); - asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", r0, sp); - rs6000_pic_func_labelno = rs6000_pic_labelno; - rs6000_output_load_toc_table (file, 12 -/* CYGNUS LOCAL -- vmakarov/prolog-epilog instruction scheduling */ - , FALSE -/* END CYGNUS LOCAL */ - ); - asm_fprintf (file, "\t{l|lwz} %s,", r0); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", r12); - asm_fprintf (file, "\t{l|lwz} %s,4(%s)\n", r12, sp); - asm_fprintf (file, "\tmtlr %s\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r0); - asm_fprintf (file, "\tbctr\n"); - asm_fprintf (file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); - assemble_name (file, buf); - fputs (" = .-.LCTOC1\n", file); - fputs ("\t.long ", file); - assemble_name (file, fname); - fputs ("\n\t.previous\n", file); - } -#endif /* TARGET_ELF */ - - else - { - asm_fprintf (file, "\t{liu|lis} %s,", r12); - assemble_name (file, fname); - asm_fprintf (file, "@ha\n"); - asm_fprintf (file, "\t{cal|la} %s,", r12); - assemble_name (file, fname); - asm_fprintf (file, "@l(%s)\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r12); - asm_fprintf (file, "\tbctr\n"); - } - - break; - } - } -#endif /* #if 0 out code to use bctr for far away jumps */ -} - - -/* Output a TOC entry. We derive the entry name from what is - being written. */ - -void -output_toc (file, x, labelno) - FILE *file; - rtx x; - int labelno; -{ - char buf[256]; - char *name = buf; - char *real_name; - rtx base = x; - int offset = 0; - - if (TARGET_NO_TOC) - abort (); - - /* if we're going to put a double constant in the TOC, make sure it's - aligned properly when strict alignment is on. */ - if (GET_CODE (x) == CONST_DOUBLE - && STRICT_ALIGNMENT - && GET_MODE (x) == DFmode - && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) { - ASM_OUTPUT_ALIGN (file, 3); - } - - - if (TARGET_ELF && TARGET_MINIMAL_TOC) - { - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); - fprintf (file, "%d = .-", labelno); - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCTOC"); - fputs ("1\n", file); - } - else - ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno); - - /* Handle FP constants specially. Note that if we have a minimal - TOC, things we put here aren't actually in the TOC, so we can allow - FP constants. */ - if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode - && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) - { - REAL_VALUE_TYPE rv; - long k[2]; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, x); - REAL_VALUE_TO_TARGET_DOUBLE (rv, k); - if (TARGET_64BIT) - { - if (TARGET_MINIMAL_TOC) - fprintf (file, "\t.llong 0x%lx%08lx\n", k[0], k[1]); - else - fprintf (file, "\t.tc FD_%lx_%lx[TC],0x%lx%08lx\n", - k[0], k[1], k[0] & 0xffffffff, k[1] & 0xffffffff); - return; - } - else - { - if (TARGET_MINIMAL_TOC) - fprintf (file, "\t.long %ld\n\t.long %ld\n", k[0], k[1]); - else - fprintf (file, "\t.tc FD_%lx_%lx[TC],%ld,%ld\n", - k[0], k[1], k[0], k[1]); - return; - } - } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode - && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) - { - REAL_VALUE_TYPE rv; - long l; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, x); - REAL_VALUE_TO_TARGET_SINGLE (rv, l); - - if (TARGET_MINIMAL_TOC) - fprintf (file, TARGET_32BIT ? "\t.long %ld\n" : "\t.llong %ld\n", l); - else - fprintf (file, "\t.tc FS_%lx[TC],%ld\n", l, l); - return; - } - else if (GET_MODE (x) == DImode - && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) - && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) - { - HOST_WIDE_INT low; - HOST_WIDE_INT high; - - if (GET_CODE (x) == CONST_DOUBLE) - { - low = CONST_DOUBLE_LOW (x); - high = CONST_DOUBLE_HIGH (x); - } - else -#if HOST_BITS_PER_WIDE_INT == 32 - { - low = INTVAL (x); - high = (low < 0) ? ~0 : 0; - } -#else - { - low = INTVAL (x) & 0xffffffff; - high = (HOST_WIDE_INT) INTVAL (x) >> 32; - } -#endif - - if (TARGET_64BIT) - { - if (TARGET_MINIMAL_TOC) - fprintf (file, "\t.llong 0x%lx%08lx\n", (long)high, (long)low); - else - fprintf (file, "\t.tc ID_%lx_%lx[TC],0x%lx%08lx\n", - (long)high, (long)low, (long)high, (long)low); - return; - } - else - { - if (TARGET_MINIMAL_TOC) - fprintf (file, "\t.long %ld\n\t.long %ld\n", - (long)high, (long)low); - else - fprintf (file, "\t.tc ID_%lx_%lx[TC],%ld,%ld\n", - (long)high, (long)low, (long)high, (long)low); - return; - } - } - - if (GET_CODE (x) == CONST) - { - base = XEXP (XEXP (x, 0), 0); - offset = INTVAL (XEXP (XEXP (x, 0), 1)); - } - - if (GET_CODE (base) == SYMBOL_REF) - name = XSTR (base, 0); - else if (GET_CODE (base) == LABEL_REF) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (base, 0))); - else if (GET_CODE (base) == CODE_LABEL) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (base)); - else - abort (); - - STRIP_NAME_ENCODING (real_name, name); - if (TARGET_MINIMAL_TOC) - fputs (TARGET_32BIT ? "\t.long " : "\t.llong ", file); - else - { - fprintf (file, "\t.tc %s", real_name); - - if (offset < 0) - fprintf (file, ".N%d", - offset); - else if (offset) - fprintf (file, ".P%d", offset); - - fputs ("[TC],", file); - } - - /* Currently C++ toc references to vtables can be emitted before it - is decided whether the vtable is public or private. If this is - the case, then the linker will eventually complain that there is - a TOC reference to an unknown section. Thus, for vtables only, - we emit the TOC reference to reference the symbol and not the - section. */ - if (!strncmp ("_vt.", name, 4)) - { - RS6000_OUTPUT_BASENAME (file, name); - if (offset < 0) - fprintf (file, "%d", offset); - else if (offset > 0) - fprintf (file, "+%d", offset); - } - else - output_addr_const (file, x); - putc ('\n', file); -} - -/* Output an assembler pseudo-op to write an ASCII string of N characters - starting at P to FILE. - - On the RS/6000, we have to do this using the .byte operation and - write out special characters outside the quoted string. - Also, the assembler is broken; very long strings are truncated, - so we must artificially break them up early. */ - -void -output_ascii (file, p, n) - FILE *file; - char *p; - int n; -{ - char c; - int i, count_string; - char *for_string = "\t.byte \""; - char *for_decimal = "\t.byte "; - char *to_close = NULL; - - count_string = 0; - for (i = 0; i < n; i++) - { - c = *p++; - if (c >= ' ' && c < 0177) - { - if (for_string) - fputs (for_string, file); - putc (c, file); - - /* Write two quotes to get one. */ - if (c == '"') - { - putc (c, file); - ++count_string; - } - - for_string = NULL; - for_decimal = "\"\n\t.byte "; - to_close = "\"\n"; - ++count_string; - - if (count_string >= 512) - { - fputs (to_close, file); - - for_string = "\t.byte \""; - for_decimal = "\t.byte "; - to_close = NULL; - count_string = 0; - } - } - else - { - if (for_decimal) - fputs (for_decimal, file); - fprintf (file, "%d", c); - - for_string = "\n\t.byte \""; - for_decimal = ", "; - to_close = "\n"; - count_string = 0; - } - } - - /* Now close the string if we have written one. Then end the line. */ - if (to_close) - fprintf (file, to_close); -} - -/* Generate a unique section name for FILENAME for a section type - represented by SECTION_DESC. Output goes into BUF. - - SECTION_DESC can be any string, as long as it is different for each - possible section type. - - We name the section in the same manner as xlc. The name begins with an - underscore followed by the filename (after stripping any leading directory - names) with the last period replaced by the string SECTION_DESC. If - FILENAME does not contain a period, SECTION_DESC is appended to the end of - the name. */ - -void -rs6000_gen_section_name (buf, filename, section_desc) - char **buf; - char *filename; - char *section_desc; -{ - char *q, *after_last_slash, *last_period; - char *p; - int len; - - after_last_slash = filename; - for (q = filename; *q; q++) - { - if (*q == '/') - after_last_slash = q + 1; - else if (*q == '.') - last_period = q; - } - - len = strlen (after_last_slash) + strlen (section_desc) + 2; - *buf = (char *) permalloc (len); - - p = *buf; - *p++ = '_'; - - for (q = after_last_slash; *q; q++) - { - if (q == last_period) - { - strcpy (p, section_desc); - p += strlen (section_desc); - } - - else if (ISALNUM (*q)) - *p++ = *q; - } - - if (last_period == 0) - strcpy (p, section_desc); - else - *p = '\0'; -} - -/* Write function profiler code. */ - -void -output_function_profiler (file, labelno) - FILE *file; - int labelno; -{ - /* The last used parameter register. */ - int last_parm_reg; - int i, j; - char buf[100]; - - ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno); - switch (DEFAULT_ABI) - { - default: - abort (); - - case ABI_V4: - case ABI_SOLARIS: - case ABI_AIX_NODESC: - fprintf (file, "\tmflr %s\n", reg_names[0]); - if (flag_pic == 1) - { - fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file); - asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", - reg_names[0], reg_names[1]); - asm_fprintf (file, "\tmflr %s\n", reg_names[12]); - asm_fprintf (file, "\t{l|lwz} %s,", reg_names[0]); - assemble_name (file, buf); - asm_fprintf (file, "@got(%s)\n", reg_names[12]); - } -#if TARGET_ELF - else if (flag_pic > 1 || TARGET_RELOCATABLE) - { - asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", - reg_names[0], reg_names[1]); - rs6000_pic_func_labelno = rs6000_pic_labelno; - rs6000_output_load_toc_table (file, 12 -/* CYGNUS LOCAL -- vmakarov/prolog-epilog instruction scheduling */ - , FALSE -/* END CYGNUS LOCAL */ - ); - asm_fprintf (file, "\t{l|lwz} %s,", reg_names[12]); - assemble_name (file, buf); - asm_fprintf (file, "X(%s)\n", reg_names[12]); - asm_fprintf (file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); - assemble_name (file, buf); - fputs ("X = .-.LCTOC1\n", file); - fputs ("\t.long ", file); - assemble_name (file, buf); - fputs ("\n\t.previous\n", file); - } -#endif - else - { - asm_fprintf (file, "\t{liu|lis} %s,", reg_names[12]); - assemble_name (file, buf); - fputs ("@ha\n", file); - asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", reg_names[0], reg_names[1]); - asm_fprintf (file, "\t{cal|la} %s,", reg_names[0]); - assemble_name (file, buf); - asm_fprintf (file, "@l(%s)\n", reg_names[12]); - } - - fprintf (file, "\tbl %s\n", RS6000_MCOUNT); - break; - - case ABI_AIX: - /* Set up a TOC entry for the profiler label. */ - toc_section (); - ASM_OUTPUT_INTERNAL_LABEL (file, "LPC", labelno); - if (TARGET_MINIMAL_TOC) - { - fputs (TARGET_32BIT ? "\t.long " : "\t.llong ", file); - assemble_name (file, buf); - putc ('\n', file); - } - else - { - fputs ("\t.tc\t", file); - assemble_name (file, buf); - fputs ("[TC],", file); - assemble_name (file, buf); - putc ('\n', file); - } - text_section (); - - /* Figure out last used parameter register. The proper thing to do is - to walk incoming args of the function. A function might have live - parameter registers even if it has no incoming args. */ - - for (last_parm_reg = 10; - last_parm_reg > 2 && ! regs_ever_live [last_parm_reg]; - last_parm_reg--) - ; - - /* Save parameter registers in regs 23-30. Don't overwrite reg 31, since - it might be set up as the frame pointer. */ - - for (i = 3, j = 30; i <= last_parm_reg; i++, j--) - asm_fprintf (file, "\tmr %d,%d\n", j, i); - - /* Load location address into r3, and call mcount. */ - - ASM_GENERATE_INTERNAL_LABEL (buf, "LPC", labelno); - asm_fprintf (file, TARGET_32BIT ? "\t{l|lwz} %s," : "\tld %s,", - reg_names[3]); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n\tbl %s\n\t%s\n", - reg_names[2], RS6000_MCOUNT, RS6000_CALL_GLUE); - - /* Restore parameter registers. */ - - for (i = 3, j = 30; i <= last_parm_reg; i++, j--) - asm_fprintf (file, "\tmr %d,%d\n", i, j); - break; - } -} - -/* Adjust the cost of a scheduling dependency. Return the new cost of - a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ - -int -rs6000_adjust_cost (insn, link, dep_insn, cost) - rtx insn; - rtx link; - rtx dep_insn ATTRIBUTE_UNUSED; - int cost; -{ - if (! recog_memoized (insn)) - return 0; - - if (REG_NOTE_KIND (link) != 0) - return 0; - - if (REG_NOTE_KIND (link) == 0) - { - /* Data dependency; DEP_INSN writes a register that INSN reads some - cycles later. */ - - /* Tell the first scheduling pass about the latency between a mtctr - and bctr (and mtlr and br/blr). The first scheduling pass will not - know about this latency since the mtctr instruction, which has the - latency associated to it, will be generated by reload. */ - if (get_attr_type (insn) == TYPE_JMPREG) - return TARGET_POWER ? 5 : 4; - - /* Fall out to return default cost. */ - } - - return cost; -} - -/* A C statement (sans semicolon) to update the integer scheduling priority - INSN_PRIORITY (INSN). Reduce the priority to execute the INSN earlier, - increase the priority to execute INSN later. Do not define this macro if - you do not need to adjust the scheduling priorities of insns. */ - -int -rs6000_adjust_priority (insn, priority) - rtx insn; - int priority; -{ - /* On machines (like the 750) which have asymetric integer units, where one - integer unit can do multiply and divides and the other can't, reduce the - priority of multiply/divide so it is scheduled before other integer - operationss. */ - -#if 0 - if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') - return priority; - - if (GET_CODE (PATTERN (insn)) == USE) - return priority; - - switch (rs6000_cpu_attr) { - case CPU_PPC750: - switch (get_attr_type (insn)) - { - default: - break; - - case TYPE_IMUL: - case TYPE_IDIV: - fprintf (stderr, "priority was %#x (%d) before adjustment\n", priority, priority); - if (priority >= 0 && priority < 0x01000000) - priority >>= 3; - break; - } - } -#endif - - return priority; -} - -/* Return how many instructions the machine can issue per cycle */ -int get_issue_rate() -{ - switch (rs6000_cpu_attr) { - case CPU_RIOS1: - return 3; /* ? */ - case CPU_RIOS2: - return 4; - case CPU_PPC601: - return 3; /* ? */ - case CPU_PPC603: - return 2; - case CPU_PPC750: - return 2; - case CPU_PPC604: - return 4; - case CPU_PPC620: - return 4; - default: - return 1; - } -} - - - -/* Output assembler code for a block containing the constant parts - of a trampoline, leaving space for the variable parts. - - The trampoline should set the static chain pointer to value placed - into the trampoline and should branch to the specified routine. */ - -void -rs6000_trampoline_template (file) - FILE *file; -{ - char *sc = reg_names[STATIC_CHAIN_REGNUM]; - char *r0 = reg_names[0]; - char *r2 = reg_names[2]; - - switch (DEFAULT_ABI) - { - default: - abort (); - - /* Under AIX, this is not code at all, but merely a data area, - since that is the way all functions are called. The first word is - the address of the function, the second word is the TOC pointer (r2), - and the third word is the static chain value. */ - case ABI_AIX: - break; - - - /* V.4/eabi function pointers are just a single pointer, so we need to - do the full gory code to load up the static chain. */ - case ABI_V4: - case ABI_SOLARIS: - case ABI_AIX_NODESC: - break; - - /* NT function pointers point to a two word area (real address, TOC) - which unfortunately does not include a static chain field. So we - use the function field to point to ..LTRAMP1 and the toc field - to point to the whole table. */ - case ABI_NT: - if (STATIC_CHAIN_REGNUM == 0 - || STATIC_CHAIN_REGNUM == 2 - || TARGET_64BIT - || !TARGET_NEW_MNEMONICS) - abort (); - - fprintf (file, "\t.ualong 0\n"); /* offset 0 */ - fprintf (file, "\t.ualong 0\n"); /* offset 4 */ - fprintf (file, "\t.ualong 0\n"); /* offset 8 */ - fprintf (file, "\t.ualong 0\n"); /* offset 12 */ - fprintf (file, "\t.ualong 0\n"); /* offset 16 */ - fprintf (file, "..LTRAMP1..0:\n"); /* offset 20 */ - fprintf (file, "\tlwz %s,8(%s)\n", r0, r2); /* offset 24 */ - fprintf (file, "\tlwz %s,12(%s)\n", sc, r2); /* offset 28 */ - fprintf (file, "\tmtctr %s\n", r0); /* offset 32 */ - fprintf (file, "\tlwz %s,16(%s)\n", r2, r2); /* offset 36 */ - fprintf (file, "\tbctr\n"); /* offset 40 */ - break; - } - - return; -} - -/* Length in units of the trampoline for entering a nested function. */ - -int -rs6000_trampoline_size () -{ - int ret = 0; - - switch (DEFAULT_ABI) - { - default: - abort (); - - case ABI_AIX: - ret = (TARGET_32BIT) ? 12 : 24; - break; - - case ABI_V4: - case ABI_SOLARIS: - case ABI_AIX_NODESC: - ret = (TARGET_32BIT) ? 40 : 48; - break; - - case ABI_NT: - ret = 20; - break; - } - - return ret; -} - -/* Emit RTL insns to initialize the variable parts of a trampoline. - FNADDR is an RTX for the address of the function's pure code. - CXT is an RTX for the static chain value for the function. */ - -void -rs6000_initialize_trampoline (addr, fnaddr, cxt) - rtx addr; - rtx fnaddr; - rtx cxt; -{ - enum machine_mode pmode = Pmode; - int regsize = (TARGET_32BIT) ? 4 : 8; - rtx ctx_reg = force_reg (pmode, cxt); - - switch (DEFAULT_ABI) - { - default: - abort (); - -/* Macros to shorten the code expansions below. */ -#define MEM_DEREF(addr) gen_rtx_MEM (pmode, memory_address (pmode, addr)) -#define MEM_PLUS(addr,offset) gen_rtx_MEM (pmode, memory_address (pmode, plus_constant (addr, offset))) - - /* Under AIX, just build the 3 word function descriptor */ - case ABI_AIX: - { - rtx fn_reg = gen_reg_rtx (pmode); - rtx toc_reg = gen_reg_rtx (pmode); - emit_move_insn (fn_reg, MEM_DEREF (fnaddr)); - emit_move_insn (toc_reg, MEM_PLUS (fnaddr, 4)); - emit_move_insn (MEM_DEREF (addr), fn_reg); - emit_move_insn (MEM_PLUS (addr, regsize), toc_reg); - emit_move_insn (MEM_PLUS (addr, 2*regsize), ctx_reg); - } - break; - - /* Under V.4/eabi, call __trampoline_setup to do the real work. */ - case ABI_V4: - case ABI_SOLARIS: - case ABI_AIX_NODESC: - emit_library_call (gen_rtx_SYMBOL_REF (SImode, "__trampoline_setup"), - FALSE, VOIDmode, 4, - addr, pmode, - GEN_INT (rs6000_trampoline_size ()), SImode, - fnaddr, pmode, - ctx_reg, pmode); - break; - - /* Under NT, update the first word to point to the ..LTRAMP1..0 header, - the second word will point to the whole trampoline, third-fifth words - will then have the real address, static chain, and toc value. */ - case ABI_NT: - { - rtx tramp_reg = gen_reg_rtx (pmode); - rtx fn_reg = gen_reg_rtx (pmode); - rtx toc_reg = gen_reg_rtx (pmode); - - emit_move_insn (tramp_reg, gen_rtx_SYMBOL_REF (pmode, "..LTRAMP1..0")); - addr = force_reg (pmode, addr); - emit_move_insn (fn_reg, MEM_DEREF (fnaddr)); - emit_move_insn (toc_reg, MEM_PLUS (fnaddr, regsize)); - emit_move_insn (MEM_DEREF (addr), tramp_reg); - emit_move_insn (MEM_PLUS (addr, regsize), addr); - emit_move_insn (MEM_PLUS (addr, 2*regsize), fn_reg); - emit_move_insn (MEM_PLUS (addr, 3*regsize), ctx_reg); - emit_move_insn (MEM_PLUS (addr, 4*regsize), gen_rtx_REG (pmode, 2)); - } - break; - } - - return; -} - - -/* If defined, a C expression whose value is nonzero if IDENTIFIER - with arguments ARGS is a valid machine specific attribute for DECL. - The attributes in ATTRIBUTES have previously been assigned to DECL. */ - -int -rs6000_valid_decl_attribute_p (decl, attributes, identifier, args) - tree decl ATTRIBUTE_UNUSED; - tree attributes ATTRIBUTE_UNUSED; - tree identifier ATTRIBUTE_UNUSED; - tree args ATTRIBUTE_UNUSED; -{ - return 0; -} - -/* If defined, a C expression whose value is nonzero if IDENTIFIER - with arguments ARGS is a valid machine specific attribute for TYPE. - The attributes in ATTRIBUTES have previously been assigned to TYPE. */ - -int -rs6000_valid_type_attribute_p (type, attributes, identifier, args) - tree type; - tree attributes ATTRIBUTE_UNUSED; - tree identifier; - tree args; -{ - if (TREE_CODE (type) != FUNCTION_TYPE - && TREE_CODE (type) != FIELD_DECL - && TREE_CODE (type) != TYPE_DECL) - return 0; - - /* Longcall attribute says that the function is not within 2**26 bytes - of the current function, and to do an indirect call. */ - if (is_attribute_p ("longcall", identifier)) - return (args == NULL_TREE); - - if (DEFAULT_ABI == ABI_NT) - { - /* Stdcall attribute says callee is responsible for popping arguments - if they are not variable. */ - if (is_attribute_p ("stdcall", identifier)) - return (args == NULL_TREE); - - /* Cdecl attribute says the callee is a normal C declaration */ - if (is_attribute_p ("cdecl", identifier)) - return (args == NULL_TREE); - - /* Dllimport attribute says the caller is to call the function - indirectly through a __imp_<name> pointer. */ - if (is_attribute_p ("dllimport", identifier)) - return (args == NULL_TREE); - - /* Dllexport attribute says the callee is to create a __imp_<name> - pointer. */ - if (is_attribute_p ("dllexport", identifier)) - return (args == NULL_TREE); - - /* Exception attribute allows the user to specify 1-2 strings or identifiers - that will fill in the 3rd and 4th fields of the structured exception - table. */ - if (is_attribute_p ("exception", identifier)) - { - int i; - - if (args == NULL_TREE) - return 0; - - for (i = 0; i < 2 && args != NULL_TREE; i++) - { - tree this_arg = TREE_VALUE (args); - args = TREE_PURPOSE (args); - - if (TREE_CODE (this_arg) != STRING_CST - && TREE_CODE (this_arg) != IDENTIFIER_NODE) - return 0; - } - - return (args == NULL_TREE); - } - } - - return 0; -} - -/* If defined, a C expression whose value is zero if the attributes on - TYPE1 and TYPE2 are incompatible, one if they are compatible, and - two if they are nearly compatible (which causes a warning to be - generated). */ - -int -rs6000_comp_type_attributes (type1, type2) - tree type1 ATTRIBUTE_UNUSED; - tree type2 ATTRIBUTE_UNUSED; -{ - return 1; -} - -/* If defined, a C statement that assigns default attributes to newly - defined TYPE. */ - -void -rs6000_set_default_type_attributes (type) - tree type ATTRIBUTE_UNUSED; -{ -} - -/* Return a dll import reference corresponding to a call's SYMBOL_REF */ -struct rtx_def * -rs6000_dll_import_ref (call_ref) - rtx call_ref; -{ - char *call_name; - int len; - char *p; - rtx reg1, reg2; - tree node; - - if (GET_CODE (call_ref) != SYMBOL_REF) - abort (); - - call_name = XSTR (call_ref, 0); - len = sizeof ("__imp_") + strlen (call_name); - p = alloca (len); - reg2 = gen_reg_rtx (Pmode); - - strcpy (p, "__imp_"); - strcat (p, call_name); - node = get_identifier (p); - - reg1 = force_reg (Pmode, gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node))); - emit_move_insn (reg2, gen_rtx_MEM (Pmode, reg1)); - - return reg2; -} - -/* Return a reference suitable for calling a function with the longcall attribute. */ -struct rtx_def * -rs6000_longcall_ref (call_ref) - rtx call_ref; -{ - char *call_name; - tree node; - - if (GET_CODE (call_ref) != SYMBOL_REF) - return call_ref; - - /* System V adds '.' to the internal name, so skip them. */ - call_name = XSTR (call_ref, 0); - if (*call_name == '.') - { - while (*call_name == '.') - call_name++; - - node = get_identifier (call_name); - call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node)); - } - - return force_reg (Pmode, call_ref); -} - - -/* A C statement or statements to switch to the appropriate section - for output of RTX in mode MODE. You can assume that RTX is some - kind of constant in RTL. The argument MODE is redundant except in - the case of a `const_int' rtx. Select the section by calling - `text_section' or one of the alternatives for other sections. - - Do not define this macro if you put all constants in the read-only - data section. */ - -#ifdef USING_SVR4_H - -void -rs6000_select_rtx_section (mode, x) - enum machine_mode mode; - rtx x; -{ - if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x)) - toc_section (); - else - const_section (); -} - -/* A C statement or statements to switch to the appropriate - section for output of DECL. DECL is either a `VAR_DECL' node - or a constant of some sort. RELOC indicates whether forming - the initial value of DECL requires link-time relocations. */ - -void -rs6000_select_section (decl, reloc) - tree decl; - int reloc; -{ - int size = int_size_in_bytes (TREE_TYPE (decl)); - - if (TREE_CODE (decl) == STRING_CST) - { - if (! flag_writable_strings) - const_section (); - else - data_section (); - } - else if (TREE_CODE (decl) == VAR_DECL) - { - if ((flag_pic && reloc) - || !TREE_READONLY (decl) - || TREE_SIDE_EFFECTS (decl) - || !DECL_INITIAL (decl) - || (DECL_INITIAL (decl) != error_mark_node - && !TREE_CONSTANT (DECL_INITIAL (decl)))) - { - if (rs6000_sdata != SDATA_NONE && (size > 0) && (size <= g_switch_value)) - sdata_section (); - else - data_section (); - } - else - { - if (rs6000_sdata != SDATA_NONE && (size > 0) && (size <= g_switch_value)) - { - if (rs6000_sdata == SDATA_EABI) - sdata2_section (); - else - sdata_section (); /* System V doesn't have .sdata2/.sbss2 */ - } - else - const_section (); - } - } - else - const_section (); -} - - - -/* If we are referencing a function that is static or is known to be - in this file, make the SYMBOL_REF special. We can use this to indicate - that we can branch to this function without emitting a no-op after the - call. For real AIX and NT calling sequences, we also replace the - function name with the real name (1 or 2 leading .'s), rather than - the function descriptor name. This saves a lot of overriding code - to read the prefixes. */ - -void -rs6000_encode_section_info (decl) - tree decl; -{ - if (TREE_CODE (decl) == FUNCTION_DECL) - { - rtx sym_ref = XEXP (DECL_RTL (decl), 0); - if (TREE_ASM_WRITTEN (decl) || ! TREE_PUBLIC (decl)) - SYMBOL_REF_FLAG (sym_ref) = 1; - - if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT) - { - char *prefix = (DEFAULT_ABI == ABI_AIX) ? "." : ".."; - char *str = permalloc (strlen (prefix) + 1 - + strlen (XSTR (sym_ref, 0))); - strcpy (str, prefix); - strcat (str, XSTR (sym_ref, 0)); - XSTR (sym_ref, 0) = str; - } - } - else if (rs6000_sdata != SDATA_NONE - && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - && TREE_CODE (decl) == VAR_DECL) - { - int size = int_size_in_bytes (TREE_TYPE (decl)); - tree section_name = DECL_SECTION_NAME (decl); - char *name = (char *)0; - int len = 0; - - if (section_name) - { - if (TREE_CODE (section_name) == STRING_CST) - { - name = TREE_STRING_POINTER (section_name); - len = TREE_STRING_LENGTH (section_name); - } - else - abort (); - } - - if ((size > 0 && size <= g_switch_value) - || (name - && ((len == sizeof (".sdata")-1 && strcmp (name, ".sdata") == 0) - || (len == sizeof (".sdata2")-1 && strcmp (name, ".sdata2") == 0) - || (len == sizeof (".sbss")-1 && strcmp (name, ".sbss") == 0) - || (len == sizeof (".sbss2")-1 && strcmp (name, ".sbss2") == 0) - || (len == sizeof (".PPC.EMB.sdata0")-1 && strcmp (name, ".PPC.EMB.sdata0") == 0) - || (len == sizeof (".PPC.EMB.sbss0")-1 && strcmp (name, ".PPC.EMB.sbss0") == 0)))) - { - rtx sym_ref = XEXP (DECL_RTL (decl), 0); - char *str = permalloc (2 + strlen (XSTR (sym_ref, 0))); - strcpy (str, "@"); - strcat (str, XSTR (sym_ref, 0)); - XSTR (sym_ref, 0) = str; - } - } -} - -#endif /* USING_SVR4_H */ - -void -rs6000_fatal_bad_address (op) - rtx op; -{ - fatal_insn ("bad address", op); -} - -/* CYGNUS LOCAL -- vmakarov/prolog-epilog instruction scheduling */ - - -void -rs6000_expand_prologue (void) -{ - rs6000_stack_t *info; - - /* Trick is here. We need information about function but we have - started new insn sequence in `gen_prologue'. */ - end_sequence (); - info = rs6000_stack_info (); - start_sequence (); - rs6000_prolog (NULL, TRUE, info); -} - -void -rs6000_expand_epilogue (void) -{ - rs6000_stack_t *info; - - /* Trick is here. We need information about function but we have - started new insn sequence in `gen_epilogue'. */ - end_sequence (); - info = rs6000_stack_info (); - start_sequence (); - rs6000_epilog (NULL, TRUE, info); -} - -/* END CYGNUS LOCAL */ |