📄 rs6000.c
字号:
/* 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 modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe 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#endifextern 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_REGNAMESstatic 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. */voidrs6000_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. */voidrs6000_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. */intdirect_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. */intany_operand (op, mode) register rtx op; enum machine_mode mode;{ return 1;}/* Returns 1 if op is the count register */intcount_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. */intfpmem_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -