arm.c revision 96263
190075Sobrien/* Output routines for GCC for ARM. 290075Sobrien Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 390075Sobrien Free Software Foundation, Inc. 490075Sobrien Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) 590075Sobrien and Martin Simmons (@harleqn.co.uk). 690075Sobrien More major hacks by Richard Earnshaw (rearnsha@arm.com). 790075Sobrien 890075SobrienThis file is part of GNU CC. 990075Sobrien 1090075SobrienGNU CC is free software; you can redistribute it and/or modify 1190075Sobrienit under the terms of the GNU General Public License as published by 1290075Sobrienthe Free Software Foundation; either version 2, or (at your option) 1390075Sobrienany later version. 1490075Sobrien 1590075SobrienGNU CC is distributed in the hope that it will be useful, 1690075Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 1790075SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1890075SobrienGNU General Public License for more details. 1990075Sobrien 2090075SobrienYou should have received a copy of the GNU General Public License 2190075Sobrienalong with GNU CC; see the file COPYING. If not, write to 2290075Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330, 2390075SobrienBoston, MA 02111-1307, USA. */ 2490075Sobrien 2590075Sobrien#include "config.h" 2690075Sobrien#include "system.h" 2790075Sobrien#include "rtl.h" 2890075Sobrien#include "tree.h" 2990075Sobrien#include "obstack.h" 3090075Sobrien#include "regs.h" 3190075Sobrien#include "hard-reg-set.h" 3290075Sobrien#include "real.h" 3390075Sobrien#include "insn-config.h" 3490075Sobrien#include "conditions.h" 3590075Sobrien#include "output.h" 3690075Sobrien#include "insn-attr.h" 3790075Sobrien#include "flags.h" 3890075Sobrien#include "reload.h" 3990075Sobrien#include "function.h" 4090075Sobrien#include "expr.h" 4190075Sobrien#include "optabs.h" 4290075Sobrien#include "toplev.h" 4390075Sobrien#include "recog.h" 4490075Sobrien#include "ggc.h" 4590075Sobrien#include "except.h" 4690075Sobrien#include "c-pragma.h" 4790075Sobrien#include "integrate.h" 4890075Sobrien#include "tm_p.h" 4990075Sobrien#include "target.h" 5090075Sobrien#include "target-def.h" 5190075Sobrien 5290075Sobrien/* Forward definitions of types. */ 5390075Sobrientypedef struct minipool_node Mnode; 5490075Sobrientypedef struct minipool_fixup Mfix; 5590075Sobrien 5690075Sobrien/* In order to improve the layout of the prototypes below 5790075Sobrien some short type abbreviations are defined here. */ 5890075Sobrien#define Hint HOST_WIDE_INT 5990075Sobrien#define Mmode enum machine_mode 6090075Sobrien#define Ulong unsigned long 6190075Sobrien#define Ccstar const char * 6290075Sobrien 6390075Sobrienconst struct attribute_spec arm_attribute_table[]; 6490075Sobrien 6590075Sobrien/* Forward function declarations. */ 6690075Sobrienstatic void arm_add_gc_roots PARAMS ((void)); 6790075Sobrienstatic int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int)); 6890075Sobrienstatic Ulong bit_count PARAMS ((signed int)); 6990075Sobrienstatic int const_ok_for_op PARAMS ((Hint, enum rtx_code)); 7090075Sobrienstatic int eliminate_lr2ip PARAMS ((rtx *)); 7190075Sobrienstatic rtx emit_multi_reg_push PARAMS ((int)); 7290075Sobrienstatic rtx emit_sfm PARAMS ((int, int)); 7390075Sobrien#ifndef AOF_ASSEMBLER 7490075Sobrienstatic bool arm_assemble_integer PARAMS ((rtx, unsigned int, int)); 7590075Sobrien#endif 7690075Sobrienstatic Ccstar fp_const_from_val PARAMS ((REAL_VALUE_TYPE *)); 7790075Sobrienstatic arm_cc get_arm_condition_code PARAMS ((rtx)); 7890075Sobrienstatic void init_fpa_table PARAMS ((void)); 7990075Sobrienstatic Hint int_log2 PARAMS ((Hint)); 8090075Sobrienstatic rtx is_jump_table PARAMS ((rtx)); 8190075Sobrienstatic Ccstar output_multi_immediate PARAMS ((rtx *, Ccstar, Ccstar, int, Hint)); 8290075Sobrienstatic void print_multi_reg PARAMS ((FILE *, Ccstar, int, int)); 8390075Sobrienstatic Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint)); 8490075Sobrienstatic Ccstar shift_op PARAMS ((rtx, Hint *)); 8590075Sobrienstatic void arm_init_machine_status PARAMS ((struct function *)); 8690075Sobrienstatic void arm_mark_machine_status PARAMS ((struct function *)); 8790075Sobrienstatic void arm_free_machine_status PARAMS ((struct function *)); 8890075Sobrienstatic int number_of_first_bit_set PARAMS ((int)); 8990075Sobrienstatic void replace_symbols_in_block PARAMS ((tree, rtx, rtx)); 9090075Sobrienstatic void thumb_exit PARAMS ((FILE *, int, rtx)); 9190075Sobrienstatic void thumb_pushpop PARAMS ((FILE *, int, int)); 9290075Sobrienstatic Ccstar thumb_condition_code PARAMS ((rtx, int)); 9390075Sobrienstatic rtx is_jump_table PARAMS ((rtx)); 9490075Sobrienstatic Hint get_jump_table_size PARAMS ((rtx)); 9590075Sobrienstatic Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint)); 9690075Sobrienstatic Mnode * add_minipool_forward_ref PARAMS ((Mfix *)); 9790075Sobrienstatic Mnode * move_minipool_fix_backward_ref PARAMS ((Mnode *, Mnode *, Hint)); 9890075Sobrienstatic Mnode * add_minipool_backward_ref PARAMS ((Mfix *)); 9990075Sobrienstatic void assign_minipool_offsets PARAMS ((Mfix *)); 10090075Sobrienstatic void arm_print_value PARAMS ((FILE *, rtx)); 10190075Sobrienstatic void dump_minipool PARAMS ((rtx)); 10290075Sobrienstatic int arm_barrier_cost PARAMS ((rtx)); 10390075Sobrienstatic Mfix * create_fix_barrier PARAMS ((Mfix *, Hint)); 10490075Sobrienstatic void push_minipool_barrier PARAMS ((rtx, Hint)); 10590075Sobrienstatic void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx)); 10690075Sobrienstatic void note_invalid_constants PARAMS ((rtx, Hint)); 10790075Sobrienstatic int current_file_function_operand PARAMS ((rtx)); 10890075Sobrienstatic Ulong arm_compute_save_reg0_reg12_mask PARAMS ((void)); 10990075Sobrienstatic Ulong arm_compute_save_reg_mask PARAMS ((void)); 11090075Sobrienstatic Ulong arm_isr_value PARAMS ((tree)); 11190075Sobrienstatic Ulong arm_compute_func_type PARAMS ((void)); 11290075Sobrienstatic tree arm_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *)); 11390075Sobrienstatic tree arm_handle_isr_attribute PARAMS ((tree *, tree, tree, int, bool *)); 11490075Sobrienstatic void arm_output_function_epilogue PARAMS ((FILE *, Hint)); 11590075Sobrienstatic void arm_output_function_prologue PARAMS ((FILE *, Hint)); 11690075Sobrienstatic void thumb_output_function_prologue PARAMS ((FILE *, Hint)); 11790075Sobrienstatic int arm_comp_type_attributes PARAMS ((tree, tree)); 11890075Sobrienstatic void arm_set_default_type_attributes PARAMS ((tree)); 11990075Sobrienstatic int arm_adjust_cost PARAMS ((rtx, rtx, rtx, int)); 12090075Sobrien#ifdef OBJECT_FORMAT_ELF 12190075Sobrienstatic void arm_elf_asm_named_section PARAMS ((const char *, unsigned int)); 12290075Sobrien#endif 12390075Sobrien 12490075Sobrien#undef Hint 12590075Sobrien#undef Mmode 12690075Sobrien#undef Ulong 12790075Sobrien#undef Ccstar 12890075Sobrien 12990075Sobrien/* Initialize the GCC target structure. */ 13090075Sobrien#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES 13190075Sobrien#undef TARGET_MERGE_DECL_ATTRIBUTES 13290075Sobrien#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes 13390075Sobrien#endif 13490075Sobrien 13590075Sobrien#undef TARGET_ATTRIBUTE_TABLE 13690075Sobrien#define TARGET_ATTRIBUTE_TABLE arm_attribute_table 13790075Sobrien 13890075Sobrien#ifdef AOF_ASSEMBLER 13990075Sobrien#undef TARGET_ASM_BYTE_OP 14090075Sobrien#define TARGET_ASM_BYTE_OP "\tDCB\t" 14190075Sobrien#undef TARGET_ASM_ALIGNED_HI_OP 14290075Sobrien#define TARGET_ASM_ALIGNED_HI_OP "\tDCW\t" 14390075Sobrien#undef TARGET_ASM_ALIGNED_SI_OP 14490075Sobrien#define TARGET_ASM_ALIGNED_SI_OP "\tDCD\t" 14590075Sobrien#else 14690075Sobrien#undef TARGET_ASM_ALIGNED_SI_OP 14790075Sobrien#define TARGET_ASM_ALIGNED_SI_OP NULL 14890075Sobrien#undef TARGET_ASM_INTEGER 14990075Sobrien#define TARGET_ASM_INTEGER arm_assemble_integer 15090075Sobrien#endif 15190075Sobrien 15290075Sobrien#undef TARGET_ASM_FUNCTION_PROLOGUE 15390075Sobrien#define TARGET_ASM_FUNCTION_PROLOGUE arm_output_function_prologue 15490075Sobrien 15590075Sobrien#undef TARGET_ASM_FUNCTION_EPILOGUE 15690075Sobrien#define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue 15790075Sobrien 15890075Sobrien#undef TARGET_COMP_TYPE_ATTRIBUTES 15990075Sobrien#define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes 16090075Sobrien 16190075Sobrien#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES 16290075Sobrien#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes 16390075Sobrien 16490075Sobrien#undef TARGET_INIT_BUILTINS 16590075Sobrien#define TARGET_INIT_BUILTINS arm_init_builtins 16690075Sobrien 16790075Sobrien#undef TARGET_EXPAND_BUILTIN 16890075Sobrien#define TARGET_EXPAND_BUILTIN arm_expand_builtin 16990075Sobrien 17090075Sobrien#undef TARGET_SCHED_ADJUST_COST 17190075Sobrien#define TARGET_SCHED_ADJUST_COST arm_adjust_cost 17290075Sobrien 17390075Sobrienstruct gcc_target targetm = TARGET_INITIALIZER; 17490075Sobrien 17590075Sobrien/* Obstack for minipool constant handling. */ 17690075Sobrienstatic struct obstack minipool_obstack; 17790075Sobrienstatic char * minipool_startobj; 17890075Sobrien 17990075Sobrien#define obstack_chunk_alloc xmalloc 18090075Sobrien#define obstack_chunk_free free 18190075Sobrien 18290075Sobrien/* The maximum number of insns skipped which 18390075Sobrien will be conditionalised if possible. */ 18490075Sobrienstatic int max_insns_skipped = 5; 18590075Sobrien 18690075Sobrienextern FILE * asm_out_file; 18790075Sobrien 18890075Sobrien/* True if we are currently building a constant table. */ 18990075Sobrienint making_const_table; 19090075Sobrien 19190075Sobrien/* Define the information needed to generate branch insns. This is 19290075Sobrien stored from the compare operation. */ 19390075Sobrienrtx arm_compare_op0, arm_compare_op1; 19490075Sobrien 19590075Sobrien/* What type of floating point are we tuning for? */ 19690075Sobrienenum floating_point_type arm_fpu; 19790075Sobrien 19890075Sobrien/* What type of floating point instructions are available? */ 19990075Sobrienenum floating_point_type arm_fpu_arch; 20090075Sobrien 20190075Sobrien/* What program mode is the cpu running in? 26-bit mode or 32-bit mode. */ 20290075Sobrienenum prog_mode_type arm_prgmode; 20390075Sobrien 20490075Sobrien/* Set by the -mfp=... option. */ 20590075Sobrienconst char * target_fp_name = NULL; 20690075Sobrien 20790075Sobrien/* Used to parse -mstructure_size_boundary command line option. */ 20890075Sobrienconst char * structure_size_string = NULL; 20990075Sobrienint arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY; 21090075Sobrien 21190075Sobrien/* Bit values used to identify processor capabilities. */ 21290075Sobrien#define FL_CO_PROC (1 << 0) /* Has external co-processor bus */ 21390075Sobrien#define FL_FAST_MULT (1 << 1) /* Fast multiply */ 21490075Sobrien#define FL_MODE26 (1 << 2) /* 26-bit mode support */ 21590075Sobrien#define FL_MODE32 (1 << 3) /* 32-bit mode support */ 21690075Sobrien#define FL_ARCH4 (1 << 4) /* Architecture rel 4 */ 21790075Sobrien#define FL_ARCH5 (1 << 5) /* Architecture rel 5 */ 21890075Sobrien#define FL_THUMB (1 << 6) /* Thumb aware */ 21990075Sobrien#define FL_LDSCHED (1 << 7) /* Load scheduling necessary */ 22090075Sobrien#define FL_STRONG (1 << 8) /* StrongARM */ 22190075Sobrien#define FL_ARCH5E (1 << 9) /* DSP extenstions to v5 */ 22290075Sobrien#define FL_XSCALE (1 << 10) /* XScale */ 22390075Sobrien 22490075Sobrien/* The bits in this mask specify which 22590075Sobrien instructions we are allowed to generate. */ 22690075Sobrienstatic int insn_flags = 0; 22790075Sobrien 22890075Sobrien/* The bits in this mask specify which instruction scheduling options should 22990075Sobrien be used. Note - there is an overlap with the FL_FAST_MULT. For some 23090075Sobrien hardware we want to be able to generate the multiply instructions, but to 23190075Sobrien tune as if they were not present in the architecture. */ 23290075Sobrienstatic int tune_flags = 0; 23390075Sobrien 23490075Sobrien/* The following are used in the arm.md file as equivalents to bits 23590075Sobrien in the above two flag variables. */ 23690075Sobrien 23790075Sobrien/* Nonzero if this is an "M" variant of the processor. */ 23890075Sobrienint arm_fast_multiply = 0; 23990075Sobrien 24090075Sobrien/* Nonzero if this chip supports the ARM Architecture 4 extensions. */ 24190075Sobrienint arm_arch4 = 0; 24290075Sobrien 24390075Sobrien/* Nonzero if this chip supports the ARM Architecture 5 extensions. */ 24490075Sobrienint arm_arch5 = 0; 24590075Sobrien 24690075Sobrien/* Nonzero if this chip supports the ARM Architecture 5E extensions. */ 24790075Sobrienint arm_arch5e = 0; 24890075Sobrien 24990075Sobrien/* Nonzero if this chip can benefit from load scheduling. */ 25090075Sobrienint arm_ld_sched = 0; 25190075Sobrien 25290075Sobrien/* Nonzero if this chip is a StrongARM. */ 25390075Sobrienint arm_is_strong = 0; 25490075Sobrien 25590075Sobrien/* Nonzero if this chip is an XScale. */ 25690075Sobrienint arm_is_xscale = 0; 25790075Sobrien 25890075Sobrien/* Nonzero if this chip is an ARM6 or an ARM7. */ 25990075Sobrienint arm_is_6_or_7 = 0; 26090075Sobrien 26190075Sobrien/* Nonzero if generating Thumb instructions. */ 26290075Sobrienint thumb_code = 0; 26390075Sobrien 26490075Sobrien/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we 26590075Sobrien must report the mode of the memory reference from PRINT_OPERAND to 26690075Sobrien PRINT_OPERAND_ADDRESS. */ 26790075Sobrienenum machine_mode output_memory_reference_mode; 26890075Sobrien 26990075Sobrien/* The register number to be used for the PIC offset register. */ 27090075Sobrienconst char * arm_pic_register_string = NULL; 27196263Sobrienint arm_pic_register = INVALID_REGNUM; 27290075Sobrien 27390075Sobrien/* Set to 1 when a return insn is output, this means that the epilogue 27490075Sobrien is not needed. */ 27590075Sobrienint return_used_this_function; 27690075Sobrien 27790075Sobrien/* Set to 1 after arm_reorg has started. Reset to start at the start of 27890075Sobrien the next function. */ 27990075Sobrienstatic int after_arm_reorg = 0; 28090075Sobrien 28190075Sobrien/* The maximum number of insns to be used when loading a constant. */ 28290075Sobrienstatic int arm_constant_limit = 3; 28390075Sobrien 28490075Sobrien/* For an explanation of these variables, see final_prescan_insn below. */ 28590075Sobrienint arm_ccfsm_state; 28690075Sobrienenum arm_cond_code arm_current_cc; 28790075Sobrienrtx arm_target_insn; 28890075Sobrienint arm_target_label; 28990075Sobrien 29090075Sobrien/* The condition codes of the ARM, and the inverse function. */ 29190075Sobrienstatic const char * const arm_condition_codes[] = 29290075Sobrien{ 29390075Sobrien "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", 29490075Sobrien "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" 29590075Sobrien}; 29690075Sobrien 29790075Sobrien#define streq(string1, string2) (strcmp (string1, string2) == 0) 29890075Sobrien 29990075Sobrien/* Initialization code. */ 30090075Sobrien 30190075Sobrienstruct processors 30290075Sobrien{ 30390075Sobrien const char *const name; 30490075Sobrien const unsigned int flags; 30590075Sobrien}; 30690075Sobrien 30790075Sobrien/* Not all of these give usefully different compilation alternatives, 30890075Sobrien but there is no simple way of generalizing them. */ 30990075Sobrienstatic const struct processors all_cores[] = 31090075Sobrien{ 31190075Sobrien /* ARM Cores */ 31290075Sobrien 31390075Sobrien {"arm2", FL_CO_PROC | FL_MODE26 }, 31490075Sobrien {"arm250", FL_CO_PROC | FL_MODE26 }, 31590075Sobrien {"arm3", FL_CO_PROC | FL_MODE26 }, 31690075Sobrien {"arm6", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 31790075Sobrien {"arm60", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 31890075Sobrien {"arm600", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 31990075Sobrien {"arm610", FL_MODE26 | FL_MODE32 }, 32090075Sobrien {"arm620", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 32190075Sobrien {"arm7", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 32290075Sobrien /* arm7m doesn't exist on its own, but only with D, (and I), but 32390075Sobrien those don't alter the code, so arm7m is sometimes used. */ 32490075Sobrien {"arm7m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 32590075Sobrien {"arm7d", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 32690075Sobrien {"arm7dm", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 32790075Sobrien {"arm7di", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 32890075Sobrien {"arm7dmi", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 32990075Sobrien {"arm70", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 33090075Sobrien {"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 33190075Sobrien {"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 33290075Sobrien {"arm710", FL_MODE26 | FL_MODE32 }, 33390075Sobrien {"arm710t", FL_MODE26 | FL_MODE32 | FL_THUMB }, 33490075Sobrien {"arm720", FL_MODE26 | FL_MODE32 }, 33590075Sobrien {"arm720t", FL_MODE26 | FL_MODE32 | FL_THUMB }, 33690075Sobrien {"arm740t", FL_MODE26 | FL_MODE32 | FL_THUMB }, 33790075Sobrien {"arm710c", FL_MODE26 | FL_MODE32 }, 33890075Sobrien {"arm7100", FL_MODE26 | FL_MODE32 }, 33990075Sobrien {"arm7500", FL_MODE26 | FL_MODE32 }, 34090075Sobrien /* Doesn't have an external co-proc, but does have embedded fpu. */ 34190075Sobrien {"arm7500fe", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 34290075Sobrien {"arm7tdmi", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 34390075Sobrien {"arm8", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 34490075Sobrien {"arm810", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 34590075Sobrien {"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 34690075Sobrien {"arm920", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 34790075Sobrien {"arm920t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 34890075Sobrien {"arm940t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 34990075Sobrien {"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 35090075Sobrien {"arm9e", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 35190075Sobrien {"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 35290075Sobrien {"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 35390075Sobrien {"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 35490075Sobrien {"strongarm1110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 35590075Sobrien {"arm10tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, 35690075Sobrien {"arm1020t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, 35790075Sobrien {"xscale", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE }, 35890075Sobrien 35990075Sobrien {NULL, 0} 36090075Sobrien}; 36190075Sobrien 36290075Sobrienstatic const struct processors all_architectures[] = 36390075Sobrien{ 36490075Sobrien /* ARM Architectures */ 36590075Sobrien 36690075Sobrien { "armv2", FL_CO_PROC | FL_MODE26 }, 36790075Sobrien { "armv2a", FL_CO_PROC | FL_MODE26 }, 36890075Sobrien { "armv3", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 36990075Sobrien { "armv3m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 37090075Sobrien { "armv4", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 }, 37190075Sobrien /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no 37290075Sobrien implementations that support it, so we will leave it out for now. */ 37390075Sobrien { "armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 37490075Sobrien { "armv5", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, 37590075Sobrien { "armv5t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, 37690075Sobrien { "armv5te", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E }, 37790075Sobrien { NULL, 0 } 37890075Sobrien}; 37990075Sobrien 38090075Sobrien/* This is a magic stucture. The 'string' field is magically filled in 38190075Sobrien with a pointer to the value specified by the user on the command line 38290075Sobrien assuming that the user has specified such a value. */ 38390075Sobrien 38490075Sobrienstruct arm_cpu_select arm_select[] = 38590075Sobrien{ 38690075Sobrien /* string name processors */ 38790075Sobrien { NULL, "-mcpu=", all_cores }, 38890075Sobrien { NULL, "-march=", all_architectures }, 38990075Sobrien { NULL, "-mtune=", all_cores } 39090075Sobrien}; 39190075Sobrien 39290075Sobrien/* Return the number of bits set in value' */ 39390075Sobrienstatic unsigned long 39490075Sobrienbit_count (value) 39590075Sobrien signed int value; 39690075Sobrien{ 39790075Sobrien unsigned long count = 0; 39890075Sobrien 39990075Sobrien while (value) 40090075Sobrien { 40190075Sobrien value &= ~(value & -value); 40290075Sobrien ++count; 40390075Sobrien } 40490075Sobrien 40590075Sobrien return count; 40690075Sobrien} 40790075Sobrien 40890075Sobrien/* Fix up any incompatible options that the user has specified. 40990075Sobrien This has now turned into a maze. */ 41090075Sobrienvoid 41190075Sobrienarm_override_options () 41290075Sobrien{ 41390075Sobrien unsigned i; 41490075Sobrien 41590075Sobrien /* Set up the flags based on the cpu/architecture selected by the user. */ 41690075Sobrien for (i = ARRAY_SIZE (arm_select); i--;) 41790075Sobrien { 41890075Sobrien struct arm_cpu_select * ptr = arm_select + i; 41990075Sobrien 42090075Sobrien if (ptr->string != NULL && ptr->string[0] != '\0') 42190075Sobrien { 42290075Sobrien const struct processors * sel; 42390075Sobrien 42490075Sobrien for (sel = ptr->processors; sel->name != NULL; sel++) 42590075Sobrien if (streq (ptr->string, sel->name)) 42690075Sobrien { 42790075Sobrien if (i == 2) 42890075Sobrien tune_flags = sel->flags; 42990075Sobrien else 43090075Sobrien { 43190075Sobrien /* If we have been given an architecture and a processor 43290075Sobrien make sure that they are compatible. We only generate 43390075Sobrien a warning though, and we prefer the CPU over the 43490075Sobrien architecture. */ 43590075Sobrien if (insn_flags != 0 && (insn_flags ^ sel->flags)) 43690075Sobrien warning ("switch -mcpu=%s conflicts with -march= switch", 43790075Sobrien ptr->string); 43890075Sobrien 43990075Sobrien insn_flags = sel->flags; 44090075Sobrien } 44190075Sobrien 44290075Sobrien break; 44390075Sobrien } 44490075Sobrien 44590075Sobrien if (sel->name == NULL) 44690075Sobrien error ("bad value (%s) for %s switch", ptr->string, ptr->name); 44790075Sobrien } 44890075Sobrien } 44990075Sobrien 45090075Sobrien /* If the user did not specify a processor, choose one for them. */ 45190075Sobrien if (insn_flags == 0) 45290075Sobrien { 45390075Sobrien const struct processors * sel; 45490075Sobrien unsigned int sought; 45590075Sobrien static const struct cpu_default 45690075Sobrien { 45790075Sobrien const int cpu; 45890075Sobrien const char *const name; 45990075Sobrien } 46090075Sobrien cpu_defaults[] = 46190075Sobrien { 46290075Sobrien { TARGET_CPU_arm2, "arm2" }, 46390075Sobrien { TARGET_CPU_arm6, "arm6" }, 46490075Sobrien { TARGET_CPU_arm610, "arm610" }, 46590075Sobrien { TARGET_CPU_arm710, "arm710" }, 46690075Sobrien { TARGET_CPU_arm7m, "arm7m" }, 46790075Sobrien { TARGET_CPU_arm7500fe, "arm7500fe" }, 46890075Sobrien { TARGET_CPU_arm7tdmi, "arm7tdmi" }, 46990075Sobrien { TARGET_CPU_arm8, "arm8" }, 47090075Sobrien { TARGET_CPU_arm810, "arm810" }, 47190075Sobrien { TARGET_CPU_arm9, "arm9" }, 47290075Sobrien { TARGET_CPU_strongarm, "strongarm" }, 47390075Sobrien { TARGET_CPU_xscale, "xscale" }, 47490075Sobrien { TARGET_CPU_generic, "arm" }, 47590075Sobrien { 0, 0 } 47690075Sobrien }; 47790075Sobrien const struct cpu_default * def; 47890075Sobrien 47990075Sobrien /* Find the default. */ 48090075Sobrien for (def = cpu_defaults; def->name; def++) 48190075Sobrien if (def->cpu == TARGET_CPU_DEFAULT) 48290075Sobrien break; 48390075Sobrien 48490075Sobrien /* Make sure we found the default CPU. */ 48590075Sobrien if (def->name == NULL) 48690075Sobrien abort (); 48790075Sobrien 48890075Sobrien /* Find the default CPU's flags. */ 48990075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 49090075Sobrien if (streq (def->name, sel->name)) 49190075Sobrien break; 49290075Sobrien 49390075Sobrien if (sel->name == NULL) 49490075Sobrien abort (); 49590075Sobrien 49690075Sobrien insn_flags = sel->flags; 49790075Sobrien 49890075Sobrien /* Now check to see if the user has specified some command line 49990075Sobrien switch that require certain abilities from the cpu. */ 50090075Sobrien sought = 0; 50190075Sobrien 50290075Sobrien if (TARGET_INTERWORK || TARGET_THUMB) 50390075Sobrien { 50490075Sobrien sought |= (FL_THUMB | FL_MODE32); 50590075Sobrien 50690075Sobrien /* Force apcs-32 to be used for interworking. */ 50790075Sobrien target_flags |= ARM_FLAG_APCS_32; 50890075Sobrien 50990075Sobrien /* There are no ARM processors that support both APCS-26 and 51090075Sobrien interworking. Therefore we force FL_MODE26 to be removed 51190075Sobrien from insn_flags here (if it was set), so that the search 51290075Sobrien below will always be able to find a compatible processor. */ 51390075Sobrien insn_flags &= ~FL_MODE26; 51490075Sobrien } 51590075Sobrien else if (!TARGET_APCS_32) 51690075Sobrien sought |= FL_MODE26; 51790075Sobrien 51890075Sobrien if (sought != 0 && ((sought & insn_flags) != sought)) 51990075Sobrien { 52090075Sobrien /* Try to locate a CPU type that supports all of the abilities 52190075Sobrien of the default CPU, plus the extra abilities requested by 52290075Sobrien the user. */ 52390075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 52490075Sobrien if ((sel->flags & sought) == (sought | insn_flags)) 52590075Sobrien break; 52690075Sobrien 52790075Sobrien if (sel->name == NULL) 52890075Sobrien { 52990075Sobrien unsigned int current_bit_count = 0; 53090075Sobrien const struct processors * best_fit = NULL; 53190075Sobrien 53290075Sobrien /* Ideally we would like to issue an error message here 53390075Sobrien saying that it was not possible to find a CPU compatible 53490075Sobrien with the default CPU, but which also supports the command 53590075Sobrien line options specified by the programmer, and so they 53690075Sobrien ought to use the -mcpu=<name> command line option to 53790075Sobrien override the default CPU type. 53890075Sobrien 53990075Sobrien Unfortunately this does not work with multilibing. We 54090075Sobrien need to be able to support multilibs for -mapcs-26 and for 54190075Sobrien -mthumb-interwork and there is no CPU that can support both 54290075Sobrien options. Instead if we cannot find a cpu that has both the 54390075Sobrien characteristics of the default cpu and the given command line 54490075Sobrien options we scan the array again looking for a best match. */ 54590075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 54690075Sobrien if ((sel->flags & sought) == sought) 54790075Sobrien { 54890075Sobrien unsigned int count; 54990075Sobrien 55090075Sobrien count = bit_count (sel->flags & insn_flags); 55190075Sobrien 55290075Sobrien if (count >= current_bit_count) 55390075Sobrien { 55490075Sobrien best_fit = sel; 55590075Sobrien current_bit_count = count; 55690075Sobrien } 55790075Sobrien } 55890075Sobrien 55990075Sobrien if (best_fit == NULL) 56090075Sobrien abort (); 56190075Sobrien else 56290075Sobrien sel = best_fit; 56390075Sobrien } 56490075Sobrien 56590075Sobrien insn_flags = sel->flags; 56690075Sobrien } 56790075Sobrien } 56890075Sobrien 56990075Sobrien /* If tuning has not been specified, tune for whichever processor or 57090075Sobrien architecture has been selected. */ 57190075Sobrien if (tune_flags == 0) 57290075Sobrien tune_flags = insn_flags; 57390075Sobrien 57490075Sobrien /* Make sure that the processor choice does not conflict with any of the 57590075Sobrien other command line choices. */ 57690075Sobrien if (TARGET_APCS_32 && !(insn_flags & FL_MODE32)) 57790075Sobrien { 57890075Sobrien /* If APCS-32 was not the default then it must have been set by the 57990075Sobrien user, so issue a warning message. If the user has specified 58090075Sobrien "-mapcs-32 -mcpu=arm2" then we loose here. */ 58190075Sobrien if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0) 58290075Sobrien warning ("target CPU does not support APCS-32" ); 58390075Sobrien target_flags &= ~ARM_FLAG_APCS_32; 58490075Sobrien } 58590075Sobrien else if (!TARGET_APCS_32 && !(insn_flags & FL_MODE26)) 58690075Sobrien { 58790075Sobrien warning ("target CPU does not support APCS-26" ); 58890075Sobrien target_flags |= ARM_FLAG_APCS_32; 58990075Sobrien } 59090075Sobrien 59190075Sobrien if (TARGET_INTERWORK && !(insn_flags & FL_THUMB)) 59290075Sobrien { 59390075Sobrien warning ("target CPU does not support interworking" ); 59490075Sobrien target_flags &= ~ARM_FLAG_INTERWORK; 59590075Sobrien } 59690075Sobrien 59790075Sobrien if (TARGET_THUMB && !(insn_flags & FL_THUMB)) 59890075Sobrien { 59990075Sobrien warning ("target CPU does not support THUMB instructions"); 60090075Sobrien target_flags &= ~ARM_FLAG_THUMB; 60190075Sobrien } 60290075Sobrien 60390075Sobrien if (TARGET_APCS_FRAME && TARGET_THUMB) 60490075Sobrien { 60590075Sobrien /* warning ("ignoring -mapcs-frame because -mthumb was used"); */ 60690075Sobrien target_flags &= ~ARM_FLAG_APCS_FRAME; 60790075Sobrien } 60890075Sobrien 60990075Sobrien /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done 61090075Sobrien from here where no function is being compiled currently. */ 61190075Sobrien if ((target_flags & (THUMB_FLAG_LEAF_BACKTRACE | THUMB_FLAG_BACKTRACE)) 61290075Sobrien && TARGET_ARM) 61390075Sobrien warning ("enabling backtrace support is only meaningful when compiling for the Thumb"); 61490075Sobrien 61590075Sobrien if (TARGET_ARM && TARGET_CALLEE_INTERWORKING) 61690075Sobrien warning ("enabling callee interworking support is only meaningful when compiling for the Thumb"); 61790075Sobrien 61890075Sobrien if (TARGET_ARM && TARGET_CALLER_INTERWORKING) 61990075Sobrien warning ("enabling caller interworking support is only meaningful when compiling for the Thumb"); 62090075Sobrien 62190075Sobrien /* If interworking is enabled then APCS-32 must be selected as well. */ 62290075Sobrien if (TARGET_INTERWORK) 62390075Sobrien { 62490075Sobrien if (!TARGET_APCS_32) 62590075Sobrien warning ("interworking forces APCS-32 to be used" ); 62690075Sobrien target_flags |= ARM_FLAG_APCS_32; 62790075Sobrien } 62890075Sobrien 62990075Sobrien if (TARGET_APCS_STACK && !TARGET_APCS_FRAME) 63090075Sobrien { 63190075Sobrien warning ("-mapcs-stack-check incompatible with -mno-apcs-frame"); 63290075Sobrien target_flags |= ARM_FLAG_APCS_FRAME; 63390075Sobrien } 63490075Sobrien 63590075Sobrien if (TARGET_POKE_FUNCTION_NAME) 63690075Sobrien target_flags |= ARM_FLAG_APCS_FRAME; 63790075Sobrien 63890075Sobrien if (TARGET_APCS_REENT && flag_pic) 63990075Sobrien error ("-fpic and -mapcs-reent are incompatible"); 64090075Sobrien 64190075Sobrien if (TARGET_APCS_REENT) 64290075Sobrien warning ("APCS reentrant code not supported. Ignored"); 64390075Sobrien 64490075Sobrien /* If this target is normally configured to use APCS frames, warn if they 64590075Sobrien are turned off and debugging is turned on. */ 64690075Sobrien if (TARGET_ARM 64790075Sobrien && write_symbols != NO_DEBUG 64890075Sobrien && !TARGET_APCS_FRAME 64990075Sobrien && (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME)) 65090075Sobrien warning ("-g with -mno-apcs-frame may not give sensible debugging"); 65190075Sobrien 65290075Sobrien /* If stack checking is disabled, we can use r10 as the PIC register, 65390075Sobrien which keeps r9 available. */ 65496263Sobrien if (flag_pic) 65596263Sobrien arm_pic_register = TARGET_APCS_STACK ? 9 : 10; 65690075Sobrien 65790075Sobrien if (TARGET_APCS_FLOAT) 65890075Sobrien warning ("passing floating point arguments in fp regs not yet supported"); 65990075Sobrien 66090075Sobrien /* Initialise boolean versions of the flags, for use in the arm.md file. */ 66190075Sobrien arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0; 66290075Sobrien arm_arch4 = (insn_flags & FL_ARCH4) != 0; 66390075Sobrien arm_arch5 = (insn_flags & FL_ARCH5) != 0; 66490075Sobrien arm_arch5e = (insn_flags & FL_ARCH5E) != 0; 66590075Sobrien arm_is_xscale = (insn_flags & FL_XSCALE) != 0; 66690075Sobrien 66790075Sobrien arm_ld_sched = (tune_flags & FL_LDSCHED) != 0; 66890075Sobrien arm_is_strong = (tune_flags & FL_STRONG) != 0; 66990075Sobrien thumb_code = (TARGET_ARM == 0); 67090075Sobrien arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32)) 67190075Sobrien && !(tune_flags & FL_ARCH4))) != 0; 67290075Sobrien 67390075Sobrien /* Default value for floating point code... if no co-processor 67490075Sobrien bus, then schedule for emulated floating point. Otherwise, 67590075Sobrien assume the user has an FPA. 67690075Sobrien Note: this does not prevent use of floating point instructions, 67790075Sobrien -msoft-float does that. */ 67890075Sobrien arm_fpu = (tune_flags & FL_CO_PROC) ? FP_HARD : FP_SOFT3; 67990075Sobrien 68090075Sobrien if (target_fp_name) 68190075Sobrien { 68290075Sobrien if (streq (target_fp_name, "2")) 68390075Sobrien arm_fpu_arch = FP_SOFT2; 68490075Sobrien else if (streq (target_fp_name, "3")) 68590075Sobrien arm_fpu_arch = FP_SOFT3; 68690075Sobrien else 68790075Sobrien error ("invalid floating point emulation option: -mfpe-%s", 68890075Sobrien target_fp_name); 68990075Sobrien } 69090075Sobrien else 69190075Sobrien arm_fpu_arch = FP_DEFAULT; 69290075Sobrien 69390075Sobrien if (TARGET_FPE && arm_fpu != FP_HARD) 69490075Sobrien arm_fpu = FP_SOFT2; 69590075Sobrien 69690075Sobrien /* For arm2/3 there is no need to do any scheduling if there is only 69790075Sobrien a floating point emulator, or we are doing software floating-point. */ 69890075Sobrien if ((TARGET_SOFT_FLOAT || arm_fpu != FP_HARD) 69990075Sobrien && (tune_flags & FL_MODE32) == 0) 70090075Sobrien flag_schedule_insns = flag_schedule_insns_after_reload = 0; 70190075Sobrien 70290075Sobrien arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26; 70390075Sobrien 70490075Sobrien if (structure_size_string != NULL) 70590075Sobrien { 70690075Sobrien int size = strtol (structure_size_string, NULL, 0); 70790075Sobrien 70890075Sobrien if (size == 8 || size == 32) 70990075Sobrien arm_structure_size_boundary = size; 71090075Sobrien else 71190075Sobrien warning ("structure size boundary can only be set to 8 or 32"); 71290075Sobrien } 71390075Sobrien 71490075Sobrien if (arm_pic_register_string != NULL) 71590075Sobrien { 71696263Sobrien int pic_register = decode_reg_name (arm_pic_register_string); 71796263Sobrien 71890075Sobrien if (!flag_pic) 71990075Sobrien warning ("-mpic-register= is useless without -fpic"); 72090075Sobrien 72190075Sobrien /* Prevent the user from choosing an obviously stupid PIC register. */ 72296263Sobrien else if (pic_register < 0 || call_used_regs[pic_register] 72396263Sobrien || pic_register == HARD_FRAME_POINTER_REGNUM 72496263Sobrien || pic_register == STACK_POINTER_REGNUM 72596263Sobrien || pic_register >= PC_REGNUM) 72690075Sobrien error ("unable to use '%s' for PIC register", arm_pic_register_string); 72790075Sobrien else 72890075Sobrien arm_pic_register = pic_register; 72990075Sobrien } 73090075Sobrien 73190075Sobrien if (TARGET_THUMB && flag_schedule_insns) 73290075Sobrien { 73390075Sobrien /* Don't warn since it's on by default in -O2. */ 73490075Sobrien flag_schedule_insns = 0; 73590075Sobrien } 73690075Sobrien 73790075Sobrien /* If optimizing for space, don't synthesize constants. 73890075Sobrien For processors with load scheduling, it never costs more than 2 cycles 73990075Sobrien to load a constant, and the load scheduler may well reduce that to 1. */ 74090075Sobrien if (optimize_size || (tune_flags & FL_LDSCHED)) 74190075Sobrien arm_constant_limit = 1; 74290075Sobrien 74390075Sobrien if (arm_is_xscale) 74490075Sobrien arm_constant_limit = 2; 74590075Sobrien 74690075Sobrien /* If optimizing for size, bump the number of instructions that we 74790075Sobrien are prepared to conditionally execute (even on a StrongARM). 74890075Sobrien Otherwise for the StrongARM, which has early execution of branches, 74990075Sobrien a sequence that is worth skipping is shorter. */ 75090075Sobrien if (optimize_size) 75190075Sobrien max_insns_skipped = 6; 75290075Sobrien else if (arm_is_strong) 75390075Sobrien max_insns_skipped = 3; 75490075Sobrien 75590075Sobrien /* Register global variables with the garbage collector. */ 75690075Sobrien arm_add_gc_roots (); 75790075Sobrien} 75890075Sobrien 75990075Sobrienstatic void 76090075Sobrienarm_add_gc_roots () 76190075Sobrien{ 76290075Sobrien ggc_add_rtx_root (&arm_compare_op0, 1); 76390075Sobrien ggc_add_rtx_root (&arm_compare_op1, 1); 76490075Sobrien ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root. */ 76590075Sobrien 76690075Sobrien gcc_obstack_init(&minipool_obstack); 76790075Sobrien minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0); 76890075Sobrien} 76990075Sobrien 77090075Sobrien/* A table of known ARM exception types. 77190075Sobrien For use with the interrupt function attribute. */ 77290075Sobrien 77390075Sobrientypedef struct 77490075Sobrien{ 77590075Sobrien const char *const arg; 77690075Sobrien const unsigned long return_value; 77790075Sobrien} 77890075Sobrienisr_attribute_arg; 77990075Sobrien 78090075Sobrienstatic const isr_attribute_arg isr_attribute_args [] = 78190075Sobrien{ 78290075Sobrien { "IRQ", ARM_FT_ISR }, 78390075Sobrien { "irq", ARM_FT_ISR }, 78490075Sobrien { "FIQ", ARM_FT_FIQ }, 78590075Sobrien { "fiq", ARM_FT_FIQ }, 78690075Sobrien { "ABORT", ARM_FT_ISR }, 78790075Sobrien { "abort", ARM_FT_ISR }, 78890075Sobrien { "ABORT", ARM_FT_ISR }, 78990075Sobrien { "abort", ARM_FT_ISR }, 79090075Sobrien { "UNDEF", ARM_FT_EXCEPTION }, 79190075Sobrien { "undef", ARM_FT_EXCEPTION }, 79290075Sobrien { "SWI", ARM_FT_EXCEPTION }, 79390075Sobrien { "swi", ARM_FT_EXCEPTION }, 79490075Sobrien { NULL, ARM_FT_NORMAL } 79590075Sobrien}; 79690075Sobrien 79790075Sobrien/* Returns the (interrupt) function type of the current 79890075Sobrien function, or ARM_FT_UNKNOWN if the type cannot be determined. */ 79990075Sobrien 80090075Sobrienstatic unsigned long 80190075Sobrienarm_isr_value (argument) 80290075Sobrien tree argument; 80390075Sobrien{ 80490075Sobrien const isr_attribute_arg * ptr; 80590075Sobrien const char * arg; 80690075Sobrien 80790075Sobrien /* No argument - default to IRQ. */ 80890075Sobrien if (argument == NULL_TREE) 80990075Sobrien return ARM_FT_ISR; 81090075Sobrien 81190075Sobrien /* Get the value of the argument. */ 81290075Sobrien if (TREE_VALUE (argument) == NULL_TREE 81390075Sobrien || TREE_CODE (TREE_VALUE (argument)) != STRING_CST) 81490075Sobrien return ARM_FT_UNKNOWN; 81590075Sobrien 81690075Sobrien arg = TREE_STRING_POINTER (TREE_VALUE (argument)); 81790075Sobrien 81890075Sobrien /* Check it against the list of known arguments. */ 81990075Sobrien for (ptr = isr_attribute_args; ptr->arg != NULL; ptr ++) 82090075Sobrien if (streq (arg, ptr->arg)) 82190075Sobrien return ptr->return_value; 82290075Sobrien 82390075Sobrien /* An unrecognised interrupt type. */ 82490075Sobrien return ARM_FT_UNKNOWN; 82590075Sobrien} 82690075Sobrien 82790075Sobrien/* Computes the type of the current function. */ 82890075Sobrien 82990075Sobrienstatic unsigned long 83090075Sobrienarm_compute_func_type () 83190075Sobrien{ 83290075Sobrien unsigned long type = ARM_FT_UNKNOWN; 83390075Sobrien tree a; 83490075Sobrien tree attr; 83590075Sobrien 83690075Sobrien if (TREE_CODE (current_function_decl) != FUNCTION_DECL) 83790075Sobrien abort (); 83890075Sobrien 83990075Sobrien /* Decide if the current function is volatile. Such functions 84090075Sobrien never return, and many memory cycles can be saved by not storing 84190075Sobrien register values that will never be needed again. This optimization 84290075Sobrien was added to speed up context switching in a kernel application. */ 84390075Sobrien if (optimize > 0 84490075Sobrien && current_function_nothrow 84590075Sobrien && TREE_THIS_VOLATILE (current_function_decl)) 84690075Sobrien type |= ARM_FT_VOLATILE; 84790075Sobrien 84890075Sobrien if (current_function_needs_context) 84990075Sobrien type |= ARM_FT_NESTED; 85090075Sobrien 85190075Sobrien attr = DECL_ATTRIBUTES (current_function_decl); 85290075Sobrien 85390075Sobrien a = lookup_attribute ("naked", attr); 85490075Sobrien if (a != NULL_TREE) 85590075Sobrien type |= ARM_FT_NAKED; 85690075Sobrien 85790075Sobrien if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX) 85890075Sobrien type |= ARM_FT_EXCEPTION_HANDLER; 85990075Sobrien else 86090075Sobrien { 86190075Sobrien a = lookup_attribute ("isr", attr); 86290075Sobrien if (a == NULL_TREE) 86390075Sobrien a = lookup_attribute ("interrupt", attr); 86490075Sobrien 86590075Sobrien if (a == NULL_TREE) 86690075Sobrien type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL; 86790075Sobrien else 86890075Sobrien type |= arm_isr_value (TREE_VALUE (a)); 86990075Sobrien } 87090075Sobrien 87190075Sobrien return type; 87290075Sobrien} 87390075Sobrien 87490075Sobrien/* Returns the type of the current function. */ 87590075Sobrien 87690075Sobrienunsigned long 87790075Sobrienarm_current_func_type () 87890075Sobrien{ 87990075Sobrien if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN) 88090075Sobrien cfun->machine->func_type = arm_compute_func_type (); 88190075Sobrien 88290075Sobrien return cfun->machine->func_type; 88390075Sobrien} 88490075Sobrien 88590075Sobrien/* Return 1 if it is possible to return using a single instruction. */ 88690075Sobrien 88790075Sobrienint 88890075Sobrienuse_return_insn (iscond) 88990075Sobrien int iscond; 89090075Sobrien{ 89190075Sobrien int regno; 89290075Sobrien unsigned int func_type; 89390075Sobrien 89490075Sobrien /* Never use a return instruction before reload has run. */ 89590075Sobrien if (!reload_completed) 89690075Sobrien return 0; 89790075Sobrien 89890075Sobrien func_type = arm_current_func_type (); 89990075Sobrien 90096263Sobrien /* Naked functions and volatile functions need special 90196263Sobrien consideration. */ 90296263Sobrien if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED)) 90390075Sobrien return 0; 90490075Sobrien 90590075Sobrien /* As do variadic functions. */ 90690075Sobrien if (current_function_pretend_args_size 90796263Sobrien || cfun->machine->uses_anonymous_args 90890075Sobrien /* Of if the function calls __builtin_eh_return () */ 90990075Sobrien || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER 91090075Sobrien /* Or if there is no frame pointer and there is a stack adjustment. */ 91190075Sobrien || ((get_frame_size () + current_function_outgoing_args_size != 0) 91290075Sobrien && !frame_pointer_needed)) 91390075Sobrien return 0; 91490075Sobrien 91590075Sobrien /* Can't be done if interworking with Thumb, and any registers have been 91690075Sobrien stacked. Similarly, on StrongARM, conditional returns are expensive 91790075Sobrien if they aren't taken and registers have been stacked. */ 91890075Sobrien if (iscond && arm_is_strong && frame_pointer_needed) 91990075Sobrien return 0; 92090075Sobrien 92190075Sobrien if ((iscond && arm_is_strong) 92290075Sobrien || TARGET_INTERWORK) 92390075Sobrien { 92490075Sobrien for (regno = 0; regno <= LAST_ARM_REGNUM; regno++) 92590075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno]) 92690075Sobrien return 0; 92790075Sobrien 92890075Sobrien if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) 92990075Sobrien return 0; 93090075Sobrien } 93190075Sobrien 93290075Sobrien /* Can't be done if any of the FPU regs are pushed, 93390075Sobrien since this also requires an insn. */ 93490075Sobrien if (TARGET_HARD_FLOAT) 93590075Sobrien for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++) 93690075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno]) 93790075Sobrien return 0; 93890075Sobrien 93990075Sobrien return 1; 94090075Sobrien} 94190075Sobrien 94290075Sobrien/* Return TRUE if int I is a valid immediate ARM constant. */ 94390075Sobrien 94490075Sobrienint 94590075Sobrienconst_ok_for_arm (i) 94690075Sobrien HOST_WIDE_INT i; 94790075Sobrien{ 94890075Sobrien unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF; 94990075Sobrien 95090075Sobrien /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must 95190075Sobrien be all zero, or all one. */ 95290075Sobrien if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0 95390075Sobrien && ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) 95490075Sobrien != ((~(unsigned HOST_WIDE_INT) 0) 95590075Sobrien & ~(unsigned HOST_WIDE_INT) 0xffffffff))) 95690075Sobrien return FALSE; 95790075Sobrien 95890075Sobrien /* Fast return for 0 and powers of 2 */ 95990075Sobrien if ((i & (i - 1)) == 0) 96090075Sobrien return TRUE; 96190075Sobrien 96290075Sobrien do 96390075Sobrien { 96490075Sobrien if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) 96590075Sobrien return TRUE; 96690075Sobrien mask = 96790075Sobrien (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) 96890075Sobrien >> (32 - 2)) | ~(unsigned HOST_WIDE_INT) 0xffffffff; 96990075Sobrien } 97090075Sobrien while (mask != ~(unsigned HOST_WIDE_INT) 0xFF); 97190075Sobrien 97290075Sobrien return FALSE; 97390075Sobrien} 97490075Sobrien 97590075Sobrien/* Return true if I is a valid constant for the operation CODE. */ 97690075Sobrienstatic int 97790075Sobrienconst_ok_for_op (i, code) 97890075Sobrien HOST_WIDE_INT i; 97990075Sobrien enum rtx_code code; 98090075Sobrien{ 98190075Sobrien if (const_ok_for_arm (i)) 98290075Sobrien return 1; 98390075Sobrien 98490075Sobrien switch (code) 98590075Sobrien { 98690075Sobrien case PLUS: 98790075Sobrien return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); 98890075Sobrien 98990075Sobrien case MINUS: /* Should only occur with (MINUS I reg) => rsb */ 99090075Sobrien case XOR: 99190075Sobrien case IOR: 99290075Sobrien return 0; 99390075Sobrien 99490075Sobrien case AND: 99590075Sobrien return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); 99690075Sobrien 99790075Sobrien default: 99890075Sobrien abort (); 99990075Sobrien } 100090075Sobrien} 100190075Sobrien 100290075Sobrien/* Emit a sequence of insns to handle a large constant. 100390075Sobrien CODE is the code of the operation required, it can be any of SET, PLUS, 100490075Sobrien IOR, AND, XOR, MINUS; 100590075Sobrien MODE is the mode in which the operation is being performed; 100690075Sobrien VAL is the integer to operate on; 100790075Sobrien SOURCE is the other operand (a register, or a null-pointer for SET); 100890075Sobrien SUBTARGETS means it is safe to create scratch registers if that will 100990075Sobrien either produce a simpler sequence, or we will want to cse the values. 101090075Sobrien Return value is the number of insns emitted. */ 101190075Sobrien 101290075Sobrienint 101390075Sobrienarm_split_constant (code, mode, val, target, source, subtargets) 101490075Sobrien enum rtx_code code; 101590075Sobrien enum machine_mode mode; 101690075Sobrien HOST_WIDE_INT val; 101790075Sobrien rtx target; 101890075Sobrien rtx source; 101990075Sobrien int subtargets; 102090075Sobrien{ 102190075Sobrien if (subtargets || code == SET 102290075Sobrien || (GET_CODE (target) == REG && GET_CODE (source) == REG 102390075Sobrien && REGNO (target) != REGNO (source))) 102490075Sobrien { 102590075Sobrien /* After arm_reorg has been called, we can't fix up expensive 102690075Sobrien constants by pushing them into memory so we must synthesise 102790075Sobrien them in-line, regardless of the cost. This is only likely to 102890075Sobrien be more costly on chips that have load delay slots and we are 102990075Sobrien compiling without running the scheduler (so no splitting 103090075Sobrien occurred before the final instruction emission). 103190075Sobrien 103290075Sobrien Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c 103390075Sobrien */ 103490075Sobrien if (!after_arm_reorg 103590075Sobrien && (arm_gen_constant (code, mode, val, target, source, 1, 0) 103690075Sobrien > arm_constant_limit + (code != SET))) 103790075Sobrien { 103890075Sobrien if (code == SET) 103990075Sobrien { 104090075Sobrien /* Currently SET is the only monadic value for CODE, all 104190075Sobrien the rest are diadic. */ 104290075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (val))); 104390075Sobrien return 1; 104490075Sobrien } 104590075Sobrien else 104690075Sobrien { 104790075Sobrien rtx temp = subtargets ? gen_reg_rtx (mode) : target; 104890075Sobrien 104990075Sobrien emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (val))); 105090075Sobrien /* For MINUS, the value is subtracted from, since we never 105190075Sobrien have subtraction of a constant. */ 105290075Sobrien if (code == MINUS) 105390075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 105490075Sobrien gen_rtx_MINUS (mode, temp, source))); 105590075Sobrien else 105690075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 105790075Sobrien gen_rtx (code, mode, source, temp))); 105890075Sobrien return 2; 105990075Sobrien } 106090075Sobrien } 106190075Sobrien } 106290075Sobrien 106390075Sobrien return arm_gen_constant (code, mode, val, target, source, subtargets, 1); 106490075Sobrien} 106590075Sobrien 106690075Sobrienstatic int 106790075Sobriencount_insns_for_constant (HOST_WIDE_INT remainder, int i) 106890075Sobrien{ 106990075Sobrien HOST_WIDE_INT temp1; 107090075Sobrien int num_insns = 0; 107190075Sobrien do 107290075Sobrien { 107390075Sobrien int end; 107490075Sobrien 107590075Sobrien if (i <= 0) 107690075Sobrien i += 32; 107790075Sobrien if (remainder & (3 << (i - 2))) 107890075Sobrien { 107990075Sobrien end = i - 8; 108090075Sobrien if (end < 0) 108190075Sobrien end += 32; 108290075Sobrien temp1 = remainder & ((0x0ff << end) 108390075Sobrien | ((i < end) ? (0xff >> (32 - end)) : 0)); 108490075Sobrien remainder &= ~temp1; 108590075Sobrien num_insns++; 108690075Sobrien i -= 6; 108790075Sobrien } 108890075Sobrien i -= 2; 108990075Sobrien } while (remainder); 109090075Sobrien return num_insns; 109190075Sobrien} 109290075Sobrien 109390075Sobrien/* As above, but extra parameter GENERATE which, if clear, suppresses 109490075Sobrien RTL generation. */ 109590075Sobrien 109690075Sobrienstatic int 109790075Sobrienarm_gen_constant (code, mode, val, target, source, subtargets, generate) 109890075Sobrien enum rtx_code code; 109990075Sobrien enum machine_mode mode; 110090075Sobrien HOST_WIDE_INT val; 110190075Sobrien rtx target; 110290075Sobrien rtx source; 110390075Sobrien int subtargets; 110490075Sobrien int generate; 110590075Sobrien{ 110690075Sobrien int can_invert = 0; 110790075Sobrien int can_negate = 0; 110890075Sobrien int can_negate_initial = 0; 110990075Sobrien int can_shift = 0; 111090075Sobrien int i; 111190075Sobrien int num_bits_set = 0; 111290075Sobrien int set_sign_bit_copies = 0; 111390075Sobrien int clear_sign_bit_copies = 0; 111490075Sobrien int clear_zero_bit_copies = 0; 111590075Sobrien int set_zero_bit_copies = 0; 111690075Sobrien int insns = 0; 111790075Sobrien unsigned HOST_WIDE_INT temp1, temp2; 111890075Sobrien unsigned HOST_WIDE_INT remainder = val & 0xffffffff; 111990075Sobrien 112090075Sobrien /* Find out which operations are safe for a given CODE. Also do a quick 112190075Sobrien check for degenerate cases; these can occur when DImode operations 112290075Sobrien are split. */ 112390075Sobrien switch (code) 112490075Sobrien { 112590075Sobrien case SET: 112690075Sobrien can_invert = 1; 112790075Sobrien can_shift = 1; 112890075Sobrien can_negate = 1; 112990075Sobrien break; 113090075Sobrien 113190075Sobrien case PLUS: 113290075Sobrien can_negate = 1; 113390075Sobrien can_negate_initial = 1; 113490075Sobrien break; 113590075Sobrien 113690075Sobrien case IOR: 113790075Sobrien if (remainder == 0xffffffff) 113890075Sobrien { 113990075Sobrien if (generate) 114090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 114190075Sobrien GEN_INT (ARM_SIGN_EXTEND (val)))); 114290075Sobrien return 1; 114390075Sobrien } 114490075Sobrien if (remainder == 0) 114590075Sobrien { 114690075Sobrien if (reload_completed && rtx_equal_p (target, source)) 114790075Sobrien return 0; 114890075Sobrien if (generate) 114990075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 115090075Sobrien return 1; 115190075Sobrien } 115290075Sobrien break; 115390075Sobrien 115490075Sobrien case AND: 115590075Sobrien if (remainder == 0) 115690075Sobrien { 115790075Sobrien if (generate) 115890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, const0_rtx)); 115990075Sobrien return 1; 116090075Sobrien } 116190075Sobrien if (remainder == 0xffffffff) 116290075Sobrien { 116390075Sobrien if (reload_completed && rtx_equal_p (target, source)) 116490075Sobrien return 0; 116590075Sobrien if (generate) 116690075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 116790075Sobrien return 1; 116890075Sobrien } 116990075Sobrien can_invert = 1; 117090075Sobrien break; 117190075Sobrien 117290075Sobrien case XOR: 117390075Sobrien if (remainder == 0) 117490075Sobrien { 117590075Sobrien if (reload_completed && rtx_equal_p (target, source)) 117690075Sobrien return 0; 117790075Sobrien if (generate) 117890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 117990075Sobrien return 1; 118090075Sobrien } 118190075Sobrien if (remainder == 0xffffffff) 118290075Sobrien { 118390075Sobrien if (generate) 118490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 118590075Sobrien gen_rtx_NOT (mode, source))); 118690075Sobrien return 1; 118790075Sobrien } 118890075Sobrien 118990075Sobrien /* We don't know how to handle this yet below. */ 119090075Sobrien abort (); 119190075Sobrien 119290075Sobrien case MINUS: 119390075Sobrien /* We treat MINUS as (val - source), since (source - val) is always 119490075Sobrien passed as (source + (-val)). */ 119590075Sobrien if (remainder == 0) 119690075Sobrien { 119790075Sobrien if (generate) 119890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 119990075Sobrien gen_rtx_NEG (mode, source))); 120090075Sobrien return 1; 120190075Sobrien } 120290075Sobrien if (const_ok_for_arm (val)) 120390075Sobrien { 120490075Sobrien if (generate) 120590075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 120690075Sobrien gen_rtx_MINUS (mode, GEN_INT (val), 120790075Sobrien source))); 120890075Sobrien return 1; 120990075Sobrien } 121090075Sobrien can_negate = 1; 121190075Sobrien 121290075Sobrien break; 121390075Sobrien 121490075Sobrien default: 121590075Sobrien abort (); 121690075Sobrien } 121790075Sobrien 121890075Sobrien /* If we can do it in one insn get out quickly. */ 121990075Sobrien if (const_ok_for_arm (val) 122090075Sobrien || (can_negate_initial && const_ok_for_arm (-val)) 122190075Sobrien || (can_invert && const_ok_for_arm (~val))) 122290075Sobrien { 122390075Sobrien if (generate) 122490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 122590075Sobrien (source ? gen_rtx (code, mode, source, 122690075Sobrien GEN_INT (val)) 122790075Sobrien : GEN_INT (val)))); 122890075Sobrien return 1; 122990075Sobrien } 123090075Sobrien 123190075Sobrien /* Calculate a few attributes that may be useful for specific 123290075Sobrien optimizations. */ 123390075Sobrien for (i = 31; i >= 0; i--) 123490075Sobrien { 123590075Sobrien if ((remainder & (1 << i)) == 0) 123690075Sobrien clear_sign_bit_copies++; 123790075Sobrien else 123890075Sobrien break; 123990075Sobrien } 124090075Sobrien 124190075Sobrien for (i = 31; i >= 0; i--) 124290075Sobrien { 124390075Sobrien if ((remainder & (1 << i)) != 0) 124490075Sobrien set_sign_bit_copies++; 124590075Sobrien else 124690075Sobrien break; 124790075Sobrien } 124890075Sobrien 124990075Sobrien for (i = 0; i <= 31; i++) 125090075Sobrien { 125190075Sobrien if ((remainder & (1 << i)) == 0) 125290075Sobrien clear_zero_bit_copies++; 125390075Sobrien else 125490075Sobrien break; 125590075Sobrien } 125690075Sobrien 125790075Sobrien for (i = 0; i <= 31; i++) 125890075Sobrien { 125990075Sobrien if ((remainder & (1 << i)) != 0) 126090075Sobrien set_zero_bit_copies++; 126190075Sobrien else 126290075Sobrien break; 126390075Sobrien } 126490075Sobrien 126590075Sobrien switch (code) 126690075Sobrien { 126790075Sobrien case SET: 126890075Sobrien /* See if we can do this by sign_extending a constant that is known 126990075Sobrien to be negative. This is a good, way of doing it, since the shift 127090075Sobrien may well merge into a subsequent insn. */ 127190075Sobrien if (set_sign_bit_copies > 1) 127290075Sobrien { 127390075Sobrien if (const_ok_for_arm 127490075Sobrien (temp1 = ARM_SIGN_EXTEND (remainder 127590075Sobrien << (set_sign_bit_copies - 1)))) 127690075Sobrien { 127790075Sobrien if (generate) 127890075Sobrien { 127990075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 128090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, 128190075Sobrien GEN_INT (temp1))); 128290075Sobrien emit_insn (gen_ashrsi3 (target, new_src, 128390075Sobrien GEN_INT (set_sign_bit_copies - 1))); 128490075Sobrien } 128590075Sobrien return 2; 128690075Sobrien } 128790075Sobrien /* For an inverted constant, we will need to set the low bits, 128890075Sobrien these will be shifted out of harm's way. */ 128990075Sobrien temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; 129090075Sobrien if (const_ok_for_arm (~temp1)) 129190075Sobrien { 129290075Sobrien if (generate) 129390075Sobrien { 129490075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 129590075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, 129690075Sobrien GEN_INT (temp1))); 129790075Sobrien emit_insn (gen_ashrsi3 (target, new_src, 129890075Sobrien GEN_INT (set_sign_bit_copies - 1))); 129990075Sobrien } 130090075Sobrien return 2; 130190075Sobrien } 130290075Sobrien } 130390075Sobrien 130490075Sobrien /* See if we can generate this by setting the bottom (or the top) 130590075Sobrien 16 bits, and then shifting these into the other half of the 130690075Sobrien word. We only look for the simplest cases, to do more would cost 130790075Sobrien too much. Be careful, however, not to generate this when the 130890075Sobrien alternative would take fewer insns. */ 130990075Sobrien if (val & 0xffff0000) 131090075Sobrien { 131190075Sobrien temp1 = remainder & 0xffff0000; 131290075Sobrien temp2 = remainder & 0x0000ffff; 131390075Sobrien 131490075Sobrien /* Overlaps outside this range are best done using other methods. */ 131590075Sobrien for (i = 9; i < 24; i++) 131690075Sobrien { 131790075Sobrien if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) 131890075Sobrien && !const_ok_for_arm (temp2)) 131990075Sobrien { 132090075Sobrien rtx new_src = (subtargets 132190075Sobrien ? (generate ? gen_reg_rtx (mode) : NULL_RTX) 132290075Sobrien : target); 132390075Sobrien insns = arm_gen_constant (code, mode, temp2, new_src, 132490075Sobrien source, subtargets, generate); 132590075Sobrien source = new_src; 132690075Sobrien if (generate) 132790075Sobrien emit_insn (gen_rtx_SET 132890075Sobrien (VOIDmode, target, 132990075Sobrien gen_rtx_IOR (mode, 133090075Sobrien gen_rtx_ASHIFT (mode, source, 133190075Sobrien GEN_INT (i)), 133290075Sobrien source))); 133390075Sobrien return insns + 1; 133490075Sobrien } 133590075Sobrien } 133690075Sobrien 133790075Sobrien /* Don't duplicate cases already considered. */ 133890075Sobrien for (i = 17; i < 24; i++) 133990075Sobrien { 134090075Sobrien if (((temp1 | (temp1 >> i)) == remainder) 134190075Sobrien && !const_ok_for_arm (temp1)) 134290075Sobrien { 134390075Sobrien rtx new_src = (subtargets 134490075Sobrien ? (generate ? gen_reg_rtx (mode) : NULL_RTX) 134590075Sobrien : target); 134690075Sobrien insns = arm_gen_constant (code, mode, temp1, new_src, 134790075Sobrien source, subtargets, generate); 134890075Sobrien source = new_src; 134990075Sobrien if (generate) 135090075Sobrien emit_insn 135190075Sobrien (gen_rtx_SET (VOIDmode, target, 135290075Sobrien gen_rtx_IOR 135390075Sobrien (mode, 135490075Sobrien gen_rtx_LSHIFTRT (mode, source, 135590075Sobrien GEN_INT (i)), 135690075Sobrien source))); 135790075Sobrien return insns + 1; 135890075Sobrien } 135990075Sobrien } 136090075Sobrien } 136190075Sobrien break; 136290075Sobrien 136390075Sobrien case IOR: 136490075Sobrien case XOR: 136590075Sobrien /* If we have IOR or XOR, and the constant can be loaded in a 136690075Sobrien single instruction, and we can find a temporary to put it in, 136790075Sobrien then this can be done in two instructions instead of 3-4. */ 136890075Sobrien if (subtargets 136990075Sobrien /* TARGET can't be NULL if SUBTARGETS is 0 */ 137090075Sobrien || (reload_completed && !reg_mentioned_p (target, source))) 137190075Sobrien { 137290075Sobrien if (const_ok_for_arm (ARM_SIGN_EXTEND (~val))) 137390075Sobrien { 137490075Sobrien if (generate) 137590075Sobrien { 137690075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 137790075Sobrien 137890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, GEN_INT (val))); 137990075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 138090075Sobrien gen_rtx (code, mode, source, sub))); 138190075Sobrien } 138290075Sobrien return 2; 138390075Sobrien } 138490075Sobrien } 138590075Sobrien 138690075Sobrien if (code == XOR) 138790075Sobrien break; 138890075Sobrien 138990075Sobrien if (set_sign_bit_copies > 8 139090075Sobrien && (val & (-1 << (32 - set_sign_bit_copies))) == val) 139190075Sobrien { 139290075Sobrien if (generate) 139390075Sobrien { 139490075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 139590075Sobrien rtx shift = GEN_INT (set_sign_bit_copies); 139690075Sobrien 139790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 139890075Sobrien gen_rtx_NOT (mode, 139990075Sobrien gen_rtx_ASHIFT (mode, 140090075Sobrien source, 140190075Sobrien shift)))); 140290075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 140390075Sobrien gen_rtx_NOT (mode, 140490075Sobrien gen_rtx_LSHIFTRT (mode, sub, 140590075Sobrien shift)))); 140690075Sobrien } 140790075Sobrien return 2; 140890075Sobrien } 140990075Sobrien 141090075Sobrien if (set_zero_bit_copies > 8 141190075Sobrien && (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder) 141290075Sobrien { 141390075Sobrien if (generate) 141490075Sobrien { 141590075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 141690075Sobrien rtx shift = GEN_INT (set_zero_bit_copies); 141790075Sobrien 141890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 141990075Sobrien gen_rtx_NOT (mode, 142090075Sobrien gen_rtx_LSHIFTRT (mode, 142190075Sobrien source, 142290075Sobrien shift)))); 142390075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 142490075Sobrien gen_rtx_NOT (mode, 142590075Sobrien gen_rtx_ASHIFT (mode, sub, 142690075Sobrien shift)))); 142790075Sobrien } 142890075Sobrien return 2; 142990075Sobrien } 143090075Sobrien 143190075Sobrien if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~val))) 143290075Sobrien { 143390075Sobrien if (generate) 143490075Sobrien { 143590075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 143690075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 143790075Sobrien gen_rtx_NOT (mode, source))); 143890075Sobrien source = sub; 143990075Sobrien if (subtargets) 144090075Sobrien sub = gen_reg_rtx (mode); 144190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 144290075Sobrien gen_rtx_AND (mode, source, 144390075Sobrien GEN_INT (temp1)))); 144490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 144590075Sobrien gen_rtx_NOT (mode, sub))); 144690075Sobrien } 144790075Sobrien return 3; 144890075Sobrien } 144990075Sobrien break; 145090075Sobrien 145190075Sobrien case AND: 145290075Sobrien /* See if two shifts will do 2 or more insn's worth of work. */ 145390075Sobrien if (clear_sign_bit_copies >= 16 && clear_sign_bit_copies < 24) 145490075Sobrien { 145590075Sobrien HOST_WIDE_INT shift_mask = ((0xffffffff 145690075Sobrien << (32 - clear_sign_bit_copies)) 145790075Sobrien & 0xffffffff); 145890075Sobrien 145990075Sobrien if ((remainder | shift_mask) != 0xffffffff) 146090075Sobrien { 146190075Sobrien if (generate) 146290075Sobrien { 146390075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 146490075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 146590075Sobrien new_src, source, subtargets, 1); 146690075Sobrien source = new_src; 146790075Sobrien } 146890075Sobrien else 146990075Sobrien { 147090075Sobrien rtx targ = subtargets ? NULL_RTX : target; 147190075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 147290075Sobrien targ, source, subtargets, 0); 147390075Sobrien } 147490075Sobrien } 147590075Sobrien 147690075Sobrien if (generate) 147790075Sobrien { 147890075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 147990075Sobrien rtx shift = GEN_INT (clear_sign_bit_copies); 148090075Sobrien 148190075Sobrien emit_insn (gen_ashlsi3 (new_src, source, shift)); 148290075Sobrien emit_insn (gen_lshrsi3 (target, new_src, shift)); 148390075Sobrien } 148490075Sobrien 148590075Sobrien return insns + 2; 148690075Sobrien } 148790075Sobrien 148890075Sobrien if (clear_zero_bit_copies >= 16 && clear_zero_bit_copies < 24) 148990075Sobrien { 149090075Sobrien HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1; 149190075Sobrien 149290075Sobrien if ((remainder | shift_mask) != 0xffffffff) 149390075Sobrien { 149490075Sobrien if (generate) 149590075Sobrien { 149690075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 149790075Sobrien 149890075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 149990075Sobrien new_src, source, subtargets, 1); 150090075Sobrien source = new_src; 150190075Sobrien } 150290075Sobrien else 150390075Sobrien { 150490075Sobrien rtx targ = subtargets ? NULL_RTX : target; 150590075Sobrien 150690075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 150790075Sobrien targ, source, subtargets, 0); 150890075Sobrien } 150990075Sobrien } 151090075Sobrien 151190075Sobrien if (generate) 151290075Sobrien { 151390075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 151490075Sobrien rtx shift = GEN_INT (clear_zero_bit_copies); 151590075Sobrien 151690075Sobrien emit_insn (gen_lshrsi3 (new_src, source, shift)); 151790075Sobrien emit_insn (gen_ashlsi3 (target, new_src, shift)); 151890075Sobrien } 151990075Sobrien 152090075Sobrien return insns + 2; 152190075Sobrien } 152290075Sobrien 152390075Sobrien break; 152490075Sobrien 152590075Sobrien default: 152690075Sobrien break; 152790075Sobrien } 152890075Sobrien 152990075Sobrien for (i = 0; i < 32; i++) 153090075Sobrien if (remainder & (1 << i)) 153190075Sobrien num_bits_set++; 153290075Sobrien 153390075Sobrien if (code == AND || (can_invert && num_bits_set > 16)) 153490075Sobrien remainder = (~remainder) & 0xffffffff; 153590075Sobrien else if (code == PLUS && num_bits_set > 16) 153690075Sobrien remainder = (-remainder) & 0xffffffff; 153790075Sobrien else 153890075Sobrien { 153990075Sobrien can_invert = 0; 154090075Sobrien can_negate = 0; 154190075Sobrien } 154290075Sobrien 154390075Sobrien /* Now try and find a way of doing the job in either two or three 154490075Sobrien instructions. 154590075Sobrien We start by looking for the largest block of zeros that are aligned on 154690075Sobrien a 2-bit boundary, we then fill up the temps, wrapping around to the 154790075Sobrien top of the word when we drop off the bottom. 154890075Sobrien In the worst case this code should produce no more than four insns. */ 154990075Sobrien { 155090075Sobrien int best_start = 0; 155190075Sobrien int best_consecutive_zeros = 0; 155290075Sobrien 155390075Sobrien for (i = 0; i < 32; i += 2) 155490075Sobrien { 155590075Sobrien int consecutive_zeros = 0; 155690075Sobrien 155790075Sobrien if (!(remainder & (3 << i))) 155890075Sobrien { 155990075Sobrien while ((i < 32) && !(remainder & (3 << i))) 156090075Sobrien { 156190075Sobrien consecutive_zeros += 2; 156290075Sobrien i += 2; 156390075Sobrien } 156490075Sobrien if (consecutive_zeros > best_consecutive_zeros) 156590075Sobrien { 156690075Sobrien best_consecutive_zeros = consecutive_zeros; 156790075Sobrien best_start = i - consecutive_zeros; 156890075Sobrien } 156990075Sobrien i -= 2; 157090075Sobrien } 157190075Sobrien } 157290075Sobrien 157390075Sobrien /* So long as it won't require any more insns to do so, it's 157490075Sobrien desirable to emit a small constant (in bits 0...9) in the last 157590075Sobrien insn. This way there is more chance that it can be combined with 157690075Sobrien a later addressing insn to form a pre-indexed load or store 157790075Sobrien operation. Consider: 157890075Sobrien 157990075Sobrien *((volatile int *)0xe0000100) = 1; 158090075Sobrien *((volatile int *)0xe0000110) = 2; 158190075Sobrien 158290075Sobrien We want this to wind up as: 158390075Sobrien 158490075Sobrien mov rA, #0xe0000000 158590075Sobrien mov rB, #1 158690075Sobrien str rB, [rA, #0x100] 158790075Sobrien mov rB, #2 158890075Sobrien str rB, [rA, #0x110] 158990075Sobrien 159090075Sobrien rather than having to synthesize both large constants from scratch. 159190075Sobrien 159290075Sobrien Therefore, we calculate how many insns would be required to emit 159390075Sobrien the constant starting from `best_start', and also starting from 159490075Sobrien zero (ie with bit 31 first to be output). If `best_start' doesn't 159590075Sobrien yield a shorter sequence, we may as well use zero. */ 159690075Sobrien if (best_start != 0 159790075Sobrien && ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder) 159890075Sobrien && (count_insns_for_constant (remainder, 0) <= 159990075Sobrien count_insns_for_constant (remainder, best_start))) 160090075Sobrien best_start = 0; 160190075Sobrien 160290075Sobrien /* Now start emitting the insns. */ 160390075Sobrien i = best_start; 160490075Sobrien do 160590075Sobrien { 160690075Sobrien int end; 160790075Sobrien 160890075Sobrien if (i <= 0) 160990075Sobrien i += 32; 161090075Sobrien if (remainder & (3 << (i - 2))) 161190075Sobrien { 161290075Sobrien end = i - 8; 161390075Sobrien if (end < 0) 161490075Sobrien end += 32; 161590075Sobrien temp1 = remainder & ((0x0ff << end) 161690075Sobrien | ((i < end) ? (0xff >> (32 - end)) : 0)); 161790075Sobrien remainder &= ~temp1; 161890075Sobrien 161990075Sobrien if (generate) 162090075Sobrien { 162190075Sobrien rtx new_src, temp1_rtx; 162290075Sobrien 162390075Sobrien if (code == SET || code == MINUS) 162490075Sobrien { 162590075Sobrien new_src = (subtargets ? gen_reg_rtx (mode) : target); 162690075Sobrien if (can_invert && code != MINUS) 162790075Sobrien temp1 = ~temp1; 162890075Sobrien } 162990075Sobrien else 163090075Sobrien { 163190075Sobrien if (remainder && subtargets) 163290075Sobrien new_src = gen_reg_rtx (mode); 163390075Sobrien else 163490075Sobrien new_src = target; 163590075Sobrien if (can_invert) 163690075Sobrien temp1 = ~temp1; 163790075Sobrien else if (can_negate) 163890075Sobrien temp1 = -temp1; 163990075Sobrien } 164090075Sobrien 164190075Sobrien temp1 = trunc_int_for_mode (temp1, mode); 164290075Sobrien temp1_rtx = GEN_INT (temp1); 164390075Sobrien 164490075Sobrien if (code == SET) 164590075Sobrien ; 164690075Sobrien else if (code == MINUS) 164790075Sobrien temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source); 164890075Sobrien else 164990075Sobrien temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx); 165090075Sobrien 165190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, temp1_rtx)); 165290075Sobrien source = new_src; 165390075Sobrien } 165490075Sobrien 165590075Sobrien if (code == SET) 165690075Sobrien { 165790075Sobrien can_invert = 0; 165890075Sobrien code = PLUS; 165990075Sobrien } 166090075Sobrien else if (code == MINUS) 166190075Sobrien code = PLUS; 166290075Sobrien 166390075Sobrien insns++; 166490075Sobrien i -= 6; 166590075Sobrien } 166690075Sobrien i -= 2; 166790075Sobrien } 166890075Sobrien while (remainder); 166990075Sobrien } 167090075Sobrien 167190075Sobrien return insns; 167290075Sobrien} 167390075Sobrien 167490075Sobrien/* Canonicalize a comparison so that we are more likely to recognize it. 167590075Sobrien This can be done for a few constant compares, where we can make the 167690075Sobrien immediate value easier to load. */ 167790075Sobrien 167890075Sobrienenum rtx_code 167990075Sobrienarm_canonicalize_comparison (code, op1) 168090075Sobrien enum rtx_code code; 168190075Sobrien rtx * op1; 168290075Sobrien{ 168390075Sobrien unsigned HOST_WIDE_INT i = INTVAL (*op1); 168490075Sobrien 168590075Sobrien switch (code) 168690075Sobrien { 168790075Sobrien case EQ: 168890075Sobrien case NE: 168990075Sobrien return code; 169090075Sobrien 169190075Sobrien case GT: 169290075Sobrien case LE: 169390075Sobrien if (i != ((((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) - 1) 169490075Sobrien && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) 169590075Sobrien { 169690075Sobrien *op1 = GEN_INT (i + 1); 169790075Sobrien return code == GT ? GE : LT; 169890075Sobrien } 169990075Sobrien break; 170090075Sobrien 170190075Sobrien case GE: 170290075Sobrien case LT: 170390075Sobrien if (i != (((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) 170490075Sobrien && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) 170590075Sobrien { 170690075Sobrien *op1 = GEN_INT (i - 1); 170790075Sobrien return code == GE ? GT : LE; 170890075Sobrien } 170990075Sobrien break; 171090075Sobrien 171190075Sobrien case GTU: 171290075Sobrien case LEU: 171390075Sobrien if (i != ~((unsigned HOST_WIDE_INT) 0) 171490075Sobrien && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) 171590075Sobrien { 171690075Sobrien *op1 = GEN_INT (i + 1); 171790075Sobrien return code == GTU ? GEU : LTU; 171890075Sobrien } 171990075Sobrien break; 172090075Sobrien 172190075Sobrien case GEU: 172290075Sobrien case LTU: 172390075Sobrien if (i != 0 172490075Sobrien && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) 172590075Sobrien { 172690075Sobrien *op1 = GEN_INT (i - 1); 172790075Sobrien return code == GEU ? GTU : LEU; 172890075Sobrien } 172990075Sobrien break; 173090075Sobrien 173190075Sobrien default: 173290075Sobrien abort (); 173390075Sobrien } 173490075Sobrien 173590075Sobrien return code; 173690075Sobrien} 173790075Sobrien 173890075Sobrien/* Decide whether a type should be returned in memory (true) 173990075Sobrien or in a register (false). This is called by the macro 174090075Sobrien RETURN_IN_MEMORY. */ 174190075Sobrien 174290075Sobrienint 174390075Sobrienarm_return_in_memory (type) 174490075Sobrien tree type; 174590075Sobrien{ 174690075Sobrien if (!AGGREGATE_TYPE_P (type)) 174790075Sobrien /* All simple types are returned in registers. */ 174890075Sobrien return 0; 174990075Sobrien 175090075Sobrien /* For the arm-wince targets we choose to be compitable with Microsoft's 175190075Sobrien ARM and Thumb compilers, which always return aggregates in memory. */ 175290075Sobrien#ifndef ARM_WINCE 175390075Sobrien /* All structures/unions bigger than one word are returned in memory. 175490075Sobrien Also catch the case where int_size_in_bytes returns -1. In this case 175590075Sobrien the aggregate is either huge or of varaible size, and in either case 175690075Sobrien we will want to return it via memory and not in a register. */ 175790075Sobrien if (((unsigned int) int_size_in_bytes (type)) > UNITS_PER_WORD) 175890075Sobrien return 1; 175990075Sobrien 176090075Sobrien if (TREE_CODE (type) == RECORD_TYPE) 176190075Sobrien { 176290075Sobrien tree field; 176390075Sobrien 176490075Sobrien /* For a struct the APCS says that we only return in a register 176590075Sobrien if the type is 'integer like' and every addressable element 176690075Sobrien has an offset of zero. For practical purposes this means 176790075Sobrien that the structure can have at most one non bit-field element 176890075Sobrien and that this element must be the first one in the structure. */ 176990075Sobrien 177090075Sobrien /* Find the first field, ignoring non FIELD_DECL things which will 177190075Sobrien have been created by C++. */ 177290075Sobrien for (field = TYPE_FIELDS (type); 177390075Sobrien field && TREE_CODE (field) != FIELD_DECL; 177490075Sobrien field = TREE_CHAIN (field)) 177590075Sobrien continue; 177690075Sobrien 177790075Sobrien if (field == NULL) 177890075Sobrien return 0; /* An empty structure. Allowed by an extension to ANSI C. */ 177990075Sobrien 178090075Sobrien /* Check that the first field is valid for returning in a register. */ 178190075Sobrien 178290075Sobrien /* ... Floats are not allowed */ 178390075Sobrien if (FLOAT_TYPE_P (TREE_TYPE (field))) 178490075Sobrien return 1; 178590075Sobrien 178690075Sobrien /* ... Aggregates that are not themselves valid for returning in 178790075Sobrien a register are not allowed. */ 178890075Sobrien if (RETURN_IN_MEMORY (TREE_TYPE (field))) 178990075Sobrien return 1; 179090075Sobrien 179190075Sobrien /* Now check the remaining fields, if any. Only bitfields are allowed, 179290075Sobrien since they are not addressable. */ 179390075Sobrien for (field = TREE_CHAIN (field); 179490075Sobrien field; 179590075Sobrien field = TREE_CHAIN (field)) 179690075Sobrien { 179790075Sobrien if (TREE_CODE (field) != FIELD_DECL) 179890075Sobrien continue; 179990075Sobrien 180090075Sobrien if (!DECL_BIT_FIELD_TYPE (field)) 180190075Sobrien return 1; 180290075Sobrien } 180390075Sobrien 180490075Sobrien return 0; 180590075Sobrien } 180690075Sobrien 180790075Sobrien if (TREE_CODE (type) == UNION_TYPE) 180890075Sobrien { 180990075Sobrien tree field; 181090075Sobrien 181190075Sobrien /* Unions can be returned in registers if every element is 181290075Sobrien integral, or can be returned in an integer register. */ 181390075Sobrien for (field = TYPE_FIELDS (type); 181490075Sobrien field; 181590075Sobrien field = TREE_CHAIN (field)) 181690075Sobrien { 181790075Sobrien if (TREE_CODE (field) != FIELD_DECL) 181890075Sobrien continue; 181990075Sobrien 182090075Sobrien if (FLOAT_TYPE_P (TREE_TYPE (field))) 182190075Sobrien return 1; 182290075Sobrien 182390075Sobrien if (RETURN_IN_MEMORY (TREE_TYPE (field))) 182490075Sobrien return 1; 182590075Sobrien } 182690075Sobrien 182790075Sobrien return 0; 182890075Sobrien } 182990075Sobrien#endif /* not ARM_WINCE */ 183090075Sobrien 183190075Sobrien /* Return all other types in memory. */ 183290075Sobrien return 1; 183390075Sobrien} 183490075Sobrien 183590075Sobrien/* Initialize a variable CUM of type CUMULATIVE_ARGS 183690075Sobrien for a call to a function whose data type is FNTYPE. 183790075Sobrien For a library call, FNTYPE is NULL. */ 183890075Sobrienvoid 183990075Sobrienarm_init_cumulative_args (pcum, fntype, libname, indirect) 184090075Sobrien CUMULATIVE_ARGS * pcum; 184190075Sobrien tree fntype; 184290075Sobrien rtx libname ATTRIBUTE_UNUSED; 184390075Sobrien int indirect ATTRIBUTE_UNUSED; 184490075Sobrien{ 184590075Sobrien /* On the ARM, the offset starts at 0. */ 184690075Sobrien pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype))) ? 1 : 0); 184790075Sobrien 184890075Sobrien pcum->call_cookie = CALL_NORMAL; 184990075Sobrien 185090075Sobrien if (TARGET_LONG_CALLS) 185190075Sobrien pcum->call_cookie = CALL_LONG; 185290075Sobrien 185390075Sobrien /* Check for long call/short call attributes. The attributes 185490075Sobrien override any command line option. */ 185590075Sobrien if (fntype) 185690075Sobrien { 185790075Sobrien if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (fntype))) 185890075Sobrien pcum->call_cookie = CALL_SHORT; 185990075Sobrien else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (fntype))) 186090075Sobrien pcum->call_cookie = CALL_LONG; 186190075Sobrien } 186290075Sobrien} 186390075Sobrien 186490075Sobrien/* Determine where to put an argument to a function. 186590075Sobrien Value is zero to push the argument on the stack, 186690075Sobrien or a hard register in which to store the argument. 186790075Sobrien 186890075Sobrien MODE is the argument's machine mode. 186990075Sobrien TYPE is the data type of the argument (as a tree). 187090075Sobrien This is null for libcalls where that information may 187190075Sobrien not be available. 187290075Sobrien CUM is a variable of type CUMULATIVE_ARGS which gives info about 187390075Sobrien the preceding args and about the function being called. 187490075Sobrien NAMED is nonzero if this argument is a named parameter 187590075Sobrien (otherwise it is an extra parameter matching an ellipsis). */ 187690075Sobrien 187790075Sobrienrtx 187890075Sobrienarm_function_arg (pcum, mode, type, named) 187990075Sobrien CUMULATIVE_ARGS * pcum; 188090075Sobrien enum machine_mode mode; 188190075Sobrien tree type ATTRIBUTE_UNUSED; 188290075Sobrien int named; 188390075Sobrien{ 188490075Sobrien if (mode == VOIDmode) 188590075Sobrien /* Compute operand 2 of the call insn. */ 188690075Sobrien return GEN_INT (pcum->call_cookie); 188790075Sobrien 188890075Sobrien if (!named || pcum->nregs >= NUM_ARG_REGS) 188990075Sobrien return NULL_RTX; 189090075Sobrien 189190075Sobrien return gen_rtx_REG (mode, pcum->nregs); 189290075Sobrien} 189390075Sobrien 189490075Sobrien/* Encode the current state of the #pragma [no_]long_calls. */ 189590075Sobrientypedef enum 189690075Sobrien{ 189790075Sobrien OFF, /* No #pramgma [no_]long_calls is in effect. */ 189890075Sobrien LONG, /* #pragma long_calls is in effect. */ 189990075Sobrien SHORT /* #pragma no_long_calls is in effect. */ 190090075Sobrien} arm_pragma_enum; 190190075Sobrien 190290075Sobrienstatic arm_pragma_enum arm_pragma_long_calls = OFF; 190390075Sobrien 190490075Sobrienvoid 190590075Sobrienarm_pr_long_calls (pfile) 190690075Sobrien cpp_reader * pfile ATTRIBUTE_UNUSED; 190790075Sobrien{ 190890075Sobrien arm_pragma_long_calls = LONG; 190990075Sobrien} 191090075Sobrien 191190075Sobrienvoid 191290075Sobrienarm_pr_no_long_calls (pfile) 191390075Sobrien cpp_reader * pfile ATTRIBUTE_UNUSED; 191490075Sobrien{ 191590075Sobrien arm_pragma_long_calls = SHORT; 191690075Sobrien} 191790075Sobrien 191890075Sobrienvoid 191990075Sobrienarm_pr_long_calls_off (pfile) 192090075Sobrien cpp_reader * pfile ATTRIBUTE_UNUSED; 192190075Sobrien{ 192290075Sobrien arm_pragma_long_calls = OFF; 192390075Sobrien} 192490075Sobrien 192590075Sobrien/* Table of machine attributes. */ 192690075Sobrienconst struct attribute_spec arm_attribute_table[] = 192790075Sobrien{ 192890075Sobrien /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ 192990075Sobrien /* Function calls made to this symbol must be done indirectly, because 193090075Sobrien it may lie outside of the 26 bit addressing range of a normal function 193190075Sobrien call. */ 193290075Sobrien { "long_call", 0, 0, false, true, true, NULL }, 193390075Sobrien /* Whereas these functions are always known to reside within the 26 bit 193490075Sobrien addressing range. */ 193590075Sobrien { "short_call", 0, 0, false, true, true, NULL }, 193690075Sobrien /* Interrupt Service Routines have special prologue and epilogue requirements. */ 193790075Sobrien { "isr", 0, 1, false, false, false, arm_handle_isr_attribute }, 193890075Sobrien { "interrupt", 0, 1, false, false, false, arm_handle_isr_attribute }, 193990075Sobrien { "naked", 0, 0, true, false, false, arm_handle_fndecl_attribute }, 194090075Sobrien#ifdef ARM_PE 194190075Sobrien /* ARM/PE has three new attributes: 194290075Sobrien interfacearm - ? 194390075Sobrien dllexport - for exporting a function/variable that will live in a dll 194490075Sobrien dllimport - for importing a function/variable from a dll 194590075Sobrien 194690075Sobrien Microsoft allows multiple declspecs in one __declspec, separating 194790075Sobrien them with spaces. We do NOT support this. Instead, use __declspec 194890075Sobrien multiple times. 194990075Sobrien */ 195090075Sobrien { "dllimport", 0, 0, true, false, false, NULL }, 195190075Sobrien { "dllexport", 0, 0, true, false, false, NULL }, 195290075Sobrien { "interfacearm", 0, 0, true, false, false, arm_handle_fndecl_attribute }, 195390075Sobrien#endif 195490075Sobrien { NULL, 0, 0, false, false, false, NULL } 195590075Sobrien}; 195690075Sobrien 195790075Sobrien/* Handle an attribute requiring a FUNCTION_DECL; 195890075Sobrien arguments as in struct attribute_spec.handler. */ 195990075Sobrien 196090075Sobrienstatic tree 196190075Sobrienarm_handle_fndecl_attribute (node, name, args, flags, no_add_attrs) 196290075Sobrien tree * node; 196390075Sobrien tree name; 196490075Sobrien tree args ATTRIBUTE_UNUSED; 196590075Sobrien int flags ATTRIBUTE_UNUSED; 196690075Sobrien bool * no_add_attrs; 196790075Sobrien{ 196890075Sobrien if (TREE_CODE (*node) != FUNCTION_DECL) 196990075Sobrien { 197090075Sobrien warning ("`%s' attribute only applies to functions", 197190075Sobrien IDENTIFIER_POINTER (name)); 197290075Sobrien *no_add_attrs = true; 197390075Sobrien } 197490075Sobrien 197590075Sobrien return NULL_TREE; 197690075Sobrien} 197790075Sobrien 197890075Sobrien/* Handle an "interrupt" or "isr" attribute; 197990075Sobrien arguments as in struct attribute_spec.handler. */ 198090075Sobrien 198190075Sobrienstatic tree 198290075Sobrienarm_handle_isr_attribute (node, name, args, flags, no_add_attrs) 198390075Sobrien tree * node; 198490075Sobrien tree name; 198590075Sobrien tree args; 198690075Sobrien int flags; 198790075Sobrien bool * no_add_attrs; 198890075Sobrien{ 198990075Sobrien if (DECL_P (*node)) 199090075Sobrien { 199190075Sobrien if (TREE_CODE (*node) != FUNCTION_DECL) 199290075Sobrien { 199390075Sobrien warning ("`%s' attribute only applies to functions", 199490075Sobrien IDENTIFIER_POINTER (name)); 199590075Sobrien *no_add_attrs = true; 199690075Sobrien } 199790075Sobrien /* FIXME: the argument if any is checked for type attributes; 199890075Sobrien should it be checked for decl ones? */ 199990075Sobrien } 200090075Sobrien else 200190075Sobrien { 200290075Sobrien if (TREE_CODE (*node) == FUNCTION_TYPE 200390075Sobrien || TREE_CODE (*node) == METHOD_TYPE) 200490075Sobrien { 200590075Sobrien if (arm_isr_value (args) == ARM_FT_UNKNOWN) 200690075Sobrien { 200790075Sobrien warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); 200890075Sobrien *no_add_attrs = true; 200990075Sobrien } 201090075Sobrien } 201190075Sobrien else if (TREE_CODE (*node) == POINTER_TYPE 201290075Sobrien && (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE 201390075Sobrien || TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE) 201490075Sobrien && arm_isr_value (args) != ARM_FT_UNKNOWN) 201590075Sobrien { 201690075Sobrien *node = build_type_copy (*node); 201790075Sobrien TREE_TYPE (*node) = build_type_attribute_variant 201890075Sobrien (TREE_TYPE (*node), 201990075Sobrien tree_cons (name, args, TYPE_ATTRIBUTES (TREE_TYPE (*node)))); 202090075Sobrien *no_add_attrs = true; 202190075Sobrien } 202290075Sobrien else 202390075Sobrien { 202490075Sobrien /* Possibly pass this attribute on from the type to a decl. */ 202590075Sobrien if (flags & ((int) ATTR_FLAG_DECL_NEXT 202690075Sobrien | (int) ATTR_FLAG_FUNCTION_NEXT 202790075Sobrien | (int) ATTR_FLAG_ARRAY_NEXT)) 202890075Sobrien { 202990075Sobrien *no_add_attrs = true; 203090075Sobrien return tree_cons (name, args, NULL_TREE); 203190075Sobrien } 203290075Sobrien else 203390075Sobrien { 203490075Sobrien warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); 203590075Sobrien } 203690075Sobrien } 203790075Sobrien } 203890075Sobrien 203990075Sobrien return NULL_TREE; 204090075Sobrien} 204190075Sobrien 204290075Sobrien/* Return 0 if the attributes for two types are incompatible, 1 if they 204390075Sobrien are compatible, and 2 if they are nearly compatible (which causes a 204490075Sobrien warning to be generated). */ 204590075Sobrien 204690075Sobrienstatic int 204790075Sobrienarm_comp_type_attributes (type1, type2) 204890075Sobrien tree type1; 204990075Sobrien tree type2; 205090075Sobrien{ 205190075Sobrien int l1, l2, s1, s2; 205290075Sobrien 205390075Sobrien /* Check for mismatch of non-default calling convention. */ 205490075Sobrien if (TREE_CODE (type1) != FUNCTION_TYPE) 205590075Sobrien return 1; 205690075Sobrien 205790075Sobrien /* Check for mismatched call attributes. */ 205890075Sobrien l1 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type1)) != NULL; 205990075Sobrien l2 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type2)) != NULL; 206090075Sobrien s1 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type1)) != NULL; 206190075Sobrien s2 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type2)) != NULL; 206290075Sobrien 206390075Sobrien /* Only bother to check if an attribute is defined. */ 206490075Sobrien if (l1 | l2 | s1 | s2) 206590075Sobrien { 206690075Sobrien /* If one type has an attribute, the other must have the same attribute. */ 206790075Sobrien if ((l1 != l2) || (s1 != s2)) 206890075Sobrien return 0; 206990075Sobrien 207090075Sobrien /* Disallow mixed attributes. */ 207190075Sobrien if ((l1 & s2) || (l2 & s1)) 207290075Sobrien return 0; 207390075Sobrien } 207490075Sobrien 207590075Sobrien /* Check for mismatched ISR attribute. */ 207690075Sobrien l1 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type1)) != NULL; 207790075Sobrien if (! l1) 207890075Sobrien l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type1)) != NULL; 207990075Sobrien l2 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type2)) != NULL; 208090075Sobrien if (! l2) 208190075Sobrien l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type2)) != NULL; 208290075Sobrien if (l1 != l2) 208390075Sobrien return 0; 208490075Sobrien 208590075Sobrien return 1; 208690075Sobrien} 208790075Sobrien 208890075Sobrien/* Encode long_call or short_call attribute by prefixing 208990075Sobrien symbol name in DECL with a special character FLAG. */ 209090075Sobrien 209190075Sobrienvoid 209290075Sobrienarm_encode_call_attribute (decl, flag) 209390075Sobrien tree decl; 209490075Sobrien int flag; 209590075Sobrien{ 209690075Sobrien const char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0); 209790075Sobrien int len = strlen (str); 209890075Sobrien char * newstr; 209990075Sobrien 210090075Sobrien /* Do not allow weak functions to be treated as short call. */ 210190075Sobrien if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR) 210290075Sobrien return; 210390075Sobrien 210490075Sobrien newstr = alloca (len + 2); 210590075Sobrien newstr[0] = flag; 210690075Sobrien strcpy (newstr + 1, str); 210790075Sobrien 210890075Sobrien newstr = (char *) ggc_alloc_string (newstr, len + 1); 210990075Sobrien XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr; 211090075Sobrien} 211190075Sobrien 211290075Sobrien/* Assigns default attributes to newly defined type. This is used to 211390075Sobrien set short_call/long_call attributes for function types of 211490075Sobrien functions defined inside corresponding #pragma scopes. */ 211590075Sobrien 211690075Sobrienstatic void 211790075Sobrienarm_set_default_type_attributes (type) 211890075Sobrien tree type; 211990075Sobrien{ 212090075Sobrien /* Add __attribute__ ((long_call)) to all functions, when 212190075Sobrien inside #pragma long_calls or __attribute__ ((short_call)), 212290075Sobrien when inside #pragma no_long_calls. */ 212390075Sobrien if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) 212490075Sobrien { 212590075Sobrien tree type_attr_list, attr_name; 212690075Sobrien type_attr_list = TYPE_ATTRIBUTES (type); 212790075Sobrien 212890075Sobrien if (arm_pragma_long_calls == LONG) 212990075Sobrien attr_name = get_identifier ("long_call"); 213090075Sobrien else if (arm_pragma_long_calls == SHORT) 213190075Sobrien attr_name = get_identifier ("short_call"); 213290075Sobrien else 213390075Sobrien return; 213490075Sobrien 213590075Sobrien type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list); 213690075Sobrien TYPE_ATTRIBUTES (type) = type_attr_list; 213790075Sobrien } 213890075Sobrien} 213990075Sobrien 214090075Sobrien/* Return 1 if the operand is a SYMBOL_REF for a function known to be 214190075Sobrien defined within the current compilation unit. If this caanot be 214290075Sobrien determined, then 0 is returned. */ 214390075Sobrien 214490075Sobrienstatic int 214590075Sobriencurrent_file_function_operand (sym_ref) 214690075Sobrien rtx sym_ref; 214790075Sobrien{ 214890075Sobrien /* This is a bit of a fib. A function will have a short call flag 214990075Sobrien applied to its name if it has the short call attribute, or it has 215090075Sobrien already been defined within the current compilation unit. */ 215190075Sobrien if (ENCODED_SHORT_CALL_ATTR_P (XSTR (sym_ref, 0))) 215290075Sobrien return 1; 215390075Sobrien 215490075Sobrien /* The current function is always defined within the current compilation 215590075Sobrien unit. if it s a weak definition however, then this may not be the real 215690075Sobrien definition of the function, and so we have to say no. */ 215790075Sobrien if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0) 215890075Sobrien && !DECL_WEAK (current_function_decl)) 215990075Sobrien return 1; 216090075Sobrien 216190075Sobrien /* We cannot make the determination - default to returning 0. */ 216290075Sobrien return 0; 216390075Sobrien} 216490075Sobrien 216590075Sobrien/* Return non-zero if a 32 bit "long_call" should be generated for 216690075Sobrien this call. We generate a long_call if the function: 216790075Sobrien 216890075Sobrien a. has an __attribute__((long call)) 216990075Sobrien or b. is within the scope of a #pragma long_calls 217090075Sobrien or c. the -mlong-calls command line switch has been specified 217190075Sobrien 217290075Sobrien However we do not generate a long call if the function: 217390075Sobrien 217490075Sobrien d. has an __attribute__ ((short_call)) 217590075Sobrien or e. is inside the scope of a #pragma no_long_calls 217690075Sobrien or f. has an __attribute__ ((section)) 217790075Sobrien or g. is defined within the current compilation unit. 217890075Sobrien 217990075Sobrien This function will be called by C fragments contained in the machine 218090075Sobrien description file. CALL_REF and CALL_COOKIE correspond to the matched 218190075Sobrien rtl operands. CALL_SYMBOL is used to distinguish between 218290075Sobrien two different callers of the function. It is set to 1 in the 218390075Sobrien "call_symbol" and "call_symbol_value" patterns and to 0 in the "call" 218490075Sobrien and "call_value" patterns. This is because of the difference in the 218590075Sobrien SYM_REFs passed by these patterns. */ 218690075Sobrien 218790075Sobrienint 218890075Sobrienarm_is_longcall_p (sym_ref, call_cookie, call_symbol) 218990075Sobrien rtx sym_ref; 219090075Sobrien int call_cookie; 219190075Sobrien int call_symbol; 219290075Sobrien{ 219390075Sobrien if (!call_symbol) 219490075Sobrien { 219590075Sobrien if (GET_CODE (sym_ref) != MEM) 219690075Sobrien return 0; 219790075Sobrien 219890075Sobrien sym_ref = XEXP (sym_ref, 0); 219990075Sobrien } 220090075Sobrien 220190075Sobrien if (GET_CODE (sym_ref) != SYMBOL_REF) 220290075Sobrien return 0; 220390075Sobrien 220490075Sobrien if (call_cookie & CALL_SHORT) 220590075Sobrien return 0; 220690075Sobrien 220790075Sobrien if (TARGET_LONG_CALLS && flag_function_sections) 220890075Sobrien return 1; 220990075Sobrien 221090075Sobrien if (current_file_function_operand (sym_ref)) 221190075Sobrien return 0; 221290075Sobrien 221390075Sobrien return (call_cookie & CALL_LONG) 221490075Sobrien || ENCODED_LONG_CALL_ATTR_P (XSTR (sym_ref, 0)) 221590075Sobrien || TARGET_LONG_CALLS; 221690075Sobrien} 221790075Sobrien 221890075Sobrien/* Return non-zero if it is ok to make a tail-call to DECL. */ 221990075Sobrien 222090075Sobrienint 222190075Sobrienarm_function_ok_for_sibcall (decl) 222290075Sobrien tree decl; 222390075Sobrien{ 222490075Sobrien int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL; 222590075Sobrien 222690075Sobrien /* Never tailcall something for which we have no decl, or if we 222790075Sobrien are in Thumb mode. */ 222890075Sobrien if (decl == NULL || TARGET_THUMB) 222990075Sobrien return 0; 223090075Sobrien 223190075Sobrien /* Get the calling method. */ 223290075Sobrien if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) 223390075Sobrien call_type = CALL_SHORT; 223490075Sobrien else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) 223590075Sobrien call_type = CALL_LONG; 223690075Sobrien 223790075Sobrien /* Cannot tail-call to long calls, since these are out of range of 223890075Sobrien a branch instruction. However, if not compiling PIC, we know 223990075Sobrien we can reach the symbol if it is in this compilation unit. */ 224090075Sobrien if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl))) 224190075Sobrien return 0; 224290075Sobrien 224390075Sobrien /* If we are interworking and the function is not declared static 224490075Sobrien then we can't tail-call it unless we know that it exists in this 224590075Sobrien compilation unit (since it might be a Thumb routine). */ 224690075Sobrien if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl)) 224790075Sobrien return 0; 224890075Sobrien 224990075Sobrien /* Never tailcall from an ISR routine - it needs a special exit sequence. */ 225090075Sobrien if (IS_INTERRUPT (arm_current_func_type ())) 225190075Sobrien return 0; 225290075Sobrien 225390075Sobrien /* Everything else is ok. */ 225490075Sobrien return 1; 225590075Sobrien} 225690075Sobrien 225790075Sobrien 225890075Sobrienint 225990075Sobrienlegitimate_pic_operand_p (x) 226090075Sobrien rtx x; 226190075Sobrien{ 226290075Sobrien if (CONSTANT_P (x) 226390075Sobrien && flag_pic 226490075Sobrien && (GET_CODE (x) == SYMBOL_REF 226590075Sobrien || (GET_CODE (x) == CONST 226690075Sobrien && GET_CODE (XEXP (x, 0)) == PLUS 226790075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF))) 226890075Sobrien return 0; 226990075Sobrien 227090075Sobrien return 1; 227190075Sobrien} 227290075Sobrien 227390075Sobrienrtx 227490075Sobrienlegitimize_pic_address (orig, mode, reg) 227590075Sobrien rtx orig; 227690075Sobrien enum machine_mode mode; 227790075Sobrien rtx reg; 227890075Sobrien{ 227990075Sobrien if (GET_CODE (orig) == SYMBOL_REF 228090075Sobrien || GET_CODE (orig) == LABEL_REF) 228190075Sobrien { 228290075Sobrien#ifndef AOF_ASSEMBLER 228390075Sobrien rtx pic_ref, address; 228490075Sobrien#endif 228590075Sobrien rtx insn; 228690075Sobrien int subregs = 0; 228790075Sobrien 228890075Sobrien if (reg == 0) 228990075Sobrien { 229090075Sobrien if (no_new_pseudos) 229190075Sobrien abort (); 229290075Sobrien else 229390075Sobrien reg = gen_reg_rtx (Pmode); 229490075Sobrien 229590075Sobrien subregs = 1; 229690075Sobrien } 229790075Sobrien 229890075Sobrien#ifdef AOF_ASSEMBLER 229990075Sobrien /* The AOF assembler can generate relocations for these directly, and 230090075Sobrien understands that the PIC register has to be added into the offset. */ 230190075Sobrien insn = emit_insn (gen_pic_load_addr_based (reg, orig)); 230290075Sobrien#else 230390075Sobrien if (subregs) 230490075Sobrien address = gen_reg_rtx (Pmode); 230590075Sobrien else 230690075Sobrien address = reg; 230790075Sobrien 230890075Sobrien if (TARGET_ARM) 230990075Sobrien emit_insn (gen_pic_load_addr_arm (address, orig)); 231090075Sobrien else 231190075Sobrien emit_insn (gen_pic_load_addr_thumb (address, orig)); 231290075Sobrien 231396263Sobrien if ((GET_CODE (orig) == LABEL_REF 231496263Sobrien || (GET_CODE (orig) == SYMBOL_REF && 231596263Sobrien ENCODED_SHORT_CALL_ATTR_P (XSTR (orig, 0)))) 231696263Sobrien && NEED_GOT_RELOC) 231790075Sobrien pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, address); 231890075Sobrien else 231990075Sobrien { 232090075Sobrien pic_ref = gen_rtx_MEM (Pmode, 232190075Sobrien gen_rtx_PLUS (Pmode, pic_offset_table_rtx, 232290075Sobrien address)); 232390075Sobrien RTX_UNCHANGING_P (pic_ref) = 1; 232490075Sobrien } 232590075Sobrien 232690075Sobrien insn = emit_move_insn (reg, pic_ref); 232790075Sobrien#endif 232890075Sobrien current_function_uses_pic_offset_table = 1; 232990075Sobrien /* Put a REG_EQUAL note on this insn, so that it can be optimized 233090075Sobrien by loop. */ 233190075Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, 233290075Sobrien REG_NOTES (insn)); 233390075Sobrien return reg; 233490075Sobrien } 233590075Sobrien else if (GET_CODE (orig) == CONST) 233690075Sobrien { 233790075Sobrien rtx base, offset; 233890075Sobrien 233990075Sobrien if (GET_CODE (XEXP (orig, 0)) == PLUS 234090075Sobrien && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) 234190075Sobrien return orig; 234290075Sobrien 234390075Sobrien if (reg == 0) 234490075Sobrien { 234590075Sobrien if (no_new_pseudos) 234690075Sobrien abort (); 234790075Sobrien else 234890075Sobrien reg = gen_reg_rtx (Pmode); 234990075Sobrien } 235090075Sobrien 235190075Sobrien if (GET_CODE (XEXP (orig, 0)) == PLUS) 235290075Sobrien { 235390075Sobrien base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); 235490075Sobrien offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, 235590075Sobrien base == reg ? 0 : reg); 235690075Sobrien } 235790075Sobrien else 235890075Sobrien abort (); 235990075Sobrien 236090075Sobrien if (GET_CODE (offset) == CONST_INT) 236190075Sobrien { 236290075Sobrien /* The base register doesn't really matter, we only want to 236390075Sobrien test the index for the appropriate mode. */ 236490075Sobrien ARM_GO_IF_LEGITIMATE_INDEX (mode, 0, offset, win); 236590075Sobrien 236690075Sobrien if (!no_new_pseudos) 236790075Sobrien offset = force_reg (Pmode, offset); 236890075Sobrien else 236990075Sobrien abort (); 237090075Sobrien 237190075Sobrien win: 237290075Sobrien if (GET_CODE (offset) == CONST_INT) 237390075Sobrien return plus_constant (base, INTVAL (offset)); 237490075Sobrien } 237590075Sobrien 237690075Sobrien if (GET_MODE_SIZE (mode) > 4 237790075Sobrien && (GET_MODE_CLASS (mode) == MODE_INT 237890075Sobrien || TARGET_SOFT_FLOAT)) 237990075Sobrien { 238090075Sobrien emit_insn (gen_addsi3 (reg, base, offset)); 238190075Sobrien return reg; 238290075Sobrien } 238390075Sobrien 238490075Sobrien return gen_rtx_PLUS (Pmode, base, offset); 238590075Sobrien } 238690075Sobrien 238790075Sobrien return orig; 238890075Sobrien} 238990075Sobrien 239090075Sobrien/* Generate code to load the PIC register. PROLOGUE is true if 239190075Sobrien called from arm_expand_prologue (in which case we want the 239290075Sobrien generated insns at the start of the function); false if called 239390075Sobrien by an exception receiver that needs the PIC register reloaded 239490075Sobrien (in which case the insns are just dumped at the current location). */ 239590075Sobrien 239690075Sobrienvoid 239790075Sobrienarm_finalize_pic (prologue) 239890075Sobrien int prologue ATTRIBUTE_UNUSED; 239990075Sobrien{ 240090075Sobrien#ifndef AOF_ASSEMBLER 240190075Sobrien rtx l1, pic_tmp, pic_tmp2, seq, pic_rtx; 240290075Sobrien rtx global_offset_table; 240390075Sobrien 240490075Sobrien if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE) 240590075Sobrien return; 240690075Sobrien 240790075Sobrien if (!flag_pic) 240890075Sobrien abort (); 240990075Sobrien 241090075Sobrien start_sequence (); 241190075Sobrien l1 = gen_label_rtx (); 241290075Sobrien 241390075Sobrien global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); 241490075Sobrien /* On the ARM the PC register contains 'dot + 8' at the time of the 241590075Sobrien addition, on the Thumb it is 'dot + 4'. */ 241690075Sobrien pic_tmp = plus_constant (gen_rtx_LABEL_REF (Pmode, l1), TARGET_ARM ? 8 : 4); 241790075Sobrien if (GOT_PCREL) 241890075Sobrien pic_tmp2 = gen_rtx_CONST (VOIDmode, 241990075Sobrien gen_rtx_PLUS (Pmode, global_offset_table, pc_rtx)); 242090075Sobrien else 242190075Sobrien pic_tmp2 = gen_rtx_CONST (VOIDmode, global_offset_table); 242290075Sobrien 242390075Sobrien pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp)); 242490075Sobrien 242590075Sobrien if (TARGET_ARM) 242690075Sobrien { 242790075Sobrien emit_insn (gen_pic_load_addr_arm (pic_offset_table_rtx, pic_rtx)); 242890075Sobrien emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1)); 242990075Sobrien } 243090075Sobrien else 243190075Sobrien { 243290075Sobrien emit_insn (gen_pic_load_addr_thumb (pic_offset_table_rtx, pic_rtx)); 243390075Sobrien emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1)); 243490075Sobrien } 243590075Sobrien 243690075Sobrien seq = gen_sequence (); 243790075Sobrien end_sequence (); 243890075Sobrien if (prologue) 243990075Sobrien emit_insn_after (seq, get_insns ()); 244090075Sobrien else 244190075Sobrien emit_insn (seq); 244290075Sobrien 244390075Sobrien /* Need to emit this whether or not we obey regdecls, 244490075Sobrien since setjmp/longjmp can cause life info to screw up. */ 244590075Sobrien emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); 244690075Sobrien#endif /* AOF_ASSEMBLER */ 244790075Sobrien} 244890075Sobrien 244990075Sobrien#define REG_OR_SUBREG_REG(X) \ 245090075Sobrien (GET_CODE (X) == REG \ 245190075Sobrien || (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG)) 245290075Sobrien 245390075Sobrien#define REG_OR_SUBREG_RTX(X) \ 245490075Sobrien (GET_CODE (X) == REG ? (X) : SUBREG_REG (X)) 245590075Sobrien 245690075Sobrien#ifndef COSTS_N_INSNS 245790075Sobrien#define COSTS_N_INSNS(N) ((N) * 4 - 2) 245890075Sobrien#endif 245990075Sobrien 246090075Sobrienint 246190075Sobrienarm_rtx_costs (x, code, outer) 246290075Sobrien rtx x; 246390075Sobrien enum rtx_code code; 246490075Sobrien enum rtx_code outer; 246590075Sobrien{ 246690075Sobrien enum machine_mode mode = GET_MODE (x); 246790075Sobrien enum rtx_code subcode; 246890075Sobrien int extra_cost; 246990075Sobrien 247090075Sobrien if (TARGET_THUMB) 247190075Sobrien { 247290075Sobrien switch (code) 247390075Sobrien { 247490075Sobrien case ASHIFT: 247590075Sobrien case ASHIFTRT: 247690075Sobrien case LSHIFTRT: 247790075Sobrien case ROTATERT: 247890075Sobrien case PLUS: 247990075Sobrien case MINUS: 248090075Sobrien case COMPARE: 248190075Sobrien case NEG: 248290075Sobrien case NOT: 248390075Sobrien return COSTS_N_INSNS (1); 248490075Sobrien 248590075Sobrien case MULT: 248690075Sobrien if (GET_CODE (XEXP (x, 1)) == CONST_INT) 248790075Sobrien { 248890075Sobrien int cycles = 0; 248990075Sobrien unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1)); 249090075Sobrien 249190075Sobrien while (i) 249290075Sobrien { 249390075Sobrien i >>= 2; 249490075Sobrien cycles++; 249590075Sobrien } 249690075Sobrien return COSTS_N_INSNS (2) + cycles; 249790075Sobrien } 249890075Sobrien return COSTS_N_INSNS (1) + 16; 249990075Sobrien 250090075Sobrien case SET: 250190075Sobrien return (COSTS_N_INSNS (1) 250290075Sobrien + 4 * ((GET_CODE (SET_SRC (x)) == MEM) 250390075Sobrien + GET_CODE (SET_DEST (x)) == MEM)); 250490075Sobrien 250590075Sobrien case CONST_INT: 250690075Sobrien if (outer == SET) 250790075Sobrien { 250890075Sobrien if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256) 250990075Sobrien return 0; 251090075Sobrien if (thumb_shiftable_const (INTVAL (x))) 251190075Sobrien return COSTS_N_INSNS (2); 251290075Sobrien return COSTS_N_INSNS (3); 251390075Sobrien } 251490075Sobrien else if (outer == PLUS 251590075Sobrien && INTVAL (x) < 256 && INTVAL (x) > -256) 251690075Sobrien return 0; 251790075Sobrien else if (outer == COMPARE 251890075Sobrien && (unsigned HOST_WIDE_INT) INTVAL (x) < 256) 251990075Sobrien return 0; 252090075Sobrien else if (outer == ASHIFT || outer == ASHIFTRT 252190075Sobrien || outer == LSHIFTRT) 252290075Sobrien return 0; 252390075Sobrien return COSTS_N_INSNS (2); 252490075Sobrien 252590075Sobrien case CONST: 252690075Sobrien case CONST_DOUBLE: 252790075Sobrien case LABEL_REF: 252890075Sobrien case SYMBOL_REF: 252990075Sobrien return COSTS_N_INSNS (3); 253090075Sobrien 253190075Sobrien case UDIV: 253290075Sobrien case UMOD: 253390075Sobrien case DIV: 253490075Sobrien case MOD: 253590075Sobrien return 100; 253690075Sobrien 253790075Sobrien case TRUNCATE: 253890075Sobrien return 99; 253990075Sobrien 254090075Sobrien case AND: 254190075Sobrien case XOR: 254290075Sobrien case IOR: 254390075Sobrien /* XXX guess. */ 254490075Sobrien return 8; 254590075Sobrien 254690075Sobrien case ADDRESSOF: 254790075Sobrien case MEM: 254890075Sobrien /* XXX another guess. */ 254990075Sobrien /* Memory costs quite a lot for the first word, but subsequent words 255090075Sobrien load at the equivalent of a single insn each. */ 255190075Sobrien return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) 255290075Sobrien + (CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); 255390075Sobrien 255490075Sobrien case IF_THEN_ELSE: 255590075Sobrien /* XXX a guess. */ 255690075Sobrien if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) 255790075Sobrien return 14; 255890075Sobrien return 2; 255990075Sobrien 256090075Sobrien case ZERO_EXTEND: 256190075Sobrien /* XXX still guessing. */ 256290075Sobrien switch (GET_MODE (XEXP (x, 0))) 256390075Sobrien { 256490075Sobrien case QImode: 256590075Sobrien return (1 + (mode == DImode ? 4 : 0) 256690075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 256790075Sobrien 256890075Sobrien case HImode: 256990075Sobrien return (4 + (mode == DImode ? 4 : 0) 257090075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 257190075Sobrien 257290075Sobrien case SImode: 257390075Sobrien return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 257490075Sobrien 257590075Sobrien default: 257690075Sobrien return 99; 257790075Sobrien } 257890075Sobrien 257990075Sobrien default: 258090075Sobrien return 99; 258190075Sobrien#if 0 258290075Sobrien case FFS: 258390075Sobrien case FLOAT: 258490075Sobrien case FIX: 258590075Sobrien case UNSIGNED_FIX: 258690075Sobrien /* XXX guess */ 258790075Sobrien fprintf (stderr, "unexpected code for thumb in rtx_costs: %s\n", 258890075Sobrien rtx_name[code]); 258990075Sobrien abort (); 259090075Sobrien#endif 259190075Sobrien } 259290075Sobrien } 259390075Sobrien 259490075Sobrien switch (code) 259590075Sobrien { 259690075Sobrien case MEM: 259790075Sobrien /* Memory costs quite a lot for the first word, but subsequent words 259890075Sobrien load at the equivalent of a single insn each. */ 259990075Sobrien return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) 260090075Sobrien + (CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); 260190075Sobrien 260290075Sobrien case DIV: 260390075Sobrien case MOD: 260490075Sobrien return 100; 260590075Sobrien 260690075Sobrien case ROTATE: 260790075Sobrien if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG) 260890075Sobrien return 4; 260990075Sobrien /* Fall through */ 261090075Sobrien case ROTATERT: 261190075Sobrien if (mode != SImode) 261290075Sobrien return 8; 261390075Sobrien /* Fall through */ 261490075Sobrien case ASHIFT: case LSHIFTRT: case ASHIFTRT: 261590075Sobrien if (mode == DImode) 261690075Sobrien return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8) 261790075Sobrien + ((GET_CODE (XEXP (x, 0)) == REG 261890075Sobrien || (GET_CODE (XEXP (x, 0)) == SUBREG 261990075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) 262090075Sobrien ? 0 : 8)); 262190075Sobrien return (1 + ((GET_CODE (XEXP (x, 0)) == REG 262290075Sobrien || (GET_CODE (XEXP (x, 0)) == SUBREG 262390075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) 262490075Sobrien ? 0 : 4) 262590075Sobrien + ((GET_CODE (XEXP (x, 1)) == REG 262690075Sobrien || (GET_CODE (XEXP (x, 1)) == SUBREG 262790075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG) 262890075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT)) 262990075Sobrien ? 0 : 4)); 263090075Sobrien 263190075Sobrien case MINUS: 263290075Sobrien if (mode == DImode) 263390075Sobrien return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8) 263490075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 0)) 263590075Sobrien || (GET_CODE (XEXP (x, 0)) == CONST_INT 263690075Sobrien && const_ok_for_arm (INTVAL (XEXP (x, 0))))) 263790075Sobrien ? 0 : 8)); 263890075Sobrien 263990075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 264090075Sobrien return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 264190075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE 264290075Sobrien && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) 264390075Sobrien ? 0 : 8) 264490075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 0)) 264590075Sobrien || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE 264690075Sobrien && const_double_rtx_ok_for_fpu (XEXP (x, 0)))) 264790075Sobrien ? 0 : 8)); 264890075Sobrien 264990075Sobrien if (((GET_CODE (XEXP (x, 0)) == CONST_INT 265090075Sobrien && const_ok_for_arm (INTVAL (XEXP (x, 0))) 265190075Sobrien && REG_OR_SUBREG_REG (XEXP (x, 1)))) 265290075Sobrien || (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT 265390075Sobrien || subcode == ASHIFTRT || subcode == LSHIFTRT 265490075Sobrien || subcode == ROTATE || subcode == ROTATERT 265590075Sobrien || (subcode == MULT 265690075Sobrien && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT 265790075Sobrien && ((INTVAL (XEXP (XEXP (x, 1), 1)) & 265890075Sobrien (INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0))) 265990075Sobrien && REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0)) 266090075Sobrien && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1)) 266190075Sobrien || GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT) 266290075Sobrien && REG_OR_SUBREG_REG (XEXP (x, 0)))) 266390075Sobrien return 1; 266490075Sobrien /* Fall through */ 266590075Sobrien 266690075Sobrien case PLUS: 266790075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 266890075Sobrien return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) 266990075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 267090075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE 267190075Sobrien && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) 267290075Sobrien ? 0 : 8)); 267390075Sobrien 267490075Sobrien /* Fall through */ 267590075Sobrien case AND: case XOR: case IOR: 267690075Sobrien extra_cost = 0; 267790075Sobrien 267890075Sobrien /* Normally the frame registers will be spilt into reg+const during 267990075Sobrien reload, so it is a bad idea to combine them with other instructions, 268090075Sobrien since then they might not be moved outside of loops. As a compromise 268190075Sobrien we allow integration with ops that have a constant as their second 268290075Sobrien operand. */ 268390075Sobrien if ((REG_OR_SUBREG_REG (XEXP (x, 0)) 268490075Sobrien && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))) 268590075Sobrien && GET_CODE (XEXP (x, 1)) != CONST_INT) 268690075Sobrien || (REG_OR_SUBREG_REG (XEXP (x, 0)) 268790075Sobrien && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))))) 268890075Sobrien extra_cost = 4; 268990075Sobrien 269090075Sobrien if (mode == DImode) 269190075Sobrien return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) 269290075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 269390075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT 269490075Sobrien && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) 269590075Sobrien ? 0 : 8)); 269690075Sobrien 269790075Sobrien if (REG_OR_SUBREG_REG (XEXP (x, 0))) 269890075Sobrien return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost) 269990075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 270090075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT 270190075Sobrien && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) 270290075Sobrien ? 0 : 4)); 270390075Sobrien 270490075Sobrien else if (REG_OR_SUBREG_REG (XEXP (x, 1))) 270590075Sobrien return (1 + extra_cost 270690075Sobrien + ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT 270790075Sobrien || subcode == LSHIFTRT || subcode == ASHIFTRT 270890075Sobrien || subcode == ROTATE || subcode == ROTATERT 270990075Sobrien || (subcode == MULT 271090075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT 271190075Sobrien && ((INTVAL (XEXP (XEXP (x, 0), 1)) & 271290075Sobrien (INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0))) 271390075Sobrien && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0))) 271490075Sobrien && ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1))) 271590075Sobrien || GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)) 271690075Sobrien ? 0 : 4)); 271790075Sobrien 271890075Sobrien return 8; 271990075Sobrien 272090075Sobrien case MULT: 272190075Sobrien /* There is no point basing this on the tuning, since it is always the 272290075Sobrien fast variant if it exists at all. */ 272390075Sobrien if (arm_fast_multiply && mode == DImode 272490075Sobrien && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1))) 272590075Sobrien && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND 272690075Sobrien || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)) 272790075Sobrien return 8; 272890075Sobrien 272990075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT 273090075Sobrien || mode == DImode) 273190075Sobrien return 30; 273290075Sobrien 273390075Sobrien if (GET_CODE (XEXP (x, 1)) == CONST_INT) 273490075Sobrien { 273590075Sobrien unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1)) 273690075Sobrien & (unsigned HOST_WIDE_INT) 0xffffffff); 273790075Sobrien int add_cost = const_ok_for_arm (i) ? 4 : 8; 273890075Sobrien int j; 273990075Sobrien 274090075Sobrien /* Tune as appropriate. */ 274190075Sobrien int booth_unit_size = ((tune_flags & FL_FAST_MULT) ? 8 : 2); 274290075Sobrien 274390075Sobrien for (j = 0; i && j < 32; j += booth_unit_size) 274490075Sobrien { 274590075Sobrien i >>= booth_unit_size; 274690075Sobrien add_cost += 2; 274790075Sobrien } 274890075Sobrien 274990075Sobrien return add_cost; 275090075Sobrien } 275190075Sobrien 275290075Sobrien return (((tune_flags & FL_FAST_MULT) ? 8 : 30) 275390075Sobrien + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4) 275490075Sobrien + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4)); 275590075Sobrien 275690075Sobrien case TRUNCATE: 275790075Sobrien if (arm_fast_multiply && mode == SImode 275890075Sobrien && GET_CODE (XEXP (x, 0)) == LSHIFTRT 275990075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT 276090075Sobrien && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) 276190075Sobrien == GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1))) 276290075Sobrien && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND 276390075Sobrien || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND)) 276490075Sobrien return 8; 276590075Sobrien return 99; 276690075Sobrien 276790075Sobrien case NEG: 276890075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 276990075Sobrien return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6); 277090075Sobrien /* Fall through */ 277190075Sobrien case NOT: 277290075Sobrien if (mode == DImode) 277390075Sobrien return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); 277490075Sobrien 277590075Sobrien return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); 277690075Sobrien 277790075Sobrien case IF_THEN_ELSE: 277890075Sobrien if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) 277990075Sobrien return 14; 278090075Sobrien return 2; 278190075Sobrien 278290075Sobrien case COMPARE: 278390075Sobrien return 1; 278490075Sobrien 278590075Sobrien case ABS: 278690075Sobrien return 4 + (mode == DImode ? 4 : 0); 278790075Sobrien 278890075Sobrien case SIGN_EXTEND: 278990075Sobrien if (GET_MODE (XEXP (x, 0)) == QImode) 279090075Sobrien return (4 + (mode == DImode ? 4 : 0) 279190075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 279290075Sobrien /* Fall through */ 279390075Sobrien case ZERO_EXTEND: 279490075Sobrien switch (GET_MODE (XEXP (x, 0))) 279590075Sobrien { 279690075Sobrien case QImode: 279790075Sobrien return (1 + (mode == DImode ? 4 : 0) 279890075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 279990075Sobrien 280090075Sobrien case HImode: 280190075Sobrien return (4 + (mode == DImode ? 4 : 0) 280290075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 280390075Sobrien 280490075Sobrien case SImode: 280590075Sobrien return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 280690075Sobrien 280790075Sobrien default: 280890075Sobrien break; 280990075Sobrien } 281090075Sobrien abort (); 281190075Sobrien 281290075Sobrien case CONST_INT: 281390075Sobrien if (const_ok_for_arm (INTVAL (x))) 281490075Sobrien return outer == SET ? 2 : -1; 281590075Sobrien else if (outer == AND 281690075Sobrien && const_ok_for_arm (~INTVAL (x))) 281790075Sobrien return -1; 281890075Sobrien else if ((outer == COMPARE 281990075Sobrien || outer == PLUS || outer == MINUS) 282090075Sobrien && const_ok_for_arm (-INTVAL (x))) 282190075Sobrien return -1; 282290075Sobrien else 282390075Sobrien return 5; 282490075Sobrien 282590075Sobrien case CONST: 282690075Sobrien case LABEL_REF: 282790075Sobrien case SYMBOL_REF: 282890075Sobrien return 6; 282990075Sobrien 283090075Sobrien case CONST_DOUBLE: 283190075Sobrien if (const_double_rtx_ok_for_fpu (x)) 283290075Sobrien return outer == SET ? 2 : -1; 283390075Sobrien else if ((outer == COMPARE || outer == PLUS) 283490075Sobrien && neg_const_double_rtx_ok_for_fpu (x)) 283590075Sobrien return -1; 283690075Sobrien return 7; 283790075Sobrien 283890075Sobrien default: 283990075Sobrien return 99; 284090075Sobrien } 284190075Sobrien} 284290075Sobrien 284390075Sobrienstatic int 284490075Sobrienarm_adjust_cost (insn, link, dep, cost) 284590075Sobrien rtx insn; 284690075Sobrien rtx link; 284790075Sobrien rtx dep; 284890075Sobrien int cost; 284990075Sobrien{ 285090075Sobrien rtx i_pat, d_pat; 285190075Sobrien 285290075Sobrien /* Some true dependencies can have a higher cost depending 285390075Sobrien on precisely how certain input operands are used. */ 285490075Sobrien if (arm_is_xscale 285590075Sobrien && REG_NOTE_KIND (link) == 0 285690075Sobrien && recog_memoized (insn) < 0 285790075Sobrien && recog_memoized (dep) < 0) 285890075Sobrien { 285990075Sobrien int shift_opnum = get_attr_shift (insn); 286090075Sobrien enum attr_type attr_type = get_attr_type (dep); 286190075Sobrien 286290075Sobrien /* If nonzero, SHIFT_OPNUM contains the operand number of a shifted 286390075Sobrien operand for INSN. If we have a shifted input operand and the 286490075Sobrien instruction we depend on is another ALU instruction, then we may 286590075Sobrien have to account for an additional stall. */ 286690075Sobrien if (shift_opnum != 0 && attr_type == TYPE_NORMAL) 286790075Sobrien { 286890075Sobrien rtx shifted_operand; 286990075Sobrien int opno; 287090075Sobrien 287190075Sobrien /* Get the shifted operand. */ 287290075Sobrien extract_insn (insn); 287390075Sobrien shifted_operand = recog_data.operand[shift_opnum]; 287490075Sobrien 287590075Sobrien /* Iterate over all the operands in DEP. If we write an operand 287690075Sobrien that overlaps with SHIFTED_OPERAND, then we have increase the 287790075Sobrien cost of this dependency. */ 287890075Sobrien extract_insn (dep); 287990075Sobrien preprocess_constraints (); 288090075Sobrien for (opno = 0; opno < recog_data.n_operands; opno++) 288190075Sobrien { 288290075Sobrien /* We can ignore strict inputs. */ 288390075Sobrien if (recog_data.operand_type[opno] == OP_IN) 288490075Sobrien continue; 288590075Sobrien 288690075Sobrien if (reg_overlap_mentioned_p (recog_data.operand[opno], 288790075Sobrien shifted_operand)) 288890075Sobrien return 2; 288990075Sobrien } 289090075Sobrien } 289190075Sobrien } 289290075Sobrien 289390075Sobrien /* XXX This is not strictly true for the FPA. */ 289490075Sobrien if (REG_NOTE_KIND (link) == REG_DEP_ANTI 289590075Sobrien || REG_NOTE_KIND (link) == REG_DEP_OUTPUT) 289690075Sobrien return 0; 289790075Sobrien 289890075Sobrien /* Call insns don't incur a stall, even if they follow a load. */ 289990075Sobrien if (REG_NOTE_KIND (link) == 0 290090075Sobrien && GET_CODE (insn) == CALL_INSN) 290190075Sobrien return 1; 290290075Sobrien 290390075Sobrien if ((i_pat = single_set (insn)) != NULL 290490075Sobrien && GET_CODE (SET_SRC (i_pat)) == MEM 290590075Sobrien && (d_pat = single_set (dep)) != NULL 290690075Sobrien && GET_CODE (SET_DEST (d_pat)) == MEM) 290790075Sobrien { 290890075Sobrien /* This is a load after a store, there is no conflict if the load reads 290990075Sobrien from a cached area. Assume that loads from the stack, and from the 291090075Sobrien constant pool are cached, and that others will miss. This is a 291190075Sobrien hack. */ 291290075Sobrien 291390075Sobrien if (CONSTANT_POOL_ADDRESS_P (XEXP (SET_SRC (i_pat), 0)) 291490075Sobrien || reg_mentioned_p (stack_pointer_rtx, XEXP (SET_SRC (i_pat), 0)) 291590075Sobrien || reg_mentioned_p (frame_pointer_rtx, XEXP (SET_SRC (i_pat), 0)) 291690075Sobrien || reg_mentioned_p (hard_frame_pointer_rtx, 291790075Sobrien XEXP (SET_SRC (i_pat), 0))) 291890075Sobrien return 1; 291990075Sobrien } 292090075Sobrien 292190075Sobrien return cost; 292290075Sobrien} 292390075Sobrien 292490075Sobrien/* This code has been fixed for cross compilation. */ 292590075Sobrien 292690075Sobrienstatic int fpa_consts_inited = 0; 292790075Sobrien 292890075Sobrienstatic const char * const strings_fpa[8] = 292990075Sobrien{ 293090075Sobrien "0", "1", "2", "3", 293190075Sobrien "4", "5", "0.5", "10" 293290075Sobrien}; 293390075Sobrien 293490075Sobrienstatic REAL_VALUE_TYPE values_fpa[8]; 293590075Sobrien 293690075Sobrienstatic void 293790075Sobrieninit_fpa_table () 293890075Sobrien{ 293990075Sobrien int i; 294090075Sobrien REAL_VALUE_TYPE r; 294190075Sobrien 294290075Sobrien for (i = 0; i < 8; i++) 294390075Sobrien { 294490075Sobrien r = REAL_VALUE_ATOF (strings_fpa[i], DFmode); 294590075Sobrien values_fpa[i] = r; 294690075Sobrien } 294790075Sobrien 294890075Sobrien fpa_consts_inited = 1; 294990075Sobrien} 295090075Sobrien 295190075Sobrien/* Return TRUE if rtx X is a valid immediate FPU constant. */ 295290075Sobrien 295390075Sobrienint 295490075Sobrienconst_double_rtx_ok_for_fpu (x) 295590075Sobrien rtx x; 295690075Sobrien{ 295790075Sobrien REAL_VALUE_TYPE r; 295890075Sobrien int i; 295990075Sobrien 296090075Sobrien if (!fpa_consts_inited) 296190075Sobrien init_fpa_table (); 296290075Sobrien 296390075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 296490075Sobrien if (REAL_VALUE_MINUS_ZERO (r)) 296590075Sobrien return 0; 296690075Sobrien 296790075Sobrien for (i = 0; i < 8; i++) 296890075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 296990075Sobrien return 1; 297090075Sobrien 297190075Sobrien return 0; 297290075Sobrien} 297390075Sobrien 297490075Sobrien/* Return TRUE if rtx X is a valid immediate FPU constant. */ 297590075Sobrien 297690075Sobrienint 297790075Sobrienneg_const_double_rtx_ok_for_fpu (x) 297890075Sobrien rtx x; 297990075Sobrien{ 298090075Sobrien REAL_VALUE_TYPE r; 298190075Sobrien int i; 298290075Sobrien 298390075Sobrien if (!fpa_consts_inited) 298490075Sobrien init_fpa_table (); 298590075Sobrien 298690075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 298790075Sobrien r = REAL_VALUE_NEGATE (r); 298890075Sobrien if (REAL_VALUE_MINUS_ZERO (r)) 298990075Sobrien return 0; 299090075Sobrien 299190075Sobrien for (i = 0; i < 8; i++) 299290075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 299390075Sobrien return 1; 299490075Sobrien 299590075Sobrien return 0; 299690075Sobrien} 299790075Sobrien 299890075Sobrien/* Predicates for `match_operand' and `match_operator'. */ 299990075Sobrien 300090075Sobrien/* s_register_operand is the same as register_operand, but it doesn't accept 300190075Sobrien (SUBREG (MEM)...). 300290075Sobrien 300390075Sobrien This function exists because at the time it was put in it led to better 300490075Sobrien code. SUBREG(MEM) always needs a reload in the places where 300590075Sobrien s_register_operand is used, and this seemed to lead to excessive 300690075Sobrien reloading. */ 300790075Sobrien 300890075Sobrienint 300990075Sobriens_register_operand (op, mode) 301090075Sobrien rtx op; 301190075Sobrien enum machine_mode mode; 301290075Sobrien{ 301390075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 301490075Sobrien return 0; 301590075Sobrien 301690075Sobrien if (GET_CODE (op) == SUBREG) 301790075Sobrien op = SUBREG_REG (op); 301890075Sobrien 301990075Sobrien /* We don't consider registers whose class is NO_REGS 302090075Sobrien to be a register operand. */ 302190075Sobrien /* XXX might have to check for lo regs only for thumb ??? */ 302290075Sobrien return (GET_CODE (op) == REG 302390075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 302490075Sobrien || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); 302590075Sobrien} 302690075Sobrien 302790075Sobrien/* A hard register operand (even before reload. */ 302890075Sobrien 302990075Sobrienint 303090075Sobrienarm_hard_register_operand (op, mode) 303190075Sobrien rtx op; 303290075Sobrien enum machine_mode mode; 303390075Sobrien{ 303490075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 303590075Sobrien return 0; 303690075Sobrien 303790075Sobrien return (GET_CODE (op) == REG 303890075Sobrien && REGNO (op) < FIRST_PSEUDO_REGISTER); 303990075Sobrien} 304090075Sobrien 304190075Sobrien/* Only accept reg, subreg(reg), const_int. */ 304290075Sobrien 304390075Sobrienint 304490075Sobrienreg_or_int_operand (op, mode) 304590075Sobrien rtx op; 304690075Sobrien enum machine_mode mode; 304790075Sobrien{ 304890075Sobrien if (GET_CODE (op) == CONST_INT) 304990075Sobrien return 1; 305090075Sobrien 305190075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 305290075Sobrien return 0; 305390075Sobrien 305490075Sobrien if (GET_CODE (op) == SUBREG) 305590075Sobrien op = SUBREG_REG (op); 305690075Sobrien 305790075Sobrien /* We don't consider registers whose class is NO_REGS 305890075Sobrien to be a register operand. */ 305990075Sobrien return (GET_CODE (op) == REG 306090075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 306190075Sobrien || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); 306290075Sobrien} 306390075Sobrien 306490075Sobrien/* Return 1 if OP is an item in memory, given that we are in reload. */ 306590075Sobrien 306690075Sobrienint 306790075Sobrienarm_reload_memory_operand (op, mode) 306890075Sobrien rtx op; 306990075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 307090075Sobrien{ 307190075Sobrien int regno = true_regnum (op); 307290075Sobrien 307390075Sobrien return (!CONSTANT_P (op) 307490075Sobrien && (regno == -1 307590075Sobrien || (GET_CODE (op) == REG 307690075Sobrien && REGNO (op) >= FIRST_PSEUDO_REGISTER))); 307790075Sobrien} 307890075Sobrien 307990075Sobrien/* Return 1 if OP is a valid memory address, but not valid for a signed byte 308090075Sobrien memory access (architecture V4). 308190075Sobrien MODE is QImode if called when computing constraints, or VOIDmode when 308290075Sobrien emitting patterns. In this latter case we cannot use memory_operand() 308390075Sobrien because it will fail on badly formed MEMs, which is precisly what we are 308490075Sobrien trying to catch. */ 308590075Sobrien 308690075Sobrienint 308790075Sobrienbad_signed_byte_operand (op, mode) 308890075Sobrien rtx op; 308990075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 309090075Sobrien{ 309190075Sobrien#if 0 309290075Sobrien if ((mode == QImode && !memory_operand (op, mode)) || GET_CODE (op) != MEM) 309390075Sobrien return 0; 309490075Sobrien#endif 309590075Sobrien if (GET_CODE (op) != MEM) 309690075Sobrien return 0; 309790075Sobrien 309890075Sobrien op = XEXP (op, 0); 309990075Sobrien 310090075Sobrien /* A sum of anything more complex than reg + reg or reg + const is bad. */ 310190075Sobrien if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS) 310290075Sobrien && (!s_register_operand (XEXP (op, 0), VOIDmode) 310390075Sobrien || (!s_register_operand (XEXP (op, 1), VOIDmode) 310490075Sobrien && GET_CODE (XEXP (op, 1)) != CONST_INT))) 310590075Sobrien return 1; 310690075Sobrien 310790075Sobrien /* Big constants are also bad. */ 310890075Sobrien if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT 310990075Sobrien && (INTVAL (XEXP (op, 1)) > 0xff 311090075Sobrien || -INTVAL (XEXP (op, 1)) > 0xff)) 311190075Sobrien return 1; 311290075Sobrien 311390075Sobrien /* Everything else is good, or can will automatically be made so. */ 311490075Sobrien return 0; 311590075Sobrien} 311690075Sobrien 311790075Sobrien/* Return TRUE for valid operands for the rhs of an ARM instruction. */ 311890075Sobrien 311990075Sobrienint 312090075Sobrienarm_rhs_operand (op, mode) 312190075Sobrien rtx op; 312290075Sobrien enum machine_mode mode; 312390075Sobrien{ 312490075Sobrien return (s_register_operand (op, mode) 312590075Sobrien || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); 312690075Sobrien} 312790075Sobrien 312890075Sobrien/* Return TRUE for valid operands for the 312990075Sobrien rhs of an ARM instruction, or a load. */ 313090075Sobrien 313190075Sobrienint 313290075Sobrienarm_rhsm_operand (op, mode) 313390075Sobrien rtx op; 313490075Sobrien enum machine_mode mode; 313590075Sobrien{ 313690075Sobrien return (s_register_operand (op, mode) 313790075Sobrien || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))) 313890075Sobrien || memory_operand (op, mode)); 313990075Sobrien} 314090075Sobrien 314190075Sobrien/* Return TRUE for valid operands for the rhs of an ARM instruction, or if a 314290075Sobrien constant that is valid when negated. */ 314390075Sobrien 314490075Sobrienint 314590075Sobrienarm_add_operand (op, mode) 314690075Sobrien rtx op; 314790075Sobrien enum machine_mode mode; 314890075Sobrien{ 314990075Sobrien if (TARGET_THUMB) 315090075Sobrien return thumb_cmp_operand (op, mode); 315190075Sobrien 315290075Sobrien return (s_register_operand (op, mode) 315390075Sobrien || (GET_CODE (op) == CONST_INT 315490075Sobrien && (const_ok_for_arm (INTVAL (op)) 315590075Sobrien || const_ok_for_arm (-INTVAL (op))))); 315690075Sobrien} 315790075Sobrien 315890075Sobrienint 315990075Sobrienarm_not_operand (op, mode) 316090075Sobrien rtx op; 316190075Sobrien enum machine_mode mode; 316290075Sobrien{ 316390075Sobrien return (s_register_operand (op, mode) 316490075Sobrien || (GET_CODE (op) == CONST_INT 316590075Sobrien && (const_ok_for_arm (INTVAL (op)) 316690075Sobrien || const_ok_for_arm (~INTVAL (op))))); 316790075Sobrien} 316890075Sobrien 316990075Sobrien/* Return TRUE if the operand is a memory reference which contains an 317090075Sobrien offsettable address. */ 317190075Sobrien 317290075Sobrienint 317390075Sobrienoffsettable_memory_operand (op, mode) 317490075Sobrien rtx op; 317590075Sobrien enum machine_mode mode; 317690075Sobrien{ 317790075Sobrien if (mode == VOIDmode) 317890075Sobrien mode = GET_MODE (op); 317990075Sobrien 318090075Sobrien return (mode == GET_MODE (op) 318190075Sobrien && GET_CODE (op) == MEM 318290075Sobrien && offsettable_address_p (reload_completed | reload_in_progress, 318390075Sobrien mode, XEXP (op, 0))); 318490075Sobrien} 318590075Sobrien 318690075Sobrien/* Return TRUE if the operand is a memory reference which is, or can be 318790075Sobrien made word aligned by adjusting the offset. */ 318890075Sobrien 318990075Sobrienint 319090075Sobrienalignable_memory_operand (op, mode) 319190075Sobrien rtx op; 319290075Sobrien enum machine_mode mode; 319390075Sobrien{ 319490075Sobrien rtx reg; 319590075Sobrien 319690075Sobrien if (mode == VOIDmode) 319790075Sobrien mode = GET_MODE (op); 319890075Sobrien 319990075Sobrien if (mode != GET_MODE (op) || GET_CODE (op) != MEM) 320090075Sobrien return 0; 320190075Sobrien 320290075Sobrien op = XEXP (op, 0); 320390075Sobrien 320490075Sobrien return ((GET_CODE (reg = op) == REG 320590075Sobrien || (GET_CODE (op) == SUBREG 320690075Sobrien && GET_CODE (reg = SUBREG_REG (op)) == REG) 320790075Sobrien || (GET_CODE (op) == PLUS 320890075Sobrien && GET_CODE (XEXP (op, 1)) == CONST_INT 320990075Sobrien && (GET_CODE (reg = XEXP (op, 0)) == REG 321090075Sobrien || (GET_CODE (XEXP (op, 0)) == SUBREG 321190075Sobrien && GET_CODE (reg = SUBREG_REG (XEXP (op, 0))) == REG)))) 321290075Sobrien && REGNO_POINTER_ALIGN (REGNO (reg)) >= 32); 321390075Sobrien} 321490075Sobrien 321590075Sobrien/* Similar to s_register_operand, but does not allow hard integer 321690075Sobrien registers. */ 321790075Sobrien 321890075Sobrienint 321990075Sobrienf_register_operand (op, mode) 322090075Sobrien rtx op; 322190075Sobrien enum machine_mode mode; 322290075Sobrien{ 322390075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 322490075Sobrien return 0; 322590075Sobrien 322690075Sobrien if (GET_CODE (op) == SUBREG) 322790075Sobrien op = SUBREG_REG (op); 322890075Sobrien 322990075Sobrien /* We don't consider registers whose class is NO_REGS 323090075Sobrien to be a register operand. */ 323190075Sobrien return (GET_CODE (op) == REG 323290075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 323390075Sobrien || REGNO_REG_CLASS (REGNO (op)) == FPU_REGS)); 323490075Sobrien} 323590075Sobrien 323690075Sobrien/* Return TRUE for valid operands for the rhs of an FPU instruction. */ 323790075Sobrien 323890075Sobrienint 323990075Sobrienfpu_rhs_operand (op, mode) 324090075Sobrien rtx op; 324190075Sobrien enum machine_mode mode; 324290075Sobrien{ 324390075Sobrien if (s_register_operand (op, mode)) 324490075Sobrien return TRUE; 324590075Sobrien 324690075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 324790075Sobrien return FALSE; 324890075Sobrien 324990075Sobrien if (GET_CODE (op) == CONST_DOUBLE) 325090075Sobrien return const_double_rtx_ok_for_fpu (op); 325190075Sobrien 325290075Sobrien return FALSE; 325390075Sobrien} 325490075Sobrien 325590075Sobrienint 325690075Sobrienfpu_add_operand (op, mode) 325790075Sobrien rtx op; 325890075Sobrien enum machine_mode mode; 325990075Sobrien{ 326090075Sobrien if (s_register_operand (op, mode)) 326190075Sobrien return TRUE; 326290075Sobrien 326390075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 326490075Sobrien return FALSE; 326590075Sobrien 326690075Sobrien if (GET_CODE (op) == CONST_DOUBLE) 326790075Sobrien return (const_double_rtx_ok_for_fpu (op) 326890075Sobrien || neg_const_double_rtx_ok_for_fpu (op)); 326990075Sobrien 327090075Sobrien return FALSE; 327190075Sobrien} 327290075Sobrien 327390075Sobrien/* Return nonzero if OP is a constant power of two. */ 327490075Sobrien 327590075Sobrienint 327690075Sobrienpower_of_two_operand (op, mode) 327790075Sobrien rtx op; 327890075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 327990075Sobrien{ 328090075Sobrien if (GET_CODE (op) == CONST_INT) 328190075Sobrien { 328290075Sobrien HOST_WIDE_INT value = INTVAL (op); 328390075Sobrien 328490075Sobrien return value != 0 && (value & (value - 1)) == 0; 328590075Sobrien } 328690075Sobrien 328790075Sobrien return FALSE; 328890075Sobrien} 328990075Sobrien 329090075Sobrien/* Return TRUE for a valid operand of a DImode operation. 329190075Sobrien Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). 329290075Sobrien Note that this disallows MEM(REG+REG), but allows 329390075Sobrien MEM(PRE/POST_INC/DEC(REG)). */ 329490075Sobrien 329590075Sobrienint 329690075Sobriendi_operand (op, mode) 329790075Sobrien rtx op; 329890075Sobrien enum machine_mode mode; 329990075Sobrien{ 330090075Sobrien if (s_register_operand (op, mode)) 330190075Sobrien return TRUE; 330290075Sobrien 330390075Sobrien if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) 330490075Sobrien return FALSE; 330590075Sobrien 330690075Sobrien if (GET_CODE (op) == SUBREG) 330790075Sobrien op = SUBREG_REG (op); 330890075Sobrien 330990075Sobrien switch (GET_CODE (op)) 331090075Sobrien { 331190075Sobrien case CONST_DOUBLE: 331290075Sobrien case CONST_INT: 331390075Sobrien return TRUE; 331490075Sobrien 331590075Sobrien case MEM: 331690075Sobrien return memory_address_p (DImode, XEXP (op, 0)); 331790075Sobrien 331890075Sobrien default: 331990075Sobrien return FALSE; 332090075Sobrien } 332190075Sobrien} 332290075Sobrien 332390075Sobrien/* Like di_operand, but don't accept constants. */ 332490075Sobrien 332590075Sobrienint 332690075Sobriennonimmediate_di_operand (op, mode) 332790075Sobrien rtx op; 332890075Sobrien enum machine_mode mode; 332990075Sobrien{ 333090075Sobrien if (s_register_operand (op, mode)) 333190075Sobrien return TRUE; 333290075Sobrien 333390075Sobrien if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) 333490075Sobrien return FALSE; 333590075Sobrien 333690075Sobrien if (GET_CODE (op) == SUBREG) 333790075Sobrien op = SUBREG_REG (op); 333890075Sobrien 333990075Sobrien if (GET_CODE (op) == MEM) 334090075Sobrien return memory_address_p (DImode, XEXP (op, 0)); 334190075Sobrien 334290075Sobrien return FALSE; 334390075Sobrien} 334490075Sobrien 334590075Sobrien/* Return TRUE for a valid operand of a DFmode operation when -msoft-float. 334690075Sobrien Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). 334790075Sobrien Note that this disallows MEM(REG+REG), but allows 334890075Sobrien MEM(PRE/POST_INC/DEC(REG)). */ 334990075Sobrien 335090075Sobrienint 335190075Sobriensoft_df_operand (op, mode) 335290075Sobrien rtx op; 335390075Sobrien enum machine_mode mode; 335490075Sobrien{ 335590075Sobrien if (s_register_operand (op, mode)) 335690075Sobrien return TRUE; 335790075Sobrien 335890075Sobrien if (mode != VOIDmode && GET_MODE (op) != mode) 335990075Sobrien return FALSE; 336090075Sobrien 336190075Sobrien if (GET_CODE (op) == SUBREG && CONSTANT_P (SUBREG_REG (op))) 336290075Sobrien return FALSE; 336390075Sobrien 336490075Sobrien if (GET_CODE (op) == SUBREG) 336590075Sobrien op = SUBREG_REG (op); 336690075Sobrien 336790075Sobrien switch (GET_CODE (op)) 336890075Sobrien { 336990075Sobrien case CONST_DOUBLE: 337090075Sobrien return TRUE; 337190075Sobrien 337290075Sobrien case MEM: 337390075Sobrien return memory_address_p (DFmode, XEXP (op, 0)); 337490075Sobrien 337590075Sobrien default: 337690075Sobrien return FALSE; 337790075Sobrien } 337890075Sobrien} 337990075Sobrien 338090075Sobrien/* Like soft_df_operand, but don't accept constants. */ 338190075Sobrien 338290075Sobrienint 338390075Sobriennonimmediate_soft_df_operand (op, mode) 338490075Sobrien rtx op; 338590075Sobrien enum machine_mode mode; 338690075Sobrien{ 338790075Sobrien if (s_register_operand (op, mode)) 338890075Sobrien return TRUE; 338990075Sobrien 339090075Sobrien if (mode != VOIDmode && GET_MODE (op) != mode) 339190075Sobrien return FALSE; 339290075Sobrien 339390075Sobrien if (GET_CODE (op) == SUBREG) 339490075Sobrien op = SUBREG_REG (op); 339590075Sobrien 339690075Sobrien if (GET_CODE (op) == MEM) 339790075Sobrien return memory_address_p (DFmode, XEXP (op, 0)); 339890075Sobrien return FALSE; 339990075Sobrien} 340090075Sobrien 340190075Sobrien/* Return TRUE for valid index operands. */ 340290075Sobrien 340390075Sobrienint 340490075Sobrienindex_operand (op, mode) 340590075Sobrien rtx op; 340690075Sobrien enum machine_mode mode; 340790075Sobrien{ 340890075Sobrien return (s_register_operand (op, mode) 340990075Sobrien || (immediate_operand (op, mode) 341090075Sobrien && (GET_CODE (op) != CONST_INT 341190075Sobrien || (INTVAL (op) < 4096 && INTVAL (op) > -4096)))); 341290075Sobrien} 341390075Sobrien 341490075Sobrien/* Return TRUE for valid shifts by a constant. This also accepts any 341590075Sobrien power of two on the (somewhat overly relaxed) assumption that the 341690075Sobrien shift operator in this case was a mult. */ 341790075Sobrien 341890075Sobrienint 341990075Sobrienconst_shift_operand (op, mode) 342090075Sobrien rtx op; 342190075Sobrien enum machine_mode mode; 342290075Sobrien{ 342390075Sobrien return (power_of_two_operand (op, mode) 342490075Sobrien || (immediate_operand (op, mode) 342590075Sobrien && (GET_CODE (op) != CONST_INT 342690075Sobrien || (INTVAL (op) < 32 && INTVAL (op) > 0)))); 342790075Sobrien} 342890075Sobrien 342990075Sobrien/* Return TRUE for arithmetic operators which can be combined with a multiply 343090075Sobrien (shift). */ 343190075Sobrien 343290075Sobrienint 343390075Sobrienshiftable_operator (x, mode) 343490075Sobrien rtx x; 343590075Sobrien enum machine_mode mode; 343690075Sobrien{ 343790075Sobrien enum rtx_code code; 343890075Sobrien 343990075Sobrien if (GET_MODE (x) != mode) 344090075Sobrien return FALSE; 344190075Sobrien 344290075Sobrien code = GET_CODE (x); 344390075Sobrien 344490075Sobrien return (code == PLUS || code == MINUS 344590075Sobrien || code == IOR || code == XOR || code == AND); 344690075Sobrien} 344790075Sobrien 344890075Sobrien/* Return TRUE for binary logical operators. */ 344990075Sobrien 345090075Sobrienint 345190075Sobrienlogical_binary_operator (x, mode) 345290075Sobrien rtx x; 345390075Sobrien enum machine_mode mode; 345490075Sobrien{ 345590075Sobrien enum rtx_code code; 345690075Sobrien 345790075Sobrien if (GET_MODE (x) != mode) 345890075Sobrien return FALSE; 345990075Sobrien 346090075Sobrien code = GET_CODE (x); 346190075Sobrien 346290075Sobrien return (code == IOR || code == XOR || code == AND); 346390075Sobrien} 346490075Sobrien 346590075Sobrien/* Return TRUE for shift operators. */ 346690075Sobrien 346790075Sobrienint 346890075Sobrienshift_operator (x, mode) 346990075Sobrien rtx x; 347090075Sobrien enum machine_mode mode; 347190075Sobrien{ 347290075Sobrien enum rtx_code code; 347390075Sobrien 347490075Sobrien if (GET_MODE (x) != mode) 347590075Sobrien return FALSE; 347690075Sobrien 347790075Sobrien code = GET_CODE (x); 347890075Sobrien 347990075Sobrien if (code == MULT) 348090075Sobrien return power_of_two_operand (XEXP (x, 1), mode); 348190075Sobrien 348290075Sobrien return (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT 348390075Sobrien || code == ROTATERT); 348490075Sobrien} 348590075Sobrien 348690075Sobrien/* Return TRUE if x is EQ or NE. */ 348790075Sobrien 348890075Sobrienint 348990075Sobrienequality_operator (x, mode) 349090075Sobrien rtx x; 349190075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 349290075Sobrien{ 349390075Sobrien return GET_CODE (x) == EQ || GET_CODE (x) == NE; 349490075Sobrien} 349590075Sobrien 349690075Sobrien/* Return TRUE if x is a comparison operator other than LTGT or UNEQ. */ 349790075Sobrien 349890075Sobrienint 349990075Sobrienarm_comparison_operator (x, mode) 350090075Sobrien rtx x; 350190075Sobrien enum machine_mode mode; 350290075Sobrien{ 350390075Sobrien return (comparison_operator (x, mode) 350490075Sobrien && GET_CODE (x) != LTGT 350590075Sobrien && GET_CODE (x) != UNEQ); 350690075Sobrien} 350790075Sobrien 350890075Sobrien/* Return TRUE for SMIN SMAX UMIN UMAX operators. */ 350990075Sobrien 351090075Sobrienint 351190075Sobrienminmax_operator (x, mode) 351290075Sobrien rtx x; 351390075Sobrien enum machine_mode mode; 351490075Sobrien{ 351590075Sobrien enum rtx_code code = GET_CODE (x); 351690075Sobrien 351790075Sobrien if (GET_MODE (x) != mode) 351890075Sobrien return FALSE; 351990075Sobrien 352090075Sobrien return code == SMIN || code == SMAX || code == UMIN || code == UMAX; 352190075Sobrien} 352290075Sobrien 352390075Sobrien/* Return TRUE if this is the condition code register, if we aren't given 352490075Sobrien a mode, accept any class CCmode register. */ 352590075Sobrien 352690075Sobrienint 352790075Sobriencc_register (x, mode) 352890075Sobrien rtx x; 352990075Sobrien enum machine_mode mode; 353090075Sobrien{ 353190075Sobrien if (mode == VOIDmode) 353290075Sobrien { 353390075Sobrien mode = GET_MODE (x); 353490075Sobrien 353590075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 353690075Sobrien return FALSE; 353790075Sobrien } 353890075Sobrien 353990075Sobrien if ( GET_MODE (x) == mode 354090075Sobrien && GET_CODE (x) == REG 354190075Sobrien && REGNO (x) == CC_REGNUM) 354290075Sobrien return TRUE; 354390075Sobrien 354490075Sobrien return FALSE; 354590075Sobrien} 354690075Sobrien 354790075Sobrien/* Return TRUE if this is the condition code register, if we aren't given 354890075Sobrien a mode, accept any class CCmode register which indicates a dominance 354990075Sobrien expression. */ 355090075Sobrien 355190075Sobrienint 355290075Sobriendominant_cc_register (x, mode) 355390075Sobrien rtx x; 355490075Sobrien enum machine_mode mode; 355590075Sobrien{ 355690075Sobrien if (mode == VOIDmode) 355790075Sobrien { 355890075Sobrien mode = GET_MODE (x); 355990075Sobrien 356090075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 356190075Sobrien return FALSE; 356290075Sobrien } 356390075Sobrien 356490075Sobrien if ( mode != CC_DNEmode && mode != CC_DEQmode 356590075Sobrien && mode != CC_DLEmode && mode != CC_DLTmode 356690075Sobrien && mode != CC_DGEmode && mode != CC_DGTmode 356790075Sobrien && mode != CC_DLEUmode && mode != CC_DLTUmode 356890075Sobrien && mode != CC_DGEUmode && mode != CC_DGTUmode) 356990075Sobrien return FALSE; 357090075Sobrien 357190075Sobrien return cc_register (x, mode); 357290075Sobrien} 357390075Sobrien 357490075Sobrien/* Return TRUE if X references a SYMBOL_REF. */ 357590075Sobrien 357690075Sobrienint 357790075Sobriensymbol_mentioned_p (x) 357890075Sobrien rtx x; 357990075Sobrien{ 358090075Sobrien const char * fmt; 358190075Sobrien int i; 358290075Sobrien 358390075Sobrien if (GET_CODE (x) == SYMBOL_REF) 358490075Sobrien return 1; 358590075Sobrien 358690075Sobrien fmt = GET_RTX_FORMAT (GET_CODE (x)); 358790075Sobrien 358890075Sobrien for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) 358990075Sobrien { 359090075Sobrien if (fmt[i] == 'E') 359190075Sobrien { 359290075Sobrien int j; 359390075Sobrien 359490075Sobrien for (j = XVECLEN (x, i) - 1; j >= 0; j--) 359590075Sobrien if (symbol_mentioned_p (XVECEXP (x, i, j))) 359690075Sobrien return 1; 359790075Sobrien } 359890075Sobrien else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i))) 359990075Sobrien return 1; 360090075Sobrien } 360190075Sobrien 360290075Sobrien return 0; 360390075Sobrien} 360490075Sobrien 360590075Sobrien/* Return TRUE if X references a LABEL_REF. */ 360690075Sobrien 360790075Sobrienint 360890075Sobrienlabel_mentioned_p (x) 360990075Sobrien rtx x; 361090075Sobrien{ 361190075Sobrien const char * fmt; 361290075Sobrien int i; 361390075Sobrien 361490075Sobrien if (GET_CODE (x) == LABEL_REF) 361590075Sobrien return 1; 361690075Sobrien 361790075Sobrien fmt = GET_RTX_FORMAT (GET_CODE (x)); 361890075Sobrien for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) 361990075Sobrien { 362090075Sobrien if (fmt[i] == 'E') 362190075Sobrien { 362290075Sobrien int j; 362390075Sobrien 362490075Sobrien for (j = XVECLEN (x, i) - 1; j >= 0; j--) 362590075Sobrien if (label_mentioned_p (XVECEXP (x, i, j))) 362690075Sobrien return 1; 362790075Sobrien } 362890075Sobrien else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i))) 362990075Sobrien return 1; 363090075Sobrien } 363190075Sobrien 363290075Sobrien return 0; 363390075Sobrien} 363490075Sobrien 363590075Sobrienenum rtx_code 363690075Sobrienminmax_code (x) 363790075Sobrien rtx x; 363890075Sobrien{ 363990075Sobrien enum rtx_code code = GET_CODE (x); 364090075Sobrien 364190075Sobrien if (code == SMAX) 364290075Sobrien return GE; 364390075Sobrien else if (code == SMIN) 364490075Sobrien return LE; 364590075Sobrien else if (code == UMIN) 364690075Sobrien return LEU; 364790075Sobrien else if (code == UMAX) 364890075Sobrien return GEU; 364990075Sobrien 365090075Sobrien abort (); 365190075Sobrien} 365290075Sobrien 365390075Sobrien/* Return 1 if memory locations are adjacent. */ 365490075Sobrien 365590075Sobrienint 365690075Sobrienadjacent_mem_locations (a, b) 365790075Sobrien rtx a, b; 365890075Sobrien{ 365990075Sobrien if ((GET_CODE (XEXP (a, 0)) == REG 366090075Sobrien || (GET_CODE (XEXP (a, 0)) == PLUS 366190075Sobrien && GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT)) 366290075Sobrien && (GET_CODE (XEXP (b, 0)) == REG 366390075Sobrien || (GET_CODE (XEXP (b, 0)) == PLUS 366490075Sobrien && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT))) 366590075Sobrien { 366690075Sobrien int val0 = 0, val1 = 0; 366790075Sobrien int reg0, reg1; 366890075Sobrien 366990075Sobrien if (GET_CODE (XEXP (a, 0)) == PLUS) 367090075Sobrien { 367190075Sobrien reg0 = REGNO (XEXP (XEXP (a, 0), 0)); 367290075Sobrien val0 = INTVAL (XEXP (XEXP (a, 0), 1)); 367390075Sobrien } 367490075Sobrien else 367590075Sobrien reg0 = REGNO (XEXP (a, 0)); 367690075Sobrien 367790075Sobrien if (GET_CODE (XEXP (b, 0)) == PLUS) 367890075Sobrien { 367990075Sobrien reg1 = REGNO (XEXP (XEXP (b, 0), 0)); 368090075Sobrien val1 = INTVAL (XEXP (XEXP (b, 0), 1)); 368190075Sobrien } 368290075Sobrien else 368390075Sobrien reg1 = REGNO (XEXP (b, 0)); 368490075Sobrien 368590075Sobrien return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4); 368690075Sobrien } 368790075Sobrien return 0; 368890075Sobrien} 368990075Sobrien 369090075Sobrien/* Return 1 if OP is a load multiple operation. It is known to be 369190075Sobrien parallel and the first section will be tested. */ 369290075Sobrien 369390075Sobrienint 369490075Sobrienload_multiple_operation (op, mode) 369590075Sobrien rtx op; 369690075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 369790075Sobrien{ 369890075Sobrien HOST_WIDE_INT count = XVECLEN (op, 0); 369990075Sobrien int dest_regno; 370090075Sobrien rtx src_addr; 370190075Sobrien HOST_WIDE_INT i = 1, base = 0; 370290075Sobrien rtx elt; 370390075Sobrien 370490075Sobrien if (count <= 1 370590075Sobrien || GET_CODE (XVECEXP (op, 0, 0)) != SET) 370690075Sobrien return 0; 370790075Sobrien 370890075Sobrien /* Check to see if this might be a write-back. */ 370990075Sobrien if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) 371090075Sobrien { 371190075Sobrien i++; 371290075Sobrien base = 1; 371390075Sobrien 371490075Sobrien /* Now check it more carefully. */ 371590075Sobrien if (GET_CODE (SET_DEST (elt)) != REG 371690075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG 371790075Sobrien || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt)) 371890075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT 371990075Sobrien || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) 372090075Sobrien return 0; 372190075Sobrien } 372290075Sobrien 372390075Sobrien /* Perform a quick check so we don't blow up below. */ 372490075Sobrien if (count <= i 372590075Sobrien || GET_CODE (XVECEXP (op, 0, i - 1)) != SET 372690075Sobrien || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG 372790075Sobrien || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM) 372890075Sobrien return 0; 372990075Sobrien 373090075Sobrien dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1))); 373190075Sobrien src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0); 373290075Sobrien 373390075Sobrien for (; i < count; i++) 373490075Sobrien { 373590075Sobrien elt = XVECEXP (op, 0, i); 373690075Sobrien 373790075Sobrien if (GET_CODE (elt) != SET 373890075Sobrien || GET_CODE (SET_DEST (elt)) != REG 373990075Sobrien || GET_MODE (SET_DEST (elt)) != SImode 374090075Sobrien || REGNO (SET_DEST (elt)) != (unsigned int)(dest_regno + i - base) 374190075Sobrien || GET_CODE (SET_SRC (elt)) != MEM 374290075Sobrien || GET_MODE (SET_SRC (elt)) != SImode 374390075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS 374490075Sobrien || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) 374590075Sobrien || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT 374690075Sobrien || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4) 374790075Sobrien return 0; 374890075Sobrien } 374990075Sobrien 375090075Sobrien return 1; 375190075Sobrien} 375290075Sobrien 375390075Sobrien/* Return 1 if OP is a store multiple operation. It is known to be 375490075Sobrien parallel and the first section will be tested. */ 375590075Sobrien 375690075Sobrienint 375790075Sobrienstore_multiple_operation (op, mode) 375890075Sobrien rtx op; 375990075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 376090075Sobrien{ 376190075Sobrien HOST_WIDE_INT count = XVECLEN (op, 0); 376290075Sobrien int src_regno; 376390075Sobrien rtx dest_addr; 376490075Sobrien HOST_WIDE_INT i = 1, base = 0; 376590075Sobrien rtx elt; 376690075Sobrien 376790075Sobrien if (count <= 1 376890075Sobrien || GET_CODE (XVECEXP (op, 0, 0)) != SET) 376990075Sobrien return 0; 377090075Sobrien 377190075Sobrien /* Check to see if this might be a write-back. */ 377290075Sobrien if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) 377390075Sobrien { 377490075Sobrien i++; 377590075Sobrien base = 1; 377690075Sobrien 377790075Sobrien /* Now check it more carefully. */ 377890075Sobrien if (GET_CODE (SET_DEST (elt)) != REG 377990075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG 378090075Sobrien || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt)) 378190075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT 378290075Sobrien || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) 378390075Sobrien return 0; 378490075Sobrien } 378590075Sobrien 378690075Sobrien /* Perform a quick check so we don't blow up below. */ 378790075Sobrien if (count <= i 378890075Sobrien || GET_CODE (XVECEXP (op, 0, i - 1)) != SET 378990075Sobrien || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM 379090075Sobrien || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG) 379190075Sobrien return 0; 379290075Sobrien 379390075Sobrien src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1))); 379490075Sobrien dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0); 379590075Sobrien 379690075Sobrien for (; i < count; i++) 379790075Sobrien { 379890075Sobrien elt = XVECEXP (op, 0, i); 379990075Sobrien 380090075Sobrien if (GET_CODE (elt) != SET 380190075Sobrien || GET_CODE (SET_SRC (elt)) != REG 380290075Sobrien || GET_MODE (SET_SRC (elt)) != SImode 380390075Sobrien || REGNO (SET_SRC (elt)) != (unsigned int)(src_regno + i - base) 380490075Sobrien || GET_CODE (SET_DEST (elt)) != MEM 380590075Sobrien || GET_MODE (SET_DEST (elt)) != SImode 380690075Sobrien || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS 380790075Sobrien || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) 380890075Sobrien || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT 380990075Sobrien || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4) 381090075Sobrien return 0; 381190075Sobrien } 381290075Sobrien 381390075Sobrien return 1; 381490075Sobrien} 381590075Sobrien 381690075Sobrienint 381790075Sobrienload_multiple_sequence (operands, nops, regs, base, load_offset) 381890075Sobrien rtx * operands; 381990075Sobrien int nops; 382090075Sobrien int * regs; 382190075Sobrien int * base; 382290075Sobrien HOST_WIDE_INT * load_offset; 382390075Sobrien{ 382490075Sobrien int unsorted_regs[4]; 382590075Sobrien HOST_WIDE_INT unsorted_offsets[4]; 382690075Sobrien int order[4]; 382790075Sobrien int base_reg = -1; 382890075Sobrien int i; 382990075Sobrien 383090075Sobrien /* Can only handle 2, 3, or 4 insns at present, 383190075Sobrien though could be easily extended if required. */ 383290075Sobrien if (nops < 2 || nops > 4) 383390075Sobrien abort (); 383490075Sobrien 383590075Sobrien /* Loop over the operands and check that the memory references are 383690075Sobrien suitable (ie immediate offsets from the same base register). At 383790075Sobrien the same time, extract the target register, and the memory 383890075Sobrien offsets. */ 383990075Sobrien for (i = 0; i < nops; i++) 384090075Sobrien { 384190075Sobrien rtx reg; 384290075Sobrien rtx offset; 384390075Sobrien 384490075Sobrien /* Convert a subreg of a mem into the mem itself. */ 384590075Sobrien if (GET_CODE (operands[nops + i]) == SUBREG) 384690075Sobrien operands[nops + i] = alter_subreg (operands + (nops + i)); 384790075Sobrien 384890075Sobrien if (GET_CODE (operands[nops + i]) != MEM) 384990075Sobrien abort (); 385090075Sobrien 385190075Sobrien /* Don't reorder volatile memory references; it doesn't seem worth 385290075Sobrien looking for the case where the order is ok anyway. */ 385390075Sobrien if (MEM_VOLATILE_P (operands[nops + i])) 385490075Sobrien return 0; 385590075Sobrien 385690075Sobrien offset = const0_rtx; 385790075Sobrien 385890075Sobrien if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG 385990075Sobrien || (GET_CODE (reg) == SUBREG 386090075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 386190075Sobrien || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS 386290075Sobrien && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) 386390075Sobrien == REG) 386490075Sobrien || (GET_CODE (reg) == SUBREG 386590075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 386690075Sobrien && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) 386790075Sobrien == CONST_INT))) 386890075Sobrien { 386990075Sobrien if (i == 0) 387090075Sobrien { 387190075Sobrien base_reg = REGNO (reg); 387290075Sobrien unsorted_regs[0] = (GET_CODE (operands[i]) == REG 387390075Sobrien ? REGNO (operands[i]) 387490075Sobrien : REGNO (SUBREG_REG (operands[i]))); 387590075Sobrien order[0] = 0; 387690075Sobrien } 387790075Sobrien else 387890075Sobrien { 387990075Sobrien if (base_reg != (int) REGNO (reg)) 388090075Sobrien /* Not addressed from the same base register. */ 388190075Sobrien return 0; 388290075Sobrien 388390075Sobrien unsorted_regs[i] = (GET_CODE (operands[i]) == REG 388490075Sobrien ? REGNO (operands[i]) 388590075Sobrien : REGNO (SUBREG_REG (operands[i]))); 388690075Sobrien if (unsorted_regs[i] < unsorted_regs[order[0]]) 388790075Sobrien order[0] = i; 388890075Sobrien } 388990075Sobrien 389090075Sobrien /* If it isn't an integer register, or if it overwrites the 389190075Sobrien base register but isn't the last insn in the list, then 389290075Sobrien we can't do this. */ 389390075Sobrien if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14 389490075Sobrien || (i != nops - 1 && unsorted_regs[i] == base_reg)) 389590075Sobrien return 0; 389690075Sobrien 389790075Sobrien unsorted_offsets[i] = INTVAL (offset); 389890075Sobrien } 389990075Sobrien else 390090075Sobrien /* Not a suitable memory address. */ 390190075Sobrien return 0; 390290075Sobrien } 390390075Sobrien 390490075Sobrien /* All the useful information has now been extracted from the 390590075Sobrien operands into unsorted_regs and unsorted_offsets; additionally, 390690075Sobrien order[0] has been set to the lowest numbered register in the 390790075Sobrien list. Sort the registers into order, and check that the memory 390890075Sobrien offsets are ascending and adjacent. */ 390990075Sobrien 391090075Sobrien for (i = 1; i < nops; i++) 391190075Sobrien { 391290075Sobrien int j; 391390075Sobrien 391490075Sobrien order[i] = order[i - 1]; 391590075Sobrien for (j = 0; j < nops; j++) 391690075Sobrien if (unsorted_regs[j] > unsorted_regs[order[i - 1]] 391790075Sobrien && (order[i] == order[i - 1] 391890075Sobrien || unsorted_regs[j] < unsorted_regs[order[i]])) 391990075Sobrien order[i] = j; 392090075Sobrien 392190075Sobrien /* Have we found a suitable register? if not, one must be used more 392290075Sobrien than once. */ 392390075Sobrien if (order[i] == order[i - 1]) 392490075Sobrien return 0; 392590075Sobrien 392690075Sobrien /* Is the memory address adjacent and ascending? */ 392790075Sobrien if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) 392890075Sobrien return 0; 392990075Sobrien } 393090075Sobrien 393190075Sobrien if (base) 393290075Sobrien { 393390075Sobrien *base = base_reg; 393490075Sobrien 393590075Sobrien for (i = 0; i < nops; i++) 393690075Sobrien regs[i] = unsorted_regs[order[i]]; 393790075Sobrien 393890075Sobrien *load_offset = unsorted_offsets[order[0]]; 393990075Sobrien } 394090075Sobrien 394190075Sobrien if (unsorted_offsets[order[0]] == 0) 394290075Sobrien return 1; /* ldmia */ 394390075Sobrien 394490075Sobrien if (unsorted_offsets[order[0]] == 4) 394590075Sobrien return 2; /* ldmib */ 394690075Sobrien 394790075Sobrien if (unsorted_offsets[order[nops - 1]] == 0) 394890075Sobrien return 3; /* ldmda */ 394990075Sobrien 395090075Sobrien if (unsorted_offsets[order[nops - 1]] == -4) 395190075Sobrien return 4; /* ldmdb */ 395290075Sobrien 395390075Sobrien /* For ARM8,9 & StrongARM, 2 ldr instructions are faster than an ldm 395490075Sobrien if the offset isn't small enough. The reason 2 ldrs are faster 395590075Sobrien is because these ARMs are able to do more than one cache access 395690075Sobrien in a single cycle. The ARM9 and StrongARM have Harvard caches, 395790075Sobrien whilst the ARM8 has a double bandwidth cache. This means that 395890075Sobrien these cores can do both an instruction fetch and a data fetch in 395990075Sobrien a single cycle, so the trick of calculating the address into a 396090075Sobrien scratch register (one of the result regs) and then doing a load 396190075Sobrien multiple actually becomes slower (and no smaller in code size). 396290075Sobrien That is the transformation 396390075Sobrien 396490075Sobrien ldr rd1, [rbase + offset] 396590075Sobrien ldr rd2, [rbase + offset + 4] 396690075Sobrien 396790075Sobrien to 396890075Sobrien 396990075Sobrien add rd1, rbase, offset 397090075Sobrien ldmia rd1, {rd1, rd2} 397190075Sobrien 397290075Sobrien produces worse code -- '3 cycles + any stalls on rd2' instead of 397390075Sobrien '2 cycles + any stalls on rd2'. On ARMs with only one cache 397490075Sobrien access per cycle, the first sequence could never complete in less 397590075Sobrien than 6 cycles, whereas the ldm sequence would only take 5 and 397690075Sobrien would make better use of sequential accesses if not hitting the 397790075Sobrien cache. 397890075Sobrien 397990075Sobrien We cheat here and test 'arm_ld_sched' which we currently know to 398090075Sobrien only be true for the ARM8, ARM9 and StrongARM. If this ever 398190075Sobrien changes, then the test below needs to be reworked. */ 398290075Sobrien if (nops == 2 && arm_ld_sched) 398390075Sobrien return 0; 398490075Sobrien 398590075Sobrien /* Can't do it without setting up the offset, only do this if it takes 398690075Sobrien no more than one insn. */ 398790075Sobrien return (const_ok_for_arm (unsorted_offsets[order[0]]) 398890075Sobrien || const_ok_for_arm (-unsorted_offsets[order[0]])) ? 5 : 0; 398990075Sobrien} 399090075Sobrien 399190075Sobrienconst char * 399290075Sobrienemit_ldm_seq (operands, nops) 399390075Sobrien rtx * operands; 399490075Sobrien int nops; 399590075Sobrien{ 399690075Sobrien int regs[4]; 399790075Sobrien int base_reg; 399890075Sobrien HOST_WIDE_INT offset; 399990075Sobrien char buf[100]; 400090075Sobrien int i; 400190075Sobrien 400290075Sobrien switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset)) 400390075Sobrien { 400490075Sobrien case 1: 400590075Sobrien strcpy (buf, "ldm%?ia\t"); 400690075Sobrien break; 400790075Sobrien 400890075Sobrien case 2: 400990075Sobrien strcpy (buf, "ldm%?ib\t"); 401090075Sobrien break; 401190075Sobrien 401290075Sobrien case 3: 401390075Sobrien strcpy (buf, "ldm%?da\t"); 401490075Sobrien break; 401590075Sobrien 401690075Sobrien case 4: 401790075Sobrien strcpy (buf, "ldm%?db\t"); 401890075Sobrien break; 401990075Sobrien 402090075Sobrien case 5: 402190075Sobrien if (offset >= 0) 402290075Sobrien sprintf (buf, "add%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, 402390075Sobrien reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], 402490075Sobrien (long) offset); 402590075Sobrien else 402690075Sobrien sprintf (buf, "sub%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, 402790075Sobrien reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], 402890075Sobrien (long) -offset); 402990075Sobrien output_asm_insn (buf, operands); 403090075Sobrien base_reg = regs[0]; 403190075Sobrien strcpy (buf, "ldm%?ia\t"); 403290075Sobrien break; 403390075Sobrien 403490075Sobrien default: 403590075Sobrien abort (); 403690075Sobrien } 403790075Sobrien 403890075Sobrien sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, 403990075Sobrien reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); 404090075Sobrien 404190075Sobrien for (i = 1; i < nops; i++) 404290075Sobrien sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, 404390075Sobrien reg_names[regs[i]]); 404490075Sobrien 404590075Sobrien strcat (buf, "}\t%@ phole ldm"); 404690075Sobrien 404790075Sobrien output_asm_insn (buf, operands); 404890075Sobrien return ""; 404990075Sobrien} 405090075Sobrien 405190075Sobrienint 405290075Sobrienstore_multiple_sequence (operands, nops, regs, base, load_offset) 405390075Sobrien rtx * operands; 405490075Sobrien int nops; 405590075Sobrien int * regs; 405690075Sobrien int * base; 405790075Sobrien HOST_WIDE_INT * load_offset; 405890075Sobrien{ 405990075Sobrien int unsorted_regs[4]; 406090075Sobrien HOST_WIDE_INT unsorted_offsets[4]; 406190075Sobrien int order[4]; 406290075Sobrien int base_reg = -1; 406390075Sobrien int i; 406490075Sobrien 406590075Sobrien /* Can only handle 2, 3, or 4 insns at present, though could be easily 406690075Sobrien extended if required. */ 406790075Sobrien if (nops < 2 || nops > 4) 406890075Sobrien abort (); 406990075Sobrien 407090075Sobrien /* Loop over the operands and check that the memory references are 407190075Sobrien suitable (ie immediate offsets from the same base register). At 407290075Sobrien the same time, extract the target register, and the memory 407390075Sobrien offsets. */ 407490075Sobrien for (i = 0; i < nops; i++) 407590075Sobrien { 407690075Sobrien rtx reg; 407790075Sobrien rtx offset; 407890075Sobrien 407990075Sobrien /* Convert a subreg of a mem into the mem itself. */ 408090075Sobrien if (GET_CODE (operands[nops + i]) == SUBREG) 408190075Sobrien operands[nops + i] = alter_subreg (operands + (nops + i)); 408290075Sobrien 408390075Sobrien if (GET_CODE (operands[nops + i]) != MEM) 408490075Sobrien abort (); 408590075Sobrien 408690075Sobrien /* Don't reorder volatile memory references; it doesn't seem worth 408790075Sobrien looking for the case where the order is ok anyway. */ 408890075Sobrien if (MEM_VOLATILE_P (operands[nops + i])) 408990075Sobrien return 0; 409090075Sobrien 409190075Sobrien offset = const0_rtx; 409290075Sobrien 409390075Sobrien if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG 409490075Sobrien || (GET_CODE (reg) == SUBREG 409590075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 409690075Sobrien || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS 409790075Sobrien && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) 409890075Sobrien == REG) 409990075Sobrien || (GET_CODE (reg) == SUBREG 410090075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 410190075Sobrien && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) 410290075Sobrien == CONST_INT))) 410390075Sobrien { 410490075Sobrien if (i == 0) 410590075Sobrien { 410690075Sobrien base_reg = REGNO (reg); 410790075Sobrien unsorted_regs[0] = (GET_CODE (operands[i]) == REG 410890075Sobrien ? REGNO (operands[i]) 410990075Sobrien : REGNO (SUBREG_REG (operands[i]))); 411090075Sobrien order[0] = 0; 411190075Sobrien } 411290075Sobrien else 411390075Sobrien { 411490075Sobrien if (base_reg != (int) REGNO (reg)) 411590075Sobrien /* Not addressed from the same base register. */ 411690075Sobrien return 0; 411790075Sobrien 411890075Sobrien unsorted_regs[i] = (GET_CODE (operands[i]) == REG 411990075Sobrien ? REGNO (operands[i]) 412090075Sobrien : REGNO (SUBREG_REG (operands[i]))); 412190075Sobrien if (unsorted_regs[i] < unsorted_regs[order[0]]) 412290075Sobrien order[0] = i; 412390075Sobrien } 412490075Sobrien 412590075Sobrien /* If it isn't an integer register, then we can't do this. */ 412690075Sobrien if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14) 412790075Sobrien return 0; 412890075Sobrien 412990075Sobrien unsorted_offsets[i] = INTVAL (offset); 413090075Sobrien } 413190075Sobrien else 413290075Sobrien /* Not a suitable memory address. */ 413390075Sobrien return 0; 413490075Sobrien } 413590075Sobrien 413690075Sobrien /* All the useful information has now been extracted from the 413790075Sobrien operands into unsorted_regs and unsorted_offsets; additionally, 413890075Sobrien order[0] has been set to the lowest numbered register in the 413990075Sobrien list. Sort the registers into order, and check that the memory 414090075Sobrien offsets are ascending and adjacent. */ 414190075Sobrien 414290075Sobrien for (i = 1; i < nops; i++) 414390075Sobrien { 414490075Sobrien int j; 414590075Sobrien 414690075Sobrien order[i] = order[i - 1]; 414790075Sobrien for (j = 0; j < nops; j++) 414890075Sobrien if (unsorted_regs[j] > unsorted_regs[order[i - 1]] 414990075Sobrien && (order[i] == order[i - 1] 415090075Sobrien || unsorted_regs[j] < unsorted_regs[order[i]])) 415190075Sobrien order[i] = j; 415290075Sobrien 415390075Sobrien /* Have we found a suitable register? if not, one must be used more 415490075Sobrien than once. */ 415590075Sobrien if (order[i] == order[i - 1]) 415690075Sobrien return 0; 415790075Sobrien 415890075Sobrien /* Is the memory address adjacent and ascending? */ 415990075Sobrien if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) 416090075Sobrien return 0; 416190075Sobrien } 416290075Sobrien 416390075Sobrien if (base) 416490075Sobrien { 416590075Sobrien *base = base_reg; 416690075Sobrien 416790075Sobrien for (i = 0; i < nops; i++) 416890075Sobrien regs[i] = unsorted_regs[order[i]]; 416990075Sobrien 417090075Sobrien *load_offset = unsorted_offsets[order[0]]; 417190075Sobrien } 417290075Sobrien 417390075Sobrien if (unsorted_offsets[order[0]] == 0) 417490075Sobrien return 1; /* stmia */ 417590075Sobrien 417690075Sobrien if (unsorted_offsets[order[0]] == 4) 417790075Sobrien return 2; /* stmib */ 417890075Sobrien 417990075Sobrien if (unsorted_offsets[order[nops - 1]] == 0) 418090075Sobrien return 3; /* stmda */ 418190075Sobrien 418290075Sobrien if (unsorted_offsets[order[nops - 1]] == -4) 418390075Sobrien return 4; /* stmdb */ 418490075Sobrien 418590075Sobrien return 0; 418690075Sobrien} 418790075Sobrien 418890075Sobrienconst char * 418990075Sobrienemit_stm_seq (operands, nops) 419090075Sobrien rtx * operands; 419190075Sobrien int nops; 419290075Sobrien{ 419390075Sobrien int regs[4]; 419490075Sobrien int base_reg; 419590075Sobrien HOST_WIDE_INT offset; 419690075Sobrien char buf[100]; 419790075Sobrien int i; 419890075Sobrien 419990075Sobrien switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset)) 420090075Sobrien { 420190075Sobrien case 1: 420290075Sobrien strcpy (buf, "stm%?ia\t"); 420390075Sobrien break; 420490075Sobrien 420590075Sobrien case 2: 420690075Sobrien strcpy (buf, "stm%?ib\t"); 420790075Sobrien break; 420890075Sobrien 420990075Sobrien case 3: 421090075Sobrien strcpy (buf, "stm%?da\t"); 421190075Sobrien break; 421290075Sobrien 421390075Sobrien case 4: 421490075Sobrien strcpy (buf, "stm%?db\t"); 421590075Sobrien break; 421690075Sobrien 421790075Sobrien default: 421890075Sobrien abort (); 421990075Sobrien } 422090075Sobrien 422190075Sobrien sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, 422290075Sobrien reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); 422390075Sobrien 422490075Sobrien for (i = 1; i < nops; i++) 422590075Sobrien sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, 422690075Sobrien reg_names[regs[i]]); 422790075Sobrien 422890075Sobrien strcat (buf, "}\t%@ phole stm"); 422990075Sobrien 423090075Sobrien output_asm_insn (buf, operands); 423190075Sobrien return ""; 423290075Sobrien} 423390075Sobrien 423490075Sobrienint 423590075Sobrienmulti_register_push (op, mode) 423690075Sobrien rtx op; 423790075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 423890075Sobrien{ 423990075Sobrien if (GET_CODE (op) != PARALLEL 424090075Sobrien || (GET_CODE (XVECEXP (op, 0, 0)) != SET) 424190075Sobrien || (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) 424290075Sobrien || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSH_MULT)) 424390075Sobrien return 0; 424490075Sobrien 424590075Sobrien return 1; 424690075Sobrien} 424790075Sobrien 424890075Sobrien/* Routines for use in generating RTL. */ 424990075Sobrien 425090075Sobrienrtx 425190075Sobrienarm_gen_load_multiple (base_regno, count, from, up, write_back, unchanging_p, 425290075Sobrien in_struct_p, scalar_p) 425390075Sobrien int base_regno; 425490075Sobrien int count; 425590075Sobrien rtx from; 425690075Sobrien int up; 425790075Sobrien int write_back; 425890075Sobrien int unchanging_p; 425990075Sobrien int in_struct_p; 426090075Sobrien int scalar_p; 426190075Sobrien{ 426290075Sobrien int i = 0, j; 426390075Sobrien rtx result; 426490075Sobrien int sign = up ? 1 : -1; 426590075Sobrien rtx mem; 426690075Sobrien 426790075Sobrien /* XScale has load-store double instructions, but they have stricter 426890075Sobrien alignment requirements than load-store multiple, so we can not 426990075Sobrien use them. 427090075Sobrien 427190075Sobrien For XScale ldm requires 2 + NREGS cycles to complete and blocks 427290075Sobrien the pipeline until completion. 427390075Sobrien 427490075Sobrien NREGS CYCLES 427590075Sobrien 1 3 427690075Sobrien 2 4 427790075Sobrien 3 5 427890075Sobrien 4 6 427990075Sobrien 428090075Sobrien An ldr instruction takes 1-3 cycles, but does not block the 428190075Sobrien pipeline. 428290075Sobrien 428390075Sobrien NREGS CYCLES 428490075Sobrien 1 1-3 428590075Sobrien 2 2-6 428690075Sobrien 3 3-9 428790075Sobrien 4 4-12 428890075Sobrien 428990075Sobrien Best case ldr will always win. However, the more ldr instructions 429090075Sobrien we issue, the less likely we are to be able to schedule them well. 429190075Sobrien Using ldr instructions also increases code size. 429290075Sobrien 429390075Sobrien As a compromise, we use ldr for counts of 1 or 2 regs, and ldm 429490075Sobrien for counts of 3 or 4 regs. */ 429590075Sobrien if (arm_is_xscale && count <= 2 && ! optimize_size) 429690075Sobrien { 429790075Sobrien rtx seq; 429890075Sobrien 429990075Sobrien start_sequence (); 430090075Sobrien 430190075Sobrien for (i = 0; i < count; i++) 430290075Sobrien { 430390075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (from, i * 4 * sign)); 430490075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 430590075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 430690075Sobrien MEM_SCALAR_P (mem) = scalar_p; 430790075Sobrien emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem); 430890075Sobrien } 430990075Sobrien 431090075Sobrien if (write_back) 431190075Sobrien emit_move_insn (from, plus_constant (from, count * 4 * sign)); 431290075Sobrien 431390075Sobrien seq = gen_sequence (); 431490075Sobrien end_sequence (); 431590075Sobrien 431690075Sobrien return seq; 431790075Sobrien } 431890075Sobrien 431990075Sobrien result = gen_rtx_PARALLEL (VOIDmode, 432090075Sobrien rtvec_alloc (count + (write_back ? 1 : 0))); 432190075Sobrien if (write_back) 432290075Sobrien { 432390075Sobrien XVECEXP (result, 0, 0) 432490075Sobrien = gen_rtx_SET (GET_MODE (from), from, 432590075Sobrien plus_constant (from, count * 4 * sign)); 432690075Sobrien i = 1; 432790075Sobrien count++; 432890075Sobrien } 432990075Sobrien 433090075Sobrien for (j = 0; i < count; i++, j++) 433190075Sobrien { 433290075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (from, j * 4 * sign)); 433390075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 433490075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 433590075Sobrien MEM_SCALAR_P (mem) = scalar_p; 433690075Sobrien XVECEXP (result, 0, i) 433790075Sobrien = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, base_regno + j), mem); 433890075Sobrien } 433990075Sobrien 434090075Sobrien return result; 434190075Sobrien} 434290075Sobrien 434390075Sobrienrtx 434490075Sobrienarm_gen_store_multiple (base_regno, count, to, up, write_back, unchanging_p, 434590075Sobrien in_struct_p, scalar_p) 434690075Sobrien int base_regno; 434790075Sobrien int count; 434890075Sobrien rtx to; 434990075Sobrien int up; 435090075Sobrien int write_back; 435190075Sobrien int unchanging_p; 435290075Sobrien int in_struct_p; 435390075Sobrien int scalar_p; 435490075Sobrien{ 435590075Sobrien int i = 0, j; 435690075Sobrien rtx result; 435790075Sobrien int sign = up ? 1 : -1; 435890075Sobrien rtx mem; 435990075Sobrien 436090075Sobrien /* See arm_gen_load_multiple for discussion of 436190075Sobrien the pros/cons of ldm/stm usage for XScale. */ 436290075Sobrien if (arm_is_xscale && count <= 2 && ! optimize_size) 436390075Sobrien { 436490075Sobrien rtx seq; 436590075Sobrien 436690075Sobrien start_sequence (); 436790075Sobrien 436890075Sobrien for (i = 0; i < count; i++) 436990075Sobrien { 437090075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (to, i * 4 * sign)); 437190075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 437290075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 437390075Sobrien MEM_SCALAR_P (mem) = scalar_p; 437490075Sobrien emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i)); 437590075Sobrien } 437690075Sobrien 437790075Sobrien if (write_back) 437890075Sobrien emit_move_insn (to, plus_constant (to, count * 4 * sign)); 437990075Sobrien 438090075Sobrien seq = gen_sequence (); 438190075Sobrien end_sequence (); 438290075Sobrien 438390075Sobrien return seq; 438490075Sobrien } 438590075Sobrien 438690075Sobrien result = gen_rtx_PARALLEL (VOIDmode, 438790075Sobrien rtvec_alloc (count + (write_back ? 1 : 0))); 438890075Sobrien if (write_back) 438990075Sobrien { 439090075Sobrien XVECEXP (result, 0, 0) 439190075Sobrien = gen_rtx_SET (GET_MODE (to), to, 439290075Sobrien plus_constant (to, count * 4 * sign)); 439390075Sobrien i = 1; 439490075Sobrien count++; 439590075Sobrien } 439690075Sobrien 439790075Sobrien for (j = 0; i < count; i++, j++) 439890075Sobrien { 439990075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (to, j * 4 * sign)); 440090075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 440190075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 440290075Sobrien MEM_SCALAR_P (mem) = scalar_p; 440390075Sobrien 440490075Sobrien XVECEXP (result, 0, i) 440590075Sobrien = gen_rtx_SET (VOIDmode, mem, gen_rtx_REG (SImode, base_regno + j)); 440690075Sobrien } 440790075Sobrien 440890075Sobrien return result; 440990075Sobrien} 441090075Sobrien 441190075Sobrienint 441290075Sobrienarm_gen_movstrqi (operands) 441390075Sobrien rtx * operands; 441490075Sobrien{ 441590075Sobrien HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes; 441690075Sobrien int i; 441790075Sobrien rtx src, dst; 441890075Sobrien rtx st_src, st_dst, fin_src, fin_dst; 441990075Sobrien rtx part_bytes_reg = NULL; 442090075Sobrien rtx mem; 442190075Sobrien int dst_unchanging_p, dst_in_struct_p, src_unchanging_p, src_in_struct_p; 442290075Sobrien int dst_scalar_p, src_scalar_p; 442390075Sobrien 442490075Sobrien if (GET_CODE (operands[2]) != CONST_INT 442590075Sobrien || GET_CODE (operands[3]) != CONST_INT 442690075Sobrien || INTVAL (operands[2]) > 64 442790075Sobrien || INTVAL (operands[3]) & 3) 442890075Sobrien return 0; 442990075Sobrien 443090075Sobrien st_dst = XEXP (operands[0], 0); 443190075Sobrien st_src = XEXP (operands[1], 0); 443290075Sobrien 443390075Sobrien dst_unchanging_p = RTX_UNCHANGING_P (operands[0]); 443490075Sobrien dst_in_struct_p = MEM_IN_STRUCT_P (operands[0]); 443590075Sobrien dst_scalar_p = MEM_SCALAR_P (operands[0]); 443690075Sobrien src_unchanging_p = RTX_UNCHANGING_P (operands[1]); 443790075Sobrien src_in_struct_p = MEM_IN_STRUCT_P (operands[1]); 443890075Sobrien src_scalar_p = MEM_SCALAR_P (operands[1]); 443990075Sobrien 444090075Sobrien fin_dst = dst = copy_to_mode_reg (SImode, st_dst); 444190075Sobrien fin_src = src = copy_to_mode_reg (SImode, st_src); 444290075Sobrien 444390075Sobrien in_words_to_go = NUM_INTS (INTVAL (operands[2])); 444490075Sobrien out_words_to_go = INTVAL (operands[2]) / 4; 444590075Sobrien last_bytes = INTVAL (operands[2]) & 3; 444690075Sobrien 444790075Sobrien if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0) 444890075Sobrien part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3); 444990075Sobrien 445090075Sobrien for (i = 0; in_words_to_go >= 2; i+=4) 445190075Sobrien { 445290075Sobrien if (in_words_to_go > 4) 445390075Sobrien emit_insn (arm_gen_load_multiple (0, 4, src, TRUE, TRUE, 445490075Sobrien src_unchanging_p, 445590075Sobrien src_in_struct_p, 445690075Sobrien src_scalar_p)); 445790075Sobrien else 445890075Sobrien emit_insn (arm_gen_load_multiple (0, in_words_to_go, src, TRUE, 445990075Sobrien FALSE, src_unchanging_p, 446090075Sobrien src_in_struct_p, src_scalar_p)); 446190075Sobrien 446290075Sobrien if (out_words_to_go) 446390075Sobrien { 446490075Sobrien if (out_words_to_go > 4) 446590075Sobrien emit_insn (arm_gen_store_multiple (0, 4, dst, TRUE, TRUE, 446690075Sobrien dst_unchanging_p, 446790075Sobrien dst_in_struct_p, 446890075Sobrien dst_scalar_p)); 446990075Sobrien else if (out_words_to_go != 1) 447090075Sobrien emit_insn (arm_gen_store_multiple (0, out_words_to_go, 447190075Sobrien dst, TRUE, 447290075Sobrien (last_bytes == 0 447390075Sobrien ? FALSE : TRUE), 447490075Sobrien dst_unchanging_p, 447590075Sobrien dst_in_struct_p, 447690075Sobrien dst_scalar_p)); 447790075Sobrien else 447890075Sobrien { 447990075Sobrien mem = gen_rtx_MEM (SImode, dst); 448090075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 448190075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 448290075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 448390075Sobrien emit_move_insn (mem, gen_rtx_REG (SImode, 0)); 448490075Sobrien if (last_bytes != 0) 448590075Sobrien emit_insn (gen_addsi3 (dst, dst, GEN_INT (4))); 448690075Sobrien } 448790075Sobrien } 448890075Sobrien 448990075Sobrien in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4; 449090075Sobrien out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4; 449190075Sobrien } 449290075Sobrien 449390075Sobrien /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do. */ 449490075Sobrien if (out_words_to_go) 449590075Sobrien { 449690075Sobrien rtx sreg; 449790075Sobrien 449890075Sobrien mem = gen_rtx_MEM (SImode, src); 449990075Sobrien RTX_UNCHANGING_P (mem) = src_unchanging_p; 450090075Sobrien MEM_IN_STRUCT_P (mem) = src_in_struct_p; 450190075Sobrien MEM_SCALAR_P (mem) = src_scalar_p; 450290075Sobrien emit_move_insn (sreg = gen_reg_rtx (SImode), mem); 450390075Sobrien emit_move_insn (fin_src = gen_reg_rtx (SImode), plus_constant (src, 4)); 450490075Sobrien 450590075Sobrien mem = gen_rtx_MEM (SImode, dst); 450690075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 450790075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 450890075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 450990075Sobrien emit_move_insn (mem, sreg); 451090075Sobrien emit_move_insn (fin_dst = gen_reg_rtx (SImode), plus_constant (dst, 4)); 451190075Sobrien in_words_to_go--; 451290075Sobrien 451390075Sobrien if (in_words_to_go) /* Sanity check */ 451490075Sobrien abort (); 451590075Sobrien } 451690075Sobrien 451790075Sobrien if (in_words_to_go) 451890075Sobrien { 451990075Sobrien if (in_words_to_go < 0) 452090075Sobrien abort (); 452190075Sobrien 452290075Sobrien mem = gen_rtx_MEM (SImode, src); 452390075Sobrien RTX_UNCHANGING_P (mem) = src_unchanging_p; 452490075Sobrien MEM_IN_STRUCT_P (mem) = src_in_struct_p; 452590075Sobrien MEM_SCALAR_P (mem) = src_scalar_p; 452690075Sobrien part_bytes_reg = copy_to_mode_reg (SImode, mem); 452790075Sobrien } 452890075Sobrien 452990075Sobrien if (last_bytes && part_bytes_reg == NULL) 453090075Sobrien abort (); 453190075Sobrien 453290075Sobrien if (BYTES_BIG_ENDIAN && last_bytes) 453390075Sobrien { 453490075Sobrien rtx tmp = gen_reg_rtx (SImode); 453590075Sobrien 453690075Sobrien /* The bytes we want are in the top end of the word. */ 453790075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, 453890075Sobrien GEN_INT (8 * (4 - last_bytes)))); 453990075Sobrien part_bytes_reg = tmp; 454090075Sobrien 454190075Sobrien while (last_bytes) 454290075Sobrien { 454390075Sobrien mem = gen_rtx_MEM (QImode, plus_constant (dst, last_bytes - 1)); 454490075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 454590075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 454690075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 454790075Sobrien emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0)); 454890075Sobrien 454990075Sobrien if (--last_bytes) 455090075Sobrien { 455190075Sobrien tmp = gen_reg_rtx (SImode); 455290075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8))); 455390075Sobrien part_bytes_reg = tmp; 455490075Sobrien } 455590075Sobrien } 455690075Sobrien 455790075Sobrien } 455890075Sobrien else 455990075Sobrien { 456090075Sobrien if (last_bytes > 1) 456190075Sobrien { 456290075Sobrien mem = gen_rtx_MEM (HImode, dst); 456390075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 456490075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 456590075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 456690075Sobrien emit_move_insn (mem, gen_rtx_SUBREG (HImode, part_bytes_reg, 0)); 456790075Sobrien last_bytes -= 2; 456890075Sobrien if (last_bytes) 456990075Sobrien { 457090075Sobrien rtx tmp = gen_reg_rtx (SImode); 457190075Sobrien 457290075Sobrien emit_insn (gen_addsi3 (dst, dst, GEN_INT (2))); 457390075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (16))); 457490075Sobrien part_bytes_reg = tmp; 457590075Sobrien } 457690075Sobrien } 457790075Sobrien 457890075Sobrien if (last_bytes) 457990075Sobrien { 458090075Sobrien mem = gen_rtx_MEM (QImode, dst); 458190075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 458290075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 458390075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 458490075Sobrien emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0)); 458590075Sobrien } 458690075Sobrien } 458790075Sobrien 458890075Sobrien return 1; 458990075Sobrien} 459090075Sobrien 459190075Sobrien/* Generate a memory reference for a half word, such that it will be loaded 459290075Sobrien into the top 16 bits of the word. We can assume that the address is 459390075Sobrien known to be alignable and of the form reg, or plus (reg, const). */ 459490075Sobrien 459590075Sobrienrtx 459690075Sobrienarm_gen_rotated_half_load (memref) 459790075Sobrien rtx memref; 459890075Sobrien{ 459990075Sobrien HOST_WIDE_INT offset = 0; 460090075Sobrien rtx base = XEXP (memref, 0); 460190075Sobrien 460290075Sobrien if (GET_CODE (base) == PLUS) 460390075Sobrien { 460490075Sobrien offset = INTVAL (XEXP (base, 1)); 460590075Sobrien base = XEXP (base, 0); 460690075Sobrien } 460790075Sobrien 460890075Sobrien /* If we aren't allowed to generate unaligned addresses, then fail. */ 460990075Sobrien if (TARGET_MMU_TRAPS 461090075Sobrien && ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 0))) 461190075Sobrien return NULL; 461290075Sobrien 461390075Sobrien base = gen_rtx_MEM (SImode, plus_constant (base, offset & ~2)); 461490075Sobrien 461590075Sobrien if ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 2)) 461690075Sobrien return base; 461790075Sobrien 461890075Sobrien return gen_rtx_ROTATE (SImode, base, GEN_INT (16)); 461990075Sobrien} 462090075Sobrien 462190075Sobrien/* Select a dominance comparison mode if possible. We support three forms. 462290075Sobrien COND_OR == 0 => (X && Y) 462390075Sobrien COND_OR == 1 => ((! X( || Y) 462490075Sobrien COND_OR == 2 => (X || Y) 462590075Sobrien If we are unable to support a dominance comparsison we return CC mode. 462690075Sobrien This will then fail to match for the RTL expressions that generate this 462790075Sobrien call. */ 462890075Sobrien 462990075Sobrienstatic enum machine_mode 463090075Sobrienselect_dominance_cc_mode (x, y, cond_or) 463190075Sobrien rtx x; 463290075Sobrien rtx y; 463390075Sobrien HOST_WIDE_INT cond_or; 463490075Sobrien{ 463590075Sobrien enum rtx_code cond1, cond2; 463690075Sobrien int swapped = 0; 463790075Sobrien 463890075Sobrien /* Currently we will probably get the wrong result if the individual 463990075Sobrien comparisons are not simple. This also ensures that it is safe to 464090075Sobrien reverse a comparison if necessary. */ 464190075Sobrien if ((arm_select_cc_mode (cond1 = GET_CODE (x), XEXP (x, 0), XEXP (x, 1)) 464290075Sobrien != CCmode) 464390075Sobrien || (arm_select_cc_mode (cond2 = GET_CODE (y), XEXP (y, 0), XEXP (y, 1)) 464490075Sobrien != CCmode)) 464590075Sobrien return CCmode; 464690075Sobrien 464790075Sobrien /* The if_then_else variant of this tests the second condition if the 464890075Sobrien first passes, but is true if the first fails. Reverse the first 464990075Sobrien condition to get a true "inclusive-or" expression. */ 465090075Sobrien if (cond_or == 1) 465190075Sobrien cond1 = reverse_condition (cond1); 465290075Sobrien 465390075Sobrien /* If the comparisons are not equal, and one doesn't dominate the other, 465490075Sobrien then we can't do this. */ 465590075Sobrien if (cond1 != cond2 465690075Sobrien && !comparison_dominates_p (cond1, cond2) 465790075Sobrien && (swapped = 1, !comparison_dominates_p (cond2, cond1))) 465890075Sobrien return CCmode; 465990075Sobrien 466090075Sobrien if (swapped) 466190075Sobrien { 466290075Sobrien enum rtx_code temp = cond1; 466390075Sobrien cond1 = cond2; 466490075Sobrien cond2 = temp; 466590075Sobrien } 466690075Sobrien 466790075Sobrien switch (cond1) 466890075Sobrien { 466990075Sobrien case EQ: 467090075Sobrien if (cond2 == EQ || !cond_or) 467190075Sobrien return CC_DEQmode; 467290075Sobrien 467390075Sobrien switch (cond2) 467490075Sobrien { 467590075Sobrien case LE: return CC_DLEmode; 467690075Sobrien case LEU: return CC_DLEUmode; 467790075Sobrien case GE: return CC_DGEmode; 467890075Sobrien case GEU: return CC_DGEUmode; 467990075Sobrien default: break; 468090075Sobrien } 468190075Sobrien 468290075Sobrien break; 468390075Sobrien 468490075Sobrien case LT: 468590075Sobrien if (cond2 == LT || !cond_or) 468690075Sobrien return CC_DLTmode; 468790075Sobrien if (cond2 == LE) 468890075Sobrien return CC_DLEmode; 468990075Sobrien if (cond2 == NE) 469090075Sobrien return CC_DNEmode; 469190075Sobrien break; 469290075Sobrien 469390075Sobrien case GT: 469490075Sobrien if (cond2 == GT || !cond_or) 469590075Sobrien return CC_DGTmode; 469690075Sobrien if (cond2 == GE) 469790075Sobrien return CC_DGEmode; 469890075Sobrien if (cond2 == NE) 469990075Sobrien return CC_DNEmode; 470090075Sobrien break; 470190075Sobrien 470290075Sobrien case LTU: 470390075Sobrien if (cond2 == LTU || !cond_or) 470490075Sobrien return CC_DLTUmode; 470590075Sobrien if (cond2 == LEU) 470690075Sobrien return CC_DLEUmode; 470790075Sobrien if (cond2 == NE) 470890075Sobrien return CC_DNEmode; 470990075Sobrien break; 471090075Sobrien 471190075Sobrien case GTU: 471290075Sobrien if (cond2 == GTU || !cond_or) 471390075Sobrien return CC_DGTUmode; 471490075Sobrien if (cond2 == GEU) 471590075Sobrien return CC_DGEUmode; 471690075Sobrien if (cond2 == NE) 471790075Sobrien return CC_DNEmode; 471890075Sobrien break; 471990075Sobrien 472090075Sobrien /* The remaining cases only occur when both comparisons are the 472190075Sobrien same. */ 472290075Sobrien case NE: 472390075Sobrien return CC_DNEmode; 472490075Sobrien 472590075Sobrien case LE: 472690075Sobrien return CC_DLEmode; 472790075Sobrien 472890075Sobrien case GE: 472990075Sobrien return CC_DGEmode; 473090075Sobrien 473190075Sobrien case LEU: 473290075Sobrien return CC_DLEUmode; 473390075Sobrien 473490075Sobrien case GEU: 473590075Sobrien return CC_DGEUmode; 473690075Sobrien 473790075Sobrien default: 473890075Sobrien break; 473990075Sobrien } 474090075Sobrien 474190075Sobrien abort (); 474290075Sobrien} 474390075Sobrien 474490075Sobrienenum machine_mode 474590075Sobrienarm_select_cc_mode (op, x, y) 474690075Sobrien enum rtx_code op; 474790075Sobrien rtx x; 474890075Sobrien rtx y; 474990075Sobrien{ 475090075Sobrien /* All floating point compares return CCFP if it is an equality 475190075Sobrien comparison, and CCFPE otherwise. */ 475290075Sobrien if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) 475390075Sobrien { 475490075Sobrien switch (op) 475590075Sobrien { 475690075Sobrien case EQ: 475790075Sobrien case NE: 475890075Sobrien case UNORDERED: 475990075Sobrien case ORDERED: 476090075Sobrien case UNLT: 476190075Sobrien case UNLE: 476290075Sobrien case UNGT: 476390075Sobrien case UNGE: 476490075Sobrien case UNEQ: 476590075Sobrien case LTGT: 476690075Sobrien return CCFPmode; 476790075Sobrien 476890075Sobrien case LT: 476990075Sobrien case LE: 477090075Sobrien case GT: 477190075Sobrien case GE: 477290075Sobrien return CCFPEmode; 477390075Sobrien 477490075Sobrien default: 477590075Sobrien abort (); 477690075Sobrien } 477790075Sobrien } 477890075Sobrien 477990075Sobrien /* A compare with a shifted operand. Because of canonicalization, the 478090075Sobrien comparison will have to be swapped when we emit the assembler. */ 478190075Sobrien if (GET_MODE (y) == SImode && GET_CODE (y) == REG 478290075Sobrien && (GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT 478390075Sobrien || GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ROTATE 478490075Sobrien || GET_CODE (x) == ROTATERT)) 478590075Sobrien return CC_SWPmode; 478690075Sobrien 478790075Sobrien /* This is a special case that is used by combine to allow a 478890075Sobrien comparison of a shifted byte load to be split into a zero-extend 478990075Sobrien followed by a comparison of the shifted integer (only valid for 479090075Sobrien equalities and unsigned inequalities). */ 479190075Sobrien if (GET_MODE (x) == SImode 479290075Sobrien && GET_CODE (x) == ASHIFT 479390075Sobrien && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 24 479490075Sobrien && GET_CODE (XEXP (x, 0)) == SUBREG 479590075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == MEM 479690075Sobrien && GET_MODE (SUBREG_REG (XEXP (x, 0))) == QImode 479790075Sobrien && (op == EQ || op == NE 479890075Sobrien || op == GEU || op == GTU || op == LTU || op == LEU) 479990075Sobrien && GET_CODE (y) == CONST_INT) 480090075Sobrien return CC_Zmode; 480190075Sobrien 480290075Sobrien /* A construct for a conditional compare, if the false arm contains 480390075Sobrien 0, then both conditions must be true, otherwise either condition 480490075Sobrien must be true. Not all conditions are possible, so CCmode is 480590075Sobrien returned if it can't be done. */ 480690075Sobrien if (GET_CODE (x) == IF_THEN_ELSE 480790075Sobrien && (XEXP (x, 2) == const0_rtx 480890075Sobrien || XEXP (x, 2) == const1_rtx) 480990075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 481090075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 481190075Sobrien return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 481290075Sobrien INTVAL (XEXP (x, 2))); 481390075Sobrien 481490075Sobrien /* Alternate canonicalizations of the above. These are somewhat cleaner. */ 481590075Sobrien if (GET_CODE (x) == AND 481690075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 481790075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 481890075Sobrien return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 0); 481990075Sobrien 482090075Sobrien if (GET_CODE (x) == IOR 482190075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 482290075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 482390075Sobrien return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 2); 482490075Sobrien 482590075Sobrien /* An operation that sets the condition codes as a side-effect, the 482690075Sobrien V flag is not set correctly, so we can only use comparisons where 482790075Sobrien this doesn't matter. (For LT and GE we can use "mi" and "pl" 482890075Sobrien instead. */ 482990075Sobrien if (GET_MODE (x) == SImode 483090075Sobrien && y == const0_rtx 483190075Sobrien && (op == EQ || op == NE || op == LT || op == GE) 483290075Sobrien && (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS 483390075Sobrien || GET_CODE (x) == AND || GET_CODE (x) == IOR 483490075Sobrien || GET_CODE (x) == XOR || GET_CODE (x) == MULT 483590075Sobrien || GET_CODE (x) == NOT || GET_CODE (x) == NEG 483690075Sobrien || GET_CODE (x) == LSHIFTRT 483790075Sobrien || GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT 483890075Sobrien || GET_CODE (x) == ROTATERT || GET_CODE (x) == ZERO_EXTRACT)) 483990075Sobrien return CC_NOOVmode; 484090075Sobrien 484190075Sobrien if (GET_MODE (x) == QImode && (op == EQ || op == NE)) 484290075Sobrien return CC_Zmode; 484390075Sobrien 484490075Sobrien if (GET_MODE (x) == SImode && (op == LTU || op == GEU) 484590075Sobrien && GET_CODE (x) == PLUS 484690075Sobrien && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y))) 484790075Sobrien return CC_Cmode; 484890075Sobrien 484990075Sobrien return CCmode; 485090075Sobrien} 485190075Sobrien 485290075Sobrien/* X and Y are two things to compare using CODE. Emit the compare insn and 485390075Sobrien return the rtx for register 0 in the proper mode. FP means this is a 485490075Sobrien floating point compare: I don't think that it is needed on the arm. */ 485590075Sobrien 485690075Sobrienrtx 485790075Sobrienarm_gen_compare_reg (code, x, y) 485890075Sobrien enum rtx_code code; 485990075Sobrien rtx x, y; 486090075Sobrien{ 486190075Sobrien enum machine_mode mode = SELECT_CC_MODE (code, x, y); 486290075Sobrien rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM); 486390075Sobrien 486490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, cc_reg, 486590075Sobrien gen_rtx_COMPARE (mode, x, y))); 486690075Sobrien 486790075Sobrien return cc_reg; 486890075Sobrien} 486990075Sobrien 487090075Sobrienvoid 487190075Sobrienarm_reload_in_hi (operands) 487290075Sobrien rtx * operands; 487390075Sobrien{ 487490075Sobrien rtx ref = operands[1]; 487590075Sobrien rtx base, scratch; 487690075Sobrien HOST_WIDE_INT offset = 0; 487790075Sobrien 487890075Sobrien if (GET_CODE (ref) == SUBREG) 487990075Sobrien { 488090075Sobrien offset = SUBREG_BYTE (ref); 488190075Sobrien ref = SUBREG_REG (ref); 488290075Sobrien } 488390075Sobrien 488490075Sobrien if (GET_CODE (ref) == REG) 488590075Sobrien { 488690075Sobrien /* We have a pseudo which has been spilt onto the stack; there 488790075Sobrien are two cases here: the first where there is a simple 488890075Sobrien stack-slot replacement and a second where the stack-slot is 488990075Sobrien out of range, or is used as a subreg. */ 489090075Sobrien if (reg_equiv_mem[REGNO (ref)]) 489190075Sobrien { 489290075Sobrien ref = reg_equiv_mem[REGNO (ref)]; 489390075Sobrien base = find_replacement (&XEXP (ref, 0)); 489490075Sobrien } 489590075Sobrien else 489690075Sobrien /* The slot is out of range, or was dressed up in a SUBREG. */ 489790075Sobrien base = reg_equiv_address[REGNO (ref)]; 489890075Sobrien } 489990075Sobrien else 490090075Sobrien base = find_replacement (&XEXP (ref, 0)); 490190075Sobrien 490290075Sobrien /* Handle the case where the address is too complex to be offset by 1. */ 490390075Sobrien if (GET_CODE (base) == MINUS 490490075Sobrien || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) 490590075Sobrien { 490690075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 490790075Sobrien 490890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); 490990075Sobrien base = base_plus; 491090075Sobrien } 491190075Sobrien else if (GET_CODE (base) == PLUS) 491290075Sobrien { 491390075Sobrien /* The addend must be CONST_INT, or we would have dealt with it above. */ 491490075Sobrien HOST_WIDE_INT hi, lo; 491590075Sobrien 491690075Sobrien offset += INTVAL (XEXP (base, 1)); 491790075Sobrien base = XEXP (base, 0); 491890075Sobrien 491990075Sobrien /* Rework the address into a legal sequence of insns. */ 492090075Sobrien /* Valid range for lo is -4095 -> 4095 */ 492190075Sobrien lo = (offset >= 0 492290075Sobrien ? (offset & 0xfff) 492390075Sobrien : -((-offset) & 0xfff)); 492490075Sobrien 492590075Sobrien /* Corner case, if lo is the max offset then we would be out of range 492690075Sobrien once we have added the additional 1 below, so bump the msb into the 492790075Sobrien pre-loading insn(s). */ 492890075Sobrien if (lo == 4095) 492990075Sobrien lo &= 0x7ff; 493090075Sobrien 493190075Sobrien hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) 493290075Sobrien ^ (HOST_WIDE_INT) 0x80000000) 493390075Sobrien - (HOST_WIDE_INT) 0x80000000); 493490075Sobrien 493590075Sobrien if (hi + lo != offset) 493690075Sobrien abort (); 493790075Sobrien 493890075Sobrien if (hi != 0) 493990075Sobrien { 494090075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 494190075Sobrien 494290075Sobrien /* Get the base address; addsi3 knows how to handle constants 494390075Sobrien that require more than one insn. */ 494490075Sobrien emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); 494590075Sobrien base = base_plus; 494690075Sobrien offset = lo; 494790075Sobrien } 494890075Sobrien } 494990075Sobrien 495090075Sobrien scratch = gen_rtx_REG (SImode, REGNO (operands[2])); 495190075Sobrien emit_insn (gen_zero_extendqisi2 (scratch, 495290075Sobrien gen_rtx_MEM (QImode, 495390075Sobrien plus_constant (base, 495490075Sobrien offset)))); 495590075Sobrien emit_insn (gen_zero_extendqisi2 (gen_rtx_SUBREG (SImode, operands[0], 0), 495690075Sobrien gen_rtx_MEM (QImode, 495790075Sobrien plus_constant (base, 495890075Sobrien offset + 1)))); 495990075Sobrien if (!BYTES_BIG_ENDIAN) 496090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), 496190075Sobrien gen_rtx_IOR (SImode, 496290075Sobrien gen_rtx_ASHIFT 496390075Sobrien (SImode, 496490075Sobrien gen_rtx_SUBREG (SImode, operands[0], 0), 496590075Sobrien GEN_INT (8)), 496690075Sobrien scratch))); 496790075Sobrien else 496890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), 496990075Sobrien gen_rtx_IOR (SImode, 497090075Sobrien gen_rtx_ASHIFT (SImode, scratch, 497190075Sobrien GEN_INT (8)), 497290075Sobrien gen_rtx_SUBREG (SImode, operands[0], 497390075Sobrien 0)))); 497490075Sobrien} 497590075Sobrien 497690075Sobrien/* Handle storing a half-word to memory during reload by synthesising as two 497790075Sobrien byte stores. Take care not to clobber the input values until after we 497890075Sobrien have moved them somewhere safe. This code assumes that if the DImode 497990075Sobrien scratch in operands[2] overlaps either the input value or output address 498090075Sobrien in some way, then that value must die in this insn (we absolutely need 498190075Sobrien two scratch registers for some corner cases). */ 498290075Sobrien 498390075Sobrienvoid 498490075Sobrienarm_reload_out_hi (operands) 498590075Sobrien rtx * operands; 498690075Sobrien{ 498790075Sobrien rtx ref = operands[0]; 498890075Sobrien rtx outval = operands[1]; 498990075Sobrien rtx base, scratch; 499090075Sobrien HOST_WIDE_INT offset = 0; 499190075Sobrien 499290075Sobrien if (GET_CODE (ref) == SUBREG) 499390075Sobrien { 499490075Sobrien offset = SUBREG_BYTE (ref); 499590075Sobrien ref = SUBREG_REG (ref); 499690075Sobrien } 499790075Sobrien 499890075Sobrien if (GET_CODE (ref) == REG) 499990075Sobrien { 500090075Sobrien /* We have a pseudo which has been spilt onto the stack; there 500190075Sobrien are two cases here: the first where there is a simple 500290075Sobrien stack-slot replacement and a second where the stack-slot is 500390075Sobrien out of range, or is used as a subreg. */ 500490075Sobrien if (reg_equiv_mem[REGNO (ref)]) 500590075Sobrien { 500690075Sobrien ref = reg_equiv_mem[REGNO (ref)]; 500790075Sobrien base = find_replacement (&XEXP (ref, 0)); 500890075Sobrien } 500990075Sobrien else 501090075Sobrien /* The slot is out of range, or was dressed up in a SUBREG. */ 501190075Sobrien base = reg_equiv_address[REGNO (ref)]; 501290075Sobrien } 501390075Sobrien else 501490075Sobrien base = find_replacement (&XEXP (ref, 0)); 501590075Sobrien 501690075Sobrien scratch = gen_rtx_REG (SImode, REGNO (operands[2])); 501790075Sobrien 501890075Sobrien /* Handle the case where the address is too complex to be offset by 1. */ 501990075Sobrien if (GET_CODE (base) == MINUS 502090075Sobrien || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) 502190075Sobrien { 502290075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 502390075Sobrien 502490075Sobrien /* Be careful not to destroy OUTVAL. */ 502590075Sobrien if (reg_overlap_mentioned_p (base_plus, outval)) 502690075Sobrien { 502790075Sobrien /* Updating base_plus might destroy outval, see if we can 502890075Sobrien swap the scratch and base_plus. */ 502990075Sobrien if (!reg_overlap_mentioned_p (scratch, outval)) 503090075Sobrien { 503190075Sobrien rtx tmp = scratch; 503290075Sobrien scratch = base_plus; 503390075Sobrien base_plus = tmp; 503490075Sobrien } 503590075Sobrien else 503690075Sobrien { 503790075Sobrien rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); 503890075Sobrien 503990075Sobrien /* Be conservative and copy OUTVAL into the scratch now, 504090075Sobrien this should only be necessary if outval is a subreg 504190075Sobrien of something larger than a word. */ 504290075Sobrien /* XXX Might this clobber base? I can't see how it can, 504390075Sobrien since scratch is known to overlap with OUTVAL, and 504490075Sobrien must be wider than a word. */ 504590075Sobrien emit_insn (gen_movhi (scratch_hi, outval)); 504690075Sobrien outval = scratch_hi; 504790075Sobrien } 504890075Sobrien } 504990075Sobrien 505090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); 505190075Sobrien base = base_plus; 505290075Sobrien } 505390075Sobrien else if (GET_CODE (base) == PLUS) 505490075Sobrien { 505590075Sobrien /* The addend must be CONST_INT, or we would have dealt with it above. */ 505690075Sobrien HOST_WIDE_INT hi, lo; 505790075Sobrien 505890075Sobrien offset += INTVAL (XEXP (base, 1)); 505990075Sobrien base = XEXP (base, 0); 506090075Sobrien 506190075Sobrien /* Rework the address into a legal sequence of insns. */ 506290075Sobrien /* Valid range for lo is -4095 -> 4095 */ 506390075Sobrien lo = (offset >= 0 506490075Sobrien ? (offset & 0xfff) 506590075Sobrien : -((-offset) & 0xfff)); 506690075Sobrien 506790075Sobrien /* Corner case, if lo is the max offset then we would be out of range 506890075Sobrien once we have added the additional 1 below, so bump the msb into the 506990075Sobrien pre-loading insn(s). */ 507090075Sobrien if (lo == 4095) 507190075Sobrien lo &= 0x7ff; 507290075Sobrien 507390075Sobrien hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) 507490075Sobrien ^ (HOST_WIDE_INT) 0x80000000) 507590075Sobrien - (HOST_WIDE_INT) 0x80000000); 507690075Sobrien 507790075Sobrien if (hi + lo != offset) 507890075Sobrien abort (); 507990075Sobrien 508090075Sobrien if (hi != 0) 508190075Sobrien { 508290075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 508390075Sobrien 508490075Sobrien /* Be careful not to destroy OUTVAL. */ 508590075Sobrien if (reg_overlap_mentioned_p (base_plus, outval)) 508690075Sobrien { 508790075Sobrien /* Updating base_plus might destroy outval, see if we 508890075Sobrien can swap the scratch and base_plus. */ 508990075Sobrien if (!reg_overlap_mentioned_p (scratch, outval)) 509090075Sobrien { 509190075Sobrien rtx tmp = scratch; 509290075Sobrien scratch = base_plus; 509390075Sobrien base_plus = tmp; 509490075Sobrien } 509590075Sobrien else 509690075Sobrien { 509790075Sobrien rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); 509890075Sobrien 509990075Sobrien /* Be conservative and copy outval into scratch now, 510090075Sobrien this should only be necessary if outval is a 510190075Sobrien subreg of something larger than a word. */ 510290075Sobrien /* XXX Might this clobber base? I can't see how it 510390075Sobrien can, since scratch is known to overlap with 510490075Sobrien outval. */ 510590075Sobrien emit_insn (gen_movhi (scratch_hi, outval)); 510690075Sobrien outval = scratch_hi; 510790075Sobrien } 510890075Sobrien } 510990075Sobrien 511090075Sobrien /* Get the base address; addsi3 knows how to handle constants 511190075Sobrien that require more than one insn. */ 511290075Sobrien emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); 511390075Sobrien base = base_plus; 511490075Sobrien offset = lo; 511590075Sobrien } 511690075Sobrien } 511790075Sobrien 511890075Sobrien if (BYTES_BIG_ENDIAN) 511990075Sobrien { 512090075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, 512190075Sobrien plus_constant (base, offset + 1)), 512290075Sobrien gen_rtx_SUBREG (QImode, outval, 0))); 512390075Sobrien emit_insn (gen_lshrsi3 (scratch, 512490075Sobrien gen_rtx_SUBREG (SImode, outval, 0), 512590075Sobrien GEN_INT (8))); 512690075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), 512790075Sobrien gen_rtx_SUBREG (QImode, scratch, 0))); 512890075Sobrien } 512990075Sobrien else 513090075Sobrien { 513190075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), 513290075Sobrien gen_rtx_SUBREG (QImode, outval, 0))); 513390075Sobrien emit_insn (gen_lshrsi3 (scratch, 513490075Sobrien gen_rtx_SUBREG (SImode, outval, 0), 513590075Sobrien GEN_INT (8))); 513690075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, 513790075Sobrien plus_constant (base, offset + 1)), 513890075Sobrien gen_rtx_SUBREG (QImode, scratch, 0))); 513990075Sobrien } 514090075Sobrien} 514190075Sobrien 514290075Sobrien/* Print a symbolic form of X to the debug file, F. */ 514390075Sobrien 514490075Sobrienstatic void 514590075Sobrienarm_print_value (f, x) 514690075Sobrien FILE * f; 514790075Sobrien rtx x; 514890075Sobrien{ 514990075Sobrien switch (GET_CODE (x)) 515090075Sobrien { 515190075Sobrien case CONST_INT: 515290075Sobrien fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x)); 515390075Sobrien return; 515490075Sobrien 515590075Sobrien case CONST_DOUBLE: 515690075Sobrien fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3)); 515790075Sobrien return; 515890075Sobrien 515990075Sobrien case CONST_STRING: 516090075Sobrien fprintf (f, "\"%s\"", XSTR (x, 0)); 516190075Sobrien return; 516290075Sobrien 516390075Sobrien case SYMBOL_REF: 516490075Sobrien fprintf (f, "`%s'", XSTR (x, 0)); 516590075Sobrien return; 516690075Sobrien 516790075Sobrien case LABEL_REF: 516890075Sobrien fprintf (f, "L%d", INSN_UID (XEXP (x, 0))); 516990075Sobrien return; 517090075Sobrien 517190075Sobrien case CONST: 517290075Sobrien arm_print_value (f, XEXP (x, 0)); 517390075Sobrien return; 517490075Sobrien 517590075Sobrien case PLUS: 517690075Sobrien arm_print_value (f, XEXP (x, 0)); 517790075Sobrien fprintf (f, "+"); 517890075Sobrien arm_print_value (f, XEXP (x, 1)); 517990075Sobrien return; 518090075Sobrien 518190075Sobrien case PC: 518290075Sobrien fprintf (f, "pc"); 518390075Sobrien return; 518490075Sobrien 518590075Sobrien default: 518690075Sobrien fprintf (f, "????"); 518790075Sobrien return; 518890075Sobrien } 518990075Sobrien} 519090075Sobrien 519190075Sobrien/* Routines for manipulation of the constant pool. */ 519290075Sobrien 519390075Sobrien/* Arm instructions cannot load a large constant directly into a 519490075Sobrien register; they have to come from a pc relative load. The constant 519590075Sobrien must therefore be placed in the addressable range of the pc 519690075Sobrien relative load. Depending on the precise pc relative load 519790075Sobrien instruction the range is somewhere between 256 bytes and 4k. This 519890075Sobrien means that we often have to dump a constant inside a function, and 519990075Sobrien generate code to branch around it. 520090075Sobrien 520190075Sobrien It is important to minimize this, since the branches will slow 520290075Sobrien things down and make the code larger. 520390075Sobrien 520490075Sobrien Normally we can hide the table after an existing unconditional 520590075Sobrien branch so that there is no interruption of the flow, but in the 520690075Sobrien worst case the code looks like this: 520790075Sobrien 520890075Sobrien ldr rn, L1 520990075Sobrien ... 521090075Sobrien b L2 521190075Sobrien align 521290075Sobrien L1: .long value 521390075Sobrien L2: 521490075Sobrien ... 521590075Sobrien 521690075Sobrien ldr rn, L3 521790075Sobrien ... 521890075Sobrien b L4 521990075Sobrien align 522090075Sobrien L3: .long value 522190075Sobrien L4: 522290075Sobrien ... 522390075Sobrien 522490075Sobrien We fix this by performing a scan after scheduling, which notices 522590075Sobrien which instructions need to have their operands fetched from the 522690075Sobrien constant table and builds the table. 522790075Sobrien 522890075Sobrien The algorithm starts by building a table of all the constants that 522990075Sobrien need fixing up and all the natural barriers in the function (places 523090075Sobrien where a constant table can be dropped without breaking the flow). 523190075Sobrien For each fixup we note how far the pc-relative replacement will be 523290075Sobrien able to reach and the offset of the instruction into the function. 523390075Sobrien 523490075Sobrien Having built the table we then group the fixes together to form 523590075Sobrien tables that are as large as possible (subject to addressing 523690075Sobrien constraints) and emit each table of constants after the last 523790075Sobrien barrier that is within range of all the instructions in the group. 523890075Sobrien If a group does not contain a barrier, then we forcibly create one 523990075Sobrien by inserting a jump instruction into the flow. Once the table has 524090075Sobrien been inserted, the insns are then modified to reference the 524190075Sobrien relevant entry in the pool. 524290075Sobrien 524390075Sobrien Possible enhancements to the algorithm (not implemented) are: 524490075Sobrien 524590075Sobrien 1) For some processors and object formats, there may be benefit in 524690075Sobrien aligning the pools to the start of cache lines; this alignment 524790075Sobrien would need to be taken into account when calculating addressability 524890075Sobrien of a pool. */ 524990075Sobrien 525090075Sobrien/* These typedefs are located at the start of this file, so that 525190075Sobrien they can be used in the prototypes there. This comment is to 525290075Sobrien remind readers of that fact so that the following structures 525390075Sobrien can be understood more easily. 525490075Sobrien 525590075Sobrien typedef struct minipool_node Mnode; 525690075Sobrien typedef struct minipool_fixup Mfix; */ 525790075Sobrien 525890075Sobrienstruct minipool_node 525990075Sobrien{ 526090075Sobrien /* Doubly linked chain of entries. */ 526190075Sobrien Mnode * next; 526290075Sobrien Mnode * prev; 526390075Sobrien /* The maximum offset into the code that this entry can be placed. While 526490075Sobrien pushing fixes for forward references, all entries are sorted in order 526590075Sobrien of increasing max_address. */ 526690075Sobrien HOST_WIDE_INT max_address; 526790075Sobrien /* Similarly for an entry inserted for a backwards ref. */ 526890075Sobrien HOST_WIDE_INT min_address; 526990075Sobrien /* The number of fixes referencing this entry. This can become zero 527090075Sobrien if we "unpush" an entry. In this case we ignore the entry when we 527190075Sobrien come to emit the code. */ 527290075Sobrien int refcount; 527390075Sobrien /* The offset from the start of the minipool. */ 527490075Sobrien HOST_WIDE_INT offset; 527590075Sobrien /* The value in table. */ 527690075Sobrien rtx value; 527790075Sobrien /* The mode of value. */ 527890075Sobrien enum machine_mode mode; 527990075Sobrien int fix_size; 528090075Sobrien}; 528190075Sobrien 528290075Sobrienstruct minipool_fixup 528390075Sobrien{ 528490075Sobrien Mfix * next; 528590075Sobrien rtx insn; 528690075Sobrien HOST_WIDE_INT address; 528790075Sobrien rtx * loc; 528890075Sobrien enum machine_mode mode; 528990075Sobrien int fix_size; 529090075Sobrien rtx value; 529190075Sobrien Mnode * minipool; 529290075Sobrien HOST_WIDE_INT forwards; 529390075Sobrien HOST_WIDE_INT backwards; 529490075Sobrien}; 529590075Sobrien 529690075Sobrien/* Fixes less than a word need padding out to a word boundary. */ 529790075Sobrien#define MINIPOOL_FIX_SIZE(mode) \ 529890075Sobrien (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4) 529990075Sobrien 530090075Sobrienstatic Mnode * minipool_vector_head; 530190075Sobrienstatic Mnode * minipool_vector_tail; 530290075Sobrienstatic rtx minipool_vector_label; 530390075Sobrien 530490075Sobrien/* The linked list of all minipool fixes required for this function. */ 530590075SobrienMfix * minipool_fix_head; 530690075SobrienMfix * minipool_fix_tail; 530790075Sobrien/* The fix entry for the current minipool, once it has been placed. */ 530890075SobrienMfix * minipool_barrier; 530990075Sobrien 531090075Sobrien/* Determines if INSN is the start of a jump table. Returns the end 531190075Sobrien of the TABLE or NULL_RTX. */ 531290075Sobrien 531390075Sobrienstatic rtx 531490075Sobrienis_jump_table (insn) 531590075Sobrien rtx insn; 531690075Sobrien{ 531790075Sobrien rtx table; 531890075Sobrien 531990075Sobrien if (GET_CODE (insn) == JUMP_INSN 532090075Sobrien && JUMP_LABEL (insn) != NULL 532190075Sobrien && ((table = next_real_insn (JUMP_LABEL (insn))) 532290075Sobrien == next_real_insn (insn)) 532390075Sobrien && table != NULL 532490075Sobrien && GET_CODE (table) == JUMP_INSN 532590075Sobrien && (GET_CODE (PATTERN (table)) == ADDR_VEC 532690075Sobrien || GET_CODE (PATTERN (table)) == ADDR_DIFF_VEC)) 532790075Sobrien return table; 532890075Sobrien 532990075Sobrien return NULL_RTX; 533090075Sobrien} 533190075Sobrien 533296263Sobrien#ifndef JUMP_TABLES_IN_TEXT_SECTION 533396263Sobrien#define JUMP_TABLES_IN_TEXT_SECTION 0 533496263Sobrien#endif 533596263Sobrien 533690075Sobrienstatic HOST_WIDE_INT 533790075Sobrienget_jump_table_size (insn) 533890075Sobrien rtx insn; 533990075Sobrien{ 534096263Sobrien /* ADDR_VECs only take room if read-only data does into the text 534196263Sobrien section. */ 534296263Sobrien if (JUMP_TABLES_IN_TEXT_SECTION 534396263Sobrien#if !defined(READONLY_DATA_SECTION) 534496263Sobrien || 1 534596263Sobrien#endif 534696263Sobrien ) 534796263Sobrien { 534896263Sobrien rtx body = PATTERN (insn); 534996263Sobrien int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0; 535090075Sobrien 535196263Sobrien return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, elt); 535296263Sobrien } 535396263Sobrien 535496263Sobrien return 0; 535590075Sobrien} 535690075Sobrien 535790075Sobrien/* Move a minipool fix MP from its current location to before MAX_MP. 535890075Sobrien If MAX_MP is NULL, then MP doesn't need moving, but the addressing 535990075Sobrien contrains may need updating. */ 536090075Sobrien 536190075Sobrienstatic Mnode * 536290075Sobrienmove_minipool_fix_forward_ref (mp, max_mp, max_address) 536390075Sobrien Mnode * mp; 536490075Sobrien Mnode * max_mp; 536590075Sobrien HOST_WIDE_INT max_address; 536690075Sobrien{ 536790075Sobrien /* This should never be true and the code below assumes these are 536890075Sobrien different. */ 536990075Sobrien if (mp == max_mp) 537090075Sobrien abort (); 537190075Sobrien 537290075Sobrien if (max_mp == NULL) 537390075Sobrien { 537490075Sobrien if (max_address < mp->max_address) 537590075Sobrien mp->max_address = max_address; 537690075Sobrien } 537790075Sobrien else 537890075Sobrien { 537990075Sobrien if (max_address > max_mp->max_address - mp->fix_size) 538090075Sobrien mp->max_address = max_mp->max_address - mp->fix_size; 538190075Sobrien else 538290075Sobrien mp->max_address = max_address; 538390075Sobrien 538490075Sobrien /* Unlink MP from its current position. Since max_mp is non-null, 538590075Sobrien mp->prev must be non-null. */ 538690075Sobrien mp->prev->next = mp->next; 538790075Sobrien if (mp->next != NULL) 538890075Sobrien mp->next->prev = mp->prev; 538990075Sobrien else 539090075Sobrien minipool_vector_tail = mp->prev; 539190075Sobrien 539290075Sobrien /* Re-insert it before MAX_MP. */ 539390075Sobrien mp->next = max_mp; 539490075Sobrien mp->prev = max_mp->prev; 539590075Sobrien max_mp->prev = mp; 539690075Sobrien 539790075Sobrien if (mp->prev != NULL) 539890075Sobrien mp->prev->next = mp; 539990075Sobrien else 540090075Sobrien minipool_vector_head = mp; 540190075Sobrien } 540290075Sobrien 540390075Sobrien /* Save the new entry. */ 540490075Sobrien max_mp = mp; 540590075Sobrien 540690075Sobrien /* Scan over the preceding entries and adjust their addresses as 540790075Sobrien required. */ 540890075Sobrien while (mp->prev != NULL 540990075Sobrien && mp->prev->max_address > mp->max_address - mp->prev->fix_size) 541090075Sobrien { 541190075Sobrien mp->prev->max_address = mp->max_address - mp->prev->fix_size; 541290075Sobrien mp = mp->prev; 541390075Sobrien } 541490075Sobrien 541590075Sobrien return max_mp; 541690075Sobrien} 541790075Sobrien 541890075Sobrien/* Add a constant to the minipool for a forward reference. Returns the 541990075Sobrien node added or NULL if the constant will not fit in this pool. */ 542090075Sobrien 542190075Sobrienstatic Mnode * 542290075Sobrienadd_minipool_forward_ref (fix) 542390075Sobrien Mfix * fix; 542490075Sobrien{ 542590075Sobrien /* If set, max_mp is the first pool_entry that has a lower 542690075Sobrien constraint than the one we are trying to add. */ 542790075Sobrien Mnode * max_mp = NULL; 542890075Sobrien HOST_WIDE_INT max_address = fix->address + fix->forwards; 542990075Sobrien Mnode * mp; 543090075Sobrien 543190075Sobrien /* If this fix's address is greater than the address of the first 543290075Sobrien entry, then we can't put the fix in this pool. We subtract the 543390075Sobrien size of the current fix to ensure that if the table is fully 543490075Sobrien packed we still have enough room to insert this value by suffling 543590075Sobrien the other fixes forwards. */ 543690075Sobrien if (minipool_vector_head && 543790075Sobrien fix->address >= minipool_vector_head->max_address - fix->fix_size) 543890075Sobrien return NULL; 543990075Sobrien 544090075Sobrien /* Scan the pool to see if a constant with the same value has 544190075Sobrien already been added. While we are doing this, also note the 544290075Sobrien location where we must insert the constant if it doesn't already 544390075Sobrien exist. */ 544490075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 544590075Sobrien { 544690075Sobrien if (GET_CODE (fix->value) == GET_CODE (mp->value) 544790075Sobrien && fix->mode == mp->mode 544890075Sobrien && (GET_CODE (fix->value) != CODE_LABEL 544990075Sobrien || (CODE_LABEL_NUMBER (fix->value) 545090075Sobrien == CODE_LABEL_NUMBER (mp->value))) 545190075Sobrien && rtx_equal_p (fix->value, mp->value)) 545290075Sobrien { 545390075Sobrien /* More than one fix references this entry. */ 545490075Sobrien mp->refcount++; 545590075Sobrien return move_minipool_fix_forward_ref (mp, max_mp, max_address); 545690075Sobrien } 545790075Sobrien 545890075Sobrien /* Note the insertion point if necessary. */ 545990075Sobrien if (max_mp == NULL 546090075Sobrien && mp->max_address > max_address) 546190075Sobrien max_mp = mp; 546290075Sobrien } 546390075Sobrien 546490075Sobrien /* The value is not currently in the minipool, so we need to create 546590075Sobrien a new entry for it. If MAX_MP is NULL, the entry will be put on 546690075Sobrien the end of the list since the placement is less constrained than 546790075Sobrien any existing entry. Otherwise, we insert the new fix before 546890075Sobrien MAX_MP and, if neceesary, adjust the constraints on the other 546990075Sobrien entries. */ 547090075Sobrien mp = xmalloc (sizeof (* mp)); 547190075Sobrien mp->fix_size = fix->fix_size; 547290075Sobrien mp->mode = fix->mode; 547390075Sobrien mp->value = fix->value; 547490075Sobrien mp->refcount = 1; 547590075Sobrien /* Not yet required for a backwards ref. */ 547690075Sobrien mp->min_address = -65536; 547790075Sobrien 547890075Sobrien if (max_mp == NULL) 547990075Sobrien { 548090075Sobrien mp->max_address = max_address; 548190075Sobrien mp->next = NULL; 548290075Sobrien mp->prev = minipool_vector_tail; 548390075Sobrien 548490075Sobrien if (mp->prev == NULL) 548590075Sobrien { 548690075Sobrien minipool_vector_head = mp; 548790075Sobrien minipool_vector_label = gen_label_rtx (); 548890075Sobrien } 548990075Sobrien else 549090075Sobrien mp->prev->next = mp; 549190075Sobrien 549290075Sobrien minipool_vector_tail = mp; 549390075Sobrien } 549490075Sobrien else 549590075Sobrien { 549690075Sobrien if (max_address > max_mp->max_address - mp->fix_size) 549790075Sobrien mp->max_address = max_mp->max_address - mp->fix_size; 549890075Sobrien else 549990075Sobrien mp->max_address = max_address; 550090075Sobrien 550190075Sobrien mp->next = max_mp; 550290075Sobrien mp->prev = max_mp->prev; 550390075Sobrien max_mp->prev = mp; 550490075Sobrien if (mp->prev != NULL) 550590075Sobrien mp->prev->next = mp; 550690075Sobrien else 550790075Sobrien minipool_vector_head = mp; 550890075Sobrien } 550990075Sobrien 551090075Sobrien /* Save the new entry. */ 551190075Sobrien max_mp = mp; 551290075Sobrien 551390075Sobrien /* Scan over the preceding entries and adjust their addresses as 551490075Sobrien required. */ 551590075Sobrien while (mp->prev != NULL 551690075Sobrien && mp->prev->max_address > mp->max_address - mp->prev->fix_size) 551790075Sobrien { 551890075Sobrien mp->prev->max_address = mp->max_address - mp->prev->fix_size; 551990075Sobrien mp = mp->prev; 552090075Sobrien } 552190075Sobrien 552290075Sobrien return max_mp; 552390075Sobrien} 552490075Sobrien 552590075Sobrienstatic Mnode * 552690075Sobrienmove_minipool_fix_backward_ref (mp, min_mp, min_address) 552790075Sobrien Mnode * mp; 552890075Sobrien Mnode * min_mp; 552990075Sobrien HOST_WIDE_INT min_address; 553090075Sobrien{ 553190075Sobrien HOST_WIDE_INT offset; 553290075Sobrien 553390075Sobrien /* This should never be true, and the code below assumes these are 553490075Sobrien different. */ 553590075Sobrien if (mp == min_mp) 553690075Sobrien abort (); 553790075Sobrien 553890075Sobrien if (min_mp == NULL) 553990075Sobrien { 554090075Sobrien if (min_address > mp->min_address) 554190075Sobrien mp->min_address = min_address; 554290075Sobrien } 554390075Sobrien else 554490075Sobrien { 554590075Sobrien /* We will adjust this below if it is too loose. */ 554690075Sobrien mp->min_address = min_address; 554790075Sobrien 554890075Sobrien /* Unlink MP from its current position. Since min_mp is non-null, 554990075Sobrien mp->next must be non-null. */ 555090075Sobrien mp->next->prev = mp->prev; 555190075Sobrien if (mp->prev != NULL) 555290075Sobrien mp->prev->next = mp->next; 555390075Sobrien else 555490075Sobrien minipool_vector_head = mp->next; 555590075Sobrien 555690075Sobrien /* Reinsert it after MIN_MP. */ 555790075Sobrien mp->prev = min_mp; 555890075Sobrien mp->next = min_mp->next; 555990075Sobrien min_mp->next = mp; 556090075Sobrien if (mp->next != NULL) 556190075Sobrien mp->next->prev = mp; 556290075Sobrien else 556390075Sobrien minipool_vector_tail = mp; 556490075Sobrien } 556590075Sobrien 556690075Sobrien min_mp = mp; 556790075Sobrien 556890075Sobrien offset = 0; 556990075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 557090075Sobrien { 557190075Sobrien mp->offset = offset; 557290075Sobrien if (mp->refcount > 0) 557390075Sobrien offset += mp->fix_size; 557490075Sobrien 557590075Sobrien if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size) 557690075Sobrien mp->next->min_address = mp->min_address + mp->fix_size; 557790075Sobrien } 557890075Sobrien 557990075Sobrien return min_mp; 558090075Sobrien} 558190075Sobrien 558290075Sobrien/* Add a constant to the minipool for a backward reference. Returns the 558390075Sobrien node added or NULL if the constant will not fit in this pool. 558490075Sobrien 558590075Sobrien Note that the code for insertion for a backwards reference can be 558690075Sobrien somewhat confusing because the calculated offsets for each fix do 558790075Sobrien not take into account the size of the pool (which is still under 558890075Sobrien construction. */ 558990075Sobrien 559090075Sobrienstatic Mnode * 559190075Sobrienadd_minipool_backward_ref (fix) 559290075Sobrien Mfix * fix; 559390075Sobrien{ 559490075Sobrien /* If set, min_mp is the last pool_entry that has a lower constraint 559590075Sobrien than the one we are trying to add. */ 559690075Sobrien Mnode * min_mp = NULL; 559790075Sobrien /* This can be negative, since it is only a constraint. */ 559890075Sobrien HOST_WIDE_INT min_address = fix->address - fix->backwards; 559990075Sobrien Mnode * mp; 560090075Sobrien 560190075Sobrien /* If we can't reach the current pool from this insn, or if we can't 560290075Sobrien insert this entry at the end of the pool without pushing other 560390075Sobrien fixes out of range, then we don't try. This ensures that we 560490075Sobrien can't fail later on. */ 560590075Sobrien if (min_address >= minipool_barrier->address 560690075Sobrien || (minipool_vector_tail->min_address + fix->fix_size 560790075Sobrien >= minipool_barrier->address)) 560890075Sobrien return NULL; 560990075Sobrien 561090075Sobrien /* Scan the pool to see if a constant with the same value has 561190075Sobrien already been added. While we are doing this, also note the 561290075Sobrien location where we must insert the constant if it doesn't already 561390075Sobrien exist. */ 561490075Sobrien for (mp = minipool_vector_tail; mp != NULL; mp = mp->prev) 561590075Sobrien { 561690075Sobrien if (GET_CODE (fix->value) == GET_CODE (mp->value) 561790075Sobrien && fix->mode == mp->mode 561890075Sobrien && (GET_CODE (fix->value) != CODE_LABEL 561990075Sobrien || (CODE_LABEL_NUMBER (fix->value) 562090075Sobrien == CODE_LABEL_NUMBER (mp->value))) 562190075Sobrien && rtx_equal_p (fix->value, mp->value) 562290075Sobrien /* Check that there is enough slack to move this entry to the 562390075Sobrien end of the table (this is conservative). */ 562490075Sobrien && (mp->max_address 562590075Sobrien > (minipool_barrier->address 562690075Sobrien + minipool_vector_tail->offset 562790075Sobrien + minipool_vector_tail->fix_size))) 562890075Sobrien { 562990075Sobrien mp->refcount++; 563090075Sobrien return move_minipool_fix_backward_ref (mp, min_mp, min_address); 563190075Sobrien } 563290075Sobrien 563390075Sobrien if (min_mp != NULL) 563490075Sobrien mp->min_address += fix->fix_size; 563590075Sobrien else 563690075Sobrien { 563790075Sobrien /* Note the insertion point if necessary. */ 563890075Sobrien if (mp->min_address < min_address) 563990075Sobrien min_mp = mp; 564090075Sobrien else if (mp->max_address 564190075Sobrien < minipool_barrier->address + mp->offset + fix->fix_size) 564290075Sobrien { 564390075Sobrien /* Inserting before this entry would push the fix beyond 564490075Sobrien its maximum address (which can happen if we have 564590075Sobrien re-located a forwards fix); force the new fix to come 564690075Sobrien after it. */ 564790075Sobrien min_mp = mp; 564890075Sobrien min_address = mp->min_address + fix->fix_size; 564990075Sobrien } 565090075Sobrien } 565190075Sobrien } 565290075Sobrien 565390075Sobrien /* We need to create a new entry. */ 565490075Sobrien mp = xmalloc (sizeof (* mp)); 565590075Sobrien mp->fix_size = fix->fix_size; 565690075Sobrien mp->mode = fix->mode; 565790075Sobrien mp->value = fix->value; 565890075Sobrien mp->refcount = 1; 565990075Sobrien mp->max_address = minipool_barrier->address + 65536; 566090075Sobrien 566190075Sobrien mp->min_address = min_address; 566290075Sobrien 566390075Sobrien if (min_mp == NULL) 566490075Sobrien { 566590075Sobrien mp->prev = NULL; 566690075Sobrien mp->next = minipool_vector_head; 566790075Sobrien 566890075Sobrien if (mp->next == NULL) 566990075Sobrien { 567090075Sobrien minipool_vector_tail = mp; 567190075Sobrien minipool_vector_label = gen_label_rtx (); 567290075Sobrien } 567390075Sobrien else 567490075Sobrien mp->next->prev = mp; 567590075Sobrien 567690075Sobrien minipool_vector_head = mp; 567790075Sobrien } 567890075Sobrien else 567990075Sobrien { 568090075Sobrien mp->next = min_mp->next; 568190075Sobrien mp->prev = min_mp; 568290075Sobrien min_mp->next = mp; 568390075Sobrien 568490075Sobrien if (mp->next != NULL) 568590075Sobrien mp->next->prev = mp; 568690075Sobrien else 568790075Sobrien minipool_vector_tail = mp; 568890075Sobrien } 568990075Sobrien 569090075Sobrien /* Save the new entry. */ 569190075Sobrien min_mp = mp; 569290075Sobrien 569390075Sobrien if (mp->prev) 569490075Sobrien mp = mp->prev; 569590075Sobrien else 569690075Sobrien mp->offset = 0; 569790075Sobrien 569890075Sobrien /* Scan over the following entries and adjust their offsets. */ 569990075Sobrien while (mp->next != NULL) 570090075Sobrien { 570190075Sobrien if (mp->next->min_address < mp->min_address + mp->fix_size) 570290075Sobrien mp->next->min_address = mp->min_address + mp->fix_size; 570390075Sobrien 570490075Sobrien if (mp->refcount) 570590075Sobrien mp->next->offset = mp->offset + mp->fix_size; 570690075Sobrien else 570790075Sobrien mp->next->offset = mp->offset; 570890075Sobrien 570990075Sobrien mp = mp->next; 571090075Sobrien } 571190075Sobrien 571290075Sobrien return min_mp; 571390075Sobrien} 571490075Sobrien 571590075Sobrienstatic void 571690075Sobrienassign_minipool_offsets (barrier) 571790075Sobrien Mfix * barrier; 571890075Sobrien{ 571990075Sobrien HOST_WIDE_INT offset = 0; 572090075Sobrien Mnode * mp; 572190075Sobrien 572290075Sobrien minipool_barrier = barrier; 572390075Sobrien 572490075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 572590075Sobrien { 572690075Sobrien mp->offset = offset; 572790075Sobrien 572890075Sobrien if (mp->refcount > 0) 572990075Sobrien offset += mp->fix_size; 573090075Sobrien } 573190075Sobrien} 573290075Sobrien 573390075Sobrien/* Output the literal table */ 573490075Sobrienstatic void 573590075Sobriendump_minipool (scan) 573690075Sobrien rtx scan; 573790075Sobrien{ 573890075Sobrien Mnode * mp; 573990075Sobrien Mnode * nmp; 574090075Sobrien 574190075Sobrien if (rtl_dump_file) 574290075Sobrien fprintf (rtl_dump_file, 574390075Sobrien ";; Emitting minipool after insn %u; address %ld\n", 574490075Sobrien INSN_UID (scan), (unsigned long) minipool_barrier->address); 574590075Sobrien 574690075Sobrien scan = emit_label_after (gen_label_rtx (), scan); 574790075Sobrien scan = emit_insn_after (gen_align_4 (), scan); 574890075Sobrien scan = emit_label_after (minipool_vector_label, scan); 574990075Sobrien 575090075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = nmp) 575190075Sobrien { 575290075Sobrien if (mp->refcount > 0) 575390075Sobrien { 575490075Sobrien if (rtl_dump_file) 575590075Sobrien { 575690075Sobrien fprintf (rtl_dump_file, 575790075Sobrien ";; Offset %u, min %ld, max %ld ", 575890075Sobrien (unsigned) mp->offset, (unsigned long) mp->min_address, 575990075Sobrien (unsigned long) mp->max_address); 576090075Sobrien arm_print_value (rtl_dump_file, mp->value); 576190075Sobrien fputc ('\n', rtl_dump_file); 576290075Sobrien } 576390075Sobrien 576490075Sobrien switch (mp->fix_size) 576590075Sobrien { 576690075Sobrien#ifdef HAVE_consttable_1 576790075Sobrien case 1: 576890075Sobrien scan = emit_insn_after (gen_consttable_1 (mp->value), scan); 576990075Sobrien break; 577090075Sobrien 577190075Sobrien#endif 577290075Sobrien#ifdef HAVE_consttable_2 577390075Sobrien case 2: 577490075Sobrien scan = emit_insn_after (gen_consttable_2 (mp->value), scan); 577590075Sobrien break; 577690075Sobrien 577790075Sobrien#endif 577890075Sobrien#ifdef HAVE_consttable_4 577990075Sobrien case 4: 578090075Sobrien scan = emit_insn_after (gen_consttable_4 (mp->value), scan); 578190075Sobrien break; 578290075Sobrien 578390075Sobrien#endif 578490075Sobrien#ifdef HAVE_consttable_8 578590075Sobrien case 8: 578690075Sobrien scan = emit_insn_after (gen_consttable_8 (mp->value), scan); 578790075Sobrien break; 578890075Sobrien 578990075Sobrien#endif 579090075Sobrien default: 579190075Sobrien abort (); 579290075Sobrien break; 579390075Sobrien } 579490075Sobrien } 579590075Sobrien 579690075Sobrien nmp = mp->next; 579790075Sobrien free (mp); 579890075Sobrien } 579990075Sobrien 580090075Sobrien minipool_vector_head = minipool_vector_tail = NULL; 580190075Sobrien scan = emit_insn_after (gen_consttable_end (), scan); 580290075Sobrien scan = emit_barrier_after (scan); 580390075Sobrien} 580490075Sobrien 580590075Sobrien/* Return the cost of forcibly inserting a barrier after INSN. */ 580690075Sobrien 580790075Sobrienstatic int 580890075Sobrienarm_barrier_cost (insn) 580990075Sobrien rtx insn; 581090075Sobrien{ 581190075Sobrien /* Basing the location of the pool on the loop depth is preferable, 581290075Sobrien but at the moment, the basic block information seems to be 581390075Sobrien corrupt by this stage of the compilation. */ 581490075Sobrien int base_cost = 50; 581590075Sobrien rtx next = next_nonnote_insn (insn); 581690075Sobrien 581790075Sobrien if (next != NULL && GET_CODE (next) == CODE_LABEL) 581890075Sobrien base_cost -= 20; 581990075Sobrien 582090075Sobrien switch (GET_CODE (insn)) 582190075Sobrien { 582290075Sobrien case CODE_LABEL: 582390075Sobrien /* It will always be better to place the table before the label, rather 582490075Sobrien than after it. */ 582590075Sobrien return 50; 582690075Sobrien 582790075Sobrien case INSN: 582890075Sobrien case CALL_INSN: 582990075Sobrien return base_cost; 583090075Sobrien 583190075Sobrien case JUMP_INSN: 583290075Sobrien return base_cost - 10; 583390075Sobrien 583490075Sobrien default: 583590075Sobrien return base_cost + 10; 583690075Sobrien } 583790075Sobrien} 583890075Sobrien 583990075Sobrien/* Find the best place in the insn stream in the range 584090075Sobrien (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier. 584190075Sobrien Create the barrier by inserting a jump and add a new fix entry for 584290075Sobrien it. */ 584390075Sobrien 584490075Sobrienstatic Mfix * 584590075Sobriencreate_fix_barrier (fix, max_address) 584690075Sobrien Mfix * fix; 584790075Sobrien HOST_WIDE_INT max_address; 584890075Sobrien{ 584990075Sobrien HOST_WIDE_INT count = 0; 585090075Sobrien rtx barrier; 585190075Sobrien rtx from = fix->insn; 585290075Sobrien rtx selected = from; 585390075Sobrien int selected_cost; 585490075Sobrien HOST_WIDE_INT selected_address; 585590075Sobrien Mfix * new_fix; 585690075Sobrien HOST_WIDE_INT max_count = max_address - fix->address; 585790075Sobrien rtx label = gen_label_rtx (); 585890075Sobrien 585990075Sobrien selected_cost = arm_barrier_cost (from); 586090075Sobrien selected_address = fix->address; 586190075Sobrien 586290075Sobrien while (from && count < max_count) 586390075Sobrien { 586490075Sobrien rtx tmp; 586590075Sobrien int new_cost; 586690075Sobrien 586790075Sobrien /* This code shouldn't have been called if there was a natural barrier 586890075Sobrien within range. */ 586990075Sobrien if (GET_CODE (from) == BARRIER) 587090075Sobrien abort (); 587190075Sobrien 587290075Sobrien /* Count the length of this insn. */ 587390075Sobrien count += get_attr_length (from); 587490075Sobrien 587590075Sobrien /* If there is a jump table, add its length. */ 587690075Sobrien tmp = is_jump_table (from); 587790075Sobrien if (tmp != NULL) 587890075Sobrien { 587990075Sobrien count += get_jump_table_size (tmp); 588090075Sobrien 588190075Sobrien /* Jump tables aren't in a basic block, so base the cost on 588290075Sobrien the dispatch insn. If we select this location, we will 588390075Sobrien still put the pool after the table. */ 588490075Sobrien new_cost = arm_barrier_cost (from); 588590075Sobrien 588690075Sobrien if (count < max_count && new_cost <= selected_cost) 588790075Sobrien { 588890075Sobrien selected = tmp; 588990075Sobrien selected_cost = new_cost; 589090075Sobrien selected_address = fix->address + count; 589190075Sobrien } 589290075Sobrien 589390075Sobrien /* Continue after the dispatch table. */ 589490075Sobrien from = NEXT_INSN (tmp); 589590075Sobrien continue; 589690075Sobrien } 589790075Sobrien 589890075Sobrien new_cost = arm_barrier_cost (from); 589990075Sobrien 590090075Sobrien if (count < max_count && new_cost <= selected_cost) 590190075Sobrien { 590290075Sobrien selected = from; 590390075Sobrien selected_cost = new_cost; 590490075Sobrien selected_address = fix->address + count; 590590075Sobrien } 590690075Sobrien 590790075Sobrien from = NEXT_INSN (from); 590890075Sobrien } 590990075Sobrien 591090075Sobrien /* Create a new JUMP_INSN that branches around a barrier. */ 591190075Sobrien from = emit_jump_insn_after (gen_jump (label), selected); 591290075Sobrien JUMP_LABEL (from) = label; 591390075Sobrien barrier = emit_barrier_after (from); 591490075Sobrien emit_label_after (label, barrier); 591590075Sobrien 591690075Sobrien /* Create a minipool barrier entry for the new barrier. */ 591790075Sobrien new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix)); 591890075Sobrien new_fix->insn = barrier; 591990075Sobrien new_fix->address = selected_address; 592090075Sobrien new_fix->next = fix->next; 592190075Sobrien fix->next = new_fix; 592290075Sobrien 592390075Sobrien return new_fix; 592490075Sobrien} 592590075Sobrien 592690075Sobrien/* Record that there is a natural barrier in the insn stream at 592790075Sobrien ADDRESS. */ 592890075Sobrienstatic void 592990075Sobrienpush_minipool_barrier (insn, address) 593090075Sobrien rtx insn; 593190075Sobrien HOST_WIDE_INT address; 593290075Sobrien{ 593390075Sobrien Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); 593490075Sobrien 593590075Sobrien fix->insn = insn; 593690075Sobrien fix->address = address; 593790075Sobrien 593890075Sobrien fix->next = NULL; 593990075Sobrien if (minipool_fix_head != NULL) 594090075Sobrien minipool_fix_tail->next = fix; 594190075Sobrien else 594290075Sobrien minipool_fix_head = fix; 594390075Sobrien 594490075Sobrien minipool_fix_tail = fix; 594590075Sobrien} 594690075Sobrien 594790075Sobrien/* Record INSN, which will need fixing up to load a value from the 594890075Sobrien minipool. ADDRESS is the offset of the insn since the start of the 594990075Sobrien function; LOC is a pointer to the part of the insn which requires 595090075Sobrien fixing; VALUE is the constant that must be loaded, which is of type 595190075Sobrien MODE. */ 595290075Sobrienstatic void 595390075Sobrienpush_minipool_fix (insn, address, loc, mode, value) 595490075Sobrien rtx insn; 595590075Sobrien HOST_WIDE_INT address; 595690075Sobrien rtx * loc; 595790075Sobrien enum machine_mode mode; 595890075Sobrien rtx value; 595990075Sobrien{ 596090075Sobrien Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); 596190075Sobrien 596290075Sobrien#ifdef AOF_ASSEMBLER 596390075Sobrien /* PIC symbol refereneces need to be converted into offsets into the 596490075Sobrien based area. */ 596590075Sobrien /* XXX This shouldn't be done here. */ 596690075Sobrien if (flag_pic && GET_CODE (value) == SYMBOL_REF) 596790075Sobrien value = aof_pic_entry (value); 596890075Sobrien#endif /* AOF_ASSEMBLER */ 596990075Sobrien 597090075Sobrien fix->insn = insn; 597190075Sobrien fix->address = address; 597290075Sobrien fix->loc = loc; 597390075Sobrien fix->mode = mode; 597490075Sobrien fix->fix_size = MINIPOOL_FIX_SIZE (mode); 597590075Sobrien fix->value = value; 597690075Sobrien fix->forwards = get_attr_pool_range (insn); 597790075Sobrien fix->backwards = get_attr_neg_pool_range (insn); 597890075Sobrien fix->minipool = NULL; 597990075Sobrien 598090075Sobrien /* If an insn doesn't have a range defined for it, then it isn't 598190075Sobrien expecting to be reworked by this code. Better to abort now than 598290075Sobrien to generate duff assembly code. */ 598390075Sobrien if (fix->forwards == 0 && fix->backwards == 0) 598490075Sobrien abort (); 598590075Sobrien 598690075Sobrien if (rtl_dump_file) 598790075Sobrien { 598890075Sobrien fprintf (rtl_dump_file, 598990075Sobrien ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ", 599090075Sobrien GET_MODE_NAME (mode), 599190075Sobrien INSN_UID (insn), (unsigned long) address, 599290075Sobrien -1 * (long)fix->backwards, (long)fix->forwards); 599390075Sobrien arm_print_value (rtl_dump_file, fix->value); 599490075Sobrien fprintf (rtl_dump_file, "\n"); 599590075Sobrien } 599690075Sobrien 599790075Sobrien /* Add it to the chain of fixes. */ 599890075Sobrien fix->next = NULL; 599990075Sobrien 600090075Sobrien if (minipool_fix_head != NULL) 600190075Sobrien minipool_fix_tail->next = fix; 600290075Sobrien else 600390075Sobrien minipool_fix_head = fix; 600490075Sobrien 600590075Sobrien minipool_fix_tail = fix; 600690075Sobrien} 600790075Sobrien 600890075Sobrien/* Scan INSN and note any of its operands that need fixing. */ 600990075Sobrien 601090075Sobrienstatic void 601190075Sobriennote_invalid_constants (insn, address) 601290075Sobrien rtx insn; 601390075Sobrien HOST_WIDE_INT address; 601490075Sobrien{ 601590075Sobrien int opno; 601690075Sobrien 601790075Sobrien extract_insn (insn); 601890075Sobrien 601990075Sobrien if (!constrain_operands (1)) 602090075Sobrien fatal_insn_not_found (insn); 602190075Sobrien 602290075Sobrien /* Fill in recog_op_alt with information about the constraints of this 602390075Sobrien insn. */ 602490075Sobrien preprocess_constraints (); 602590075Sobrien 602690075Sobrien for (opno = 0; opno < recog_data.n_operands; opno++) 602790075Sobrien { 602890075Sobrien /* Things we need to fix can only occur in inputs. */ 602990075Sobrien if (recog_data.operand_type[opno] != OP_IN) 603090075Sobrien continue; 603190075Sobrien 603290075Sobrien /* If this alternative is a memory reference, then any mention 603390075Sobrien of constants in this alternative is really to fool reload 603490075Sobrien into allowing us to accept one there. We need to fix them up 603590075Sobrien now so that we output the right code. */ 603690075Sobrien if (recog_op_alt[opno][which_alternative].memory_ok) 603790075Sobrien { 603890075Sobrien rtx op = recog_data.operand[opno]; 603990075Sobrien 604090075Sobrien if (CONSTANT_P (op)) 604190075Sobrien push_minipool_fix (insn, address, recog_data.operand_loc[opno], 604290075Sobrien recog_data.operand_mode[opno], op); 604390075Sobrien#if 0 604490075Sobrien /* RWE: Now we look correctly at the operands for the insn, 604590075Sobrien this shouldn't be needed any more. */ 604690075Sobrien#ifndef AOF_ASSEMBLER 604790075Sobrien /* XXX Is this still needed? */ 604890075Sobrien else if (GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_PIC_SYM) 604990075Sobrien push_minipool_fix (insn, address, recog_data.operand_loc[opno], 605090075Sobrien recog_data.operand_mode[opno], 605190075Sobrien XVECEXP (op, 0, 0)); 605290075Sobrien#endif 605390075Sobrien#endif 605490075Sobrien else if (GET_CODE (op) == MEM 605590075Sobrien && GET_CODE (XEXP (op, 0)) == SYMBOL_REF 605690075Sobrien && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0))) 605790075Sobrien push_minipool_fix (insn, address, recog_data.operand_loc[opno], 605890075Sobrien recog_data.operand_mode[opno], 605990075Sobrien get_pool_constant (XEXP (op, 0))); 606090075Sobrien } 606190075Sobrien } 606290075Sobrien} 606390075Sobrien 606490075Sobrienvoid 606590075Sobrienarm_reorg (first) 606690075Sobrien rtx first; 606790075Sobrien{ 606890075Sobrien rtx insn; 606990075Sobrien HOST_WIDE_INT address = 0; 607090075Sobrien Mfix * fix; 607190075Sobrien 607290075Sobrien minipool_fix_head = minipool_fix_tail = NULL; 607390075Sobrien 607490075Sobrien /* The first insn must always be a note, or the code below won't 607590075Sobrien scan it properly. */ 607690075Sobrien if (GET_CODE (first) != NOTE) 607790075Sobrien abort (); 607890075Sobrien 607990075Sobrien /* Scan all the insns and record the operands that will need fixing. */ 608090075Sobrien for (insn = next_nonnote_insn (first); insn; insn = next_nonnote_insn (insn)) 608190075Sobrien { 608290075Sobrien if (GET_CODE (insn) == BARRIER) 608390075Sobrien push_minipool_barrier (insn, address); 608490075Sobrien else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN 608590075Sobrien || GET_CODE (insn) == JUMP_INSN) 608690075Sobrien { 608790075Sobrien rtx table; 608890075Sobrien 608990075Sobrien note_invalid_constants (insn, address); 609090075Sobrien address += get_attr_length (insn); 609190075Sobrien 609290075Sobrien /* If the insn is a vector jump, add the size of the table 609390075Sobrien and skip the table. */ 609490075Sobrien if ((table = is_jump_table (insn)) != NULL) 609590075Sobrien { 609690075Sobrien address += get_jump_table_size (table); 609790075Sobrien insn = table; 609890075Sobrien } 609990075Sobrien } 610090075Sobrien } 610190075Sobrien 610290075Sobrien fix = minipool_fix_head; 610390075Sobrien 610490075Sobrien /* Now scan the fixups and perform the required changes. */ 610590075Sobrien while (fix) 610690075Sobrien { 610790075Sobrien Mfix * ftmp; 610890075Sobrien Mfix * fdel; 610990075Sobrien Mfix * last_added_fix; 611090075Sobrien Mfix * last_barrier = NULL; 611190075Sobrien Mfix * this_fix; 611290075Sobrien 611390075Sobrien /* Skip any further barriers before the next fix. */ 611490075Sobrien while (fix && GET_CODE (fix->insn) == BARRIER) 611590075Sobrien fix = fix->next; 611690075Sobrien 611790075Sobrien /* No more fixes. */ 611890075Sobrien if (fix == NULL) 611990075Sobrien break; 612090075Sobrien 612190075Sobrien last_added_fix = NULL; 612290075Sobrien 612390075Sobrien for (ftmp = fix; ftmp; ftmp = ftmp->next) 612490075Sobrien { 612590075Sobrien if (GET_CODE (ftmp->insn) == BARRIER) 612690075Sobrien { 612790075Sobrien if (ftmp->address >= minipool_vector_head->max_address) 612890075Sobrien break; 612990075Sobrien 613090075Sobrien last_barrier = ftmp; 613190075Sobrien } 613290075Sobrien else if ((ftmp->minipool = add_minipool_forward_ref (ftmp)) == NULL) 613390075Sobrien break; 613490075Sobrien 613590075Sobrien last_added_fix = ftmp; /* Keep track of the last fix added. */ 613690075Sobrien } 613790075Sobrien 613890075Sobrien /* If we found a barrier, drop back to that; any fixes that we 613990075Sobrien could have reached but come after the barrier will now go in 614090075Sobrien the next mini-pool. */ 614190075Sobrien if (last_barrier != NULL) 614290075Sobrien { 614390075Sobrien /* Reduce the refcount for those fixes that won't go into this 614490075Sobrien pool after all. */ 614590075Sobrien for (fdel = last_barrier->next; 614690075Sobrien fdel && fdel != ftmp; 614790075Sobrien fdel = fdel->next) 614890075Sobrien { 614990075Sobrien fdel->minipool->refcount--; 615090075Sobrien fdel->minipool = NULL; 615190075Sobrien } 615290075Sobrien 615390075Sobrien ftmp = last_barrier; 615490075Sobrien } 615590075Sobrien else 615690075Sobrien { 615790075Sobrien /* ftmp is first fix that we can't fit into this pool and 615890075Sobrien there no natural barriers that we could use. Insert a 615990075Sobrien new barrier in the code somewhere between the previous 616090075Sobrien fix and this one, and arrange to jump around it. */ 616190075Sobrien HOST_WIDE_INT max_address; 616290075Sobrien 616390075Sobrien /* The last item on the list of fixes must be a barrier, so 616490075Sobrien we can never run off the end of the list of fixes without 616590075Sobrien last_barrier being set. */ 616690075Sobrien if (ftmp == NULL) 616790075Sobrien abort (); 616890075Sobrien 616990075Sobrien max_address = minipool_vector_head->max_address; 617090075Sobrien /* Check that there isn't another fix that is in range that 617190075Sobrien we couldn't fit into this pool because the pool was 617290075Sobrien already too large: we need to put the pool before such an 617390075Sobrien instruction. */ 617490075Sobrien if (ftmp->address < max_address) 617590075Sobrien max_address = ftmp->address; 617690075Sobrien 617790075Sobrien last_barrier = create_fix_barrier (last_added_fix, max_address); 617890075Sobrien } 617990075Sobrien 618090075Sobrien assign_minipool_offsets (last_barrier); 618190075Sobrien 618290075Sobrien while (ftmp) 618390075Sobrien { 618490075Sobrien if (GET_CODE (ftmp->insn) != BARRIER 618590075Sobrien && ((ftmp->minipool = add_minipool_backward_ref (ftmp)) 618690075Sobrien == NULL)) 618790075Sobrien break; 618890075Sobrien 618990075Sobrien ftmp = ftmp->next; 619090075Sobrien } 619190075Sobrien 619290075Sobrien /* Scan over the fixes we have identified for this pool, fixing them 619390075Sobrien up and adding the constants to the pool itself. */ 619490075Sobrien for (this_fix = fix; this_fix && ftmp != this_fix; 619590075Sobrien this_fix = this_fix->next) 619690075Sobrien if (GET_CODE (this_fix->insn) != BARRIER) 619790075Sobrien { 619890075Sobrien rtx addr 619990075Sobrien = plus_constant (gen_rtx_LABEL_REF (VOIDmode, 620090075Sobrien minipool_vector_label), 620190075Sobrien this_fix->minipool->offset); 620290075Sobrien *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr); 620390075Sobrien } 620490075Sobrien 620590075Sobrien dump_minipool (last_barrier->insn); 620690075Sobrien fix = ftmp; 620790075Sobrien } 620890075Sobrien 620990075Sobrien /* From now on we must synthesize any constants that we can't handle 621090075Sobrien directly. This can happen if the RTL gets split during final 621190075Sobrien instruction generation. */ 621290075Sobrien after_arm_reorg = 1; 621390075Sobrien 621490075Sobrien /* Free the minipool memory. */ 621590075Sobrien obstack_free (&minipool_obstack, minipool_startobj); 621690075Sobrien} 621790075Sobrien 621890075Sobrien/* Routines to output assembly language. */ 621990075Sobrien 622090075Sobrien/* If the rtx is the correct value then return the string of the number. 622190075Sobrien In this way we can ensure that valid double constants are generated even 622290075Sobrien when cross compiling. */ 622390075Sobrien 622490075Sobrienconst char * 622590075Sobrienfp_immediate_constant (x) 622690075Sobrien rtx x; 622790075Sobrien{ 622890075Sobrien REAL_VALUE_TYPE r; 622990075Sobrien int i; 623090075Sobrien 623190075Sobrien if (!fpa_consts_inited) 623290075Sobrien init_fpa_table (); 623390075Sobrien 623490075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 623590075Sobrien for (i = 0; i < 8; i++) 623690075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 623790075Sobrien return strings_fpa[i]; 623890075Sobrien 623990075Sobrien abort (); 624090075Sobrien} 624190075Sobrien 624290075Sobrien/* As for fp_immediate_constant, but value is passed directly, not in rtx. */ 624390075Sobrien 624490075Sobrienstatic const char * 624590075Sobrienfp_const_from_val (r) 624690075Sobrien REAL_VALUE_TYPE * r; 624790075Sobrien{ 624890075Sobrien int i; 624990075Sobrien 625090075Sobrien if (!fpa_consts_inited) 625190075Sobrien init_fpa_table (); 625290075Sobrien 625390075Sobrien for (i = 0; i < 8; i++) 625490075Sobrien if (REAL_VALUES_EQUAL (*r, values_fpa[i])) 625590075Sobrien return strings_fpa[i]; 625690075Sobrien 625790075Sobrien abort (); 625890075Sobrien} 625990075Sobrien 626090075Sobrien/* Output the operands of a LDM/STM instruction to STREAM. 626190075Sobrien MASK is the ARM register set mask of which only bits 0-15 are important. 626290075Sobrien REG is the base register, either the frame pointer or the stack pointer, 626390075Sobrien INSTR is the possibly suffixed load or store instruction. */ 626490075Sobrien 626590075Sobrienstatic void 626690075Sobrienprint_multi_reg (stream, instr, reg, mask) 626790075Sobrien FILE * stream; 626890075Sobrien const char * instr; 626990075Sobrien int reg; 627090075Sobrien int mask; 627190075Sobrien{ 627290075Sobrien int i; 627390075Sobrien int not_first = FALSE; 627490075Sobrien 627590075Sobrien fputc ('\t', stream); 627690075Sobrien asm_fprintf (stream, instr, reg); 627790075Sobrien fputs (", {", stream); 627890075Sobrien 627990075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 628090075Sobrien if (mask & (1 << i)) 628190075Sobrien { 628290075Sobrien if (not_first) 628390075Sobrien fprintf (stream, ", "); 628490075Sobrien 628590075Sobrien asm_fprintf (stream, "%r", i); 628690075Sobrien not_first = TRUE; 628790075Sobrien } 628890075Sobrien 628990075Sobrien fprintf (stream, "}%s\n", TARGET_APCS_32 ? "" : "^"); 629090075Sobrien} 629190075Sobrien 629290075Sobrien/* Output a 'call' insn. */ 629390075Sobrien 629490075Sobrienconst char * 629590075Sobrienoutput_call (operands) 629690075Sobrien rtx * operands; 629790075Sobrien{ 629890075Sobrien /* Handle calls to lr using ip (which may be clobbered in subr anyway). */ 629990075Sobrien 630090075Sobrien if (REGNO (operands[0]) == LR_REGNUM) 630190075Sobrien { 630290075Sobrien operands[0] = gen_rtx_REG (SImode, IP_REGNUM); 630390075Sobrien output_asm_insn ("mov%?\t%0, %|lr", operands); 630490075Sobrien } 630590075Sobrien 630690075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 630790075Sobrien 630890075Sobrien if (TARGET_INTERWORK) 630990075Sobrien output_asm_insn ("bx%?\t%0", operands); 631090075Sobrien else 631190075Sobrien output_asm_insn ("mov%?\t%|pc, %0", operands); 631290075Sobrien 631390075Sobrien return ""; 631490075Sobrien} 631590075Sobrien 631690075Sobrienstatic int 631790075Sobrieneliminate_lr2ip (x) 631890075Sobrien rtx * x; 631990075Sobrien{ 632090075Sobrien int something_changed = 0; 632190075Sobrien rtx x0 = * x; 632290075Sobrien int code = GET_CODE (x0); 632390075Sobrien int i, j; 632490075Sobrien const char * fmt; 632590075Sobrien 632690075Sobrien switch (code) 632790075Sobrien { 632890075Sobrien case REG: 632990075Sobrien if (REGNO (x0) == LR_REGNUM) 633090075Sobrien { 633190075Sobrien *x = gen_rtx_REG (SImode, IP_REGNUM); 633290075Sobrien return 1; 633390075Sobrien } 633490075Sobrien return 0; 633590075Sobrien default: 633690075Sobrien /* Scan through the sub-elements and change any references there. */ 633790075Sobrien fmt = GET_RTX_FORMAT (code); 633890075Sobrien 633990075Sobrien for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) 634090075Sobrien if (fmt[i] == 'e') 634190075Sobrien something_changed |= eliminate_lr2ip (&XEXP (x0, i)); 634290075Sobrien else if (fmt[i] == 'E') 634390075Sobrien for (j = 0; j < XVECLEN (x0, i); j++) 634490075Sobrien something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j)); 634590075Sobrien 634690075Sobrien return something_changed; 634790075Sobrien } 634890075Sobrien} 634990075Sobrien 635090075Sobrien/* Output a 'call' insn that is a reference in memory. */ 635190075Sobrien 635290075Sobrienconst char * 635390075Sobrienoutput_call_mem (operands) 635490075Sobrien rtx * operands; 635590075Sobrien{ 635690075Sobrien operands[0] = copy_rtx (operands[0]); /* Be ultra careful. */ 635790075Sobrien /* Handle calls using lr by using ip (which may be clobbered in subr anyway). */ 635890075Sobrien if (eliminate_lr2ip (&operands[0])) 635990075Sobrien output_asm_insn ("mov%?\t%|ip, %|lr", operands); 636090075Sobrien 636190075Sobrien if (TARGET_INTERWORK) 636290075Sobrien { 636390075Sobrien output_asm_insn ("ldr%?\t%|ip, %0", operands); 636490075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 636590075Sobrien output_asm_insn ("bx%?\t%|ip", operands); 636690075Sobrien } 636790075Sobrien else 636890075Sobrien { 636990075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 637090075Sobrien output_asm_insn ("ldr%?\t%|pc, %0", operands); 637190075Sobrien } 637290075Sobrien 637390075Sobrien return ""; 637490075Sobrien} 637590075Sobrien 637690075Sobrien 637790075Sobrien/* Output a move from arm registers to an fpu registers. 637890075Sobrien OPERANDS[0] is an fpu register. 637990075Sobrien OPERANDS[1] is the first registers of an arm register pair. */ 638090075Sobrien 638190075Sobrienconst char * 638290075Sobrienoutput_mov_long_double_fpu_from_arm (operands) 638390075Sobrien rtx * operands; 638490075Sobrien{ 638590075Sobrien int arm_reg0 = REGNO (operands[1]); 638690075Sobrien rtx ops[3]; 638790075Sobrien 638890075Sobrien if (arm_reg0 == IP_REGNUM) 638990075Sobrien abort (); 639090075Sobrien 639190075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 639290075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 639390075Sobrien ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); 639490075Sobrien 639590075Sobrien output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops); 639690075Sobrien output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands); 639790075Sobrien 639890075Sobrien return ""; 639990075Sobrien} 640090075Sobrien 640190075Sobrien/* Output a move from an fpu register to arm registers. 640290075Sobrien OPERANDS[0] is the first registers of an arm register pair. 640390075Sobrien OPERANDS[1] is an fpu register. */ 640490075Sobrien 640590075Sobrienconst char * 640690075Sobrienoutput_mov_long_double_arm_from_fpu (operands) 640790075Sobrien rtx * operands; 640890075Sobrien{ 640990075Sobrien int arm_reg0 = REGNO (operands[0]); 641090075Sobrien rtx ops[3]; 641190075Sobrien 641290075Sobrien if (arm_reg0 == IP_REGNUM) 641390075Sobrien abort (); 641490075Sobrien 641590075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 641690075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 641790075Sobrien ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); 641890075Sobrien 641990075Sobrien output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands); 642090075Sobrien output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops); 642190075Sobrien return ""; 642290075Sobrien} 642390075Sobrien 642490075Sobrien/* Output a move from arm registers to arm registers of a long double 642590075Sobrien OPERANDS[0] is the destination. 642690075Sobrien OPERANDS[1] is the source. */ 642790075Sobrien 642890075Sobrienconst char * 642990075Sobrienoutput_mov_long_double_arm_from_arm (operands) 643090075Sobrien rtx * operands; 643190075Sobrien{ 643290075Sobrien /* We have to be careful here because the two might overlap. */ 643390075Sobrien int dest_start = REGNO (operands[0]); 643490075Sobrien int src_start = REGNO (operands[1]); 643590075Sobrien rtx ops[2]; 643690075Sobrien int i; 643790075Sobrien 643890075Sobrien if (dest_start < src_start) 643990075Sobrien { 644090075Sobrien for (i = 0; i < 3; i++) 644190075Sobrien { 644290075Sobrien ops[0] = gen_rtx_REG (SImode, dest_start + i); 644390075Sobrien ops[1] = gen_rtx_REG (SImode, src_start + i); 644490075Sobrien output_asm_insn ("mov%?\t%0, %1", ops); 644590075Sobrien } 644690075Sobrien } 644790075Sobrien else 644890075Sobrien { 644990075Sobrien for (i = 2; i >= 0; i--) 645090075Sobrien { 645190075Sobrien ops[0] = gen_rtx_REG (SImode, dest_start + i); 645290075Sobrien ops[1] = gen_rtx_REG (SImode, src_start + i); 645390075Sobrien output_asm_insn ("mov%?\t%0, %1", ops); 645490075Sobrien } 645590075Sobrien } 645690075Sobrien 645790075Sobrien return ""; 645890075Sobrien} 645990075Sobrien 646090075Sobrien 646190075Sobrien/* Output a move from arm registers to an fpu registers. 646290075Sobrien OPERANDS[0] is an fpu register. 646390075Sobrien OPERANDS[1] is the first registers of an arm register pair. */ 646490075Sobrien 646590075Sobrienconst char * 646690075Sobrienoutput_mov_double_fpu_from_arm (operands) 646790075Sobrien rtx * operands; 646890075Sobrien{ 646990075Sobrien int arm_reg0 = REGNO (operands[1]); 647090075Sobrien rtx ops[2]; 647190075Sobrien 647290075Sobrien if (arm_reg0 == IP_REGNUM) 647390075Sobrien abort (); 647490075Sobrien 647590075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 647690075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 647790075Sobrien output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops); 647890075Sobrien output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands); 647990075Sobrien return ""; 648090075Sobrien} 648190075Sobrien 648290075Sobrien/* Output a move from an fpu register to arm registers. 648390075Sobrien OPERANDS[0] is the first registers of an arm register pair. 648490075Sobrien OPERANDS[1] is an fpu register. */ 648590075Sobrien 648690075Sobrienconst char * 648790075Sobrienoutput_mov_double_arm_from_fpu (operands) 648890075Sobrien rtx * operands; 648990075Sobrien{ 649090075Sobrien int arm_reg0 = REGNO (operands[0]); 649190075Sobrien rtx ops[2]; 649290075Sobrien 649390075Sobrien if (arm_reg0 == IP_REGNUM) 649490075Sobrien abort (); 649590075Sobrien 649690075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 649790075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 649890075Sobrien output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands); 649990075Sobrien output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops); 650090075Sobrien return ""; 650190075Sobrien} 650290075Sobrien 650390075Sobrien/* Output a move between double words. 650490075Sobrien It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM 650590075Sobrien or MEM<-REG and all MEMs must be offsettable addresses. */ 650690075Sobrien 650790075Sobrienconst char * 650890075Sobrienoutput_move_double (operands) 650990075Sobrien rtx * operands; 651090075Sobrien{ 651190075Sobrien enum rtx_code code0 = GET_CODE (operands[0]); 651290075Sobrien enum rtx_code code1 = GET_CODE (operands[1]); 651390075Sobrien rtx otherops[3]; 651490075Sobrien 651590075Sobrien if (code0 == REG) 651690075Sobrien { 651790075Sobrien int reg0 = REGNO (operands[0]); 651890075Sobrien 651990075Sobrien otherops[0] = gen_rtx_REG (SImode, 1 + reg0); 652090075Sobrien 652190075Sobrien if (code1 == REG) 652290075Sobrien { 652390075Sobrien int reg1 = REGNO (operands[1]); 652490075Sobrien if (reg1 == IP_REGNUM) 652590075Sobrien abort (); 652690075Sobrien 652790075Sobrien /* Ensure the second source is not overwritten. */ 652890075Sobrien if (reg1 == reg0 + (WORDS_BIG_ENDIAN ? -1 : 1)) 652990075Sobrien output_asm_insn ("mov%?\t%Q0, %Q1\n\tmov%?\t%R0, %R1", operands); 653090075Sobrien else 653190075Sobrien output_asm_insn ("mov%?\t%R0, %R1\n\tmov%?\t%Q0, %Q1", operands); 653290075Sobrien } 653390075Sobrien else if (code1 == CONST_DOUBLE) 653490075Sobrien { 653590075Sobrien if (GET_MODE (operands[1]) == DFmode) 653690075Sobrien { 653790075Sobrien long l[2]; 653890075Sobrien union real_extract u; 653990075Sobrien 654090075Sobrien memcpy (&u, &CONST_DOUBLE_LOW (operands[1]), sizeof (u)); 654190075Sobrien REAL_VALUE_TO_TARGET_DOUBLE (u.d, l); 654290075Sobrien otherops[1] = GEN_INT (l[1]); 654390075Sobrien operands[1] = GEN_INT (l[0]); 654490075Sobrien } 654590075Sobrien else if (GET_MODE (operands[1]) != VOIDmode) 654690075Sobrien abort (); 654790075Sobrien else if (WORDS_BIG_ENDIAN) 654890075Sobrien { 654990075Sobrien otherops[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); 655090075Sobrien operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); 655190075Sobrien } 655290075Sobrien else 655390075Sobrien { 655490075Sobrien otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); 655590075Sobrien operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); 655690075Sobrien } 655790075Sobrien 655890075Sobrien output_mov_immediate (operands); 655990075Sobrien output_mov_immediate (otherops); 656090075Sobrien } 656190075Sobrien else if (code1 == CONST_INT) 656290075Sobrien { 656390075Sobrien#if HOST_BITS_PER_WIDE_INT > 32 656490075Sobrien /* If HOST_WIDE_INT is more than 32 bits, the intval tells us 656590075Sobrien what the upper word is. */ 656690075Sobrien if (WORDS_BIG_ENDIAN) 656790075Sobrien { 656890075Sobrien otherops[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); 656990075Sobrien operands[1] = GEN_INT (INTVAL (operands[1]) >> 32); 657090075Sobrien } 657190075Sobrien else 657290075Sobrien { 657390075Sobrien otherops[1] = GEN_INT (INTVAL (operands[1]) >> 32); 657490075Sobrien operands[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); 657590075Sobrien } 657690075Sobrien#else 657790075Sobrien /* Sign extend the intval into the high-order word. */ 657890075Sobrien if (WORDS_BIG_ENDIAN) 657990075Sobrien { 658090075Sobrien otherops[1] = operands[1]; 658190075Sobrien operands[1] = (INTVAL (operands[1]) < 0 658290075Sobrien ? constm1_rtx : const0_rtx); 658390075Sobrien } 658490075Sobrien else 658590075Sobrien otherops[1] = INTVAL (operands[1]) < 0 ? constm1_rtx : const0_rtx; 658690075Sobrien#endif 658790075Sobrien output_mov_immediate (otherops); 658890075Sobrien output_mov_immediate (operands); 658990075Sobrien } 659090075Sobrien else if (code1 == MEM) 659190075Sobrien { 659290075Sobrien switch (GET_CODE (XEXP (operands[1], 0))) 659390075Sobrien { 659490075Sobrien case REG: 659590075Sobrien output_asm_insn ("ldm%?ia\t%m1, %M0", operands); 659690075Sobrien break; 659790075Sobrien 659890075Sobrien case PRE_INC: 659990075Sobrien abort (); /* Should never happen now. */ 660090075Sobrien break; 660190075Sobrien 660290075Sobrien case PRE_DEC: 660390075Sobrien output_asm_insn ("ldm%?db\t%m1!, %M0", operands); 660490075Sobrien break; 660590075Sobrien 660690075Sobrien case POST_INC: 660790075Sobrien output_asm_insn ("ldm%?ia\t%m1!, %M0", operands); 660890075Sobrien break; 660990075Sobrien 661090075Sobrien case POST_DEC: 661190075Sobrien abort (); /* Should never happen now. */ 661290075Sobrien break; 661390075Sobrien 661490075Sobrien case LABEL_REF: 661590075Sobrien case CONST: 661690075Sobrien output_asm_insn ("adr%?\t%0, %1", operands); 661790075Sobrien output_asm_insn ("ldm%?ia\t%0, %M0", operands); 661890075Sobrien break; 661990075Sobrien 662090075Sobrien default: 662190075Sobrien if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1), 662290075Sobrien GET_MODE (XEXP (XEXP (operands[1], 0), 1)))) 662390075Sobrien { 662490075Sobrien otherops[0] = operands[0]; 662590075Sobrien otherops[1] = XEXP (XEXP (operands[1], 0), 0); 662690075Sobrien otherops[2] = XEXP (XEXP (operands[1], 0), 1); 662790075Sobrien 662890075Sobrien if (GET_CODE (XEXP (operands[1], 0)) == PLUS) 662990075Sobrien { 663090075Sobrien if (GET_CODE (otherops[2]) == CONST_INT) 663190075Sobrien { 663290075Sobrien switch (INTVAL (otherops[2])) 663390075Sobrien { 663490075Sobrien case -8: 663590075Sobrien output_asm_insn ("ldm%?db\t%1, %M0", otherops); 663690075Sobrien return ""; 663790075Sobrien case -4: 663890075Sobrien output_asm_insn ("ldm%?da\t%1, %M0", otherops); 663990075Sobrien return ""; 664090075Sobrien case 4: 664190075Sobrien output_asm_insn ("ldm%?ib\t%1, %M0", otherops); 664290075Sobrien return ""; 664390075Sobrien } 664490075Sobrien 664590075Sobrien if (!(const_ok_for_arm (INTVAL (otherops[2])))) 664690075Sobrien output_asm_insn ("sub%?\t%0, %1, #%n2", otherops); 664790075Sobrien else 664890075Sobrien output_asm_insn ("add%?\t%0, %1, %2", otherops); 664990075Sobrien } 665090075Sobrien else 665190075Sobrien output_asm_insn ("add%?\t%0, %1, %2", otherops); 665290075Sobrien } 665390075Sobrien else 665490075Sobrien output_asm_insn ("sub%?\t%0, %1, %2", otherops); 665590075Sobrien 665690075Sobrien return "ldm%?ia\t%0, %M0"; 665790075Sobrien } 665890075Sobrien else 665990075Sobrien { 666090075Sobrien otherops[1] = adjust_address (operands[1], VOIDmode, 4); 666190075Sobrien /* Take care of overlapping base/data reg. */ 666290075Sobrien if (reg_mentioned_p (operands[0], operands[1])) 666390075Sobrien { 666490075Sobrien output_asm_insn ("ldr%?\t%0, %1", otherops); 666590075Sobrien output_asm_insn ("ldr%?\t%0, %1", operands); 666690075Sobrien } 666790075Sobrien else 666890075Sobrien { 666990075Sobrien output_asm_insn ("ldr%?\t%0, %1", operands); 667090075Sobrien output_asm_insn ("ldr%?\t%0, %1", otherops); 667190075Sobrien } 667290075Sobrien } 667390075Sobrien } 667490075Sobrien } 667590075Sobrien else 667690075Sobrien abort (); /* Constraints should prevent this. */ 667790075Sobrien } 667890075Sobrien else if (code0 == MEM && code1 == REG) 667990075Sobrien { 668090075Sobrien if (REGNO (operands[1]) == IP_REGNUM) 668190075Sobrien abort (); 668290075Sobrien 668390075Sobrien switch (GET_CODE (XEXP (operands[0], 0))) 668490075Sobrien { 668590075Sobrien case REG: 668690075Sobrien output_asm_insn ("stm%?ia\t%m0, %M1", operands); 668790075Sobrien break; 668890075Sobrien 668990075Sobrien case PRE_INC: 669090075Sobrien abort (); /* Should never happen now. */ 669190075Sobrien break; 669290075Sobrien 669390075Sobrien case PRE_DEC: 669490075Sobrien output_asm_insn ("stm%?db\t%m0!, %M1", operands); 669590075Sobrien break; 669690075Sobrien 669790075Sobrien case POST_INC: 669890075Sobrien output_asm_insn ("stm%?ia\t%m0!, %M1", operands); 669990075Sobrien break; 670090075Sobrien 670190075Sobrien case POST_DEC: 670290075Sobrien abort (); /* Should never happen now. */ 670390075Sobrien break; 670490075Sobrien 670590075Sobrien case PLUS: 670690075Sobrien if (GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT) 670790075Sobrien { 670890075Sobrien switch (INTVAL (XEXP (XEXP (operands[0], 0), 1))) 670990075Sobrien { 671090075Sobrien case -8: 671190075Sobrien output_asm_insn ("stm%?db\t%m0, %M1", operands); 671290075Sobrien return ""; 671390075Sobrien 671490075Sobrien case -4: 671590075Sobrien output_asm_insn ("stm%?da\t%m0, %M1", operands); 671690075Sobrien return ""; 671790075Sobrien 671890075Sobrien case 4: 671990075Sobrien output_asm_insn ("stm%?ib\t%m0, %M1", operands); 672090075Sobrien return ""; 672190075Sobrien } 672290075Sobrien } 672390075Sobrien /* Fall through */ 672490075Sobrien 672590075Sobrien default: 672690075Sobrien otherops[0] = adjust_address (operands[0], VOIDmode, 4); 672790075Sobrien otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1])); 672890075Sobrien output_asm_insn ("str%?\t%1, %0", operands); 672990075Sobrien output_asm_insn ("str%?\t%1, %0", otherops); 673090075Sobrien } 673190075Sobrien } 673290075Sobrien else 673390075Sobrien /* Constraints should prevent this. */ 673490075Sobrien abort (); 673590075Sobrien 673690075Sobrien return ""; 673790075Sobrien} 673890075Sobrien 673990075Sobrien 674090075Sobrien/* Output an arbitrary MOV reg, #n. 674190075Sobrien OPERANDS[0] is a register. OPERANDS[1] is a const_int. */ 674290075Sobrien 674390075Sobrienconst char * 674490075Sobrienoutput_mov_immediate (operands) 674590075Sobrien rtx * operands; 674690075Sobrien{ 674790075Sobrien HOST_WIDE_INT n = INTVAL (operands[1]); 674890075Sobrien 674990075Sobrien /* Try to use one MOV. */ 675090075Sobrien if (const_ok_for_arm (n)) 675190075Sobrien output_asm_insn ("mov%?\t%0, %1", operands); 675290075Sobrien 675390075Sobrien /* Try to use one MVN. */ 675490075Sobrien else if (const_ok_for_arm (~n)) 675590075Sobrien { 675690075Sobrien operands[1] = GEN_INT (~n); 675790075Sobrien output_asm_insn ("mvn%?\t%0, %1", operands); 675890075Sobrien } 675990075Sobrien else 676090075Sobrien { 676190075Sobrien int n_ones = 0; 676290075Sobrien int i; 676390075Sobrien 676490075Sobrien /* If all else fails, make it out of ORRs or BICs as appropriate. */ 676590075Sobrien for (i = 0; i < 32; i ++) 676690075Sobrien if (n & 1 << i) 676790075Sobrien n_ones ++; 676890075Sobrien 676990075Sobrien if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */ 677090075Sobrien output_multi_immediate (operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, ~ n); 677190075Sobrien else 677290075Sobrien output_multi_immediate (operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, n); 677390075Sobrien } 677490075Sobrien 677590075Sobrien return ""; 677690075Sobrien} 677790075Sobrien 677890075Sobrien/* Output an ADD r, s, #n where n may be too big for one instruction. 677990075Sobrien If adding zero to one register, output nothing. */ 678090075Sobrien 678190075Sobrienconst char * 678290075Sobrienoutput_add_immediate (operands) 678390075Sobrien rtx * operands; 678490075Sobrien{ 678590075Sobrien HOST_WIDE_INT n = INTVAL (operands[2]); 678690075Sobrien 678790075Sobrien if (n != 0 || REGNO (operands[0]) != REGNO (operands[1])) 678890075Sobrien { 678990075Sobrien if (n < 0) 679090075Sobrien output_multi_immediate (operands, 679190075Sobrien "sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2, 679290075Sobrien -n); 679390075Sobrien else 679490075Sobrien output_multi_immediate (operands, 679590075Sobrien "add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2, 679690075Sobrien n); 679790075Sobrien } 679890075Sobrien 679990075Sobrien return ""; 680090075Sobrien} 680190075Sobrien 680290075Sobrien/* Output a multiple immediate operation. 680390075Sobrien OPERANDS is the vector of operands referred to in the output patterns. 680490075Sobrien INSTR1 is the output pattern to use for the first constant. 680590075Sobrien INSTR2 is the output pattern to use for subsequent constants. 680690075Sobrien IMMED_OP is the index of the constant slot in OPERANDS. 680790075Sobrien N is the constant value. */ 680890075Sobrien 680990075Sobrienstatic const char * 681090075Sobrienoutput_multi_immediate (operands, instr1, instr2, immed_op, n) 681190075Sobrien rtx * operands; 681290075Sobrien const char * instr1; 681390075Sobrien const char * instr2; 681490075Sobrien int immed_op; 681590075Sobrien HOST_WIDE_INT n; 681690075Sobrien{ 681790075Sobrien#if HOST_BITS_PER_WIDE_INT > 32 681890075Sobrien n &= 0xffffffff; 681990075Sobrien#endif 682090075Sobrien 682190075Sobrien if (n == 0) 682290075Sobrien { 682390075Sobrien /* Quick and easy output. */ 682490075Sobrien operands[immed_op] = const0_rtx; 682590075Sobrien output_asm_insn (instr1, operands); 682690075Sobrien } 682790075Sobrien else 682890075Sobrien { 682990075Sobrien int i; 683090075Sobrien const char * instr = instr1; 683190075Sobrien 683290075Sobrien /* Note that n is never zero here (which would give no output). */ 683390075Sobrien for (i = 0; i < 32; i += 2) 683490075Sobrien { 683590075Sobrien if (n & (3 << i)) 683690075Sobrien { 683790075Sobrien operands[immed_op] = GEN_INT (n & (255 << i)); 683890075Sobrien output_asm_insn (instr, operands); 683990075Sobrien instr = instr2; 684090075Sobrien i += 6; 684190075Sobrien } 684290075Sobrien } 684390075Sobrien } 684490075Sobrien 684590075Sobrien return ""; 684690075Sobrien} 684790075Sobrien 684890075Sobrien/* Return the appropriate ARM instruction for the operation code. 684990075Sobrien The returned result should not be overwritten. OP is the rtx of the 685090075Sobrien operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator 685190075Sobrien was shifted. */ 685290075Sobrien 685390075Sobrienconst char * 685490075Sobrienarithmetic_instr (op, shift_first_arg) 685590075Sobrien rtx op; 685690075Sobrien int shift_first_arg; 685790075Sobrien{ 685890075Sobrien switch (GET_CODE (op)) 685990075Sobrien { 686090075Sobrien case PLUS: 686190075Sobrien return "add"; 686290075Sobrien 686390075Sobrien case MINUS: 686490075Sobrien return shift_first_arg ? "rsb" : "sub"; 686590075Sobrien 686690075Sobrien case IOR: 686790075Sobrien return "orr"; 686890075Sobrien 686990075Sobrien case XOR: 687090075Sobrien return "eor"; 687190075Sobrien 687290075Sobrien case AND: 687390075Sobrien return "and"; 687490075Sobrien 687590075Sobrien default: 687690075Sobrien abort (); 687790075Sobrien } 687890075Sobrien} 687990075Sobrien 688090075Sobrien/* Ensure valid constant shifts and return the appropriate shift mnemonic 688190075Sobrien for the operation code. The returned result should not be overwritten. 688290075Sobrien OP is the rtx code of the shift. 688390075Sobrien On exit, *AMOUNTP will be -1 if the shift is by a register, or a constant 688490075Sobrien shift. */ 688590075Sobrien 688690075Sobrienstatic const char * 688790075Sobrienshift_op (op, amountp) 688890075Sobrien rtx op; 688990075Sobrien HOST_WIDE_INT *amountp; 689090075Sobrien{ 689190075Sobrien const char * mnem; 689290075Sobrien enum rtx_code code = GET_CODE (op); 689390075Sobrien 689490075Sobrien if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG) 689590075Sobrien *amountp = -1; 689690075Sobrien else if (GET_CODE (XEXP (op, 1)) == CONST_INT) 689790075Sobrien *amountp = INTVAL (XEXP (op, 1)); 689890075Sobrien else 689990075Sobrien abort (); 690090075Sobrien 690190075Sobrien switch (code) 690290075Sobrien { 690390075Sobrien case ASHIFT: 690490075Sobrien mnem = "asl"; 690590075Sobrien break; 690690075Sobrien 690790075Sobrien case ASHIFTRT: 690890075Sobrien mnem = "asr"; 690990075Sobrien break; 691090075Sobrien 691190075Sobrien case LSHIFTRT: 691290075Sobrien mnem = "lsr"; 691390075Sobrien break; 691490075Sobrien 691590075Sobrien case ROTATERT: 691690075Sobrien mnem = "ror"; 691790075Sobrien break; 691890075Sobrien 691990075Sobrien case MULT: 692090075Sobrien /* We never have to worry about the amount being other than a 692190075Sobrien power of 2, since this case can never be reloaded from a reg. */ 692290075Sobrien if (*amountp != -1) 692390075Sobrien *amountp = int_log2 (*amountp); 692490075Sobrien else 692590075Sobrien abort (); 692690075Sobrien return "asl"; 692790075Sobrien 692890075Sobrien default: 692990075Sobrien abort (); 693090075Sobrien } 693190075Sobrien 693290075Sobrien if (*amountp != -1) 693390075Sobrien { 693490075Sobrien /* This is not 100% correct, but follows from the desire to merge 693590075Sobrien multiplication by a power of 2 with the recognizer for a 693690075Sobrien shift. >=32 is not a valid shift for "asl", so we must try and 693790075Sobrien output a shift that produces the correct arithmetical result. 693890075Sobrien Using lsr #32 is identical except for the fact that the carry bit 693990075Sobrien is not set correctly if we set the flags; but we never use the 694090075Sobrien carry bit from such an operation, so we can ignore that. */ 694190075Sobrien if (code == ROTATERT) 694290075Sobrien /* Rotate is just modulo 32. */ 694390075Sobrien *amountp &= 31; 694490075Sobrien else if (*amountp != (*amountp & 31)) 694590075Sobrien { 694690075Sobrien if (code == ASHIFT) 694790075Sobrien mnem = "lsr"; 694890075Sobrien *amountp = 32; 694990075Sobrien } 695090075Sobrien 695190075Sobrien /* Shifts of 0 are no-ops. */ 695290075Sobrien if (*amountp == 0) 695390075Sobrien return NULL; 695490075Sobrien } 695590075Sobrien 695690075Sobrien return mnem; 695790075Sobrien} 695890075Sobrien 695990075Sobrien/* Obtain the shift from the POWER of two. */ 696090075Sobrien 696190075Sobrienstatic HOST_WIDE_INT 696290075Sobrienint_log2 (power) 696390075Sobrien HOST_WIDE_INT power; 696490075Sobrien{ 696590075Sobrien HOST_WIDE_INT shift = 0; 696690075Sobrien 696790075Sobrien while ((((HOST_WIDE_INT) 1 << shift) & power) == 0) 696890075Sobrien { 696990075Sobrien if (shift > 31) 697090075Sobrien abort (); 697190075Sobrien shift ++; 697290075Sobrien } 697390075Sobrien 697490075Sobrien return shift; 697590075Sobrien} 697690075Sobrien 697790075Sobrien/* Output a .ascii pseudo-op, keeping track of lengths. This is because 697890075Sobrien /bin/as is horribly restrictive. */ 697990075Sobrien#define MAX_ASCII_LEN 51 698090075Sobrien 698190075Sobrienvoid 698290075Sobrienoutput_ascii_pseudo_op (stream, p, len) 698390075Sobrien FILE * stream; 698490075Sobrien const unsigned char * p; 698590075Sobrien int len; 698690075Sobrien{ 698790075Sobrien int i; 698890075Sobrien int len_so_far = 0; 698990075Sobrien 699090075Sobrien fputs ("\t.ascii\t\"", stream); 699190075Sobrien 699290075Sobrien for (i = 0; i < len; i++) 699390075Sobrien { 699490075Sobrien int c = p[i]; 699590075Sobrien 699690075Sobrien if (len_so_far >= MAX_ASCII_LEN) 699790075Sobrien { 699890075Sobrien fputs ("\"\n\t.ascii\t\"", stream); 699990075Sobrien len_so_far = 0; 700090075Sobrien } 700190075Sobrien 700290075Sobrien switch (c) 700390075Sobrien { 700490075Sobrien case TARGET_TAB: 700590075Sobrien fputs ("\\t", stream); 700690075Sobrien len_so_far += 2; 700790075Sobrien break; 700890075Sobrien 700990075Sobrien case TARGET_FF: 701090075Sobrien fputs ("\\f", stream); 701190075Sobrien len_so_far += 2; 701290075Sobrien break; 701390075Sobrien 701490075Sobrien case TARGET_BS: 701590075Sobrien fputs ("\\b", stream); 701690075Sobrien len_so_far += 2; 701790075Sobrien break; 701890075Sobrien 701990075Sobrien case TARGET_CR: 702090075Sobrien fputs ("\\r", stream); 702190075Sobrien len_so_far += 2; 702290075Sobrien break; 702390075Sobrien 702490075Sobrien case TARGET_NEWLINE: 702590075Sobrien fputs ("\\n", stream); 702690075Sobrien c = p [i + 1]; 702790075Sobrien if ((c >= ' ' && c <= '~') 702890075Sobrien || c == TARGET_TAB) 702990075Sobrien /* This is a good place for a line break. */ 703090075Sobrien len_so_far = MAX_ASCII_LEN; 703190075Sobrien else 703290075Sobrien len_so_far += 2; 703390075Sobrien break; 703490075Sobrien 703590075Sobrien case '\"': 703690075Sobrien case '\\': 703790075Sobrien putc ('\\', stream); 703890075Sobrien len_so_far++; 703990075Sobrien /* drop through. */ 704090075Sobrien 704190075Sobrien default: 704290075Sobrien if (c >= ' ' && c <= '~') 704390075Sobrien { 704490075Sobrien putc (c, stream); 704590075Sobrien len_so_far++; 704690075Sobrien } 704790075Sobrien else 704890075Sobrien { 704990075Sobrien fprintf (stream, "\\%03o", c); 705090075Sobrien len_so_far += 4; 705190075Sobrien } 705290075Sobrien break; 705390075Sobrien } 705490075Sobrien } 705590075Sobrien 705690075Sobrien fputs ("\"\n", stream); 705790075Sobrien} 705890075Sobrien 705990075Sobrien/* Compute the register sabe mask for registers 0 through 12 706090075Sobrien inclusive. This code is used by both arm_compute_save_reg_mask 706190075Sobrien and arm_compute_initial_elimination_offset. */ 706290075Sobrien 706390075Sobrienstatic unsigned long 706490075Sobrienarm_compute_save_reg0_reg12_mask () 706590075Sobrien{ 706690075Sobrien unsigned long func_type = arm_current_func_type (); 706790075Sobrien unsigned int save_reg_mask = 0; 706890075Sobrien unsigned int reg; 706990075Sobrien 707090075Sobrien if (IS_INTERRUPT (func_type)) 707190075Sobrien { 707290075Sobrien unsigned int max_reg; 707390075Sobrien /* Interrupt functions must not corrupt any registers, 707490075Sobrien even call clobbered ones. If this is a leaf function 707590075Sobrien we can just examine the registers used by the RTL, but 707690075Sobrien otherwise we have to assume that whatever function is 707790075Sobrien called might clobber anything, and so we have to save 707890075Sobrien all the call-clobbered registers as well. */ 707990075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_FIQ) 708090075Sobrien /* FIQ handlers have registers r8 - r12 banked, so 708190075Sobrien we only need to check r0 - r7, Normal ISRs only 708290075Sobrien bank r14 and r15, so we must check up to r12. 708390075Sobrien r13 is the stack pointer which is always preserved, 708490075Sobrien so we do not need to consider it here. */ 708590075Sobrien max_reg = 7; 708690075Sobrien else 708790075Sobrien max_reg = 12; 708890075Sobrien 708990075Sobrien for (reg = 0; reg <= max_reg; reg++) 709090075Sobrien if (regs_ever_live[reg] 709190075Sobrien || (! current_function_is_leaf && call_used_regs [reg])) 709290075Sobrien save_reg_mask |= (1 << reg); 709390075Sobrien } 709490075Sobrien else 709590075Sobrien { 709690075Sobrien /* In the normal case we only need to save those registers 709790075Sobrien which are call saved and which are used by this function. */ 709890075Sobrien for (reg = 0; reg <= 10; reg++) 709990075Sobrien if (regs_ever_live[reg] && ! call_used_regs [reg]) 710090075Sobrien save_reg_mask |= (1 << reg); 710190075Sobrien 710290075Sobrien /* Handle the frame pointer as a special case. */ 710390075Sobrien if (! TARGET_APCS_FRAME 710490075Sobrien && ! frame_pointer_needed 710590075Sobrien && regs_ever_live[HARD_FRAME_POINTER_REGNUM] 710690075Sobrien && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) 710790075Sobrien save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM; 710890075Sobrien 710990075Sobrien /* If we aren't loading the PIC register, 711090075Sobrien don't stack it even though it may be live. */ 711190075Sobrien if (flag_pic 711290075Sobrien && ! TARGET_SINGLE_PIC_BASE 711390075Sobrien && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) 711490075Sobrien save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM; 711590075Sobrien } 711690075Sobrien 711790075Sobrien return save_reg_mask; 711890075Sobrien} 711990075Sobrien 712090075Sobrien/* Compute a bit mask of which registers need to be 712190075Sobrien saved on the stack for the current function. */ 712290075Sobrien 712390075Sobrienstatic unsigned long 712490075Sobrienarm_compute_save_reg_mask () 712590075Sobrien{ 712690075Sobrien unsigned int save_reg_mask = 0; 712790075Sobrien unsigned long func_type = arm_current_func_type (); 712890075Sobrien 712990075Sobrien if (IS_NAKED (func_type)) 713090075Sobrien /* This should never really happen. */ 713190075Sobrien return 0; 713290075Sobrien 713390075Sobrien /* If we are creating a stack frame, then we must save the frame pointer, 713490075Sobrien IP (which will hold the old stack pointer), LR and the PC. */ 713590075Sobrien if (frame_pointer_needed) 713690075Sobrien save_reg_mask |= 713790075Sobrien (1 << ARM_HARD_FRAME_POINTER_REGNUM) 713890075Sobrien | (1 << IP_REGNUM) 713990075Sobrien | (1 << LR_REGNUM) 714090075Sobrien | (1 << PC_REGNUM); 714190075Sobrien 714290075Sobrien /* Volatile functions do not return, so there 714390075Sobrien is no need to save any other registers. */ 714490075Sobrien if (IS_VOLATILE (func_type)) 714590075Sobrien return save_reg_mask; 714690075Sobrien 714790075Sobrien save_reg_mask |= arm_compute_save_reg0_reg12_mask (); 714890075Sobrien 714990075Sobrien /* Decide if we need to save the link register. 715090075Sobrien Interrupt routines have their own banked link register, 715190075Sobrien so they never need to save it. 715296263Sobrien Otherwise if we do not use the link register we do not need to save 715390075Sobrien it. If we are pushing other registers onto the stack however, we 715490075Sobrien can save an instruction in the epilogue by pushing the link register 715590075Sobrien now and then popping it back into the PC. This incurs extra memory 715690075Sobrien accesses though, so we only do it when optimising for size, and only 715790075Sobrien if we know that we will not need a fancy return sequence. */ 715896263Sobrien if (regs_ever_live [LR_REGNUM] 715990075Sobrien || (save_reg_mask 716090075Sobrien && optimize_size 716196263Sobrien && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)) 716290075Sobrien save_reg_mask |= 1 << LR_REGNUM; 716390075Sobrien 716490075Sobrien if (cfun->machine->lr_save_eliminated) 716590075Sobrien save_reg_mask &= ~ (1 << LR_REGNUM); 716690075Sobrien 716790075Sobrien return save_reg_mask; 716890075Sobrien} 716990075Sobrien 717090075Sobrien/* Generate a function exit sequence. If REALLY_RETURN is true, then do 717190075Sobrien everything bar the final return instruction. */ 717290075Sobrien 717390075Sobrienconst char * 717490075Sobrienoutput_return_instruction (operand, really_return, reverse) 717590075Sobrien rtx operand; 717690075Sobrien int really_return; 717790075Sobrien int reverse; 717890075Sobrien{ 717990075Sobrien char conditional[10]; 718090075Sobrien char instr[100]; 718190075Sobrien int reg; 718290075Sobrien unsigned long live_regs_mask; 718390075Sobrien unsigned long func_type; 718490075Sobrien 718590075Sobrien func_type = arm_current_func_type (); 718690075Sobrien 718790075Sobrien if (IS_NAKED (func_type)) 718890075Sobrien return ""; 718990075Sobrien 719090075Sobrien if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) 719190075Sobrien { 719290075Sobrien /* If this function was declared non-returning, and we have found a tail 719390075Sobrien call, then we have to trust that the called function won't return. */ 719490075Sobrien if (really_return) 719590075Sobrien { 719690075Sobrien rtx ops[2]; 719790075Sobrien 719890075Sobrien /* Otherwise, trap an attempted return by aborting. */ 719990075Sobrien ops[0] = operand; 720090075Sobrien ops[1] = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" 720190075Sobrien : "abort"); 720290075Sobrien assemble_external_libcall (ops[1]); 720390075Sobrien output_asm_insn (reverse ? "bl%D0\t%a1" : "bl%d0\t%a1", ops); 720490075Sobrien } 720590075Sobrien 720690075Sobrien return ""; 720790075Sobrien } 720890075Sobrien 720990075Sobrien if (current_function_calls_alloca && !really_return) 721090075Sobrien abort (); 721190075Sobrien 721290075Sobrien /* Construct the conditional part of the instruction(s) to be emitted. */ 721390075Sobrien sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd'); 721490075Sobrien 721590075Sobrien return_used_this_function = 1; 721690075Sobrien 721790075Sobrien live_regs_mask = arm_compute_save_reg_mask (); 721890075Sobrien 721996263Sobrien if (live_regs_mask) 722090075Sobrien { 722196263Sobrien const char * return_reg; 722296263Sobrien 722396263Sobrien /* If we do not have any special requirements for function exit 722496263Sobrien (eg interworking, or ISR) then we can load the return address 722596263Sobrien directly into the PC. Otherwise we must load it into LR. */ 722696263Sobrien if (really_return 722796263Sobrien && ! TARGET_INTERWORK) 722896263Sobrien return_reg = reg_names[PC_REGNUM]; 722990075Sobrien else 723096263Sobrien return_reg = reg_names[LR_REGNUM]; 723196263Sobrien 723290075Sobrien if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM)) 723390075Sobrien /* There are two possible reasons for the IP register being saved. 723490075Sobrien Either a stack frame was created, in which case IP contains the 723590075Sobrien old stack pointer, or an ISR routine corrupted it. If this in an 723690075Sobrien ISR routine then just restore IP, otherwise restore IP into SP. */ 723790075Sobrien if (! IS_INTERRUPT (func_type)) 723890075Sobrien { 723990075Sobrien live_regs_mask &= ~ (1 << IP_REGNUM); 724090075Sobrien live_regs_mask |= (1 << SP_REGNUM); 724190075Sobrien } 724290075Sobrien 724396263Sobrien /* On some ARM architectures it is faster to use LDR rather than 724496263Sobrien LDM to load a single register. On other architectures, the 724596263Sobrien cost is the same. In 26 bit mode, or for exception handlers, 724696263Sobrien we have to use LDM to load the PC so that the CPSR is also 724796263Sobrien restored. */ 724896263Sobrien for (reg = 0; reg <= LAST_ARM_REGNUM; reg++) 724990075Sobrien { 725096263Sobrien if (live_regs_mask == (unsigned int)(1 << reg)) 725196263Sobrien break; 725290075Sobrien } 725396263Sobrien if (reg <= LAST_ARM_REGNUM 725496263Sobrien && (reg != LR_REGNUM 725596263Sobrien || ! really_return 725696263Sobrien || (TARGET_APCS_32 && ! IS_INTERRUPT (func_type)))) 725796263Sobrien { 725896263Sobrien sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional, 725996263Sobrien (reg == LR_REGNUM) ? return_reg : reg_names[reg]); 726096263Sobrien } 726190075Sobrien else 726290075Sobrien { 726396263Sobrien char *p; 726496263Sobrien int first = 1; 726590075Sobrien 726696263Sobrien /* Generate the load multiple instruction to restore the registers. */ 726796263Sobrien if (frame_pointer_needed) 726896263Sobrien sprintf (instr, "ldm%sea\t%%|fp, {", conditional); 726990075Sobrien else 727096263Sobrien sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional); 727190075Sobrien 727296263Sobrien p = instr + strlen (instr); 727390075Sobrien 727496263Sobrien for (reg = 0; reg <= SP_REGNUM; reg++) 727596263Sobrien if (live_regs_mask & (1 << reg)) 727696263Sobrien { 727796263Sobrien int l = strlen (reg_names[reg]); 727890075Sobrien 727996263Sobrien if (first) 728096263Sobrien first = 0; 728196263Sobrien else 728296263Sobrien { 728396263Sobrien memcpy (p, ", ", 2); 728496263Sobrien p += 2; 728596263Sobrien } 728690075Sobrien 728796263Sobrien memcpy (p, "%|", 2); 728896263Sobrien memcpy (p + 2, reg_names[reg], l); 728996263Sobrien p += l + 2; 729096263Sobrien } 729196263Sobrien 729296263Sobrien if (live_regs_mask & (1 << LR_REGNUM)) 729396263Sobrien { 729496263Sobrien int l = strlen (return_reg); 729590075Sobrien 729696263Sobrien if (! first) 729790075Sobrien { 729896263Sobrien memcpy (p, ", ", 2); 729996263Sobrien p += 2; 730096263Sobrien } 730190075Sobrien 730296263Sobrien memcpy (p, "%|", 2); 730396263Sobrien memcpy (p + 2, return_reg, l); 730496263Sobrien strcpy (p + 2 + l, ((TARGET_APCS_32 730596263Sobrien && !IS_INTERRUPT (func_type)) 730696263Sobrien || !really_return) 730796263Sobrien ? "}" : "}^"); 730890075Sobrien } 730996263Sobrien else 731096263Sobrien strcpy (p, "}"); 731190075Sobrien } 731296263Sobrien 731396263Sobrien output_asm_insn (instr, & operand); 731496263Sobrien 731596263Sobrien /* See if we need to generate an extra instruction to 731696263Sobrien perform the actual function return. */ 731796263Sobrien if (really_return 731896263Sobrien && func_type != ARM_FT_INTERWORKED 731996263Sobrien && (live_regs_mask & (1 << LR_REGNUM)) != 0) 732096263Sobrien { 732196263Sobrien /* The return has already been handled 732296263Sobrien by loading the LR into the PC. */ 732396263Sobrien really_return = 0; 732496263Sobrien } 732590075Sobrien } 732696263Sobrien 732796263Sobrien if (really_return) 732890075Sobrien { 732990075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 733090075Sobrien { 733190075Sobrien case ARM_FT_ISR: 733290075Sobrien case ARM_FT_FIQ: 733390075Sobrien sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional); 733490075Sobrien break; 733590075Sobrien 733690075Sobrien case ARM_FT_INTERWORKED: 733790075Sobrien sprintf (instr, "bx%s\t%%|lr", conditional); 733890075Sobrien break; 733990075Sobrien 734090075Sobrien case ARM_FT_EXCEPTION: 734190075Sobrien sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional); 734290075Sobrien break; 734390075Sobrien 734490075Sobrien default: 734596263Sobrien /* ARMv5 implementations always provide BX, so interworking 734696263Sobrien is the default unless APCS-26 is in use. */ 734796263Sobrien if ((insn_flags & FL_ARCH5) != 0 && TARGET_APCS_32) 734896263Sobrien sprintf (instr, "bx%s\t%%|lr", conditional); 734996263Sobrien else 735096263Sobrien sprintf (instr, "mov%s%s\t%%|pc, %%|lr", 735196263Sobrien conditional, TARGET_APCS_32 ? "" : "s"); 735290075Sobrien break; 735390075Sobrien } 735496263Sobrien 735596263Sobrien output_asm_insn (instr, & operand); 735690075Sobrien } 735790075Sobrien 735890075Sobrien return ""; 735990075Sobrien} 736090075Sobrien 736190075Sobrien/* Write the function name into the code section, directly preceding 736290075Sobrien the function prologue. 736390075Sobrien 736490075Sobrien Code will be output similar to this: 736590075Sobrien t0 736690075Sobrien .ascii "arm_poke_function_name", 0 736790075Sobrien .align 736890075Sobrien t1 736990075Sobrien .word 0xff000000 + (t1 - t0) 737090075Sobrien arm_poke_function_name 737190075Sobrien mov ip, sp 737290075Sobrien stmfd sp!, {fp, ip, lr, pc} 737390075Sobrien sub fp, ip, #4 737490075Sobrien 737590075Sobrien When performing a stack backtrace, code can inspect the value 737690075Sobrien of 'pc' stored at 'fp' + 0. If the trace function then looks 737790075Sobrien at location pc - 12 and the top 8 bits are set, then we know 737890075Sobrien that there is a function name embedded immediately preceding this 737990075Sobrien location and has length ((pc[-3]) & 0xff000000). 738090075Sobrien 738190075Sobrien We assume that pc is declared as a pointer to an unsigned long. 738290075Sobrien 738390075Sobrien It is of no benefit to output the function name if we are assembling 738490075Sobrien a leaf function. These function types will not contain a stack 738590075Sobrien backtrace structure, therefore it is not possible to determine the 738690075Sobrien function name. */ 738790075Sobrien 738890075Sobrienvoid 738990075Sobrienarm_poke_function_name (stream, name) 739090075Sobrien FILE * stream; 739190075Sobrien const char * name; 739290075Sobrien{ 739390075Sobrien unsigned long alignlength; 739490075Sobrien unsigned long length; 739590075Sobrien rtx x; 739690075Sobrien 739790075Sobrien length = strlen (name) + 1; 739890075Sobrien alignlength = ROUND_UP (length); 739990075Sobrien 740090075Sobrien ASM_OUTPUT_ASCII (stream, name, length); 740190075Sobrien ASM_OUTPUT_ALIGN (stream, 2); 740290075Sobrien x = GEN_INT ((unsigned HOST_WIDE_INT) 0xff000000 + alignlength); 740390075Sobrien assemble_aligned_integer (UNITS_PER_WORD, x); 740490075Sobrien} 740590075Sobrien 740690075Sobrien/* Place some comments into the assembler stream 740790075Sobrien describing the current function. */ 740890075Sobrien 740990075Sobrienstatic void 741090075Sobrienarm_output_function_prologue (f, frame_size) 741190075Sobrien FILE * f; 741290075Sobrien HOST_WIDE_INT frame_size; 741390075Sobrien{ 741490075Sobrien unsigned long func_type; 741590075Sobrien 741690075Sobrien if (!TARGET_ARM) 741790075Sobrien { 741890075Sobrien thumb_output_function_prologue (f, frame_size); 741990075Sobrien return; 742090075Sobrien } 742190075Sobrien 742290075Sobrien /* Sanity check. */ 742390075Sobrien if (arm_ccfsm_state || arm_target_insn) 742490075Sobrien abort (); 742590075Sobrien 742690075Sobrien func_type = arm_current_func_type (); 742790075Sobrien 742890075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 742990075Sobrien { 743090075Sobrien default: 743190075Sobrien case ARM_FT_NORMAL: 743290075Sobrien break; 743390075Sobrien case ARM_FT_INTERWORKED: 743490075Sobrien asm_fprintf (f, "\t%@ Function supports interworking.\n"); 743590075Sobrien break; 743690075Sobrien case ARM_FT_EXCEPTION_HANDLER: 743790075Sobrien asm_fprintf (f, "\t%@ C++ Exception Handler.\n"); 743890075Sobrien break; 743990075Sobrien case ARM_FT_ISR: 744090075Sobrien asm_fprintf (f, "\t%@ Interrupt Service Routine.\n"); 744190075Sobrien break; 744290075Sobrien case ARM_FT_FIQ: 744390075Sobrien asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n"); 744490075Sobrien break; 744590075Sobrien case ARM_FT_EXCEPTION: 744690075Sobrien asm_fprintf (f, "\t%@ ARM Exception Handler.\n"); 744790075Sobrien break; 744890075Sobrien } 744990075Sobrien 745090075Sobrien if (IS_NAKED (func_type)) 745190075Sobrien asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n"); 745290075Sobrien 745390075Sobrien if (IS_VOLATILE (func_type)) 745490075Sobrien asm_fprintf (f, "\t%@ Volatile: function does not return.\n"); 745590075Sobrien 745690075Sobrien if (IS_NESTED (func_type)) 745790075Sobrien asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n"); 745890075Sobrien 745990075Sobrien asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n", 746090075Sobrien current_function_args_size, 746190075Sobrien current_function_pretend_args_size, frame_size); 746290075Sobrien 746396263Sobrien asm_fprintf (f, "\t%@ frame_needed = %d, uses_anonymous_args = %d\n", 746490075Sobrien frame_pointer_needed, 746596263Sobrien cfun->machine->uses_anonymous_args); 746690075Sobrien 746790075Sobrien if (cfun->machine->lr_save_eliminated) 746890075Sobrien asm_fprintf (f, "\t%@ link register save eliminated.\n"); 746990075Sobrien 747090075Sobrien#ifdef AOF_ASSEMBLER 747190075Sobrien if (flag_pic) 747290075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM); 747390075Sobrien#endif 747490075Sobrien 747590075Sobrien return_used_this_function = 0; 747690075Sobrien} 747790075Sobrien 747890075Sobrienconst char * 747990075Sobrienarm_output_epilogue (really_return) 748090075Sobrien int really_return; 748190075Sobrien{ 748290075Sobrien int reg; 748390075Sobrien unsigned long saved_regs_mask; 748490075Sobrien unsigned long func_type; 748596263Sobrien /* Floats_offset is the offset from the "virtual" frame. In an APCS 748696263Sobrien frame that is $fp + 4 for a non-variadic function. */ 748796263Sobrien int floats_offset = 0; 748890075Sobrien rtx operands[3]; 748990075Sobrien int frame_size = get_frame_size (); 749090075Sobrien FILE * f = asm_out_file; 749190075Sobrien rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; 749290075Sobrien 749390075Sobrien /* If we have already generated the return instruction 749490075Sobrien then it is futile to generate anything else. */ 749590075Sobrien if (use_return_insn (FALSE) && return_used_this_function) 749690075Sobrien return ""; 749790075Sobrien 749890075Sobrien func_type = arm_current_func_type (); 749990075Sobrien 750090075Sobrien if (IS_NAKED (func_type)) 750190075Sobrien /* Naked functions don't have epilogues. */ 750290075Sobrien return ""; 750390075Sobrien 750490075Sobrien if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) 750590075Sobrien { 750690075Sobrien rtx op; 750790075Sobrien 750890075Sobrien /* A volatile function should never return. Call abort. */ 750990075Sobrien op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort"); 751090075Sobrien assemble_external_libcall (op); 751190075Sobrien output_asm_insn ("bl\t%a0", &op); 751290075Sobrien 751390075Sobrien return ""; 751490075Sobrien } 751590075Sobrien 751690075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER 751790075Sobrien && ! really_return) 751890075Sobrien /* If we are throwing an exception, then we really must 751990075Sobrien be doing a return, so we can't tail-call. */ 752090075Sobrien abort (); 752190075Sobrien 752290075Sobrien saved_regs_mask = arm_compute_save_reg_mask (); 752390075Sobrien 752496263Sobrien /* XXX We should adjust floats_offset for any anonymous args, and then 752596263Sobrien re-adjust vfp_offset below to compensate. */ 752696263Sobrien 752790075Sobrien /* Compute how far away the floats will be. */ 752890075Sobrien for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++) 752990075Sobrien if (saved_regs_mask & (1 << reg)) 753090075Sobrien floats_offset += 4; 753190075Sobrien 753290075Sobrien if (frame_pointer_needed) 753390075Sobrien { 753496263Sobrien int vfp_offset = 4; 753596263Sobrien 753690075Sobrien if (arm_fpu_arch == FP_SOFT2) 753790075Sobrien { 753890075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 753990075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 754090075Sobrien { 754190075Sobrien floats_offset += 12; 754290075Sobrien asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n", 754396263Sobrien reg, FP_REGNUM, floats_offset - vfp_offset); 754490075Sobrien } 754590075Sobrien } 754690075Sobrien else 754790075Sobrien { 754890075Sobrien int start_reg = LAST_ARM_FP_REGNUM; 754990075Sobrien 755090075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 755190075Sobrien { 755290075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 755390075Sobrien { 755490075Sobrien floats_offset += 12; 755590075Sobrien 755690075Sobrien /* We can't unstack more than four registers at once. */ 755790075Sobrien if (start_reg - reg == 3) 755890075Sobrien { 755990075Sobrien asm_fprintf (f, "\tlfm\t%r, 4, [%r, #-%d]\n", 756096263Sobrien reg, FP_REGNUM, floats_offset - vfp_offset); 756190075Sobrien start_reg = reg - 1; 756290075Sobrien } 756390075Sobrien } 756490075Sobrien else 756590075Sobrien { 756690075Sobrien if (reg != start_reg) 756790075Sobrien asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", 756890075Sobrien reg + 1, start_reg - reg, 756996263Sobrien FP_REGNUM, floats_offset - vfp_offset); 757090075Sobrien start_reg = reg - 1; 757190075Sobrien } 757290075Sobrien } 757390075Sobrien 757490075Sobrien /* Just in case the last register checked also needs unstacking. */ 757590075Sobrien if (reg != start_reg) 757690075Sobrien asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", 757790075Sobrien reg + 1, start_reg - reg, 757896263Sobrien FP_REGNUM, floats_offset - vfp_offset); 757990075Sobrien } 758090075Sobrien 758190075Sobrien /* saved_regs_mask should contain the IP, which at the time of stack 758290075Sobrien frame generation actually contains the old stack pointer. So a 758390075Sobrien quick way to unwind the stack is just pop the IP register directly 758490075Sobrien into the stack pointer. */ 758590075Sobrien if ((saved_regs_mask & (1 << IP_REGNUM)) == 0) 758690075Sobrien abort (); 758790075Sobrien saved_regs_mask &= ~ (1 << IP_REGNUM); 758890075Sobrien saved_regs_mask |= (1 << SP_REGNUM); 758990075Sobrien 759090075Sobrien /* There are two registers left in saved_regs_mask - LR and PC. We 759190075Sobrien only need to restore the LR register (the return address), but to 759290075Sobrien save time we can load it directly into the PC, unless we need a 759390075Sobrien special function exit sequence, or we are not really returning. */ 759490075Sobrien if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL) 759590075Sobrien /* Delete the LR from the register mask, so that the LR on 759690075Sobrien the stack is loaded into the PC in the register mask. */ 759790075Sobrien saved_regs_mask &= ~ (1 << LR_REGNUM); 759890075Sobrien else 759990075Sobrien saved_regs_mask &= ~ (1 << PC_REGNUM); 760090075Sobrien 760190075Sobrien print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask); 760290075Sobrien 760390075Sobrien if (IS_INTERRUPT (func_type)) 760490075Sobrien /* Interrupt handlers will have pushed the 760590075Sobrien IP onto the stack, so restore it now. */ 760690075Sobrien print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, 1 << IP_REGNUM); 760790075Sobrien } 760890075Sobrien else 760990075Sobrien { 761090075Sobrien /* Restore stack pointer if necessary. */ 761190075Sobrien if (frame_size + current_function_outgoing_args_size != 0) 761290075Sobrien { 761390075Sobrien operands[0] = operands[1] = stack_pointer_rtx; 761490075Sobrien operands[2] = GEN_INT (frame_size 761590075Sobrien + current_function_outgoing_args_size); 761690075Sobrien output_add_immediate (operands); 761790075Sobrien } 761890075Sobrien 761990075Sobrien if (arm_fpu_arch == FP_SOFT2) 762090075Sobrien { 762190075Sobrien for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) 762290075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 762390075Sobrien asm_fprintf (f, "\tldfe\t%r, [%r], #12\n", 762490075Sobrien reg, SP_REGNUM); 762590075Sobrien } 762690075Sobrien else 762790075Sobrien { 762890075Sobrien int start_reg = FIRST_ARM_FP_REGNUM; 762990075Sobrien 763090075Sobrien for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) 763190075Sobrien { 763290075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 763390075Sobrien { 763490075Sobrien if (reg - start_reg == 3) 763590075Sobrien { 763690075Sobrien asm_fprintf (f, "\tlfmfd\t%r, 4, [%r]!\n", 763790075Sobrien start_reg, SP_REGNUM); 763890075Sobrien start_reg = reg + 1; 763990075Sobrien } 764090075Sobrien } 764190075Sobrien else 764290075Sobrien { 764390075Sobrien if (reg != start_reg) 764490075Sobrien asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", 764590075Sobrien start_reg, reg - start_reg, 764690075Sobrien SP_REGNUM); 764790075Sobrien 764890075Sobrien start_reg = reg + 1; 764990075Sobrien } 765090075Sobrien } 765190075Sobrien 765290075Sobrien /* Just in case the last register checked also needs unstacking. */ 765390075Sobrien if (reg != start_reg) 765490075Sobrien asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", 765590075Sobrien start_reg, reg - start_reg, SP_REGNUM); 765690075Sobrien } 765790075Sobrien 765890075Sobrien /* If we can, restore the LR into the PC. */ 765990075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL 766090075Sobrien && really_return 766190075Sobrien && current_function_pretend_args_size == 0 766290075Sobrien && saved_regs_mask & (1 << LR_REGNUM)) 766390075Sobrien { 766490075Sobrien saved_regs_mask &= ~ (1 << LR_REGNUM); 766590075Sobrien saved_regs_mask |= (1 << PC_REGNUM); 766690075Sobrien } 766790075Sobrien 766890075Sobrien /* Load the registers off the stack. If we only have one register 766990075Sobrien to load use the LDR instruction - it is faster. */ 767090075Sobrien if (saved_regs_mask == (1 << LR_REGNUM)) 767190075Sobrien { 767296263Sobrien /* The exception handler ignores the LR, so we do 767390075Sobrien not really need to load it off the stack. */ 767490075Sobrien if (eh_ofs) 767590075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM); 767690075Sobrien else 767790075Sobrien asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM); 767890075Sobrien } 767990075Sobrien else if (saved_regs_mask) 768090075Sobrien print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask); 768190075Sobrien 768290075Sobrien if (current_function_pretend_args_size) 768390075Sobrien { 768490075Sobrien /* Unwind the pre-pushed regs. */ 768590075Sobrien operands[0] = operands[1] = stack_pointer_rtx; 768690075Sobrien operands[2] = GEN_INT (current_function_pretend_args_size); 768790075Sobrien output_add_immediate (operands); 768890075Sobrien } 768990075Sobrien } 769090075Sobrien 769190075Sobrien#if 0 769290075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER) 769390075Sobrien /* Adjust the stack to remove the exception handler stuff. */ 769490075Sobrien asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM, 769590075Sobrien REGNO (eh_ofs)); 769690075Sobrien#endif 769790075Sobrien 769896263Sobrien if (! really_return 769996263Sobrien || (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL 770096263Sobrien && current_function_pretend_args_size == 0 770196263Sobrien && saved_regs_mask & (1 << PC_REGNUM))) 770290075Sobrien return ""; 770390075Sobrien 770490075Sobrien /* Generate the return instruction. */ 770590075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 770690075Sobrien { 770790075Sobrien case ARM_FT_EXCEPTION_HANDLER: 770890075Sobrien /* Even in 26-bit mode we do a mov (rather than a movs) 770990075Sobrien because we don't have the PSR bits set in the address. */ 771090075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM); 771190075Sobrien break; 771290075Sobrien 771390075Sobrien case ARM_FT_ISR: 771490075Sobrien case ARM_FT_FIQ: 771590075Sobrien asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM); 771690075Sobrien break; 771790075Sobrien 771890075Sobrien case ARM_FT_EXCEPTION: 771990075Sobrien asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); 772090075Sobrien break; 772190075Sobrien 772290075Sobrien case ARM_FT_INTERWORKED: 772390075Sobrien asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM); 772490075Sobrien break; 772590075Sobrien 772690075Sobrien default: 772790075Sobrien if (frame_pointer_needed) 772890075Sobrien /* If we used the frame pointer then the return adddress 772990075Sobrien will have been loaded off the stack directly into the 773090075Sobrien PC, so there is no need to issue a MOV instruction 773190075Sobrien here. */ 773290075Sobrien ; 773390075Sobrien else if (current_function_pretend_args_size == 0 773490075Sobrien && (saved_regs_mask & (1 << LR_REGNUM))) 773590075Sobrien /* Similarly we may have been able to load LR into the PC 773690075Sobrien even if we did not create a stack frame. */ 773790075Sobrien ; 773890075Sobrien else if (TARGET_APCS_32) 773990075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM); 774090075Sobrien else 774190075Sobrien asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); 774290075Sobrien break; 774390075Sobrien } 774490075Sobrien 774590075Sobrien return ""; 774690075Sobrien} 774790075Sobrien 774890075Sobrienstatic void 774990075Sobrienarm_output_function_epilogue (file, frame_size) 775090075Sobrien FILE *file ATTRIBUTE_UNUSED; 775190075Sobrien HOST_WIDE_INT frame_size; 775290075Sobrien{ 775390075Sobrien if (TARGET_THUMB) 775490075Sobrien { 775590075Sobrien /* ??? Probably not safe to set this here, since it assumes that a 775690075Sobrien function will be emitted as assembly immediately after we generate 775790075Sobrien RTL for it. This does not happen for inline functions. */ 775890075Sobrien return_used_this_function = 0; 775990075Sobrien } 776090075Sobrien else 776190075Sobrien { 776290075Sobrien if (use_return_insn (FALSE) 776390075Sobrien && return_used_this_function 776490075Sobrien && (frame_size + current_function_outgoing_args_size) != 0 776590075Sobrien && !frame_pointer_needed) 776690075Sobrien abort (); 776790075Sobrien 776890075Sobrien /* Reset the ARM-specific per-function variables. */ 776990075Sobrien after_arm_reorg = 0; 777090075Sobrien } 777190075Sobrien} 777290075Sobrien 777390075Sobrien/* Generate and emit an insn that we will recognize as a push_multi. 777490075Sobrien Unfortunately, since this insn does not reflect very well the actual 777590075Sobrien semantics of the operation, we need to annotate the insn for the benefit 777690075Sobrien of DWARF2 frame unwind information. */ 777790075Sobrien 777890075Sobrienstatic rtx 777990075Sobrienemit_multi_reg_push (mask) 778090075Sobrien int mask; 778190075Sobrien{ 778290075Sobrien int num_regs = 0; 778390075Sobrien int num_dwarf_regs; 778490075Sobrien int i, j; 778590075Sobrien rtx par; 778690075Sobrien rtx dwarf; 778790075Sobrien int dwarf_par_index; 778890075Sobrien rtx tmp, reg; 778990075Sobrien 779090075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 779190075Sobrien if (mask & (1 << i)) 779290075Sobrien num_regs++; 779390075Sobrien 779490075Sobrien if (num_regs == 0 || num_regs > 16) 779590075Sobrien abort (); 779690075Sobrien 779790075Sobrien /* We don't record the PC in the dwarf frame information. */ 779890075Sobrien num_dwarf_regs = num_regs; 779990075Sobrien if (mask & (1 << PC_REGNUM)) 780090075Sobrien num_dwarf_regs--; 780190075Sobrien 780290075Sobrien /* For the body of the insn we are going to generate an UNSPEC in 780390075Sobrien parallel with several USEs. This allows the insn to be recognised 780490075Sobrien by the push_multi pattern in the arm.md file. The insn looks 780590075Sobrien something like this: 780690075Sobrien 780790075Sobrien (parallel [ 780890075Sobrien (set (mem:BLK (pre_dec:BLK (reg:SI sp))) 780990075Sobrien (unspec:BLK [(reg:SI r4)] UNSPEC_PUSH_MULT)) 781090075Sobrien (use (reg:SI 11 fp)) 781190075Sobrien (use (reg:SI 12 ip)) 781290075Sobrien (use (reg:SI 14 lr)) 781390075Sobrien (use (reg:SI 15 pc)) 781490075Sobrien ]) 781590075Sobrien 781690075Sobrien For the frame note however, we try to be more explicit and actually 781790075Sobrien show each register being stored into the stack frame, plus a (single) 781890075Sobrien decrement of the stack pointer. We do it this way in order to be 781990075Sobrien friendly to the stack unwinding code, which only wants to see a single 782090075Sobrien stack decrement per instruction. The RTL we generate for the note looks 782190075Sobrien something like this: 782290075Sobrien 782390075Sobrien (sequence [ 782490075Sobrien (set (reg:SI sp) (plus:SI (reg:SI sp) (const_int -20))) 782590075Sobrien (set (mem:SI (reg:SI sp)) (reg:SI r4)) 782690075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 4))) (reg:SI fp)) 782790075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 8))) (reg:SI ip)) 782890075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 12))) (reg:SI lr)) 782990075Sobrien ]) 783090075Sobrien 783190075Sobrien This sequence is used both by the code to support stack unwinding for 783290075Sobrien exceptions handlers and the code to generate dwarf2 frame debugging. */ 783390075Sobrien 783490075Sobrien par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs)); 783590075Sobrien dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_dwarf_regs + 1)); 783690075Sobrien RTX_FRAME_RELATED_P (dwarf) = 1; 783790075Sobrien dwarf_par_index = 1; 783890075Sobrien 783990075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 784090075Sobrien { 784190075Sobrien if (mask & (1 << i)) 784290075Sobrien { 784390075Sobrien reg = gen_rtx_REG (SImode, i); 784490075Sobrien 784590075Sobrien XVECEXP (par, 0, 0) 784690075Sobrien = gen_rtx_SET (VOIDmode, 784790075Sobrien gen_rtx_MEM (BLKmode, 784890075Sobrien gen_rtx_PRE_DEC (BLKmode, 784990075Sobrien stack_pointer_rtx)), 785090075Sobrien gen_rtx_UNSPEC (BLKmode, 785190075Sobrien gen_rtvec (1, reg), 785290075Sobrien UNSPEC_PUSH_MULT)); 785390075Sobrien 785490075Sobrien if (i != PC_REGNUM) 785590075Sobrien { 785690075Sobrien tmp = gen_rtx_SET (VOIDmode, 785790075Sobrien gen_rtx_MEM (SImode, stack_pointer_rtx), 785890075Sobrien reg); 785990075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 786090075Sobrien XVECEXP (dwarf, 0, dwarf_par_index) = tmp; 786190075Sobrien dwarf_par_index++; 786290075Sobrien } 786390075Sobrien 786490075Sobrien break; 786590075Sobrien } 786690075Sobrien } 786790075Sobrien 786890075Sobrien for (j = 1, i++; j < num_regs; i++) 786990075Sobrien { 787090075Sobrien if (mask & (1 << i)) 787190075Sobrien { 787290075Sobrien reg = gen_rtx_REG (SImode, i); 787390075Sobrien 787490075Sobrien XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg); 787590075Sobrien 787690075Sobrien if (i != PC_REGNUM) 787790075Sobrien { 787890075Sobrien tmp = gen_rtx_SET (VOIDmode, 787990075Sobrien gen_rtx_MEM (SImode, 788090075Sobrien plus_constant (stack_pointer_rtx, 788190075Sobrien 4 * j)), 788290075Sobrien reg); 788390075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 788490075Sobrien XVECEXP (dwarf, 0, dwarf_par_index++) = tmp; 788590075Sobrien } 788690075Sobrien 788790075Sobrien j++; 788890075Sobrien } 788990075Sobrien } 789090075Sobrien 789190075Sobrien par = emit_insn (par); 789290075Sobrien 789390075Sobrien tmp = gen_rtx_SET (SImode, 789490075Sobrien stack_pointer_rtx, 789590075Sobrien gen_rtx_PLUS (SImode, 789690075Sobrien stack_pointer_rtx, 789790075Sobrien GEN_INT (-4 * num_regs))); 789890075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 789990075Sobrien XVECEXP (dwarf, 0, 0) = tmp; 790090075Sobrien 790190075Sobrien REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 790290075Sobrien REG_NOTES (par)); 790390075Sobrien return par; 790490075Sobrien} 790590075Sobrien 790690075Sobrienstatic rtx 790790075Sobrienemit_sfm (base_reg, count) 790890075Sobrien int base_reg; 790990075Sobrien int count; 791090075Sobrien{ 791190075Sobrien rtx par; 791290075Sobrien rtx dwarf; 791390075Sobrien rtx tmp, reg; 791490075Sobrien int i; 791590075Sobrien 791690075Sobrien par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); 791790075Sobrien dwarf = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); 791890075Sobrien RTX_FRAME_RELATED_P (dwarf) = 1; 791990075Sobrien 792090075Sobrien reg = gen_rtx_REG (XFmode, base_reg++); 792190075Sobrien 792290075Sobrien XVECEXP (par, 0, 0) 792390075Sobrien = gen_rtx_SET (VOIDmode, 792490075Sobrien gen_rtx_MEM (BLKmode, 792590075Sobrien gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), 792690075Sobrien gen_rtx_UNSPEC (BLKmode, 792790075Sobrien gen_rtvec (1, reg), 792890075Sobrien UNSPEC_PUSH_MULT)); 792990075Sobrien tmp 793090075Sobrien = gen_rtx_SET (VOIDmode, 793190075Sobrien gen_rtx_MEM (XFmode, 793290075Sobrien gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), 793390075Sobrien reg); 793490075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 793590075Sobrien XVECEXP (dwarf, 0, count - 1) = tmp; 793690075Sobrien 793790075Sobrien for (i = 1; i < count; i++) 793890075Sobrien { 793990075Sobrien reg = gen_rtx_REG (XFmode, base_reg++); 794090075Sobrien XVECEXP (par, 0, i) = gen_rtx_USE (VOIDmode, reg); 794190075Sobrien 794290075Sobrien tmp = gen_rtx_SET (VOIDmode, 794390075Sobrien gen_rtx_MEM (XFmode, 794490075Sobrien gen_rtx_PRE_DEC (BLKmode, 794590075Sobrien stack_pointer_rtx)), 794690075Sobrien reg); 794790075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 794890075Sobrien XVECEXP (dwarf, 0, count - i - 1) = tmp; 794990075Sobrien } 795090075Sobrien 795190075Sobrien par = emit_insn (par); 795290075Sobrien REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 795390075Sobrien REG_NOTES (par)); 795490075Sobrien return par; 795590075Sobrien} 795690075Sobrien 795790075Sobrien/* Compute the distance from register FROM to register TO. 795890075Sobrien These can be the arg pointer (26), the soft frame pointer (25), 795990075Sobrien the stack pointer (13) or the hard frame pointer (11). 796090075Sobrien Typical stack layout looks like this: 796190075Sobrien 796290075Sobrien old stack pointer -> | | 796390075Sobrien ---- 796490075Sobrien | | \ 796590075Sobrien | | saved arguments for 796690075Sobrien | | vararg functions 796790075Sobrien | | / 796890075Sobrien -- 796990075Sobrien hard FP & arg pointer -> | | \ 797090075Sobrien | | stack 797190075Sobrien | | frame 797290075Sobrien | | / 797390075Sobrien -- 797490075Sobrien | | \ 797590075Sobrien | | call saved 797690075Sobrien | | registers 797790075Sobrien soft frame pointer -> | | / 797890075Sobrien -- 797990075Sobrien | | \ 798090075Sobrien | | local 798190075Sobrien | | variables 798290075Sobrien | | / 798390075Sobrien -- 798490075Sobrien | | \ 798590075Sobrien | | outgoing 798690075Sobrien | | arguments 798790075Sobrien current stack pointer -> | | / 798890075Sobrien -- 798990075Sobrien 799090075Sobrien For a given funciton some or all of these stack compomnents 799190075Sobrien may not be needed, giving rise to the possibility of 799290075Sobrien eliminating some of the registers. 799390075Sobrien 799490075Sobrien The values returned by this function must reflect the behaviour 799590075Sobrien of arm_expand_prologue() and arm_compute_save_reg_mask(). 799690075Sobrien 799790075Sobrien The sign of the number returned reflects the direction of stack 799890075Sobrien growth, so the values are positive for all eliminations except 799990075Sobrien from the soft frame pointer to the hard frame pointer. */ 800090075Sobrien 800190075Sobrienunsigned int 800290075Sobrienarm_compute_initial_elimination_offset (from, to) 800390075Sobrien unsigned int from; 800490075Sobrien unsigned int to; 800590075Sobrien{ 800690075Sobrien unsigned int local_vars = (get_frame_size () + 3) & ~3; 800790075Sobrien unsigned int outgoing_args = current_function_outgoing_args_size; 800890075Sobrien unsigned int stack_frame; 800990075Sobrien unsigned int call_saved_registers; 801090075Sobrien unsigned long func_type; 801190075Sobrien 801290075Sobrien func_type = arm_current_func_type (); 801390075Sobrien 801490075Sobrien /* Volatile functions never return, so there is 801590075Sobrien no need to save call saved registers. */ 801690075Sobrien call_saved_registers = 0; 801790075Sobrien if (! IS_VOLATILE (func_type)) 801890075Sobrien { 801990075Sobrien unsigned int reg_mask; 802090075Sobrien unsigned int reg; 802190075Sobrien 802290075Sobrien /* Make sure that we compute which registers will be saved 802390075Sobrien on the stack using the same algorithm that is used by 802490075Sobrien arm_compute_save_reg_mask(). */ 802590075Sobrien reg_mask = arm_compute_save_reg0_reg12_mask (); 802690075Sobrien 802790075Sobrien /* Now count the number of bits set in save_reg_mask. 802890075Sobrien For each set bit we need 4 bytes of stack space. */ 802990075Sobrien while (reg_mask) 803090075Sobrien { 803190075Sobrien call_saved_registers += 4; 803290075Sobrien reg_mask = reg_mask & ~ (reg_mask & - reg_mask); 803390075Sobrien } 803490075Sobrien 803590075Sobrien if (regs_ever_live[LR_REGNUM] 803690075Sobrien /* If a stack frame is going to be created, the LR will 803790075Sobrien be saved as part of that, so we do not need to allow 803890075Sobrien for it here. */ 803990075Sobrien && ! frame_pointer_needed) 804090075Sobrien call_saved_registers += 4; 804190075Sobrien 804290075Sobrien /* If the hard floating point registers are going to be 804390075Sobrien used then they must be saved on the stack as well. 804490075Sobrien Each register occupies 12 bytes of stack space. */ 804590075Sobrien for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg ++) 804690075Sobrien if (regs_ever_live[reg] && ! call_used_regs[reg]) 804790075Sobrien call_saved_registers += 12; 804890075Sobrien } 804990075Sobrien 805090075Sobrien /* The stack frame contains 4 registers - the old frame pointer, 805190075Sobrien the old stack pointer, the return address and PC of the start 805290075Sobrien of the function. */ 805390075Sobrien stack_frame = frame_pointer_needed ? 16 : 0; 805490075Sobrien 805590075Sobrien /* OK, now we have enough information to compute the distances. 805690075Sobrien There must be an entry in these switch tables for each pair 805790075Sobrien of registers in ELIMINABLE_REGS, even if some of the entries 805890075Sobrien seem to be redundant or useless. */ 805990075Sobrien switch (from) 806090075Sobrien { 806190075Sobrien case ARG_POINTER_REGNUM: 806290075Sobrien switch (to) 806390075Sobrien { 806490075Sobrien case THUMB_HARD_FRAME_POINTER_REGNUM: 806590075Sobrien return 0; 806690075Sobrien 806790075Sobrien case FRAME_POINTER_REGNUM: 806890075Sobrien /* This is the reverse of the soft frame pointer 806990075Sobrien to hard frame pointer elimination below. */ 807090075Sobrien if (call_saved_registers == 0 && stack_frame == 0) 807190075Sobrien return 0; 807290075Sobrien return (call_saved_registers + stack_frame - 4); 807390075Sobrien 807490075Sobrien case ARM_HARD_FRAME_POINTER_REGNUM: 807590075Sobrien /* If there is no stack frame then the hard 807690075Sobrien frame pointer and the arg pointer coincide. */ 807790075Sobrien if (stack_frame == 0 && call_saved_registers != 0) 807890075Sobrien return 0; 807990075Sobrien /* FIXME: Not sure about this. Maybe we should always return 0 ? */ 808090075Sobrien return (frame_pointer_needed 808190075Sobrien && current_function_needs_context 808296263Sobrien && ! cfun->machine->uses_anonymous_args) ? 4 : 0; 808390075Sobrien 808490075Sobrien case STACK_POINTER_REGNUM: 808590075Sobrien /* If nothing has been pushed on the stack at all 808690075Sobrien then this will return -4. This *is* correct! */ 808790075Sobrien return call_saved_registers + stack_frame + local_vars + outgoing_args - 4; 808890075Sobrien 808990075Sobrien default: 809090075Sobrien abort (); 809190075Sobrien } 809290075Sobrien break; 809390075Sobrien 809490075Sobrien case FRAME_POINTER_REGNUM: 809590075Sobrien switch (to) 809690075Sobrien { 809790075Sobrien case THUMB_HARD_FRAME_POINTER_REGNUM: 809890075Sobrien return 0; 809990075Sobrien 810090075Sobrien case ARM_HARD_FRAME_POINTER_REGNUM: 810190075Sobrien /* The hard frame pointer points to the top entry in the 810290075Sobrien stack frame. The soft frame pointer to the bottom entry 810390075Sobrien in the stack frame. If there is no stack frame at all, 810490075Sobrien then they are identical. */ 810590075Sobrien if (call_saved_registers == 0 && stack_frame == 0) 810690075Sobrien return 0; 810790075Sobrien return - (call_saved_registers + stack_frame - 4); 810890075Sobrien 810990075Sobrien case STACK_POINTER_REGNUM: 811090075Sobrien return local_vars + outgoing_args; 811190075Sobrien 811290075Sobrien default: 811390075Sobrien abort (); 811490075Sobrien } 811590075Sobrien break; 811690075Sobrien 811790075Sobrien default: 811890075Sobrien /* You cannot eliminate from the stack pointer. 811990075Sobrien In theory you could eliminate from the hard frame 812090075Sobrien pointer to the stack pointer, but this will never 812190075Sobrien happen, since if a stack frame is not needed the 812290075Sobrien hard frame pointer will never be used. */ 812390075Sobrien abort (); 812490075Sobrien } 812590075Sobrien} 812690075Sobrien 812790075Sobrien/* Generate the prologue instructions for entry into an ARM function. */ 812890075Sobrien 812990075Sobrienvoid 813090075Sobrienarm_expand_prologue () 813190075Sobrien{ 813290075Sobrien int reg; 813390075Sobrien rtx amount; 813490075Sobrien rtx insn; 813590075Sobrien rtx ip_rtx; 813690075Sobrien unsigned long live_regs_mask; 813790075Sobrien unsigned long func_type; 813890075Sobrien int fp_offset = 0; 813990075Sobrien int saved_pretend_args = 0; 814090075Sobrien unsigned int args_to_push; 814190075Sobrien 814290075Sobrien func_type = arm_current_func_type (); 814390075Sobrien 814490075Sobrien /* Naked functions don't have prologues. */ 814590075Sobrien if (IS_NAKED (func_type)) 814690075Sobrien return; 814790075Sobrien 814890075Sobrien /* Make a copy of c_f_p_a_s as we may need to modify it locally. */ 814990075Sobrien args_to_push = current_function_pretend_args_size; 815090075Sobrien 815190075Sobrien /* Compute which register we will have to save onto the stack. */ 815290075Sobrien live_regs_mask = arm_compute_save_reg_mask (); 815390075Sobrien 815490075Sobrien ip_rtx = gen_rtx_REG (SImode, IP_REGNUM); 815590075Sobrien 815690075Sobrien if (frame_pointer_needed) 815790075Sobrien { 815890075Sobrien if (IS_INTERRUPT (func_type)) 815990075Sobrien { 816090075Sobrien /* Interrupt functions must not corrupt any registers. 816190075Sobrien Creating a frame pointer however, corrupts the IP 816290075Sobrien register, so we must push it first. */ 816390075Sobrien insn = emit_multi_reg_push (1 << IP_REGNUM); 816490075Sobrien 816590075Sobrien /* Do not set RTX_FRAME_RELATED_P on this insn. 816690075Sobrien The dwarf stack unwinding code only wants to see one 816790075Sobrien stack decrement per function, and this is not it. If 816890075Sobrien this instruction is labeled as being part of the frame 816990075Sobrien creation sequence then dwarf2out_frame_debug_expr will 817090075Sobrien abort when it encounters the assignment of IP to FP 817190075Sobrien later on, since the use of SP here establishes SP as 817290075Sobrien the CFA register and not IP. 817390075Sobrien 817490075Sobrien Anyway this instruction is not really part of the stack 817590075Sobrien frame creation although it is part of the prologue. */ 817690075Sobrien } 817790075Sobrien else if (IS_NESTED (func_type)) 817890075Sobrien { 817990075Sobrien /* The Static chain register is the same as the IP register 818090075Sobrien used as a scratch register during stack frame creation. 818190075Sobrien To get around this need to find somewhere to store IP 818290075Sobrien whilst the frame is being created. We try the following 818390075Sobrien places in order: 818490075Sobrien 818590075Sobrien 1. The last argument register. 818690075Sobrien 2. A slot on the stack above the frame. (This only 818790075Sobrien works if the function is not a varargs function). 818890075Sobrien 3. Register r3, after pushing the argument registers 818990075Sobrien onto the stack. 819090075Sobrien 819190075Sobrien Note - we only need to tell the dwarf2 backend about the SP 819290075Sobrien adjustment in the second variant; the static chain register 819390075Sobrien doesn't need to be unwound, as it doesn't contain a value 819490075Sobrien inherited from the caller. */ 819590075Sobrien 819690075Sobrien if (regs_ever_live[3] == 0) 819790075Sobrien { 819890075Sobrien insn = gen_rtx_REG (SImode, 3); 819990075Sobrien insn = gen_rtx_SET (SImode, insn, ip_rtx); 820090075Sobrien insn = emit_insn (insn); 820190075Sobrien } 820290075Sobrien else if (args_to_push == 0) 820390075Sobrien { 820490075Sobrien rtx dwarf; 820590075Sobrien insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx); 820690075Sobrien insn = gen_rtx_MEM (SImode, insn); 820790075Sobrien insn = gen_rtx_SET (VOIDmode, insn, ip_rtx); 820890075Sobrien insn = emit_insn (insn); 820990075Sobrien 821090075Sobrien fp_offset = 4; 821190075Sobrien 821290075Sobrien /* Just tell the dwarf backend that we adjusted SP. */ 821390075Sobrien dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx, 821490075Sobrien gen_rtx_PLUS (SImode, stack_pointer_rtx, 821590075Sobrien GEN_INT (-fp_offset))); 821690075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 821790075Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, 821890075Sobrien dwarf, REG_NOTES (insn)); 821990075Sobrien } 822090075Sobrien else 822190075Sobrien { 822290075Sobrien /* Store the args on the stack. */ 822396263Sobrien if (cfun->machine->uses_anonymous_args) 822490075Sobrien insn = emit_multi_reg_push 822590075Sobrien ((0xf0 >> (args_to_push / 4)) & 0xf); 822690075Sobrien else 822790075Sobrien insn = emit_insn 822890075Sobrien (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 822990075Sobrien GEN_INT (- args_to_push))); 823090075Sobrien 823190075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 823290075Sobrien 823390075Sobrien saved_pretend_args = 1; 823490075Sobrien fp_offset = args_to_push; 823590075Sobrien args_to_push = 0; 823690075Sobrien 823790075Sobrien /* Now reuse r3 to preserve IP. */ 823890075Sobrien insn = gen_rtx_REG (SImode, 3); 823990075Sobrien insn = gen_rtx_SET (SImode, insn, ip_rtx); 824090075Sobrien (void) emit_insn (insn); 824190075Sobrien } 824290075Sobrien } 824390075Sobrien 824490075Sobrien if (fp_offset) 824590075Sobrien { 824690075Sobrien insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset)); 824790075Sobrien insn = gen_rtx_SET (SImode, ip_rtx, insn); 824890075Sobrien } 824990075Sobrien else 825090075Sobrien insn = gen_movsi (ip_rtx, stack_pointer_rtx); 825190075Sobrien 825290075Sobrien insn = emit_insn (insn); 825390075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 825490075Sobrien } 825590075Sobrien 825690075Sobrien if (args_to_push) 825790075Sobrien { 825890075Sobrien /* Push the argument registers, or reserve space for them. */ 825996263Sobrien if (cfun->machine->uses_anonymous_args) 826090075Sobrien insn = emit_multi_reg_push 826190075Sobrien ((0xf0 >> (args_to_push / 4)) & 0xf); 826290075Sobrien else 826390075Sobrien insn = emit_insn 826490075Sobrien (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 826590075Sobrien GEN_INT (- args_to_push))); 826690075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 826790075Sobrien } 826890075Sobrien 826996263Sobrien /* If this is an interrupt service routine, and the link register is 827096263Sobrien going to be pushed, subtracting four now will mean that the 827196263Sobrien function return can be done with a single instruction. */ 827296263Sobrien if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ) 827396263Sobrien && (live_regs_mask & (1 << LR_REGNUM)) != 0) 827496263Sobrien { 827596263Sobrien emit_insn (gen_rtx_SET (SImode, 827696263Sobrien gen_rtx_REG (SImode, LR_REGNUM), 827796263Sobrien gen_rtx_PLUS (SImode, 827896263Sobrien gen_rtx_REG (SImode, LR_REGNUM), 827996263Sobrien GEN_INT (-4)))); 828096263Sobrien } 828196263Sobrien 828290075Sobrien if (live_regs_mask) 828390075Sobrien { 828490075Sobrien insn = emit_multi_reg_push (live_regs_mask); 828590075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 828690075Sobrien } 828790075Sobrien 828890075Sobrien if (! IS_VOLATILE (func_type)) 828990075Sobrien { 829090075Sobrien /* Save any floating point call-saved registers used by this function. */ 829190075Sobrien if (arm_fpu_arch == FP_SOFT2) 829290075Sobrien { 829390075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --) 829490075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 829590075Sobrien { 829690075Sobrien insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx); 829790075Sobrien insn = gen_rtx_MEM (XFmode, insn); 829890075Sobrien insn = emit_insn (gen_rtx_SET (VOIDmode, insn, 829990075Sobrien gen_rtx_REG (XFmode, reg))); 830090075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 830190075Sobrien } 830290075Sobrien } 830390075Sobrien else 830490075Sobrien { 830590075Sobrien int start_reg = LAST_ARM_FP_REGNUM; 830690075Sobrien 830790075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --) 830890075Sobrien { 830990075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 831090075Sobrien { 831190075Sobrien if (start_reg - reg == 3) 831290075Sobrien { 831390075Sobrien insn = emit_sfm (reg, 4); 831490075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 831590075Sobrien start_reg = reg - 1; 831690075Sobrien } 831790075Sobrien } 831890075Sobrien else 831990075Sobrien { 832090075Sobrien if (start_reg != reg) 832190075Sobrien { 832290075Sobrien insn = emit_sfm (reg + 1, start_reg - reg); 832390075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 832490075Sobrien } 832590075Sobrien start_reg = reg - 1; 832690075Sobrien } 832790075Sobrien } 832890075Sobrien 832990075Sobrien if (start_reg != reg) 833090075Sobrien { 833190075Sobrien insn = emit_sfm (reg + 1, start_reg - reg); 833290075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 833390075Sobrien } 833490075Sobrien } 833590075Sobrien } 833690075Sobrien 833790075Sobrien if (frame_pointer_needed) 833890075Sobrien { 833990075Sobrien /* Create the new frame pointer. */ 834090075Sobrien insn = GEN_INT (-(4 + args_to_push + fp_offset)); 834190075Sobrien insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn)); 834290075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 834390075Sobrien 834490075Sobrien if (IS_NESTED (func_type)) 834590075Sobrien { 834690075Sobrien /* Recover the static chain register. */ 834790075Sobrien if (regs_ever_live [3] == 0 834890075Sobrien || saved_pretend_args) 834990075Sobrien insn = gen_rtx_REG (SImode, 3); 835090075Sobrien else /* if (current_function_pretend_args_size == 0) */ 835190075Sobrien { 835290075Sobrien insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, GEN_INT (4)); 835390075Sobrien insn = gen_rtx_MEM (SImode, insn); 835490075Sobrien } 835590075Sobrien 835690075Sobrien emit_insn (gen_rtx_SET (SImode, ip_rtx, insn)); 835790075Sobrien /* Add a USE to stop propagate_one_insn() from barfing. */ 835890075Sobrien emit_insn (gen_prologue_use (ip_rtx)); 835990075Sobrien } 836090075Sobrien } 836190075Sobrien 836290075Sobrien amount = GEN_INT (-(get_frame_size () 836390075Sobrien + current_function_outgoing_args_size)); 836490075Sobrien 836590075Sobrien if (amount != const0_rtx) 836690075Sobrien { 836790075Sobrien /* This add can produce multiple insns for a large constant, so we 836890075Sobrien need to get tricky. */ 836990075Sobrien rtx last = get_last_insn (); 837090075Sobrien insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 837190075Sobrien amount)); 837290075Sobrien do 837390075Sobrien { 837490075Sobrien last = last ? NEXT_INSN (last) : get_insns (); 837590075Sobrien RTX_FRAME_RELATED_P (last) = 1; 837690075Sobrien } 837790075Sobrien while (last != insn); 837890075Sobrien 837990075Sobrien /* If the frame pointer is needed, emit a special barrier that 838090075Sobrien will prevent the scheduler from moving stores to the frame 838190075Sobrien before the stack adjustment. */ 838290075Sobrien if (frame_pointer_needed) 838390075Sobrien { 838490075Sobrien rtx unspec = gen_rtx_UNSPEC (SImode, 838590075Sobrien gen_rtvec (2, stack_pointer_rtx, 838690075Sobrien hard_frame_pointer_rtx), 838790075Sobrien UNSPEC_PRLG_STK); 838890075Sobrien 838990075Sobrien insn = emit_insn (gen_rtx_CLOBBER (VOIDmode, 839090075Sobrien gen_rtx_MEM (BLKmode, unspec))); 839190075Sobrien } 839290075Sobrien } 839390075Sobrien 839490075Sobrien /* If we are profiling, make sure no instructions are scheduled before 839590075Sobrien the call to mcount. Similarly if the user has requested no 839690075Sobrien scheduling in the prolog. */ 839790075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 839890075Sobrien emit_insn (gen_blockage ()); 839990075Sobrien 840090075Sobrien /* If the link register is being kept alive, with the return address in it, 840190075Sobrien then make sure that it does not get reused by the ce2 pass. */ 840290075Sobrien if ((live_regs_mask & (1 << LR_REGNUM)) == 0) 840390075Sobrien { 840490075Sobrien emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM))); 840590075Sobrien cfun->machine->lr_save_eliminated = 1; 840690075Sobrien } 840790075Sobrien} 840890075Sobrien 840990075Sobrien/* If CODE is 'd', then the X is a condition operand and the instruction 841090075Sobrien should only be executed if the condition is true. 841190075Sobrien if CODE is 'D', then the X is a condition operand and the instruction 841290075Sobrien should only be executed if the condition is false: however, if the mode 841390075Sobrien of the comparison is CCFPEmode, then always execute the instruction -- we 841490075Sobrien do this because in these circumstances !GE does not necessarily imply LT; 841590075Sobrien in these cases the instruction pattern will take care to make sure that 841690075Sobrien an instruction containing %d will follow, thereby undoing the effects of 841790075Sobrien doing this instruction unconditionally. 841890075Sobrien If CODE is 'N' then X is a floating point operand that must be negated 841990075Sobrien before output. 842090075Sobrien If CODE is 'B' then output a bitwise inverted value of X (a const int). 842190075Sobrien If X is a REG and CODE is `M', output a ldm/stm style multi-reg. */ 842290075Sobrien 842390075Sobrienvoid 842490075Sobrienarm_print_operand (stream, x, code) 842590075Sobrien FILE * stream; 842690075Sobrien rtx x; 842790075Sobrien int code; 842890075Sobrien{ 842990075Sobrien switch (code) 843090075Sobrien { 843190075Sobrien case '@': 843290075Sobrien fputs (ASM_COMMENT_START, stream); 843390075Sobrien return; 843490075Sobrien 843590075Sobrien case '_': 843690075Sobrien fputs (user_label_prefix, stream); 843790075Sobrien return; 843890075Sobrien 843990075Sobrien case '|': 844090075Sobrien fputs (REGISTER_PREFIX, stream); 844190075Sobrien return; 844290075Sobrien 844390075Sobrien case '?': 844490075Sobrien if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) 844590075Sobrien { 844690075Sobrien if (TARGET_THUMB || current_insn_predicate != NULL) 844790075Sobrien abort (); 844890075Sobrien 844990075Sobrien fputs (arm_condition_codes[arm_current_cc], stream); 845090075Sobrien } 845190075Sobrien else if (current_insn_predicate) 845290075Sobrien { 845390075Sobrien enum arm_cond_code code; 845490075Sobrien 845590075Sobrien if (TARGET_THUMB) 845690075Sobrien abort (); 845790075Sobrien 845890075Sobrien code = get_arm_condition_code (current_insn_predicate); 845990075Sobrien fputs (arm_condition_codes[code], stream); 846090075Sobrien } 846190075Sobrien return; 846290075Sobrien 846390075Sobrien case 'N': 846490075Sobrien { 846590075Sobrien REAL_VALUE_TYPE r; 846690075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 846790075Sobrien r = REAL_VALUE_NEGATE (r); 846890075Sobrien fprintf (stream, "%s", fp_const_from_val (&r)); 846990075Sobrien } 847090075Sobrien return; 847190075Sobrien 847290075Sobrien case 'B': 847390075Sobrien if (GET_CODE (x) == CONST_INT) 847490075Sobrien { 847590075Sobrien HOST_WIDE_INT val; 847690075Sobrien val = ARM_SIGN_EXTEND (~INTVAL (x)); 847790075Sobrien fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val); 847890075Sobrien } 847990075Sobrien else 848090075Sobrien { 848190075Sobrien putc ('~', stream); 848290075Sobrien output_addr_const (stream, x); 848390075Sobrien } 848490075Sobrien return; 848590075Sobrien 848690075Sobrien case 'i': 848790075Sobrien fprintf (stream, "%s", arithmetic_instr (x, 1)); 848890075Sobrien return; 848990075Sobrien 849090075Sobrien case 'I': 849190075Sobrien fprintf (stream, "%s", arithmetic_instr (x, 0)); 849290075Sobrien return; 849390075Sobrien 849490075Sobrien case 'S': 849590075Sobrien { 849690075Sobrien HOST_WIDE_INT val; 849790075Sobrien const char * shift = shift_op (x, &val); 849890075Sobrien 849990075Sobrien if (shift) 850090075Sobrien { 850190075Sobrien fprintf (stream, ", %s ", shift_op (x, &val)); 850290075Sobrien if (val == -1) 850390075Sobrien arm_print_operand (stream, XEXP (x, 1), 0); 850490075Sobrien else 850590075Sobrien { 850690075Sobrien fputc ('#', stream); 850790075Sobrien fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val); 850890075Sobrien } 850990075Sobrien } 851090075Sobrien } 851190075Sobrien return; 851290075Sobrien 851390075Sobrien /* An explanation of the 'Q', 'R' and 'H' register operands: 851490075Sobrien 851590075Sobrien In a pair of registers containing a DI or DF value the 'Q' 851690075Sobrien operand returns the register number of the register containing 851790075Sobrien the least signficant part of the value. The 'R' operand returns 851890075Sobrien the register number of the register containing the most 851990075Sobrien significant part of the value. 852090075Sobrien 852190075Sobrien The 'H' operand returns the higher of the two register numbers. 852290075Sobrien On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the 852390075Sobrien same as the 'Q' operand, since the most signficant part of the 852490075Sobrien value is held in the lower number register. The reverse is true 852590075Sobrien on systems where WORDS_BIG_ENDIAN is false. 852690075Sobrien 852790075Sobrien The purpose of these operands is to distinguish between cases 852890075Sobrien where the endian-ness of the values is important (for example 852990075Sobrien when they are added together), and cases where the endian-ness 853090075Sobrien is irrelevant, but the order of register operations is important. 853190075Sobrien For example when loading a value from memory into a register 853290075Sobrien pair, the endian-ness does not matter. Provided that the value 853390075Sobrien from the lower memory address is put into the lower numbered 853490075Sobrien register, and the value from the higher address is put into the 853590075Sobrien higher numbered register, the load will work regardless of whether 853690075Sobrien the value being loaded is big-wordian or little-wordian. The 853790075Sobrien order of the two register loads can matter however, if the address 853890075Sobrien of the memory location is actually held in one of the registers 853990075Sobrien being overwritten by the load. */ 854090075Sobrien case 'Q': 854190075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 854290075Sobrien abort (); 854390075Sobrien asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)); 854490075Sobrien return; 854590075Sobrien 854690075Sobrien case 'R': 854790075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 854890075Sobrien abort (); 854990075Sobrien asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)); 855090075Sobrien return; 855190075Sobrien 855290075Sobrien case 'H': 855390075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 855490075Sobrien abort (); 855590075Sobrien asm_fprintf (stream, "%r", REGNO (x) + 1); 855690075Sobrien return; 855790075Sobrien 855890075Sobrien case 'm': 855990075Sobrien asm_fprintf (stream, "%r", 856090075Sobrien GET_CODE (XEXP (x, 0)) == REG 856190075Sobrien ? REGNO (XEXP (x, 0)) : REGNO (XEXP (XEXP (x, 0), 0))); 856290075Sobrien return; 856390075Sobrien 856490075Sobrien case 'M': 856590075Sobrien asm_fprintf (stream, "{%r-%r}", 856690075Sobrien REGNO (x), 856790075Sobrien REGNO (x) + NUM_REGS (GET_MODE (x)) - 1); 856890075Sobrien return; 856990075Sobrien 857090075Sobrien case 'd': 857190075Sobrien if (!x) 857290075Sobrien return; 857390075Sobrien 857490075Sobrien if (TARGET_ARM) 857590075Sobrien fputs (arm_condition_codes[get_arm_condition_code (x)], 857690075Sobrien stream); 857790075Sobrien else 857890075Sobrien fputs (thumb_condition_code (x, 0), stream); 857990075Sobrien return; 858090075Sobrien 858190075Sobrien case 'D': 858290075Sobrien if (!x) 858390075Sobrien return; 858490075Sobrien 858590075Sobrien if (TARGET_ARM) 858690075Sobrien fputs (arm_condition_codes[ARM_INVERSE_CONDITION_CODE 858790075Sobrien (get_arm_condition_code (x))], 858890075Sobrien stream); 858990075Sobrien else 859090075Sobrien fputs (thumb_condition_code (x, 1), stream); 859190075Sobrien return; 859290075Sobrien 859390075Sobrien default: 859490075Sobrien if (x == 0) 859590075Sobrien abort (); 859690075Sobrien 859790075Sobrien if (GET_CODE (x) == REG) 859890075Sobrien asm_fprintf (stream, "%r", REGNO (x)); 859990075Sobrien else if (GET_CODE (x) == MEM) 860090075Sobrien { 860190075Sobrien output_memory_reference_mode = GET_MODE (x); 860290075Sobrien output_address (XEXP (x, 0)); 860390075Sobrien } 860490075Sobrien else if (GET_CODE (x) == CONST_DOUBLE) 860590075Sobrien fprintf (stream, "#%s", fp_immediate_constant (x)); 860690075Sobrien else if (GET_CODE (x) == NEG) 860790075Sobrien abort (); /* This should never happen now. */ 860890075Sobrien else 860990075Sobrien { 861090075Sobrien fputc ('#', stream); 861190075Sobrien output_addr_const (stream, x); 861290075Sobrien } 861390075Sobrien } 861490075Sobrien} 861590075Sobrien 861690075Sobrien#ifndef AOF_ASSEMBLER 861790075Sobrien/* Target hook for assembling integer objects. The ARM version needs to 861890075Sobrien handle word-sized values specially. */ 861990075Sobrien 862090075Sobrienstatic bool 862190075Sobrienarm_assemble_integer (x, size, aligned_p) 862290075Sobrien rtx x; 862390075Sobrien unsigned int size; 862490075Sobrien int aligned_p; 862590075Sobrien{ 862690075Sobrien if (size == UNITS_PER_WORD && aligned_p) 862790075Sobrien { 862890075Sobrien fputs ("\t.word\t", asm_out_file); 862990075Sobrien output_addr_const (asm_out_file, x); 863090075Sobrien 863190075Sobrien /* Mark symbols as position independent. We only do this in the 863290075Sobrien .text segment, not in the .data segment. */ 863390075Sobrien if (NEED_GOT_RELOC && flag_pic && making_const_table && 863490075Sobrien (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)) 863590075Sobrien { 863696263Sobrien if (GET_CODE (x) == SYMBOL_REF 863796263Sobrien && (CONSTANT_POOL_ADDRESS_P (x) 863896263Sobrien || ENCODED_SHORT_CALL_ATTR_P (XSTR (x, 0)))) 863990075Sobrien fputs ("(GOTOFF)", asm_out_file); 864090075Sobrien else if (GET_CODE (x) == LABEL_REF) 864190075Sobrien fputs ("(GOTOFF)", asm_out_file); 864290075Sobrien else 864390075Sobrien fputs ("(GOT)", asm_out_file); 864490075Sobrien } 864590075Sobrien fputc ('\n', asm_out_file); 864690075Sobrien return true; 864790075Sobrien } 864890075Sobrien 864990075Sobrien return default_assemble_integer (x, size, aligned_p); 865090075Sobrien} 865190075Sobrien#endif 865290075Sobrien 865390075Sobrien/* A finite state machine takes care of noticing whether or not instructions 865490075Sobrien can be conditionally executed, and thus decrease execution time and code 865590075Sobrien size by deleting branch instructions. The fsm is controlled by 865690075Sobrien final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. */ 865790075Sobrien 865890075Sobrien/* The state of the fsm controlling condition codes are: 865990075Sobrien 0: normal, do nothing special 866090075Sobrien 1: make ASM_OUTPUT_OPCODE not output this instruction 866190075Sobrien 2: make ASM_OUTPUT_OPCODE not output this instruction 866290075Sobrien 3: make instructions conditional 866390075Sobrien 4: make instructions conditional 866490075Sobrien 866590075Sobrien State transitions (state->state by whom under condition): 866690075Sobrien 0 -> 1 final_prescan_insn if the `target' is a label 866790075Sobrien 0 -> 2 final_prescan_insn if the `target' is an unconditional branch 866890075Sobrien 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch 866990075Sobrien 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch 867090075Sobrien 3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached 867190075Sobrien (the target label has CODE_LABEL_NUMBER equal to arm_target_label). 867290075Sobrien 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached 867390075Sobrien (the target insn is arm_target_insn). 867490075Sobrien 867590075Sobrien If the jump clobbers the conditions then we use states 2 and 4. 867690075Sobrien 867790075Sobrien A similar thing can be done with conditional return insns. 867890075Sobrien 867990075Sobrien XXX In case the `target' is an unconditional branch, this conditionalising 868090075Sobrien of the instructions always reduces code size, but not always execution 868190075Sobrien time. But then, I want to reduce the code size to somewhere near what 868290075Sobrien /bin/cc produces. */ 868390075Sobrien 868490075Sobrien/* Returns the index of the ARM condition code string in 868590075Sobrien `arm_condition_codes'. COMPARISON should be an rtx like 868690075Sobrien `(eq (...) (...))'. */ 868790075Sobrien 868890075Sobrienstatic enum arm_cond_code 868990075Sobrienget_arm_condition_code (comparison) 869090075Sobrien rtx comparison; 869190075Sobrien{ 869290075Sobrien enum machine_mode mode = GET_MODE (XEXP (comparison, 0)); 869390075Sobrien int code; 869490075Sobrien enum rtx_code comp_code = GET_CODE (comparison); 869590075Sobrien 869690075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 869790075Sobrien mode = SELECT_CC_MODE (comp_code, XEXP (comparison, 0), 869890075Sobrien XEXP (comparison, 1)); 869990075Sobrien 870090075Sobrien switch (mode) 870190075Sobrien { 870290075Sobrien case CC_DNEmode: code = ARM_NE; goto dominance; 870390075Sobrien case CC_DEQmode: code = ARM_EQ; goto dominance; 870490075Sobrien case CC_DGEmode: code = ARM_GE; goto dominance; 870590075Sobrien case CC_DGTmode: code = ARM_GT; goto dominance; 870690075Sobrien case CC_DLEmode: code = ARM_LE; goto dominance; 870790075Sobrien case CC_DLTmode: code = ARM_LT; goto dominance; 870890075Sobrien case CC_DGEUmode: code = ARM_CS; goto dominance; 870990075Sobrien case CC_DGTUmode: code = ARM_HI; goto dominance; 871090075Sobrien case CC_DLEUmode: code = ARM_LS; goto dominance; 871190075Sobrien case CC_DLTUmode: code = ARM_CC; 871290075Sobrien 871390075Sobrien dominance: 871490075Sobrien if (comp_code != EQ && comp_code != NE) 871590075Sobrien abort (); 871690075Sobrien 871790075Sobrien if (comp_code == EQ) 871890075Sobrien return ARM_INVERSE_CONDITION_CODE (code); 871990075Sobrien return code; 872090075Sobrien 872190075Sobrien case CC_NOOVmode: 872290075Sobrien switch (comp_code) 872390075Sobrien { 872490075Sobrien case NE: return ARM_NE; 872590075Sobrien case EQ: return ARM_EQ; 872690075Sobrien case GE: return ARM_PL; 872790075Sobrien case LT: return ARM_MI; 872890075Sobrien default: abort (); 872990075Sobrien } 873090075Sobrien 873190075Sobrien case CC_Zmode: 873290075Sobrien switch (comp_code) 873390075Sobrien { 873490075Sobrien case NE: return ARM_NE; 873590075Sobrien case EQ: return ARM_EQ; 873690075Sobrien default: abort (); 873790075Sobrien } 873890075Sobrien 873990075Sobrien case CCFPEmode: 874090075Sobrien case CCFPmode: 874190075Sobrien /* These encodings assume that AC=1 in the FPA system control 874290075Sobrien byte. This allows us to handle all cases except UNEQ and 874390075Sobrien LTGT. */ 874490075Sobrien switch (comp_code) 874590075Sobrien { 874690075Sobrien case GE: return ARM_GE; 874790075Sobrien case GT: return ARM_GT; 874890075Sobrien case LE: return ARM_LS; 874990075Sobrien case LT: return ARM_MI; 875090075Sobrien case NE: return ARM_NE; 875190075Sobrien case EQ: return ARM_EQ; 875290075Sobrien case ORDERED: return ARM_VC; 875390075Sobrien case UNORDERED: return ARM_VS; 875490075Sobrien case UNLT: return ARM_LT; 875590075Sobrien case UNLE: return ARM_LE; 875690075Sobrien case UNGT: return ARM_HI; 875790075Sobrien case UNGE: return ARM_PL; 875890075Sobrien /* UNEQ and LTGT do not have a representation. */ 875990075Sobrien case UNEQ: /* Fall through. */ 876090075Sobrien case LTGT: /* Fall through. */ 876190075Sobrien default: abort (); 876290075Sobrien } 876390075Sobrien 876490075Sobrien case CC_SWPmode: 876590075Sobrien switch (comp_code) 876690075Sobrien { 876790075Sobrien case NE: return ARM_NE; 876890075Sobrien case EQ: return ARM_EQ; 876990075Sobrien case GE: return ARM_LE; 877090075Sobrien case GT: return ARM_LT; 877190075Sobrien case LE: return ARM_GE; 877290075Sobrien case LT: return ARM_GT; 877390075Sobrien case GEU: return ARM_LS; 877490075Sobrien case GTU: return ARM_CC; 877590075Sobrien case LEU: return ARM_CS; 877690075Sobrien case LTU: return ARM_HI; 877790075Sobrien default: abort (); 877890075Sobrien } 877990075Sobrien 878090075Sobrien case CC_Cmode: 878190075Sobrien switch (comp_code) 878290075Sobrien { 878390075Sobrien case LTU: return ARM_CS; 878490075Sobrien case GEU: return ARM_CC; 878590075Sobrien default: abort (); 878690075Sobrien } 878790075Sobrien 878890075Sobrien case CCmode: 878990075Sobrien switch (comp_code) 879090075Sobrien { 879190075Sobrien case NE: return ARM_NE; 879290075Sobrien case EQ: return ARM_EQ; 879390075Sobrien case GE: return ARM_GE; 879490075Sobrien case GT: return ARM_GT; 879590075Sobrien case LE: return ARM_LE; 879690075Sobrien case LT: return ARM_LT; 879790075Sobrien case GEU: return ARM_CS; 879890075Sobrien case GTU: return ARM_HI; 879990075Sobrien case LEU: return ARM_LS; 880090075Sobrien case LTU: return ARM_CC; 880190075Sobrien default: abort (); 880290075Sobrien } 880390075Sobrien 880490075Sobrien default: abort (); 880590075Sobrien } 880690075Sobrien 880790075Sobrien abort (); 880890075Sobrien} 880990075Sobrien 881090075Sobrien 881190075Sobrienvoid 881290075Sobrienarm_final_prescan_insn (insn) 881390075Sobrien rtx insn; 881490075Sobrien{ 881590075Sobrien /* BODY will hold the body of INSN. */ 881690075Sobrien rtx body = PATTERN (insn); 881790075Sobrien 881890075Sobrien /* This will be 1 if trying to repeat the trick, and things need to be 881990075Sobrien reversed if it appears to fail. */ 882090075Sobrien int reverse = 0; 882190075Sobrien 882290075Sobrien /* JUMP_CLOBBERS will be one implies that the conditions if a branch is 882390075Sobrien taken are clobbered, even if the rtl suggests otherwise. It also 882490075Sobrien means that we have to grub around within the jump expression to find 882590075Sobrien out what the conditions are when the jump isn't taken. */ 882690075Sobrien int jump_clobbers = 0; 882790075Sobrien 882890075Sobrien /* If we start with a return insn, we only succeed if we find another one. */ 882990075Sobrien int seeking_return = 0; 883090075Sobrien 883190075Sobrien /* START_INSN will hold the insn from where we start looking. This is the 883290075Sobrien first insn after the following code_label if REVERSE is true. */ 883390075Sobrien rtx start_insn = insn; 883490075Sobrien 883590075Sobrien /* If in state 4, check if the target branch is reached, in order to 883690075Sobrien change back to state 0. */ 883790075Sobrien if (arm_ccfsm_state == 4) 883890075Sobrien { 883990075Sobrien if (insn == arm_target_insn) 884090075Sobrien { 884190075Sobrien arm_target_insn = NULL; 884290075Sobrien arm_ccfsm_state = 0; 884390075Sobrien } 884490075Sobrien return; 884590075Sobrien } 884690075Sobrien 884790075Sobrien /* If in state 3, it is possible to repeat the trick, if this insn is an 884890075Sobrien unconditional branch to a label, and immediately following this branch 884990075Sobrien is the previous target label which is only used once, and the label this 885090075Sobrien branch jumps to is not too far off. */ 885190075Sobrien if (arm_ccfsm_state == 3) 885290075Sobrien { 885390075Sobrien if (simplejump_p (insn)) 885490075Sobrien { 885590075Sobrien start_insn = next_nonnote_insn (start_insn); 885690075Sobrien if (GET_CODE (start_insn) == BARRIER) 885790075Sobrien { 885890075Sobrien /* XXX Isn't this always a barrier? */ 885990075Sobrien start_insn = next_nonnote_insn (start_insn); 886090075Sobrien } 886190075Sobrien if (GET_CODE (start_insn) == CODE_LABEL 886290075Sobrien && CODE_LABEL_NUMBER (start_insn) == arm_target_label 886390075Sobrien && LABEL_NUSES (start_insn) == 1) 886490075Sobrien reverse = TRUE; 886590075Sobrien else 886690075Sobrien return; 886790075Sobrien } 886890075Sobrien else if (GET_CODE (body) == RETURN) 886990075Sobrien { 887090075Sobrien start_insn = next_nonnote_insn (start_insn); 887190075Sobrien if (GET_CODE (start_insn) == BARRIER) 887290075Sobrien start_insn = next_nonnote_insn (start_insn); 887390075Sobrien if (GET_CODE (start_insn) == CODE_LABEL 887490075Sobrien && CODE_LABEL_NUMBER (start_insn) == arm_target_label 887590075Sobrien && LABEL_NUSES (start_insn) == 1) 887690075Sobrien { 887790075Sobrien reverse = TRUE; 887890075Sobrien seeking_return = 1; 887990075Sobrien } 888090075Sobrien else 888190075Sobrien return; 888290075Sobrien } 888390075Sobrien else 888490075Sobrien return; 888590075Sobrien } 888690075Sobrien 888790075Sobrien if (arm_ccfsm_state != 0 && !reverse) 888890075Sobrien abort (); 888990075Sobrien if (GET_CODE (insn) != JUMP_INSN) 889090075Sobrien return; 889190075Sobrien 889290075Sobrien /* This jump might be paralleled with a clobber of the condition codes 889390075Sobrien the jump should always come first */ 889490075Sobrien if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0) 889590075Sobrien body = XVECEXP (body, 0, 0); 889690075Sobrien 889790075Sobrien#if 0 889890075Sobrien /* If this is a conditional return then we don't want to know */ 889990075Sobrien if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC 890090075Sobrien && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE 890190075Sobrien && (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN 890290075Sobrien || GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)) 890390075Sobrien return; 890490075Sobrien#endif 890590075Sobrien 890690075Sobrien if (reverse 890790075Sobrien || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC 890890075Sobrien && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) 890990075Sobrien { 891090075Sobrien int insns_skipped; 891190075Sobrien int fail = FALSE, succeed = FALSE; 891290075Sobrien /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ 891390075Sobrien int then_not_else = TRUE; 891490075Sobrien rtx this_insn = start_insn, label = 0; 891590075Sobrien 891690075Sobrien /* If the jump cannot be done with one instruction, we cannot 891790075Sobrien conditionally execute the instruction in the inverse case. */ 891890075Sobrien if (get_attr_conds (insn) == CONDS_JUMP_CLOB) 891990075Sobrien { 892090075Sobrien jump_clobbers = 1; 892190075Sobrien return; 892290075Sobrien } 892390075Sobrien 892490075Sobrien /* Register the insn jumped to. */ 892590075Sobrien if (reverse) 892690075Sobrien { 892790075Sobrien if (!seeking_return) 892890075Sobrien label = XEXP (SET_SRC (body), 0); 892990075Sobrien } 893090075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) 893190075Sobrien label = XEXP (XEXP (SET_SRC (body), 1), 0); 893290075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) 893390075Sobrien { 893490075Sobrien label = XEXP (XEXP (SET_SRC (body), 2), 0); 893590075Sobrien then_not_else = FALSE; 893690075Sobrien } 893790075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN) 893890075Sobrien seeking_return = 1; 893990075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN) 894090075Sobrien { 894190075Sobrien seeking_return = 1; 894290075Sobrien then_not_else = FALSE; 894390075Sobrien } 894490075Sobrien else 894590075Sobrien abort (); 894690075Sobrien 894790075Sobrien /* See how many insns this branch skips, and what kind of insns. If all 894890075Sobrien insns are okay, and the label or unconditional branch to the same 894990075Sobrien label is not too far away, succeed. */ 895090075Sobrien for (insns_skipped = 0; 895190075Sobrien !fail && !succeed && insns_skipped++ < max_insns_skipped;) 895290075Sobrien { 895390075Sobrien rtx scanbody; 895490075Sobrien 895590075Sobrien this_insn = next_nonnote_insn (this_insn); 895690075Sobrien if (!this_insn) 895790075Sobrien break; 895890075Sobrien 895990075Sobrien switch (GET_CODE (this_insn)) 896090075Sobrien { 896190075Sobrien case CODE_LABEL: 896290075Sobrien /* Succeed if it is the target label, otherwise fail since 896390075Sobrien control falls in from somewhere else. */ 896490075Sobrien if (this_insn == label) 896590075Sobrien { 896690075Sobrien if (jump_clobbers) 896790075Sobrien { 896890075Sobrien arm_ccfsm_state = 2; 896990075Sobrien this_insn = next_nonnote_insn (this_insn); 897090075Sobrien } 897190075Sobrien else 897290075Sobrien arm_ccfsm_state = 1; 897390075Sobrien succeed = TRUE; 897490075Sobrien } 897590075Sobrien else 897690075Sobrien fail = TRUE; 897790075Sobrien break; 897890075Sobrien 897990075Sobrien case BARRIER: 898090075Sobrien /* Succeed if the following insn is the target label. 898190075Sobrien Otherwise fail. 898290075Sobrien If return insns are used then the last insn in a function 898390075Sobrien will be a barrier. */ 898490075Sobrien this_insn = next_nonnote_insn (this_insn); 898590075Sobrien if (this_insn && this_insn == label) 898690075Sobrien { 898790075Sobrien if (jump_clobbers) 898890075Sobrien { 898990075Sobrien arm_ccfsm_state = 2; 899090075Sobrien this_insn = next_nonnote_insn (this_insn); 899190075Sobrien } 899290075Sobrien else 899390075Sobrien arm_ccfsm_state = 1; 899490075Sobrien succeed = TRUE; 899590075Sobrien } 899690075Sobrien else 899790075Sobrien fail = TRUE; 899890075Sobrien break; 899990075Sobrien 900090075Sobrien case CALL_INSN: 900190075Sobrien /* If using 32-bit addresses the cc is not preserved over 900290075Sobrien calls. */ 900390075Sobrien if (TARGET_APCS_32) 900490075Sobrien { 900590075Sobrien /* Succeed if the following insn is the target label, 900690075Sobrien or if the following two insns are a barrier and 900790075Sobrien the target label. */ 900890075Sobrien this_insn = next_nonnote_insn (this_insn); 900990075Sobrien if (this_insn && GET_CODE (this_insn) == BARRIER) 901090075Sobrien this_insn = next_nonnote_insn (this_insn); 901190075Sobrien 901290075Sobrien if (this_insn && this_insn == label 901390075Sobrien && insns_skipped < max_insns_skipped) 901490075Sobrien { 901590075Sobrien if (jump_clobbers) 901690075Sobrien { 901790075Sobrien arm_ccfsm_state = 2; 901890075Sobrien this_insn = next_nonnote_insn (this_insn); 901990075Sobrien } 902090075Sobrien else 902190075Sobrien arm_ccfsm_state = 1; 902290075Sobrien succeed = TRUE; 902390075Sobrien } 902490075Sobrien else 902590075Sobrien fail = TRUE; 902690075Sobrien } 902790075Sobrien break; 902890075Sobrien 902990075Sobrien case JUMP_INSN: 903090075Sobrien /* If this is an unconditional branch to the same label, succeed. 903190075Sobrien If it is to another label, do nothing. If it is conditional, 903290075Sobrien fail. */ 903390075Sobrien /* XXX Probably, the tests for SET and the PC are unnecessary. */ 903490075Sobrien 903590075Sobrien scanbody = PATTERN (this_insn); 903690075Sobrien if (GET_CODE (scanbody) == SET 903790075Sobrien && GET_CODE (SET_DEST (scanbody)) == PC) 903890075Sobrien { 903990075Sobrien if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF 904090075Sobrien && XEXP (SET_SRC (scanbody), 0) == label && !reverse) 904190075Sobrien { 904290075Sobrien arm_ccfsm_state = 2; 904390075Sobrien succeed = TRUE; 904490075Sobrien } 904590075Sobrien else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) 904690075Sobrien fail = TRUE; 904790075Sobrien } 904890075Sobrien /* Fail if a conditional return is undesirable (eg on a 904990075Sobrien StrongARM), but still allow this if optimizing for size. */ 905090075Sobrien else if (GET_CODE (scanbody) == RETURN 905190075Sobrien && !use_return_insn (TRUE) 905290075Sobrien && !optimize_size) 905390075Sobrien fail = TRUE; 905490075Sobrien else if (GET_CODE (scanbody) == RETURN 905590075Sobrien && seeking_return) 905690075Sobrien { 905790075Sobrien arm_ccfsm_state = 2; 905890075Sobrien succeed = TRUE; 905990075Sobrien } 906090075Sobrien else if (GET_CODE (scanbody) == PARALLEL) 906190075Sobrien { 906290075Sobrien switch (get_attr_conds (this_insn)) 906390075Sobrien { 906490075Sobrien case CONDS_NOCOND: 906590075Sobrien break; 906690075Sobrien default: 906790075Sobrien fail = TRUE; 906890075Sobrien break; 906990075Sobrien } 907090075Sobrien } 907190075Sobrien else 907290075Sobrien fail = TRUE; /* Unrecognized jump (eg epilogue). */ 907390075Sobrien 907490075Sobrien break; 907590075Sobrien 907690075Sobrien case INSN: 907790075Sobrien /* Instructions using or affecting the condition codes make it 907890075Sobrien fail. */ 907990075Sobrien scanbody = PATTERN (this_insn); 908090075Sobrien if (!(GET_CODE (scanbody) == SET 908190075Sobrien || GET_CODE (scanbody) == PARALLEL) 908290075Sobrien || get_attr_conds (this_insn) != CONDS_NOCOND) 908390075Sobrien fail = TRUE; 908490075Sobrien break; 908590075Sobrien 908690075Sobrien default: 908790075Sobrien break; 908890075Sobrien } 908990075Sobrien } 909090075Sobrien if (succeed) 909190075Sobrien { 909290075Sobrien if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse)) 909390075Sobrien arm_target_label = CODE_LABEL_NUMBER (label); 909490075Sobrien else if (seeking_return || arm_ccfsm_state == 2) 909590075Sobrien { 909690075Sobrien while (this_insn && GET_CODE (PATTERN (this_insn)) == USE) 909790075Sobrien { 909890075Sobrien this_insn = next_nonnote_insn (this_insn); 909990075Sobrien if (this_insn && (GET_CODE (this_insn) == BARRIER 910090075Sobrien || GET_CODE (this_insn) == CODE_LABEL)) 910190075Sobrien abort (); 910290075Sobrien } 910390075Sobrien if (!this_insn) 910490075Sobrien { 910590075Sobrien /* Oh, dear! we ran off the end.. give up */ 910690075Sobrien recog (PATTERN (insn), insn, NULL); 910790075Sobrien arm_ccfsm_state = 0; 910890075Sobrien arm_target_insn = NULL; 910990075Sobrien return; 911090075Sobrien } 911190075Sobrien arm_target_insn = this_insn; 911290075Sobrien } 911390075Sobrien else 911490075Sobrien abort (); 911590075Sobrien if (jump_clobbers) 911690075Sobrien { 911790075Sobrien if (reverse) 911890075Sobrien abort (); 911990075Sobrien arm_current_cc = 912090075Sobrien get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body), 912190075Sobrien 0), 0), 1)); 912290075Sobrien if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND) 912390075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 912490075Sobrien if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE) 912590075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 912690075Sobrien } 912790075Sobrien else 912890075Sobrien { 912990075Sobrien /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from 913090075Sobrien what it was. */ 913190075Sobrien if (!reverse) 913290075Sobrien arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 913390075Sobrien 0)); 913490075Sobrien } 913590075Sobrien 913690075Sobrien if (reverse || then_not_else) 913790075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 913890075Sobrien } 913990075Sobrien 914090075Sobrien /* Restore recog_data (getting the attributes of other insns can 914190075Sobrien destroy this array, but final.c assumes that it remains intact 914290075Sobrien across this call; since the insn has been recognized already we 914390075Sobrien call recog direct). */ 914490075Sobrien recog (PATTERN (insn), insn, NULL); 914590075Sobrien } 914690075Sobrien} 914790075Sobrien 914890075Sobrien/* Returns true if REGNO is a valid register 914990075Sobrien for holding a quantity of tyoe MODE. */ 915090075Sobrien 915190075Sobrienint 915290075Sobrienarm_hard_regno_mode_ok (regno, mode) 915390075Sobrien unsigned int regno; 915490075Sobrien enum machine_mode mode; 915590075Sobrien{ 915690075Sobrien if (GET_MODE_CLASS (mode) == MODE_CC) 915790075Sobrien return regno == CC_REGNUM; 915890075Sobrien 915990075Sobrien if (TARGET_THUMB) 916090075Sobrien /* For the Thumb we only allow values bigger than SImode in 916190075Sobrien registers 0 - 6, so that there is always a second low 916290075Sobrien register available to hold the upper part of the value. 916390075Sobrien We probably we ought to ensure that the register is the 916490075Sobrien start of an even numbered register pair. */ 916590075Sobrien return (NUM_REGS (mode) < 2) || (regno < LAST_LO_REGNUM); 916690075Sobrien 916790075Sobrien if (regno <= LAST_ARM_REGNUM) 916896263Sobrien /* We allow any value to be stored in the general regisetrs. */ 916996263Sobrien return 1; 917090075Sobrien 917190075Sobrien if ( regno == FRAME_POINTER_REGNUM 917290075Sobrien || regno == ARG_POINTER_REGNUM) 917390075Sobrien /* We only allow integers in the fake hard registers. */ 917490075Sobrien return GET_MODE_CLASS (mode) == MODE_INT; 917590075Sobrien 917690075Sobrien /* The only registers left are the FPU registers 917790075Sobrien which we only allow to hold FP values. */ 917890075Sobrien return GET_MODE_CLASS (mode) == MODE_FLOAT 917990075Sobrien && regno >= FIRST_ARM_FP_REGNUM 918090075Sobrien && regno <= LAST_ARM_FP_REGNUM; 918190075Sobrien} 918290075Sobrien 918390075Sobrienint 918490075Sobrienarm_regno_class (regno) 918590075Sobrien int regno; 918690075Sobrien{ 918790075Sobrien if (TARGET_THUMB) 918890075Sobrien { 918990075Sobrien if (regno == STACK_POINTER_REGNUM) 919090075Sobrien return STACK_REG; 919190075Sobrien if (regno == CC_REGNUM) 919290075Sobrien return CC_REG; 919390075Sobrien if (regno < 8) 919490075Sobrien return LO_REGS; 919590075Sobrien return HI_REGS; 919690075Sobrien } 919790075Sobrien 919890075Sobrien if ( regno <= LAST_ARM_REGNUM 919990075Sobrien || regno == FRAME_POINTER_REGNUM 920090075Sobrien || regno == ARG_POINTER_REGNUM) 920190075Sobrien return GENERAL_REGS; 920290075Sobrien 920390075Sobrien if (regno == CC_REGNUM) 920490075Sobrien return NO_REGS; 920590075Sobrien 920690075Sobrien return FPU_REGS; 920790075Sobrien} 920890075Sobrien 920990075Sobrien/* Handle a special case when computing the offset 921090075Sobrien of an argument from the frame pointer. */ 921190075Sobrien 921290075Sobrienint 921390075Sobrienarm_debugger_arg_offset (value, addr) 921490075Sobrien int value; 921590075Sobrien rtx addr; 921690075Sobrien{ 921790075Sobrien rtx insn; 921890075Sobrien 921990075Sobrien /* We are only interested if dbxout_parms() failed to compute the offset. */ 922090075Sobrien if (value != 0) 922190075Sobrien return 0; 922290075Sobrien 922390075Sobrien /* We can only cope with the case where the address is held in a register. */ 922490075Sobrien if (GET_CODE (addr) != REG) 922590075Sobrien return 0; 922690075Sobrien 922790075Sobrien /* If we are using the frame pointer to point at the argument, then 922890075Sobrien an offset of 0 is correct. */ 922990075Sobrien if (REGNO (addr) == (unsigned) HARD_FRAME_POINTER_REGNUM) 923090075Sobrien return 0; 923190075Sobrien 923290075Sobrien /* If we are using the stack pointer to point at the 923390075Sobrien argument, then an offset of 0 is correct. */ 923490075Sobrien if ((TARGET_THUMB || !frame_pointer_needed) 923590075Sobrien && REGNO (addr) == SP_REGNUM) 923690075Sobrien return 0; 923790075Sobrien 923890075Sobrien /* Oh dear. The argument is pointed to by a register rather 923990075Sobrien than being held in a register, or being stored at a known 924090075Sobrien offset from the frame pointer. Since GDB only understands 924190075Sobrien those two kinds of argument we must translate the address 924290075Sobrien held in the register into an offset from the frame pointer. 924390075Sobrien We do this by searching through the insns for the function 924490075Sobrien looking to see where this register gets its value. If the 924590075Sobrien register is initialised from the frame pointer plus an offset 924690075Sobrien then we are in luck and we can continue, otherwise we give up. 924790075Sobrien 924890075Sobrien This code is exercised by producing debugging information 924990075Sobrien for a function with arguments like this: 925090075Sobrien 925190075Sobrien double func (double a, double b, int c, double d) {return d;} 925290075Sobrien 925390075Sobrien Without this code the stab for parameter 'd' will be set to 925490075Sobrien an offset of 0 from the frame pointer, rather than 8. */ 925590075Sobrien 925690075Sobrien /* The if() statement says: 925790075Sobrien 925890075Sobrien If the insn is a normal instruction 925990075Sobrien and if the insn is setting the value in a register 926090075Sobrien and if the register being set is the register holding the address of the argument 926190075Sobrien and if the address is computing by an addition 926290075Sobrien that involves adding to a register 926390075Sobrien which is the frame pointer 926490075Sobrien a constant integer 926590075Sobrien 926690075Sobrien then... */ 926790075Sobrien 926890075Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 926990075Sobrien { 927090075Sobrien if ( GET_CODE (insn) == INSN 927190075Sobrien && GET_CODE (PATTERN (insn)) == SET 927290075Sobrien && REGNO (XEXP (PATTERN (insn), 0)) == REGNO (addr) 927390075Sobrien && GET_CODE (XEXP (PATTERN (insn), 1)) == PLUS 927490075Sobrien && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 0)) == REG 927590075Sobrien && REGNO (XEXP (XEXP (PATTERN (insn), 1), 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM 927690075Sobrien && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 1)) == CONST_INT 927790075Sobrien ) 927890075Sobrien { 927990075Sobrien value = INTVAL (XEXP (XEXP (PATTERN (insn), 1), 1)); 928090075Sobrien 928190075Sobrien break; 928290075Sobrien } 928390075Sobrien } 928490075Sobrien 928590075Sobrien if (value == 0) 928690075Sobrien { 928790075Sobrien debug_rtx (addr); 928890075Sobrien warning ("unable to compute real location of stacked parameter"); 928990075Sobrien value = 8; /* XXX magic hack */ 929090075Sobrien } 929190075Sobrien 929290075Sobrien return value; 929390075Sobrien} 929490075Sobrien 929590075Sobrien#define def_builtin(NAME, TYPE, CODE) \ 929690075Sobrien builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL) 929790075Sobrien 929890075Sobrienvoid 929990075Sobrienarm_init_builtins () 930090075Sobrien{ 930190075Sobrien tree endlink = void_list_node; 930290075Sobrien tree int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink); 930390075Sobrien tree pchar_type_node = build_pointer_type (char_type_node); 930490075Sobrien 930590075Sobrien tree int_ftype_int, void_ftype_pchar; 930690075Sobrien 930790075Sobrien /* void func (void *) */ 930890075Sobrien void_ftype_pchar 930990075Sobrien = build_function_type (void_type_node, 931090075Sobrien tree_cons (NULL_TREE, pchar_type_node, endlink)); 931190075Sobrien 931290075Sobrien /* int func (int) */ 931390075Sobrien int_ftype_int 931490075Sobrien = build_function_type (integer_type_node, int_endlink); 931590075Sobrien 931690075Sobrien /* Initialize arm V5 builtins. */ 931790075Sobrien if (arm_arch5) 931890075Sobrien def_builtin ("__builtin_clz", int_ftype_int, ARM_BUILTIN_CLZ); 931990075Sobrien} 932090075Sobrien 932190075Sobrien/* Expand an expression EXP that calls a built-in function, 932290075Sobrien with result going to TARGET if that's convenient 932390075Sobrien (and in mode MODE if that's convenient). 932490075Sobrien SUBTARGET may be used as the target for computing one of EXP's operands. 932590075Sobrien IGNORE is nonzero if the value is to be ignored. */ 932690075Sobrien 932790075Sobrienrtx 932890075Sobrienarm_expand_builtin (exp, target, subtarget, mode, ignore) 932990075Sobrien tree exp; 933090075Sobrien rtx target; 933190075Sobrien rtx subtarget ATTRIBUTE_UNUSED; 933290075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 933390075Sobrien int ignore ATTRIBUTE_UNUSED; 933490075Sobrien{ 933590075Sobrien enum insn_code icode; 933690075Sobrien tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); 933790075Sobrien tree arglist = TREE_OPERAND (exp, 1); 933890075Sobrien tree arg0; 933990075Sobrien rtx op0, pat; 934090075Sobrien enum machine_mode tmode, mode0; 934190075Sobrien int fcode = DECL_FUNCTION_CODE (fndecl); 934290075Sobrien 934390075Sobrien switch (fcode) 934490075Sobrien { 934590075Sobrien default: 934690075Sobrien break; 934790075Sobrien 934890075Sobrien case ARM_BUILTIN_CLZ: 934990075Sobrien icode = CODE_FOR_clz; 935090075Sobrien arg0 = TREE_VALUE (arglist); 935190075Sobrien op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 935290075Sobrien tmode = insn_data[icode].operand[0].mode; 935390075Sobrien mode0 = insn_data[icode].operand[1].mode; 935490075Sobrien 935590075Sobrien if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 935690075Sobrien op0 = copy_to_mode_reg (mode0, op0); 935790075Sobrien if (target == 0 935890075Sobrien || GET_MODE (target) != tmode 935990075Sobrien || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 936090075Sobrien target = gen_reg_rtx (tmode); 936190075Sobrien pat = GEN_FCN (icode) (target, op0); 936290075Sobrien if (! pat) 936390075Sobrien return 0; 936490075Sobrien emit_insn (pat); 936590075Sobrien return target; 936690075Sobrien } 936790075Sobrien 936890075Sobrien /* @@@ Should really do something sensible here. */ 936990075Sobrien return NULL_RTX; 937090075Sobrien} 937190075Sobrien 937290075Sobrien/* Recursively search through all of the blocks in a function 937390075Sobrien checking to see if any of the variables created in that 937490075Sobrien function match the RTX called 'orig'. If they do then 937590075Sobrien replace them with the RTX called 'new'. */ 937690075Sobrien 937790075Sobrienstatic void 937890075Sobrienreplace_symbols_in_block (block, orig, new) 937990075Sobrien tree block; 938090075Sobrien rtx orig; 938190075Sobrien rtx new; 938290075Sobrien{ 938390075Sobrien for (; block; block = BLOCK_CHAIN (block)) 938490075Sobrien { 938590075Sobrien tree sym; 938690075Sobrien 938790075Sobrien if (!TREE_USED (block)) 938890075Sobrien continue; 938990075Sobrien 939090075Sobrien for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym)) 939190075Sobrien { 939290075Sobrien if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL) 939390075Sobrien || DECL_IGNORED_P (sym) 939490075Sobrien || TREE_CODE (sym) != VAR_DECL 939590075Sobrien || DECL_EXTERNAL (sym) 939690075Sobrien || !rtx_equal_p (DECL_RTL (sym), orig) 939790075Sobrien ) 939890075Sobrien continue; 939990075Sobrien 940090075Sobrien SET_DECL_RTL (sym, new); 940190075Sobrien } 940290075Sobrien 940390075Sobrien replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new); 940490075Sobrien } 940590075Sobrien} 940690075Sobrien 940790075Sobrien/* Return the number (counting from 0) of 940890075Sobrien the least significant set bit in MASK. */ 940990075Sobrien 941090075Sobrien#ifdef __GNUC__ 941190075Sobrieninline 941290075Sobrien#endif 941390075Sobrienstatic int 941490075Sobriennumber_of_first_bit_set (mask) 941590075Sobrien int mask; 941690075Sobrien{ 941790075Sobrien int bit; 941890075Sobrien 941990075Sobrien for (bit = 0; 942090075Sobrien (mask & (1 << bit)) == 0; 942190075Sobrien ++bit) 942290075Sobrien continue; 942390075Sobrien 942490075Sobrien return bit; 942590075Sobrien} 942690075Sobrien 942790075Sobrien/* Generate code to return from a thumb function. 942890075Sobrien If 'reg_containing_return_addr' is -1, then the return address is 942990075Sobrien actually on the stack, at the stack pointer. */ 943090075Sobrienstatic void 943190075Sobrienthumb_exit (f, reg_containing_return_addr, eh_ofs) 943290075Sobrien FILE * f; 943390075Sobrien int reg_containing_return_addr; 943490075Sobrien rtx eh_ofs; 943590075Sobrien{ 943690075Sobrien unsigned regs_available_for_popping; 943790075Sobrien unsigned regs_to_pop; 943890075Sobrien int pops_needed; 943990075Sobrien unsigned available; 944090075Sobrien unsigned required; 944190075Sobrien int mode; 944290075Sobrien int size; 944390075Sobrien int restore_a4 = FALSE; 944490075Sobrien 944590075Sobrien /* Compute the registers we need to pop. */ 944690075Sobrien regs_to_pop = 0; 944790075Sobrien pops_needed = 0; 944890075Sobrien 944990075Sobrien /* There is an assumption here, that if eh_ofs is not NULL, the 945090075Sobrien normal return address will have been pushed. */ 945190075Sobrien if (reg_containing_return_addr == -1 || eh_ofs) 945290075Sobrien { 945390075Sobrien /* When we are generating a return for __builtin_eh_return, 945490075Sobrien reg_containing_return_addr must specify the return regno. */ 945590075Sobrien if (eh_ofs && reg_containing_return_addr == -1) 945690075Sobrien abort (); 945790075Sobrien 945890075Sobrien regs_to_pop |= 1 << LR_REGNUM; 945990075Sobrien ++pops_needed; 946090075Sobrien } 946190075Sobrien 946290075Sobrien if (TARGET_BACKTRACE) 946390075Sobrien { 946490075Sobrien /* Restore the (ARM) frame pointer and stack pointer. */ 946590075Sobrien regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM); 946690075Sobrien pops_needed += 2; 946790075Sobrien } 946890075Sobrien 946990075Sobrien /* If there is nothing to pop then just emit the BX instruction and 947090075Sobrien return. */ 947190075Sobrien if (pops_needed == 0) 947290075Sobrien { 947390075Sobrien if (eh_ofs) 947490075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 947590075Sobrien 947690075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 947790075Sobrien return; 947890075Sobrien } 947990075Sobrien /* Otherwise if we are not supporting interworking and we have not created 948090075Sobrien a backtrace structure and the function was not entered in ARM mode then 948190075Sobrien just pop the return address straight into the PC. */ 948290075Sobrien else if (!TARGET_INTERWORK 948390075Sobrien && !TARGET_BACKTRACE 948490075Sobrien && !is_called_in_ARM_mode (current_function_decl)) 948590075Sobrien { 948690075Sobrien if (eh_ofs) 948790075Sobrien { 948890075Sobrien asm_fprintf (f, "\tadd\t%r, #4\n", SP_REGNUM); 948990075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 949090075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 949190075Sobrien } 949290075Sobrien else 949390075Sobrien asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM); 949490075Sobrien 949590075Sobrien return; 949690075Sobrien } 949790075Sobrien 949890075Sobrien /* Find out how many of the (return) argument registers we can corrupt. */ 949990075Sobrien regs_available_for_popping = 0; 950090075Sobrien 950190075Sobrien /* If returning via __builtin_eh_return, the bottom three registers 950290075Sobrien all contain information needed for the return. */ 950390075Sobrien if (eh_ofs) 950490075Sobrien size = 12; 950590075Sobrien else 950690075Sobrien { 950790075Sobrien#ifdef RTX_CODE 950890075Sobrien /* If we can deduce the registers used from the function's 950990075Sobrien return value. This is more reliable that examining 951090075Sobrien regs_ever_live[] because that will be set if the register is 951190075Sobrien ever used in the function, not just if the register is used 951290075Sobrien to hold a return value. */ 951390075Sobrien 951490075Sobrien if (current_function_return_rtx != 0) 951590075Sobrien mode = GET_MODE (current_function_return_rtx); 951690075Sobrien else 951790075Sobrien#endif 951890075Sobrien mode = DECL_MODE (DECL_RESULT (current_function_decl)); 951990075Sobrien 952090075Sobrien size = GET_MODE_SIZE (mode); 952190075Sobrien 952290075Sobrien if (size == 0) 952390075Sobrien { 952490075Sobrien /* In a void function we can use any argument register. 952590075Sobrien In a function that returns a structure on the stack 952690075Sobrien we can use the second and third argument registers. */ 952790075Sobrien if (mode == VOIDmode) 952890075Sobrien regs_available_for_popping = 952990075Sobrien (1 << ARG_REGISTER (1)) 953090075Sobrien | (1 << ARG_REGISTER (2)) 953190075Sobrien | (1 << ARG_REGISTER (3)); 953290075Sobrien else 953390075Sobrien regs_available_for_popping = 953490075Sobrien (1 << ARG_REGISTER (2)) 953590075Sobrien | (1 << ARG_REGISTER (3)); 953690075Sobrien } 953790075Sobrien else if (size <= 4) 953890075Sobrien regs_available_for_popping = 953990075Sobrien (1 << ARG_REGISTER (2)) 954090075Sobrien | (1 << ARG_REGISTER (3)); 954190075Sobrien else if (size <= 8) 954290075Sobrien regs_available_for_popping = 954390075Sobrien (1 << ARG_REGISTER (3)); 954490075Sobrien } 954590075Sobrien 954690075Sobrien /* Match registers to be popped with registers into which we pop them. */ 954790075Sobrien for (available = regs_available_for_popping, 954890075Sobrien required = regs_to_pop; 954990075Sobrien required != 0 && available != 0; 955090075Sobrien available &= ~(available & - available), 955190075Sobrien required &= ~(required & - required)) 955290075Sobrien -- pops_needed; 955390075Sobrien 955490075Sobrien /* If we have any popping registers left over, remove them. */ 955590075Sobrien if (available > 0) 955690075Sobrien regs_available_for_popping &= ~available; 955790075Sobrien 955890075Sobrien /* Otherwise if we need another popping register we can use 955990075Sobrien the fourth argument register. */ 956090075Sobrien else if (pops_needed) 956190075Sobrien { 956290075Sobrien /* If we have not found any free argument registers and 956390075Sobrien reg a4 contains the return address, we must move it. */ 956490075Sobrien if (regs_available_for_popping == 0 956590075Sobrien && reg_containing_return_addr == LAST_ARG_REGNUM) 956690075Sobrien { 956790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); 956890075Sobrien reg_containing_return_addr = LR_REGNUM; 956990075Sobrien } 957090075Sobrien else if (size > 12) 957190075Sobrien { 957290075Sobrien /* Register a4 is being used to hold part of the return value, 957390075Sobrien but we have dire need of a free, low register. */ 957490075Sobrien restore_a4 = TRUE; 957590075Sobrien 957690075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM); 957790075Sobrien } 957890075Sobrien 957990075Sobrien if (reg_containing_return_addr != LAST_ARG_REGNUM) 958090075Sobrien { 958190075Sobrien /* The fourth argument register is available. */ 958290075Sobrien regs_available_for_popping |= 1 << LAST_ARG_REGNUM; 958390075Sobrien 958490075Sobrien --pops_needed; 958590075Sobrien } 958690075Sobrien } 958790075Sobrien 958890075Sobrien /* Pop as many registers as we can. */ 958990075Sobrien thumb_pushpop (f, regs_available_for_popping, FALSE); 959090075Sobrien 959190075Sobrien /* Process the registers we popped. */ 959290075Sobrien if (reg_containing_return_addr == -1) 959390075Sobrien { 959490075Sobrien /* The return address was popped into the lowest numbered register. */ 959590075Sobrien regs_to_pop &= ~(1 << LR_REGNUM); 959690075Sobrien 959790075Sobrien reg_containing_return_addr = 959890075Sobrien number_of_first_bit_set (regs_available_for_popping); 959990075Sobrien 960090075Sobrien /* Remove this register for the mask of available registers, so that 960190075Sobrien the return address will not be corrupted by futher pops. */ 960290075Sobrien regs_available_for_popping &= ~(1 << reg_containing_return_addr); 960390075Sobrien } 960490075Sobrien 960590075Sobrien /* If we popped other registers then handle them here. */ 960690075Sobrien if (regs_available_for_popping) 960790075Sobrien { 960890075Sobrien int frame_pointer; 960990075Sobrien 961090075Sobrien /* Work out which register currently contains the frame pointer. */ 961190075Sobrien frame_pointer = number_of_first_bit_set (regs_available_for_popping); 961290075Sobrien 961390075Sobrien /* Move it into the correct place. */ 961490075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", 961590075Sobrien ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer); 961690075Sobrien 961790075Sobrien /* (Temporarily) remove it from the mask of popped registers. */ 961890075Sobrien regs_available_for_popping &= ~(1 << frame_pointer); 961990075Sobrien regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM); 962090075Sobrien 962190075Sobrien if (regs_available_for_popping) 962290075Sobrien { 962390075Sobrien int stack_pointer; 962490075Sobrien 962590075Sobrien /* We popped the stack pointer as well, 962690075Sobrien find the register that contains it. */ 962790075Sobrien stack_pointer = number_of_first_bit_set (regs_available_for_popping); 962890075Sobrien 962990075Sobrien /* Move it into the stack register. */ 963090075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer); 963190075Sobrien 963290075Sobrien /* At this point we have popped all necessary registers, so 963390075Sobrien do not worry about restoring regs_available_for_popping 963490075Sobrien to its correct value: 963590075Sobrien 963690075Sobrien assert (pops_needed == 0) 963790075Sobrien assert (regs_available_for_popping == (1 << frame_pointer)) 963890075Sobrien assert (regs_to_pop == (1 << STACK_POINTER)) */ 963990075Sobrien } 964090075Sobrien else 964190075Sobrien { 964290075Sobrien /* Since we have just move the popped value into the frame 964390075Sobrien pointer, the popping register is available for reuse, and 964490075Sobrien we know that we still have the stack pointer left to pop. */ 964590075Sobrien regs_available_for_popping |= (1 << frame_pointer); 964690075Sobrien } 964790075Sobrien } 964890075Sobrien 964990075Sobrien /* If we still have registers left on the stack, but we no longer have 965090075Sobrien any registers into which we can pop them, then we must move the return 965190075Sobrien address into the link register and make available the register that 965290075Sobrien contained it. */ 965390075Sobrien if (regs_available_for_popping == 0 && pops_needed > 0) 965490075Sobrien { 965590075Sobrien regs_available_for_popping |= 1 << reg_containing_return_addr; 965690075Sobrien 965790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, 965890075Sobrien reg_containing_return_addr); 965990075Sobrien 966090075Sobrien reg_containing_return_addr = LR_REGNUM; 966190075Sobrien } 966290075Sobrien 966390075Sobrien /* If we have registers left on the stack then pop some more. 966490075Sobrien We know that at most we will want to pop FP and SP. */ 966590075Sobrien if (pops_needed > 0) 966690075Sobrien { 966790075Sobrien int popped_into; 966890075Sobrien int move_to; 966990075Sobrien 967090075Sobrien thumb_pushpop (f, regs_available_for_popping, FALSE); 967190075Sobrien 967290075Sobrien /* We have popped either FP or SP. 967390075Sobrien Move whichever one it is into the correct register. */ 967490075Sobrien popped_into = number_of_first_bit_set (regs_available_for_popping); 967590075Sobrien move_to = number_of_first_bit_set (regs_to_pop); 967690075Sobrien 967790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into); 967890075Sobrien 967990075Sobrien regs_to_pop &= ~(1 << move_to); 968090075Sobrien 968190075Sobrien --pops_needed; 968290075Sobrien } 968390075Sobrien 968490075Sobrien /* If we still have not popped everything then we must have only 968590075Sobrien had one register available to us and we are now popping the SP. */ 968690075Sobrien if (pops_needed > 0) 968790075Sobrien { 968890075Sobrien int popped_into; 968990075Sobrien 969090075Sobrien thumb_pushpop (f, regs_available_for_popping, FALSE); 969190075Sobrien 969290075Sobrien popped_into = number_of_first_bit_set (regs_available_for_popping); 969390075Sobrien 969490075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into); 969590075Sobrien /* 969690075Sobrien assert (regs_to_pop == (1 << STACK_POINTER)) 969790075Sobrien assert (pops_needed == 1) 969890075Sobrien */ 969990075Sobrien } 970090075Sobrien 970190075Sobrien /* If necessary restore the a4 register. */ 970290075Sobrien if (restore_a4) 970390075Sobrien { 970490075Sobrien if (reg_containing_return_addr != LR_REGNUM) 970590075Sobrien { 970690075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); 970790075Sobrien reg_containing_return_addr = LR_REGNUM; 970890075Sobrien } 970990075Sobrien 971090075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); 971190075Sobrien } 971290075Sobrien 971390075Sobrien if (eh_ofs) 971490075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 971590075Sobrien 971690075Sobrien /* Return to caller. */ 971790075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 971890075Sobrien} 971990075Sobrien 972090075Sobrien/* Emit code to push or pop registers to or from the stack. */ 972190075Sobrien 972290075Sobrienstatic void 972390075Sobrienthumb_pushpop (f, mask, push) 972490075Sobrien FILE * f; 972590075Sobrien int mask; 972690075Sobrien int push; 972790075Sobrien{ 972890075Sobrien int regno; 972990075Sobrien int lo_mask = mask & 0xFF; 973090075Sobrien 973190075Sobrien if (lo_mask == 0 && !push && (mask & (1 << 15))) 973290075Sobrien { 973390075Sobrien /* Special case. Do not generate a POP PC statement here, do it in 973490075Sobrien thumb_exit() */ 973590075Sobrien thumb_exit (f, -1, NULL_RTX); 973690075Sobrien return; 973790075Sobrien } 973890075Sobrien 973990075Sobrien fprintf (f, "\t%s\t{", push ? "push" : "pop"); 974090075Sobrien 974190075Sobrien /* Look at the low registers first. */ 974290075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1) 974390075Sobrien { 974490075Sobrien if (lo_mask & 1) 974590075Sobrien { 974690075Sobrien asm_fprintf (f, "%r", regno); 974790075Sobrien 974890075Sobrien if ((lo_mask & ~1) != 0) 974990075Sobrien fprintf (f, ", "); 975090075Sobrien } 975190075Sobrien } 975290075Sobrien 975390075Sobrien if (push && (mask & (1 << LR_REGNUM))) 975490075Sobrien { 975590075Sobrien /* Catch pushing the LR. */ 975690075Sobrien if (mask & 0xFF) 975790075Sobrien fprintf (f, ", "); 975890075Sobrien 975990075Sobrien asm_fprintf (f, "%r", LR_REGNUM); 976090075Sobrien } 976190075Sobrien else if (!push && (mask & (1 << PC_REGNUM))) 976290075Sobrien { 976390075Sobrien /* Catch popping the PC. */ 976490075Sobrien if (TARGET_INTERWORK || TARGET_BACKTRACE) 976590075Sobrien { 976690075Sobrien /* The PC is never poped directly, instead 976790075Sobrien it is popped into r3 and then BX is used. */ 976890075Sobrien fprintf (f, "}\n"); 976990075Sobrien 977090075Sobrien thumb_exit (f, -1, NULL_RTX); 977190075Sobrien 977290075Sobrien return; 977390075Sobrien } 977490075Sobrien else 977590075Sobrien { 977690075Sobrien if (mask & 0xFF) 977790075Sobrien fprintf (f, ", "); 977890075Sobrien 977990075Sobrien asm_fprintf (f, "%r", PC_REGNUM); 978090075Sobrien } 978190075Sobrien } 978290075Sobrien 978390075Sobrien fprintf (f, "}\n"); 978490075Sobrien} 978590075Sobrien 978690075Sobrienvoid 978790075Sobrienthumb_final_prescan_insn (insn) 978890075Sobrien rtx insn; 978990075Sobrien{ 979090075Sobrien if (flag_print_asm_name) 979190075Sobrien asm_fprintf (asm_out_file, "%@ 0x%04x\n", 979290075Sobrien INSN_ADDRESSES (INSN_UID (insn))); 979390075Sobrien} 979490075Sobrien 979590075Sobrienint 979690075Sobrienthumb_shiftable_const (val) 979790075Sobrien unsigned HOST_WIDE_INT val; 979890075Sobrien{ 979990075Sobrien unsigned HOST_WIDE_INT mask = 0xff; 980090075Sobrien int i; 980190075Sobrien 980290075Sobrien if (val == 0) /* XXX */ 980390075Sobrien return 0; 980490075Sobrien 980590075Sobrien for (i = 0; i < 25; i++) 980690075Sobrien if ((val & (mask << i)) == val) 980790075Sobrien return 1; 980890075Sobrien 980990075Sobrien return 0; 981090075Sobrien} 981190075Sobrien 981290075Sobrien/* Returns non-zero if the current function contains, 981390075Sobrien or might contain a far jump. */ 981490075Sobrien 981590075Sobrienint 981690075Sobrienthumb_far_jump_used_p (int in_prologue) 981790075Sobrien{ 981890075Sobrien rtx insn; 981990075Sobrien 982090075Sobrien /* This test is only important for leaf functions. */ 982190075Sobrien /* assert (!leaf_function_p ()); */ 982290075Sobrien 982390075Sobrien /* If we have already decided that far jumps may be used, 982490075Sobrien do not bother checking again, and always return true even if 982590075Sobrien it turns out that they are not being used. Once we have made 982690075Sobrien the decision that far jumps are present (and that hence the link 982790075Sobrien register will be pushed onto the stack) we cannot go back on it. */ 982890075Sobrien if (cfun->machine->far_jump_used) 982990075Sobrien return 1; 983090075Sobrien 983190075Sobrien /* If this function is not being called from the prologue/epilogue 983290075Sobrien generation code then it must be being called from the 983390075Sobrien INITIAL_ELIMINATION_OFFSET macro. */ 983490075Sobrien if (!in_prologue) 983590075Sobrien { 983690075Sobrien /* In this case we know that we are being asked about the elimination 983790075Sobrien of the arg pointer register. If that register is not being used, 983890075Sobrien then there are no arguments on the stack, and we do not have to 983990075Sobrien worry that a far jump might force the prologue to push the link 984090075Sobrien register, changing the stack offsets. In this case we can just 984190075Sobrien return false, since the presence of far jumps in the function will 984290075Sobrien not affect stack offsets. 984390075Sobrien 984490075Sobrien If the arg pointer is live (or if it was live, but has now been 984590075Sobrien eliminated and so set to dead) then we do have to test to see if 984690075Sobrien the function might contain a far jump. This test can lead to some 984790075Sobrien false negatives, since before reload is completed, then length of 984890075Sobrien branch instructions is not known, so gcc defaults to returning their 984990075Sobrien longest length, which in turn sets the far jump attribute to true. 985090075Sobrien 985190075Sobrien A false negative will not result in bad code being generated, but it 985290075Sobrien will result in a needless push and pop of the link register. We 985390075Sobrien hope that this does not occur too often. */ 985490075Sobrien if (regs_ever_live [ARG_POINTER_REGNUM]) 985590075Sobrien cfun->machine->arg_pointer_live = 1; 985690075Sobrien else if (!cfun->machine->arg_pointer_live) 985790075Sobrien return 0; 985890075Sobrien } 985990075Sobrien 986090075Sobrien /* Check to see if the function contains a branch 986190075Sobrien insn with the far jump attribute set. */ 986290075Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 986390075Sobrien { 986490075Sobrien if (GET_CODE (insn) == JUMP_INSN 986590075Sobrien /* Ignore tablejump patterns. */ 986690075Sobrien && GET_CODE (PATTERN (insn)) != ADDR_VEC 986790075Sobrien && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC 986890075Sobrien && get_attr_far_jump (insn) == FAR_JUMP_YES 986990075Sobrien ) 987090075Sobrien { 987190075Sobrien /* Record the fact that we have decied that 987290075Sobrien the function does use far jumps. */ 987390075Sobrien cfun->machine->far_jump_used = 1; 987490075Sobrien return 1; 987590075Sobrien } 987690075Sobrien } 987790075Sobrien 987890075Sobrien return 0; 987990075Sobrien} 988090075Sobrien 988190075Sobrien/* Return non-zero if FUNC must be entered in ARM mode. */ 988290075Sobrien 988390075Sobrienint 988490075Sobrienis_called_in_ARM_mode (func) 988590075Sobrien tree func; 988690075Sobrien{ 988790075Sobrien if (TREE_CODE (func) != FUNCTION_DECL) 988890075Sobrien abort (); 988990075Sobrien 989090075Sobrien /* Ignore the problem about functions whoes address is taken. */ 989190075Sobrien if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func)) 989290075Sobrien return TRUE; 989390075Sobrien 989490075Sobrien#ifdef ARM_PE 989590075Sobrien return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE; 989690075Sobrien#else 989790075Sobrien return FALSE; 989890075Sobrien#endif 989990075Sobrien} 990090075Sobrien 990190075Sobrien/* The bits which aren't usefully expanded as rtl. */ 990290075Sobrien 990390075Sobrienconst char * 990490075Sobrienthumb_unexpanded_epilogue () 990590075Sobrien{ 990690075Sobrien int regno; 990790075Sobrien int live_regs_mask = 0; 990890075Sobrien int high_regs_pushed = 0; 990990075Sobrien int leaf_function = leaf_function_p (); 991090075Sobrien int had_to_push_lr; 991190075Sobrien rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; 991290075Sobrien 991390075Sobrien if (return_used_this_function) 991490075Sobrien return ""; 991590075Sobrien 991690075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 991790075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno] 991890075Sobrien && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) 991990075Sobrien live_regs_mask |= 1 << regno; 992090075Sobrien 992190075Sobrien for (regno = 8; regno < 13; regno++) 992290075Sobrien { 992390075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno] 992490075Sobrien && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) 992590075Sobrien high_regs_pushed++; 992690075Sobrien } 992790075Sobrien 992890075Sobrien /* The prolog may have pushed some high registers to use as 992990075Sobrien work registers. eg the testuite file: 993090075Sobrien gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c 993190075Sobrien compiles to produce: 993290075Sobrien push {r4, r5, r6, r7, lr} 993390075Sobrien mov r7, r9 993490075Sobrien mov r6, r8 993590075Sobrien push {r6, r7} 993690075Sobrien as part of the prolog. We have to undo that pushing here. */ 993790075Sobrien 993890075Sobrien if (high_regs_pushed) 993990075Sobrien { 994090075Sobrien int mask = live_regs_mask; 994190075Sobrien int next_hi_reg; 994290075Sobrien int size; 994390075Sobrien int mode; 994490075Sobrien 994590075Sobrien#ifdef RTX_CODE 994690075Sobrien /* If we can deduce the registers used from the function's return value. 994790075Sobrien This is more reliable that examining regs_ever_live[] because that 994890075Sobrien will be set if the register is ever used in the function, not just if 994990075Sobrien the register is used to hold a return value. */ 995090075Sobrien 995190075Sobrien if (current_function_return_rtx != 0) 995290075Sobrien mode = GET_MODE (current_function_return_rtx); 995390075Sobrien else 995490075Sobrien#endif 995590075Sobrien mode = DECL_MODE (DECL_RESULT (current_function_decl)); 995690075Sobrien 995790075Sobrien size = GET_MODE_SIZE (mode); 995890075Sobrien 995990075Sobrien /* Unless we are returning a type of size > 12 register r3 is 996090075Sobrien available. */ 996190075Sobrien if (size < 13) 996290075Sobrien mask |= 1 << 3; 996390075Sobrien 996490075Sobrien if (mask == 0) 996590075Sobrien /* Oh dear! We have no low registers into which we can pop 996690075Sobrien high registers! */ 996790075Sobrien internal_error 996890075Sobrien ("no low registers available for popping high registers"); 996990075Sobrien 997090075Sobrien for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++) 997190075Sobrien if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg] 997290075Sobrien && !(TARGET_SINGLE_PIC_BASE && (next_hi_reg == arm_pic_register))) 997390075Sobrien break; 997490075Sobrien 997590075Sobrien while (high_regs_pushed) 997690075Sobrien { 997790075Sobrien /* Find lo register(s) into which the high register(s) can 997890075Sobrien be popped. */ 997990075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 998090075Sobrien { 998190075Sobrien if (mask & (1 << regno)) 998290075Sobrien high_regs_pushed--; 998390075Sobrien if (high_regs_pushed == 0) 998490075Sobrien break; 998590075Sobrien } 998690075Sobrien 998790075Sobrien mask &= (2 << regno) - 1; /* A noop if regno == 8 */ 998890075Sobrien 998990075Sobrien /* Pop the values into the low register(s). */ 999090075Sobrien thumb_pushpop (asm_out_file, mask, 0); 999190075Sobrien 999290075Sobrien /* Move the value(s) into the high registers. */ 999390075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 999490075Sobrien { 999590075Sobrien if (mask & (1 << regno)) 999690075Sobrien { 999790075Sobrien asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg, 999890075Sobrien regno); 999990075Sobrien 1000090075Sobrien for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++) 1000190075Sobrien if (regs_ever_live[next_hi_reg] 1000290075Sobrien && !call_used_regs[next_hi_reg] 1000390075Sobrien && !(TARGET_SINGLE_PIC_BASE 1000490075Sobrien && (next_hi_reg == arm_pic_register))) 1000590075Sobrien break; 1000690075Sobrien } 1000790075Sobrien } 1000890075Sobrien } 1000990075Sobrien } 1001090075Sobrien 1001190075Sobrien had_to_push_lr = (live_regs_mask || !leaf_function 1001290075Sobrien || thumb_far_jump_used_p (1)); 1001390075Sobrien 1001490075Sobrien if (TARGET_BACKTRACE 1001590075Sobrien && ((live_regs_mask & 0xFF) == 0) 1001690075Sobrien && regs_ever_live [LAST_ARG_REGNUM] != 0) 1001790075Sobrien { 1001890075Sobrien /* The stack backtrace structure creation code had to 1001990075Sobrien push R7 in order to get a work register, so we pop 1002090075Sobrien it now. */ 1002190075Sobrien live_regs_mask |= (1 << LAST_LO_REGNUM); 1002290075Sobrien } 1002390075Sobrien 1002490075Sobrien if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE) 1002590075Sobrien { 1002690075Sobrien if (had_to_push_lr 1002790075Sobrien && !is_called_in_ARM_mode (current_function_decl) 1002890075Sobrien && !eh_ofs) 1002990075Sobrien live_regs_mask |= 1 << PC_REGNUM; 1003090075Sobrien 1003190075Sobrien /* Either no argument registers were pushed or a backtrace 1003290075Sobrien structure was created which includes an adjusted stack 1003390075Sobrien pointer, so just pop everything. */ 1003490075Sobrien if (live_regs_mask) 1003590075Sobrien thumb_pushpop (asm_out_file, live_regs_mask, FALSE); 1003690075Sobrien 1003790075Sobrien if (eh_ofs) 1003890075Sobrien thumb_exit (asm_out_file, 2, eh_ofs); 1003990075Sobrien /* We have either just popped the return address into the 1004090075Sobrien PC or it is was kept in LR for the entire function or 1004190075Sobrien it is still on the stack because we do not want to 1004290075Sobrien return by doing a pop {pc}. */ 1004390075Sobrien else if ((live_regs_mask & (1 << PC_REGNUM)) == 0) 1004490075Sobrien thumb_exit (asm_out_file, 1004590075Sobrien (had_to_push_lr 1004690075Sobrien && is_called_in_ARM_mode (current_function_decl)) ? 1004790075Sobrien -1 : LR_REGNUM, NULL_RTX); 1004890075Sobrien } 1004990075Sobrien else 1005090075Sobrien { 1005190075Sobrien /* Pop everything but the return address. */ 1005290075Sobrien live_regs_mask &= ~(1 << PC_REGNUM); 1005390075Sobrien 1005490075Sobrien if (live_regs_mask) 1005590075Sobrien thumb_pushpop (asm_out_file, live_regs_mask, FALSE); 1005690075Sobrien 1005790075Sobrien if (had_to_push_lr) 1005890075Sobrien /* Get the return address into a temporary register. */ 1005990075Sobrien thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0); 1006090075Sobrien 1006190075Sobrien /* Remove the argument registers that were pushed onto the stack. */ 1006290075Sobrien asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n", 1006390075Sobrien SP_REGNUM, SP_REGNUM, 1006490075Sobrien current_function_pretend_args_size); 1006590075Sobrien 1006690075Sobrien if (eh_ofs) 1006790075Sobrien thumb_exit (asm_out_file, 2, eh_ofs); 1006890075Sobrien else 1006990075Sobrien thumb_exit (asm_out_file, 1007090075Sobrien had_to_push_lr ? LAST_ARG_REGNUM : LR_REGNUM, NULL_RTX); 1007190075Sobrien } 1007290075Sobrien 1007390075Sobrien return ""; 1007490075Sobrien} 1007590075Sobrien 1007690075Sobrien/* Functions to save and restore machine-specific function data. */ 1007790075Sobrien 1007890075Sobrienstatic void 1007990075Sobrienarm_mark_machine_status (p) 1008090075Sobrien struct function * p; 1008190075Sobrien{ 1008290075Sobrien machine_function *machine = p->machine; 1008390075Sobrien 1008490075Sobrien if (machine) 1008590075Sobrien ggc_mark_rtx (machine->eh_epilogue_sp_ofs); 1008690075Sobrien} 1008790075Sobrien 1008890075Sobrienstatic void 1008990075Sobrienarm_init_machine_status (p) 1009090075Sobrien struct function * p; 1009190075Sobrien{ 1009290075Sobrien p->machine = 1009390075Sobrien (machine_function *) xcalloc (1, sizeof (machine_function)); 1009490075Sobrien 1009590075Sobrien#if ARM_FT_UNKNOWWN != 0 1009690075Sobrien ((machine_function *) p->machine)->func_type = ARM_FT_UNKNOWN; 1009790075Sobrien#endif 1009890075Sobrien} 1009990075Sobrien 1010090075Sobrienstatic void 1010190075Sobrienarm_free_machine_status (p) 1010290075Sobrien struct function * p; 1010390075Sobrien{ 1010490075Sobrien if (p->machine) 1010590075Sobrien { 1010690075Sobrien free (p->machine); 1010790075Sobrien p->machine = NULL; 1010890075Sobrien } 1010990075Sobrien} 1011090075Sobrien 1011190075Sobrien/* Return an RTX indicating where the return address to the 1011290075Sobrien calling function can be found. */ 1011390075Sobrien 1011490075Sobrienrtx 1011590075Sobrienarm_return_addr (count, frame) 1011690075Sobrien int count; 1011790075Sobrien rtx frame ATTRIBUTE_UNUSED; 1011890075Sobrien{ 1011990075Sobrien if (count != 0) 1012090075Sobrien return NULL_RTX; 1012190075Sobrien 1012290075Sobrien if (TARGET_APCS_32) 1012390075Sobrien return get_hard_reg_initial_val (Pmode, LR_REGNUM); 1012490075Sobrien else 1012590075Sobrien { 1012690075Sobrien rtx lr = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM), 1012790075Sobrien GEN_INT (RETURN_ADDR_MASK26)); 1012890075Sobrien return get_func_hard_reg_initial_val (cfun, lr); 1012990075Sobrien } 1013090075Sobrien} 1013190075Sobrien 1013290075Sobrien/* Do anything needed before RTL is emitted for each function. */ 1013390075Sobrien 1013490075Sobrienvoid 1013590075Sobrienarm_init_expanders () 1013690075Sobrien{ 1013790075Sobrien /* Arrange to initialize and mark the machine per-function status. */ 1013890075Sobrien init_machine_status = arm_init_machine_status; 1013990075Sobrien mark_machine_status = arm_mark_machine_status; 1014090075Sobrien free_machine_status = arm_free_machine_status; 1014190075Sobrien} 1014290075Sobrien 1014390075Sobrien/* Generate the rest of a function's prologue. */ 1014490075Sobrien 1014590075Sobrienvoid 1014690075Sobrienthumb_expand_prologue () 1014790075Sobrien{ 1014890075Sobrien HOST_WIDE_INT amount = (get_frame_size () 1014990075Sobrien + current_function_outgoing_args_size); 1015090075Sobrien unsigned long func_type; 1015190075Sobrien 1015290075Sobrien func_type = arm_current_func_type (); 1015390075Sobrien 1015490075Sobrien /* Naked functions don't have prologues. */ 1015590075Sobrien if (IS_NAKED (func_type)) 1015690075Sobrien return; 1015790075Sobrien 1015890075Sobrien if (IS_INTERRUPT (func_type)) 1015990075Sobrien { 1016090075Sobrien error ("interrupt Service Routines cannot be coded in Thumb mode"); 1016190075Sobrien return; 1016290075Sobrien } 1016390075Sobrien 1016490075Sobrien if (frame_pointer_needed) 1016590075Sobrien emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); 1016690075Sobrien 1016790075Sobrien if (amount) 1016890075Sobrien { 1016990075Sobrien amount = ROUND_UP (amount); 1017090075Sobrien 1017190075Sobrien if (amount < 512) 1017290075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 1017390075Sobrien GEN_INT (- amount))); 1017490075Sobrien else 1017590075Sobrien { 1017690075Sobrien int regno; 1017790075Sobrien rtx reg; 1017890075Sobrien 1017990075Sobrien /* The stack decrement is too big for an immediate value in a single 1018090075Sobrien insn. In theory we could issue multiple subtracts, but after 1018190075Sobrien three of them it becomes more space efficient to place the full 1018290075Sobrien value in the constant pool and load into a register. (Also the 1018390075Sobrien ARM debugger really likes to see only one stack decrement per 1018490075Sobrien function). So instead we look for a scratch register into which 1018590075Sobrien we can load the decrement, and then we subtract this from the 1018690075Sobrien stack pointer. Unfortunately on the thumb the only available 1018790075Sobrien scratch registers are the argument registers, and we cannot use 1018890075Sobrien these as they may hold arguments to the function. Instead we 1018990075Sobrien attempt to locate a call preserved register which is used by this 1019090075Sobrien function. If we can find one, then we know that it will have 1019190075Sobrien been pushed at the start of the prologue and so we can corrupt 1019290075Sobrien it now. */ 1019390075Sobrien for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++) 1019490075Sobrien if (regs_ever_live[regno] 1019590075Sobrien && !call_used_regs[regno] /* Paranoia */ 1019690075Sobrien && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)) 1019790075Sobrien && !(frame_pointer_needed 1019890075Sobrien && (regno == THUMB_HARD_FRAME_POINTER_REGNUM))) 1019990075Sobrien break; 1020090075Sobrien 1020190075Sobrien if (regno > LAST_LO_REGNUM) /* Very unlikely */ 1020290075Sobrien { 1020390075Sobrien rtx spare = gen_rtx (REG, SImode, IP_REGNUM); 1020490075Sobrien 1020590075Sobrien /* Choose an arbitary, non-argument low register. */ 1020690075Sobrien reg = gen_rtx (REG, SImode, LAST_LO_REGNUM); 1020790075Sobrien 1020890075Sobrien /* Save it by copying it into a high, scratch register. */ 1020990075Sobrien emit_insn (gen_movsi (spare, reg)); 1021090075Sobrien /* Add a USE to stop propagate_one_insn() from barfing. */ 1021190075Sobrien emit_insn (gen_prologue_use (spare)); 1021290075Sobrien 1021390075Sobrien /* Decrement the stack. */ 1021490075Sobrien emit_insn (gen_movsi (reg, GEN_INT (- amount))); 1021590075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 1021690075Sobrien reg)); 1021790075Sobrien 1021890075Sobrien /* Restore the low register's original value. */ 1021990075Sobrien emit_insn (gen_movsi (reg, spare)); 1022090075Sobrien 1022190075Sobrien /* Emit a USE of the restored scratch register, so that flow 1022290075Sobrien analysis will not consider the restore redundant. The 1022390075Sobrien register won't be used again in this function and isn't 1022490075Sobrien restored by the epilogue. */ 1022590075Sobrien emit_insn (gen_prologue_use (reg)); 1022690075Sobrien } 1022790075Sobrien else 1022890075Sobrien { 1022990075Sobrien reg = gen_rtx (REG, SImode, regno); 1023090075Sobrien 1023190075Sobrien emit_insn (gen_movsi (reg, GEN_INT (- amount))); 1023290075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 1023390075Sobrien reg)); 1023490075Sobrien } 1023590075Sobrien } 1023690075Sobrien } 1023790075Sobrien 1023890075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 1023990075Sobrien emit_insn (gen_blockage ()); 1024090075Sobrien} 1024190075Sobrien 1024290075Sobrienvoid 1024390075Sobrienthumb_expand_epilogue () 1024490075Sobrien{ 1024590075Sobrien HOST_WIDE_INT amount = (get_frame_size () 1024690075Sobrien + current_function_outgoing_args_size); 1024790075Sobrien 1024890075Sobrien /* Naked functions don't have prologues. */ 1024990075Sobrien if (IS_NAKED (arm_current_func_type ())) 1025090075Sobrien return; 1025190075Sobrien 1025290075Sobrien if (frame_pointer_needed) 1025390075Sobrien emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); 1025490075Sobrien else if (amount) 1025590075Sobrien { 1025690075Sobrien amount = ROUND_UP (amount); 1025790075Sobrien 1025890075Sobrien if (amount < 512) 1025990075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 1026090075Sobrien GEN_INT (amount))); 1026190075Sobrien else 1026290075Sobrien { 1026390075Sobrien /* r3 is always free in the epilogue. */ 1026490075Sobrien rtx reg = gen_rtx (REG, SImode, LAST_ARG_REGNUM); 1026590075Sobrien 1026690075Sobrien emit_insn (gen_movsi (reg, GEN_INT (amount))); 1026790075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); 1026890075Sobrien } 1026990075Sobrien } 1027090075Sobrien 1027190075Sobrien /* Emit a USE (stack_pointer_rtx), so that 1027290075Sobrien the stack adjustment will not be deleted. */ 1027390075Sobrien emit_insn (gen_prologue_use (stack_pointer_rtx)); 1027490075Sobrien 1027590075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 1027690075Sobrien emit_insn (gen_blockage ()); 1027790075Sobrien} 1027890075Sobrien 1027990075Sobrienstatic void 1028090075Sobrienthumb_output_function_prologue (f, size) 1028190075Sobrien FILE * f; 1028290075Sobrien HOST_WIDE_INT size ATTRIBUTE_UNUSED; 1028390075Sobrien{ 1028490075Sobrien int live_regs_mask = 0; 1028590075Sobrien int high_regs_pushed = 0; 1028690075Sobrien int regno; 1028790075Sobrien 1028890075Sobrien if (IS_NAKED (arm_current_func_type ())) 1028990075Sobrien return; 1029090075Sobrien 1029190075Sobrien if (is_called_in_ARM_mode (current_function_decl)) 1029290075Sobrien { 1029390075Sobrien const char * name; 1029490075Sobrien 1029590075Sobrien if (GET_CODE (DECL_RTL (current_function_decl)) != MEM) 1029690075Sobrien abort (); 1029790075Sobrien if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF) 1029890075Sobrien abort (); 1029990075Sobrien name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); 1030090075Sobrien 1030190075Sobrien /* Generate code sequence to switch us into Thumb mode. */ 1030290075Sobrien /* The .code 32 directive has already been emitted by 1030390075Sobrien ASM_DECLARE_FUNCTION_NAME. */ 1030490075Sobrien asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM); 1030590075Sobrien asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM); 1030690075Sobrien 1030790075Sobrien /* Generate a label, so that the debugger will notice the 1030890075Sobrien change in instruction sets. This label is also used by 1030990075Sobrien the assembler to bypass the ARM code when this function 1031090075Sobrien is called from a Thumb encoded function elsewhere in the 1031190075Sobrien same file. Hence the definition of STUB_NAME here must 1031290075Sobrien agree with the definition in gas/config/tc-arm.c */ 1031390075Sobrien 1031490075Sobrien#define STUB_NAME ".real_start_of" 1031590075Sobrien 1031690075Sobrien asm_fprintf (f, "\t.code\t16\n"); 1031790075Sobrien#ifdef ARM_PE 1031890075Sobrien if (arm_dllexport_name_p (name)) 1031990075Sobrien name = arm_strip_name_encoding (name); 1032090075Sobrien#endif 1032190075Sobrien asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name); 1032290075Sobrien asm_fprintf (f, "\t.thumb_func\n"); 1032390075Sobrien asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name); 1032490075Sobrien } 1032590075Sobrien 1032690075Sobrien if (current_function_pretend_args_size) 1032790075Sobrien { 1032896263Sobrien if (cfun->machine->uses_anonymous_args) 1032990075Sobrien { 1033090075Sobrien int num_pushes; 1033190075Sobrien 1033290075Sobrien asm_fprintf (f, "\tpush\t{"); 1033390075Sobrien 1033490075Sobrien num_pushes = NUM_INTS (current_function_pretend_args_size); 1033590075Sobrien 1033690075Sobrien for (regno = LAST_ARG_REGNUM + 1 - num_pushes; 1033790075Sobrien regno <= LAST_ARG_REGNUM; 1033890075Sobrien regno++) 1033990075Sobrien asm_fprintf (f, "%r%s", regno, 1034090075Sobrien regno == LAST_ARG_REGNUM ? "" : ", "); 1034190075Sobrien 1034290075Sobrien asm_fprintf (f, "}\n"); 1034390075Sobrien } 1034490075Sobrien else 1034590075Sobrien asm_fprintf (f, "\tsub\t%r, %r, #%d\n", 1034690075Sobrien SP_REGNUM, SP_REGNUM, 1034790075Sobrien current_function_pretend_args_size); 1034890075Sobrien } 1034990075Sobrien 1035090075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 1035190075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno] 1035290075Sobrien && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) 1035390075Sobrien live_regs_mask |= 1 << regno; 1035490075Sobrien 1035590075Sobrien if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1)) 1035690075Sobrien live_regs_mask |= 1 << LR_REGNUM; 1035790075Sobrien 1035890075Sobrien if (TARGET_BACKTRACE) 1035990075Sobrien { 1036090075Sobrien int offset; 1036190075Sobrien int work_register = 0; 1036290075Sobrien int wr; 1036390075Sobrien 1036490075Sobrien /* We have been asked to create a stack backtrace structure. 1036590075Sobrien The code looks like this: 1036690075Sobrien 1036790075Sobrien 0 .align 2 1036890075Sobrien 0 func: 1036990075Sobrien 0 sub SP, #16 Reserve space for 4 registers. 1037090075Sobrien 2 push {R7} Get a work register. 1037190075Sobrien 4 add R7, SP, #20 Get the stack pointer before the push. 1037290075Sobrien 6 str R7, [SP, #8] Store the stack pointer (before reserving the space). 1037390075Sobrien 8 mov R7, PC Get hold of the start of this code plus 12. 1037490075Sobrien 10 str R7, [SP, #16] Store it. 1037590075Sobrien 12 mov R7, FP Get hold of the current frame pointer. 1037690075Sobrien 14 str R7, [SP, #4] Store it. 1037790075Sobrien 16 mov R7, LR Get hold of the current return address. 1037890075Sobrien 18 str R7, [SP, #12] Store it. 1037990075Sobrien 20 add R7, SP, #16 Point at the start of the backtrace structure. 1038090075Sobrien 22 mov FP, R7 Put this value into the frame pointer. */ 1038190075Sobrien 1038290075Sobrien if ((live_regs_mask & 0xFF) == 0) 1038390075Sobrien { 1038490075Sobrien /* See if the a4 register is free. */ 1038590075Sobrien 1038690075Sobrien if (regs_ever_live [LAST_ARG_REGNUM] == 0) 1038790075Sobrien work_register = LAST_ARG_REGNUM; 1038890075Sobrien else /* We must push a register of our own */ 1038990075Sobrien live_regs_mask |= (1 << LAST_LO_REGNUM); 1039090075Sobrien } 1039190075Sobrien 1039290075Sobrien if (work_register == 0) 1039390075Sobrien { 1039490075Sobrien /* Select a register from the list that will be pushed to 1039590075Sobrien use as our work register. */ 1039690075Sobrien for (work_register = (LAST_LO_REGNUM + 1); work_register--;) 1039790075Sobrien if ((1 << work_register) & live_regs_mask) 1039890075Sobrien break; 1039990075Sobrien } 1040090075Sobrien 1040190075Sobrien asm_fprintf 1040290075Sobrien (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n", 1040390075Sobrien SP_REGNUM, SP_REGNUM); 1040490075Sobrien 1040590075Sobrien if (live_regs_mask) 1040690075Sobrien thumb_pushpop (f, live_regs_mask, 1); 1040790075Sobrien 1040890075Sobrien for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1) 1040990075Sobrien if (wr & live_regs_mask) 1041090075Sobrien offset += 4; 1041190075Sobrien 1041290075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, 1041390075Sobrien offset + 16 + current_function_pretend_args_size); 1041490075Sobrien 1041590075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1041690075Sobrien offset + 4); 1041790075Sobrien 1041890075Sobrien /* Make sure that the instruction fetching the PC is in the right place 1041990075Sobrien to calculate "start of backtrace creation code + 12". */ 1042090075Sobrien if (live_regs_mask) 1042190075Sobrien { 1042290075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); 1042390075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1042490075Sobrien offset + 12); 1042590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, 1042690075Sobrien ARM_HARD_FRAME_POINTER_REGNUM); 1042790075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1042890075Sobrien offset); 1042990075Sobrien } 1043090075Sobrien else 1043190075Sobrien { 1043290075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, 1043390075Sobrien ARM_HARD_FRAME_POINTER_REGNUM); 1043490075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1043590075Sobrien offset); 1043690075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); 1043790075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1043890075Sobrien offset + 12); 1043990075Sobrien } 1044090075Sobrien 1044190075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, LR_REGNUM); 1044290075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1044390075Sobrien offset + 8); 1044490075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, 1044590075Sobrien offset + 12); 1044690075Sobrien asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n", 1044790075Sobrien ARM_HARD_FRAME_POINTER_REGNUM, work_register); 1044890075Sobrien } 1044990075Sobrien else if (live_regs_mask) 1045090075Sobrien thumb_pushpop (f, live_regs_mask, 1); 1045190075Sobrien 1045290075Sobrien for (regno = 8; regno < 13; regno++) 1045390075Sobrien { 1045490075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno] 1045590075Sobrien && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))) 1045690075Sobrien high_regs_pushed++; 1045790075Sobrien } 1045890075Sobrien 1045990075Sobrien if (high_regs_pushed) 1046090075Sobrien { 1046190075Sobrien int pushable_regs = 0; 1046290075Sobrien int mask = live_regs_mask & 0xff; 1046390075Sobrien int next_hi_reg; 1046490075Sobrien 1046590075Sobrien for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--) 1046690075Sobrien { 1046790075Sobrien if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg] 1046890075Sobrien && !(TARGET_SINGLE_PIC_BASE 1046990075Sobrien && (next_hi_reg == arm_pic_register))) 1047090075Sobrien break; 1047190075Sobrien } 1047290075Sobrien 1047390075Sobrien pushable_regs = mask; 1047490075Sobrien 1047590075Sobrien if (pushable_regs == 0) 1047690075Sobrien { 1047790075Sobrien /* Desperation time -- this probably will never happen. */ 1047890075Sobrien if (regs_ever_live[LAST_ARG_REGNUM] 1047990075Sobrien || !call_used_regs[LAST_ARG_REGNUM]) 1048090075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM); 1048190075Sobrien mask = 1 << LAST_ARG_REGNUM; 1048290075Sobrien } 1048390075Sobrien 1048490075Sobrien while (high_regs_pushed > 0) 1048590075Sobrien { 1048690075Sobrien for (regno = LAST_LO_REGNUM; regno >= 0; regno--) 1048790075Sobrien { 1048890075Sobrien if (mask & (1 << regno)) 1048990075Sobrien { 1049090075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg); 1049190075Sobrien 1049290075Sobrien high_regs_pushed--; 1049390075Sobrien 1049490075Sobrien if (high_regs_pushed) 1049590075Sobrien for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM; 1049690075Sobrien next_hi_reg--) 1049790075Sobrien { 1049890075Sobrien if (regs_ever_live[next_hi_reg] 1049990075Sobrien && !call_used_regs[next_hi_reg] 1050090075Sobrien && !(TARGET_SINGLE_PIC_BASE 1050190075Sobrien && (next_hi_reg == arm_pic_register))) 1050290075Sobrien break; 1050390075Sobrien } 1050490075Sobrien else 1050590075Sobrien { 1050690075Sobrien mask &= ~((1 << regno) - 1); 1050790075Sobrien break; 1050890075Sobrien } 1050990075Sobrien } 1051090075Sobrien } 1051190075Sobrien 1051290075Sobrien thumb_pushpop (f, mask, 1); 1051390075Sobrien } 1051490075Sobrien 1051590075Sobrien if (pushable_regs == 0 1051690075Sobrien && (regs_ever_live[LAST_ARG_REGNUM] 1051790075Sobrien || !call_used_regs[LAST_ARG_REGNUM])) 1051890075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); 1051990075Sobrien } 1052090075Sobrien} 1052190075Sobrien 1052290075Sobrien/* Handle the case of a double word load into a low register from 1052390075Sobrien a computed memory address. The computed address may involve a 1052490075Sobrien register which is overwritten by the load. */ 1052590075Sobrien 1052690075Sobrienconst char * 1052790075Sobrienthumb_load_double_from_address (operands) 1052890075Sobrien rtx *operands; 1052990075Sobrien{ 1053090075Sobrien rtx addr; 1053190075Sobrien rtx base; 1053290075Sobrien rtx offset; 1053390075Sobrien rtx arg1; 1053490075Sobrien rtx arg2; 1053590075Sobrien 1053690075Sobrien if (GET_CODE (operands[0]) != REG) 1053790075Sobrien abort (); 1053890075Sobrien 1053990075Sobrien if (GET_CODE (operands[1]) != MEM) 1054090075Sobrien abort (); 1054190075Sobrien 1054290075Sobrien /* Get the memory address. */ 1054390075Sobrien addr = XEXP (operands[1], 0); 1054490075Sobrien 1054590075Sobrien /* Work out how the memory address is computed. */ 1054690075Sobrien switch (GET_CODE (addr)) 1054790075Sobrien { 1054890075Sobrien case REG: 1054990075Sobrien operands[2] = gen_rtx (MEM, SImode, 1055090075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1055190075Sobrien 1055290075Sobrien if (REGNO (operands[0]) == REGNO (addr)) 1055390075Sobrien { 1055490075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1055590075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1055690075Sobrien } 1055790075Sobrien else 1055890075Sobrien { 1055990075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1056090075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1056190075Sobrien } 1056290075Sobrien break; 1056390075Sobrien 1056490075Sobrien case CONST: 1056590075Sobrien /* Compute <address> + 4 for the high order load. */ 1056690075Sobrien operands[2] = gen_rtx (MEM, SImode, 1056790075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1056890075Sobrien 1056990075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1057090075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1057190075Sobrien break; 1057290075Sobrien 1057390075Sobrien case PLUS: 1057490075Sobrien arg1 = XEXP (addr, 0); 1057590075Sobrien arg2 = XEXP (addr, 1); 1057690075Sobrien 1057790075Sobrien if (CONSTANT_P (arg1)) 1057890075Sobrien base = arg2, offset = arg1; 1057990075Sobrien else 1058090075Sobrien base = arg1, offset = arg2; 1058190075Sobrien 1058290075Sobrien if (GET_CODE (base) != REG) 1058390075Sobrien abort (); 1058490075Sobrien 1058590075Sobrien /* Catch the case of <address> = <reg> + <reg> */ 1058690075Sobrien if (GET_CODE (offset) == REG) 1058790075Sobrien { 1058890075Sobrien int reg_offset = REGNO (offset); 1058990075Sobrien int reg_base = REGNO (base); 1059090075Sobrien int reg_dest = REGNO (operands[0]); 1059190075Sobrien 1059290075Sobrien /* Add the base and offset registers together into the 1059390075Sobrien higher destination register. */ 1059490075Sobrien asm_fprintf (asm_out_file, "\tadd\t%r, %r, %r", 1059590075Sobrien reg_dest + 1, reg_base, reg_offset); 1059690075Sobrien 1059790075Sobrien /* Load the lower destination register from the address in 1059890075Sobrien the higher destination register. */ 1059990075Sobrien asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #0]", 1060090075Sobrien reg_dest, reg_dest + 1); 1060190075Sobrien 1060290075Sobrien /* Load the higher destination register from its own address 1060390075Sobrien plus 4. */ 1060490075Sobrien asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #4]", 1060590075Sobrien reg_dest + 1, reg_dest + 1); 1060690075Sobrien } 1060790075Sobrien else 1060890075Sobrien { 1060990075Sobrien /* Compute <address> + 4 for the high order load. */ 1061090075Sobrien operands[2] = gen_rtx (MEM, SImode, 1061190075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1061290075Sobrien 1061390075Sobrien /* If the computed address is held in the low order register 1061490075Sobrien then load the high order register first, otherwise always 1061590075Sobrien load the low order register first. */ 1061690075Sobrien if (REGNO (operands[0]) == REGNO (base)) 1061790075Sobrien { 1061890075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1061990075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1062090075Sobrien } 1062190075Sobrien else 1062290075Sobrien { 1062390075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1062490075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1062590075Sobrien } 1062690075Sobrien } 1062790075Sobrien break; 1062890075Sobrien 1062990075Sobrien case LABEL_REF: 1063090075Sobrien /* With no registers to worry about we can just load the value 1063190075Sobrien directly. */ 1063290075Sobrien operands[2] = gen_rtx (MEM, SImode, 1063390075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1063490075Sobrien 1063590075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1063690075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1063790075Sobrien break; 1063890075Sobrien 1063990075Sobrien default: 1064090075Sobrien abort (); 1064190075Sobrien break; 1064290075Sobrien } 1064390075Sobrien 1064490075Sobrien return ""; 1064590075Sobrien} 1064690075Sobrien 1064790075Sobrien 1064890075Sobrienconst char * 1064990075Sobrienthumb_output_move_mem_multiple (n, operands) 1065090075Sobrien int n; 1065190075Sobrien rtx * operands; 1065290075Sobrien{ 1065390075Sobrien rtx tmp; 1065490075Sobrien 1065590075Sobrien switch (n) 1065690075Sobrien { 1065790075Sobrien case 2: 1065890075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1065990075Sobrien { 1066090075Sobrien tmp = operands[4]; 1066190075Sobrien operands[4] = operands[5]; 1066290075Sobrien operands[5] = tmp; 1066390075Sobrien } 1066490075Sobrien output_asm_insn ("ldmia\t%1!, {%4, %5}", operands); 1066590075Sobrien output_asm_insn ("stmia\t%0!, {%4, %5}", operands); 1066690075Sobrien break; 1066790075Sobrien 1066890075Sobrien case 3: 1066990075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1067090075Sobrien { 1067190075Sobrien tmp = operands[4]; 1067290075Sobrien operands[4] = operands[5]; 1067390075Sobrien operands[5] = tmp; 1067490075Sobrien } 1067590075Sobrien if (REGNO (operands[5]) > REGNO (operands[6])) 1067690075Sobrien { 1067790075Sobrien tmp = operands[5]; 1067890075Sobrien operands[5] = operands[6]; 1067990075Sobrien operands[6] = tmp; 1068090075Sobrien } 1068190075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1068290075Sobrien { 1068390075Sobrien tmp = operands[4]; 1068490075Sobrien operands[4] = operands[5]; 1068590075Sobrien operands[5] = tmp; 1068690075Sobrien } 1068790075Sobrien 1068890075Sobrien output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands); 1068990075Sobrien output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands); 1069090075Sobrien break; 1069190075Sobrien 1069290075Sobrien default: 1069390075Sobrien abort (); 1069490075Sobrien } 1069590075Sobrien 1069690075Sobrien return ""; 1069790075Sobrien} 1069890075Sobrien 1069990075Sobrien/* Routines for generating rtl. */ 1070090075Sobrien 1070190075Sobrienvoid 1070290075Sobrienthumb_expand_movstrqi (operands) 1070390075Sobrien rtx * operands; 1070490075Sobrien{ 1070590075Sobrien rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); 1070690075Sobrien rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); 1070790075Sobrien HOST_WIDE_INT len = INTVAL (operands[2]); 1070890075Sobrien HOST_WIDE_INT offset = 0; 1070990075Sobrien 1071090075Sobrien while (len >= 12) 1071190075Sobrien { 1071290075Sobrien emit_insn (gen_movmem12b (out, in, out, in)); 1071390075Sobrien len -= 12; 1071490075Sobrien } 1071590075Sobrien 1071690075Sobrien if (len >= 8) 1071790075Sobrien { 1071890075Sobrien emit_insn (gen_movmem8b (out, in, out, in)); 1071990075Sobrien len -= 8; 1072090075Sobrien } 1072190075Sobrien 1072290075Sobrien if (len >= 4) 1072390075Sobrien { 1072490075Sobrien rtx reg = gen_reg_rtx (SImode); 1072590075Sobrien emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in))); 1072690075Sobrien emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg)); 1072790075Sobrien len -= 4; 1072890075Sobrien offset += 4; 1072990075Sobrien } 1073090075Sobrien 1073190075Sobrien if (len >= 2) 1073290075Sobrien { 1073390075Sobrien rtx reg = gen_reg_rtx (HImode); 1073490075Sobrien emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, 1073590075Sobrien plus_constant (in, offset)))); 1073690075Sobrien emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)), 1073790075Sobrien reg)); 1073890075Sobrien len -= 2; 1073990075Sobrien offset += 2; 1074090075Sobrien } 1074190075Sobrien 1074290075Sobrien if (len) 1074390075Sobrien { 1074490075Sobrien rtx reg = gen_reg_rtx (QImode); 1074590075Sobrien emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode, 1074690075Sobrien plus_constant (in, offset)))); 1074790075Sobrien emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)), 1074890075Sobrien reg)); 1074990075Sobrien } 1075090075Sobrien} 1075190075Sobrien 1075290075Sobrienint 1075390075Sobrienthumb_cmp_operand (op, mode) 1075490075Sobrien rtx op; 1075590075Sobrien enum machine_mode mode; 1075690075Sobrien{ 1075790075Sobrien return ((GET_CODE (op) == CONST_INT 1075890075Sobrien && (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256) 1075990075Sobrien || register_operand (op, mode)); 1076090075Sobrien} 1076190075Sobrien 1076290075Sobrienstatic const char * 1076390075Sobrienthumb_condition_code (x, invert) 1076490075Sobrien rtx x; 1076590075Sobrien int invert; 1076690075Sobrien{ 1076790075Sobrien static const char * const conds[] = 1076890075Sobrien { 1076990075Sobrien "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", 1077090075Sobrien "hi", "ls", "ge", "lt", "gt", "le" 1077190075Sobrien }; 1077290075Sobrien int val; 1077390075Sobrien 1077490075Sobrien switch (GET_CODE (x)) 1077590075Sobrien { 1077690075Sobrien case EQ: val = 0; break; 1077790075Sobrien case NE: val = 1; break; 1077890075Sobrien case GEU: val = 2; break; 1077990075Sobrien case LTU: val = 3; break; 1078090075Sobrien case GTU: val = 8; break; 1078190075Sobrien case LEU: val = 9; break; 1078290075Sobrien case GE: val = 10; break; 1078390075Sobrien case LT: val = 11; break; 1078490075Sobrien case GT: val = 12; break; 1078590075Sobrien case LE: val = 13; break; 1078690075Sobrien default: 1078790075Sobrien abort (); 1078890075Sobrien } 1078990075Sobrien 1079090075Sobrien return conds[val ^ invert]; 1079190075Sobrien} 1079290075Sobrien 1079390075Sobrien/* Handle storing a half-word to memory during reload. */ 1079490075Sobrien 1079590075Sobrienvoid 1079690075Sobrienthumb_reload_out_hi (operands) 1079790075Sobrien rtx * operands; 1079890075Sobrien{ 1079990075Sobrien emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2])); 1080090075Sobrien} 1080190075Sobrien 1080290075Sobrien/* Handle storing a half-word to memory during reload. */ 1080390075Sobrien 1080490075Sobrienvoid 1080590075Sobrienthumb_reload_in_hi (operands) 1080690075Sobrien rtx * operands ATTRIBUTE_UNUSED; 1080790075Sobrien{ 1080890075Sobrien abort (); 1080990075Sobrien} 1081090075Sobrien 1081190075Sobrien/* Return the length of a function name prefix 1081290075Sobrien that starts with the character 'c'. */ 1081390075Sobrien 1081490075Sobrienstatic int 1081590075Sobrienarm_get_strip_length (char c) 1081690075Sobrien{ 1081790075Sobrien switch (c) 1081890075Sobrien { 1081990075Sobrien ARM_NAME_ENCODING_LENGTHS 1082090075Sobrien default: return 0; 1082190075Sobrien } 1082290075Sobrien} 1082390075Sobrien 1082490075Sobrien/* Return a pointer to a function's name with any 1082590075Sobrien and all prefix encodings stripped from it. */ 1082690075Sobrien 1082790075Sobrienconst char * 1082890075Sobrienarm_strip_name_encoding (const char * name) 1082990075Sobrien{ 1083090075Sobrien int skip; 1083190075Sobrien 1083290075Sobrien while ((skip = arm_get_strip_length (* name))) 1083390075Sobrien name += skip; 1083490075Sobrien 1083590075Sobrien return name; 1083690075Sobrien} 1083790075Sobrien 1083890075Sobrien#ifdef AOF_ASSEMBLER 1083990075Sobrien/* Special functions only needed when producing AOF syntax assembler. */ 1084090075Sobrien 1084190075Sobrienrtx aof_pic_label = NULL_RTX; 1084290075Sobrienstruct pic_chain 1084390075Sobrien{ 1084490075Sobrien struct pic_chain * next; 1084590075Sobrien const char * symname; 1084690075Sobrien}; 1084790075Sobrien 1084890075Sobrienstatic struct pic_chain * aof_pic_chain = NULL; 1084990075Sobrien 1085090075Sobrienrtx 1085190075Sobrienaof_pic_entry (x) 1085290075Sobrien rtx x; 1085390075Sobrien{ 1085490075Sobrien struct pic_chain ** chainp; 1085590075Sobrien int offset; 1085690075Sobrien 1085790075Sobrien if (aof_pic_label == NULL_RTX) 1085890075Sobrien { 1085990075Sobrien /* We mark this here and not in arm_add_gc_roots() to avoid 1086090075Sobrien polluting even more code with ifdefs, and because it never 1086190075Sobrien contains anything useful until we assign to it here. */ 1086290075Sobrien ggc_add_rtx_root (&aof_pic_label, 1); 1086390075Sobrien aof_pic_label = gen_rtx_SYMBOL_REF (Pmode, "x$adcons"); 1086490075Sobrien } 1086590075Sobrien 1086690075Sobrien for (offset = 0, chainp = &aof_pic_chain; *chainp; 1086790075Sobrien offset += 4, chainp = &(*chainp)->next) 1086890075Sobrien if ((*chainp)->symname == XSTR (x, 0)) 1086990075Sobrien return plus_constant (aof_pic_label, offset); 1087090075Sobrien 1087190075Sobrien *chainp = (struct pic_chain *) xmalloc (sizeof (struct pic_chain)); 1087290075Sobrien (*chainp)->next = NULL; 1087390075Sobrien (*chainp)->symname = XSTR (x, 0); 1087490075Sobrien return plus_constant (aof_pic_label, offset); 1087590075Sobrien} 1087690075Sobrien 1087790075Sobrienvoid 1087890075Sobrienaof_dump_pic_table (f) 1087990075Sobrien FILE * f; 1088090075Sobrien{ 1088190075Sobrien struct pic_chain * chain; 1088290075Sobrien 1088390075Sobrien if (aof_pic_chain == NULL) 1088490075Sobrien return; 1088590075Sobrien 1088690075Sobrien asm_fprintf (f, "\tAREA |%r$$adcons|, BASED %r\n", 1088790075Sobrien PIC_OFFSET_TABLE_REGNUM, 1088890075Sobrien PIC_OFFSET_TABLE_REGNUM); 1088990075Sobrien fputs ("|x$adcons|\n", f); 1089090075Sobrien 1089190075Sobrien for (chain = aof_pic_chain; chain; chain = chain->next) 1089290075Sobrien { 1089390075Sobrien fputs ("\tDCD\t", f); 1089490075Sobrien assemble_name (f, chain->symname); 1089590075Sobrien fputs ("\n", f); 1089690075Sobrien } 1089790075Sobrien} 1089890075Sobrien 1089990075Sobrienint arm_text_section_count = 1; 1090090075Sobrien 1090190075Sobrienchar * 1090290075Sobrienaof_text_section () 1090390075Sobrien{ 1090490075Sobrien static char buf[100]; 1090590075Sobrien sprintf (buf, "\tAREA |C$$code%d|, CODE, READONLY", 1090690075Sobrien arm_text_section_count++); 1090790075Sobrien if (flag_pic) 1090890075Sobrien strcat (buf, ", PIC, REENTRANT"); 1090990075Sobrien return buf; 1091090075Sobrien} 1091190075Sobrien 1091290075Sobrienstatic int arm_data_section_count = 1; 1091390075Sobrien 1091490075Sobrienchar * 1091590075Sobrienaof_data_section () 1091690075Sobrien{ 1091790075Sobrien static char buf[100]; 1091890075Sobrien sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++); 1091990075Sobrien return buf; 1092090075Sobrien} 1092190075Sobrien 1092290075Sobrien/* The AOF assembler is religiously strict about declarations of 1092390075Sobrien imported and exported symbols, so that it is impossible to declare 1092490075Sobrien a function as imported near the beginning of the file, and then to 1092590075Sobrien export it later on. It is, however, possible to delay the decision 1092690075Sobrien until all the functions in the file have been compiled. To get 1092790075Sobrien around this, we maintain a list of the imports and exports, and 1092890075Sobrien delete from it any that are subsequently defined. At the end of 1092990075Sobrien compilation we spit the remainder of the list out before the END 1093090075Sobrien directive. */ 1093190075Sobrien 1093290075Sobrienstruct import 1093390075Sobrien{ 1093490075Sobrien struct import * next; 1093590075Sobrien const char * name; 1093690075Sobrien}; 1093790075Sobrien 1093890075Sobrienstatic struct import * imports_list = NULL; 1093990075Sobrien 1094090075Sobrienvoid 1094190075Sobrienaof_add_import (name) 1094290075Sobrien const char * name; 1094390075Sobrien{ 1094490075Sobrien struct import * new; 1094590075Sobrien 1094690075Sobrien for (new = imports_list; new; new = new->next) 1094790075Sobrien if (new->name == name) 1094890075Sobrien return; 1094990075Sobrien 1095090075Sobrien new = (struct import *) xmalloc (sizeof (struct import)); 1095190075Sobrien new->next = imports_list; 1095290075Sobrien imports_list = new; 1095390075Sobrien new->name = name; 1095490075Sobrien} 1095590075Sobrien 1095690075Sobrienvoid 1095790075Sobrienaof_delete_import (name) 1095890075Sobrien const char * name; 1095990075Sobrien{ 1096090075Sobrien struct import ** old; 1096190075Sobrien 1096290075Sobrien for (old = &imports_list; *old; old = & (*old)->next) 1096390075Sobrien { 1096490075Sobrien if ((*old)->name == name) 1096590075Sobrien { 1096690075Sobrien *old = (*old)->next; 1096790075Sobrien return; 1096890075Sobrien } 1096990075Sobrien } 1097090075Sobrien} 1097190075Sobrien 1097290075Sobrienint arm_main_function = 0; 1097390075Sobrien 1097490075Sobrienvoid 1097590075Sobrienaof_dump_imports (f) 1097690075Sobrien FILE * f; 1097790075Sobrien{ 1097890075Sobrien /* The AOF assembler needs this to cause the startup code to be extracted 1097990075Sobrien from the library. Brining in __main causes the whole thing to work 1098090075Sobrien automagically. */ 1098190075Sobrien if (arm_main_function) 1098290075Sobrien { 1098390075Sobrien text_section (); 1098490075Sobrien fputs ("\tIMPORT __main\n", f); 1098590075Sobrien fputs ("\tDCD __main\n", f); 1098690075Sobrien } 1098790075Sobrien 1098890075Sobrien /* Now dump the remaining imports. */ 1098990075Sobrien while (imports_list) 1099090075Sobrien { 1099190075Sobrien fprintf (f, "\tIMPORT\t"); 1099290075Sobrien assemble_name (f, imports_list->name); 1099390075Sobrien fputc ('\n', f); 1099490075Sobrien imports_list = imports_list->next; 1099590075Sobrien } 1099690075Sobrien} 1099790075Sobrien#endif /* AOF_ASSEMBLER */ 1099890075Sobrien 1099990075Sobrien#ifdef OBJECT_FORMAT_ELF 1100090075Sobrien/* Switch to an arbitrary section NAME with attributes as specified 1100190075Sobrien by FLAGS. ALIGN specifies any known alignment requirements for 1100290075Sobrien the section; 0 if the default should be used. 1100390075Sobrien 1100490075Sobrien Differs from the default elf version only in the prefix character 1100590075Sobrien used before the section type. */ 1100690075Sobrien 1100790075Sobrienstatic void 1100890075Sobrienarm_elf_asm_named_section (name, flags) 1100990075Sobrien const char *name; 1101090075Sobrien unsigned int flags; 1101190075Sobrien{ 1101290075Sobrien char flagchars[8], *f = flagchars; 1101390075Sobrien const char *type; 1101490075Sobrien 1101590075Sobrien if (!(flags & SECTION_DEBUG)) 1101690075Sobrien *f++ = 'a'; 1101790075Sobrien if (flags & SECTION_WRITE) 1101890075Sobrien *f++ = 'w'; 1101990075Sobrien if (flags & SECTION_CODE) 1102090075Sobrien *f++ = 'x'; 1102190075Sobrien if (flags & SECTION_SMALL) 1102290075Sobrien *f++ = 's'; 1102390075Sobrien if (flags & SECTION_MERGE) 1102490075Sobrien *f++ = 'M'; 1102590075Sobrien if (flags & SECTION_STRINGS) 1102690075Sobrien *f++ = 'S'; 1102790075Sobrien *f = '\0'; 1102890075Sobrien 1102990075Sobrien if (flags & SECTION_BSS) 1103090075Sobrien type = "nobits"; 1103190075Sobrien else 1103290075Sobrien type = "progbits"; 1103390075Sobrien 1103490075Sobrien if (flags & SECTION_ENTSIZE) 1103590075Sobrien fprintf (asm_out_file, "\t.section\t%s,\"%s\",%%%s,%d\n", 1103690075Sobrien name, flagchars, type, flags & SECTION_ENTSIZE); 1103790075Sobrien else 1103890075Sobrien fprintf (asm_out_file, "\t.section\t%s,\"%s\",%%%s\n", 1103990075Sobrien name, flagchars, type); 1104090075Sobrien} 1104190075Sobrien#endif 11042