📄 rs6000.c
字号:
/* Subroutines used for code generation on IBM RS/6000. Copyright (C) 1991, 1993, 1994, 1995 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"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;char *rs6000_cpu_string;/* 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;#endif/* Whether a System V.4 varargs area was created. */int rs6000_sysv_varargs_p;/* Temporary memory used to convert integer -> float */static rtx stack_temps[NUM_MACHINE_MODES];/* Print the options used in the assembly file. */extern char *version_string, *language_string;struct asm_option{ char *string; int *variable; int on_value;};#define MAX_LINE 79static intoutput_option (file, type, name, pos) FILE *file; char *type; char *name; int pos;{ int type_len = strlen (type); int name_len = strlen (name); if (1 + type_len + name_len + pos > MAX_LINE) { fprintf (file, "\n # %s%s", type, name); return 3 + type_len + name_len; } fprintf (file, " %s%s", type, name); return pos + 1 + type_len + name_len;}static struct { char *name; int value; } m_options[] = TARGET_SWITCHES;voidoutput_options (file, f_options, f_len, W_options, W_len) FILE *file; struct asm_option *f_options; int f_len; struct asm_option *W_options; int W_len;{ int j; int flags = target_flags; int pos = 32767; fprintf (file, " # %s %s", language_string, version_string); if (optimize) { char opt_string[20]; sprintf (opt_string, "%d", optimize); pos = output_option (file, "-O", opt_string, pos); } if (profile_flag) pos = output_option (file, "-p", "", pos); if (profile_block_flag) pos = output_option (file, "-a", "", pos); if (inhibit_warnings) pos = output_option (file, "-w", "", pos); for (j = 0; j < f_len; j++) { if (*f_options[j].variable == f_options[j].on_value) pos = output_option (file, "-f", f_options[j].string, pos); } for (j = 0; j < W_len; j++) { if (*W_options[j].variable == W_options[j].on_value) pos = output_option (file, "-W", W_options[j].string, pos); } for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) { if (m_options[j].name[0] != '\0' && m_options[j].value > 0 && ((m_options[j].value & flags) == m_options[j].value)) { pos = output_option (file, "-m", m_options[j].name, pos); flags &= ~ m_options[j].value; } } if (rs6000_cpu_string != (char *)0) pos = output_option (file, "-mcpu=", rs6000_cpu_string, pos); fputs ("\n\n", file);}/* Override command line options. Mostly we process the processor type and sometimes adjust other TARGET_ options. */voidrs6000_override_options (){ int i; /* 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, 0, POWER_MASKS | POWERPC_MASKS}, {"power", PROCESSOR_POWER, MASK_POWER | MASK_MULTIPLE | MASK_STRING, MASK_POWER2 | 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, 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}, {"603", 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}}; 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 */ if (rs6000_cpu_string == 0) rs6000_cpu = PROCESSOR_DEFAULT; else { for (i = 0; i < ptt_size; i++) if (! strcmp (rs6000_cpu_string, processor_target_table[i].name)) { rs6000_cpu = processor_target_table[i].processor; target_flags |= processor_target_table[i].target_enable; target_flags &= ~processor_target_table[i].target_disable; break; } if (i == ptt_size) { error ("bad value (%s) for -mcpu= switch", rs6000_cpu_string); rs6000_cpu_string = "default"; rs6000_cpu = PROCESSOR_DEFAULT; } } /* 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"); } }#ifdef SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS;#endif}/* 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;}/* Return 1 if OP is a constant that can fit in a D field. */intshort_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (unsigned) (INTVAL (op) + 0x8000) < 0x10000);}/* Similar for a unsigned D field. */intu_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. */intnon_short_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (unsigned) (INTVAL (op) + 0x8000) >= 0x10000);}/* Returns 1 if OP is a register that is not special (i.e., not MQ, ctr, or lr). */intgpc_reg_operand (op, mode) register rtx op; enum machine_mode mode;{ return (register_operand (op, mode) && (GET_CODE (op) != REG || REGNO (op) >= 67 || REGNO (op) < 64));}/* Returns 1 if OP is either a pseudo-register or a register denoting a CR field. */intcc_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. */intreg_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. */intreg_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. */intreg_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. */intreg_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 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}. */inteasy_fp_constant (op, mode) register rtx op; register enum machine_mode mode;{ rtx low, high; if (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != mode || GET_MODE_CLASS (mode) != MODE_FLOAT) return 0; high = operand_subword (op, 0, 0, mode);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -