📄 arm.c
字号:
/* Output routines for GCC for ARM/RISCiX. Copyright (C) 1991, 93, 94, 95, 96, 1997 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 (rwe11@cl.cam.ac.uk)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 <stdio.h>#include <string.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"/* The maximum number of insns skipped which will be conditionalised if possible. */#define MAX_INSNS_SKIPPED 5/* Some function declarations. */extern FILE *asm_out_file;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 ((enum rtx_code, rtx, rtx, HOST_WIDE_INT));static HOST_WIDE_INT add_constant PROTO ((rtx, enum machine_mode));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));/* Define the information needed to generate branch insns. This is stored from the compare operation. */rtx arm_compare_op0, arm_compare_op1;int arm_compare_fp;/* What type of cpu are we compiling for? */enum processor_type arm_cpu;/* 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 */char *target_fp_name = NULL;/* 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;/* Set to the features we should tune the code for (multiply speed etc). */int tune_flags = 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;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 ();/* Initialization code */struct arm_cpu_select arm_select[4] ={ /* switch name, tune arch */ { (char *)0, "--with-cpu=", 1, 1 }, { (char *)0, "-mcpu=", 1, 1 }, { (char *)0, "-march=", 0, 1 }, { (char *)0, "-mtune=", 1, 0 },};#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 */struct processors{ char *name; enum processor_type type; 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_procs[] ={ {"arm2", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26}, {"arm250", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26}, {"arm3", PROCESSOR_ARM2, FL_CO_PROC | FL_MODE26}, {"arm6", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26}, {"arm600", PROCESSOR_ARM6, FL_CO_PROC | FL_MODE32 | FL_MODE26}, {"arm610", PROCESSOR_ARM6, FL_MODE32 | FL_MODE26}, {"arm7", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26}, /* arm7m doesn't exist on its own, only in conjunction with D, (and I), but those don't alter the code, so it is sometimes known as the arm7m */ {"arm7m", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_MODE26)}, {"arm7dm", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_MODE26)}, {"arm7dmi", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_MODE26)}, {"arm700", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26}, {"arm710", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26}, {"arm7100", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26}, {"arm7500", PROCESSOR_ARM7, FL_MODE32 | FL_MODE26}, /* Doesn't really have an external co-proc, but does have embedded fpu */ {"arm7500fe", PROCESSOR_ARM7, FL_CO_PROC | FL_MODE32 | FL_MODE26}, {"arm7tdmi", PROCESSOR_ARM7, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_ARCH4 | FL_THUMB)}, {"arm8", PROCESSOR_ARM8, (FL_FAST_MULT | FL_MODE32 | FL_MODE26 | FL_ARCH4)}, {"arm810", PROCESSOR_ARM8, (FL_FAST_MULT | FL_MODE32 | FL_MODE26 | FL_ARCH4)}, {"strongarm", PROCESSOR_STARM, (FL_FAST_MULT | FL_MODE32 | FL_MODE26 | FL_ARCH4)}, {"strongarm110", PROCESSOR_STARM, (FL_FAST_MULT | FL_MODE32 | FL_MODE26 | FL_ARCH4)}, {"armv2", PROCESSOR_NONE, FL_CO_PROC | FL_MODE26}, {"armv2a", PROCESSOR_NONE, FL_CO_PROC | FL_MODE26}, {"armv3", PROCESSOR_NONE, FL_CO_PROC | FL_MODE32 | FL_MODE26}, {"armv3m", PROCESSOR_NONE, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_MODE26)}, {"armv4", PROCESSOR_NONE, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_MODE26 | 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", PROCESSOR_NONE, (FL_CO_PROC | FL_FAST_MULT | FL_MODE32 | FL_ARCH4)}, {NULL, 0, 0}};/* Fix up any incompatible options that the user has specified. This has now turned into a maze. */voidarm_override_options (){ int arm_thumb_aware = 0; int flags = 0; int i; struct arm_cpu_select *ptr; static struct cpu_default { int cpu; char *name; } cpu_defaults[] = { { TARGET_CPU_arm2, "arm2" }, { TARGET_CPU_arm6, "arm6" }, { TARGET_CPU_arm610, "arm610" }, { TARGET_CPU_arm7dm, "arm7dm" }, { TARGET_CPU_arm7500fe, "arm7500fe" }, { TARGET_CPU_arm7tdmi, "arm7tdmi" }, { TARGET_CPU_arm8, "arm8" }, { TARGET_CPU_arm810, "arm810" }, { TARGET_CPU_strongarm, "strongarm" }, { 0, 0 } }; struct cpu_default *def; /* Set the default. */ for (def = &cpu_defaults[0]; def->name; ++def) if (def->cpu == TARGET_CPU_DEFAULT) break; if (! def->name) abort (); arm_select[0].string = def->name; for (i = 0; i < sizeof (arm_select) / sizeof (arm_select[0]); i++) { ptr = &arm_select[i]; if (ptr->string != (char *)0 && ptr->string[0] != '\0') { struct processors *sel; for (sel = all_procs; sel->name != NULL; sel++) if (! strcmp (ptr->string, sel->name)) { /* -march= is the only flag that can take an architecture type, so if we match when the tune bit is set, the option was invalid. */ if (ptr->set_tune_p) { if (sel->type == PROCESSOR_NONE) continue; /* Its an architecture, not a cpu */ arm_cpu = sel->type; tune_flags = sel->flags; } if (ptr->set_arch_p) flags = sel->flags; break; } if (sel->name == NULL) error ("bad value (%s) for %s switch", ptr->string, ptr->name); } } 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; if (TARGET_6) warning ("Option '-m6' deprecated. Use: '-mapcs-32' or -mcpu=<proc>"); if (TARGET_3) warning ("Option '-m3' deprecated. Use: '-mapcs-26' or -mcpu=<proc>"); if (TARGET_APCS_REENT && flag_pic) fatal ("-fpic and -mapcs-reent are incompatible"); if (TARGET_APCS_REENT) warning ("APCS reentrant code not supported."); /* If stack checking is disabled, we can use r10 as the PIC register, which keeps r9 available. */ if (flag_pic && ! TARGET_APCS_STACK) arm_pic_register = 10; /* Well, I'm about to have a go, but pic is NOT going to be compatible with APCS reentrancy, since that requires too much support in the assembler and linker, and the ARMASM assembler seems to lack some required directives. */ if (flag_pic) warning ("Position independent code not supported. Ignored"); if (TARGET_APCS_FLOAT) warning ("Passing floating point arguments in fp regs not yet supported"); if (TARGET_APCS_STACK && ! TARGET_APCS) { warning ("-mapcs-stack-check incompatible with -mno-apcs-frame"); target_flags |= ARM_FLAG_APCS_FRAME; } /* Default is to tune for an FPA */ arm_fpu = FP_HARD; /* Default value for floating point code... if no co-processor bus, then schedule for emulated floating point. Otherwise, assume the user has an FPA. Note: this does not prevent use of floating point instructions, -msoft-float does that. */ if (tune_flags & FL_CO_PROC == 0) arm_fpu = FP_SOFT3; arm_fast_multiply = (flags & FL_FAST_MULT) != 0; arm_arch4 = (flags & FL_ARCH4) != 0; arm_thumb_aware = (flags & FL_THUMB) != 0; if (target_fp_name) { if (strcmp (target_fp_name, "2") == 0) arm_fpu_arch = FP_SOFT2; else if (strcmp (target_fp_name, "3") == 0) arm_fpu_arch = FP_HARD; else fatal ("Invalid floating point emulation option: -mfpe=%s", target_fp_name); } else arm_fpu_arch = FP_DEFAULT; if (TARGET_THUMB_INTERWORK && ! arm_thumb_aware) { warning ("This processor variant does not support Thumb interworking"); target_flags &= ~ARM_FLAG_THUMB; } if (TARGET_FPE && arm_fpu != FP_HARD) arm_fpu = FP_SOFT2; /* For arm2/3 there is no need to do any scheduling if there is only a floating point emulator, or we are doing software floating-point. */ if ((TARGET_SOFT_FLOAT || arm_fpu != FP_HARD) && arm_cpu == PROCESSOR_ARM2) flag_schedule_insns = flag_schedule_insns_after_reload = 0; arm_prog_mode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26;}/* Return 1 if it is possible to return using a single instruction */intuse_return_insn (){ int regno; if (!reload_completed ||current_function_pretend_args_size || current_function_anonymous_args || ((get_frame_size () + current_function_outgoing_args_size != 0) && !(TARGET_APCS || frame_pointer_needed))) return 0; /* Can't be done if interworking with Thumb, and any registers have been stacked */ if (TARGET_THUMB_INTERWORK) for (regno = 0; regno < 16; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) return 0; /* Can't be done if any of the FPU regs are pushed, since this also requires an insn */ for (regno = 16; regno < 24; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) return 0; /* If a function is naked, don't use the "return" insn. */ if (arm_naked_function_p (current_function_decl)) return 0; return 1;}/* Return TRUE if int I is a valid immediate ARM constant. */intconst_ok_for_arm (i) HOST_WIDE_INT i;{ unsigned HOST_WIDE_INT mask = ~0xFF; /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must be all zero, or all one. */ if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0 && ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != (((HOST_WIDE_INT) -1) & ~(unsigned HOST_WIDE_INT) 0xffffffff))) return FALSE; /* Fast return for 0 and powers of 2 */ if ((i & (i - 1)) == 0) return TRUE; do { if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) return TRUE; mask = (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) >> (32 - 2)) | ~((unsigned HOST_WIDE_INT) 0xffffffff); } while (mask != ~0xFF); return FALSE;}/* Return true if I is a valid constant for the operation CODE. */intconst_ok_for_op (i, code, mode) HOST_WIDE_INT i; enum rtx_code code; enum machine_mode mode;{ if (const_ok_for_arm (i)) return 1; switch (code) { case PLUS: return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); case MINUS: /* Should only occur with (MINUS I reg) => rsb */ case XOR: case IOR: return 0; case AND: return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); default: abort (); }}/* Emit a sequence of insns to handle a large constant. CODE is the code of the operation required, it can be any of SET, PLUS, IOR, AND, XOR, MINUS; MODE is the mode in which the operation is being performed; VAL is the integer to operate on; SOURCE is the other operand (a register, or a null-pointer for SET); SUBTARGETS means it is safe to create scratch registers if that will either produce a simpler sequence, or we will want to cse the values. Return value is the number of insns emitted. */intarm_split_constant (code, mode, val, target, source, subtargets) enum rtx_code code; enum machine_mode mode; HOST_WIDE_INT val; rtx target; rtx source; int subtargets;{ if (subtargets || code == SET || (GET_CODE (target) == REG && GET_CODE (source) == REG && REGNO (target) != REGNO (source))) { rtx temp; if (arm_gen_constant (code, mode, val, target, source, 1, 0) > arm_constant_limit + (code != SET)) { if (code == SET) { /* Currently SET is the only monadic value for CODE, all
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -