| /* Subroutines used for code generation on IBM RS/6000. |
| Copyright (C) 1991, 93, 94, 95, 96, 1997 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 <stdio.h> |
| #include <ctype.h> |
| #include "config.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 "output.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" |
| |
| #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; |
| |
| #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; |
| { |
| int i, j; |
| struct rs6000_cpu_select *ptr; |
| |
| /* Simplify the entries below by making a mask for any POWER |
| variant and any PowerPC variant. */ |
| |
| #define POWER_MASKS (MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING) |
| #define POWERPC_MASKS (MASK_POWERPC | MASK_PPC_GPOPT \ |
| | MASK_PPC_GFXOPT | MASK_POWERPC64) |
| #define POWERPC_OPT_MASKS (MASK_PPC_GPOPT | MASK_PPC_GFXOPT) |
| |
| static struct ptt |
| { |
| char *name; /* Canonical processor name. */ |
| enum processor_type processor; /* Processor type enum value. */ |
| int target_enable; /* Target flags to enable. */ |
| int target_disable; /* Target flags to disable. */ |
| } processor_target_table[] |
| = {{"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS, |
| POWER_MASKS | POWERPC_MASKS}, |
| {"power", PROCESSOR_POWER, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING, |
| MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"power2", PROCESSOR_POWER, |
| MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING, |
| POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"powerpc", PROCESSOR_POWERPC, |
| MASK_POWERPC | MASK_NEW_MNEMONICS, |
| POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}, |
| {"rios", PROCESSOR_RIOS1, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING, |
| MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"rios1", PROCESSOR_RIOS1, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING, |
| MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"rsc", PROCESSOR_PPC601, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING, |
| MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"rsc1", PROCESSOR_PPC601, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING, |
| MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"rios2", PROCESSOR_RIOS2, |
| MASK_POWER | MASK_MULTIPLE | MASK_STRING | MASK_POWER2, |
| POWERPC_MASKS | MASK_NEW_MNEMONICS}, |
| {"403", PROCESSOR_PPC403, |
| MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS | 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}, |
| {"604", PROCESSOR_PPC604, |
| MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, |
| POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, |
| {"604e", PROCESSOR_PPC604, |
| MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, |
| POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, |
| {"620", PROCESSOR_PPC620, |
| MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS, |
| POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64}, |
| {"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}}; |
| |
| int ptt_size = sizeof (processor_target_table) / sizeof (struct ptt); |
| |
| int multiple = TARGET_MULTIPLE; /* save current -mmultiple/-mno-multiple status */ |
| int string = TARGET_STRING; /* save current -mstring/-mno-string status */ |
| |
| profile_block_flag = 0; |
| |
| /* Identify the processor type */ |
| rs6000_select[0].string = default_cpu; |
| rs6000_cpu = PROCESSOR_DEFAULT; |
| |
| for (i = 0; i < sizeof (rs6000_select) / sizeof (rs6000_select[0]); i++) |
| { |
| ptr = &rs6000_select[i]; |
| if (ptr->string != (char *)0 && ptr->string[0] != '\0') |
| { |
| for (j = 0; j < ptt_size; j++) |
| if (! strcmp (ptr->string, processor_target_table[j].name)) |
| { |
| if (ptr->set_tune_p) |
| rs6000_cpu = processor_target_table[j].processor; |
| |
| if (ptr->set_arch_p) |
| { |
| target_flags |= processor_target_table[j].target_enable; |
| target_flags &= ~processor_target_table[j].target_disable; |
| } |
| break; |
| } |
| |
| if (i == ptt_size) |
| error ("bad value (%s) for %s switch", ptr->string, ptr->name); |
| } |
| } |
| |
| /* If -mmultiple or -mno-multiple was explicitly used, don't |
| override with the processor default */ |
| if (TARGET_MULTIPLE_SET) |
| target_flags = (target_flags & ~MASK_MULTIPLE) | multiple; |
| |
| /* If -mstring or -mno-string was explicitly used, don't |
| override with the processor default */ |
| if (TARGET_STRING_SET) |
| target_flags = (target_flags & ~MASK_STRING) | string; |
| |
| /* Don't allow -mmultiple or -mstring on little endian systems, because the |
| hardware doesn't support the instructions used in little endian mode */ |
| if (!BYTES_BIG_ENDIAN) |
| { |
| if (TARGET_MULTIPLE) |
| { |
| target_flags &= ~MASK_MULTIPLE; |
| if (TARGET_MULTIPLE_SET) |
| warning ("-mmultiple is not supported on little endian systems"); |
| } |
| |
| if (TARGET_STRING) |
| { |
| target_flags &= ~MASK_STRING; |
| if (TARGET_STRING_SET) |
| warning ("-mstring is not supported on little endian systems"); |
| } |
| } |
| |
| /* 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 |
| |
| #ifdef SUBTARGET_OVERRIDE_OPTIONS |
| SUBTARGET_OVERRIDE_OPTIONS; |
| #endif |
| } |
| |
| /* Do anything needed at the start of the asm file. */ |
| |
| void |
| rs6000_file_start (file, default_cpu) |
| FILE *file; |
| char *default_cpu; |
| { |
| int 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); |
| } |
| |
| |
| /* Create a CONST_DOUBLE like immed_double_const, except reverse the |
| two parts of the constant if the target is little endian. */ |
| |
| struct rtx_def * |
| rs6000_immed_double_const (i0, i1, mode) |
| HOST_WIDE_INT i0, i1; |
| enum machine_mode mode; |
| { |
| if (! WORDS_BIG_ENDIAN) |
| return immed_double_const (i1, i0, mode); |
| |
| return immed_double_const (i0, i1, mode); |
| } |
| |
| |
| /* 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; |
| enum machine_mode mode; |
| { |
| return 1; |
| } |
| |
| /* Returns 1 if op is the count register */ |
| int |
| count_register_operand(op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| if (GET_CODE (op) != REG) |
| return 0; |
| |
| if (REGNO (op) == COUNT_REGISTER_REGNUM) |
| return 1; |
| |
| if (REGNO (op) > FIRST_PSEUDO_REGISTER) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* 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; |
| { |
| 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; |
| { |
| return (GET_CODE (op) == CONST_INT |
| && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x8000) < 0x10000); |
| } |
| |
| /* Similar for a unsigned D field. */ |
| |
| int |
| u_short_cint_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| return (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0); |
| } |
| |
| /* 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; |
| { |
| 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 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; |
| { |
| if (GET_CODE (op) == CONST_INT |
| && (INTVAL (op) & 0xffff0000) == 0) |
| return 1; |
| |
| return 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 || 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; |
| { |
| 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; |
| { |
| 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 && TARGET_32BIT) |
| return (num_insns_constant_wide (CONST_DOUBLE_LOW (op)) |
| + num_insns_constant_wide (CONST_DOUBLE_HIGH (op))); |
| |
| else if (GET_CODE (op) == CONST_DOUBLE && TARGET_64BIT) |
| { |
| HOST_WIDE_INT low = CONST_DOUBLE_LOW (op); |
| HOST_WIDE_INT high = CONST_DOUBLE_HIGH (op); |
| |
| 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 (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 && TARGET_32BIT) |
| return 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; |
| { |
| 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) & 0xffff0000) == 0 |
| || (INTVAL (op) & 0xffff) == 0))); |
| } |
| |
| /* 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; |
| { |
| return (GET_CODE (op) == CONST_INT |
| && (INTVAL (op) & 0xffff0000) != 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 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; |
| { |
| return GET_CODE (op) == CONST_INT && mask_constant (INTVAL (op)); |
| } |
| |
| /* 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 constant but not a valid operand for an AND |
| insn. */ |
| |
| int |
| non_and_cint_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| return GET_CODE (op) == CONST_INT && ! and_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; |
| { |
| 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) == 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; |
| enum machine_mode mode; |
| { |
| #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 |
| sym_ref = XEXP (XEXP (op, 0), 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; |
| 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 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 = ((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 = ((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 */ |
| else if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && |
| align_words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG) |
| { |
| return NULL_RTX; |
| } |
| |
| else if (align_words < GP_ARG_NUM_REG) |
| return gen_rtx (REG, mode, GP_ARG_MIN_REG + align_words); |
| |
| return NULL_RTX; |
| } |
| |
| /* 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; |
| enum machine_mode mode; |
| tree type; |
| int named; |
| { |
| 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_64BIT) ? 8 : 4; |
| |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, |
| "setup_vararg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, no_rtl= %d\n", |
| cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), no_rtl); |
| |
| if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && !no_rtl) |
| { |
| rs6000_sysv_varargs_p = 1; |
| save_area = plus_constant (frame_pointer_rtx, RS6000_VARARGS_OFFSET); |
| } |
| |
| if (cum->words < 8) |
| { |
| int first_reg_offset = cum->words; |
| |
| if (MUST_PASS_IN_STACK (mode, type)) |
| first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1); |
| |
| if (first_reg_offset > GP_ARG_NUM_REG) |
| first_reg_offset = GP_ARG_NUM_REG; |
| |
| if (!no_rtl && first_reg_offset != GP_ARG_NUM_REG) |
| move_block_from_reg |
| (GP_ARG_MIN_REG + first_reg_offset, |
| gen_rtx (MEM, BLKmode, |
| plus_constant (save_area, first_reg_offset * reg_size)), |
| GP_ARG_NUM_REG - first_reg_offset, |
| (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD); |
| |
| *pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD; |
| } |
| |
| /* Save FP registers if needed. */ |
| if ((DEFAULT_ABI == ABI_V4 || 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; |
| { |
| 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_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem); |
| MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (orig_mem); |
| #ifdef MEM_UNALIGNED_P |
| MEM_UNALIGNED_P (mem) = MEM_UNALIGNED_P (orig_mem); |
| #endif |
| return mem; |
| } |
| |
| /* Expand a block move operation, and return 1 if successful. Return 0 |
| if we should let the compiler generate normal code. |
| |
| operands[0] is the destination |
| operands[1] is the source |
| operands[2] is the length |
| operands[3] is the alignment */ |
| |
| #define MAX_MOVE_REG 4 |
| |
| int |
| expand_block_move (operands) |
| rtx operands[]; |
| { |
| rtx orig_dest = operands[0]; |
| rtx orig_src = operands[1]; |
| rtx bytes_rtx = operands[2]; |
| rtx align_rtx = operands[3]; |
| int constp = (GET_CODE (bytes_rtx) == CONST_INT); |
| int align = XINT (align_rtx, 0); |
| int bytes; |
| int offset; |
| int num_reg; |
| int i; |
| rtx src_reg; |
| rtx dest_reg; |
| rtx src_addr; |
| rtx dest_addr; |
| rtx tmp_reg; |
| rtx stores[MAX_MOVE_REG]; |
| int move_bytes; |
| |
| /* If this is not a fixed size move, just call memcpy */ |
| if (!constp) |
| return 0; |
| |
| /* Anything to move? */ |
| bytes = INTVAL (bytes_rtx); |
| if (bytes <= 0) |
| return 1; |
| |
| /* Don't support real large moves. If string instructions are not used, |
| then don't generate more than 8 loads. */ |
| if (TARGET_STRING) |
| { |
| if (bytes > 4*8) |
| return 0; |
| } |
| else if (!STRICT_ALIGNMENT) |
| { |
| if (bytes > 4*8) |
| return 0; |
| } |
| else if (bytes > 8*align) |
| return 0; |
| |
| /* Move the address into scratch registers. */ |
| dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0)); |
| src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); |
| |
| if (TARGET_STRING) /* string instructions are available */ |
| { |
| for ( ; bytes > 0; bytes -= move_bytes) |
| { |
| if (bytes > 24 /* move up to 32 bytes at a time */ |
| && !fixed_regs[5] |
| && !fixed_regs[6] |
| && !fixed_regs[7] |
| && !fixed_regs[8] |
| && !fixed_regs[9] |
| && !fixed_regs[10] |
| && !fixed_regs[11] |
| && !fixed_regs[12]) |
| { |
| move_bytes = (bytes > 32) ? 32 : bytes; |
| emit_insn (gen_movstrsi_8reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), |
| expand_block_move_mem (BLKmode, src_reg, orig_src), |
| GEN_INT ((move_bytes == 32) ? 0 : move_bytes), |
| align_rtx)); |
| } |
| else if (bytes > 16 /* move up to 24 bytes at a time */ |
| && !fixed_regs[7] |
| && !fixed_regs[8] |
| && !fixed_regs[9] |
| && !fixed_regs[10] |
| && !fixed_regs[11] |
| && !fixed_regs[12]) |
| { |
| move_bytes = (bytes > 24) ? 24 : bytes; |
| emit_insn (gen_movstrsi_6reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), |
| expand_block_move_mem (BLKmode, src_reg, orig_src), |
| GEN_INT (move_bytes), |
| align_rtx)); |
| } |
| else if (bytes > 8 /* move up to 16 bytes at a time */ |
| && !fixed_regs[9] |
| && !fixed_regs[10] |
| && !fixed_regs[11] |
| && !fixed_regs[12]) |
| { |
| move_bytes = (bytes > 16) ? 16 : bytes; |
| emit_insn (gen_movstrsi_4reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), |
| expand_block_move_mem (BLKmode, src_reg, orig_src), |
| GEN_INT (move_bytes), |
| align_rtx)); |
| } |
| else if (bytes > 4 && !TARGET_64BIT) |
| { /* move up to 8 bytes at a time */ |
| move_bytes = (bytes > 8) ? 8 : bytes; |
| emit_insn (gen_movstrsi_2reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), |
| expand_block_move_mem (BLKmode, src_reg, orig_src), |
| GEN_INT (move_bytes), |
| align_rtx)); |
| } |
| else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT)) |
| { /* move 4 bytes */ |
| move_bytes = 4; |
| tmp_reg = gen_reg_rtx (SImode); |
| emit_move_insn (tmp_reg, expand_block_move_mem (SImode, src_reg, orig_src)); |
| emit_move_insn (expand_block_move_mem (SImode, dest_reg, orig_dest), tmp_reg); |
| } |
| else if (bytes == 2 && (align >= 2 || !STRICT_ALIGNMENT)) |
| { /* move 2 bytes */ |
| move_bytes = 2; |
| tmp_reg = gen_reg_rtx (HImode); |
| emit_move_insn (tmp_reg, expand_block_move_mem (HImode, src_reg, orig_src)); |
| emit_move_insn (expand_block_move_mem (HImode, dest_reg, orig_dest), tmp_reg); |
| } |
| else if (bytes == 1) /* move 1 byte */ |
| { |
| move_bytes = 1; |
| tmp_reg = gen_reg_rtx (QImode); |
| emit_move_insn (tmp_reg, expand_block_move_mem (QImode, src_reg, orig_src)); |
| emit_move_insn (expand_block_move_mem (QImode, dest_reg, orig_dest), tmp_reg); |
| } |
| else |
| { /* move up to 4 bytes at a time */ |
| move_bytes = (bytes > 4) ? 4 : bytes; |
| emit_insn (gen_movstrsi_1reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest), |
| expand_block_move_mem (BLKmode, src_reg, orig_src), |
| GEN_INT (move_bytes), |
| align_rtx)); |
| } |
| |
| if (bytes > move_bytes) |
| { |
| emit_insn (gen_addsi3 (src_reg, src_reg, GEN_INT (move_bytes))); |
| emit_insn (gen_addsi3 (dest_reg, dest_reg, GEN_INT (move_bytes))); |
| } |
| } |
| } |
| |
| else /* string instructions not available */ |
| { |
| num_reg = offset = 0; |
| for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes)) |
| { |
| /* Calculate the correct offset for src/dest */ |
| if (offset == 0) |
| { |
| src_addr = src_reg; |
| dest_addr = dest_reg; |
| } |
| else |
| { |
| src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset)); |
| dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset)); |
| } |
| |
| /* Generate the appropriate load and store, saving the stores for later */ |
| if (bytes >= 8 && TARGET_64BIT && (align >= 8 || !STRICT_ALIGNMENT)) |
| { |
| move_bytes = 8; |
| tmp_reg = gen_reg_rtx (DImode); |
| emit_insn (gen_movdi (tmp_reg, expand_block_move_mem (DImode, src_addr, orig_src))); |
| stores[ num_reg++ ] = gen_movdi (expand_block_move_mem (DImode, dest_addr, orig_dest), tmp_reg); |
| } |
| else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT)) |
| { |
| move_bytes = 4; |
| tmp_reg = gen_reg_rtx (SImode); |
| emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (SImode, src_addr, orig_src))); |
| stores[ num_reg++ ] = gen_movsi (expand_block_move_mem (SImode, dest_addr, orig_dest), tmp_reg); |
| } |
| else if (bytes >= 2 && (align >= 2 || !STRICT_ALIGNMENT)) |
| { |
| move_bytes = 2; |
| tmp_reg = gen_reg_rtx (HImode); |
| emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (HImode, src_addr, orig_src))); |
| stores[ num_reg++ ] = gen_movhi (expand_block_move_mem (HImode, dest_addr, orig_dest), tmp_reg); |
| } |
| else |
| { |
| move_bytes = 1; |
| tmp_reg = gen_reg_rtx (QImode); |
| emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (QImode, src_addr, orig_src))); |
| stores[ num_reg++ ] = gen_movqi (expand_block_move_mem (QImode, dest_addr, orig_dest), tmp_reg); |
| } |
| |
| if (num_reg >= MAX_MOVE_REG) |
| { |
| for (i = 0; i < num_reg; i++) |
| emit_insn (stores[i]); |
| num_reg = 0; |
| } |
| } |
| |
| for (i = 0; i < num_reg; i++) |
| emit_insn (stores[i]); |
| } |
| |
| return 1; |
| } |
| |
| |
| /* Return 1 if OP is a load multiple operation. It is known to be a |
| PARALLEL and the first section will be tested. */ |
| |
| int |
| load_multiple_operation (op, mode) |
| rtx op; |
| enum machine_mode mode; |
| { |
| int count = XVECLEN (op, 0); |
| int dest_regno; |
| rtx src_addr; |
| int i; |
| |
| /* Perform a quick check so we don't blow up below. */ |
| if (count <= 1 |
| || GET_CODE (XVECEXP (op, 0, 0)) != SET |
| || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG |
| || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) |
| return 0; |
| |
| dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); |
| src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); |
| |
| for (i = 1; i < count; i++) |
| { |
| rtx elt = XVECEXP (op, 0, i); |
| |
| if (GET_CODE (elt) != SET |
| || GET_CODE (SET_DEST (elt)) != REG |
| || GET_MODE (SET_DEST (elt)) != SImode |
| || REGNO (SET_DEST (elt)) != dest_regno + i |
| || GET_CODE (SET_SRC (elt)) != MEM |
| || GET_MODE (SET_SRC (elt)) != SImode |
| || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS |
| || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) |
| || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT |
| || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Similar, but tests for store multiple. Here, the second vector element |
| is a CLOBBER. It will be tested later. */ |
| |
| int |
| store_multiple_operation (op, mode) |
| rtx op; |
| enum machine_mode mode; |
| { |
| int count = XVECLEN (op, 0) - 1; |
| int src_regno; |
| rtx dest_addr; |
| int i; |
| |
| /* Perform a quick check so we don't blow up below. */ |
| if (count <= 1 |
| || GET_CODE (XVECEXP (op, 0, 0)) != SET |
| || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM |
| || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) |
| return 0; |
| |
| src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); |
| dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); |
| |
| for (i = 1; i < count; i++) |
| { |
| rtx elt = XVECEXP (op, 0, i + 1); |
| |
| if (GET_CODE (elt) != SET |
| || GET_CODE (SET_SRC (elt)) != REG |
| || GET_MODE (SET_SRC (elt)) != SImode |
| || REGNO (SET_SRC (elt)) != src_regno + i |
| || GET_CODE (SET_DEST (elt)) != MEM |
| || GET_MODE (SET_DEST (elt)) != SImode |
| || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS |
| || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) |
| || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT |
| || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Return 1 if OP is a comparison operation that is valid for a branch insn. |
| We only check the opcode against the mode of the CC value here. */ |
| |
| int |
| branch_comparison_operator (op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| enum rtx_code code = GET_CODE (op); |
| enum machine_mode cc_mode; |
| |
| if (GET_RTX_CLASS (code) != '<') |
| return 0; |
| |
| cc_mode = GET_MODE (XEXP (op, 0)); |
| if (GET_MODE_CLASS (cc_mode) != MODE_CC) |
| return 0; |
| |
| 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; |
| } |
| |
| /* 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; |
| rtx in; |
| { |
| int regno = true_regnum (in); |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| 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; |
| { |
| enum rtx_code code = GET_CODE (op); |
| enum machine_mode cc_mode; |
| int cc_regnum; |
| int base_bit; |
| |
| if (GET_RTX_CLASS (code) != '<') |
| return -1; |
| |
| cc_mode = GET_MODE (XEXP (op, 0)); |
| cc_regnum = REGNO (XEXP (op, 0)); |
| base_bit = 4 * (cc_regnum - 68); |
| |
| /* 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 occurances of register FROM with an new pseduo 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; |
| } |
| |
| 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 pseduo 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 occurrance 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; |
| }; |
| |
| /* 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; |
| } |
| |
| 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; |
| |
| 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") |
| #else |
| #define SMALL_DATA_RELOC "sda21" |
| #endif |
| |
| void |
| print_operand (file, x, code) |
| FILE *file; |
| rtx x; |
| char code; |
| { |
| int i; |
| 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 '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 four. */ |
| if (GET_CODE (XEXP (x, 0)) == PRE_INC |
| || GET_CODE (XEXP (x, 0)) == PRE_DEC) |
| output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4)); |
| else |
| output_address (plus_constant (XEXP (x, 0), 4)); |
| if (small_data_operand (x, GET_MODE (x))) |
| fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, reg_names[0]); |
| } |
| 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 < 0 && (val & 1) == 0) |
| { |
| putc ('0', file); |
| return; |
| } |
| else if (val >= 0) |
| { |
| for (i = 1; i < 32; i++) |
| if ((val <<= 1) < 0) |
| 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 >= 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) >= 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 %%N 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; |
| |
| 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 '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 '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[0]); |
| } |
| return; |
| |
| case 'z': |
| /* X is a SYMBOL_REF. Write out the name preceded by a |
| period and without any trailing data in brackets. Used for function |
| names. 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[0]); |
| } |
| 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[0]); |
| |
| #ifdef TARGET_NO_TOC |
| else if (TARGET_NO_TOC) |
| ; |
| #endif |
| else |
| fprintf (file, "(%s)", reg_names[ TARGET_MINIMAL_TOC ? 30 : 2 ]); |
| } |
| else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG) |
| { |
| if (REGNO (XEXP (x, 0)) == 0) |
| fprintf (file, "%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: |
| |
| SP----> +---------------------------------------+ |
| | back chain to caller | 0 |
| +---------------------------------------+ |
| | saved CR | 4 |
| +---------------------------------------+ |
| | saved LR | 8 |
| +---------------------------------------+ |
| | reserved for compilers | 12 |
| +---------------------------------------+ |
| | reserved for binders | 16 |
| +---------------------------------------+ |
| | saved TOC pointer | 20 |
| +---------------------------------------+ |
| | Parameter save area (P) | 24 |
| +---------------------------------------+ |
| | Alloca space (A) | 24+P |
| +---------------------------------------+ |
| | Local variable space (L) | 24+P+A |
| +---------------------------------------+ |
| | 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 | |
| +---------------------------------------+ |
| |
| 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 | |
| +---------------------------------------+ |
| |
| |
| 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. */ |
| |
| #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_64BIT ? 8 : 4; |
| 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 |