📄 arm.c
字号:
/* Output routines for GCC for ARM. Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) and Martin Simmons (@harleqn.co.uk). More major hacks by Richard Earnshaw (rearnsha@arm.com).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 "config.h"#include "system.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "insn-flags.h"#include "output.h"#include "insn-attr.h"#include "flags.h"#include "reload.h"#include "tree.h"#include "expr.h"#include "toplev.h"#include "recog.h"/* The maximum number of insns skipped which will be conditionalised if possible. */static int max_insns_skipped = 5;extern FILE * asm_out_file;/* Some function declarations. */static HOST_WIDE_INT int_log2 PROTO ((HOST_WIDE_INT));static char * output_multi_immediate PROTO ((rtx *, char *, char *, int, HOST_WIDE_INT));static int arm_gen_constant PROTO ((enum rtx_code, enum machine_mode, HOST_WIDE_INT, rtx, rtx, int, int));static int arm_naked_function_p PROTO ((tree));static void init_fpa_table PROTO ((void));static enum machine_mode select_dominance_cc_mode PROTO ((rtx, rtx, HOST_WIDE_INT));static HOST_WIDE_INT add_constant PROTO ((rtx, enum machine_mode, int *));static void dump_table PROTO ((rtx));static int fixit PROTO ((rtx, enum machine_mode, int));static rtx find_barrier PROTO ((rtx, int));static int broken_move PROTO ((rtx));static char * fp_const_from_val PROTO ((REAL_VALUE_TYPE *));static int eliminate_lr2ip PROTO ((rtx *));static char * shift_op PROTO ((rtx, HOST_WIDE_INT *));static int pattern_really_clobbers_lr PROTO ((rtx));static int function_really_clobbers_lr PROTO ((rtx));static void emit_multi_reg_push PROTO ((int));static void emit_sfm PROTO ((int, int));static enum arm_cond_code get_arm_condition_code PROTO ((rtx));static int const_ok_for_op RTX_CODE_PROTO ((HOST_WIDE_INT, Rcode));/* True if we are currently building a constant table. */int making_const_table;/* Define the information needed to generate branch insns. This is stored from the compare operation. */rtx arm_compare_op0, arm_compare_op1;/* What type of floating point are we tuning for? */enum floating_point_type arm_fpu;/* What type of floating point instructions are available? */enum floating_point_type arm_fpu_arch;/* What program mode is the cpu running in? 26-bit mode or 32-bit mode */enum prog_mode_type arm_prgmode;/* Set by the -mfp=... option */const char * target_fp_name = NULL;/* Used to parse -mstructure_size_boundary command line option. */const char * structure_size_string = NULL;int arm_structure_size_boundary = 32; /* Used to be 8 *//* Bit values used to identify processor capabilities. */#define FL_CO_PROC 0x01 /* Has external co-processor bus */#define FL_FAST_MULT 0x02 /* Fast multiply */#define FL_MODE26 0x04 /* 26-bit mode support */#define FL_MODE32 0x08 /* 32-bit mode support */#define FL_ARCH4 0x10 /* Architecture rel 4 */#define FL_THUMB 0x20 /* Thumb aware */#define FL_LDSCHED 0x40 /* Load scheduling necessary */#define FL_STRONG 0x80 /* StrongARM *//* The bits in this mask specify which instructions we are allowed to generate. */static int insn_flags = 0;/* The bits in this mask specify which instruction scheduling options should be used. Note - there is an overlap with the FL_FAST_MULT. For some hardware we want to be able to generate the multiply instructions, but to tune as if they were not present in the architecture. */static int tune_flags = 0;/* The following are used in the arm.md file as equivalents to bits in the above two flag variables. *//* Nonzero if this is an "M" variant of the processor. */int arm_fast_multiply = 0;/* Nonzero if this chip supports the ARM Architecture 4 extensions */int arm_arch4 = 0;/* Nonzero if this chip can benefit from load scheduling. */int arm_ld_sched = 0;/* Nonzero if this chip is a StrongARM. */int arm_is_strong = 0;/* Nonzero if this chip is a an ARM6 or an ARM7. */int arm_is_6_or_7 = 0;/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we must report the mode of the memory reference from PRINT_OPERAND to PRINT_OPERAND_ADDRESS. */enum machine_mode output_memory_reference_mode;/* Nonzero if the prologue must setup `fp'. */int current_function_anonymous_args;/* The register number to be used for the PIC offset register. */int arm_pic_register = 9;/* Location counter of .text segment. */int arm_text_location = 0;/* Set to one if we think that lr is only saved because of subroutine calls, but all of these can be `put after' return insns */int lr_save_eliminated;/* Set to 1 when a return insn is output, this means that the epilogue is not needed. */static int return_used_this_function;/* Set to 1 after arm_reorg has started. Reset to start at the start of the next function. */static int after_arm_reorg = 0;/* The maximum number of insns to be used when loading a constant. */static int arm_constant_limit = 3;/* For an explanation of these variables, see final_prescan_insn below. */int arm_ccfsm_state;enum arm_cond_code arm_current_cc;rtx arm_target_insn;int arm_target_label;/* The condition codes of the ARM, and the inverse function. */char * arm_condition_codes[] ={ "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"};static enum arm_cond_code get_arm_condition_code ();#define streq(string1, string2) (strcmp (string1, string2) == 0)/* Initialization code */struct processors{ char * name; unsigned int flags;};/* Not all of these give usefully different compilation alternatives, but there is no simple way of generalizing them. */static struct processors all_cores[] ={ /* ARM Cores */ {"arm2", FL_CO_PROC | FL_MODE26 }, {"arm250", FL_CO_PROC | FL_MODE26 }, {"arm3", FL_CO_PROC | FL_MODE26 }, {"arm6", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm60", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm600", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm610", FL_MODE26 | FL_MODE32 }, {"arm620", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm7", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm7m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, /* arm7m doesn't exist on its own, */ {"arm7d", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, /* but only with D, (and I), */ {"arm7dm", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, /* but those don't alter the code, */ {"arm7di", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, /* so arm7m is sometimes used. */ {"arm7dmi", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, {"arm70", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"arm710", FL_MODE26 | FL_MODE32 }, {"arm710c", FL_MODE26 | FL_MODE32 }, {"arm7100", FL_MODE26 | FL_MODE32 }, {"arm7500", FL_MODE26 | FL_MODE32 }, {"arm7500fe", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, /* Doesn't really have an external co-proc, but does have embedded fpu. */ {"arm7tdmi", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, {"arm8", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, {"arm810", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, {"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, {"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, {"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, {"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, {"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, {NULL, 0}};static struct processors all_architectures[] ={ /* ARM Architectures */ {"armv2", FL_CO_PROC | FL_MODE26 }, {"armv2a", FL_CO_PROC | FL_MODE26 }, {"armv3", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, {"armv3m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, {"armv4", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 }, /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no implementations that support it, so we will leave it out for now. */ {"armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, {NULL, 0}};/* This is a magic stucture. The 'string' field is magically filled in with a pointer to the value specified by the user on the command line assuming that the user has specified such a value. */struct arm_cpu_select arm_select[] ={ /* string name processors */ { NULL, "-mcpu=", all_cores }, { NULL, "-march=", all_architectures }, { NULL, "-mtune=", all_cores }};/* Return the number of bits set in value' */static unsigned intbit_count (value) signed int value;{ unsigned int count = 0; while (value) { value &= ~(value & - value); ++ count; } return count;}/* Fix up any incompatible options that the user has specified. This has now turned into a maze. */voidarm_override_options (){ unsigned i; /* Set up the flags based on the cpu/architecture selected by the user. */ for (i = sizeof (arm_select) / sizeof (arm_select[0]); i--;) { struct arm_cpu_select * ptr = arm_select + i; if (ptr->string != NULL && ptr->string[0] != '\0') { struct processors * sel; for (sel = ptr->processors; sel->name != NULL; sel ++) if (streq (ptr->string, sel->name)) { if (i == 2) tune_flags = sel->flags; else { /* If we have been given an architecture and a processor make sure that they are compatible. We only generate a warning though, and we prefer the CPU over the architecture. */ if (insn_flags != 0 && (insn_flags ^ sel->flags)) warning ("switch -mcpu=%s conflicts with -mtune= switch", ptr->string); insn_flags = sel->flags; } break; } if (sel->name == NULL) error ("bad value (%s) for %s switch", ptr->string, ptr->name); } } /* If the user did not specify a processor, choose one for them. */ if (insn_flags == 0) { struct processors * sel; unsigned int sought; static struct cpu_default { int cpu; char * name; } cpu_defaults[] = { { TARGET_CPU_arm2, "arm2" }, { TARGET_CPU_arm6, "arm6" }, { TARGET_CPU_arm610, "arm610" }, { TARGET_CPU_arm710, "arm710" }, { TARGET_CPU_arm7m, "arm7m" }, { TARGET_CPU_arm7500fe, "arm7500fe" }, { TARGET_CPU_arm7tdmi, "arm7tdmi" }, { TARGET_CPU_arm8, "arm8" }, { TARGET_CPU_arm810, "arm810" }, { TARGET_CPU_arm9, "arm9" }, { TARGET_CPU_strongarm, "strongarm" }, { TARGET_CPU_generic, "arm" }, { 0, 0 } }; struct cpu_default * def; /* Find the default. */ for (def = cpu_defaults; def->name; def ++) if (def->cpu == TARGET_CPU_DEFAULT) break; /* Make sure we found the default CPU. */ if (def->name == NULL) abort (); /* Find the default CPU's flags. */ for (sel = all_cores; sel->name != NULL; sel ++) if (streq (def->name, sel->name)) break; if (sel->name == NULL) abort (); insn_flags = sel->flags; /* Now check to see if the user has specified some command line switch that require certain abilities from the cpu. */ sought = 0; if (TARGET_THUMB_INTERWORK) { sought |= (FL_THUMB | FL_MODE32); /* Force apcs-32 to be used for interworking. */ target_flags |= ARM_FLAG_APCS_32; /* There are no ARM processor that supports both APCS-26 and interworking. Therefore we force FL_MODE26 to be removed from insn_flags here (if it was set), so that the search below will always be able to find a compatible processor. */ insn_flags &= ~ FL_MODE26; } if (! TARGET_APCS_32) sought |= FL_MODE26; if (sought != 0 && ((sought & insn_flags) != sought)) { /* Try to locate a CPU type that supports all of the abilities of the default CPU, plus the extra abilities requested by the user. */ for (sel = all_cores; sel->name != NULL; sel ++) if ((sel->flags & sought) == (sought | insn_flags)) break; if (sel->name == NULL) { unsigned int current_bit_count = 0; struct processors * best_fit = NULL; /* Ideally we would like to issue an error message here saying that it was not possible to find a CPU compatible with the default CPU, but which also supports the command line options specified by the programmer, and so they ought to use the -mcpu=<name> command line option to override the default CPU type. Unfortunately this does not work with multilibing. We need to be able to support multilibs for -mapcs-26 and for -mthumb-interwork and there is no CPU that can support both options. Instead if we cannot find a cpu that has both the characteristics of the default cpu and the given command line options we scan the array again looking for a best match. */ for (sel = all_cores; sel->name != NULL; sel ++) if ((sel->flags & sought) == sought) { unsigned int count; count = bit_count (sel->flags & insn_flags); if (count >= current_bit_count) { best_fit = sel; current_bit_count = count; } } if (best_fit == NULL) abort (); else sel = best_fit; } insn_flags = sel->flags; } } /* If tuning has not been specified, tune for whichever processor or architecture has been selected. */ if (tune_flags == 0) tune_flags = insn_flags; /* Make sure that the processor choice does not conflict with any of the other command line choices. */ if (TARGET_APCS_32 && !(insn_flags & FL_MODE32)) { /* If APCS-32 was not the default then it must have been set by the user, so issue a warning message. If the user has specified "-mapcs-32 -mcpu=arm2" then we loose here. */ if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0) warning ("target CPU does not support APCS-32" ); target_flags &= ~ ARM_FLAG_APCS_32; } else if (! TARGET_APCS_32 && !(insn_flags & FL_MODE26)) { warning ("target CPU does not support APCS-26" ); target_flags |= ARM_FLAG_APCS_32; } if (TARGET_THUMB_INTERWORK && !(insn_flags & FL_THUMB)) { warning ("target CPU does not support interworking" ); target_flags &= ~ARM_FLAG_THUMB; } /* If interworking is enabled then APCS-32 must be selected as well. */ if (TARGET_THUMB_INTERWORK) { if (! TARGET_APCS_32) warning ("interworking forces APCS-32 to be used" ); target_flags |= ARM_FLAG_APCS_32; } if (TARGET_APCS_STACK && ! TARGET_APCS) { warning ("-mapcs-stack-check incompatible with -mno-apcs-frame"); target_flags |= ARM_FLAG_APCS_FRAME; } if (write_symbols != NO_DEBUG && flag_omit_frame_pointer) warning ("-g with -fomit-frame-pointer may not give sensible debugging"); if (TARGET_POKE_FUNCTION_NAME) target_flags |= ARM_FLAG_APCS_FRAME;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -