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