arm.c revision 146895
190075Sobrien/* Output routines for GCC for ARM. 2132718Skan Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 3146895Skan 2002, 2003, 2004, 2005 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 8132718Skan This file is part of GCC. 990075Sobrien 10132718Skan GCC is free software; you can redistribute it and/or modify it 11132718Skan under the terms of the GNU General Public License as published 12132718Skan by the Free Software Foundation; either version 2, or (at your 13132718Skan option) any later version. 1490075Sobrien 15132718Skan GCC is distributed in the hope that it will be useful, but WITHOUT 16132718Skan ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17132718Skan or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 18132718Skan License for more details. 1990075Sobrien 20132718Skan You should have received a copy of the GNU General Public License 21132718Skan along with GCC; see the file COPYING. If not, write to 22132718Skan the Free Software Foundation, 59 Temple Place - Suite 330, 23132718Skan Boston, MA 02111-1307, USA. */ 2490075Sobrien 2590075Sobrien#include "config.h" 2690075Sobrien#include "system.h" 27132718Skan#include "coretypes.h" 28132718Skan#include "tm.h" 2990075Sobrien#include "rtl.h" 3090075Sobrien#include "tree.h" 3190075Sobrien#include "obstack.h" 3290075Sobrien#include "regs.h" 3390075Sobrien#include "hard-reg-set.h" 3490075Sobrien#include "real.h" 3590075Sobrien#include "insn-config.h" 3690075Sobrien#include "conditions.h" 3790075Sobrien#include "output.h" 3890075Sobrien#include "insn-attr.h" 3990075Sobrien#include "flags.h" 4090075Sobrien#include "reload.h" 4190075Sobrien#include "function.h" 4290075Sobrien#include "expr.h" 4390075Sobrien#include "optabs.h" 4490075Sobrien#include "toplev.h" 4590075Sobrien#include "recog.h" 4690075Sobrien#include "ggc.h" 4790075Sobrien#include "except.h" 4890075Sobrien#include "c-pragma.h" 4990075Sobrien#include "integrate.h" 5090075Sobrien#include "tm_p.h" 5190075Sobrien#include "target.h" 5290075Sobrien#include "target-def.h" 53132718Skan#include "debug.h" 5490075Sobrien 5590075Sobrien/* Forward definitions of types. */ 5690075Sobrientypedef struct minipool_node Mnode; 5790075Sobrientypedef struct minipool_fixup Mfix; 5890075Sobrien 5990075Sobrienconst struct attribute_spec arm_attribute_table[]; 6090075Sobrien 6190075Sobrien/* Forward function declarations. */ 62132718Skanstatic void arm_add_gc_roots (void); 63132718Skanstatic int arm_gen_constant (enum rtx_code, enum machine_mode, HOST_WIDE_INT, 64132718Skan rtx, rtx, int, int); 65132718Skanstatic unsigned bit_count (unsigned long); 66132718Skanstatic int arm_address_register_rtx_p (rtx, int); 67132718Skanstatic int arm_legitimate_index_p (enum machine_mode, rtx, int); 68132718Skanstatic int thumb_base_register_rtx_p (rtx, enum machine_mode, int); 69132718Skaninline static int thumb_index_register_rtx_p (rtx, int); 70132718Skanstatic int const_ok_for_op (HOST_WIDE_INT, enum rtx_code); 71132718Skanstatic rtx emit_multi_reg_push (int); 72132718Skanstatic rtx emit_sfm (int, int); 7390075Sobrien#ifndef AOF_ASSEMBLER 74132718Skanstatic bool arm_assemble_integer (rtx, unsigned int, int); 7590075Sobrien#endif 76132718Skanstatic const char *fp_const_from_val (REAL_VALUE_TYPE *); 77132718Skanstatic arm_cc get_arm_condition_code (rtx); 78132718Skanstatic void init_fpa_table (void); 79132718Skanstatic HOST_WIDE_INT int_log2 (HOST_WIDE_INT); 80132718Skanstatic rtx is_jump_table (rtx); 81132718Skanstatic const char *output_multi_immediate (rtx *, const char *, const char *, 82132718Skan int, HOST_WIDE_INT); 83132718Skanstatic void print_multi_reg (FILE *, const char *, int, int); 84132718Skanstatic const char *shift_op (rtx, HOST_WIDE_INT *); 85132718Skanstatic struct machine_function *arm_init_machine_status (void); 86132718Skanstatic int number_of_first_bit_set (int); 87132718Skanstatic void replace_symbols_in_block (tree, rtx, rtx); 88132718Skanstatic void thumb_exit (FILE *, int, rtx); 89132718Skanstatic void thumb_pushpop (FILE *, int, int, int *, int); 90132718Skanstatic rtx is_jump_table (rtx); 91132718Skanstatic HOST_WIDE_INT get_jump_table_size (rtx); 92132718Skanstatic Mnode *move_minipool_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT); 93132718Skanstatic Mnode *add_minipool_forward_ref (Mfix *); 94132718Skanstatic Mnode *move_minipool_fix_backward_ref (Mnode *, Mnode *, HOST_WIDE_INT); 95132718Skanstatic Mnode *add_minipool_backward_ref (Mfix *); 96132718Skanstatic void assign_minipool_offsets (Mfix *); 97132718Skanstatic void arm_print_value (FILE *, rtx); 98132718Skanstatic void dump_minipool (rtx); 99132718Skanstatic int arm_barrier_cost (rtx); 100132718Skanstatic Mfix *create_fix_barrier (Mfix *, HOST_WIDE_INT); 101132718Skanstatic void push_minipool_barrier (rtx, HOST_WIDE_INT); 102132718Skanstatic void push_minipool_fix (rtx, HOST_WIDE_INT, rtx *, enum machine_mode, 103132718Skan rtx); 104132718Skanstatic void arm_reorg (void); 105132718Skanstatic bool note_invalid_constants (rtx, HOST_WIDE_INT, int); 106132718Skanstatic int current_file_function_operand (rtx); 107132718Skanstatic unsigned long arm_compute_save_reg0_reg12_mask (void); 108132718Skanstatic unsigned long arm_compute_save_reg_mask (void); 109132718Skanstatic unsigned long arm_isr_value (tree); 110132718Skanstatic unsigned long arm_compute_func_type (void); 111132718Skanstatic tree arm_handle_fndecl_attribute (tree *, tree, tree, int, bool *); 112132718Skanstatic tree arm_handle_isr_attribute (tree *, tree, tree, int, bool *); 113132718Skanstatic void arm_output_function_epilogue (FILE *, HOST_WIDE_INT); 114132718Skanstatic void arm_output_function_prologue (FILE *, HOST_WIDE_INT); 115132718Skanstatic void thumb_output_function_prologue (FILE *, HOST_WIDE_INT); 116132718Skanstatic int arm_comp_type_attributes (tree, tree); 117132718Skanstatic void arm_set_default_type_attributes (tree); 118132718Skanstatic int arm_adjust_cost (rtx, rtx, rtx, int); 119132718Skanstatic int arm_use_dfa_pipeline_interface (void); 120132718Skanstatic int count_insns_for_constant (HOST_WIDE_INT, int); 121132718Skanstatic int arm_get_strip_length (int); 122132718Skanstatic bool arm_function_ok_for_sibcall (tree, tree); 123132718Skanstatic void arm_internal_label (FILE *, const char *, unsigned long); 124132718Skanstatic void arm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, 125132718Skan tree); 126132718Skanstatic int arm_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code); 127132718Skanstatic bool arm_rtx_costs (rtx, int, int, int *); 128132718Skanstatic int arm_address_cost (rtx); 129132718Skanstatic bool arm_memory_load_p (rtx); 130132718Skanstatic bool arm_cirrus_insn_p (rtx); 131132718Skanstatic void cirrus_reorg (rtx); 132132718Skanstatic void arm_init_builtins (void); 133132718Skanstatic rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int); 134132718Skanstatic void arm_init_iwmmxt_builtins (void); 135132718Skanstatic rtx safe_vector_operand (rtx, enum machine_mode); 136132718Skanstatic rtx arm_expand_binop_builtin (enum insn_code, tree, rtx); 137132718Skanstatic rtx arm_expand_unop_builtin (enum insn_code, tree, rtx, int); 138132718Skanstatic rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int); 139132718Skan 14090075Sobrien#ifdef OBJECT_FORMAT_ELF 141132718Skanstatic void arm_elf_asm_named_section (const char *, unsigned int); 14290075Sobrien#endif 143117395Skan#ifndef ARM_PE 144132718Skanstatic void arm_encode_section_info (tree, rtx, int); 145117395Skan#endif 146117395Skan#ifdef AOF_ASSEMBLER 147132718Skanstatic void aof_globalize_label (FILE *, const char *); 148132718Skanstatic void aof_dump_imports (FILE *); 149132718Skanstatic void aof_dump_pic_table (FILE *); 150132718Skanstatic void aof_file_start (void); 151132718Skanstatic void aof_file_end (void); 152117395Skan#endif 15390075Sobrien 15490075Sobrien 15590075Sobrien/* Initialize the GCC target structure. */ 15690075Sobrien#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES 15790075Sobrien#undef TARGET_MERGE_DECL_ATTRIBUTES 15890075Sobrien#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes 15990075Sobrien#endif 16090075Sobrien 16190075Sobrien#undef TARGET_ATTRIBUTE_TABLE 16290075Sobrien#define TARGET_ATTRIBUTE_TABLE arm_attribute_table 16390075Sobrien 16490075Sobrien#ifdef AOF_ASSEMBLER 16590075Sobrien#undef TARGET_ASM_BYTE_OP 16690075Sobrien#define TARGET_ASM_BYTE_OP "\tDCB\t" 16790075Sobrien#undef TARGET_ASM_ALIGNED_HI_OP 16890075Sobrien#define TARGET_ASM_ALIGNED_HI_OP "\tDCW\t" 16990075Sobrien#undef TARGET_ASM_ALIGNED_SI_OP 17090075Sobrien#define TARGET_ASM_ALIGNED_SI_OP "\tDCD\t" 171117395Skan#undef TARGET_ASM_GLOBALIZE_LABEL 172117395Skan#define TARGET_ASM_GLOBALIZE_LABEL aof_globalize_label 173132718Skan#undef TARGET_ASM_FILE_START 174132718Skan#define TARGET_ASM_FILE_START aof_file_start 175132718Skan#undef TARGET_ASM_FILE_END 176132718Skan#define TARGET_ASM_FILE_END aof_file_end 17790075Sobrien#else 17890075Sobrien#undef TARGET_ASM_ALIGNED_SI_OP 17990075Sobrien#define TARGET_ASM_ALIGNED_SI_OP NULL 18090075Sobrien#undef TARGET_ASM_INTEGER 18190075Sobrien#define TARGET_ASM_INTEGER arm_assemble_integer 18290075Sobrien#endif 18390075Sobrien 18490075Sobrien#undef TARGET_ASM_FUNCTION_PROLOGUE 18590075Sobrien#define TARGET_ASM_FUNCTION_PROLOGUE arm_output_function_prologue 18690075Sobrien 18790075Sobrien#undef TARGET_ASM_FUNCTION_EPILOGUE 18890075Sobrien#define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue 18990075Sobrien 19090075Sobrien#undef TARGET_COMP_TYPE_ATTRIBUTES 19190075Sobrien#define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes 19290075Sobrien 19390075Sobrien#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES 19490075Sobrien#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes 19590075Sobrien 19690075Sobrien#undef TARGET_SCHED_ADJUST_COST 19790075Sobrien#define TARGET_SCHED_ADJUST_COST arm_adjust_cost 19890075Sobrien 199132718Skan#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE 200132718Skan#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE arm_use_dfa_pipeline_interface 201132718Skan 202117395Skan#undef TARGET_ENCODE_SECTION_INFO 203117395Skan#ifdef ARM_PE 204117395Skan#define TARGET_ENCODE_SECTION_INFO arm_pe_encode_section_info 205117395Skan#else 206117395Skan#define TARGET_ENCODE_SECTION_INFO arm_encode_section_info 207117395Skan#endif 208117395Skan 209132718Skan#undef TARGET_STRIP_NAME_ENCODING 210117395Skan#define TARGET_STRIP_NAME_ENCODING arm_strip_name_encoding 211117395Skan 212132718Skan#undef TARGET_ASM_INTERNAL_LABEL 213132718Skan#define TARGET_ASM_INTERNAL_LABEL arm_internal_label 214132718Skan 215132718Skan#undef TARGET_FUNCTION_OK_FOR_SIBCALL 216132718Skan#define TARGET_FUNCTION_OK_FOR_SIBCALL arm_function_ok_for_sibcall 217132718Skan 218132718Skan#undef TARGET_ASM_OUTPUT_MI_THUNK 219117395Skan#define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk 220132718Skan#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK 221117395Skan#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall 222117395Skan 223132718Skan#undef TARGET_RTX_COSTS 224132718Skan#define TARGET_RTX_COSTS arm_rtx_costs 225132718Skan#undef TARGET_ADDRESS_COST 226132718Skan#define TARGET_ADDRESS_COST arm_address_cost 227132718Skan 228132718Skan#undef TARGET_MACHINE_DEPENDENT_REORG 229132718Skan#define TARGET_MACHINE_DEPENDENT_REORG arm_reorg 230132718Skan 231132718Skan#undef TARGET_INIT_BUILTINS 232132718Skan#define TARGET_INIT_BUILTINS arm_init_builtins 233132718Skan#undef TARGET_EXPAND_BUILTIN 234132718Skan#define TARGET_EXPAND_BUILTIN arm_expand_builtin 235132718Skan 23690075Sobrienstruct gcc_target targetm = TARGET_INITIALIZER; 23790075Sobrien 23890075Sobrien/* Obstack for minipool constant handling. */ 23990075Sobrienstatic struct obstack minipool_obstack; 24090075Sobrienstatic char * minipool_startobj; 24190075Sobrien 24290075Sobrien/* The maximum number of insns skipped which 24390075Sobrien will be conditionalised if possible. */ 24490075Sobrienstatic int max_insns_skipped = 5; 24590075Sobrien 24690075Sobrienextern FILE * asm_out_file; 24790075Sobrien 24890075Sobrien/* True if we are currently building a constant table. */ 24990075Sobrienint making_const_table; 25090075Sobrien 25190075Sobrien/* Define the information needed to generate branch insns. This is 25290075Sobrien stored from the compare operation. */ 25390075Sobrienrtx arm_compare_op0, arm_compare_op1; 25490075Sobrien 25590075Sobrien/* What type of floating point are we tuning for? */ 256132718Skanenum fputype arm_fpu_tune; 25790075Sobrien 25890075Sobrien/* What type of floating point instructions are available? */ 259132718Skanenum fputype arm_fpu_arch; 26090075Sobrien 26190075Sobrien/* What program mode is the cpu running in? 26-bit mode or 32-bit mode. */ 26290075Sobrienenum prog_mode_type arm_prgmode; 26390075Sobrien 26490075Sobrien/* Set by the -mfp=... option. */ 26590075Sobrienconst char * target_fp_name = NULL; 26690075Sobrien 26790075Sobrien/* Used to parse -mstructure_size_boundary command line option. */ 26890075Sobrienconst char * structure_size_string = NULL; 26990075Sobrienint arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY; 27090075Sobrien 27190075Sobrien/* Bit values used to identify processor capabilities. */ 27290075Sobrien#define FL_CO_PROC (1 << 0) /* Has external co-processor bus */ 27390075Sobrien#define FL_FAST_MULT (1 << 1) /* Fast multiply */ 27490075Sobrien#define FL_MODE26 (1 << 2) /* 26-bit mode support */ 27590075Sobrien#define FL_MODE32 (1 << 3) /* 32-bit mode support */ 27690075Sobrien#define FL_ARCH4 (1 << 4) /* Architecture rel 4 */ 27790075Sobrien#define FL_ARCH5 (1 << 5) /* Architecture rel 5 */ 27890075Sobrien#define FL_THUMB (1 << 6) /* Thumb aware */ 27990075Sobrien#define FL_LDSCHED (1 << 7) /* Load scheduling necessary */ 28090075Sobrien#define FL_STRONG (1 << 8) /* StrongARM */ 281132718Skan#define FL_ARCH5E (1 << 9) /* DSP extensions to v5 */ 28290075Sobrien#define FL_XSCALE (1 << 10) /* XScale */ 283132718Skan#define FL_CIRRUS (1 << 11) /* Cirrus/DSP. */ 284132718Skan#define FL_IWMMXT (1 << 29) /* XScale v2 or "Intel Wireless MMX technology". */ 285132718Skan#define FL_ARCH6J (1 << 12) /* Architecture rel 6. Adds 286132718Skan media instructions. */ 287132718Skan#define FL_VFPV2 (1 << 13) /* Vector Floating Point V2. */ 28890075Sobrien 28990075Sobrien/* The bits in this mask specify which 29090075Sobrien instructions we are allowed to generate. */ 291117395Skanstatic unsigned long insn_flags = 0; 29290075Sobrien 29390075Sobrien/* The bits in this mask specify which instruction scheduling options should 29490075Sobrien be used. Note - there is an overlap with the FL_FAST_MULT. For some 29590075Sobrien hardware we want to be able to generate the multiply instructions, but to 29690075Sobrien tune as if they were not present in the architecture. */ 297117395Skanstatic unsigned long tune_flags = 0; 29890075Sobrien 29990075Sobrien/* The following are used in the arm.md file as equivalents to bits 30090075Sobrien in the above two flag variables. */ 30190075Sobrien 30290075Sobrien/* Nonzero if this is an "M" variant of the processor. */ 30390075Sobrienint arm_fast_multiply = 0; 30490075Sobrien 30590075Sobrien/* Nonzero if this chip supports the ARM Architecture 4 extensions. */ 30690075Sobrienint arm_arch4 = 0; 30790075Sobrien 30890075Sobrien/* Nonzero if this chip supports the ARM Architecture 5 extensions. */ 30990075Sobrienint arm_arch5 = 0; 31090075Sobrien 31190075Sobrien/* Nonzero if this chip supports the ARM Architecture 5E extensions. */ 31290075Sobrienint arm_arch5e = 0; 31390075Sobrien 31490075Sobrien/* Nonzero if this chip can benefit from load scheduling. */ 31590075Sobrienint arm_ld_sched = 0; 31690075Sobrien 31790075Sobrien/* Nonzero if this chip is a StrongARM. */ 31890075Sobrienint arm_is_strong = 0; 31990075Sobrien 320132718Skan/* Nonzero if this chip supports Intel Wireless MMX technology. */ 321132718Skanint arm_arch_iwmmxt = 0; 322132718Skan 32390075Sobrien/* Nonzero if this chip is an XScale. */ 324132718Skanint arm_arch_xscale = 0; 32590075Sobrien 326132718Skan/* Nonzero if tuning for XScale */ 327132718Skanint arm_tune_xscale = 0; 328132718Skan 32990075Sobrien/* Nonzero if this chip is an ARM6 or an ARM7. */ 33090075Sobrienint arm_is_6_or_7 = 0; 33190075Sobrien 332132718Skan/* Nonzero if this chip is a Cirrus/DSP. */ 333132718Skanint arm_is_cirrus = 0; 334132718Skan 33590075Sobrien/* Nonzero if generating Thumb instructions. */ 33690075Sobrienint thumb_code = 0; 33790075Sobrien 33890075Sobrien/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we 33990075Sobrien must report the mode of the memory reference from PRINT_OPERAND to 34090075Sobrien PRINT_OPERAND_ADDRESS. */ 34190075Sobrienenum machine_mode output_memory_reference_mode; 34290075Sobrien 34390075Sobrien/* The register number to be used for the PIC offset register. */ 34490075Sobrienconst char * arm_pic_register_string = NULL; 34596263Sobrienint arm_pic_register = INVALID_REGNUM; 34690075Sobrien 34790075Sobrien/* Set to 1 when a return insn is output, this means that the epilogue 34890075Sobrien is not needed. */ 34990075Sobrienint return_used_this_function; 35090075Sobrien 35190075Sobrien/* Set to 1 after arm_reorg has started. Reset to start at the start of 35290075Sobrien the next function. */ 35390075Sobrienstatic int after_arm_reorg = 0; 35490075Sobrien 35590075Sobrien/* The maximum number of insns to be used when loading a constant. */ 35690075Sobrienstatic int arm_constant_limit = 3; 35790075Sobrien 35890075Sobrien/* For an explanation of these variables, see final_prescan_insn below. */ 35990075Sobrienint arm_ccfsm_state; 36090075Sobrienenum arm_cond_code arm_current_cc; 36190075Sobrienrtx arm_target_insn; 36290075Sobrienint arm_target_label; 36390075Sobrien 36490075Sobrien/* The condition codes of the ARM, and the inverse function. */ 36590075Sobrienstatic const char * const arm_condition_codes[] = 36690075Sobrien{ 36790075Sobrien "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", 36890075Sobrien "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" 36990075Sobrien}; 37090075Sobrien 37190075Sobrien#define streq(string1, string2) (strcmp (string1, string2) == 0) 37290075Sobrien 37390075Sobrien/* Initialization code. */ 37490075Sobrien 37590075Sobrienstruct processors 37690075Sobrien{ 37790075Sobrien const char *const name; 378117395Skan const unsigned long flags; 37990075Sobrien}; 38090075Sobrien 38190075Sobrien/* Not all of these give usefully different compilation alternatives, 38290075Sobrien but there is no simple way of generalizing them. */ 38390075Sobrienstatic const struct processors all_cores[] = 38490075Sobrien{ 38590075Sobrien /* ARM Cores */ 38690075Sobrien 38790075Sobrien {"arm2", FL_CO_PROC | FL_MODE26 }, 38890075Sobrien {"arm250", FL_CO_PROC | FL_MODE26 }, 38990075Sobrien {"arm3", FL_CO_PROC | FL_MODE26 }, 39090075Sobrien {"arm6", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 39190075Sobrien {"arm60", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 39290075Sobrien {"arm600", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 39390075Sobrien {"arm610", FL_MODE26 | FL_MODE32 }, 39490075Sobrien {"arm620", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 39590075Sobrien {"arm7", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 39690075Sobrien /* arm7m doesn't exist on its own, but only with D, (and I), but 39790075Sobrien those don't alter the code, so arm7m is sometimes used. */ 39890075Sobrien {"arm7m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 39990075Sobrien {"arm7d", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 40090075Sobrien {"arm7dm", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 40190075Sobrien {"arm7di", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 40290075Sobrien {"arm7dmi", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 40390075Sobrien {"arm70", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 40490075Sobrien {"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 40590075Sobrien {"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 40690075Sobrien {"arm710", FL_MODE26 | FL_MODE32 }, 40790075Sobrien {"arm720", FL_MODE26 | FL_MODE32 }, 40890075Sobrien {"arm710c", FL_MODE26 | FL_MODE32 }, 40990075Sobrien {"arm7100", FL_MODE26 | FL_MODE32 }, 41090075Sobrien {"arm7500", FL_MODE26 | FL_MODE32 }, 411132718Skan /* Doesn't have an external co-proc, but does have embedded fpa. */ 41290075Sobrien {"arm7500fe", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 413132718Skan /* V4 Architecture Processors */ 41490075Sobrien {"arm7tdmi", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 415132718Skan {"arm710t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 416132718Skan {"arm720t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 417132718Skan {"arm740t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 41890075Sobrien {"arm8", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 41990075Sobrien {"arm810", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 42090075Sobrien {"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 42190075Sobrien {"arm920", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 42290075Sobrien {"arm920t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 42390075Sobrien {"arm940t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 42490075Sobrien {"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED }, 42590075Sobrien {"arm9e", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED }, 426132718Skan {"ep9312", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_CIRRUS }, 42790075Sobrien {"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 42890075Sobrien {"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 42990075Sobrien {"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 43090075Sobrien {"strongarm1110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG }, 431132718Skan /* V5 Architecture Processors */ 43290075Sobrien {"arm10tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, 43390075Sobrien {"arm1020t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 }, 434132718Skan {"arm926ejs", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E }, 435132718Skan {"arm1026ejs", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E }, 43690075Sobrien {"xscale", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE }, 437132718Skan {"iwmmxt", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE | FL_IWMMXT }, 438132718Skan /* V6 Architecture Processors */ 439132718Skan {"arm1136js", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J }, 440132718Skan {"arm1136jfs", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J | FL_VFPV2 }, 44190075Sobrien {NULL, 0} 44290075Sobrien}; 44390075Sobrien 44490075Sobrienstatic const struct processors all_architectures[] = 44590075Sobrien{ 44690075Sobrien /* ARM Architectures */ 44790075Sobrien 44890075Sobrien { "armv2", FL_CO_PROC | FL_MODE26 }, 44990075Sobrien { "armv2a", FL_CO_PROC | FL_MODE26 }, 45090075Sobrien { "armv3", FL_CO_PROC | FL_MODE26 | FL_MODE32 }, 45190075Sobrien { "armv3m", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT }, 45290075Sobrien { "armv4", FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 }, 45390075Sobrien /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no 45490075Sobrien implementations that support it, so we will leave it out for now. */ 45590075Sobrien { "armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB }, 45690075Sobrien { "armv5", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, 45790075Sobrien { "armv5t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 }, 45890075Sobrien { "armv5te", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E }, 459132718Skan { "armv6j", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J }, 460132718Skan { "ep9312", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_CIRRUS }, 461132718Skan {"iwmmxt", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE | FL_IWMMXT }, 46290075Sobrien { NULL, 0 } 46390075Sobrien}; 46490075Sobrien 465132718Skan/* This is a magic structure. The 'string' field is magically filled in 46690075Sobrien with a pointer to the value specified by the user on the command line 46790075Sobrien assuming that the user has specified such a value. */ 46890075Sobrien 46990075Sobrienstruct arm_cpu_select arm_select[] = 47090075Sobrien{ 47190075Sobrien /* string name processors */ 47290075Sobrien { NULL, "-mcpu=", all_cores }, 47390075Sobrien { NULL, "-march=", all_architectures }, 47490075Sobrien { NULL, "-mtune=", all_cores } 47590075Sobrien}; 47690075Sobrien 477117395Skan/* Return the number of bits set in VALUE. */ 478117395Skanstatic unsigned 479132718Skanbit_count (unsigned long value) 48090075Sobrien{ 48190075Sobrien unsigned long count = 0; 48290075Sobrien 48390075Sobrien while (value) 48490075Sobrien { 485117395Skan count++; 486117395Skan value &= value - 1; /* Clear the least-significant set bit. */ 48790075Sobrien } 48890075Sobrien 48990075Sobrien return count; 49090075Sobrien} 49190075Sobrien 49290075Sobrien/* Fix up any incompatible options that the user has specified. 49390075Sobrien This has now turned into a maze. */ 49490075Sobrienvoid 495132718Skanarm_override_options (void) 49690075Sobrien{ 49790075Sobrien unsigned i; 49890075Sobrien 49990075Sobrien /* Set up the flags based on the cpu/architecture selected by the user. */ 50090075Sobrien for (i = ARRAY_SIZE (arm_select); i--;) 50190075Sobrien { 50290075Sobrien struct arm_cpu_select * ptr = arm_select + i; 50390075Sobrien 50490075Sobrien if (ptr->string != NULL && ptr->string[0] != '\0') 50590075Sobrien { 50690075Sobrien const struct processors * sel; 50790075Sobrien 50890075Sobrien for (sel = ptr->processors; sel->name != NULL; sel++) 50990075Sobrien if (streq (ptr->string, sel->name)) 51090075Sobrien { 51190075Sobrien if (i == 2) 51290075Sobrien tune_flags = sel->flags; 51390075Sobrien else 51490075Sobrien { 51590075Sobrien /* If we have been given an architecture and a processor 51690075Sobrien make sure that they are compatible. We only generate 51790075Sobrien a warning though, and we prefer the CPU over the 51890075Sobrien architecture. */ 51990075Sobrien if (insn_flags != 0 && (insn_flags ^ sel->flags)) 52090075Sobrien warning ("switch -mcpu=%s conflicts with -march= switch", 52190075Sobrien ptr->string); 52290075Sobrien 52390075Sobrien insn_flags = sel->flags; 52490075Sobrien } 52590075Sobrien 52690075Sobrien break; 52790075Sobrien } 52890075Sobrien 52990075Sobrien if (sel->name == NULL) 53090075Sobrien error ("bad value (%s) for %s switch", ptr->string, ptr->name); 53190075Sobrien } 53290075Sobrien } 53390075Sobrien 53490075Sobrien /* If the user did not specify a processor, choose one for them. */ 53590075Sobrien if (insn_flags == 0) 53690075Sobrien { 53790075Sobrien const struct processors * sel; 53890075Sobrien unsigned int sought; 53990075Sobrien static const struct cpu_default 54090075Sobrien { 54190075Sobrien const int cpu; 54290075Sobrien const char *const name; 54390075Sobrien } 54490075Sobrien cpu_defaults[] = 54590075Sobrien { 54690075Sobrien { TARGET_CPU_arm2, "arm2" }, 54790075Sobrien { TARGET_CPU_arm6, "arm6" }, 54890075Sobrien { TARGET_CPU_arm610, "arm610" }, 54990075Sobrien { TARGET_CPU_arm710, "arm710" }, 55090075Sobrien { TARGET_CPU_arm7m, "arm7m" }, 55190075Sobrien { TARGET_CPU_arm7500fe, "arm7500fe" }, 55290075Sobrien { TARGET_CPU_arm7tdmi, "arm7tdmi" }, 55390075Sobrien { TARGET_CPU_arm8, "arm8" }, 55490075Sobrien { TARGET_CPU_arm810, "arm810" }, 55590075Sobrien { TARGET_CPU_arm9, "arm9" }, 55690075Sobrien { TARGET_CPU_strongarm, "strongarm" }, 55790075Sobrien { TARGET_CPU_xscale, "xscale" }, 558132718Skan { TARGET_CPU_ep9312, "ep9312" }, 559132718Skan { TARGET_CPU_iwmmxt, "iwmmxt" }, 560132718Skan { TARGET_CPU_arm926ej_s, "arm926ej-s" }, 561132718Skan { TARGET_CPU_arm1026ej_s, "arm1026ej-s" }, 562132718Skan { TARGET_CPU_arm1136j_s, "arm1136j_s" }, 563132718Skan { TARGET_CPU_arm1136jf_s, "arm1136jf_s" }, 56490075Sobrien { TARGET_CPU_generic, "arm" }, 56590075Sobrien { 0, 0 } 56690075Sobrien }; 56790075Sobrien const struct cpu_default * def; 56890075Sobrien 56990075Sobrien /* Find the default. */ 57090075Sobrien for (def = cpu_defaults; def->name; def++) 57190075Sobrien if (def->cpu == TARGET_CPU_DEFAULT) 57290075Sobrien break; 57390075Sobrien 57490075Sobrien /* Make sure we found the default CPU. */ 57590075Sobrien if (def->name == NULL) 57690075Sobrien abort (); 57790075Sobrien 57890075Sobrien /* Find the default CPU's flags. */ 57990075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 58090075Sobrien if (streq (def->name, sel->name)) 58190075Sobrien break; 58290075Sobrien 58390075Sobrien if (sel->name == NULL) 58490075Sobrien abort (); 58590075Sobrien 58690075Sobrien insn_flags = sel->flags; 58790075Sobrien 58890075Sobrien /* Now check to see if the user has specified some command line 58990075Sobrien switch that require certain abilities from the cpu. */ 59090075Sobrien sought = 0; 59190075Sobrien 59290075Sobrien if (TARGET_INTERWORK || TARGET_THUMB) 59390075Sobrien { 59490075Sobrien sought |= (FL_THUMB | FL_MODE32); 59590075Sobrien 59690075Sobrien /* Force apcs-32 to be used for interworking. */ 59790075Sobrien target_flags |= ARM_FLAG_APCS_32; 59890075Sobrien 59990075Sobrien /* There are no ARM processors that support both APCS-26 and 60090075Sobrien interworking. Therefore we force FL_MODE26 to be removed 60190075Sobrien from insn_flags here (if it was set), so that the search 60290075Sobrien below will always be able to find a compatible processor. */ 60390075Sobrien insn_flags &= ~FL_MODE26; 60490075Sobrien } 60590075Sobrien else if (!TARGET_APCS_32) 60690075Sobrien sought |= FL_MODE26; 60790075Sobrien 60890075Sobrien if (sought != 0 && ((sought & insn_flags) != sought)) 60990075Sobrien { 61090075Sobrien /* Try to locate a CPU type that supports all of the abilities 61190075Sobrien of the default CPU, plus the extra abilities requested by 61290075Sobrien the user. */ 61390075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 61490075Sobrien if ((sel->flags & sought) == (sought | insn_flags)) 61590075Sobrien break; 61690075Sobrien 61790075Sobrien if (sel->name == NULL) 61890075Sobrien { 619117395Skan unsigned current_bit_count = 0; 62090075Sobrien const struct processors * best_fit = NULL; 62190075Sobrien 62290075Sobrien /* Ideally we would like to issue an error message here 62390075Sobrien saying that it was not possible to find a CPU compatible 62490075Sobrien with the default CPU, but which also supports the command 62590075Sobrien line options specified by the programmer, and so they 62690075Sobrien ought to use the -mcpu=<name> command line option to 62790075Sobrien override the default CPU type. 62890075Sobrien 62990075Sobrien Unfortunately this does not work with multilibing. We 63090075Sobrien need to be able to support multilibs for -mapcs-26 and for 63190075Sobrien -mthumb-interwork and there is no CPU that can support both 63290075Sobrien options. Instead if we cannot find a cpu that has both the 63390075Sobrien characteristics of the default cpu and the given command line 63490075Sobrien options we scan the array again looking for a best match. */ 63590075Sobrien for (sel = all_cores; sel->name != NULL; sel++) 63690075Sobrien if ((sel->flags & sought) == sought) 63790075Sobrien { 638117395Skan unsigned count; 63990075Sobrien 64090075Sobrien count = bit_count (sel->flags & insn_flags); 64190075Sobrien 64290075Sobrien if (count >= current_bit_count) 64390075Sobrien { 64490075Sobrien best_fit = sel; 64590075Sobrien current_bit_count = count; 64690075Sobrien } 64790075Sobrien } 64890075Sobrien 64990075Sobrien if (best_fit == NULL) 65090075Sobrien abort (); 65190075Sobrien else 65290075Sobrien sel = best_fit; 65390075Sobrien } 65490075Sobrien 65590075Sobrien insn_flags = sel->flags; 65690075Sobrien } 65790075Sobrien } 65890075Sobrien 65990075Sobrien /* If tuning has not been specified, tune for whichever processor or 66090075Sobrien architecture has been selected. */ 66190075Sobrien if (tune_flags == 0) 66290075Sobrien tune_flags = insn_flags; 663117395Skan 66490075Sobrien /* Make sure that the processor choice does not conflict with any of the 66590075Sobrien other command line choices. */ 66690075Sobrien if (TARGET_APCS_32 && !(insn_flags & FL_MODE32)) 66790075Sobrien { 66890075Sobrien /* If APCS-32 was not the default then it must have been set by the 66990075Sobrien user, so issue a warning message. If the user has specified 67090075Sobrien "-mapcs-32 -mcpu=arm2" then we loose here. */ 67190075Sobrien if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0) 67290075Sobrien warning ("target CPU does not support APCS-32" ); 67390075Sobrien target_flags &= ~ARM_FLAG_APCS_32; 67490075Sobrien } 67590075Sobrien else if (!TARGET_APCS_32 && !(insn_flags & FL_MODE26)) 67690075Sobrien { 67790075Sobrien warning ("target CPU does not support APCS-26" ); 67890075Sobrien target_flags |= ARM_FLAG_APCS_32; 67990075Sobrien } 68090075Sobrien 68190075Sobrien if (TARGET_INTERWORK && !(insn_flags & FL_THUMB)) 68290075Sobrien { 68390075Sobrien warning ("target CPU does not support interworking" ); 68490075Sobrien target_flags &= ~ARM_FLAG_INTERWORK; 68590075Sobrien } 68690075Sobrien 68790075Sobrien if (TARGET_THUMB && !(insn_flags & FL_THUMB)) 68890075Sobrien { 68990075Sobrien warning ("target CPU does not support THUMB instructions"); 69090075Sobrien target_flags &= ~ARM_FLAG_THUMB; 69190075Sobrien } 69290075Sobrien 693132718Skan if (!TARGET_APCS_32) 694132718Skan inform ("future releases of GCC will not support -mapcs-26"); 695132718Skan 69690075Sobrien if (TARGET_APCS_FRAME && TARGET_THUMB) 69790075Sobrien { 69890075Sobrien /* warning ("ignoring -mapcs-frame because -mthumb was used"); */ 69990075Sobrien target_flags &= ~ARM_FLAG_APCS_FRAME; 70090075Sobrien } 70190075Sobrien 70290075Sobrien /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done 70390075Sobrien from here where no function is being compiled currently. */ 70490075Sobrien if ((target_flags & (THUMB_FLAG_LEAF_BACKTRACE | THUMB_FLAG_BACKTRACE)) 70590075Sobrien && TARGET_ARM) 70690075Sobrien warning ("enabling backtrace support is only meaningful when compiling for the Thumb"); 70790075Sobrien 70890075Sobrien if (TARGET_ARM && TARGET_CALLEE_INTERWORKING) 70990075Sobrien warning ("enabling callee interworking support is only meaningful when compiling for the Thumb"); 71090075Sobrien 71190075Sobrien if (TARGET_ARM && TARGET_CALLER_INTERWORKING) 71290075Sobrien warning ("enabling caller interworking support is only meaningful when compiling for the Thumb"); 71390075Sobrien 71490075Sobrien /* If interworking is enabled then APCS-32 must be selected as well. */ 71590075Sobrien if (TARGET_INTERWORK) 71690075Sobrien { 71790075Sobrien if (!TARGET_APCS_32) 71890075Sobrien warning ("interworking forces APCS-32 to be used" ); 71990075Sobrien target_flags |= ARM_FLAG_APCS_32; 72090075Sobrien } 72190075Sobrien 72290075Sobrien if (TARGET_APCS_STACK && !TARGET_APCS_FRAME) 72390075Sobrien { 72490075Sobrien warning ("-mapcs-stack-check incompatible with -mno-apcs-frame"); 72590075Sobrien target_flags |= ARM_FLAG_APCS_FRAME; 72690075Sobrien } 72790075Sobrien 72890075Sobrien if (TARGET_POKE_FUNCTION_NAME) 72990075Sobrien target_flags |= ARM_FLAG_APCS_FRAME; 73090075Sobrien 73190075Sobrien if (TARGET_APCS_REENT && flag_pic) 73290075Sobrien error ("-fpic and -mapcs-reent are incompatible"); 73390075Sobrien 73490075Sobrien if (TARGET_APCS_REENT) 73590075Sobrien warning ("APCS reentrant code not supported. Ignored"); 73690075Sobrien 73790075Sobrien /* If this target is normally configured to use APCS frames, warn if they 73890075Sobrien are turned off and debugging is turned on. */ 73990075Sobrien if (TARGET_ARM 74090075Sobrien && write_symbols != NO_DEBUG 74190075Sobrien && !TARGET_APCS_FRAME 74290075Sobrien && (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME)) 74390075Sobrien warning ("-g with -mno-apcs-frame may not give sensible debugging"); 74490075Sobrien 74590075Sobrien /* If stack checking is disabled, we can use r10 as the PIC register, 74690075Sobrien which keeps r9 available. */ 74796263Sobrien if (flag_pic) 74896263Sobrien arm_pic_register = TARGET_APCS_STACK ? 9 : 10; 74990075Sobrien 75090075Sobrien if (TARGET_APCS_FLOAT) 75190075Sobrien warning ("passing floating point arguments in fp regs not yet supported"); 75290075Sobrien 753117395Skan /* Initialize boolean versions of the flags, for use in the arm.md file. */ 75490075Sobrien arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0; 75590075Sobrien arm_arch4 = (insn_flags & FL_ARCH4) != 0; 75690075Sobrien arm_arch5 = (insn_flags & FL_ARCH5) != 0; 75790075Sobrien arm_arch5e = (insn_flags & FL_ARCH5E) != 0; 758132718Skan arm_arch_xscale = (insn_flags & FL_XSCALE) != 0; 75990075Sobrien 76090075Sobrien arm_ld_sched = (tune_flags & FL_LDSCHED) != 0; 76190075Sobrien arm_is_strong = (tune_flags & FL_STRONG) != 0; 76290075Sobrien thumb_code = (TARGET_ARM == 0); 76390075Sobrien arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32)) 76490075Sobrien && !(tune_flags & FL_ARCH4))) != 0; 765132718Skan arm_tune_xscale = (tune_flags & FL_XSCALE) != 0; 766132718Skan arm_is_cirrus = (tune_flags & FL_CIRRUS) != 0; 767132718Skan arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0; 76890075Sobrien 769132718Skan if (TARGET_IWMMXT && (! TARGET_ATPCS)) 770132718Skan target_flags |= ARM_FLAG_ATPCS; 771132718Skan 772132718Skan if (arm_is_cirrus) 773132718Skan { 774132718Skan arm_fpu_tune = FPUTYPE_MAVERICK; 775132718Skan 776132718Skan /* Ignore -mhard-float if -mcpu=ep9312. */ 777132718Skan if (TARGET_HARD_FLOAT) 778132718Skan target_flags ^= ARM_FLAG_SOFT_FLOAT; 779132718Skan } 780132718Skan else 781132718Skan /* Default value for floating point code... if no co-processor 782132718Skan bus, then schedule for emulated floating point. Otherwise, 783132718Skan assume the user has an FPA. 784132718Skan Note: this does not prevent use of floating point instructions, 785132718Skan -msoft-float does that. */ 786132718Skan arm_fpu_tune = (tune_flags & FL_CO_PROC) ? FPUTYPE_FPA : FPUTYPE_FPA_EMU3; 78790075Sobrien 78890075Sobrien if (target_fp_name) 78990075Sobrien { 79090075Sobrien if (streq (target_fp_name, "2")) 791132718Skan arm_fpu_arch = FPUTYPE_FPA_EMU2; 79290075Sobrien else if (streq (target_fp_name, "3")) 793132718Skan arm_fpu_arch = FPUTYPE_FPA_EMU3; 79490075Sobrien else 79590075Sobrien error ("invalid floating point emulation option: -mfpe-%s", 79690075Sobrien target_fp_name); 79790075Sobrien } 79890075Sobrien else 799132718Skan arm_fpu_arch = FPUTYPE_DEFAULT; 80090075Sobrien 801132718Skan if (TARGET_FPE) 802132718Skan { 803132718Skan if (arm_fpu_tune == FPUTYPE_FPA_EMU3) 804132718Skan arm_fpu_tune = FPUTYPE_FPA_EMU2; 805132718Skan else if (arm_fpu_tune == FPUTYPE_MAVERICK) 806132718Skan warning ("-mfpe switch not supported by ep9312 target cpu - ignored."); 807132718Skan else if (arm_fpu_tune != FPUTYPE_FPA) 808132718Skan arm_fpu_tune = FPUTYPE_FPA_EMU2; 809132718Skan } 81090075Sobrien 81190075Sobrien /* For arm2/3 there is no need to do any scheduling if there is only 81290075Sobrien a floating point emulator, or we are doing software floating-point. */ 813132718Skan if ((TARGET_SOFT_FLOAT || arm_fpu_tune != FPUTYPE_FPA) 81490075Sobrien && (tune_flags & FL_MODE32) == 0) 81590075Sobrien flag_schedule_insns = flag_schedule_insns_after_reload = 0; 81690075Sobrien 81790075Sobrien arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26; 81890075Sobrien 81990075Sobrien if (structure_size_string != NULL) 82090075Sobrien { 82190075Sobrien int size = strtol (structure_size_string, NULL, 0); 82290075Sobrien 82390075Sobrien if (size == 8 || size == 32) 82490075Sobrien arm_structure_size_boundary = size; 82590075Sobrien else 82690075Sobrien warning ("structure size boundary can only be set to 8 or 32"); 82790075Sobrien } 82890075Sobrien 82990075Sobrien if (arm_pic_register_string != NULL) 83090075Sobrien { 83196263Sobrien int pic_register = decode_reg_name (arm_pic_register_string); 832117395Skan 83390075Sobrien if (!flag_pic) 83490075Sobrien warning ("-mpic-register= is useless without -fpic"); 83590075Sobrien 83690075Sobrien /* Prevent the user from choosing an obviously stupid PIC register. */ 83796263Sobrien else if (pic_register < 0 || call_used_regs[pic_register] 83896263Sobrien || pic_register == HARD_FRAME_POINTER_REGNUM 83996263Sobrien || pic_register == STACK_POINTER_REGNUM 84096263Sobrien || pic_register >= PC_REGNUM) 84190075Sobrien error ("unable to use '%s' for PIC register", arm_pic_register_string); 84290075Sobrien else 84390075Sobrien arm_pic_register = pic_register; 84490075Sobrien } 84590075Sobrien 84690075Sobrien if (TARGET_THUMB && flag_schedule_insns) 84790075Sobrien { 84890075Sobrien /* Don't warn since it's on by default in -O2. */ 84990075Sobrien flag_schedule_insns = 0; 85090075Sobrien } 85190075Sobrien 85290075Sobrien if (optimize_size) 853132718Skan { 854132718Skan /* There's some dispute as to whether this should be 1 or 2. However, 855132718Skan experiments seem to show that in pathological cases a setting of 856132718Skan 1 degrades less severely than a setting of 2. This could change if 857132718Skan other parts of the compiler change their behavior. */ 858132718Skan arm_constant_limit = 1; 85990075Sobrien 860132718Skan /* If optimizing for size, bump the number of instructions that we 861132718Skan are prepared to conditionally execute (even on a StrongARM). */ 862132718Skan max_insns_skipped = 6; 863132718Skan } 864132718Skan else 865132718Skan { 866132718Skan /* For processors with load scheduling, it never costs more than 867132718Skan 2 cycles to load a constant, and the load scheduler may well 868132718Skan reduce that to 1. */ 869132718Skan if (tune_flags & FL_LDSCHED) 870132718Skan arm_constant_limit = 1; 871132718Skan 872132718Skan /* On XScale the longer latency of a load makes it more difficult 873132718Skan to achieve a good schedule, so it's faster to synthesize 874132718Skan constants that can be done in two insns. */ 875132718Skan if (arm_tune_xscale) 876132718Skan arm_constant_limit = 2; 877132718Skan 878132718Skan /* StrongARM has early execution of branches, so a sequence 879132718Skan that is worth skipping is shorter. */ 880132718Skan if (arm_is_strong) 881132718Skan max_insns_skipped = 3; 882132718Skan } 883132718Skan 88490075Sobrien /* Register global variables with the garbage collector. */ 88590075Sobrien arm_add_gc_roots (); 88690075Sobrien} 88790075Sobrien 88890075Sobrienstatic void 889132718Skanarm_add_gc_roots (void) 89090075Sobrien{ 89190075Sobrien gcc_obstack_init(&minipool_obstack); 89290075Sobrien minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0); 89390075Sobrien} 89490075Sobrien 89590075Sobrien/* A table of known ARM exception types. 89690075Sobrien For use with the interrupt function attribute. */ 89790075Sobrien 89890075Sobrientypedef struct 89990075Sobrien{ 90090075Sobrien const char *const arg; 90190075Sobrien const unsigned long return_value; 90290075Sobrien} 90390075Sobrienisr_attribute_arg; 90490075Sobrien 90590075Sobrienstatic const isr_attribute_arg isr_attribute_args [] = 90690075Sobrien{ 90790075Sobrien { "IRQ", ARM_FT_ISR }, 90890075Sobrien { "irq", ARM_FT_ISR }, 90990075Sobrien { "FIQ", ARM_FT_FIQ }, 91090075Sobrien { "fiq", ARM_FT_FIQ }, 91190075Sobrien { "ABORT", ARM_FT_ISR }, 91290075Sobrien { "abort", ARM_FT_ISR }, 91390075Sobrien { "ABORT", ARM_FT_ISR }, 91490075Sobrien { "abort", ARM_FT_ISR }, 91590075Sobrien { "UNDEF", ARM_FT_EXCEPTION }, 91690075Sobrien { "undef", ARM_FT_EXCEPTION }, 91790075Sobrien { "SWI", ARM_FT_EXCEPTION }, 91890075Sobrien { "swi", ARM_FT_EXCEPTION }, 91990075Sobrien { NULL, ARM_FT_NORMAL } 92090075Sobrien}; 92190075Sobrien 92290075Sobrien/* Returns the (interrupt) function type of the current 92390075Sobrien function, or ARM_FT_UNKNOWN if the type cannot be determined. */ 92490075Sobrien 92590075Sobrienstatic unsigned long 926132718Skanarm_isr_value (tree argument) 92790075Sobrien{ 92890075Sobrien const isr_attribute_arg * ptr; 92990075Sobrien const char * arg; 93090075Sobrien 93190075Sobrien /* No argument - default to IRQ. */ 93290075Sobrien if (argument == NULL_TREE) 93390075Sobrien return ARM_FT_ISR; 93490075Sobrien 93590075Sobrien /* Get the value of the argument. */ 93690075Sobrien if (TREE_VALUE (argument) == NULL_TREE 93790075Sobrien || TREE_CODE (TREE_VALUE (argument)) != STRING_CST) 93890075Sobrien return ARM_FT_UNKNOWN; 93990075Sobrien 94090075Sobrien arg = TREE_STRING_POINTER (TREE_VALUE (argument)); 94190075Sobrien 94290075Sobrien /* Check it against the list of known arguments. */ 943132718Skan for (ptr = isr_attribute_args; ptr->arg != NULL; ptr++) 94490075Sobrien if (streq (arg, ptr->arg)) 94590075Sobrien return ptr->return_value; 94690075Sobrien 947117395Skan /* An unrecognized interrupt type. */ 94890075Sobrien return ARM_FT_UNKNOWN; 94990075Sobrien} 95090075Sobrien 95190075Sobrien/* Computes the type of the current function. */ 95290075Sobrien 95390075Sobrienstatic unsigned long 954132718Skanarm_compute_func_type (void) 95590075Sobrien{ 95690075Sobrien unsigned long type = ARM_FT_UNKNOWN; 95790075Sobrien tree a; 95890075Sobrien tree attr; 95990075Sobrien 96090075Sobrien if (TREE_CODE (current_function_decl) != FUNCTION_DECL) 96190075Sobrien abort (); 96290075Sobrien 96390075Sobrien /* Decide if the current function is volatile. Such functions 96490075Sobrien never return, and many memory cycles can be saved by not storing 96590075Sobrien register values that will never be needed again. This optimization 96690075Sobrien was added to speed up context switching in a kernel application. */ 96790075Sobrien if (optimize > 0 96890075Sobrien && current_function_nothrow 96990075Sobrien && TREE_THIS_VOLATILE (current_function_decl)) 97090075Sobrien type |= ARM_FT_VOLATILE; 97190075Sobrien 97290075Sobrien if (current_function_needs_context) 97390075Sobrien type |= ARM_FT_NESTED; 97490075Sobrien 97590075Sobrien attr = DECL_ATTRIBUTES (current_function_decl); 97690075Sobrien 97790075Sobrien a = lookup_attribute ("naked", attr); 97890075Sobrien if (a != NULL_TREE) 97990075Sobrien type |= ARM_FT_NAKED; 98090075Sobrien 98190075Sobrien if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX) 98290075Sobrien type |= ARM_FT_EXCEPTION_HANDLER; 98390075Sobrien else 98490075Sobrien { 98590075Sobrien a = lookup_attribute ("isr", attr); 98690075Sobrien if (a == NULL_TREE) 98790075Sobrien a = lookup_attribute ("interrupt", attr); 98890075Sobrien 98990075Sobrien if (a == NULL_TREE) 99090075Sobrien type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL; 99190075Sobrien else 99290075Sobrien type |= arm_isr_value (TREE_VALUE (a)); 99390075Sobrien } 99490075Sobrien 99590075Sobrien return type; 99690075Sobrien} 99790075Sobrien 99890075Sobrien/* Returns the type of the current function. */ 99990075Sobrien 100090075Sobrienunsigned long 1001132718Skanarm_current_func_type (void) 100290075Sobrien{ 100390075Sobrien if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN) 100490075Sobrien cfun->machine->func_type = arm_compute_func_type (); 100590075Sobrien 100690075Sobrien return cfun->machine->func_type; 100790075Sobrien} 100890075Sobrien 1009132718Skan/* Return 1 if it is possible to return using a single instruction. 1010132718Skan If SIBLING is non-null, this is a test for a return before a sibling 1011132718Skan call. SIBLING is the call insn, so we can examine its register usage. */ 101290075Sobrien 101390075Sobrienint 1014132718Skanuse_return_insn (int iscond, rtx sibling) 101590075Sobrien{ 101690075Sobrien int regno; 101790075Sobrien unsigned int func_type; 1018107590Sobrien unsigned long saved_int_regs; 1019132718Skan unsigned HOST_WIDE_INT stack_adjust; 102090075Sobrien 102190075Sobrien /* Never use a return instruction before reload has run. */ 102290075Sobrien if (!reload_completed) 102390075Sobrien return 0; 1024132718Skan 102590075Sobrien func_type = arm_current_func_type (); 102690075Sobrien 102796263Sobrien /* Naked functions and volatile functions need special 102896263Sobrien consideration. */ 102996263Sobrien if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED)) 103090075Sobrien return 0; 1031117395Skan 1032117395Skan /* So do interrupt functions that use the frame pointer. */ 1033117395Skan if (IS_INTERRUPT (func_type) && frame_pointer_needed) 1034117395Skan return 0; 1035132718Skan 1036132718Skan stack_adjust = arm_get_frame_size () + current_function_outgoing_args_size; 1037132718Skan 103890075Sobrien /* As do variadic functions. */ 103990075Sobrien if (current_function_pretend_args_size 104096263Sobrien || cfun->machine->uses_anonymous_args 1041132718Skan /* Or if the function calls __builtin_eh_return () */ 104290075Sobrien || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER 1043132718Skan /* Or if the function calls alloca */ 1044132718Skan || current_function_calls_alloca 1045132718Skan /* Or if there is a stack adjustment. However, if the stack pointer 1046132718Skan is saved on the stack, we can use a pre-incrementing stack load. */ 1047132718Skan || !(stack_adjust == 0 || (frame_pointer_needed && stack_adjust == 4))) 104890075Sobrien return 0; 104990075Sobrien 1050107590Sobrien saved_int_regs = arm_compute_save_reg_mask (); 1051107590Sobrien 1052132718Skan /* Unfortunately, the insn 1053132718Skan 1054132718Skan ldmib sp, {..., sp, ...} 1055132718Skan 1056132718Skan triggers a bug on most SA-110 based devices, such that the stack 1057132718Skan pointer won't be correctly restored if the instruction takes a 1058132718Skan page fault. We work around this problem by popping r3 along with 1059132718Skan the other registers, since that is never slower than executing 1060132718Skan another instruction. 1061132718Skan 1062132718Skan We test for !arm_arch5 here, because code for any architecture 1063132718Skan less than this could potentially be run on one of the buggy 1064132718Skan chips. */ 1065132718Skan if (stack_adjust == 4 && !arm_arch5) 1066132718Skan { 1067132718Skan /* Validate that r3 is a call-clobbered register (always true in 1068132718Skan the default abi) ... */ 1069132718Skan if (!call_used_regs[3]) 1070132718Skan return 0; 1071132718Skan 1072132718Skan /* ... that it isn't being used for a return value (always true 1073132718Skan until we implement return-in-regs), or for a tail-call 1074132718Skan argument ... */ 1075132718Skan if (sibling) 1076132718Skan { 1077132718Skan if (GET_CODE (sibling) != CALL_INSN) 1078132718Skan abort (); 1079132718Skan 1080132718Skan if (find_regno_fusage (sibling, USE, 3)) 1081132718Skan return 0; 1082132718Skan } 1083132718Skan 1084132718Skan /* ... and that there are no call-saved registers in r0-r2 1085132718Skan (always true in the default ABI). */ 1086132718Skan if (saved_int_regs & 0x7) 1087132718Skan return 0; 1088132718Skan } 1089132718Skan 109090075Sobrien /* Can't be done if interworking with Thumb, and any registers have been 1091107590Sobrien stacked. */ 1092107590Sobrien if (TARGET_INTERWORK && saved_int_regs != 0) 109390075Sobrien return 0; 1094107590Sobrien 1095107590Sobrien /* On StrongARM, conditional returns are expensive if they aren't 1096107590Sobrien taken and multiple registers have been stacked. */ 1097107590Sobrien if (iscond && arm_is_strong) 109890075Sobrien { 1099107590Sobrien /* Conditional return when just the LR is stored is a simple 1100107590Sobrien conditional-load instruction, that's not expensive. */ 1101107590Sobrien if (saved_int_regs != 0 && saved_int_regs != (1 << LR_REGNUM)) 1102107590Sobrien return 0; 110390075Sobrien 110490075Sobrien if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) 110590075Sobrien return 0; 110690075Sobrien } 1107107590Sobrien 1108107590Sobrien /* If there are saved registers but the LR isn't saved, then we need 1109107590Sobrien two instructions for the return. */ 1110107590Sobrien if (saved_int_regs && !(saved_int_regs & (1 << LR_REGNUM))) 1111107590Sobrien return 0; 1112107590Sobrien 1113132718Skan /* Can't be done if any of the FPA regs are pushed, 111490075Sobrien since this also requires an insn. */ 111590075Sobrien if (TARGET_HARD_FLOAT) 111690075Sobrien for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++) 111790075Sobrien if (regs_ever_live[regno] && !call_used_regs[regno]) 111890075Sobrien return 0; 111990075Sobrien 1120132718Skan if (TARGET_REALLY_IWMMXT) 1121132718Skan for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++) 1122132718Skan if (regs_ever_live[regno] && ! call_used_regs [regno]) 1123132718Skan return 0; 1124132718Skan 112590075Sobrien return 1; 112690075Sobrien} 112790075Sobrien 112890075Sobrien/* Return TRUE if int I is a valid immediate ARM constant. */ 112990075Sobrien 113090075Sobrienint 1131132718Skanconst_ok_for_arm (HOST_WIDE_INT i) 113290075Sobrien{ 113390075Sobrien unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF; 113490075Sobrien 113590075Sobrien /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must 113690075Sobrien be all zero, or all one. */ 113790075Sobrien if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0 113890075Sobrien && ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) 113990075Sobrien != ((~(unsigned HOST_WIDE_INT) 0) 114090075Sobrien & ~(unsigned HOST_WIDE_INT) 0xffffffff))) 114190075Sobrien return FALSE; 114290075Sobrien 114390075Sobrien /* Fast return for 0 and powers of 2 */ 114490075Sobrien if ((i & (i - 1)) == 0) 114590075Sobrien return TRUE; 114690075Sobrien 114790075Sobrien do 114890075Sobrien { 114990075Sobrien if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) 115090075Sobrien return TRUE; 115190075Sobrien mask = 115290075Sobrien (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) 115390075Sobrien >> (32 - 2)) | ~(unsigned HOST_WIDE_INT) 0xffffffff; 115490075Sobrien } 115590075Sobrien while (mask != ~(unsigned HOST_WIDE_INT) 0xFF); 115690075Sobrien 115790075Sobrien return FALSE; 115890075Sobrien} 115990075Sobrien 116090075Sobrien/* Return true if I is a valid constant for the operation CODE. */ 116190075Sobrienstatic int 1162132718Skanconst_ok_for_op (HOST_WIDE_INT i, enum rtx_code code) 116390075Sobrien{ 116490075Sobrien if (const_ok_for_arm (i)) 116590075Sobrien return 1; 116690075Sobrien 116790075Sobrien switch (code) 116890075Sobrien { 116990075Sobrien case PLUS: 117090075Sobrien return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); 117190075Sobrien 117290075Sobrien case MINUS: /* Should only occur with (MINUS I reg) => rsb */ 117390075Sobrien case XOR: 117490075Sobrien case IOR: 117590075Sobrien return 0; 117690075Sobrien 117790075Sobrien case AND: 117890075Sobrien return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); 117990075Sobrien 118090075Sobrien default: 118190075Sobrien abort (); 118290075Sobrien } 118390075Sobrien} 118490075Sobrien 118590075Sobrien/* Emit a sequence of insns to handle a large constant. 118690075Sobrien CODE is the code of the operation required, it can be any of SET, PLUS, 118790075Sobrien IOR, AND, XOR, MINUS; 118890075Sobrien MODE is the mode in which the operation is being performed; 118990075Sobrien VAL is the integer to operate on; 119090075Sobrien SOURCE is the other operand (a register, or a null-pointer for SET); 119190075Sobrien SUBTARGETS means it is safe to create scratch registers if that will 119290075Sobrien either produce a simpler sequence, or we will want to cse the values. 119390075Sobrien Return value is the number of insns emitted. */ 119490075Sobrien 119590075Sobrienint 1196132718Skanarm_split_constant (enum rtx_code code, enum machine_mode mode, 1197132718Skan HOST_WIDE_INT val, rtx target, rtx source, int subtargets) 119890075Sobrien{ 119990075Sobrien if (subtargets || code == SET 120090075Sobrien || (GET_CODE (target) == REG && GET_CODE (source) == REG 120190075Sobrien && REGNO (target) != REGNO (source))) 120290075Sobrien { 120390075Sobrien /* After arm_reorg has been called, we can't fix up expensive 1204117395Skan constants by pushing them into memory so we must synthesize 120590075Sobrien them in-line, regardless of the cost. This is only likely to 120690075Sobrien be more costly on chips that have load delay slots and we are 120790075Sobrien compiling without running the scheduler (so no splitting 120890075Sobrien occurred before the final instruction emission). 120990075Sobrien 121090075Sobrien Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c 121190075Sobrien */ 121290075Sobrien if (!after_arm_reorg 121390075Sobrien && (arm_gen_constant (code, mode, val, target, source, 1, 0) 121490075Sobrien > arm_constant_limit + (code != SET))) 121590075Sobrien { 121690075Sobrien if (code == SET) 121790075Sobrien { 121890075Sobrien /* Currently SET is the only monadic value for CODE, all 121990075Sobrien the rest are diadic. */ 122090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (val))); 122190075Sobrien return 1; 122290075Sobrien } 122390075Sobrien else 122490075Sobrien { 122590075Sobrien rtx temp = subtargets ? gen_reg_rtx (mode) : target; 122690075Sobrien 122790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (val))); 122890075Sobrien /* For MINUS, the value is subtracted from, since we never 122990075Sobrien have subtraction of a constant. */ 123090075Sobrien if (code == MINUS) 123190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 123290075Sobrien gen_rtx_MINUS (mode, temp, source))); 123390075Sobrien else 123490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 123590075Sobrien gen_rtx (code, mode, source, temp))); 123690075Sobrien return 2; 123790075Sobrien } 123890075Sobrien } 123990075Sobrien } 124090075Sobrien 124190075Sobrien return arm_gen_constant (code, mode, val, target, source, subtargets, 1); 124290075Sobrien} 124390075Sobrien 124490075Sobrienstatic int 1245132718Skancount_insns_for_constant (HOST_WIDE_INT remainder, int i) 124690075Sobrien{ 124790075Sobrien HOST_WIDE_INT temp1; 124890075Sobrien int num_insns = 0; 124990075Sobrien do 125090075Sobrien { 125190075Sobrien int end; 125290075Sobrien 125390075Sobrien if (i <= 0) 125490075Sobrien i += 32; 125590075Sobrien if (remainder & (3 << (i - 2))) 125690075Sobrien { 125790075Sobrien end = i - 8; 125890075Sobrien if (end < 0) 125990075Sobrien end += 32; 126090075Sobrien temp1 = remainder & ((0x0ff << end) 126190075Sobrien | ((i < end) ? (0xff >> (32 - end)) : 0)); 126290075Sobrien remainder &= ~temp1; 126390075Sobrien num_insns++; 126490075Sobrien i -= 6; 126590075Sobrien } 126690075Sobrien i -= 2; 126790075Sobrien } while (remainder); 126890075Sobrien return num_insns; 126990075Sobrien} 127090075Sobrien 127190075Sobrien/* As above, but extra parameter GENERATE which, if clear, suppresses 127290075Sobrien RTL generation. */ 127390075Sobrien 127490075Sobrienstatic int 1275132718Skanarm_gen_constant (enum rtx_code code, enum machine_mode mode, 1276132718Skan HOST_WIDE_INT val, rtx target, rtx source, int subtargets, 1277132718Skan int generate) 127890075Sobrien{ 127990075Sobrien int can_invert = 0; 128090075Sobrien int can_negate = 0; 128190075Sobrien int can_negate_initial = 0; 128290075Sobrien int can_shift = 0; 128390075Sobrien int i; 128490075Sobrien int num_bits_set = 0; 128590075Sobrien int set_sign_bit_copies = 0; 128690075Sobrien int clear_sign_bit_copies = 0; 128790075Sobrien int clear_zero_bit_copies = 0; 128890075Sobrien int set_zero_bit_copies = 0; 128990075Sobrien int insns = 0; 129090075Sobrien unsigned HOST_WIDE_INT temp1, temp2; 129190075Sobrien unsigned HOST_WIDE_INT remainder = val & 0xffffffff; 129290075Sobrien 129390075Sobrien /* Find out which operations are safe for a given CODE. Also do a quick 129490075Sobrien check for degenerate cases; these can occur when DImode operations 129590075Sobrien are split. */ 129690075Sobrien switch (code) 129790075Sobrien { 129890075Sobrien case SET: 129990075Sobrien can_invert = 1; 130090075Sobrien can_shift = 1; 130190075Sobrien can_negate = 1; 130290075Sobrien break; 130390075Sobrien 130490075Sobrien case PLUS: 130590075Sobrien can_negate = 1; 130690075Sobrien can_negate_initial = 1; 130790075Sobrien break; 130890075Sobrien 130990075Sobrien case IOR: 131090075Sobrien if (remainder == 0xffffffff) 131190075Sobrien { 131290075Sobrien if (generate) 131390075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 131490075Sobrien GEN_INT (ARM_SIGN_EXTEND (val)))); 131590075Sobrien return 1; 131690075Sobrien } 131790075Sobrien if (remainder == 0) 131890075Sobrien { 131990075Sobrien if (reload_completed && rtx_equal_p (target, source)) 132090075Sobrien return 0; 132190075Sobrien if (generate) 132290075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 132390075Sobrien return 1; 132490075Sobrien } 132590075Sobrien break; 132690075Sobrien 132790075Sobrien case AND: 132890075Sobrien if (remainder == 0) 132990075Sobrien { 133090075Sobrien if (generate) 133190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, const0_rtx)); 133290075Sobrien return 1; 133390075Sobrien } 133490075Sobrien if (remainder == 0xffffffff) 133590075Sobrien { 133690075Sobrien if (reload_completed && rtx_equal_p (target, source)) 133790075Sobrien return 0; 133890075Sobrien if (generate) 133990075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 134090075Sobrien return 1; 134190075Sobrien } 134290075Sobrien can_invert = 1; 134390075Sobrien break; 134490075Sobrien 134590075Sobrien case XOR: 134690075Sobrien if (remainder == 0) 134790075Sobrien { 134890075Sobrien if (reload_completed && rtx_equal_p (target, source)) 134990075Sobrien return 0; 135090075Sobrien if (generate) 135190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, source)); 135290075Sobrien return 1; 135390075Sobrien } 135490075Sobrien if (remainder == 0xffffffff) 135590075Sobrien { 135690075Sobrien if (generate) 135790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 135890075Sobrien gen_rtx_NOT (mode, source))); 135990075Sobrien return 1; 136090075Sobrien } 136190075Sobrien 136290075Sobrien /* We don't know how to handle this yet below. */ 136390075Sobrien abort (); 136490075Sobrien 136590075Sobrien case MINUS: 136690075Sobrien /* We treat MINUS as (val - source), since (source - val) is always 136790075Sobrien passed as (source + (-val)). */ 136890075Sobrien if (remainder == 0) 136990075Sobrien { 137090075Sobrien if (generate) 137190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 137290075Sobrien gen_rtx_NEG (mode, source))); 137390075Sobrien return 1; 137490075Sobrien } 137590075Sobrien if (const_ok_for_arm (val)) 137690075Sobrien { 137790075Sobrien if (generate) 137890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 137990075Sobrien gen_rtx_MINUS (mode, GEN_INT (val), 138090075Sobrien source))); 138190075Sobrien return 1; 138290075Sobrien } 138390075Sobrien can_negate = 1; 138490075Sobrien 138590075Sobrien break; 138690075Sobrien 138790075Sobrien default: 138890075Sobrien abort (); 138990075Sobrien } 139090075Sobrien 139190075Sobrien /* If we can do it in one insn get out quickly. */ 139290075Sobrien if (const_ok_for_arm (val) 139390075Sobrien || (can_negate_initial && const_ok_for_arm (-val)) 139490075Sobrien || (can_invert && const_ok_for_arm (~val))) 139590075Sobrien { 139690075Sobrien if (generate) 139790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 139890075Sobrien (source ? gen_rtx (code, mode, source, 139990075Sobrien GEN_INT (val)) 140090075Sobrien : GEN_INT (val)))); 140190075Sobrien return 1; 140290075Sobrien } 140390075Sobrien 140490075Sobrien /* Calculate a few attributes that may be useful for specific 140590075Sobrien optimizations. */ 140690075Sobrien for (i = 31; i >= 0; i--) 140790075Sobrien { 140890075Sobrien if ((remainder & (1 << i)) == 0) 140990075Sobrien clear_sign_bit_copies++; 141090075Sobrien else 141190075Sobrien break; 141290075Sobrien } 141390075Sobrien 141490075Sobrien for (i = 31; i >= 0; i--) 141590075Sobrien { 141690075Sobrien if ((remainder & (1 << i)) != 0) 141790075Sobrien set_sign_bit_copies++; 141890075Sobrien else 141990075Sobrien break; 142090075Sobrien } 142190075Sobrien 142290075Sobrien for (i = 0; i <= 31; i++) 142390075Sobrien { 142490075Sobrien if ((remainder & (1 << i)) == 0) 142590075Sobrien clear_zero_bit_copies++; 142690075Sobrien else 142790075Sobrien break; 142890075Sobrien } 142990075Sobrien 143090075Sobrien for (i = 0; i <= 31; i++) 143190075Sobrien { 143290075Sobrien if ((remainder & (1 << i)) != 0) 143390075Sobrien set_zero_bit_copies++; 143490075Sobrien else 143590075Sobrien break; 143690075Sobrien } 143790075Sobrien 143890075Sobrien switch (code) 143990075Sobrien { 144090075Sobrien case SET: 144190075Sobrien /* See if we can do this by sign_extending a constant that is known 144290075Sobrien to be negative. This is a good, way of doing it, since the shift 144390075Sobrien may well merge into a subsequent insn. */ 144490075Sobrien if (set_sign_bit_copies > 1) 144590075Sobrien { 144690075Sobrien if (const_ok_for_arm 144790075Sobrien (temp1 = ARM_SIGN_EXTEND (remainder 144890075Sobrien << (set_sign_bit_copies - 1)))) 144990075Sobrien { 145090075Sobrien if (generate) 145190075Sobrien { 145290075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 145390075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, 145490075Sobrien GEN_INT (temp1))); 145590075Sobrien emit_insn (gen_ashrsi3 (target, new_src, 145690075Sobrien GEN_INT (set_sign_bit_copies - 1))); 145790075Sobrien } 145890075Sobrien return 2; 145990075Sobrien } 146090075Sobrien /* For an inverted constant, we will need to set the low bits, 146190075Sobrien these will be shifted out of harm's way. */ 146290075Sobrien temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; 146390075Sobrien if (const_ok_for_arm (~temp1)) 146490075Sobrien { 146590075Sobrien if (generate) 146690075Sobrien { 146790075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 146890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, 146990075Sobrien GEN_INT (temp1))); 147090075Sobrien emit_insn (gen_ashrsi3 (target, new_src, 147190075Sobrien GEN_INT (set_sign_bit_copies - 1))); 147290075Sobrien } 147390075Sobrien return 2; 147490075Sobrien } 147590075Sobrien } 147690075Sobrien 147790075Sobrien /* See if we can generate this by setting the bottom (or the top) 147890075Sobrien 16 bits, and then shifting these into the other half of the 147990075Sobrien word. We only look for the simplest cases, to do more would cost 148090075Sobrien too much. Be careful, however, not to generate this when the 148190075Sobrien alternative would take fewer insns. */ 148290075Sobrien if (val & 0xffff0000) 148390075Sobrien { 148490075Sobrien temp1 = remainder & 0xffff0000; 148590075Sobrien temp2 = remainder & 0x0000ffff; 148690075Sobrien 148790075Sobrien /* Overlaps outside this range are best done using other methods. */ 148890075Sobrien for (i = 9; i < 24; i++) 148990075Sobrien { 149090075Sobrien if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) 149190075Sobrien && !const_ok_for_arm (temp2)) 149290075Sobrien { 149390075Sobrien rtx new_src = (subtargets 149490075Sobrien ? (generate ? gen_reg_rtx (mode) : NULL_RTX) 149590075Sobrien : target); 149690075Sobrien insns = arm_gen_constant (code, mode, temp2, new_src, 149790075Sobrien source, subtargets, generate); 149890075Sobrien source = new_src; 149990075Sobrien if (generate) 150090075Sobrien emit_insn (gen_rtx_SET 150190075Sobrien (VOIDmode, target, 150290075Sobrien gen_rtx_IOR (mode, 150390075Sobrien gen_rtx_ASHIFT (mode, source, 150490075Sobrien GEN_INT (i)), 150590075Sobrien source))); 150690075Sobrien return insns + 1; 150790075Sobrien } 150890075Sobrien } 150990075Sobrien 151090075Sobrien /* Don't duplicate cases already considered. */ 151190075Sobrien for (i = 17; i < 24; i++) 151290075Sobrien { 151390075Sobrien if (((temp1 | (temp1 >> i)) == remainder) 151490075Sobrien && !const_ok_for_arm (temp1)) 151590075Sobrien { 151690075Sobrien rtx new_src = (subtargets 151790075Sobrien ? (generate ? gen_reg_rtx (mode) : NULL_RTX) 151890075Sobrien : target); 151990075Sobrien insns = arm_gen_constant (code, mode, temp1, new_src, 152090075Sobrien source, subtargets, generate); 152190075Sobrien source = new_src; 152290075Sobrien if (generate) 152390075Sobrien emit_insn 152490075Sobrien (gen_rtx_SET (VOIDmode, target, 152590075Sobrien gen_rtx_IOR 152690075Sobrien (mode, 152790075Sobrien gen_rtx_LSHIFTRT (mode, source, 152890075Sobrien GEN_INT (i)), 152990075Sobrien source))); 153090075Sobrien return insns + 1; 153190075Sobrien } 153290075Sobrien } 153390075Sobrien } 153490075Sobrien break; 153590075Sobrien 153690075Sobrien case IOR: 153790075Sobrien case XOR: 153890075Sobrien /* If we have IOR or XOR, and the constant can be loaded in a 153990075Sobrien single instruction, and we can find a temporary to put it in, 154090075Sobrien then this can be done in two instructions instead of 3-4. */ 154190075Sobrien if (subtargets 154290075Sobrien /* TARGET can't be NULL if SUBTARGETS is 0 */ 154390075Sobrien || (reload_completed && !reg_mentioned_p (target, source))) 154490075Sobrien { 154590075Sobrien if (const_ok_for_arm (ARM_SIGN_EXTEND (~val))) 154690075Sobrien { 154790075Sobrien if (generate) 154890075Sobrien { 154990075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 155090075Sobrien 155190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, GEN_INT (val))); 155290075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 155390075Sobrien gen_rtx (code, mode, source, sub))); 155490075Sobrien } 155590075Sobrien return 2; 155690075Sobrien } 155790075Sobrien } 155890075Sobrien 155990075Sobrien if (code == XOR) 156090075Sobrien break; 156190075Sobrien 156290075Sobrien if (set_sign_bit_copies > 8 156390075Sobrien && (val & (-1 << (32 - set_sign_bit_copies))) == val) 156490075Sobrien { 156590075Sobrien if (generate) 156690075Sobrien { 156790075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 156890075Sobrien rtx shift = GEN_INT (set_sign_bit_copies); 156990075Sobrien 157090075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 157190075Sobrien gen_rtx_NOT (mode, 157290075Sobrien gen_rtx_ASHIFT (mode, 157390075Sobrien source, 157490075Sobrien shift)))); 157590075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 157690075Sobrien gen_rtx_NOT (mode, 157790075Sobrien gen_rtx_LSHIFTRT (mode, sub, 157890075Sobrien shift)))); 157990075Sobrien } 158090075Sobrien return 2; 158190075Sobrien } 158290075Sobrien 158390075Sobrien if (set_zero_bit_copies > 8 158490075Sobrien && (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder) 158590075Sobrien { 158690075Sobrien if (generate) 158790075Sobrien { 158890075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 158990075Sobrien rtx shift = GEN_INT (set_zero_bit_copies); 159090075Sobrien 159190075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 159290075Sobrien gen_rtx_NOT (mode, 159390075Sobrien gen_rtx_LSHIFTRT (mode, 159490075Sobrien source, 159590075Sobrien shift)))); 159690075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 159790075Sobrien gen_rtx_NOT (mode, 159890075Sobrien gen_rtx_ASHIFT (mode, sub, 159990075Sobrien shift)))); 160090075Sobrien } 160190075Sobrien return 2; 160290075Sobrien } 160390075Sobrien 160490075Sobrien if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~val))) 160590075Sobrien { 160690075Sobrien if (generate) 160790075Sobrien { 160890075Sobrien rtx sub = subtargets ? gen_reg_rtx (mode) : target; 160990075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 161090075Sobrien gen_rtx_NOT (mode, source))); 161190075Sobrien source = sub; 161290075Sobrien if (subtargets) 161390075Sobrien sub = gen_reg_rtx (mode); 161490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, sub, 161590075Sobrien gen_rtx_AND (mode, source, 161690075Sobrien GEN_INT (temp1)))); 161790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, target, 161890075Sobrien gen_rtx_NOT (mode, sub))); 161990075Sobrien } 162090075Sobrien return 3; 162190075Sobrien } 162290075Sobrien break; 162390075Sobrien 162490075Sobrien case AND: 162590075Sobrien /* See if two shifts will do 2 or more insn's worth of work. */ 162690075Sobrien if (clear_sign_bit_copies >= 16 && clear_sign_bit_copies < 24) 162790075Sobrien { 162890075Sobrien HOST_WIDE_INT shift_mask = ((0xffffffff 162990075Sobrien << (32 - clear_sign_bit_copies)) 163090075Sobrien & 0xffffffff); 163190075Sobrien 163290075Sobrien if ((remainder | shift_mask) != 0xffffffff) 163390075Sobrien { 163490075Sobrien if (generate) 163590075Sobrien { 163690075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 163790075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 163890075Sobrien new_src, source, subtargets, 1); 163990075Sobrien source = new_src; 164090075Sobrien } 164190075Sobrien else 164290075Sobrien { 164390075Sobrien rtx targ = subtargets ? NULL_RTX : target; 164490075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 164590075Sobrien targ, source, subtargets, 0); 164690075Sobrien } 164790075Sobrien } 164890075Sobrien 164990075Sobrien if (generate) 165090075Sobrien { 165190075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 165290075Sobrien rtx shift = GEN_INT (clear_sign_bit_copies); 165390075Sobrien 165490075Sobrien emit_insn (gen_ashlsi3 (new_src, source, shift)); 165590075Sobrien emit_insn (gen_lshrsi3 (target, new_src, shift)); 165690075Sobrien } 165790075Sobrien 165890075Sobrien return insns + 2; 165990075Sobrien } 166090075Sobrien 166190075Sobrien if (clear_zero_bit_copies >= 16 && clear_zero_bit_copies < 24) 166290075Sobrien { 166390075Sobrien HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1; 166490075Sobrien 166590075Sobrien if ((remainder | shift_mask) != 0xffffffff) 166690075Sobrien { 166790075Sobrien if (generate) 166890075Sobrien { 166990075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 167090075Sobrien 167190075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 167290075Sobrien new_src, source, subtargets, 1); 167390075Sobrien source = new_src; 167490075Sobrien } 167590075Sobrien else 167690075Sobrien { 167790075Sobrien rtx targ = subtargets ? NULL_RTX : target; 167890075Sobrien 167990075Sobrien insns = arm_gen_constant (AND, mode, remainder | shift_mask, 168090075Sobrien targ, source, subtargets, 0); 168190075Sobrien } 168290075Sobrien } 168390075Sobrien 168490075Sobrien if (generate) 168590075Sobrien { 168690075Sobrien rtx new_src = subtargets ? gen_reg_rtx (mode) : target; 168790075Sobrien rtx shift = GEN_INT (clear_zero_bit_copies); 168890075Sobrien 168990075Sobrien emit_insn (gen_lshrsi3 (new_src, source, shift)); 169090075Sobrien emit_insn (gen_ashlsi3 (target, new_src, shift)); 169190075Sobrien } 169290075Sobrien 169390075Sobrien return insns + 2; 169490075Sobrien } 169590075Sobrien 169690075Sobrien break; 169790075Sobrien 169890075Sobrien default: 169990075Sobrien break; 170090075Sobrien } 170190075Sobrien 170290075Sobrien for (i = 0; i < 32; i++) 170390075Sobrien if (remainder & (1 << i)) 170490075Sobrien num_bits_set++; 170590075Sobrien 170690075Sobrien if (code == AND || (can_invert && num_bits_set > 16)) 170790075Sobrien remainder = (~remainder) & 0xffffffff; 170890075Sobrien else if (code == PLUS && num_bits_set > 16) 170990075Sobrien remainder = (-remainder) & 0xffffffff; 171090075Sobrien else 171190075Sobrien { 171290075Sobrien can_invert = 0; 171390075Sobrien can_negate = 0; 171490075Sobrien } 171590075Sobrien 171690075Sobrien /* Now try and find a way of doing the job in either two or three 171790075Sobrien instructions. 171890075Sobrien We start by looking for the largest block of zeros that are aligned on 171990075Sobrien a 2-bit boundary, we then fill up the temps, wrapping around to the 172090075Sobrien top of the word when we drop off the bottom. 172190075Sobrien In the worst case this code should produce no more than four insns. */ 172290075Sobrien { 172390075Sobrien int best_start = 0; 172490075Sobrien int best_consecutive_zeros = 0; 172590075Sobrien 172690075Sobrien for (i = 0; i < 32; i += 2) 172790075Sobrien { 172890075Sobrien int consecutive_zeros = 0; 172990075Sobrien 173090075Sobrien if (!(remainder & (3 << i))) 173190075Sobrien { 173290075Sobrien while ((i < 32) && !(remainder & (3 << i))) 173390075Sobrien { 173490075Sobrien consecutive_zeros += 2; 173590075Sobrien i += 2; 173690075Sobrien } 173790075Sobrien if (consecutive_zeros > best_consecutive_zeros) 173890075Sobrien { 173990075Sobrien best_consecutive_zeros = consecutive_zeros; 174090075Sobrien best_start = i - consecutive_zeros; 174190075Sobrien } 174290075Sobrien i -= 2; 174390075Sobrien } 174490075Sobrien } 174590075Sobrien 174690075Sobrien /* So long as it won't require any more insns to do so, it's 174790075Sobrien desirable to emit a small constant (in bits 0...9) in the last 174890075Sobrien insn. This way there is more chance that it can be combined with 174990075Sobrien a later addressing insn to form a pre-indexed load or store 175090075Sobrien operation. Consider: 175190075Sobrien 175290075Sobrien *((volatile int *)0xe0000100) = 1; 175390075Sobrien *((volatile int *)0xe0000110) = 2; 175490075Sobrien 175590075Sobrien We want this to wind up as: 175690075Sobrien 175790075Sobrien mov rA, #0xe0000000 175890075Sobrien mov rB, #1 175990075Sobrien str rB, [rA, #0x100] 176090075Sobrien mov rB, #2 176190075Sobrien str rB, [rA, #0x110] 176290075Sobrien 176390075Sobrien rather than having to synthesize both large constants from scratch. 176490075Sobrien 176590075Sobrien Therefore, we calculate how many insns would be required to emit 176690075Sobrien the constant starting from `best_start', and also starting from 176790075Sobrien zero (ie with bit 31 first to be output). If `best_start' doesn't 176890075Sobrien yield a shorter sequence, we may as well use zero. */ 176990075Sobrien if (best_start != 0 177090075Sobrien && ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder) 177190075Sobrien && (count_insns_for_constant (remainder, 0) <= 177290075Sobrien count_insns_for_constant (remainder, best_start))) 177390075Sobrien best_start = 0; 177490075Sobrien 177590075Sobrien /* Now start emitting the insns. */ 177690075Sobrien i = best_start; 177790075Sobrien do 177890075Sobrien { 177990075Sobrien int end; 178090075Sobrien 178190075Sobrien if (i <= 0) 178290075Sobrien i += 32; 178390075Sobrien if (remainder & (3 << (i - 2))) 178490075Sobrien { 178590075Sobrien end = i - 8; 178690075Sobrien if (end < 0) 178790075Sobrien end += 32; 178890075Sobrien temp1 = remainder & ((0x0ff << end) 178990075Sobrien | ((i < end) ? (0xff >> (32 - end)) : 0)); 179090075Sobrien remainder &= ~temp1; 179190075Sobrien 179290075Sobrien if (generate) 179390075Sobrien { 179490075Sobrien rtx new_src, temp1_rtx; 179590075Sobrien 179690075Sobrien if (code == SET || code == MINUS) 179790075Sobrien { 179890075Sobrien new_src = (subtargets ? gen_reg_rtx (mode) : target); 179990075Sobrien if (can_invert && code != MINUS) 180090075Sobrien temp1 = ~temp1; 180190075Sobrien } 180290075Sobrien else 180390075Sobrien { 180490075Sobrien if (remainder && subtargets) 180590075Sobrien new_src = gen_reg_rtx (mode); 180690075Sobrien else 180790075Sobrien new_src = target; 180890075Sobrien if (can_invert) 180990075Sobrien temp1 = ~temp1; 181090075Sobrien else if (can_negate) 181190075Sobrien temp1 = -temp1; 181290075Sobrien } 181390075Sobrien 181490075Sobrien temp1 = trunc_int_for_mode (temp1, mode); 181590075Sobrien temp1_rtx = GEN_INT (temp1); 181690075Sobrien 181790075Sobrien if (code == SET) 181890075Sobrien ; 181990075Sobrien else if (code == MINUS) 182090075Sobrien temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source); 182190075Sobrien else 182290075Sobrien temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx); 182390075Sobrien 182490075Sobrien emit_insn (gen_rtx_SET (VOIDmode, new_src, temp1_rtx)); 182590075Sobrien source = new_src; 182690075Sobrien } 182790075Sobrien 182890075Sobrien if (code == SET) 182990075Sobrien { 183090075Sobrien can_invert = 0; 183190075Sobrien code = PLUS; 183290075Sobrien } 183390075Sobrien else if (code == MINUS) 183490075Sobrien code = PLUS; 183590075Sobrien 183690075Sobrien insns++; 183790075Sobrien i -= 6; 183890075Sobrien } 183990075Sobrien i -= 2; 184090075Sobrien } 184190075Sobrien while (remainder); 184290075Sobrien } 184390075Sobrien 184490075Sobrien return insns; 184590075Sobrien} 184690075Sobrien 184790075Sobrien/* Canonicalize a comparison so that we are more likely to recognize it. 184890075Sobrien This can be done for a few constant compares, where we can make the 184990075Sobrien immediate value easier to load. */ 185090075Sobrien 185190075Sobrienenum rtx_code 1852132718Skanarm_canonicalize_comparison (enum rtx_code code, rtx * op1) 185390075Sobrien{ 185490075Sobrien unsigned HOST_WIDE_INT i = INTVAL (*op1); 185590075Sobrien 185690075Sobrien switch (code) 185790075Sobrien { 185890075Sobrien case EQ: 185990075Sobrien case NE: 186090075Sobrien return code; 186190075Sobrien 186290075Sobrien case GT: 186390075Sobrien case LE: 186490075Sobrien if (i != ((((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) - 1) 186590075Sobrien && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) 186690075Sobrien { 186790075Sobrien *op1 = GEN_INT (i + 1); 186890075Sobrien return code == GT ? GE : LT; 186990075Sobrien } 187090075Sobrien break; 187190075Sobrien 187290075Sobrien case GE: 187390075Sobrien case LT: 187490075Sobrien if (i != (((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) 187590075Sobrien && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) 187690075Sobrien { 187790075Sobrien *op1 = GEN_INT (i - 1); 187890075Sobrien return code == GE ? GT : LE; 187990075Sobrien } 188090075Sobrien break; 188190075Sobrien 188290075Sobrien case GTU: 188390075Sobrien case LEU: 188490075Sobrien if (i != ~((unsigned HOST_WIDE_INT) 0) 188590075Sobrien && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1)))) 188690075Sobrien { 188790075Sobrien *op1 = GEN_INT (i + 1); 188890075Sobrien return code == GTU ? GEU : LTU; 188990075Sobrien } 189090075Sobrien break; 189190075Sobrien 189290075Sobrien case GEU: 189390075Sobrien case LTU: 189490075Sobrien if (i != 0 189590075Sobrien && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1)))) 189690075Sobrien { 189790075Sobrien *op1 = GEN_INT (i - 1); 189890075Sobrien return code == GEU ? GTU : LEU; 189990075Sobrien } 190090075Sobrien break; 190190075Sobrien 190290075Sobrien default: 190390075Sobrien abort (); 190490075Sobrien } 190590075Sobrien 190690075Sobrien return code; 190790075Sobrien} 190890075Sobrien 190990075Sobrien/* Decide whether a type should be returned in memory (true) 191090075Sobrien or in a register (false). This is called by the macro 191190075Sobrien RETURN_IN_MEMORY. */ 191290075Sobrienint 1913132718Skanarm_return_in_memory (tree type) 191490075Sobrien{ 1915117395Skan HOST_WIDE_INT size; 1916117395Skan 191790075Sobrien if (!AGGREGATE_TYPE_P (type)) 191890075Sobrien /* All simple types are returned in registers. */ 191990075Sobrien return 0; 1920117395Skan 1921117395Skan size = int_size_in_bytes (type); 1922117395Skan 1923117395Skan if (TARGET_ATPCS) 1924117395Skan { 1925117395Skan /* ATPCS returns aggregate types in memory only if they are 1926117395Skan larger than a word (or are variable size). */ 1927117395Skan return (size < 0 || size > UNITS_PER_WORD); 1928117395Skan } 192990075Sobrien 1930132718Skan /* For the arm-wince targets we choose to be compatible with Microsoft's 193190075Sobrien ARM and Thumb compilers, which always return aggregates in memory. */ 193290075Sobrien#ifndef ARM_WINCE 193390075Sobrien /* All structures/unions bigger than one word are returned in memory. 193490075Sobrien Also catch the case where int_size_in_bytes returns -1. In this case 1935132718Skan the aggregate is either huge or of variable size, and in either case 193690075Sobrien we will want to return it via memory and not in a register. */ 1937117395Skan if (size < 0 || size > UNITS_PER_WORD) 193890075Sobrien return 1; 193990075Sobrien 194090075Sobrien if (TREE_CODE (type) == RECORD_TYPE) 194190075Sobrien { 194290075Sobrien tree field; 194390075Sobrien 194490075Sobrien /* For a struct the APCS says that we only return in a register 194590075Sobrien if the type is 'integer like' and every addressable element 194690075Sobrien has an offset of zero. For practical purposes this means 194790075Sobrien that the structure can have at most one non bit-field element 194890075Sobrien and that this element must be the first one in the structure. */ 194990075Sobrien 195090075Sobrien /* Find the first field, ignoring non FIELD_DECL things which will 195190075Sobrien have been created by C++. */ 195290075Sobrien for (field = TYPE_FIELDS (type); 195390075Sobrien field && TREE_CODE (field) != FIELD_DECL; 195490075Sobrien field = TREE_CHAIN (field)) 195590075Sobrien continue; 195690075Sobrien 195790075Sobrien if (field == NULL) 195890075Sobrien return 0; /* An empty structure. Allowed by an extension to ANSI C. */ 195990075Sobrien 196090075Sobrien /* Check that the first field is valid for returning in a register. */ 196190075Sobrien 196290075Sobrien /* ... Floats are not allowed */ 196390075Sobrien if (FLOAT_TYPE_P (TREE_TYPE (field))) 196490075Sobrien return 1; 196590075Sobrien 196690075Sobrien /* ... Aggregates that are not themselves valid for returning in 196790075Sobrien a register are not allowed. */ 196890075Sobrien if (RETURN_IN_MEMORY (TREE_TYPE (field))) 196990075Sobrien return 1; 197090075Sobrien 197190075Sobrien /* Now check the remaining fields, if any. Only bitfields are allowed, 197290075Sobrien since they are not addressable. */ 197390075Sobrien for (field = TREE_CHAIN (field); 197490075Sobrien field; 197590075Sobrien field = TREE_CHAIN (field)) 197690075Sobrien { 197790075Sobrien if (TREE_CODE (field) != FIELD_DECL) 197890075Sobrien continue; 197990075Sobrien 198090075Sobrien if (!DECL_BIT_FIELD_TYPE (field)) 198190075Sobrien return 1; 198290075Sobrien } 198390075Sobrien 198490075Sobrien return 0; 198590075Sobrien } 198690075Sobrien 198790075Sobrien if (TREE_CODE (type) == UNION_TYPE) 198890075Sobrien { 198990075Sobrien tree field; 199090075Sobrien 199190075Sobrien /* Unions can be returned in registers if every element is 199290075Sobrien integral, or can be returned in an integer register. */ 199390075Sobrien for (field = TYPE_FIELDS (type); 199490075Sobrien field; 199590075Sobrien field = TREE_CHAIN (field)) 199690075Sobrien { 199790075Sobrien if (TREE_CODE (field) != FIELD_DECL) 199890075Sobrien continue; 199990075Sobrien 200090075Sobrien if (FLOAT_TYPE_P (TREE_TYPE (field))) 200190075Sobrien return 1; 200290075Sobrien 200390075Sobrien if (RETURN_IN_MEMORY (TREE_TYPE (field))) 200490075Sobrien return 1; 200590075Sobrien } 200690075Sobrien 200790075Sobrien return 0; 200890075Sobrien } 200990075Sobrien#endif /* not ARM_WINCE */ 201090075Sobrien 201190075Sobrien /* Return all other types in memory. */ 201290075Sobrien return 1; 201390075Sobrien} 201490075Sobrien 2015132718Skan/* Indicate whether or not words of a double are in big-endian order. */ 2016117395Skan 2017117395Skanint 2018132718Skanarm_float_words_big_endian (void) 2019117395Skan{ 2020132718Skan if (TARGET_CIRRUS) 2021132718Skan return 0; 2022117395Skan 2023117395Skan /* For FPA, float words are always big-endian. For VFP, floats words 2024117395Skan follow the memory system mode. */ 2025117395Skan 2026117395Skan if (TARGET_HARD_FLOAT) 2027117395Skan { 2028117395Skan /* FIXME: TARGET_HARD_FLOAT currently implies FPA. */ 2029117395Skan return 1; 2030117395Skan } 2031117395Skan 2032117395Skan if (TARGET_VFP) 2033117395Skan return (TARGET_BIG_END ? 1 : 0); 2034117395Skan 2035117395Skan return 1; 2036117395Skan} 2037117395Skan 203890075Sobrien/* Initialize a variable CUM of type CUMULATIVE_ARGS 203990075Sobrien for a call to a function whose data type is FNTYPE. 204090075Sobrien For a library call, FNTYPE is NULL. */ 204190075Sobrienvoid 2042132718Skanarm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, 2043132718Skan rtx libname ATTRIBUTE_UNUSED, 2044132718Skan tree fndecl ATTRIBUTE_UNUSED) 204590075Sobrien{ 204690075Sobrien /* On the ARM, the offset starts at 0. */ 2047132718Skan pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype), fntype)) ? 1 : 0); 2048132718Skan pcum->iwmmxt_nregs = 0; 204990075Sobrien 205090075Sobrien pcum->call_cookie = CALL_NORMAL; 205190075Sobrien 205290075Sobrien if (TARGET_LONG_CALLS) 205390075Sobrien pcum->call_cookie = CALL_LONG; 205490075Sobrien 205590075Sobrien /* Check for long call/short call attributes. The attributes 205690075Sobrien override any command line option. */ 205790075Sobrien if (fntype) 205890075Sobrien { 205990075Sobrien if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (fntype))) 206090075Sobrien pcum->call_cookie = CALL_SHORT; 206190075Sobrien else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (fntype))) 206290075Sobrien pcum->call_cookie = CALL_LONG; 206390075Sobrien } 2064132718Skan 2065132718Skan /* Varargs vectors are treated the same as long long. 2066132718Skan named_count avoids having to change the way arm handles 'named' */ 2067132718Skan pcum->named_count = 0; 2068132718Skan pcum->nargs = 0; 2069132718Skan 2070132718Skan if (TARGET_REALLY_IWMMXT && fntype) 2071132718Skan { 2072132718Skan tree fn_arg; 2073132718Skan 2074132718Skan for (fn_arg = TYPE_ARG_TYPES (fntype); 2075132718Skan fn_arg; 2076132718Skan fn_arg = TREE_CHAIN (fn_arg)) 2077132718Skan pcum->named_count += 1; 2078132718Skan 2079132718Skan if (! pcum->named_count) 2080132718Skan pcum->named_count = INT_MAX; 2081132718Skan } 208290075Sobrien} 208390075Sobrien 208490075Sobrien/* Determine where to put an argument to a function. 208590075Sobrien Value is zero to push the argument on the stack, 208690075Sobrien or a hard register in which to store the argument. 208790075Sobrien 208890075Sobrien MODE is the argument's machine mode. 208990075Sobrien TYPE is the data type of the argument (as a tree). 209090075Sobrien This is null for libcalls where that information may 209190075Sobrien not be available. 209290075Sobrien CUM is a variable of type CUMULATIVE_ARGS which gives info about 209390075Sobrien the preceding args and about the function being called. 209490075Sobrien NAMED is nonzero if this argument is a named parameter 209590075Sobrien (otherwise it is an extra parameter matching an ellipsis). */ 209690075Sobrien 209790075Sobrienrtx 2098132718Skanarm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode, 2099132718Skan tree type ATTRIBUTE_UNUSED, int named) 210090075Sobrien{ 2101132718Skan if (TARGET_REALLY_IWMMXT) 2102132718Skan { 2103132718Skan if (VECTOR_MODE_SUPPORTED_P (mode)) 2104132718Skan { 2105132718Skan /* varargs vectors are treated the same as long long. 2106132718Skan named_count avoids having to change the way arm handles 'named' */ 2107132718Skan if (pcum->named_count <= pcum->nargs + 1) 2108132718Skan { 2109132718Skan if (pcum->nregs == 1) 2110132718Skan pcum->nregs += 1; 2111132718Skan if (pcum->nregs <= 2) 2112132718Skan return gen_rtx_REG (mode, pcum->nregs); 2113132718Skan else 2114132718Skan return NULL_RTX; 2115132718Skan } 2116132718Skan else if (pcum->iwmmxt_nregs <= 9) 2117132718Skan return gen_rtx_REG (mode, pcum->iwmmxt_nregs + FIRST_IWMMXT_REGNUM); 2118132718Skan else 2119132718Skan return NULL_RTX; 2120132718Skan } 2121132718Skan else if ((mode == DImode || mode == DFmode) && pcum->nregs & 1) 2122132718Skan pcum->nregs += 1; 2123132718Skan } 2124132718Skan 212590075Sobrien if (mode == VOIDmode) 212690075Sobrien /* Compute operand 2 of the call insn. */ 212790075Sobrien return GEN_INT (pcum->call_cookie); 212890075Sobrien 212990075Sobrien if (!named || pcum->nregs >= NUM_ARG_REGS) 213090075Sobrien return NULL_RTX; 213190075Sobrien 213290075Sobrien return gen_rtx_REG (mode, pcum->nregs); 213390075Sobrien} 2134117395Skan 2135117395Skan/* Variable sized types are passed by reference. This is a GCC 2136117395Skan extension to the ARM ABI. */ 2137117395Skan 2138117395Skanint 2139132718Skanarm_function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, 2140132718Skan enum machine_mode mode ATTRIBUTE_UNUSED, 2141132718Skan tree type, int named ATTRIBUTE_UNUSED) 2142117395Skan{ 2143117395Skan return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST; 2144117395Skan} 2145117395Skan 2146117395Skan/* Implement va_arg. */ 2147117395Skan 2148117395Skanrtx 2149132718Skanarm_va_arg (tree valist, tree type) 2150117395Skan{ 2151117395Skan /* Variable sized types are passed by reference. */ 2152117395Skan if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) 2153117395Skan { 2154117395Skan rtx addr = std_expand_builtin_va_arg (valist, build_pointer_type (type)); 2155117395Skan return gen_rtx_MEM (ptr_mode, force_reg (Pmode, addr)); 2156117395Skan } 2157117395Skan 2158132718Skan if (FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), NULL) == IWMMXT_ALIGNMENT) 2159132718Skan { 2160132718Skan tree minus_eight; 2161132718Skan tree t; 2162132718Skan 2163132718Skan /* Maintain 64-bit alignment of the valist pointer by 2164132718Skan constructing: valist = ((valist + (8 - 1)) & -8). */ 2165132718Skan minus_eight = build_int_2 (- (IWMMXT_ALIGNMENT / BITS_PER_UNIT), -1); 2166132718Skan t = build_int_2 ((IWMMXT_ALIGNMENT / BITS_PER_UNIT) - 1, 0); 2167132718Skan t = build (PLUS_EXPR, TREE_TYPE (valist), valist, t); 2168132718Skan t = build (BIT_AND_EXPR, TREE_TYPE (t), t, minus_eight); 2169132718Skan t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t); 2170132718Skan TREE_SIDE_EFFECTS (t) = 1; 2171132718Skan expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); 2172132718Skan 2173132718Skan /* This is to stop the combine pass optimizing 2174132718Skan away the alignment adjustment. */ 2175132718Skan mark_reg_pointer (arg_pointer_rtx, PARM_BOUNDARY); 2176132718Skan } 2177132718Skan 2178117395Skan return std_expand_builtin_va_arg (valist, type); 2179117395Skan} 218090075Sobrien 218190075Sobrien/* Encode the current state of the #pragma [no_]long_calls. */ 218290075Sobrientypedef enum 218390075Sobrien{ 218490075Sobrien OFF, /* No #pramgma [no_]long_calls is in effect. */ 218590075Sobrien LONG, /* #pragma long_calls is in effect. */ 218690075Sobrien SHORT /* #pragma no_long_calls is in effect. */ 218790075Sobrien} arm_pragma_enum; 218890075Sobrien 218990075Sobrienstatic arm_pragma_enum arm_pragma_long_calls = OFF; 219090075Sobrien 219190075Sobrienvoid 2192132718Skanarm_pr_long_calls (struct cpp_reader * pfile ATTRIBUTE_UNUSED) 219390075Sobrien{ 219490075Sobrien arm_pragma_long_calls = LONG; 219590075Sobrien} 219690075Sobrien 219790075Sobrienvoid 2198132718Skanarm_pr_no_long_calls (struct cpp_reader * pfile ATTRIBUTE_UNUSED) 219990075Sobrien{ 220090075Sobrien arm_pragma_long_calls = SHORT; 220190075Sobrien} 220290075Sobrien 220390075Sobrienvoid 2204132718Skanarm_pr_long_calls_off (struct cpp_reader * pfile ATTRIBUTE_UNUSED) 220590075Sobrien{ 220690075Sobrien arm_pragma_long_calls = OFF; 220790075Sobrien} 220890075Sobrien 220990075Sobrien/* Table of machine attributes. */ 221090075Sobrienconst struct attribute_spec arm_attribute_table[] = 221190075Sobrien{ 221290075Sobrien /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ 221390075Sobrien /* Function calls made to this symbol must be done indirectly, because 221490075Sobrien it may lie outside of the 26 bit addressing range of a normal function 221590075Sobrien call. */ 221690075Sobrien { "long_call", 0, 0, false, true, true, NULL }, 221790075Sobrien /* Whereas these functions are always known to reside within the 26 bit 221890075Sobrien addressing range. */ 221990075Sobrien { "short_call", 0, 0, false, true, true, NULL }, 222090075Sobrien /* Interrupt Service Routines have special prologue and epilogue requirements. */ 222190075Sobrien { "isr", 0, 1, false, false, false, arm_handle_isr_attribute }, 222290075Sobrien { "interrupt", 0, 1, false, false, false, arm_handle_isr_attribute }, 222390075Sobrien { "naked", 0, 0, true, false, false, arm_handle_fndecl_attribute }, 222490075Sobrien#ifdef ARM_PE 222590075Sobrien /* ARM/PE has three new attributes: 222690075Sobrien interfacearm - ? 222790075Sobrien dllexport - for exporting a function/variable that will live in a dll 222890075Sobrien dllimport - for importing a function/variable from a dll 222990075Sobrien 223090075Sobrien Microsoft allows multiple declspecs in one __declspec, separating 223190075Sobrien them with spaces. We do NOT support this. Instead, use __declspec 223290075Sobrien multiple times. 223390075Sobrien */ 223490075Sobrien { "dllimport", 0, 0, true, false, false, NULL }, 223590075Sobrien { "dllexport", 0, 0, true, false, false, NULL }, 223690075Sobrien { "interfacearm", 0, 0, true, false, false, arm_handle_fndecl_attribute }, 223790075Sobrien#endif 223890075Sobrien { NULL, 0, 0, false, false, false, NULL } 223990075Sobrien}; 224090075Sobrien 224190075Sobrien/* Handle an attribute requiring a FUNCTION_DECL; 224290075Sobrien arguments as in struct attribute_spec.handler. */ 224390075Sobrienstatic tree 2244132718Skanarm_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, 2245132718Skan int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) 224690075Sobrien{ 224790075Sobrien if (TREE_CODE (*node) != FUNCTION_DECL) 224890075Sobrien { 224990075Sobrien warning ("`%s' attribute only applies to functions", 225090075Sobrien IDENTIFIER_POINTER (name)); 225190075Sobrien *no_add_attrs = true; 225290075Sobrien } 225390075Sobrien 225490075Sobrien return NULL_TREE; 225590075Sobrien} 225690075Sobrien 225790075Sobrien/* Handle an "interrupt" or "isr" attribute; 225890075Sobrien arguments as in struct attribute_spec.handler. */ 225990075Sobrienstatic tree 2260132718Skanarm_handle_isr_attribute (tree *node, tree name, tree args, int flags, 2261132718Skan bool *no_add_attrs) 226290075Sobrien{ 226390075Sobrien if (DECL_P (*node)) 226490075Sobrien { 226590075Sobrien if (TREE_CODE (*node) != FUNCTION_DECL) 226690075Sobrien { 226790075Sobrien warning ("`%s' attribute only applies to functions", 226890075Sobrien IDENTIFIER_POINTER (name)); 226990075Sobrien *no_add_attrs = true; 227090075Sobrien } 227190075Sobrien /* FIXME: the argument if any is checked for type attributes; 227290075Sobrien should it be checked for decl ones? */ 227390075Sobrien } 227490075Sobrien else 227590075Sobrien { 227690075Sobrien if (TREE_CODE (*node) == FUNCTION_TYPE 227790075Sobrien || TREE_CODE (*node) == METHOD_TYPE) 227890075Sobrien { 227990075Sobrien if (arm_isr_value (args) == ARM_FT_UNKNOWN) 228090075Sobrien { 228190075Sobrien warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); 228290075Sobrien *no_add_attrs = true; 228390075Sobrien } 228490075Sobrien } 228590075Sobrien else if (TREE_CODE (*node) == POINTER_TYPE 228690075Sobrien && (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE 228790075Sobrien || TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE) 228890075Sobrien && arm_isr_value (args) != ARM_FT_UNKNOWN) 228990075Sobrien { 229090075Sobrien *node = build_type_copy (*node); 229190075Sobrien TREE_TYPE (*node) = build_type_attribute_variant 229290075Sobrien (TREE_TYPE (*node), 229390075Sobrien tree_cons (name, args, TYPE_ATTRIBUTES (TREE_TYPE (*node)))); 229490075Sobrien *no_add_attrs = true; 229590075Sobrien } 229690075Sobrien else 229790075Sobrien { 229890075Sobrien /* Possibly pass this attribute on from the type to a decl. */ 229990075Sobrien if (flags & ((int) ATTR_FLAG_DECL_NEXT 230090075Sobrien | (int) ATTR_FLAG_FUNCTION_NEXT 230190075Sobrien | (int) ATTR_FLAG_ARRAY_NEXT)) 230290075Sobrien { 230390075Sobrien *no_add_attrs = true; 230490075Sobrien return tree_cons (name, args, NULL_TREE); 230590075Sobrien } 230690075Sobrien else 230790075Sobrien { 230890075Sobrien warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); 230990075Sobrien } 231090075Sobrien } 231190075Sobrien } 231290075Sobrien 231390075Sobrien return NULL_TREE; 231490075Sobrien} 231590075Sobrien 231690075Sobrien/* Return 0 if the attributes for two types are incompatible, 1 if they 231790075Sobrien are compatible, and 2 if they are nearly compatible (which causes a 231890075Sobrien warning to be generated). */ 231990075Sobrienstatic int 2320132718Skanarm_comp_type_attributes (tree type1, tree type2) 232190075Sobrien{ 232290075Sobrien int l1, l2, s1, s2; 232390075Sobrien 232490075Sobrien /* Check for mismatch of non-default calling convention. */ 232590075Sobrien if (TREE_CODE (type1) != FUNCTION_TYPE) 232690075Sobrien return 1; 232790075Sobrien 232890075Sobrien /* Check for mismatched call attributes. */ 232990075Sobrien l1 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type1)) != NULL; 233090075Sobrien l2 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type2)) != NULL; 233190075Sobrien s1 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type1)) != NULL; 233290075Sobrien s2 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type2)) != NULL; 233390075Sobrien 233490075Sobrien /* Only bother to check if an attribute is defined. */ 233590075Sobrien if (l1 | l2 | s1 | s2) 233690075Sobrien { 233790075Sobrien /* If one type has an attribute, the other must have the same attribute. */ 233890075Sobrien if ((l1 != l2) || (s1 != s2)) 233990075Sobrien return 0; 234090075Sobrien 234190075Sobrien /* Disallow mixed attributes. */ 234290075Sobrien if ((l1 & s2) || (l2 & s1)) 234390075Sobrien return 0; 234490075Sobrien } 234590075Sobrien 234690075Sobrien /* Check for mismatched ISR attribute. */ 234790075Sobrien l1 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type1)) != NULL; 234890075Sobrien if (! l1) 234990075Sobrien l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type1)) != NULL; 235090075Sobrien l2 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type2)) != NULL; 235190075Sobrien if (! l2) 235290075Sobrien l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type2)) != NULL; 235390075Sobrien if (l1 != l2) 235490075Sobrien return 0; 235590075Sobrien 235690075Sobrien return 1; 235790075Sobrien} 235890075Sobrien 235990075Sobrien/* Encode long_call or short_call attribute by prefixing 236090075Sobrien symbol name in DECL with a special character FLAG. */ 236190075Sobrienvoid 2362132718Skanarm_encode_call_attribute (tree decl, int flag) 236390075Sobrien{ 236490075Sobrien const char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0); 236590075Sobrien int len = strlen (str); 236690075Sobrien char * newstr; 236790075Sobrien 236890075Sobrien /* Do not allow weak functions to be treated as short call. */ 236990075Sobrien if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR) 237090075Sobrien return; 237190075Sobrien 237290075Sobrien newstr = alloca (len + 2); 237390075Sobrien newstr[0] = flag; 237490075Sobrien strcpy (newstr + 1, str); 237590075Sobrien 237690075Sobrien newstr = (char *) ggc_alloc_string (newstr, len + 1); 237790075Sobrien XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr; 237890075Sobrien} 237990075Sobrien 238090075Sobrien/* Assigns default attributes to newly defined type. This is used to 238190075Sobrien set short_call/long_call attributes for function types of 238290075Sobrien functions defined inside corresponding #pragma scopes. */ 238390075Sobrienstatic void 2384132718Skanarm_set_default_type_attributes (tree type) 238590075Sobrien{ 238690075Sobrien /* Add __attribute__ ((long_call)) to all functions, when 238790075Sobrien inside #pragma long_calls or __attribute__ ((short_call)), 238890075Sobrien when inside #pragma no_long_calls. */ 238990075Sobrien if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) 239090075Sobrien { 239190075Sobrien tree type_attr_list, attr_name; 239290075Sobrien type_attr_list = TYPE_ATTRIBUTES (type); 239390075Sobrien 239490075Sobrien if (arm_pragma_long_calls == LONG) 239590075Sobrien attr_name = get_identifier ("long_call"); 239690075Sobrien else if (arm_pragma_long_calls == SHORT) 239790075Sobrien attr_name = get_identifier ("short_call"); 239890075Sobrien else 239990075Sobrien return; 240090075Sobrien 240190075Sobrien type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list); 240290075Sobrien TYPE_ATTRIBUTES (type) = type_attr_list; 240390075Sobrien } 240490075Sobrien} 240590075Sobrien 240690075Sobrien/* Return 1 if the operand is a SYMBOL_REF for a function known to be 2407132718Skan defined within the current compilation unit. If this cannot be 240890075Sobrien determined, then 0 is returned. */ 240990075Sobrienstatic int 2410132718Skancurrent_file_function_operand (rtx sym_ref) 241190075Sobrien{ 241290075Sobrien /* This is a bit of a fib. A function will have a short call flag 241390075Sobrien applied to its name if it has the short call attribute, or it has 241490075Sobrien already been defined within the current compilation unit. */ 241590075Sobrien if (ENCODED_SHORT_CALL_ATTR_P (XSTR (sym_ref, 0))) 241690075Sobrien return 1; 241790075Sobrien 241890075Sobrien /* The current function is always defined within the current compilation 241990075Sobrien unit. if it s a weak definition however, then this may not be the real 242090075Sobrien definition of the function, and so we have to say no. */ 242190075Sobrien if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0) 242290075Sobrien && !DECL_WEAK (current_function_decl)) 242390075Sobrien return 1; 242490075Sobrien 242590075Sobrien /* We cannot make the determination - default to returning 0. */ 242690075Sobrien return 0; 242790075Sobrien} 242890075Sobrien 2429117395Skan/* Return nonzero if a 32 bit "long_call" should be generated for 243090075Sobrien this call. We generate a long_call if the function: 243190075Sobrien 243290075Sobrien a. has an __attribute__((long call)) 243390075Sobrien or b. is within the scope of a #pragma long_calls 243490075Sobrien or c. the -mlong-calls command line switch has been specified 243590075Sobrien 243690075Sobrien However we do not generate a long call if the function: 243790075Sobrien 243890075Sobrien d. has an __attribute__ ((short_call)) 243990075Sobrien or e. is inside the scope of a #pragma no_long_calls 244090075Sobrien or f. has an __attribute__ ((section)) 244190075Sobrien or g. is defined within the current compilation unit. 244290075Sobrien 244390075Sobrien This function will be called by C fragments contained in the machine 244490075Sobrien description file. CALL_REF and CALL_COOKIE correspond to the matched 244590075Sobrien rtl operands. CALL_SYMBOL is used to distinguish between 244690075Sobrien two different callers of the function. It is set to 1 in the 244790075Sobrien "call_symbol" and "call_symbol_value" patterns and to 0 in the "call" 244890075Sobrien and "call_value" patterns. This is because of the difference in the 244990075Sobrien SYM_REFs passed by these patterns. */ 245090075Sobrienint 2451132718Skanarm_is_longcall_p (rtx sym_ref, int call_cookie, int call_symbol) 245290075Sobrien{ 245390075Sobrien if (!call_symbol) 245490075Sobrien { 245590075Sobrien if (GET_CODE (sym_ref) != MEM) 245690075Sobrien return 0; 245790075Sobrien 245890075Sobrien sym_ref = XEXP (sym_ref, 0); 245990075Sobrien } 246090075Sobrien 246190075Sobrien if (GET_CODE (sym_ref) != SYMBOL_REF) 246290075Sobrien return 0; 246390075Sobrien 246490075Sobrien if (call_cookie & CALL_SHORT) 246590075Sobrien return 0; 246690075Sobrien 246790075Sobrien if (TARGET_LONG_CALLS && flag_function_sections) 246890075Sobrien return 1; 246990075Sobrien 247090075Sobrien if (current_file_function_operand (sym_ref)) 247190075Sobrien return 0; 247290075Sobrien 247390075Sobrien return (call_cookie & CALL_LONG) 247490075Sobrien || ENCODED_LONG_CALL_ATTR_P (XSTR (sym_ref, 0)) 247590075Sobrien || TARGET_LONG_CALLS; 247690075Sobrien} 247790075Sobrien 2478117395Skan/* Return nonzero if it is ok to make a tail-call to DECL. */ 2479132718Skanstatic bool 2480132718Skanarm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) 248190075Sobrien{ 248290075Sobrien int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL; 248390075Sobrien 2484132718Skan if (cfun->machine->sibcall_blocked) 2485132718Skan return false; 2486132718Skan 248790075Sobrien /* Never tailcall something for which we have no decl, or if we 248890075Sobrien are in Thumb mode. */ 248990075Sobrien if (decl == NULL || TARGET_THUMB) 2490132718Skan return false; 249190075Sobrien 249290075Sobrien /* Get the calling method. */ 249390075Sobrien if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) 249490075Sobrien call_type = CALL_SHORT; 249590075Sobrien else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) 249690075Sobrien call_type = CALL_LONG; 249790075Sobrien 249890075Sobrien /* Cannot tail-call to long calls, since these are out of range of 249990075Sobrien a branch instruction. However, if not compiling PIC, we know 250090075Sobrien we can reach the symbol if it is in this compilation unit. */ 250190075Sobrien if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl))) 2502132718Skan return false; 250390075Sobrien 250490075Sobrien /* If we are interworking and the function is not declared static 250590075Sobrien then we can't tail-call it unless we know that it exists in this 250690075Sobrien compilation unit (since it might be a Thumb routine). */ 250790075Sobrien if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl)) 2508132718Skan return false; 250990075Sobrien 251090075Sobrien /* Never tailcall from an ISR routine - it needs a special exit sequence. */ 251190075Sobrien if (IS_INTERRUPT (arm_current_func_type ())) 2512132718Skan return false; 251390075Sobrien 251490075Sobrien /* Everything else is ok. */ 2515132718Skan return true; 251690075Sobrien} 251790075Sobrien 251890075Sobrien 2519132718Skan/* Addressing mode support functions. */ 2520132718Skan 2521132718Skan/* Return nonzero if X is a legitimate immediate operand when compiling 2522132718Skan for PIC. */ 252390075Sobrienint 2524132718Skanlegitimate_pic_operand_p (rtx x) 252590075Sobrien{ 252690075Sobrien if (CONSTANT_P (x) 252790075Sobrien && flag_pic 252890075Sobrien && (GET_CODE (x) == SYMBOL_REF 252990075Sobrien || (GET_CODE (x) == CONST 253090075Sobrien && GET_CODE (XEXP (x, 0)) == PLUS 253190075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF))) 253290075Sobrien return 0; 253390075Sobrien 253490075Sobrien return 1; 253590075Sobrien} 253690075Sobrien 253790075Sobrienrtx 2538132718Skanlegitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) 253990075Sobrien{ 254090075Sobrien if (GET_CODE (orig) == SYMBOL_REF 254190075Sobrien || GET_CODE (orig) == LABEL_REF) 254290075Sobrien { 254390075Sobrien#ifndef AOF_ASSEMBLER 254490075Sobrien rtx pic_ref, address; 254590075Sobrien#endif 254690075Sobrien rtx insn; 254790075Sobrien int subregs = 0; 254890075Sobrien 254990075Sobrien if (reg == 0) 255090075Sobrien { 255190075Sobrien if (no_new_pseudos) 255290075Sobrien abort (); 255390075Sobrien else 255490075Sobrien reg = gen_reg_rtx (Pmode); 255590075Sobrien 255690075Sobrien subregs = 1; 255790075Sobrien } 255890075Sobrien 255990075Sobrien#ifdef AOF_ASSEMBLER 256090075Sobrien /* The AOF assembler can generate relocations for these directly, and 256190075Sobrien understands that the PIC register has to be added into the offset. */ 256290075Sobrien insn = emit_insn (gen_pic_load_addr_based (reg, orig)); 256390075Sobrien#else 256490075Sobrien if (subregs) 256590075Sobrien address = gen_reg_rtx (Pmode); 256690075Sobrien else 256790075Sobrien address = reg; 256890075Sobrien 256990075Sobrien if (TARGET_ARM) 257090075Sobrien emit_insn (gen_pic_load_addr_arm (address, orig)); 257190075Sobrien else 257290075Sobrien emit_insn (gen_pic_load_addr_thumb (address, orig)); 257390075Sobrien 257496263Sobrien if ((GET_CODE (orig) == LABEL_REF 257596263Sobrien || (GET_CODE (orig) == SYMBOL_REF && 2576132718Skan SYMBOL_REF_LOCAL_P (orig))) 257796263Sobrien && NEED_GOT_RELOC) 257890075Sobrien pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, address); 257990075Sobrien else 258090075Sobrien { 258190075Sobrien pic_ref = gen_rtx_MEM (Pmode, 258290075Sobrien gen_rtx_PLUS (Pmode, pic_offset_table_rtx, 258390075Sobrien address)); 258490075Sobrien RTX_UNCHANGING_P (pic_ref) = 1; 258590075Sobrien } 258690075Sobrien 258790075Sobrien insn = emit_move_insn (reg, pic_ref); 258890075Sobrien#endif 258990075Sobrien current_function_uses_pic_offset_table = 1; 259090075Sobrien /* Put a REG_EQUAL note on this insn, so that it can be optimized 259190075Sobrien by loop. */ 259290075Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, 259390075Sobrien REG_NOTES (insn)); 259490075Sobrien return reg; 259590075Sobrien } 259690075Sobrien else if (GET_CODE (orig) == CONST) 259790075Sobrien { 259890075Sobrien rtx base, offset; 259990075Sobrien 260090075Sobrien if (GET_CODE (XEXP (orig, 0)) == PLUS 260190075Sobrien && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) 260290075Sobrien return orig; 260390075Sobrien 260490075Sobrien if (reg == 0) 260590075Sobrien { 260690075Sobrien if (no_new_pseudos) 260790075Sobrien abort (); 260890075Sobrien else 260990075Sobrien reg = gen_reg_rtx (Pmode); 261090075Sobrien } 261190075Sobrien 261290075Sobrien if (GET_CODE (XEXP (orig, 0)) == PLUS) 261390075Sobrien { 261490075Sobrien base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); 261590075Sobrien offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, 261690075Sobrien base == reg ? 0 : reg); 261790075Sobrien } 261890075Sobrien else 261990075Sobrien abort (); 262090075Sobrien 262190075Sobrien if (GET_CODE (offset) == CONST_INT) 262290075Sobrien { 262390075Sobrien /* The base register doesn't really matter, we only want to 262490075Sobrien test the index for the appropriate mode. */ 2625132718Skan if (!arm_legitimate_index_p (mode, offset, 0)) 2626132718Skan { 2627132718Skan if (!no_new_pseudos) 2628132718Skan offset = force_reg (Pmode, offset); 2629132718Skan else 2630132718Skan abort (); 2631132718Skan } 263290075Sobrien 263390075Sobrien if (GET_CODE (offset) == CONST_INT) 263490075Sobrien return plus_constant (base, INTVAL (offset)); 263590075Sobrien } 263690075Sobrien 263790075Sobrien if (GET_MODE_SIZE (mode) > 4 263890075Sobrien && (GET_MODE_CLASS (mode) == MODE_INT 263990075Sobrien || TARGET_SOFT_FLOAT)) 264090075Sobrien { 264190075Sobrien emit_insn (gen_addsi3 (reg, base, offset)); 264290075Sobrien return reg; 264390075Sobrien } 264490075Sobrien 264590075Sobrien return gen_rtx_PLUS (Pmode, base, offset); 264690075Sobrien } 264790075Sobrien 264890075Sobrien return orig; 264990075Sobrien} 265090075Sobrien 265190075Sobrien/* Generate code to load the PIC register. PROLOGUE is true if 265290075Sobrien called from arm_expand_prologue (in which case we want the 265390075Sobrien generated insns at the start of the function); false if called 265490075Sobrien by an exception receiver that needs the PIC register reloaded 265590075Sobrien (in which case the insns are just dumped at the current location). */ 265690075Sobrienvoid 2657132718Skanarm_finalize_pic (int prologue ATTRIBUTE_UNUSED) 265890075Sobrien{ 265990075Sobrien#ifndef AOF_ASSEMBLER 266090075Sobrien rtx l1, pic_tmp, pic_tmp2, seq, pic_rtx; 266190075Sobrien rtx global_offset_table; 266290075Sobrien 266390075Sobrien if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE) 266490075Sobrien return; 266590075Sobrien 266690075Sobrien if (!flag_pic) 266790075Sobrien abort (); 266890075Sobrien 266990075Sobrien start_sequence (); 267090075Sobrien l1 = gen_label_rtx (); 267190075Sobrien 267290075Sobrien global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); 267390075Sobrien /* On the ARM the PC register contains 'dot + 8' at the time of the 267490075Sobrien addition, on the Thumb it is 'dot + 4'. */ 267590075Sobrien pic_tmp = plus_constant (gen_rtx_LABEL_REF (Pmode, l1), TARGET_ARM ? 8 : 4); 267690075Sobrien if (GOT_PCREL) 267790075Sobrien pic_tmp2 = gen_rtx_CONST (VOIDmode, 267890075Sobrien gen_rtx_PLUS (Pmode, global_offset_table, pc_rtx)); 267990075Sobrien else 268090075Sobrien pic_tmp2 = gen_rtx_CONST (VOIDmode, global_offset_table); 268190075Sobrien 268290075Sobrien pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp)); 268390075Sobrien 268490075Sobrien if (TARGET_ARM) 268590075Sobrien { 268690075Sobrien emit_insn (gen_pic_load_addr_arm (pic_offset_table_rtx, pic_rtx)); 268790075Sobrien emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1)); 268890075Sobrien } 268990075Sobrien else 269090075Sobrien { 269190075Sobrien emit_insn (gen_pic_load_addr_thumb (pic_offset_table_rtx, pic_rtx)); 269290075Sobrien emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1)); 269390075Sobrien } 269490075Sobrien 2695117395Skan seq = get_insns (); 269690075Sobrien end_sequence (); 269790075Sobrien if (prologue) 269890075Sobrien emit_insn_after (seq, get_insns ()); 269990075Sobrien else 270090075Sobrien emit_insn (seq); 270190075Sobrien 270290075Sobrien /* Need to emit this whether or not we obey regdecls, 270390075Sobrien since setjmp/longjmp can cause life info to screw up. */ 270490075Sobrien emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); 270590075Sobrien#endif /* AOF_ASSEMBLER */ 270690075Sobrien} 270790075Sobrien 2708132718Skan/* Return nonzero if X is valid as an ARM state addressing register. */ 2709132718Skanstatic int 2710132718Skanarm_address_register_rtx_p (rtx x, int strict_p) 2711132718Skan{ 2712132718Skan int regno; 2713132718Skan 2714132718Skan if (GET_CODE (x) != REG) 2715132718Skan return 0; 2716132718Skan 2717132718Skan regno = REGNO (x); 2718132718Skan 2719132718Skan if (strict_p) 2720132718Skan return ARM_REGNO_OK_FOR_BASE_P (regno); 2721132718Skan 2722132718Skan return (regno <= LAST_ARM_REGNUM 2723132718Skan || regno >= FIRST_PSEUDO_REGISTER 2724132718Skan || regno == FRAME_POINTER_REGNUM 2725132718Skan || regno == ARG_POINTER_REGNUM); 2726132718Skan} 2727132718Skan 2728132718Skan/* Return nonzero if X is a valid ARM state address operand. */ 2729132718Skanint 2730132718Skanarm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p) 2731132718Skan{ 2732132718Skan if (arm_address_register_rtx_p (x, strict_p)) 2733132718Skan return 1; 2734132718Skan 2735132718Skan else if (GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC) 2736132718Skan return arm_address_register_rtx_p (XEXP (x, 0), strict_p); 2737132718Skan 2738132718Skan else if ((GET_CODE (x) == POST_MODIFY || GET_CODE (x) == PRE_MODIFY) 2739132718Skan && GET_MODE_SIZE (mode) <= 4 2740132718Skan && arm_address_register_rtx_p (XEXP (x, 0), strict_p) 2741132718Skan && GET_CODE (XEXP (x, 1)) == PLUS 2742132718Skan && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) 2743132718Skan return arm_legitimate_index_p (mode, XEXP (XEXP (x, 1), 1), strict_p); 2744132718Skan 2745132718Skan /* After reload constants split into minipools will have addresses 2746132718Skan from a LABEL_REF. */ 2747132718Skan else if (reload_completed 2748132718Skan && (GET_CODE (x) == LABEL_REF 2749132718Skan || (GET_CODE (x) == CONST 2750132718Skan && GET_CODE (XEXP (x, 0)) == PLUS 2751132718Skan && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF 2752132718Skan && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))) 2753132718Skan return 1; 2754132718Skan 2755132718Skan else if (mode == TImode) 2756132718Skan return 0; 2757132718Skan 2758132718Skan else if (mode == DImode || (TARGET_SOFT_FLOAT && mode == DFmode)) 2759132718Skan { 2760132718Skan if (GET_CODE (x) == PLUS 2761132718Skan && arm_address_register_rtx_p (XEXP (x, 0), strict_p) 2762132718Skan && GET_CODE (XEXP (x, 1)) == CONST_INT) 2763132718Skan { 2764132718Skan HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); 2765132718Skan 2766132718Skan if (val == 4 || val == -4 || val == -8) 2767132718Skan return 1; 2768132718Skan } 2769132718Skan } 2770132718Skan 2771132718Skan else if (GET_CODE (x) == PLUS) 2772132718Skan { 2773132718Skan rtx xop0 = XEXP (x, 0); 2774132718Skan rtx xop1 = XEXP (x, 1); 2775132718Skan 2776132718Skan return ((arm_address_register_rtx_p (xop0, strict_p) 2777132718Skan && arm_legitimate_index_p (mode, xop1, strict_p)) 2778132718Skan || (arm_address_register_rtx_p (xop1, strict_p) 2779132718Skan && arm_legitimate_index_p (mode, xop0, strict_p))); 2780132718Skan } 2781132718Skan 2782132718Skan#if 0 2783132718Skan /* Reload currently can't handle MINUS, so disable this for now */ 2784132718Skan else if (GET_CODE (x) == MINUS) 2785132718Skan { 2786132718Skan rtx xop0 = XEXP (x, 0); 2787132718Skan rtx xop1 = XEXP (x, 1); 2788132718Skan 2789132718Skan return (arm_address_register_rtx_p (xop0, strict_p) 2790132718Skan && arm_legitimate_index_p (mode, xop1, strict_p)); 2791132718Skan } 2792132718Skan#endif 2793132718Skan 2794132718Skan else if (GET_MODE_CLASS (mode) != MODE_FLOAT 2795132718Skan && GET_CODE (x) == SYMBOL_REF 2796132718Skan && CONSTANT_POOL_ADDRESS_P (x) 2797132718Skan && ! (flag_pic 2798132718Skan && symbol_mentioned_p (get_pool_constant (x)))) 2799132718Skan return 1; 2800132718Skan 2801132718Skan else if ((GET_CODE (x) == PRE_INC || GET_CODE (x) == POST_DEC) 2802132718Skan && (GET_MODE_SIZE (mode) <= 4) 2803132718Skan && arm_address_register_rtx_p (XEXP (x, 0), strict_p)) 2804132718Skan return 1; 2805132718Skan 2806132718Skan return 0; 2807132718Skan} 2808132718Skan 2809132718Skan/* Return nonzero if INDEX is valid for an address index operand in 2810132718Skan ARM state. */ 2811132718Skanstatic int 2812132718Skanarm_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p) 2813132718Skan{ 2814132718Skan HOST_WIDE_INT range; 2815132718Skan enum rtx_code code = GET_CODE (index); 2816132718Skan 2817132718Skan if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT) 2818132718Skan return (code == CONST_INT && INTVAL (index) < 1024 2819132718Skan && INTVAL (index) > -1024 2820132718Skan && (INTVAL (index) & 3) == 0); 2821132718Skan 2822132718Skan if (TARGET_CIRRUS 2823132718Skan && (GET_MODE_CLASS (mode) == MODE_FLOAT || mode == DImode)) 2824132718Skan return (code == CONST_INT 2825132718Skan && INTVAL (index) < 255 2826132718Skan && INTVAL (index) > -255); 2827132718Skan 2828132718Skan if (arm_address_register_rtx_p (index, strict_p) 2829132718Skan && GET_MODE_SIZE (mode) <= 4) 2830132718Skan return 1; 2831132718Skan 2832132718Skan if (TARGET_REALLY_IWMMXT && VALID_IWMMXT_REG_MODE (mode)) 2833132718Skan return (code == CONST_INT 2834132718Skan && INTVAL (index) < 256 2835132718Skan && INTVAL (index) > -256); 2836132718Skan 2837132718Skan /* XXX What about ldrsb? */ 2838132718Skan if (GET_MODE_SIZE (mode) <= 4 && code == MULT 2839132718Skan && (!arm_arch4 || (mode) != HImode)) 2840132718Skan { 2841132718Skan rtx xiop0 = XEXP (index, 0); 2842132718Skan rtx xiop1 = XEXP (index, 1); 2843132718Skan 2844132718Skan return ((arm_address_register_rtx_p (xiop0, strict_p) 2845132718Skan && power_of_two_operand (xiop1, SImode)) 2846132718Skan || (arm_address_register_rtx_p (xiop1, strict_p) 2847132718Skan && power_of_two_operand (xiop0, SImode))); 2848132718Skan } 2849132718Skan 2850132718Skan if (GET_MODE_SIZE (mode) <= 4 2851132718Skan && (code == LSHIFTRT || code == ASHIFTRT 2852132718Skan || code == ASHIFT || code == ROTATERT) 2853132718Skan && (!arm_arch4 || (mode) != HImode)) 2854132718Skan { 2855132718Skan rtx op = XEXP (index, 1); 2856132718Skan 2857132718Skan return (arm_address_register_rtx_p (XEXP (index, 0), strict_p) 2858132718Skan && GET_CODE (op) == CONST_INT 2859132718Skan && INTVAL (op) > 0 2860132718Skan && INTVAL (op) <= 31); 2861132718Skan } 2862132718Skan 2863132718Skan /* XXX For ARM v4 we may be doing a sign-extend operation during the 2864132718Skan load, but that has a restricted addressing range and we are unable 2865132718Skan to tell here whether that is the case. To be safe we restrict all 2866132718Skan loads to that range. */ 2867132718Skan if (arm_arch4) 2868132718Skan range = (mode == HImode || mode == QImode) ? 256 : 4096; 2869132718Skan else 2870132718Skan range = (mode == HImode) ? 4095 : 4096; 2871132718Skan 2872132718Skan return (code == CONST_INT 2873132718Skan && INTVAL (index) < range 2874132718Skan && INTVAL (index) > -range); 2875132718Skan} 2876132718Skan 2877132718Skan/* Return nonzero if X is valid as a Thumb state base register. */ 2878132718Skanstatic int 2879132718Skanthumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p) 2880132718Skan{ 2881132718Skan int regno; 2882132718Skan 2883132718Skan if (GET_CODE (x) != REG) 2884132718Skan return 0; 2885132718Skan 2886132718Skan regno = REGNO (x); 2887132718Skan 2888132718Skan if (strict_p) 2889132718Skan return THUMB_REGNO_MODE_OK_FOR_BASE_P (regno, mode); 2890132718Skan 2891132718Skan return (regno <= LAST_LO_REGNUM 2892132718Skan || regno > LAST_VIRTUAL_REGISTER 2893132718Skan || regno == FRAME_POINTER_REGNUM 2894132718Skan || (GET_MODE_SIZE (mode) >= 4 2895132718Skan && (regno == STACK_POINTER_REGNUM 2896132718Skan || regno >= FIRST_PSEUDO_REGISTER 2897132718Skan || x == hard_frame_pointer_rtx 2898132718Skan || x == arg_pointer_rtx))); 2899132718Skan} 2900132718Skan 2901132718Skan/* Return nonzero if x is a legitimate index register. This is the case 2902132718Skan for any base register that can access a QImode object. */ 2903132718Skaninline static int 2904132718Skanthumb_index_register_rtx_p (rtx x, int strict_p) 2905132718Skan{ 2906132718Skan return thumb_base_register_rtx_p (x, QImode, strict_p); 2907132718Skan} 2908132718Skan 2909132718Skan/* Return nonzero if x is a legitimate Thumb-state address. 2910132718Skan 2911132718Skan The AP may be eliminated to either the SP or the FP, so we use the 2912132718Skan least common denominator, e.g. SImode, and offsets from 0 to 64. 2913132718Skan 2914132718Skan ??? Verify whether the above is the right approach. 2915132718Skan 2916132718Skan ??? Also, the FP may be eliminated to the SP, so perhaps that 2917132718Skan needs special handling also. 2918132718Skan 2919132718Skan ??? Look at how the mips16 port solves this problem. It probably uses 2920132718Skan better ways to solve some of these problems. 2921132718Skan 2922132718Skan Although it is not incorrect, we don't accept QImode and HImode 2923132718Skan addresses based on the frame pointer or arg pointer until the 2924132718Skan reload pass starts. This is so that eliminating such addresses 2925132718Skan into stack based ones won't produce impossible code. */ 2926132718Skanint 2927132718Skanthumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p) 2928132718Skan{ 2929132718Skan /* ??? Not clear if this is right. Experiment. */ 2930132718Skan if (GET_MODE_SIZE (mode) < 4 2931132718Skan && !(reload_in_progress || reload_completed) 2932132718Skan && (reg_mentioned_p (frame_pointer_rtx, x) 2933132718Skan || reg_mentioned_p (arg_pointer_rtx, x) 2934132718Skan || reg_mentioned_p (virtual_incoming_args_rtx, x) 2935132718Skan || reg_mentioned_p (virtual_outgoing_args_rtx, x) 2936132718Skan || reg_mentioned_p (virtual_stack_dynamic_rtx, x) 2937132718Skan || reg_mentioned_p (virtual_stack_vars_rtx, x))) 2938132718Skan return 0; 2939132718Skan 2940132718Skan /* Accept any base register. SP only in SImode or larger. */ 2941132718Skan else if (thumb_base_register_rtx_p (x, mode, strict_p)) 2942132718Skan return 1; 2943132718Skan 2944132718Skan /* This is PC relative data before arm_reorg runs. */ 2945132718Skan else if (GET_MODE_SIZE (mode) >= 4 && CONSTANT_P (x) 2946132718Skan && GET_CODE (x) == SYMBOL_REF 2947132718Skan && CONSTANT_POOL_ADDRESS_P (x) && ! flag_pic) 2948132718Skan return 1; 2949132718Skan 2950132718Skan /* This is PC relative data after arm_reorg runs. */ 2951132718Skan else if (GET_MODE_SIZE (mode) >= 4 && reload_completed 2952132718Skan && (GET_CODE (x) == LABEL_REF 2953132718Skan || (GET_CODE (x) == CONST 2954132718Skan && GET_CODE (XEXP (x, 0)) == PLUS 2955132718Skan && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF 2956132718Skan && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))) 2957132718Skan return 1; 2958132718Skan 2959132718Skan /* Post-inc indexing only supported for SImode and larger. */ 2960132718Skan else if (GET_CODE (x) == POST_INC && GET_MODE_SIZE (mode) >= 4 2961132718Skan && thumb_index_register_rtx_p (XEXP (x, 0), strict_p)) 2962132718Skan return 1; 2963132718Skan 2964132718Skan else if (GET_CODE (x) == PLUS) 2965132718Skan { 2966132718Skan /* REG+REG address can be any two index registers. */ 2967132718Skan /* We disallow FRAME+REG addressing since we know that FRAME 2968132718Skan will be replaced with STACK, and SP relative addressing only 2969132718Skan permits SP+OFFSET. */ 2970132718Skan if (GET_MODE_SIZE (mode) <= 4 2971132718Skan && XEXP (x, 0) != frame_pointer_rtx 2972132718Skan && XEXP (x, 1) != frame_pointer_rtx 2973132718Skan && thumb_index_register_rtx_p (XEXP (x, 0), strict_p) 2974132718Skan && thumb_index_register_rtx_p (XEXP (x, 1), strict_p)) 2975132718Skan return 1; 2976132718Skan 2977132718Skan /* REG+const has 5-7 bit offset for non-SP registers. */ 2978132718Skan else if ((thumb_index_register_rtx_p (XEXP (x, 0), strict_p) 2979132718Skan || XEXP (x, 0) == arg_pointer_rtx) 2980132718Skan && GET_CODE (XEXP (x, 1)) == CONST_INT 2981132718Skan && thumb_legitimate_offset_p (mode, INTVAL (XEXP (x, 1)))) 2982132718Skan return 1; 2983132718Skan 2984132718Skan /* REG+const has 10 bit offset for SP, but only SImode and 2985132718Skan larger is supported. */ 2986132718Skan /* ??? Should probably check for DI/DFmode overflow here 2987132718Skan just like GO_IF_LEGITIMATE_OFFSET does. */ 2988132718Skan else if (GET_CODE (XEXP (x, 0)) == REG 2989132718Skan && REGNO (XEXP (x, 0)) == STACK_POINTER_REGNUM 2990132718Skan && GET_MODE_SIZE (mode) >= 4 2991132718Skan && GET_CODE (XEXP (x, 1)) == CONST_INT 2992132718Skan && INTVAL (XEXP (x, 1)) >= 0 2993132718Skan && INTVAL (XEXP (x, 1)) + GET_MODE_SIZE (mode) <= 1024 2994132718Skan && (INTVAL (XEXP (x, 1)) & 3) == 0) 2995132718Skan return 1; 2996132718Skan 2997132718Skan else if (GET_CODE (XEXP (x, 0)) == REG 2998132718Skan && REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM 2999132718Skan && GET_MODE_SIZE (mode) >= 4 3000132718Skan && GET_CODE (XEXP (x, 1)) == CONST_INT 3001132718Skan && (INTVAL (XEXP (x, 1)) & 3) == 0) 3002132718Skan return 1; 3003132718Skan } 3004132718Skan 3005132718Skan else if (GET_MODE_CLASS (mode) != MODE_FLOAT 3006132718Skan && GET_MODE_SIZE (mode) == 4 3007132718Skan && GET_CODE (x) == SYMBOL_REF 3008132718Skan && CONSTANT_POOL_ADDRESS_P (x) 3009132718Skan && !(flag_pic 3010132718Skan && symbol_mentioned_p (get_pool_constant (x)))) 3011132718Skan return 1; 3012132718Skan 3013132718Skan return 0; 3014132718Skan} 3015132718Skan 3016132718Skan/* Return nonzero if VAL can be used as an offset in a Thumb-state address 3017132718Skan instruction of mode MODE. */ 3018132718Skanint 3019132718Skanthumb_legitimate_offset_p (enum machine_mode mode, HOST_WIDE_INT val) 3020132718Skan{ 3021132718Skan switch (GET_MODE_SIZE (mode)) 3022132718Skan { 3023132718Skan case 1: 3024132718Skan return val >= 0 && val < 32; 3025132718Skan 3026132718Skan case 2: 3027132718Skan return val >= 0 && val < 64 && (val & 1) == 0; 3028132718Skan 3029132718Skan default: 3030132718Skan return (val >= 0 3031132718Skan && (val + GET_MODE_SIZE (mode)) <= 128 3032132718Skan && (val & 3) == 0); 3033132718Skan } 3034132718Skan} 3035132718Skan 3036132718Skan/* Try machine-dependent ways of modifying an illegitimate address 3037132718Skan to be legitimate. If we find one, return the new, valid address. */ 3038132718Skanrtx 3039132718Skanarm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode) 3040132718Skan{ 3041132718Skan if (GET_CODE (x) == PLUS) 3042132718Skan { 3043132718Skan rtx xop0 = XEXP (x, 0); 3044132718Skan rtx xop1 = XEXP (x, 1); 3045132718Skan 3046132718Skan if (CONSTANT_P (xop0) && !symbol_mentioned_p (xop0)) 3047132718Skan xop0 = force_reg (SImode, xop0); 3048132718Skan 3049132718Skan if (CONSTANT_P (xop1) && !symbol_mentioned_p (xop1)) 3050132718Skan xop1 = force_reg (SImode, xop1); 3051132718Skan 3052132718Skan if (ARM_BASE_REGISTER_RTX_P (xop0) 3053132718Skan && GET_CODE (xop1) == CONST_INT) 3054132718Skan { 3055132718Skan HOST_WIDE_INT n, low_n; 3056132718Skan rtx base_reg, val; 3057132718Skan n = INTVAL (xop1); 3058132718Skan 3059132718Skan if (mode == DImode || (TARGET_SOFT_FLOAT && mode == DFmode)) 3060132718Skan { 3061132718Skan low_n = n & 0x0f; 3062132718Skan n &= ~0x0f; 3063132718Skan if (low_n > 4) 3064132718Skan { 3065132718Skan n += 16; 3066132718Skan low_n -= 16; 3067132718Skan } 3068132718Skan } 3069132718Skan else 3070132718Skan { 3071132718Skan low_n = ((mode) == TImode ? 0 3072132718Skan : n >= 0 ? (n & 0xfff) : -((-n) & 0xfff)); 3073132718Skan n -= low_n; 3074132718Skan } 3075132718Skan 3076132718Skan base_reg = gen_reg_rtx (SImode); 3077132718Skan val = force_operand (gen_rtx_PLUS (SImode, xop0, 3078132718Skan GEN_INT (n)), NULL_RTX); 3079132718Skan emit_move_insn (base_reg, val); 3080132718Skan x = (low_n == 0 ? base_reg 3081132718Skan : gen_rtx_PLUS (SImode, base_reg, GEN_INT (low_n))); 3082132718Skan } 3083132718Skan else if (xop0 != XEXP (x, 0) || xop1 != XEXP (x, 1)) 3084132718Skan x = gen_rtx_PLUS (SImode, xop0, xop1); 3085132718Skan } 3086132718Skan 3087132718Skan /* XXX We don't allow MINUS any more -- see comment in 3088132718Skan arm_legitimate_address_p (). */ 3089132718Skan else if (GET_CODE (x) == MINUS) 3090132718Skan { 3091132718Skan rtx xop0 = XEXP (x, 0); 3092132718Skan rtx xop1 = XEXP (x, 1); 3093132718Skan 3094132718Skan if (CONSTANT_P (xop0)) 3095132718Skan xop0 = force_reg (SImode, xop0); 3096132718Skan 3097132718Skan if (CONSTANT_P (xop1) && ! symbol_mentioned_p (xop1)) 3098132718Skan xop1 = force_reg (SImode, xop1); 3099132718Skan 3100132718Skan if (xop0 != XEXP (x, 0) || xop1 != XEXP (x, 1)) 3101132718Skan x = gen_rtx_MINUS (SImode, xop0, xop1); 3102132718Skan } 3103132718Skan 3104132718Skan if (flag_pic) 3105132718Skan { 3106132718Skan /* We need to find and carefully transform any SYMBOL and LABEL 3107132718Skan references; so go back to the original address expression. */ 3108132718Skan rtx new_x = legitimize_pic_address (orig_x, mode, NULL_RTX); 3109132718Skan 3110132718Skan if (new_x != orig_x) 3111132718Skan x = new_x; 3112132718Skan } 3113132718Skan 3114132718Skan return x; 3115132718Skan} 3116132718Skan 3117132718Skan 3118132718Skan 311990075Sobrien#define REG_OR_SUBREG_REG(X) \ 312090075Sobrien (GET_CODE (X) == REG \ 312190075Sobrien || (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG)) 312290075Sobrien 312390075Sobrien#define REG_OR_SUBREG_RTX(X) \ 312490075Sobrien (GET_CODE (X) == REG ? (X) : SUBREG_REG (X)) 312590075Sobrien 312690075Sobrien#ifndef COSTS_N_INSNS 312790075Sobrien#define COSTS_N_INSNS(N) ((N) * 4 - 2) 312890075Sobrien#endif 3129132718Skan/* Worker routine for arm_rtx_costs. */ 3130132718Skanstatic inline int 3131132718Skanarm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer) 313290075Sobrien{ 313390075Sobrien enum machine_mode mode = GET_MODE (x); 313490075Sobrien enum rtx_code subcode; 313590075Sobrien int extra_cost; 313690075Sobrien 313790075Sobrien if (TARGET_THUMB) 313890075Sobrien { 313990075Sobrien switch (code) 314090075Sobrien { 314190075Sobrien case ASHIFT: 314290075Sobrien case ASHIFTRT: 314390075Sobrien case LSHIFTRT: 314490075Sobrien case ROTATERT: 314590075Sobrien case PLUS: 314690075Sobrien case MINUS: 314790075Sobrien case COMPARE: 314890075Sobrien case NEG: 314990075Sobrien case NOT: 315090075Sobrien return COSTS_N_INSNS (1); 315190075Sobrien 315290075Sobrien case MULT: 315390075Sobrien if (GET_CODE (XEXP (x, 1)) == CONST_INT) 315490075Sobrien { 315590075Sobrien int cycles = 0; 315690075Sobrien unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1)); 315790075Sobrien 315890075Sobrien while (i) 315990075Sobrien { 316090075Sobrien i >>= 2; 316190075Sobrien cycles++; 316290075Sobrien } 316390075Sobrien return COSTS_N_INSNS (2) + cycles; 316490075Sobrien } 316590075Sobrien return COSTS_N_INSNS (1) + 16; 316690075Sobrien 316790075Sobrien case SET: 316890075Sobrien return (COSTS_N_INSNS (1) 316990075Sobrien + 4 * ((GET_CODE (SET_SRC (x)) == MEM) 317090075Sobrien + GET_CODE (SET_DEST (x)) == MEM)); 317190075Sobrien 317290075Sobrien case CONST_INT: 317390075Sobrien if (outer == SET) 317490075Sobrien { 317590075Sobrien if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256) 317690075Sobrien return 0; 317790075Sobrien if (thumb_shiftable_const (INTVAL (x))) 317890075Sobrien return COSTS_N_INSNS (2); 317990075Sobrien return COSTS_N_INSNS (3); 318090075Sobrien } 3181132718Skan else if ((outer == PLUS || outer == COMPARE) 318290075Sobrien && INTVAL (x) < 256 && INTVAL (x) > -256) 3183132718Skan return 0; 3184132718Skan else if (outer == AND 3185132718Skan && INTVAL (x) < 256 && INTVAL (x) >= -256) 3186132718Skan return COSTS_N_INSNS (1); 318790075Sobrien else if (outer == ASHIFT || outer == ASHIFTRT 318890075Sobrien || outer == LSHIFTRT) 318990075Sobrien return 0; 319090075Sobrien return COSTS_N_INSNS (2); 319190075Sobrien 319290075Sobrien case CONST: 319390075Sobrien case CONST_DOUBLE: 319490075Sobrien case LABEL_REF: 319590075Sobrien case SYMBOL_REF: 319690075Sobrien return COSTS_N_INSNS (3); 319790075Sobrien 319890075Sobrien case UDIV: 319990075Sobrien case UMOD: 320090075Sobrien case DIV: 320190075Sobrien case MOD: 320290075Sobrien return 100; 320390075Sobrien 320490075Sobrien case TRUNCATE: 320590075Sobrien return 99; 320690075Sobrien 320790075Sobrien case AND: 320890075Sobrien case XOR: 320990075Sobrien case IOR: 3210132718Skan /* XXX guess. */ 321190075Sobrien return 8; 321290075Sobrien 321390075Sobrien case ADDRESSOF: 321490075Sobrien case MEM: 321590075Sobrien /* XXX another guess. */ 321690075Sobrien /* Memory costs quite a lot for the first word, but subsequent words 321790075Sobrien load at the equivalent of a single insn each. */ 321890075Sobrien return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) 3219117395Skan + ((GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) 3220117395Skan ? 4 : 0)); 322190075Sobrien 322290075Sobrien case IF_THEN_ELSE: 3223132718Skan /* XXX a guess. */ 322490075Sobrien if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) 322590075Sobrien return 14; 322690075Sobrien return 2; 322790075Sobrien 322890075Sobrien case ZERO_EXTEND: 322990075Sobrien /* XXX still guessing. */ 323090075Sobrien switch (GET_MODE (XEXP (x, 0))) 323190075Sobrien { 323290075Sobrien case QImode: 323390075Sobrien return (1 + (mode == DImode ? 4 : 0) 323490075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 323590075Sobrien 323690075Sobrien case HImode: 323790075Sobrien return (4 + (mode == DImode ? 4 : 0) 323890075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 323990075Sobrien 324090075Sobrien case SImode: 324190075Sobrien return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 324290075Sobrien 324390075Sobrien default: 324490075Sobrien return 99; 324590075Sobrien } 324690075Sobrien 324790075Sobrien default: 324890075Sobrien return 99; 324990075Sobrien } 325090075Sobrien } 325190075Sobrien 325290075Sobrien switch (code) 325390075Sobrien { 325490075Sobrien case MEM: 325590075Sobrien /* Memory costs quite a lot for the first word, but subsequent words 325690075Sobrien load at the equivalent of a single insn each. */ 325790075Sobrien return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) 3258117395Skan + (GET_CODE (x) == SYMBOL_REF 3259117395Skan && CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); 326090075Sobrien 326190075Sobrien case DIV: 326290075Sobrien case MOD: 3263132718Skan case UDIV: 3264132718Skan case UMOD: 3265132718Skan return optimize_size ? COSTS_N_INSNS (2) : 100; 326690075Sobrien 326790075Sobrien case ROTATE: 326890075Sobrien if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG) 326990075Sobrien return 4; 327090075Sobrien /* Fall through */ 327190075Sobrien case ROTATERT: 327290075Sobrien if (mode != SImode) 327390075Sobrien return 8; 327490075Sobrien /* Fall through */ 327590075Sobrien case ASHIFT: case LSHIFTRT: case ASHIFTRT: 327690075Sobrien if (mode == DImode) 327790075Sobrien return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8) 327890075Sobrien + ((GET_CODE (XEXP (x, 0)) == REG 327990075Sobrien || (GET_CODE (XEXP (x, 0)) == SUBREG 328090075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) 328190075Sobrien ? 0 : 8)); 328290075Sobrien return (1 + ((GET_CODE (XEXP (x, 0)) == REG 328390075Sobrien || (GET_CODE (XEXP (x, 0)) == SUBREG 328490075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) 328590075Sobrien ? 0 : 4) 328690075Sobrien + ((GET_CODE (XEXP (x, 1)) == REG 328790075Sobrien || (GET_CODE (XEXP (x, 1)) == SUBREG 328890075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG) 328990075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT)) 329090075Sobrien ? 0 : 4)); 329190075Sobrien 329290075Sobrien case MINUS: 329390075Sobrien if (mode == DImode) 329490075Sobrien return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8) 329590075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 0)) 329690075Sobrien || (GET_CODE (XEXP (x, 0)) == CONST_INT 329790075Sobrien && const_ok_for_arm (INTVAL (XEXP (x, 0))))) 329890075Sobrien ? 0 : 8)); 329990075Sobrien 330090075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 330190075Sobrien return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 330290075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE 3303132718Skan && const_double_rtx_ok_for_fpa (XEXP (x, 1)))) 330490075Sobrien ? 0 : 8) 330590075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 0)) 330690075Sobrien || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE 3307132718Skan && const_double_rtx_ok_for_fpa (XEXP (x, 0)))) 330890075Sobrien ? 0 : 8)); 330990075Sobrien 331090075Sobrien if (((GET_CODE (XEXP (x, 0)) == CONST_INT 331190075Sobrien && const_ok_for_arm (INTVAL (XEXP (x, 0))) 331290075Sobrien && REG_OR_SUBREG_REG (XEXP (x, 1)))) 331390075Sobrien || (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT 331490075Sobrien || subcode == ASHIFTRT || subcode == LSHIFTRT 331590075Sobrien || subcode == ROTATE || subcode == ROTATERT 331690075Sobrien || (subcode == MULT 331790075Sobrien && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT 331890075Sobrien && ((INTVAL (XEXP (XEXP (x, 1), 1)) & 331990075Sobrien (INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0))) 332090075Sobrien && REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0)) 332190075Sobrien && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1)) 332290075Sobrien || GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT) 332390075Sobrien && REG_OR_SUBREG_REG (XEXP (x, 0)))) 332490075Sobrien return 1; 332590075Sobrien /* Fall through */ 332690075Sobrien 332790075Sobrien case PLUS: 332890075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 332990075Sobrien return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) 333090075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 333190075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE 3332132718Skan && const_double_rtx_ok_for_fpa (XEXP (x, 1)))) 333390075Sobrien ? 0 : 8)); 333490075Sobrien 333590075Sobrien /* Fall through */ 333690075Sobrien case AND: case XOR: case IOR: 333790075Sobrien extra_cost = 0; 333890075Sobrien 333990075Sobrien /* Normally the frame registers will be spilt into reg+const during 334090075Sobrien reload, so it is a bad idea to combine them with other instructions, 334190075Sobrien since then they might not be moved outside of loops. As a compromise 334290075Sobrien we allow integration with ops that have a constant as their second 334390075Sobrien operand. */ 334490075Sobrien if ((REG_OR_SUBREG_REG (XEXP (x, 0)) 334590075Sobrien && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))) 334690075Sobrien && GET_CODE (XEXP (x, 1)) != CONST_INT) 334790075Sobrien || (REG_OR_SUBREG_REG (XEXP (x, 0)) 334890075Sobrien && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))))) 334990075Sobrien extra_cost = 4; 335090075Sobrien 335190075Sobrien if (mode == DImode) 335290075Sobrien return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) 335390075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 335490075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT 335590075Sobrien && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) 335690075Sobrien ? 0 : 8)); 335790075Sobrien 335890075Sobrien if (REG_OR_SUBREG_REG (XEXP (x, 0))) 335990075Sobrien return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost) 336090075Sobrien + ((REG_OR_SUBREG_REG (XEXP (x, 1)) 336190075Sobrien || (GET_CODE (XEXP (x, 1)) == CONST_INT 336290075Sobrien && const_ok_for_op (INTVAL (XEXP (x, 1)), code))) 336390075Sobrien ? 0 : 4)); 336490075Sobrien 336590075Sobrien else if (REG_OR_SUBREG_REG (XEXP (x, 1))) 336690075Sobrien return (1 + extra_cost 336790075Sobrien + ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT 336890075Sobrien || subcode == LSHIFTRT || subcode == ASHIFTRT 336990075Sobrien || subcode == ROTATE || subcode == ROTATERT 337090075Sobrien || (subcode == MULT 337190075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT 337290075Sobrien && ((INTVAL (XEXP (XEXP (x, 0), 1)) & 337390075Sobrien (INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0))) 337490075Sobrien && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0))) 337590075Sobrien && ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1))) 337690075Sobrien || GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)) 337790075Sobrien ? 0 : 4)); 337890075Sobrien 337990075Sobrien return 8; 338090075Sobrien 338190075Sobrien case MULT: 338290075Sobrien /* There is no point basing this on the tuning, since it is always the 338390075Sobrien fast variant if it exists at all. */ 338490075Sobrien if (arm_fast_multiply && mode == DImode 338590075Sobrien && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1))) 338690075Sobrien && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND 338790075Sobrien || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)) 338890075Sobrien return 8; 338990075Sobrien 339090075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT 339190075Sobrien || mode == DImode) 339290075Sobrien return 30; 339390075Sobrien 339490075Sobrien if (GET_CODE (XEXP (x, 1)) == CONST_INT) 339590075Sobrien { 339690075Sobrien unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1)) 339790075Sobrien & (unsigned HOST_WIDE_INT) 0xffffffff); 3398132718Skan int cost, const_ok = const_ok_for_arm (i); 3399132718Skan int j, booth_unit_size; 3400132718Skan 3401132718Skan if (arm_tune_xscale) 3402132718Skan { 3403132718Skan unsigned HOST_WIDE_INT masked_const; 3404132718Skan 3405132718Skan /* The cost will be related to two insns. 3406132718Skan First a load of the constant (MOV or LDR), then a multiply. */ 3407132718Skan cost = 2; 3408132718Skan if (! const_ok) 3409132718Skan cost += 1; /* LDR is probably more expensive because 3410132718Skan of longer result latency. */ 3411132718Skan masked_const = i & 0xffff8000; 3412132718Skan if (masked_const != 0 && masked_const != 0xffff8000) 3413132718Skan { 3414132718Skan masked_const = i & 0xf8000000; 3415132718Skan if (masked_const == 0 || masked_const == 0xf8000000) 3416132718Skan cost += 1; 3417132718Skan else 3418132718Skan cost += 2; 3419132718Skan } 3420132718Skan return cost; 3421132718Skan } 342290075Sobrien 342390075Sobrien /* Tune as appropriate. */ 3424132718Skan cost = const_ok ? 4 : 8; 3425132718Skan booth_unit_size = ((tune_flags & FL_FAST_MULT) ? 8 : 2); 342690075Sobrien for (j = 0; i && j < 32; j += booth_unit_size) 342790075Sobrien { 342890075Sobrien i >>= booth_unit_size; 3429132718Skan cost += 2; 343090075Sobrien } 343190075Sobrien 3432132718Skan return cost; 343390075Sobrien } 343490075Sobrien 343590075Sobrien return (((tune_flags & FL_FAST_MULT) ? 8 : 30) 343690075Sobrien + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4) 343790075Sobrien + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4)); 343890075Sobrien 343990075Sobrien case TRUNCATE: 344090075Sobrien if (arm_fast_multiply && mode == SImode 344190075Sobrien && GET_CODE (XEXP (x, 0)) == LSHIFTRT 344290075Sobrien && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT 344390075Sobrien && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) 344490075Sobrien == GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1))) 344590075Sobrien && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND 344690075Sobrien || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND)) 344790075Sobrien return 8; 344890075Sobrien return 99; 344990075Sobrien 345090075Sobrien case NEG: 345190075Sobrien if (GET_MODE_CLASS (mode) == MODE_FLOAT) 345290075Sobrien return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6); 345390075Sobrien /* Fall through */ 345490075Sobrien case NOT: 345590075Sobrien if (mode == DImode) 345690075Sobrien return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); 345790075Sobrien 345890075Sobrien return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); 345990075Sobrien 346090075Sobrien case IF_THEN_ELSE: 346190075Sobrien if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) 346290075Sobrien return 14; 346390075Sobrien return 2; 346490075Sobrien 346590075Sobrien case COMPARE: 346690075Sobrien return 1; 346790075Sobrien 346890075Sobrien case ABS: 346990075Sobrien return 4 + (mode == DImode ? 4 : 0); 347090075Sobrien 347190075Sobrien case SIGN_EXTEND: 347290075Sobrien if (GET_MODE (XEXP (x, 0)) == QImode) 347390075Sobrien return (4 + (mode == DImode ? 4 : 0) 347490075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 347590075Sobrien /* Fall through */ 347690075Sobrien case ZERO_EXTEND: 347790075Sobrien switch (GET_MODE (XEXP (x, 0))) 347890075Sobrien { 347990075Sobrien case QImode: 348090075Sobrien return (1 + (mode == DImode ? 4 : 0) 348190075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 348290075Sobrien 348390075Sobrien case HImode: 348490075Sobrien return (4 + (mode == DImode ? 4 : 0) 348590075Sobrien + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 348690075Sobrien 348790075Sobrien case SImode: 348890075Sobrien return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); 348990075Sobrien 3490132718Skan case V8QImode: 3491132718Skan case V4HImode: 3492132718Skan case V2SImode: 3493132718Skan case V4QImode: 3494132718Skan case V2HImode: 3495132718Skan return 1; 3496132718Skan 349790075Sobrien default: 349890075Sobrien break; 349990075Sobrien } 350090075Sobrien abort (); 350190075Sobrien 350290075Sobrien case CONST_INT: 350390075Sobrien if (const_ok_for_arm (INTVAL (x))) 350490075Sobrien return outer == SET ? 2 : -1; 350590075Sobrien else if (outer == AND 350690075Sobrien && const_ok_for_arm (~INTVAL (x))) 350790075Sobrien return -1; 350890075Sobrien else if ((outer == COMPARE 350990075Sobrien || outer == PLUS || outer == MINUS) 351090075Sobrien && const_ok_for_arm (-INTVAL (x))) 351190075Sobrien return -1; 351290075Sobrien else 351390075Sobrien return 5; 351490075Sobrien 351590075Sobrien case CONST: 351690075Sobrien case LABEL_REF: 351790075Sobrien case SYMBOL_REF: 351890075Sobrien return 6; 351990075Sobrien 352090075Sobrien case CONST_DOUBLE: 3521132718Skan if (const_double_rtx_ok_for_fpa (x)) 352290075Sobrien return outer == SET ? 2 : -1; 352390075Sobrien else if ((outer == COMPARE || outer == PLUS) 3524132718Skan && neg_const_double_rtx_ok_for_fpa (x)) 352590075Sobrien return -1; 352690075Sobrien return 7; 352790075Sobrien 352890075Sobrien default: 352990075Sobrien return 99; 353090075Sobrien } 353190075Sobrien} 353290075Sobrien 3533132718Skanstatic bool 3534132718Skanarm_rtx_costs (rtx x, int code, int outer_code, int *total) 3535132718Skan{ 3536132718Skan *total = arm_rtx_costs_1 (x, code, outer_code); 3537132718Skan return true; 3538132718Skan} 3539132718Skan 3540132718Skan/* All address computations that can be done are free, but rtx cost returns 3541132718Skan the same for practically all of them. So we weight the different types 3542132718Skan of address here in the order (most pref first): 3543132718Skan PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL. */ 3544132718Skanstatic inline int 3545132718Skanarm_arm_address_cost (rtx x) 3546132718Skan{ 3547132718Skan enum rtx_code c = GET_CODE (x); 3548132718Skan 3549132718Skan if (c == PRE_INC || c == PRE_DEC || c == POST_INC || c == POST_DEC) 3550132718Skan return 0; 3551132718Skan if (c == MEM || c == LABEL_REF || c == SYMBOL_REF) 3552132718Skan return 10; 3553132718Skan 3554132718Skan if (c == PLUS || c == MINUS) 3555132718Skan { 3556132718Skan char cl0 = GET_RTX_CLASS (GET_CODE (XEXP (x, 0))); 3557132718Skan char cl1 = GET_RTX_CLASS (GET_CODE (XEXP (x, 1))); 3558132718Skan 3559132718Skan if (GET_CODE (XEXP (x, 0)) == CONST_INT) 3560132718Skan return 2; 3561132718Skan 3562132718Skan if (cl0 == '2' || cl0 == 'c' || cl1 == '2' || cl1 == 'c') 3563132718Skan return 3; 3564132718Skan 3565132718Skan return 4; 3566132718Skan } 3567132718Skan 3568132718Skan return 6; 3569132718Skan} 3570132718Skan 3571132718Skanstatic inline int 3572132718Skanarm_thumb_address_cost (rtx x) 3573132718Skan{ 3574132718Skan enum rtx_code c = GET_CODE (x); 3575132718Skan 3576132718Skan if (c == REG) 3577132718Skan return 1; 3578132718Skan if (c == PLUS 3579132718Skan && GET_CODE (XEXP (x, 0)) == REG 3580132718Skan && GET_CODE (XEXP (x, 1)) == CONST_INT) 3581132718Skan return 1; 3582132718Skan 3583132718Skan return 2; 3584132718Skan} 3585132718Skan 358690075Sobrienstatic int 3587132718Skanarm_address_cost (rtx x) 358890075Sobrien{ 3589132718Skan return TARGET_ARM ? arm_arm_address_cost (x) : arm_thumb_address_cost (x); 3590132718Skan} 3591132718Skan 3592132718Skanstatic int 3593132718Skanarm_use_dfa_pipeline_interface (void) 3594132718Skan{ 3595132718Skan return true; 3596132718Skan} 3597132718Skan 3598132718Skanstatic int 3599132718Skanarm_adjust_cost (rtx insn, rtx link, rtx dep, int cost) 3600132718Skan{ 360190075Sobrien rtx i_pat, d_pat; 360290075Sobrien 360390075Sobrien /* Some true dependencies can have a higher cost depending 360490075Sobrien on precisely how certain input operands are used. */ 3605132718Skan if (arm_tune_xscale 360690075Sobrien && REG_NOTE_KIND (link) == 0 3607132718Skan && recog_memoized (insn) >= 0 3608132718Skan && recog_memoized (dep) >= 0) 360990075Sobrien { 361090075Sobrien int shift_opnum = get_attr_shift (insn); 361190075Sobrien enum attr_type attr_type = get_attr_type (dep); 361290075Sobrien 361390075Sobrien /* If nonzero, SHIFT_OPNUM contains the operand number of a shifted 361490075Sobrien operand for INSN. If we have a shifted input operand and the 361590075Sobrien instruction we depend on is another ALU instruction, then we may 361690075Sobrien have to account for an additional stall. */ 361790075Sobrien if (shift_opnum != 0 && attr_type == TYPE_NORMAL) 361890075Sobrien { 361990075Sobrien rtx shifted_operand; 362090075Sobrien int opno; 362190075Sobrien 362290075Sobrien /* Get the shifted operand. */ 362390075Sobrien extract_insn (insn); 362490075Sobrien shifted_operand = recog_data.operand[shift_opnum]; 362590075Sobrien 362690075Sobrien /* Iterate over all the operands in DEP. If we write an operand 362790075Sobrien that overlaps with SHIFTED_OPERAND, then we have increase the 362890075Sobrien cost of this dependency. */ 362990075Sobrien extract_insn (dep); 363090075Sobrien preprocess_constraints (); 363190075Sobrien for (opno = 0; opno < recog_data.n_operands; opno++) 363290075Sobrien { 363390075Sobrien /* We can ignore strict inputs. */ 363490075Sobrien if (recog_data.operand_type[opno] == OP_IN) 363590075Sobrien continue; 363690075Sobrien 363790075Sobrien if (reg_overlap_mentioned_p (recog_data.operand[opno], 363890075Sobrien shifted_operand)) 363990075Sobrien return 2; 364090075Sobrien } 364190075Sobrien } 364290075Sobrien } 364390075Sobrien 364490075Sobrien /* XXX This is not strictly true for the FPA. */ 364590075Sobrien if (REG_NOTE_KIND (link) == REG_DEP_ANTI 364690075Sobrien || REG_NOTE_KIND (link) == REG_DEP_OUTPUT) 364790075Sobrien return 0; 364890075Sobrien 364990075Sobrien /* Call insns don't incur a stall, even if they follow a load. */ 365090075Sobrien if (REG_NOTE_KIND (link) == 0 365190075Sobrien && GET_CODE (insn) == CALL_INSN) 365290075Sobrien return 1; 365390075Sobrien 365490075Sobrien if ((i_pat = single_set (insn)) != NULL 365590075Sobrien && GET_CODE (SET_SRC (i_pat)) == MEM 365690075Sobrien && (d_pat = single_set (dep)) != NULL 365790075Sobrien && GET_CODE (SET_DEST (d_pat)) == MEM) 365890075Sobrien { 3659117395Skan rtx src_mem = XEXP (SET_SRC (i_pat), 0); 366090075Sobrien /* This is a load after a store, there is no conflict if the load reads 366190075Sobrien from a cached area. Assume that loads from the stack, and from the 366290075Sobrien constant pool are cached, and that others will miss. This is a 366390075Sobrien hack. */ 366490075Sobrien 3665117395Skan if ((GET_CODE (src_mem) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (src_mem)) 3666117395Skan || reg_mentioned_p (stack_pointer_rtx, src_mem) 3667117395Skan || reg_mentioned_p (frame_pointer_rtx, src_mem) 3668117395Skan || reg_mentioned_p (hard_frame_pointer_rtx, src_mem)) 366990075Sobrien return 1; 367090075Sobrien } 367190075Sobrien 367290075Sobrien return cost; 367390075Sobrien} 367490075Sobrien 367590075Sobrienstatic int fpa_consts_inited = 0; 367690075Sobrien 367790075Sobrienstatic const char * const strings_fpa[8] = 367890075Sobrien{ 367990075Sobrien "0", "1", "2", "3", 368090075Sobrien "4", "5", "0.5", "10" 368190075Sobrien}; 368290075Sobrien 368390075Sobrienstatic REAL_VALUE_TYPE values_fpa[8]; 368490075Sobrien 368590075Sobrienstatic void 3686132718Skaninit_fpa_table (void) 368790075Sobrien{ 368890075Sobrien int i; 368990075Sobrien REAL_VALUE_TYPE r; 369090075Sobrien 369190075Sobrien for (i = 0; i < 8; i++) 369290075Sobrien { 369390075Sobrien r = REAL_VALUE_ATOF (strings_fpa[i], DFmode); 369490075Sobrien values_fpa[i] = r; 369590075Sobrien } 369690075Sobrien 369790075Sobrien fpa_consts_inited = 1; 369890075Sobrien} 369990075Sobrien 3700132718Skan/* Return TRUE if rtx X is a valid immediate FPA constant. */ 370190075Sobrienint 3702132718Skanconst_double_rtx_ok_for_fpa (rtx x) 370390075Sobrien{ 370490075Sobrien REAL_VALUE_TYPE r; 370590075Sobrien int i; 370690075Sobrien 370790075Sobrien if (!fpa_consts_inited) 370890075Sobrien init_fpa_table (); 370990075Sobrien 371090075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 371190075Sobrien if (REAL_VALUE_MINUS_ZERO (r)) 371290075Sobrien return 0; 371390075Sobrien 371490075Sobrien for (i = 0; i < 8; i++) 371590075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 371690075Sobrien return 1; 371790075Sobrien 371890075Sobrien return 0; 371990075Sobrien} 372090075Sobrien 3721132718Skan/* Return TRUE if rtx X is a valid immediate FPA constant. */ 372290075Sobrienint 3723132718Skanneg_const_double_rtx_ok_for_fpa (rtx x) 372490075Sobrien{ 372590075Sobrien REAL_VALUE_TYPE r; 372690075Sobrien int i; 372790075Sobrien 372890075Sobrien if (!fpa_consts_inited) 372990075Sobrien init_fpa_table (); 373090075Sobrien 373190075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 373290075Sobrien r = REAL_VALUE_NEGATE (r); 373390075Sobrien if (REAL_VALUE_MINUS_ZERO (r)) 373490075Sobrien return 0; 373590075Sobrien 373690075Sobrien for (i = 0; i < 8; i++) 373790075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 373890075Sobrien return 1; 373990075Sobrien 374090075Sobrien return 0; 374190075Sobrien} 374290075Sobrien 374390075Sobrien/* Predicates for `match_operand' and `match_operator'. */ 374490075Sobrien 374590075Sobrien/* s_register_operand is the same as register_operand, but it doesn't accept 374690075Sobrien (SUBREG (MEM)...). 374790075Sobrien 374890075Sobrien This function exists because at the time it was put in it led to better 374990075Sobrien code. SUBREG(MEM) always needs a reload in the places where 375090075Sobrien s_register_operand is used, and this seemed to lead to excessive 375190075Sobrien reloading. */ 375290075Sobrienint 3753132718Skans_register_operand (rtx op, enum machine_mode mode) 375490075Sobrien{ 375590075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 375690075Sobrien return 0; 375790075Sobrien 375890075Sobrien if (GET_CODE (op) == SUBREG) 375990075Sobrien op = SUBREG_REG (op); 376090075Sobrien 376190075Sobrien /* We don't consider registers whose class is NO_REGS 376290075Sobrien to be a register operand. */ 376390075Sobrien /* XXX might have to check for lo regs only for thumb ??? */ 376490075Sobrien return (GET_CODE (op) == REG 376590075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 376690075Sobrien || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); 376790075Sobrien} 376890075Sobrien 376990075Sobrien/* A hard register operand (even before reload. */ 377090075Sobrienint 3771132718Skanarm_hard_register_operand (rtx op, enum machine_mode mode) 377290075Sobrien{ 377390075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 377490075Sobrien return 0; 377590075Sobrien 377690075Sobrien return (GET_CODE (op) == REG 377790075Sobrien && REGNO (op) < FIRST_PSEUDO_REGISTER); 377890075Sobrien} 377990075Sobrien 378090075Sobrien/* Only accept reg, subreg(reg), const_int. */ 378190075Sobrienint 3782132718Skanreg_or_int_operand (rtx op, enum machine_mode mode) 378390075Sobrien{ 378490075Sobrien if (GET_CODE (op) == CONST_INT) 378590075Sobrien return 1; 378690075Sobrien 378790075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 378890075Sobrien return 0; 378990075Sobrien 379090075Sobrien if (GET_CODE (op) == SUBREG) 379190075Sobrien op = SUBREG_REG (op); 379290075Sobrien 379390075Sobrien /* We don't consider registers whose class is NO_REGS 379490075Sobrien to be a register operand. */ 379590075Sobrien return (GET_CODE (op) == REG 379690075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 379790075Sobrien || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); 379890075Sobrien} 379990075Sobrien 380090075Sobrien/* Return 1 if OP is an item in memory, given that we are in reload. */ 380190075Sobrienint 3802132718Skanarm_reload_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 380390075Sobrien{ 380490075Sobrien int regno = true_regnum (op); 380590075Sobrien 380690075Sobrien return (!CONSTANT_P (op) 380790075Sobrien && (regno == -1 380890075Sobrien || (GET_CODE (op) == REG 380990075Sobrien && REGNO (op) >= FIRST_PSEUDO_REGISTER))); 381090075Sobrien} 381190075Sobrien 381290075Sobrien/* Return 1 if OP is a valid memory address, but not valid for a signed byte 381390075Sobrien memory access (architecture V4). 381490075Sobrien MODE is QImode if called when computing constraints, or VOIDmode when 381590075Sobrien emitting patterns. In this latter case we cannot use memory_operand() 3816132718Skan because it will fail on badly formed MEMs, which is precisely what we are 381790075Sobrien trying to catch. */ 381890075Sobrienint 3819132718Skanbad_signed_byte_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 382090075Sobrien{ 382190075Sobrien if (GET_CODE (op) != MEM) 382290075Sobrien return 0; 382390075Sobrien 382490075Sobrien op = XEXP (op, 0); 382590075Sobrien 382690075Sobrien /* A sum of anything more complex than reg + reg or reg + const is bad. */ 382790075Sobrien if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS) 382890075Sobrien && (!s_register_operand (XEXP (op, 0), VOIDmode) 382990075Sobrien || (!s_register_operand (XEXP (op, 1), VOIDmode) 383090075Sobrien && GET_CODE (XEXP (op, 1)) != CONST_INT))) 383190075Sobrien return 1; 383290075Sobrien 383390075Sobrien /* Big constants are also bad. */ 383490075Sobrien if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT 383590075Sobrien && (INTVAL (XEXP (op, 1)) > 0xff 383690075Sobrien || -INTVAL (XEXP (op, 1)) > 0xff)) 383790075Sobrien return 1; 383890075Sobrien 383990075Sobrien /* Everything else is good, or can will automatically be made so. */ 384090075Sobrien return 0; 384190075Sobrien} 384290075Sobrien 384390075Sobrien/* Return TRUE for valid operands for the rhs of an ARM instruction. */ 384490075Sobrienint 3845132718Skanarm_rhs_operand (rtx op, enum machine_mode mode) 384690075Sobrien{ 384790075Sobrien return (s_register_operand (op, mode) 384890075Sobrien || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); 384990075Sobrien} 385090075Sobrien 385190075Sobrien/* Return TRUE for valid operands for the 385290075Sobrien rhs of an ARM instruction, or a load. */ 385390075Sobrienint 3854132718Skanarm_rhsm_operand (rtx op, enum machine_mode mode) 385590075Sobrien{ 385690075Sobrien return (s_register_operand (op, mode) 385790075Sobrien || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))) 385890075Sobrien || memory_operand (op, mode)); 385990075Sobrien} 386090075Sobrien 386190075Sobrien/* Return TRUE for valid operands for the rhs of an ARM instruction, or if a 386290075Sobrien constant that is valid when negated. */ 386390075Sobrienint 3864132718Skanarm_add_operand (rtx op, enum machine_mode mode) 386590075Sobrien{ 386690075Sobrien if (TARGET_THUMB) 386790075Sobrien return thumb_cmp_operand (op, mode); 386890075Sobrien 386990075Sobrien return (s_register_operand (op, mode) 387090075Sobrien || (GET_CODE (op) == CONST_INT 387190075Sobrien && (const_ok_for_arm (INTVAL (op)) 387290075Sobrien || const_ok_for_arm (-INTVAL (op))))); 387390075Sobrien} 387490075Sobrien 3875132718Skan/* Return TRUE for valid ARM constants (or when valid if negated). */ 387690075Sobrienint 3877132718Skanarm_addimm_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 387890075Sobrien{ 3879132718Skan return (GET_CODE (op) == CONST_INT 3880132718Skan && (const_ok_for_arm (INTVAL (op)) 3881132718Skan || const_ok_for_arm (-INTVAL (op)))); 3882132718Skan} 3883132718Skan 3884132718Skanint 3885132718Skanarm_not_operand (rtx op, enum machine_mode mode) 3886132718Skan{ 388790075Sobrien return (s_register_operand (op, mode) 388890075Sobrien || (GET_CODE (op) == CONST_INT 388990075Sobrien && (const_ok_for_arm (INTVAL (op)) 389090075Sobrien || const_ok_for_arm (~INTVAL (op))))); 389190075Sobrien} 389290075Sobrien 389390075Sobrien/* Return TRUE if the operand is a memory reference which contains an 389490075Sobrien offsettable address. */ 389590075Sobrienint 3896132718Skanoffsettable_memory_operand (rtx op, enum machine_mode mode) 389790075Sobrien{ 389890075Sobrien if (mode == VOIDmode) 389990075Sobrien mode = GET_MODE (op); 390090075Sobrien 390190075Sobrien return (mode == GET_MODE (op) 390290075Sobrien && GET_CODE (op) == MEM 390390075Sobrien && offsettable_address_p (reload_completed | reload_in_progress, 390490075Sobrien mode, XEXP (op, 0))); 390590075Sobrien} 390690075Sobrien 390790075Sobrien/* Return TRUE if the operand is a memory reference which is, or can be 390890075Sobrien made word aligned by adjusting the offset. */ 390990075Sobrienint 3910132718Skanalignable_memory_operand (rtx op, enum machine_mode mode) 391190075Sobrien{ 391290075Sobrien rtx reg; 391390075Sobrien 391490075Sobrien if (mode == VOIDmode) 391590075Sobrien mode = GET_MODE (op); 391690075Sobrien 391790075Sobrien if (mode != GET_MODE (op) || GET_CODE (op) != MEM) 391890075Sobrien return 0; 391990075Sobrien 392090075Sobrien op = XEXP (op, 0); 392190075Sobrien 392290075Sobrien return ((GET_CODE (reg = op) == REG 392390075Sobrien || (GET_CODE (op) == SUBREG 392490075Sobrien && GET_CODE (reg = SUBREG_REG (op)) == REG) 392590075Sobrien || (GET_CODE (op) == PLUS 392690075Sobrien && GET_CODE (XEXP (op, 1)) == CONST_INT 392790075Sobrien && (GET_CODE (reg = XEXP (op, 0)) == REG 392890075Sobrien || (GET_CODE (XEXP (op, 0)) == SUBREG 392990075Sobrien && GET_CODE (reg = SUBREG_REG (XEXP (op, 0))) == REG)))) 393090075Sobrien && REGNO_POINTER_ALIGN (REGNO (reg)) >= 32); 393190075Sobrien} 393290075Sobrien 393390075Sobrien/* Similar to s_register_operand, but does not allow hard integer 393490075Sobrien registers. */ 393590075Sobrienint 3936132718Skanf_register_operand (rtx op, enum machine_mode mode) 393790075Sobrien{ 393890075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 393990075Sobrien return 0; 394090075Sobrien 394190075Sobrien if (GET_CODE (op) == SUBREG) 394290075Sobrien op = SUBREG_REG (op); 394390075Sobrien 394490075Sobrien /* We don't consider registers whose class is NO_REGS 394590075Sobrien to be a register operand. */ 394690075Sobrien return (GET_CODE (op) == REG 394790075Sobrien && (REGNO (op) >= FIRST_PSEUDO_REGISTER 3948132718Skan || REGNO_REG_CLASS (REGNO (op)) == FPA_REGS)); 394990075Sobrien} 395090075Sobrien 3951132718Skan/* Return TRUE for valid operands for the rhs of an FPA instruction. */ 395290075Sobrienint 3953132718Skanfpa_rhs_operand (rtx op, enum machine_mode mode) 395490075Sobrien{ 395590075Sobrien if (s_register_operand (op, mode)) 395690075Sobrien return TRUE; 395790075Sobrien 395890075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 395990075Sobrien return FALSE; 396090075Sobrien 396190075Sobrien if (GET_CODE (op) == CONST_DOUBLE) 3962132718Skan return const_double_rtx_ok_for_fpa (op); 396390075Sobrien 396490075Sobrien return FALSE; 396590075Sobrien} 396690075Sobrien 396790075Sobrienint 3968132718Skanfpa_add_operand (rtx op, enum machine_mode mode) 396990075Sobrien{ 397090075Sobrien if (s_register_operand (op, mode)) 397190075Sobrien return TRUE; 397290075Sobrien 397390075Sobrien if (GET_MODE (op) != mode && mode != VOIDmode) 397490075Sobrien return FALSE; 397590075Sobrien 397690075Sobrien if (GET_CODE (op) == CONST_DOUBLE) 3977132718Skan return (const_double_rtx_ok_for_fpa (op) 3978132718Skan || neg_const_double_rtx_ok_for_fpa (op)); 397990075Sobrien 398090075Sobrien return FALSE; 398190075Sobrien} 398290075Sobrien 3983132718Skan/* Return nonzero if OP is a valid Cirrus memory address pattern. */ 3984132718Skanint 3985132718Skancirrus_memory_offset (rtx op) 3986132718Skan{ 3987132718Skan /* Reject eliminable registers. */ 3988132718Skan if (! (reload_in_progress || reload_completed) 3989132718Skan && ( reg_mentioned_p (frame_pointer_rtx, op) 3990132718Skan || reg_mentioned_p (arg_pointer_rtx, op) 3991132718Skan || reg_mentioned_p (virtual_incoming_args_rtx, op) 3992132718Skan || reg_mentioned_p (virtual_outgoing_args_rtx, op) 3993132718Skan || reg_mentioned_p (virtual_stack_dynamic_rtx, op) 3994132718Skan || reg_mentioned_p (virtual_stack_vars_rtx, op))) 3995132718Skan return 0; 399690075Sobrien 3997132718Skan if (GET_CODE (op) == MEM) 3998132718Skan { 3999132718Skan rtx ind; 4000132718Skan 4001132718Skan ind = XEXP (op, 0); 4002132718Skan 4003132718Skan /* Match: (mem (reg)). */ 4004132718Skan if (GET_CODE (ind) == REG) 4005132718Skan return 1; 4006132718Skan 4007132718Skan /* Match: 4008132718Skan (mem (plus (reg) 4009132718Skan (const))). */ 4010132718Skan if (GET_CODE (ind) == PLUS 4011132718Skan && GET_CODE (XEXP (ind, 0)) == REG 4012132718Skan && REG_MODE_OK_FOR_BASE_P (XEXP (ind, 0), VOIDmode) 4013132718Skan && GET_CODE (XEXP (ind, 1)) == CONST_INT) 4014132718Skan return 1; 4015132718Skan } 4016132718Skan 4017132718Skan return 0; 4018132718Skan} 4019132718Skan 4020132718Skan/* Return nonzero if OP is a Cirrus or general register. */ 402190075Sobrienint 4022132718Skancirrus_register_operand (rtx op, enum machine_mode mode) 402390075Sobrien{ 4024132718Skan if (GET_MODE (op) != mode && mode != VOIDmode) 4025132718Skan return FALSE; 4026132718Skan 4027132718Skan if (GET_CODE (op) == SUBREG) 4028132718Skan op = SUBREG_REG (op); 4029132718Skan 4030132718Skan return (GET_CODE (op) == REG 4031132718Skan && (REGNO_REG_CLASS (REGNO (op)) == CIRRUS_REGS 4032132718Skan || REGNO_REG_CLASS (REGNO (op)) == GENERAL_REGS)); 4033132718Skan} 4034132718Skan 4035132718Skan/* Return nonzero if OP is a cirrus FP register. */ 4036132718Skanint 4037132718Skancirrus_fp_register (rtx op, enum machine_mode mode) 4038132718Skan{ 4039132718Skan if (GET_MODE (op) != mode && mode != VOIDmode) 4040132718Skan return FALSE; 4041132718Skan 4042132718Skan if (GET_CODE (op) == SUBREG) 4043132718Skan op = SUBREG_REG (op); 4044132718Skan 4045132718Skan return (GET_CODE (op) == REG 4046132718Skan && (REGNO (op) >= FIRST_PSEUDO_REGISTER 4047132718Skan || REGNO_REG_CLASS (REGNO (op)) == CIRRUS_REGS)); 4048132718Skan} 4049132718Skan 4050132718Skan/* Return nonzero if OP is a 6bit constant (0..63). */ 4051132718Skanint 4052132718Skancirrus_shift_const (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 4053132718Skan{ 4054132718Skan return (GET_CODE (op) == CONST_INT 4055132718Skan && INTVAL (op) >= 0 4056132718Skan && INTVAL (op) < 64); 4057132718Skan} 4058132718Skan 4059146895Skan/* Return true if X is a register that will be eliminated later on. */ 4060146895Skanint 4061146895Skanarm_eliminable_register (rtx x) 4062146895Skan{ 4063146895Skan return REG_P (x) && (REGNO (x) == FRAME_POINTER_REGNUM 4064146895Skan || REGNO (x) == ARG_POINTER_REGNUM 4065146895Skan || (REGNO (x) >= FIRST_VIRTUAL_REGISTER 4066146895Skan && REGNO (x) <= LAST_VIRTUAL_REGISTER)); 4067146895Skan} 4068146895Skan 4069132718Skan/* Returns TRUE if INSN is an "LDR REG, ADDR" instruction. 4070132718Skan Use by the Cirrus Maverick code which has to workaround 4071132718Skan a hardware bug triggered by such instructions. */ 4072132718Skanstatic bool 4073132718Skanarm_memory_load_p (rtx insn) 4074132718Skan{ 4075132718Skan rtx body, lhs, rhs;; 4076132718Skan 4077132718Skan if (insn == NULL_RTX || GET_CODE (insn) != INSN) 4078132718Skan return false; 4079132718Skan 4080132718Skan body = PATTERN (insn); 4081132718Skan 4082132718Skan if (GET_CODE (body) != SET) 4083132718Skan return false; 4084132718Skan 4085132718Skan lhs = XEXP (body, 0); 4086132718Skan rhs = XEXP (body, 1); 4087132718Skan 4088132718Skan lhs = REG_OR_SUBREG_RTX (lhs); 4089132718Skan 4090132718Skan /* If the destination is not a general purpose 4091132718Skan register we do not have to worry. */ 4092132718Skan if (GET_CODE (lhs) != REG 4093132718Skan || REGNO_REG_CLASS (REGNO (lhs)) != GENERAL_REGS) 4094132718Skan return false; 4095132718Skan 4096132718Skan /* As well as loads from memory we also have to react 4097132718Skan to loads of invalid constants which will be turned 4098132718Skan into loads from the minipool. */ 4099132718Skan return (GET_CODE (rhs) == MEM 4100132718Skan || GET_CODE (rhs) == SYMBOL_REF 4101132718Skan || note_invalid_constants (insn, -1, false)); 4102132718Skan} 4103132718Skan 4104132718Skan/* Return TRUE if INSN is a Cirrus instruction. */ 4105132718Skanstatic bool 4106132718Skanarm_cirrus_insn_p (rtx insn) 4107132718Skan{ 4108132718Skan enum attr_cirrus attr; 4109132718Skan 4110132718Skan /* get_attr aborts on USE and CLOBBER. */ 4111132718Skan if (!insn 4112132718Skan || GET_CODE (insn) != INSN 4113132718Skan || GET_CODE (PATTERN (insn)) == USE 4114132718Skan || GET_CODE (PATTERN (insn)) == CLOBBER) 4115132718Skan return 0; 4116132718Skan 4117132718Skan attr = get_attr_cirrus (insn); 4118132718Skan 4119132718Skan return attr != CIRRUS_NOT; 4120132718Skan} 4121132718Skan 4122132718Skan/* Cirrus reorg for invalid instruction combinations. */ 4123132718Skanstatic void 4124132718Skancirrus_reorg (rtx first) 4125132718Skan{ 4126132718Skan enum attr_cirrus attr; 4127132718Skan rtx body = PATTERN (first); 4128132718Skan rtx t; 4129132718Skan int nops; 4130132718Skan 4131132718Skan /* Any branch must be followed by 2 non Cirrus instructions. */ 4132132718Skan if (GET_CODE (first) == JUMP_INSN && GET_CODE (body) != RETURN) 4133132718Skan { 4134132718Skan nops = 0; 4135132718Skan t = next_nonnote_insn (first); 4136132718Skan 4137132718Skan if (arm_cirrus_insn_p (t)) 4138132718Skan ++ nops; 4139132718Skan 4140132718Skan if (arm_cirrus_insn_p (next_nonnote_insn (t))) 4141132718Skan ++ nops; 4142132718Skan 4143132718Skan while (nops --) 4144132718Skan emit_insn_after (gen_nop (), first); 4145132718Skan 4146132718Skan return; 4147132718Skan } 4148132718Skan 4149132718Skan /* (float (blah)) is in parallel with a clobber. */ 4150132718Skan if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0) 4151132718Skan body = XVECEXP (body, 0, 0); 4152132718Skan 4153132718Skan if (GET_CODE (body) == SET) 4154132718Skan { 4155132718Skan rtx lhs = XEXP (body, 0), rhs = XEXP (body, 1); 4156132718Skan 4157132718Skan /* cfldrd, cfldr64, cfstrd, cfstr64 must 4158132718Skan be followed by a non Cirrus insn. */ 4159132718Skan if (get_attr_cirrus (first) == CIRRUS_DOUBLE) 4160132718Skan { 4161132718Skan if (arm_cirrus_insn_p (next_nonnote_insn (first))) 4162132718Skan emit_insn_after (gen_nop (), first); 4163132718Skan 4164132718Skan return; 4165132718Skan } 4166132718Skan else if (arm_memory_load_p (first)) 4167132718Skan { 4168132718Skan unsigned int arm_regno; 4169132718Skan 4170132718Skan /* Any ldr/cfmvdlr, ldr/cfmvdhr, ldr/cfmvsr, ldr/cfmv64lr, 4171132718Skan ldr/cfmv64hr combination where the Rd field is the same 4172132718Skan in both instructions must be split with a non Cirrus 4173132718Skan insn. Example: 4174132718Skan 4175132718Skan ldr r0, blah 4176132718Skan nop 4177132718Skan cfmvsr mvf0, r0. */ 4178132718Skan 4179132718Skan /* Get Arm register number for ldr insn. */ 4180132718Skan if (GET_CODE (lhs) == REG) 4181132718Skan arm_regno = REGNO (lhs); 4182132718Skan else if (GET_CODE (rhs) == REG) 4183132718Skan arm_regno = REGNO (rhs); 4184132718Skan else 4185132718Skan abort (); 4186132718Skan 4187132718Skan /* Next insn. */ 4188132718Skan first = next_nonnote_insn (first); 4189132718Skan 4190132718Skan if (! arm_cirrus_insn_p (first)) 4191132718Skan return; 4192132718Skan 4193132718Skan body = PATTERN (first); 4194132718Skan 4195132718Skan /* (float (blah)) is in parallel with a clobber. */ 4196132718Skan if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0)) 4197132718Skan body = XVECEXP (body, 0, 0); 4198132718Skan 4199132718Skan if (GET_CODE (body) == FLOAT) 4200132718Skan body = XEXP (body, 0); 4201132718Skan 4202132718Skan if (get_attr_cirrus (first) == CIRRUS_MOVE 4203132718Skan && GET_CODE (XEXP (body, 1)) == REG 4204132718Skan && arm_regno == REGNO (XEXP (body, 1))) 4205132718Skan emit_insn_after (gen_nop (), first); 4206132718Skan 4207132718Skan return; 4208132718Skan } 4209132718Skan } 4210132718Skan 4211132718Skan /* get_attr aborts on USE and CLOBBER. */ 4212132718Skan if (!first 4213132718Skan || GET_CODE (first) != INSN 4214132718Skan || GET_CODE (PATTERN (first)) == USE 4215132718Skan || GET_CODE (PATTERN (first)) == CLOBBER) 4216132718Skan return; 4217132718Skan 4218132718Skan attr = get_attr_cirrus (first); 4219132718Skan 4220132718Skan /* Any coprocessor compare instruction (cfcmps, cfcmpd, ...) 4221132718Skan must be followed by a non-coprocessor instruction. */ 4222132718Skan if (attr == CIRRUS_COMPARE) 4223132718Skan { 4224132718Skan nops = 0; 4225132718Skan 4226132718Skan t = next_nonnote_insn (first); 4227132718Skan 4228132718Skan if (arm_cirrus_insn_p (t)) 4229132718Skan ++ nops; 4230132718Skan 4231132718Skan if (arm_cirrus_insn_p (next_nonnote_insn (t))) 4232132718Skan ++ nops; 4233132718Skan 4234132718Skan while (nops --) 4235132718Skan emit_insn_after (gen_nop (), first); 4236132718Skan 4237132718Skan return; 4238132718Skan } 4239132718Skan} 4240132718Skan 4241132718Skan/* Return nonzero if OP is a constant power of two. */ 4242132718Skanint 4243132718Skanpower_of_two_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 4244132718Skan{ 424590075Sobrien if (GET_CODE (op) == CONST_INT) 424690075Sobrien { 424790075Sobrien HOST_WIDE_INT value = INTVAL (op); 424890075Sobrien 424990075Sobrien return value != 0 && (value & (value - 1)) == 0; 425090075Sobrien } 425190075Sobrien 425290075Sobrien return FALSE; 425390075Sobrien} 425490075Sobrien 425590075Sobrien/* Return TRUE for a valid operand of a DImode operation. 425690075Sobrien Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). 425790075Sobrien Note that this disallows MEM(REG+REG), but allows 425890075Sobrien MEM(PRE/POST_INC/DEC(REG)). */ 425990075Sobrienint 4260132718Skandi_operand (rtx op, enum machine_mode mode) 426190075Sobrien{ 426290075Sobrien if (s_register_operand (op, mode)) 426390075Sobrien return TRUE; 426490075Sobrien 426590075Sobrien if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) 426690075Sobrien return FALSE; 426790075Sobrien 426890075Sobrien if (GET_CODE (op) == SUBREG) 426990075Sobrien op = SUBREG_REG (op); 427090075Sobrien 427190075Sobrien switch (GET_CODE (op)) 427290075Sobrien { 427390075Sobrien case CONST_DOUBLE: 427490075Sobrien case CONST_INT: 427590075Sobrien return TRUE; 427690075Sobrien 427790075Sobrien case MEM: 427890075Sobrien return memory_address_p (DImode, XEXP (op, 0)); 427990075Sobrien 428090075Sobrien default: 428190075Sobrien return FALSE; 428290075Sobrien } 428390075Sobrien} 428490075Sobrien 428590075Sobrien/* Like di_operand, but don't accept constants. */ 428690075Sobrienint 4287132718Skannonimmediate_di_operand (rtx op, enum machine_mode mode) 428890075Sobrien{ 428990075Sobrien if (s_register_operand (op, mode)) 429090075Sobrien return TRUE; 429190075Sobrien 429290075Sobrien if (mode != VOIDmode && GET_MODE (op) != VOIDmode && GET_MODE (op) != DImode) 429390075Sobrien return FALSE; 429490075Sobrien 429590075Sobrien if (GET_CODE (op) == SUBREG) 429690075Sobrien op = SUBREG_REG (op); 429790075Sobrien 429890075Sobrien if (GET_CODE (op) == MEM) 429990075Sobrien return memory_address_p (DImode, XEXP (op, 0)); 430090075Sobrien 430190075Sobrien return FALSE; 430290075Sobrien} 430390075Sobrien 430490075Sobrien/* Return TRUE for a valid operand of a DFmode operation when -msoft-float. 430590075Sobrien Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address). 430690075Sobrien Note that this disallows MEM(REG+REG), but allows 430790075Sobrien MEM(PRE/POST_INC/DEC(REG)). */ 430890075Sobrienint 4309132718Skansoft_df_operand (rtx op, enum machine_mode mode) 431090075Sobrien{ 431190075Sobrien if (s_register_operand (op, mode)) 431290075Sobrien return TRUE; 431390075Sobrien 431490075Sobrien if (mode != VOIDmode && GET_MODE (op) != mode) 431590075Sobrien return FALSE; 431690075Sobrien 431790075Sobrien if (GET_CODE (op) == SUBREG && CONSTANT_P (SUBREG_REG (op))) 431890075Sobrien return FALSE; 431990075Sobrien 432090075Sobrien if (GET_CODE (op) == SUBREG) 432190075Sobrien op = SUBREG_REG (op); 432290075Sobrien 432390075Sobrien switch (GET_CODE (op)) 432490075Sobrien { 432590075Sobrien case CONST_DOUBLE: 432690075Sobrien return TRUE; 432790075Sobrien 432890075Sobrien case MEM: 432990075Sobrien return memory_address_p (DFmode, XEXP (op, 0)); 433090075Sobrien 433190075Sobrien default: 433290075Sobrien return FALSE; 433390075Sobrien } 433490075Sobrien} 433590075Sobrien 433690075Sobrien/* Like soft_df_operand, but don't accept constants. */ 433790075Sobrienint 4338132718Skannonimmediate_soft_df_operand (rtx op, enum machine_mode mode) 433990075Sobrien{ 434090075Sobrien if (s_register_operand (op, mode)) 434190075Sobrien return TRUE; 434290075Sobrien 434390075Sobrien if (mode != VOIDmode && GET_MODE (op) != mode) 434490075Sobrien return FALSE; 434590075Sobrien 434690075Sobrien if (GET_CODE (op) == SUBREG) 434790075Sobrien op = SUBREG_REG (op); 434890075Sobrien 434990075Sobrien if (GET_CODE (op) == MEM) 435090075Sobrien return memory_address_p (DFmode, XEXP (op, 0)); 435190075Sobrien return FALSE; 435290075Sobrien} 435390075Sobrien 435490075Sobrien/* Return TRUE for valid index operands. */ 435590075Sobrienint 4356132718Skanindex_operand (rtx op, enum machine_mode mode) 435790075Sobrien{ 435890075Sobrien return (s_register_operand (op, mode) 435990075Sobrien || (immediate_operand (op, mode) 436090075Sobrien && (GET_CODE (op) != CONST_INT 436190075Sobrien || (INTVAL (op) < 4096 && INTVAL (op) > -4096)))); 436290075Sobrien} 436390075Sobrien 436490075Sobrien/* Return TRUE for valid shifts by a constant. This also accepts any 436590075Sobrien power of two on the (somewhat overly relaxed) assumption that the 436690075Sobrien shift operator in this case was a mult. */ 436790075Sobrienint 4368132718Skanconst_shift_operand (rtx op, enum machine_mode mode) 436990075Sobrien{ 437090075Sobrien return (power_of_two_operand (op, mode) 437190075Sobrien || (immediate_operand (op, mode) 437290075Sobrien && (GET_CODE (op) != CONST_INT 437390075Sobrien || (INTVAL (op) < 32 && INTVAL (op) > 0)))); 437490075Sobrien} 437590075Sobrien 437690075Sobrien/* Return TRUE for arithmetic operators which can be combined with a multiply 437790075Sobrien (shift). */ 437890075Sobrienint 4379132718Skanshiftable_operator (rtx x, enum machine_mode mode) 438090075Sobrien{ 438190075Sobrien enum rtx_code code; 438290075Sobrien 438390075Sobrien if (GET_MODE (x) != mode) 438490075Sobrien return FALSE; 438590075Sobrien 438690075Sobrien code = GET_CODE (x); 438790075Sobrien 438890075Sobrien return (code == PLUS || code == MINUS 438990075Sobrien || code == IOR || code == XOR || code == AND); 439090075Sobrien} 439190075Sobrien 439290075Sobrien/* Return TRUE for binary logical operators. */ 439390075Sobrienint 4394132718Skanlogical_binary_operator (rtx x, enum machine_mode mode) 439590075Sobrien{ 439690075Sobrien enum rtx_code code; 439790075Sobrien 439890075Sobrien if (GET_MODE (x) != mode) 439990075Sobrien return FALSE; 440090075Sobrien 440190075Sobrien code = GET_CODE (x); 440290075Sobrien 440390075Sobrien return (code == IOR || code == XOR || code == AND); 440490075Sobrien} 440590075Sobrien 440690075Sobrien/* Return TRUE for shift operators. */ 440790075Sobrienint 4408132718Skanshift_operator (rtx x,enum machine_mode mode) 440990075Sobrien{ 441090075Sobrien enum rtx_code code; 441190075Sobrien 441290075Sobrien if (GET_MODE (x) != mode) 441390075Sobrien return FALSE; 441490075Sobrien 441590075Sobrien code = GET_CODE (x); 441690075Sobrien 441790075Sobrien if (code == MULT) 441890075Sobrien return power_of_two_operand (XEXP (x, 1), mode); 441990075Sobrien 442090075Sobrien return (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT 442190075Sobrien || code == ROTATERT); 442290075Sobrien} 442390075Sobrien 442490075Sobrien/* Return TRUE if x is EQ or NE. */ 442590075Sobrienint 4426132718Skanequality_operator (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED) 442790075Sobrien{ 442890075Sobrien return GET_CODE (x) == EQ || GET_CODE (x) == NE; 442990075Sobrien} 443090075Sobrien 443190075Sobrien/* Return TRUE if x is a comparison operator other than LTGT or UNEQ. */ 443290075Sobrienint 4433132718Skanarm_comparison_operator (rtx x, enum machine_mode mode) 443490075Sobrien{ 443590075Sobrien return (comparison_operator (x, mode) 443690075Sobrien && GET_CODE (x) != LTGT 443790075Sobrien && GET_CODE (x) != UNEQ); 443890075Sobrien} 443990075Sobrien 444090075Sobrien/* Return TRUE for SMIN SMAX UMIN UMAX operators. */ 444190075Sobrienint 4442132718Skanminmax_operator (rtx x, enum machine_mode mode) 444390075Sobrien{ 444490075Sobrien enum rtx_code code = GET_CODE (x); 444590075Sobrien 444690075Sobrien if (GET_MODE (x) != mode) 444790075Sobrien return FALSE; 444890075Sobrien 444990075Sobrien return code == SMIN || code == SMAX || code == UMIN || code == UMAX; 445090075Sobrien} 445190075Sobrien 445290075Sobrien/* Return TRUE if this is the condition code register, if we aren't given 445390075Sobrien a mode, accept any class CCmode register. */ 445490075Sobrienint 4455132718Skancc_register (rtx x, enum machine_mode mode) 445690075Sobrien{ 445790075Sobrien if (mode == VOIDmode) 445890075Sobrien { 445990075Sobrien mode = GET_MODE (x); 446090075Sobrien 446190075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 446290075Sobrien return FALSE; 446390075Sobrien } 446490075Sobrien 446590075Sobrien if ( GET_MODE (x) == mode 446690075Sobrien && GET_CODE (x) == REG 446790075Sobrien && REGNO (x) == CC_REGNUM) 446890075Sobrien return TRUE; 446990075Sobrien 447090075Sobrien return FALSE; 447190075Sobrien} 447290075Sobrien 447390075Sobrien/* Return TRUE if this is the condition code register, if we aren't given 447490075Sobrien a mode, accept any class CCmode register which indicates a dominance 447590075Sobrien expression. */ 447690075Sobrienint 4477132718Skandominant_cc_register (rtx x, enum machine_mode mode) 447890075Sobrien{ 447990075Sobrien if (mode == VOIDmode) 448090075Sobrien { 448190075Sobrien mode = GET_MODE (x); 448290075Sobrien 448390075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 448490075Sobrien return FALSE; 448590075Sobrien } 448690075Sobrien 4487132718Skan if (mode != CC_DNEmode && mode != CC_DEQmode 448890075Sobrien && mode != CC_DLEmode && mode != CC_DLTmode 448990075Sobrien && mode != CC_DGEmode && mode != CC_DGTmode 449090075Sobrien && mode != CC_DLEUmode && mode != CC_DLTUmode 449190075Sobrien && mode != CC_DGEUmode && mode != CC_DGTUmode) 449290075Sobrien return FALSE; 449390075Sobrien 449490075Sobrien return cc_register (x, mode); 449590075Sobrien} 449690075Sobrien 449790075Sobrien/* Return TRUE if X references a SYMBOL_REF. */ 449890075Sobrienint 4499132718Skansymbol_mentioned_p (rtx x) 450090075Sobrien{ 450190075Sobrien const char * fmt; 450290075Sobrien int i; 450390075Sobrien 450490075Sobrien if (GET_CODE (x) == SYMBOL_REF) 450590075Sobrien return 1; 450690075Sobrien 450790075Sobrien fmt = GET_RTX_FORMAT (GET_CODE (x)); 450890075Sobrien 450990075Sobrien for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) 451090075Sobrien { 451190075Sobrien if (fmt[i] == 'E') 451290075Sobrien { 451390075Sobrien int j; 451490075Sobrien 451590075Sobrien for (j = XVECLEN (x, i) - 1; j >= 0; j--) 451690075Sobrien if (symbol_mentioned_p (XVECEXP (x, i, j))) 451790075Sobrien return 1; 451890075Sobrien } 451990075Sobrien else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i))) 452090075Sobrien return 1; 452190075Sobrien } 452290075Sobrien 452390075Sobrien return 0; 452490075Sobrien} 452590075Sobrien 452690075Sobrien/* Return TRUE if X references a LABEL_REF. */ 452790075Sobrienint 4528132718Skanlabel_mentioned_p (rtx x) 452990075Sobrien{ 453090075Sobrien const char * fmt; 453190075Sobrien int i; 453290075Sobrien 453390075Sobrien if (GET_CODE (x) == LABEL_REF) 453490075Sobrien return 1; 453590075Sobrien 453690075Sobrien fmt = GET_RTX_FORMAT (GET_CODE (x)); 453790075Sobrien for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) 453890075Sobrien { 453990075Sobrien if (fmt[i] == 'E') 454090075Sobrien { 454190075Sobrien int j; 454290075Sobrien 454390075Sobrien for (j = XVECLEN (x, i) - 1; j >= 0; j--) 454490075Sobrien if (label_mentioned_p (XVECEXP (x, i, j))) 454590075Sobrien return 1; 454690075Sobrien } 454790075Sobrien else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i))) 454890075Sobrien return 1; 454990075Sobrien } 455090075Sobrien 455190075Sobrien return 0; 455290075Sobrien} 455390075Sobrien 455490075Sobrienenum rtx_code 4555132718Skanminmax_code (rtx x) 455690075Sobrien{ 455790075Sobrien enum rtx_code code = GET_CODE (x); 455890075Sobrien 455990075Sobrien if (code == SMAX) 456090075Sobrien return GE; 456190075Sobrien else if (code == SMIN) 456290075Sobrien return LE; 456390075Sobrien else if (code == UMIN) 456490075Sobrien return LEU; 456590075Sobrien else if (code == UMAX) 456690075Sobrien return GEU; 456790075Sobrien 456890075Sobrien abort (); 456990075Sobrien} 457090075Sobrien 457190075Sobrien/* Return 1 if memory locations are adjacent. */ 457290075Sobrienint 4573132718Skanadjacent_mem_locations (rtx a, rtx b) 457490075Sobrien{ 457590075Sobrien if ((GET_CODE (XEXP (a, 0)) == REG 457690075Sobrien || (GET_CODE (XEXP (a, 0)) == PLUS 457790075Sobrien && GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT)) 457890075Sobrien && (GET_CODE (XEXP (b, 0)) == REG 457990075Sobrien || (GET_CODE (XEXP (b, 0)) == PLUS 458090075Sobrien && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT))) 458190075Sobrien { 4582146895Skan HOST_WIDE_INT val0 = 0, val1 = 0; 4583146895Skan rtx reg0, reg1; 4584146895Skan int val_diff; 4585146895Skan 458690075Sobrien if (GET_CODE (XEXP (a, 0)) == PLUS) 458790075Sobrien { 4588146895Skan reg0 = XEXP (XEXP (a, 0), 0); 458990075Sobrien val0 = INTVAL (XEXP (XEXP (a, 0), 1)); 459090075Sobrien } 459190075Sobrien else 4592146895Skan reg0 = XEXP (a, 0); 459390075Sobrien 459490075Sobrien if (GET_CODE (XEXP (b, 0)) == PLUS) 459590075Sobrien { 4596146895Skan reg1 = XEXP (XEXP (b, 0), 0); 459790075Sobrien val1 = INTVAL (XEXP (XEXP (b, 0), 1)); 459890075Sobrien } 459990075Sobrien else 4600146895Skan reg1 = XEXP (b, 0); 460190075Sobrien 4602132718Skan /* Don't accept any offset that will require multiple 4603132718Skan instructions to handle, since this would cause the 4604132718Skan arith_adjacentmem pattern to output an overlong sequence. */ 4605132718Skan if (!const_ok_for_op (PLUS, val0) || !const_ok_for_op (PLUS, val1)) 4606132718Skan return 0; 4607146895Skan 4608146895Skan /* Don't allow an eliminable register: register elimination can make 4609146895Skan the offset too large. */ 4610146895Skan if (arm_eliminable_register (reg0)) 4611146895Skan return 0; 4612146895Skan 4613146895Skan val_diff = val1 - val0; 4614146895Skan return ((REGNO (reg0) == REGNO (reg1)) 4615146895Skan && (val_diff == 4 || val_diff == -4)); 461690075Sobrien } 4617146895Skan 461890075Sobrien return 0; 461990075Sobrien} 462090075Sobrien 462190075Sobrien/* Return 1 if OP is a load multiple operation. It is known to be 462290075Sobrien parallel and the first section will be tested. */ 462390075Sobrienint 4624132718Skanload_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 462590075Sobrien{ 462690075Sobrien HOST_WIDE_INT count = XVECLEN (op, 0); 462790075Sobrien int dest_regno; 462890075Sobrien rtx src_addr; 462990075Sobrien HOST_WIDE_INT i = 1, base = 0; 463090075Sobrien rtx elt; 463190075Sobrien 463290075Sobrien if (count <= 1 463390075Sobrien || GET_CODE (XVECEXP (op, 0, 0)) != SET) 463490075Sobrien return 0; 463590075Sobrien 463690075Sobrien /* Check to see if this might be a write-back. */ 463790075Sobrien if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) 463890075Sobrien { 463990075Sobrien i++; 464090075Sobrien base = 1; 464190075Sobrien 464290075Sobrien /* Now check it more carefully. */ 464390075Sobrien if (GET_CODE (SET_DEST (elt)) != REG 464490075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG 464590075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT 464690075Sobrien || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) 464790075Sobrien return 0; 464890075Sobrien } 464990075Sobrien 465090075Sobrien /* Perform a quick check so we don't blow up below. */ 465190075Sobrien if (count <= i 465290075Sobrien || GET_CODE (XVECEXP (op, 0, i - 1)) != SET 465390075Sobrien || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG 465490075Sobrien || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM) 465590075Sobrien return 0; 465690075Sobrien 465790075Sobrien dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1))); 465890075Sobrien src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0); 465990075Sobrien 466090075Sobrien for (; i < count; i++) 466190075Sobrien { 466290075Sobrien elt = XVECEXP (op, 0, i); 466390075Sobrien 466490075Sobrien if (GET_CODE (elt) != SET 466590075Sobrien || GET_CODE (SET_DEST (elt)) != REG 466690075Sobrien || GET_MODE (SET_DEST (elt)) != SImode 466790075Sobrien || REGNO (SET_DEST (elt)) != (unsigned int)(dest_regno + i - base) 466890075Sobrien || GET_CODE (SET_SRC (elt)) != MEM 466990075Sobrien || GET_MODE (SET_SRC (elt)) != SImode 467090075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS 467190075Sobrien || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) 467290075Sobrien || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT 467390075Sobrien || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4) 467490075Sobrien return 0; 467590075Sobrien } 467690075Sobrien 467790075Sobrien return 1; 467890075Sobrien} 467990075Sobrien 468090075Sobrien/* Return 1 if OP is a store multiple operation. It is known to be 468190075Sobrien parallel and the first section will be tested. */ 468290075Sobrienint 4683132718Skanstore_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 468490075Sobrien{ 468590075Sobrien HOST_WIDE_INT count = XVECLEN (op, 0); 468690075Sobrien int src_regno; 468790075Sobrien rtx dest_addr; 468890075Sobrien HOST_WIDE_INT i = 1, base = 0; 468990075Sobrien rtx elt; 469090075Sobrien 469190075Sobrien if (count <= 1 469290075Sobrien || GET_CODE (XVECEXP (op, 0, 0)) != SET) 469390075Sobrien return 0; 469490075Sobrien 469590075Sobrien /* Check to see if this might be a write-back. */ 469690075Sobrien if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) 469790075Sobrien { 469890075Sobrien i++; 469990075Sobrien base = 1; 470090075Sobrien 470190075Sobrien /* Now check it more carefully. */ 470290075Sobrien if (GET_CODE (SET_DEST (elt)) != REG 470390075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG 470490075Sobrien || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT 470590075Sobrien || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) 470690075Sobrien return 0; 470790075Sobrien } 470890075Sobrien 470990075Sobrien /* Perform a quick check so we don't blow up below. */ 471090075Sobrien if (count <= i 471190075Sobrien || GET_CODE (XVECEXP (op, 0, i - 1)) != SET 471290075Sobrien || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM 471390075Sobrien || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG) 471490075Sobrien return 0; 471590075Sobrien 471690075Sobrien src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1))); 471790075Sobrien dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0); 471890075Sobrien 471990075Sobrien for (; i < count; i++) 472090075Sobrien { 472190075Sobrien elt = XVECEXP (op, 0, i); 472290075Sobrien 472390075Sobrien if (GET_CODE (elt) != SET 472490075Sobrien || GET_CODE (SET_SRC (elt)) != REG 472590075Sobrien || GET_MODE (SET_SRC (elt)) != SImode 472690075Sobrien || REGNO (SET_SRC (elt)) != (unsigned int)(src_regno + i - base) 472790075Sobrien || GET_CODE (SET_DEST (elt)) != MEM 472890075Sobrien || GET_MODE (SET_DEST (elt)) != SImode 472990075Sobrien || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS 473090075Sobrien || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) 473190075Sobrien || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT 473290075Sobrien || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4) 473390075Sobrien return 0; 473490075Sobrien } 473590075Sobrien 473690075Sobrien return 1; 473790075Sobrien} 473890075Sobrien 473990075Sobrienint 4740132718Skanload_multiple_sequence (rtx *operands, int nops, int *regs, int *base, 4741132718Skan HOST_WIDE_INT *load_offset) 474290075Sobrien{ 474390075Sobrien int unsorted_regs[4]; 474490075Sobrien HOST_WIDE_INT unsorted_offsets[4]; 474590075Sobrien int order[4]; 474690075Sobrien int base_reg = -1; 474790075Sobrien int i; 474890075Sobrien 474990075Sobrien /* Can only handle 2, 3, or 4 insns at present, 475090075Sobrien though could be easily extended if required. */ 475190075Sobrien if (nops < 2 || nops > 4) 475290075Sobrien abort (); 475390075Sobrien 475490075Sobrien /* Loop over the operands and check that the memory references are 475590075Sobrien suitable (ie immediate offsets from the same base register). At 475690075Sobrien the same time, extract the target register, and the memory 475790075Sobrien offsets. */ 475890075Sobrien for (i = 0; i < nops; i++) 475990075Sobrien { 476090075Sobrien rtx reg; 476190075Sobrien rtx offset; 476290075Sobrien 476390075Sobrien /* Convert a subreg of a mem into the mem itself. */ 476490075Sobrien if (GET_CODE (operands[nops + i]) == SUBREG) 476590075Sobrien operands[nops + i] = alter_subreg (operands + (nops + i)); 476690075Sobrien 476790075Sobrien if (GET_CODE (operands[nops + i]) != MEM) 476890075Sobrien abort (); 476990075Sobrien 477090075Sobrien /* Don't reorder volatile memory references; it doesn't seem worth 477190075Sobrien looking for the case where the order is ok anyway. */ 477290075Sobrien if (MEM_VOLATILE_P (operands[nops + i])) 477390075Sobrien return 0; 477490075Sobrien 477590075Sobrien offset = const0_rtx; 477690075Sobrien 477790075Sobrien if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG 477890075Sobrien || (GET_CODE (reg) == SUBREG 477990075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 478090075Sobrien || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS 478190075Sobrien && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) 478290075Sobrien == REG) 478390075Sobrien || (GET_CODE (reg) == SUBREG 478490075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 478590075Sobrien && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) 478690075Sobrien == CONST_INT))) 478790075Sobrien { 478890075Sobrien if (i == 0) 478990075Sobrien { 479090075Sobrien base_reg = REGNO (reg); 479190075Sobrien unsorted_regs[0] = (GET_CODE (operands[i]) == REG 479290075Sobrien ? REGNO (operands[i]) 479390075Sobrien : REGNO (SUBREG_REG (operands[i]))); 479490075Sobrien order[0] = 0; 479590075Sobrien } 479690075Sobrien else 479790075Sobrien { 479890075Sobrien if (base_reg != (int) REGNO (reg)) 479990075Sobrien /* Not addressed from the same base register. */ 480090075Sobrien return 0; 480190075Sobrien 480290075Sobrien unsorted_regs[i] = (GET_CODE (operands[i]) == REG 480390075Sobrien ? REGNO (operands[i]) 480490075Sobrien : REGNO (SUBREG_REG (operands[i]))); 480590075Sobrien if (unsorted_regs[i] < unsorted_regs[order[0]]) 480690075Sobrien order[0] = i; 480790075Sobrien } 480890075Sobrien 480990075Sobrien /* If it isn't an integer register, or if it overwrites the 481090075Sobrien base register but isn't the last insn in the list, then 481190075Sobrien we can't do this. */ 481290075Sobrien if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14 481390075Sobrien || (i != nops - 1 && unsorted_regs[i] == base_reg)) 481490075Sobrien return 0; 481590075Sobrien 481690075Sobrien unsorted_offsets[i] = INTVAL (offset); 481790075Sobrien } 481890075Sobrien else 481990075Sobrien /* Not a suitable memory address. */ 482090075Sobrien return 0; 482190075Sobrien } 482290075Sobrien 482390075Sobrien /* All the useful information has now been extracted from the 482490075Sobrien operands into unsorted_regs and unsorted_offsets; additionally, 482590075Sobrien order[0] has been set to the lowest numbered register in the 482690075Sobrien list. Sort the registers into order, and check that the memory 482790075Sobrien offsets are ascending and adjacent. */ 482890075Sobrien 482990075Sobrien for (i = 1; i < nops; i++) 483090075Sobrien { 483190075Sobrien int j; 483290075Sobrien 483390075Sobrien order[i] = order[i - 1]; 483490075Sobrien for (j = 0; j < nops; j++) 483590075Sobrien if (unsorted_regs[j] > unsorted_regs[order[i - 1]] 483690075Sobrien && (order[i] == order[i - 1] 483790075Sobrien || unsorted_regs[j] < unsorted_regs[order[i]])) 483890075Sobrien order[i] = j; 483990075Sobrien 484090075Sobrien /* Have we found a suitable register? if not, one must be used more 484190075Sobrien than once. */ 484290075Sobrien if (order[i] == order[i - 1]) 484390075Sobrien return 0; 484490075Sobrien 484590075Sobrien /* Is the memory address adjacent and ascending? */ 484690075Sobrien if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) 484790075Sobrien return 0; 484890075Sobrien } 484990075Sobrien 485090075Sobrien if (base) 485190075Sobrien { 485290075Sobrien *base = base_reg; 485390075Sobrien 485490075Sobrien for (i = 0; i < nops; i++) 485590075Sobrien regs[i] = unsorted_regs[order[i]]; 485690075Sobrien 485790075Sobrien *load_offset = unsorted_offsets[order[0]]; 485890075Sobrien } 485990075Sobrien 486090075Sobrien if (unsorted_offsets[order[0]] == 0) 486190075Sobrien return 1; /* ldmia */ 486290075Sobrien 486390075Sobrien if (unsorted_offsets[order[0]] == 4) 486490075Sobrien return 2; /* ldmib */ 486590075Sobrien 486690075Sobrien if (unsorted_offsets[order[nops - 1]] == 0) 486790075Sobrien return 3; /* ldmda */ 486890075Sobrien 486990075Sobrien if (unsorted_offsets[order[nops - 1]] == -4) 487090075Sobrien return 4; /* ldmdb */ 487190075Sobrien 487290075Sobrien /* For ARM8,9 & StrongARM, 2 ldr instructions are faster than an ldm 487390075Sobrien if the offset isn't small enough. The reason 2 ldrs are faster 487490075Sobrien is because these ARMs are able to do more than one cache access 487590075Sobrien in a single cycle. The ARM9 and StrongARM have Harvard caches, 487690075Sobrien whilst the ARM8 has a double bandwidth cache. This means that 487790075Sobrien these cores can do both an instruction fetch and a data fetch in 487890075Sobrien a single cycle, so the trick of calculating the address into a 487990075Sobrien scratch register (one of the result regs) and then doing a load 488090075Sobrien multiple actually becomes slower (and no smaller in code size). 488190075Sobrien That is the transformation 488290075Sobrien 488390075Sobrien ldr rd1, [rbase + offset] 488490075Sobrien ldr rd2, [rbase + offset + 4] 488590075Sobrien 488690075Sobrien to 488790075Sobrien 488890075Sobrien add rd1, rbase, offset 488990075Sobrien ldmia rd1, {rd1, rd2} 489090075Sobrien 489190075Sobrien produces worse code -- '3 cycles + any stalls on rd2' instead of 489290075Sobrien '2 cycles + any stalls on rd2'. On ARMs with only one cache 489390075Sobrien access per cycle, the first sequence could never complete in less 489490075Sobrien than 6 cycles, whereas the ldm sequence would only take 5 and 489590075Sobrien would make better use of sequential accesses if not hitting the 489690075Sobrien cache. 489790075Sobrien 489890075Sobrien We cheat here and test 'arm_ld_sched' which we currently know to 489990075Sobrien only be true for the ARM8, ARM9 and StrongARM. If this ever 490090075Sobrien changes, then the test below needs to be reworked. */ 490190075Sobrien if (nops == 2 && arm_ld_sched) 490290075Sobrien return 0; 490390075Sobrien 490490075Sobrien /* Can't do it without setting up the offset, only do this if it takes 490590075Sobrien no more than one insn. */ 490690075Sobrien return (const_ok_for_arm (unsorted_offsets[order[0]]) 490790075Sobrien || const_ok_for_arm (-unsorted_offsets[order[0]])) ? 5 : 0; 490890075Sobrien} 490990075Sobrien 491090075Sobrienconst char * 4911132718Skanemit_ldm_seq (rtx *operands, int nops) 491290075Sobrien{ 491390075Sobrien int regs[4]; 491490075Sobrien int base_reg; 491590075Sobrien HOST_WIDE_INT offset; 491690075Sobrien char buf[100]; 491790075Sobrien int i; 491890075Sobrien 491990075Sobrien switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset)) 492090075Sobrien { 492190075Sobrien case 1: 492290075Sobrien strcpy (buf, "ldm%?ia\t"); 492390075Sobrien break; 492490075Sobrien 492590075Sobrien case 2: 492690075Sobrien strcpy (buf, "ldm%?ib\t"); 492790075Sobrien break; 492890075Sobrien 492990075Sobrien case 3: 493090075Sobrien strcpy (buf, "ldm%?da\t"); 493190075Sobrien break; 493290075Sobrien 493390075Sobrien case 4: 493490075Sobrien strcpy (buf, "ldm%?db\t"); 493590075Sobrien break; 493690075Sobrien 493790075Sobrien case 5: 493890075Sobrien if (offset >= 0) 493990075Sobrien sprintf (buf, "add%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, 494090075Sobrien reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], 494190075Sobrien (long) offset); 494290075Sobrien else 494390075Sobrien sprintf (buf, "sub%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX, 494490075Sobrien reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg], 494590075Sobrien (long) -offset); 494690075Sobrien output_asm_insn (buf, operands); 494790075Sobrien base_reg = regs[0]; 494890075Sobrien strcpy (buf, "ldm%?ia\t"); 494990075Sobrien break; 495090075Sobrien 495190075Sobrien default: 495290075Sobrien abort (); 495390075Sobrien } 495490075Sobrien 495590075Sobrien sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, 495690075Sobrien reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); 495790075Sobrien 495890075Sobrien for (i = 1; i < nops; i++) 495990075Sobrien sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, 496090075Sobrien reg_names[regs[i]]); 496190075Sobrien 496290075Sobrien strcat (buf, "}\t%@ phole ldm"); 496390075Sobrien 496490075Sobrien output_asm_insn (buf, operands); 496590075Sobrien return ""; 496690075Sobrien} 496790075Sobrien 496890075Sobrienint 4969132718Skanstore_multiple_sequence (rtx *operands, int nops, int *regs, int *base, 4970132718Skan HOST_WIDE_INT * load_offset) 497190075Sobrien{ 497290075Sobrien int unsorted_regs[4]; 497390075Sobrien HOST_WIDE_INT unsorted_offsets[4]; 497490075Sobrien int order[4]; 497590075Sobrien int base_reg = -1; 497690075Sobrien int i; 497790075Sobrien 497890075Sobrien /* Can only handle 2, 3, or 4 insns at present, though could be easily 497990075Sobrien extended if required. */ 498090075Sobrien if (nops < 2 || nops > 4) 498190075Sobrien abort (); 498290075Sobrien 498390075Sobrien /* Loop over the operands and check that the memory references are 498490075Sobrien suitable (ie immediate offsets from the same base register). At 498590075Sobrien the same time, extract the target register, and the memory 498690075Sobrien offsets. */ 498790075Sobrien for (i = 0; i < nops; i++) 498890075Sobrien { 498990075Sobrien rtx reg; 499090075Sobrien rtx offset; 499190075Sobrien 499290075Sobrien /* Convert a subreg of a mem into the mem itself. */ 499390075Sobrien if (GET_CODE (operands[nops + i]) == SUBREG) 499490075Sobrien operands[nops + i] = alter_subreg (operands + (nops + i)); 499590075Sobrien 499690075Sobrien if (GET_CODE (operands[nops + i]) != MEM) 499790075Sobrien abort (); 499890075Sobrien 499990075Sobrien /* Don't reorder volatile memory references; it doesn't seem worth 500090075Sobrien looking for the case where the order is ok anyway. */ 500190075Sobrien if (MEM_VOLATILE_P (operands[nops + i])) 500290075Sobrien return 0; 500390075Sobrien 500490075Sobrien offset = const0_rtx; 500590075Sobrien 500690075Sobrien if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG 500790075Sobrien || (GET_CODE (reg) == SUBREG 500890075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 500990075Sobrien || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS 501090075Sobrien && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0)) 501190075Sobrien == REG) 501290075Sobrien || (GET_CODE (reg) == SUBREG 501390075Sobrien && GET_CODE (reg = SUBREG_REG (reg)) == REG)) 501490075Sobrien && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1)) 501590075Sobrien == CONST_INT))) 501690075Sobrien { 501790075Sobrien if (i == 0) 501890075Sobrien { 501990075Sobrien base_reg = REGNO (reg); 502090075Sobrien unsorted_regs[0] = (GET_CODE (operands[i]) == REG 502190075Sobrien ? REGNO (operands[i]) 502290075Sobrien : REGNO (SUBREG_REG (operands[i]))); 502390075Sobrien order[0] = 0; 502490075Sobrien } 502590075Sobrien else 502690075Sobrien { 502790075Sobrien if (base_reg != (int) REGNO (reg)) 502890075Sobrien /* Not addressed from the same base register. */ 502990075Sobrien return 0; 503090075Sobrien 503190075Sobrien unsorted_regs[i] = (GET_CODE (operands[i]) == REG 503290075Sobrien ? REGNO (operands[i]) 503390075Sobrien : REGNO (SUBREG_REG (operands[i]))); 503490075Sobrien if (unsorted_regs[i] < unsorted_regs[order[0]]) 503590075Sobrien order[0] = i; 503690075Sobrien } 503790075Sobrien 503890075Sobrien /* If it isn't an integer register, then we can't do this. */ 503990075Sobrien if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14) 504090075Sobrien return 0; 504190075Sobrien 504290075Sobrien unsorted_offsets[i] = INTVAL (offset); 504390075Sobrien } 504490075Sobrien else 504590075Sobrien /* Not a suitable memory address. */ 504690075Sobrien return 0; 504790075Sobrien } 504890075Sobrien 504990075Sobrien /* All the useful information has now been extracted from the 505090075Sobrien operands into unsorted_regs and unsorted_offsets; additionally, 505190075Sobrien order[0] has been set to the lowest numbered register in the 505290075Sobrien list. Sort the registers into order, and check that the memory 505390075Sobrien offsets are ascending and adjacent. */ 505490075Sobrien 505590075Sobrien for (i = 1; i < nops; i++) 505690075Sobrien { 505790075Sobrien int j; 505890075Sobrien 505990075Sobrien order[i] = order[i - 1]; 506090075Sobrien for (j = 0; j < nops; j++) 506190075Sobrien if (unsorted_regs[j] > unsorted_regs[order[i - 1]] 506290075Sobrien && (order[i] == order[i - 1] 506390075Sobrien || unsorted_regs[j] < unsorted_regs[order[i]])) 506490075Sobrien order[i] = j; 506590075Sobrien 506690075Sobrien /* Have we found a suitable register? if not, one must be used more 506790075Sobrien than once. */ 506890075Sobrien if (order[i] == order[i - 1]) 506990075Sobrien return 0; 507090075Sobrien 507190075Sobrien /* Is the memory address adjacent and ascending? */ 507290075Sobrien if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4) 507390075Sobrien return 0; 507490075Sobrien } 507590075Sobrien 507690075Sobrien if (base) 507790075Sobrien { 507890075Sobrien *base = base_reg; 507990075Sobrien 508090075Sobrien for (i = 0; i < nops; i++) 508190075Sobrien regs[i] = unsorted_regs[order[i]]; 508290075Sobrien 508390075Sobrien *load_offset = unsorted_offsets[order[0]]; 508490075Sobrien } 508590075Sobrien 508690075Sobrien if (unsorted_offsets[order[0]] == 0) 508790075Sobrien return 1; /* stmia */ 508890075Sobrien 508990075Sobrien if (unsorted_offsets[order[0]] == 4) 509090075Sobrien return 2; /* stmib */ 509190075Sobrien 509290075Sobrien if (unsorted_offsets[order[nops - 1]] == 0) 509390075Sobrien return 3; /* stmda */ 509490075Sobrien 509590075Sobrien if (unsorted_offsets[order[nops - 1]] == -4) 509690075Sobrien return 4; /* stmdb */ 509790075Sobrien 509890075Sobrien return 0; 509990075Sobrien} 510090075Sobrien 510190075Sobrienconst char * 5102132718Skanemit_stm_seq (rtx *operands, int nops) 510390075Sobrien{ 510490075Sobrien int regs[4]; 510590075Sobrien int base_reg; 510690075Sobrien HOST_WIDE_INT offset; 510790075Sobrien char buf[100]; 510890075Sobrien int i; 510990075Sobrien 511090075Sobrien switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset)) 511190075Sobrien { 511290075Sobrien case 1: 511390075Sobrien strcpy (buf, "stm%?ia\t"); 511490075Sobrien break; 511590075Sobrien 511690075Sobrien case 2: 511790075Sobrien strcpy (buf, "stm%?ib\t"); 511890075Sobrien break; 511990075Sobrien 512090075Sobrien case 3: 512190075Sobrien strcpy (buf, "stm%?da\t"); 512290075Sobrien break; 512390075Sobrien 512490075Sobrien case 4: 512590075Sobrien strcpy (buf, "stm%?db\t"); 512690075Sobrien break; 512790075Sobrien 512890075Sobrien default: 512990075Sobrien abort (); 513090075Sobrien } 513190075Sobrien 513290075Sobrien sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX, 513390075Sobrien reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]); 513490075Sobrien 513590075Sobrien for (i = 1; i < nops; i++) 513690075Sobrien sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX, 513790075Sobrien reg_names[regs[i]]); 513890075Sobrien 513990075Sobrien strcat (buf, "}\t%@ phole stm"); 514090075Sobrien 514190075Sobrien output_asm_insn (buf, operands); 514290075Sobrien return ""; 514390075Sobrien} 514490075Sobrien 514590075Sobrienint 5146132718Skanmulti_register_push (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 514790075Sobrien{ 514890075Sobrien if (GET_CODE (op) != PARALLEL 514990075Sobrien || (GET_CODE (XVECEXP (op, 0, 0)) != SET) 515090075Sobrien || (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) 515190075Sobrien || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSH_MULT)) 515290075Sobrien return 0; 515390075Sobrien 515490075Sobrien return 1; 515590075Sobrien} 515690075Sobrien 515790075Sobrien/* Routines for use in generating RTL. */ 515890075Sobrien 515990075Sobrienrtx 5160132718Skanarm_gen_load_multiple (int base_regno, int count, rtx from, int up, 5161132718Skan int write_back, int unchanging_p, int in_struct_p, 5162132718Skan int scalar_p) 516390075Sobrien{ 516490075Sobrien int i = 0, j; 516590075Sobrien rtx result; 516690075Sobrien int sign = up ? 1 : -1; 516790075Sobrien rtx mem; 516890075Sobrien 516990075Sobrien /* XScale has load-store double instructions, but they have stricter 517090075Sobrien alignment requirements than load-store multiple, so we can not 517190075Sobrien use them. 517290075Sobrien 517390075Sobrien For XScale ldm requires 2 + NREGS cycles to complete and blocks 517490075Sobrien the pipeline until completion. 517590075Sobrien 517690075Sobrien NREGS CYCLES 517790075Sobrien 1 3 517890075Sobrien 2 4 517990075Sobrien 3 5 518090075Sobrien 4 6 518190075Sobrien 518290075Sobrien An ldr instruction takes 1-3 cycles, but does not block the 518390075Sobrien pipeline. 518490075Sobrien 518590075Sobrien NREGS CYCLES 518690075Sobrien 1 1-3 518790075Sobrien 2 2-6 518890075Sobrien 3 3-9 518990075Sobrien 4 4-12 519090075Sobrien 519190075Sobrien Best case ldr will always win. However, the more ldr instructions 519290075Sobrien we issue, the less likely we are to be able to schedule them well. 519390075Sobrien Using ldr instructions also increases code size. 519490075Sobrien 519590075Sobrien As a compromise, we use ldr for counts of 1 or 2 regs, and ldm 519690075Sobrien for counts of 3 or 4 regs. */ 5197132718Skan if (arm_tune_xscale && count <= 2 && ! optimize_size) 519890075Sobrien { 519990075Sobrien rtx seq; 520090075Sobrien 520190075Sobrien start_sequence (); 520290075Sobrien 520390075Sobrien for (i = 0; i < count; i++) 520490075Sobrien { 520590075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (from, i * 4 * sign)); 520690075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 520790075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 520890075Sobrien MEM_SCALAR_P (mem) = scalar_p; 520990075Sobrien emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem); 521090075Sobrien } 521190075Sobrien 521290075Sobrien if (write_back) 521390075Sobrien emit_move_insn (from, plus_constant (from, count * 4 * sign)); 521490075Sobrien 5215117395Skan seq = get_insns (); 521690075Sobrien end_sequence (); 521790075Sobrien 521890075Sobrien return seq; 521990075Sobrien } 522090075Sobrien 522190075Sobrien result = gen_rtx_PARALLEL (VOIDmode, 522290075Sobrien rtvec_alloc (count + (write_back ? 1 : 0))); 522390075Sobrien if (write_back) 522490075Sobrien { 522590075Sobrien XVECEXP (result, 0, 0) 522690075Sobrien = gen_rtx_SET (GET_MODE (from), from, 522790075Sobrien plus_constant (from, count * 4 * sign)); 522890075Sobrien i = 1; 522990075Sobrien count++; 523090075Sobrien } 523190075Sobrien 523290075Sobrien for (j = 0; i < count; i++, j++) 523390075Sobrien { 523490075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (from, j * 4 * sign)); 523590075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 523690075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 523790075Sobrien MEM_SCALAR_P (mem) = scalar_p; 523890075Sobrien XVECEXP (result, 0, i) 523990075Sobrien = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, base_regno + j), mem); 524090075Sobrien } 524190075Sobrien 524290075Sobrien return result; 524390075Sobrien} 524490075Sobrien 524590075Sobrienrtx 5246132718Skanarm_gen_store_multiple (int base_regno, int count, rtx to, int up, 5247132718Skan int write_back, int unchanging_p, int in_struct_p, 5248132718Skan int scalar_p) 524990075Sobrien{ 525090075Sobrien int i = 0, j; 525190075Sobrien rtx result; 525290075Sobrien int sign = up ? 1 : -1; 525390075Sobrien rtx mem; 525490075Sobrien 525590075Sobrien /* See arm_gen_load_multiple for discussion of 525690075Sobrien the pros/cons of ldm/stm usage for XScale. */ 5257132718Skan if (arm_tune_xscale && count <= 2 && ! optimize_size) 525890075Sobrien { 525990075Sobrien rtx seq; 526090075Sobrien 526190075Sobrien start_sequence (); 526290075Sobrien 526390075Sobrien for (i = 0; i < count; i++) 526490075Sobrien { 526590075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (to, i * 4 * sign)); 526690075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 526790075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 526890075Sobrien MEM_SCALAR_P (mem) = scalar_p; 526990075Sobrien emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i)); 527090075Sobrien } 527190075Sobrien 527290075Sobrien if (write_back) 527390075Sobrien emit_move_insn (to, plus_constant (to, count * 4 * sign)); 527490075Sobrien 5275117395Skan seq = get_insns (); 527690075Sobrien end_sequence (); 527790075Sobrien 527890075Sobrien return seq; 527990075Sobrien } 528090075Sobrien 528190075Sobrien result = gen_rtx_PARALLEL (VOIDmode, 528290075Sobrien rtvec_alloc (count + (write_back ? 1 : 0))); 528390075Sobrien if (write_back) 528490075Sobrien { 528590075Sobrien XVECEXP (result, 0, 0) 528690075Sobrien = gen_rtx_SET (GET_MODE (to), to, 528790075Sobrien plus_constant (to, count * 4 * sign)); 528890075Sobrien i = 1; 528990075Sobrien count++; 529090075Sobrien } 529190075Sobrien 529290075Sobrien for (j = 0; i < count; i++, j++) 529390075Sobrien { 529490075Sobrien mem = gen_rtx_MEM (SImode, plus_constant (to, j * 4 * sign)); 529590075Sobrien RTX_UNCHANGING_P (mem) = unchanging_p; 529690075Sobrien MEM_IN_STRUCT_P (mem) = in_struct_p; 529790075Sobrien MEM_SCALAR_P (mem) = scalar_p; 529890075Sobrien 529990075Sobrien XVECEXP (result, 0, i) 530090075Sobrien = gen_rtx_SET (VOIDmode, mem, gen_rtx_REG (SImode, base_regno + j)); 530190075Sobrien } 530290075Sobrien 530390075Sobrien return result; 530490075Sobrien} 530590075Sobrien 530690075Sobrienint 5307132718Skanarm_gen_movstrqi (rtx *operands) 530890075Sobrien{ 530990075Sobrien HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes; 531090075Sobrien int i; 531190075Sobrien rtx src, dst; 531290075Sobrien rtx st_src, st_dst, fin_src, fin_dst; 531390075Sobrien rtx part_bytes_reg = NULL; 531490075Sobrien rtx mem; 531590075Sobrien int dst_unchanging_p, dst_in_struct_p, src_unchanging_p, src_in_struct_p; 531690075Sobrien int dst_scalar_p, src_scalar_p; 531790075Sobrien 531890075Sobrien if (GET_CODE (operands[2]) != CONST_INT 531990075Sobrien || GET_CODE (operands[3]) != CONST_INT 532090075Sobrien || INTVAL (operands[2]) > 64 532190075Sobrien || INTVAL (operands[3]) & 3) 532290075Sobrien return 0; 532390075Sobrien 532490075Sobrien st_dst = XEXP (operands[0], 0); 532590075Sobrien st_src = XEXP (operands[1], 0); 532690075Sobrien 532790075Sobrien dst_unchanging_p = RTX_UNCHANGING_P (operands[0]); 532890075Sobrien dst_in_struct_p = MEM_IN_STRUCT_P (operands[0]); 532990075Sobrien dst_scalar_p = MEM_SCALAR_P (operands[0]); 533090075Sobrien src_unchanging_p = RTX_UNCHANGING_P (operands[1]); 533190075Sobrien src_in_struct_p = MEM_IN_STRUCT_P (operands[1]); 533290075Sobrien src_scalar_p = MEM_SCALAR_P (operands[1]); 533390075Sobrien 533490075Sobrien fin_dst = dst = copy_to_mode_reg (SImode, st_dst); 533590075Sobrien fin_src = src = copy_to_mode_reg (SImode, st_src); 533690075Sobrien 5337117395Skan in_words_to_go = ARM_NUM_INTS (INTVAL (operands[2])); 533890075Sobrien out_words_to_go = INTVAL (operands[2]) / 4; 533990075Sobrien last_bytes = INTVAL (operands[2]) & 3; 534090075Sobrien 534190075Sobrien if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0) 534290075Sobrien part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3); 534390075Sobrien 534490075Sobrien for (i = 0; in_words_to_go >= 2; i+=4) 534590075Sobrien { 534690075Sobrien if (in_words_to_go > 4) 534790075Sobrien emit_insn (arm_gen_load_multiple (0, 4, src, TRUE, TRUE, 534890075Sobrien src_unchanging_p, 534990075Sobrien src_in_struct_p, 535090075Sobrien src_scalar_p)); 535190075Sobrien else 535290075Sobrien emit_insn (arm_gen_load_multiple (0, in_words_to_go, src, TRUE, 535390075Sobrien FALSE, src_unchanging_p, 535490075Sobrien src_in_struct_p, src_scalar_p)); 535590075Sobrien 535690075Sobrien if (out_words_to_go) 535790075Sobrien { 535890075Sobrien if (out_words_to_go > 4) 535990075Sobrien emit_insn (arm_gen_store_multiple (0, 4, dst, TRUE, TRUE, 536090075Sobrien dst_unchanging_p, 536190075Sobrien dst_in_struct_p, 536290075Sobrien dst_scalar_p)); 536390075Sobrien else if (out_words_to_go != 1) 536490075Sobrien emit_insn (arm_gen_store_multiple (0, out_words_to_go, 536590075Sobrien dst, TRUE, 536690075Sobrien (last_bytes == 0 536790075Sobrien ? FALSE : TRUE), 536890075Sobrien dst_unchanging_p, 536990075Sobrien dst_in_struct_p, 537090075Sobrien dst_scalar_p)); 537190075Sobrien else 537290075Sobrien { 537390075Sobrien mem = gen_rtx_MEM (SImode, dst); 537490075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 537590075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 537690075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 537790075Sobrien emit_move_insn (mem, gen_rtx_REG (SImode, 0)); 537890075Sobrien if (last_bytes != 0) 537990075Sobrien emit_insn (gen_addsi3 (dst, dst, GEN_INT (4))); 538090075Sobrien } 538190075Sobrien } 538290075Sobrien 538390075Sobrien in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4; 538490075Sobrien out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4; 538590075Sobrien } 538690075Sobrien 538790075Sobrien /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do. */ 538890075Sobrien if (out_words_to_go) 538990075Sobrien { 539090075Sobrien rtx sreg; 539190075Sobrien 539290075Sobrien mem = gen_rtx_MEM (SImode, src); 539390075Sobrien RTX_UNCHANGING_P (mem) = src_unchanging_p; 539490075Sobrien MEM_IN_STRUCT_P (mem) = src_in_struct_p; 539590075Sobrien MEM_SCALAR_P (mem) = src_scalar_p; 539690075Sobrien emit_move_insn (sreg = gen_reg_rtx (SImode), mem); 539790075Sobrien emit_move_insn (fin_src = gen_reg_rtx (SImode), plus_constant (src, 4)); 539890075Sobrien 539990075Sobrien mem = gen_rtx_MEM (SImode, dst); 540090075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 540190075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 540290075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 540390075Sobrien emit_move_insn (mem, sreg); 540490075Sobrien emit_move_insn (fin_dst = gen_reg_rtx (SImode), plus_constant (dst, 4)); 540590075Sobrien in_words_to_go--; 540690075Sobrien 540790075Sobrien if (in_words_to_go) /* Sanity check */ 540890075Sobrien abort (); 540990075Sobrien } 541090075Sobrien 541190075Sobrien if (in_words_to_go) 541290075Sobrien { 541390075Sobrien if (in_words_to_go < 0) 541490075Sobrien abort (); 541590075Sobrien 541690075Sobrien mem = gen_rtx_MEM (SImode, src); 541790075Sobrien RTX_UNCHANGING_P (mem) = src_unchanging_p; 541890075Sobrien MEM_IN_STRUCT_P (mem) = src_in_struct_p; 541990075Sobrien MEM_SCALAR_P (mem) = src_scalar_p; 542090075Sobrien part_bytes_reg = copy_to_mode_reg (SImode, mem); 542190075Sobrien } 542290075Sobrien 542390075Sobrien if (last_bytes && part_bytes_reg == NULL) 542490075Sobrien abort (); 542590075Sobrien 542690075Sobrien if (BYTES_BIG_ENDIAN && last_bytes) 542790075Sobrien { 542890075Sobrien rtx tmp = gen_reg_rtx (SImode); 542990075Sobrien 543090075Sobrien /* The bytes we want are in the top end of the word. */ 543190075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, 543290075Sobrien GEN_INT (8 * (4 - last_bytes)))); 543390075Sobrien part_bytes_reg = tmp; 543490075Sobrien 543590075Sobrien while (last_bytes) 543690075Sobrien { 543790075Sobrien mem = gen_rtx_MEM (QImode, plus_constant (dst, last_bytes - 1)); 543890075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 543990075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 544090075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 5441102780Skan emit_move_insn (mem, gen_lowpart (QImode, part_bytes_reg)); 5442102780Skan 544390075Sobrien if (--last_bytes) 544490075Sobrien { 544590075Sobrien tmp = gen_reg_rtx (SImode); 544690075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8))); 544790075Sobrien part_bytes_reg = tmp; 544890075Sobrien } 544990075Sobrien } 545090075Sobrien 545190075Sobrien } 545290075Sobrien else 545390075Sobrien { 545490075Sobrien if (last_bytes > 1) 545590075Sobrien { 545690075Sobrien mem = gen_rtx_MEM (HImode, dst); 545790075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 545890075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 545990075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 5460102780Skan emit_move_insn (mem, gen_lowpart (HImode, part_bytes_reg)); 546190075Sobrien last_bytes -= 2; 546290075Sobrien if (last_bytes) 546390075Sobrien { 546490075Sobrien rtx tmp = gen_reg_rtx (SImode); 546590075Sobrien 546690075Sobrien emit_insn (gen_addsi3 (dst, dst, GEN_INT (2))); 546790075Sobrien emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (16))); 546890075Sobrien part_bytes_reg = tmp; 546990075Sobrien } 547090075Sobrien } 547190075Sobrien 547290075Sobrien if (last_bytes) 547390075Sobrien { 547490075Sobrien mem = gen_rtx_MEM (QImode, dst); 547590075Sobrien RTX_UNCHANGING_P (mem) = dst_unchanging_p; 547690075Sobrien MEM_IN_STRUCT_P (mem) = dst_in_struct_p; 547790075Sobrien MEM_SCALAR_P (mem) = dst_scalar_p; 5478102780Skan emit_move_insn (mem, gen_lowpart (QImode, part_bytes_reg)); 547990075Sobrien } 548090075Sobrien } 548190075Sobrien 548290075Sobrien return 1; 548390075Sobrien} 548490075Sobrien 548590075Sobrien/* Generate a memory reference for a half word, such that it will be loaded 548690075Sobrien into the top 16 bits of the word. We can assume that the address is 548790075Sobrien known to be alignable and of the form reg, or plus (reg, const). */ 548890075Sobrien 548990075Sobrienrtx 5490132718Skanarm_gen_rotated_half_load (rtx memref) 549190075Sobrien{ 549290075Sobrien HOST_WIDE_INT offset = 0; 549390075Sobrien rtx base = XEXP (memref, 0); 549490075Sobrien 549590075Sobrien if (GET_CODE (base) == PLUS) 549690075Sobrien { 549790075Sobrien offset = INTVAL (XEXP (base, 1)); 549890075Sobrien base = XEXP (base, 0); 549990075Sobrien } 550090075Sobrien 550190075Sobrien /* If we aren't allowed to generate unaligned addresses, then fail. */ 550290075Sobrien if (TARGET_MMU_TRAPS 550390075Sobrien && ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 0))) 550490075Sobrien return NULL; 550590075Sobrien 550690075Sobrien base = gen_rtx_MEM (SImode, plus_constant (base, offset & ~2)); 550790075Sobrien 550890075Sobrien if ((BYTES_BIG_ENDIAN ? 1 : 0) ^ ((offset & 2) == 2)) 550990075Sobrien return base; 551090075Sobrien 551190075Sobrien return gen_rtx_ROTATE (SImode, base, GEN_INT (16)); 551290075Sobrien} 551390075Sobrien 5514132718Skan/* Select a dominance comparison mode if possible for a test of the general 5515132718Skan form (OP (COND_OR (X) (Y)) (const_int 0)). We support three forms. 5516132718Skan COND_OR == DOM_CC_X_AND_Y => (X && Y) 5517132718Skan COND_OR == DOM_CC_NX_OR_Y => ((! X) || Y) 5518132718Skan COND_OR == DOM_CC_X_OR_Y => (X || Y) 5519132718Skan In all cases OP will be either EQ or NE, but we don't need to know which 5520132718Skan here. If we are unable to support a dominance comparison we return 5521132718Skan CC mode. This will then fail to match for the RTL expressions that 5522132718Skan generate this call. */ 5523132718Skanenum machine_mode 5524132718Skanarm_select_dominance_cc_mode (rtx x, rtx y, HOST_WIDE_INT cond_or) 552590075Sobrien{ 552690075Sobrien enum rtx_code cond1, cond2; 552790075Sobrien int swapped = 0; 552890075Sobrien 552990075Sobrien /* Currently we will probably get the wrong result if the individual 553090075Sobrien comparisons are not simple. This also ensures that it is safe to 553190075Sobrien reverse a comparison if necessary. */ 553290075Sobrien if ((arm_select_cc_mode (cond1 = GET_CODE (x), XEXP (x, 0), XEXP (x, 1)) 553390075Sobrien != CCmode) 553490075Sobrien || (arm_select_cc_mode (cond2 = GET_CODE (y), XEXP (y, 0), XEXP (y, 1)) 553590075Sobrien != CCmode)) 553690075Sobrien return CCmode; 553790075Sobrien 553890075Sobrien /* The if_then_else variant of this tests the second condition if the 553990075Sobrien first passes, but is true if the first fails. Reverse the first 554090075Sobrien condition to get a true "inclusive-or" expression. */ 5541132718Skan if (cond_or == DOM_CC_NX_OR_Y) 554290075Sobrien cond1 = reverse_condition (cond1); 554390075Sobrien 554490075Sobrien /* If the comparisons are not equal, and one doesn't dominate the other, 554590075Sobrien then we can't do this. */ 554690075Sobrien if (cond1 != cond2 554790075Sobrien && !comparison_dominates_p (cond1, cond2) 554890075Sobrien && (swapped = 1, !comparison_dominates_p (cond2, cond1))) 554990075Sobrien return CCmode; 555090075Sobrien 555190075Sobrien if (swapped) 555290075Sobrien { 555390075Sobrien enum rtx_code temp = cond1; 555490075Sobrien cond1 = cond2; 555590075Sobrien cond2 = temp; 555690075Sobrien } 555790075Sobrien 555890075Sobrien switch (cond1) 555990075Sobrien { 556090075Sobrien case EQ: 5561132718Skan if (cond2 == EQ || cond_or == DOM_CC_X_AND_Y) 556290075Sobrien return CC_DEQmode; 556390075Sobrien 556490075Sobrien switch (cond2) 556590075Sobrien { 556690075Sobrien case LE: return CC_DLEmode; 556790075Sobrien case LEU: return CC_DLEUmode; 556890075Sobrien case GE: return CC_DGEmode; 556990075Sobrien case GEU: return CC_DGEUmode; 557090075Sobrien default: break; 557190075Sobrien } 557290075Sobrien 557390075Sobrien break; 557490075Sobrien 557590075Sobrien case LT: 5576132718Skan if (cond2 == LT || cond_or == DOM_CC_X_AND_Y) 557790075Sobrien return CC_DLTmode; 557890075Sobrien if (cond2 == LE) 557990075Sobrien return CC_DLEmode; 558090075Sobrien if (cond2 == NE) 558190075Sobrien return CC_DNEmode; 558290075Sobrien break; 558390075Sobrien 558490075Sobrien case GT: 5585132718Skan if (cond2 == GT || cond_or == DOM_CC_X_AND_Y) 558690075Sobrien return CC_DGTmode; 558790075Sobrien if (cond2 == GE) 558890075Sobrien return CC_DGEmode; 558990075Sobrien if (cond2 == NE) 559090075Sobrien return CC_DNEmode; 559190075Sobrien break; 559290075Sobrien 559390075Sobrien case LTU: 5594132718Skan if (cond2 == LTU || cond_or == DOM_CC_X_AND_Y) 559590075Sobrien return CC_DLTUmode; 559690075Sobrien if (cond2 == LEU) 559790075Sobrien return CC_DLEUmode; 559890075Sobrien if (cond2 == NE) 559990075Sobrien return CC_DNEmode; 560090075Sobrien break; 560190075Sobrien 560290075Sobrien case GTU: 5603132718Skan if (cond2 == GTU || cond_or == DOM_CC_X_AND_Y) 560490075Sobrien return CC_DGTUmode; 560590075Sobrien if (cond2 == GEU) 560690075Sobrien return CC_DGEUmode; 560790075Sobrien if (cond2 == NE) 560890075Sobrien return CC_DNEmode; 560990075Sobrien break; 561090075Sobrien 561190075Sobrien /* The remaining cases only occur when both comparisons are the 561290075Sobrien same. */ 561390075Sobrien case NE: 561490075Sobrien return CC_DNEmode; 561590075Sobrien 561690075Sobrien case LE: 561790075Sobrien return CC_DLEmode; 561890075Sobrien 561990075Sobrien case GE: 562090075Sobrien return CC_DGEmode; 562190075Sobrien 562290075Sobrien case LEU: 562390075Sobrien return CC_DLEUmode; 562490075Sobrien 562590075Sobrien case GEU: 562690075Sobrien return CC_DGEUmode; 562790075Sobrien 562890075Sobrien default: 562990075Sobrien break; 563090075Sobrien } 563190075Sobrien 563290075Sobrien abort (); 563390075Sobrien} 563490075Sobrien 563590075Sobrienenum machine_mode 5636132718Skanarm_select_cc_mode (enum rtx_code op, rtx x, rtx y) 563790075Sobrien{ 563890075Sobrien /* All floating point compares return CCFP if it is an equality 563990075Sobrien comparison, and CCFPE otherwise. */ 564090075Sobrien if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) 564190075Sobrien { 564290075Sobrien switch (op) 564390075Sobrien { 564490075Sobrien case EQ: 564590075Sobrien case NE: 564690075Sobrien case UNORDERED: 564790075Sobrien case ORDERED: 564890075Sobrien case UNLT: 564990075Sobrien case UNLE: 565090075Sobrien case UNGT: 565190075Sobrien case UNGE: 565290075Sobrien case UNEQ: 565390075Sobrien case LTGT: 565490075Sobrien return CCFPmode; 565590075Sobrien 565690075Sobrien case LT: 565790075Sobrien case LE: 565890075Sobrien case GT: 565990075Sobrien case GE: 5660132718Skan if (TARGET_CIRRUS) 5661132718Skan return CCFPmode; 566290075Sobrien return CCFPEmode; 566390075Sobrien 566490075Sobrien default: 566590075Sobrien abort (); 566690075Sobrien } 566790075Sobrien } 566890075Sobrien 566990075Sobrien /* A compare with a shifted operand. Because of canonicalization, the 567090075Sobrien comparison will have to be swapped when we emit the assembler. */ 567190075Sobrien if (GET_MODE (y) == SImode && GET_CODE (y) == REG 567290075Sobrien && (GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT 567390075Sobrien || GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ROTATE 567490075Sobrien || GET_CODE (x) == ROTATERT)) 567590075Sobrien return CC_SWPmode; 567690075Sobrien 567790075Sobrien /* This is a special case that is used by combine to allow a 567890075Sobrien comparison of a shifted byte load to be split into a zero-extend 567990075Sobrien followed by a comparison of the shifted integer (only valid for 568090075Sobrien equalities and unsigned inequalities). */ 568190075Sobrien if (GET_MODE (x) == SImode 568290075Sobrien && GET_CODE (x) == ASHIFT 568390075Sobrien && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 24 568490075Sobrien && GET_CODE (XEXP (x, 0)) == SUBREG 568590075Sobrien && GET_CODE (SUBREG_REG (XEXP (x, 0))) == MEM 568690075Sobrien && GET_MODE (SUBREG_REG (XEXP (x, 0))) == QImode 568790075Sobrien && (op == EQ || op == NE 568890075Sobrien || op == GEU || op == GTU || op == LTU || op == LEU) 568990075Sobrien && GET_CODE (y) == CONST_INT) 569090075Sobrien return CC_Zmode; 569190075Sobrien 569290075Sobrien /* A construct for a conditional compare, if the false arm contains 569390075Sobrien 0, then both conditions must be true, otherwise either condition 569490075Sobrien must be true. Not all conditions are possible, so CCmode is 569590075Sobrien returned if it can't be done. */ 569690075Sobrien if (GET_CODE (x) == IF_THEN_ELSE 569790075Sobrien && (XEXP (x, 2) == const0_rtx 569890075Sobrien || XEXP (x, 2) == const1_rtx) 569990075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 570090075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 5701132718Skan return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 5702132718Skan INTVAL (XEXP (x, 2))); 570390075Sobrien 570490075Sobrien /* Alternate canonicalizations of the above. These are somewhat cleaner. */ 570590075Sobrien if (GET_CODE (x) == AND 570690075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 570790075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 5708132718Skan return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 5709132718Skan DOM_CC_X_AND_Y); 571090075Sobrien 571190075Sobrien if (GET_CODE (x) == IOR 571290075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' 571390075Sobrien && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<') 5714132718Skan return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 5715132718Skan DOM_CC_X_OR_Y); 571690075Sobrien 5717132718Skan /* An operation (on Thumb) where we want to test for a single bit. 5718132718Skan This is done by shifting that bit up into the top bit of a 5719132718Skan scratch register; we can then branch on the sign bit. */ 5720132718Skan if (TARGET_THUMB 5721132718Skan && GET_MODE (x) == SImode 5722132718Skan && (op == EQ || op == NE) 5723132718Skan && (GET_CODE (x) == ZERO_EXTRACT)) 5724132718Skan return CC_Nmode; 5725132718Skan 572690075Sobrien /* An operation that sets the condition codes as a side-effect, the 572790075Sobrien V flag is not set correctly, so we can only use comparisons where 572890075Sobrien this doesn't matter. (For LT and GE we can use "mi" and "pl" 5729132718Skan instead.) */ 573090075Sobrien if (GET_MODE (x) == SImode 573190075Sobrien && y == const0_rtx 573290075Sobrien && (op == EQ || op == NE || op == LT || op == GE) 573390075Sobrien && (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS 573490075Sobrien || GET_CODE (x) == AND || GET_CODE (x) == IOR 573590075Sobrien || GET_CODE (x) == XOR || GET_CODE (x) == MULT 573690075Sobrien || GET_CODE (x) == NOT || GET_CODE (x) == NEG 573790075Sobrien || GET_CODE (x) == LSHIFTRT 573890075Sobrien || GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT 5739132718Skan || GET_CODE (x) == ROTATERT 5740132718Skan || (TARGET_ARM && GET_CODE (x) == ZERO_EXTRACT))) 574190075Sobrien return CC_NOOVmode; 574290075Sobrien 574390075Sobrien if (GET_MODE (x) == QImode && (op == EQ || op == NE)) 574490075Sobrien return CC_Zmode; 574590075Sobrien 574690075Sobrien if (GET_MODE (x) == SImode && (op == LTU || op == GEU) 574790075Sobrien && GET_CODE (x) == PLUS 574890075Sobrien && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y))) 574990075Sobrien return CC_Cmode; 575090075Sobrien 575190075Sobrien return CCmode; 575290075Sobrien} 575390075Sobrien 575490075Sobrien/* X and Y are two things to compare using CODE. Emit the compare insn and 575590075Sobrien return the rtx for register 0 in the proper mode. FP means this is a 575690075Sobrien floating point compare: I don't think that it is needed on the arm. */ 575790075Sobrienrtx 5758132718Skanarm_gen_compare_reg (enum rtx_code code, rtx x, rtx y) 575990075Sobrien{ 576090075Sobrien enum machine_mode mode = SELECT_CC_MODE (code, x, y); 576190075Sobrien rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM); 576290075Sobrien 576390075Sobrien emit_insn (gen_rtx_SET (VOIDmode, cc_reg, 576490075Sobrien gen_rtx_COMPARE (mode, x, y))); 576590075Sobrien 576690075Sobrien return cc_reg; 576790075Sobrien} 576890075Sobrien 5769117395Skan/* Generate a sequence of insns that will generate the correct return 5770117395Skan address mask depending on the physical architecture that the program 5771117395Skan is running on. */ 5772117395Skanrtx 5773132718Skanarm_gen_return_addr_mask (void) 5774117395Skan{ 5775117395Skan rtx reg = gen_reg_rtx (Pmode); 5776117395Skan 5777117395Skan emit_insn (gen_return_addr_mask (reg)); 5778117395Skan return reg; 5779117395Skan} 5780117395Skan 578190075Sobrienvoid 5782132718Skanarm_reload_in_hi (rtx *operands) 578390075Sobrien{ 578490075Sobrien rtx ref = operands[1]; 578590075Sobrien rtx base, scratch; 578690075Sobrien HOST_WIDE_INT offset = 0; 578790075Sobrien 578890075Sobrien if (GET_CODE (ref) == SUBREG) 578990075Sobrien { 579090075Sobrien offset = SUBREG_BYTE (ref); 579190075Sobrien ref = SUBREG_REG (ref); 579290075Sobrien } 579390075Sobrien 579490075Sobrien if (GET_CODE (ref) == REG) 579590075Sobrien { 579690075Sobrien /* We have a pseudo which has been spilt onto the stack; there 579790075Sobrien are two cases here: the first where there is a simple 579890075Sobrien stack-slot replacement and a second where the stack-slot is 579990075Sobrien out of range, or is used as a subreg. */ 580090075Sobrien if (reg_equiv_mem[REGNO (ref)]) 580190075Sobrien { 580290075Sobrien ref = reg_equiv_mem[REGNO (ref)]; 580390075Sobrien base = find_replacement (&XEXP (ref, 0)); 580490075Sobrien } 580590075Sobrien else 580690075Sobrien /* The slot is out of range, or was dressed up in a SUBREG. */ 580790075Sobrien base = reg_equiv_address[REGNO (ref)]; 580890075Sobrien } 580990075Sobrien else 581090075Sobrien base = find_replacement (&XEXP (ref, 0)); 581190075Sobrien 581290075Sobrien /* Handle the case where the address is too complex to be offset by 1. */ 581390075Sobrien if (GET_CODE (base) == MINUS 581490075Sobrien || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) 581590075Sobrien { 581690075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 581790075Sobrien 581890075Sobrien emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); 581990075Sobrien base = base_plus; 582090075Sobrien } 582190075Sobrien else if (GET_CODE (base) == PLUS) 582290075Sobrien { 582390075Sobrien /* The addend must be CONST_INT, or we would have dealt with it above. */ 582490075Sobrien HOST_WIDE_INT hi, lo; 582590075Sobrien 582690075Sobrien offset += INTVAL (XEXP (base, 1)); 582790075Sobrien base = XEXP (base, 0); 582890075Sobrien 582990075Sobrien /* Rework the address into a legal sequence of insns. */ 583090075Sobrien /* Valid range for lo is -4095 -> 4095 */ 583190075Sobrien lo = (offset >= 0 583290075Sobrien ? (offset & 0xfff) 583390075Sobrien : -((-offset) & 0xfff)); 583490075Sobrien 583590075Sobrien /* Corner case, if lo is the max offset then we would be out of range 583690075Sobrien once we have added the additional 1 below, so bump the msb into the 583790075Sobrien pre-loading insn(s). */ 583890075Sobrien if (lo == 4095) 583990075Sobrien lo &= 0x7ff; 584090075Sobrien 584190075Sobrien hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) 584290075Sobrien ^ (HOST_WIDE_INT) 0x80000000) 584390075Sobrien - (HOST_WIDE_INT) 0x80000000); 584490075Sobrien 584590075Sobrien if (hi + lo != offset) 584690075Sobrien abort (); 584790075Sobrien 584890075Sobrien if (hi != 0) 584990075Sobrien { 585090075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 585190075Sobrien 585290075Sobrien /* Get the base address; addsi3 knows how to handle constants 585390075Sobrien that require more than one insn. */ 585490075Sobrien emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); 585590075Sobrien base = base_plus; 585690075Sobrien offset = lo; 585790075Sobrien } 585890075Sobrien } 585990075Sobrien 5860117395Skan /* Operands[2] may overlap operands[0] (though it won't overlap 5861117395Skan operands[1]), that's why we asked for a DImode reg -- so we can 5862117395Skan use the bit that does not overlap. */ 5863117395Skan if (REGNO (operands[2]) == REGNO (operands[0])) 5864117395Skan scratch = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 5865117395Skan else 5866117395Skan scratch = gen_rtx_REG (SImode, REGNO (operands[2])); 5867117395Skan 586890075Sobrien emit_insn (gen_zero_extendqisi2 (scratch, 586990075Sobrien gen_rtx_MEM (QImode, 587090075Sobrien plus_constant (base, 587190075Sobrien offset)))); 587290075Sobrien emit_insn (gen_zero_extendqisi2 (gen_rtx_SUBREG (SImode, operands[0], 0), 587390075Sobrien gen_rtx_MEM (QImode, 587490075Sobrien plus_constant (base, 587590075Sobrien offset + 1)))); 587690075Sobrien if (!BYTES_BIG_ENDIAN) 587790075Sobrien emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), 587890075Sobrien gen_rtx_IOR (SImode, 587990075Sobrien gen_rtx_ASHIFT 588090075Sobrien (SImode, 588190075Sobrien gen_rtx_SUBREG (SImode, operands[0], 0), 588290075Sobrien GEN_INT (8)), 588390075Sobrien scratch))); 588490075Sobrien else 588590075Sobrien emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0), 588690075Sobrien gen_rtx_IOR (SImode, 588790075Sobrien gen_rtx_ASHIFT (SImode, scratch, 588890075Sobrien GEN_INT (8)), 588990075Sobrien gen_rtx_SUBREG (SImode, operands[0], 589090075Sobrien 0)))); 589190075Sobrien} 589290075Sobrien 5893132718Skan/* Handle storing a half-word to memory during reload by synthesizing as two 589490075Sobrien byte stores. Take care not to clobber the input values until after we 589590075Sobrien have moved them somewhere safe. This code assumes that if the DImode 589690075Sobrien scratch in operands[2] overlaps either the input value or output address 589790075Sobrien in some way, then that value must die in this insn (we absolutely need 589890075Sobrien two scratch registers for some corner cases). */ 589990075Sobrienvoid 5900132718Skanarm_reload_out_hi (rtx *operands) 590190075Sobrien{ 590290075Sobrien rtx ref = operands[0]; 590390075Sobrien rtx outval = operands[1]; 590490075Sobrien rtx base, scratch; 590590075Sobrien HOST_WIDE_INT offset = 0; 590690075Sobrien 590790075Sobrien if (GET_CODE (ref) == SUBREG) 590890075Sobrien { 590990075Sobrien offset = SUBREG_BYTE (ref); 591090075Sobrien ref = SUBREG_REG (ref); 591190075Sobrien } 591290075Sobrien 591390075Sobrien if (GET_CODE (ref) == REG) 591490075Sobrien { 591590075Sobrien /* We have a pseudo which has been spilt onto the stack; there 591690075Sobrien are two cases here: the first where there is a simple 591790075Sobrien stack-slot replacement and a second where the stack-slot is 591890075Sobrien out of range, or is used as a subreg. */ 591990075Sobrien if (reg_equiv_mem[REGNO (ref)]) 592090075Sobrien { 592190075Sobrien ref = reg_equiv_mem[REGNO (ref)]; 592290075Sobrien base = find_replacement (&XEXP (ref, 0)); 592390075Sobrien } 592490075Sobrien else 592590075Sobrien /* The slot is out of range, or was dressed up in a SUBREG. */ 592690075Sobrien base = reg_equiv_address[REGNO (ref)]; 592790075Sobrien } 592890075Sobrien else 592990075Sobrien base = find_replacement (&XEXP (ref, 0)); 593090075Sobrien 593190075Sobrien scratch = gen_rtx_REG (SImode, REGNO (operands[2])); 593290075Sobrien 593390075Sobrien /* Handle the case where the address is too complex to be offset by 1. */ 593490075Sobrien if (GET_CODE (base) == MINUS 593590075Sobrien || (GET_CODE (base) == PLUS && GET_CODE (XEXP (base, 1)) != CONST_INT)) 593690075Sobrien { 593790075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 593890075Sobrien 593990075Sobrien /* Be careful not to destroy OUTVAL. */ 594090075Sobrien if (reg_overlap_mentioned_p (base_plus, outval)) 594190075Sobrien { 594290075Sobrien /* Updating base_plus might destroy outval, see if we can 594390075Sobrien swap the scratch and base_plus. */ 594490075Sobrien if (!reg_overlap_mentioned_p (scratch, outval)) 594590075Sobrien { 594690075Sobrien rtx tmp = scratch; 594790075Sobrien scratch = base_plus; 594890075Sobrien base_plus = tmp; 594990075Sobrien } 595090075Sobrien else 595190075Sobrien { 595290075Sobrien rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); 595390075Sobrien 595490075Sobrien /* Be conservative and copy OUTVAL into the scratch now, 595590075Sobrien this should only be necessary if outval is a subreg 595690075Sobrien of something larger than a word. */ 595790075Sobrien /* XXX Might this clobber base? I can't see how it can, 595890075Sobrien since scratch is known to overlap with OUTVAL, and 595990075Sobrien must be wider than a word. */ 596090075Sobrien emit_insn (gen_movhi (scratch_hi, outval)); 596190075Sobrien outval = scratch_hi; 596290075Sobrien } 596390075Sobrien } 596490075Sobrien 596590075Sobrien emit_insn (gen_rtx_SET (VOIDmode, base_plus, base)); 596690075Sobrien base = base_plus; 596790075Sobrien } 596890075Sobrien else if (GET_CODE (base) == PLUS) 596990075Sobrien { 597090075Sobrien /* The addend must be CONST_INT, or we would have dealt with it above. */ 597190075Sobrien HOST_WIDE_INT hi, lo; 597290075Sobrien 597390075Sobrien offset += INTVAL (XEXP (base, 1)); 597490075Sobrien base = XEXP (base, 0); 597590075Sobrien 597690075Sobrien /* Rework the address into a legal sequence of insns. */ 597790075Sobrien /* Valid range for lo is -4095 -> 4095 */ 597890075Sobrien lo = (offset >= 0 597990075Sobrien ? (offset & 0xfff) 598090075Sobrien : -((-offset) & 0xfff)); 598190075Sobrien 598290075Sobrien /* Corner case, if lo is the max offset then we would be out of range 598390075Sobrien once we have added the additional 1 below, so bump the msb into the 598490075Sobrien pre-loading insn(s). */ 598590075Sobrien if (lo == 4095) 598690075Sobrien lo &= 0x7ff; 598790075Sobrien 598890075Sobrien hi = ((((offset - lo) & (HOST_WIDE_INT) 0xffffffff) 598990075Sobrien ^ (HOST_WIDE_INT) 0x80000000) 599090075Sobrien - (HOST_WIDE_INT) 0x80000000); 599190075Sobrien 599290075Sobrien if (hi + lo != offset) 599390075Sobrien abort (); 599490075Sobrien 599590075Sobrien if (hi != 0) 599690075Sobrien { 599790075Sobrien rtx base_plus = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); 599890075Sobrien 599990075Sobrien /* Be careful not to destroy OUTVAL. */ 600090075Sobrien if (reg_overlap_mentioned_p (base_plus, outval)) 600190075Sobrien { 600290075Sobrien /* Updating base_plus might destroy outval, see if we 600390075Sobrien can swap the scratch and base_plus. */ 600490075Sobrien if (!reg_overlap_mentioned_p (scratch, outval)) 600590075Sobrien { 600690075Sobrien rtx tmp = scratch; 600790075Sobrien scratch = base_plus; 600890075Sobrien base_plus = tmp; 600990075Sobrien } 601090075Sobrien else 601190075Sobrien { 601290075Sobrien rtx scratch_hi = gen_rtx_REG (HImode, REGNO (operands[2])); 601390075Sobrien 601490075Sobrien /* Be conservative and copy outval into scratch now, 601590075Sobrien this should only be necessary if outval is a 601690075Sobrien subreg of something larger than a word. */ 601790075Sobrien /* XXX Might this clobber base? I can't see how it 601890075Sobrien can, since scratch is known to overlap with 601990075Sobrien outval. */ 602090075Sobrien emit_insn (gen_movhi (scratch_hi, outval)); 602190075Sobrien outval = scratch_hi; 602290075Sobrien } 602390075Sobrien } 602490075Sobrien 602590075Sobrien /* Get the base address; addsi3 knows how to handle constants 602690075Sobrien that require more than one insn. */ 602790075Sobrien emit_insn (gen_addsi3 (base_plus, base, GEN_INT (hi))); 602890075Sobrien base = base_plus; 602990075Sobrien offset = lo; 603090075Sobrien } 603190075Sobrien } 603290075Sobrien 603390075Sobrien if (BYTES_BIG_ENDIAN) 603490075Sobrien { 603590075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, 603690075Sobrien plus_constant (base, offset + 1)), 6037102780Skan gen_lowpart (QImode, outval))); 603890075Sobrien emit_insn (gen_lshrsi3 (scratch, 603990075Sobrien gen_rtx_SUBREG (SImode, outval, 0), 604090075Sobrien GEN_INT (8))); 604190075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), 6042102780Skan gen_lowpart (QImode, scratch))); 604390075Sobrien } 604490075Sobrien else 604590075Sobrien { 604690075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (base, offset)), 6047102780Skan gen_lowpart (QImode, outval))); 604890075Sobrien emit_insn (gen_lshrsi3 (scratch, 604990075Sobrien gen_rtx_SUBREG (SImode, outval, 0), 605090075Sobrien GEN_INT (8))); 605190075Sobrien emit_insn (gen_movqi (gen_rtx_MEM (QImode, 605290075Sobrien plus_constant (base, offset + 1)), 6053102780Skan gen_lowpart (QImode, scratch))); 605490075Sobrien } 605590075Sobrien} 605690075Sobrien 605790075Sobrien/* Print a symbolic form of X to the debug file, F. */ 605890075Sobrienstatic void 6059132718Skanarm_print_value (FILE *f, rtx x) 606090075Sobrien{ 606190075Sobrien switch (GET_CODE (x)) 606290075Sobrien { 606390075Sobrien case CONST_INT: 606490075Sobrien fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x)); 606590075Sobrien return; 606690075Sobrien 606790075Sobrien case CONST_DOUBLE: 606890075Sobrien fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3)); 606990075Sobrien return; 607090075Sobrien 6071132718Skan case CONST_VECTOR: 6072132718Skan { 6073132718Skan int i; 6074132718Skan 6075132718Skan fprintf (f, "<"); 6076132718Skan for (i = 0; i < CONST_VECTOR_NUNITS (x); i++) 6077132718Skan { 6078132718Skan fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (CONST_VECTOR_ELT (x, i))); 6079132718Skan if (i < (CONST_VECTOR_NUNITS (x) - 1)) 6080132718Skan fputc (',', f); 6081132718Skan } 6082132718Skan fprintf (f, ">"); 6083132718Skan } 6084132718Skan return; 6085132718Skan 608690075Sobrien case CONST_STRING: 608790075Sobrien fprintf (f, "\"%s\"", XSTR (x, 0)); 608890075Sobrien return; 608990075Sobrien 609090075Sobrien case SYMBOL_REF: 609190075Sobrien fprintf (f, "`%s'", XSTR (x, 0)); 609290075Sobrien return; 609390075Sobrien 609490075Sobrien case LABEL_REF: 609590075Sobrien fprintf (f, "L%d", INSN_UID (XEXP (x, 0))); 609690075Sobrien return; 609790075Sobrien 609890075Sobrien case CONST: 609990075Sobrien arm_print_value (f, XEXP (x, 0)); 610090075Sobrien return; 610190075Sobrien 610290075Sobrien case PLUS: 610390075Sobrien arm_print_value (f, XEXP (x, 0)); 610490075Sobrien fprintf (f, "+"); 610590075Sobrien arm_print_value (f, XEXP (x, 1)); 610690075Sobrien return; 610790075Sobrien 610890075Sobrien case PC: 610990075Sobrien fprintf (f, "pc"); 611090075Sobrien return; 611190075Sobrien 611290075Sobrien default: 611390075Sobrien fprintf (f, "????"); 611490075Sobrien return; 611590075Sobrien } 611690075Sobrien} 611790075Sobrien 611890075Sobrien/* Routines for manipulation of the constant pool. */ 611990075Sobrien 612090075Sobrien/* Arm instructions cannot load a large constant directly into a 612190075Sobrien register; they have to come from a pc relative load. The constant 612290075Sobrien must therefore be placed in the addressable range of the pc 612390075Sobrien relative load. Depending on the precise pc relative load 612490075Sobrien instruction the range is somewhere between 256 bytes and 4k. This 612590075Sobrien means that we often have to dump a constant inside a function, and 612690075Sobrien generate code to branch around it. 612790075Sobrien 612890075Sobrien It is important to minimize this, since the branches will slow 612990075Sobrien things down and make the code larger. 613090075Sobrien 613190075Sobrien Normally we can hide the table after an existing unconditional 613290075Sobrien branch so that there is no interruption of the flow, but in the 613390075Sobrien worst case the code looks like this: 613490075Sobrien 613590075Sobrien ldr rn, L1 613690075Sobrien ... 613790075Sobrien b L2 613890075Sobrien align 613990075Sobrien L1: .long value 614090075Sobrien L2: 614190075Sobrien ... 614290075Sobrien 614390075Sobrien ldr rn, L3 614490075Sobrien ... 614590075Sobrien b L4 614690075Sobrien align 614790075Sobrien L3: .long value 614890075Sobrien L4: 614990075Sobrien ... 615090075Sobrien 615190075Sobrien We fix this by performing a scan after scheduling, which notices 615290075Sobrien which instructions need to have their operands fetched from the 615390075Sobrien constant table and builds the table. 615490075Sobrien 615590075Sobrien The algorithm starts by building a table of all the constants that 615690075Sobrien need fixing up and all the natural barriers in the function (places 615790075Sobrien where a constant table can be dropped without breaking the flow). 615890075Sobrien For each fixup we note how far the pc-relative replacement will be 615990075Sobrien able to reach and the offset of the instruction into the function. 616090075Sobrien 616190075Sobrien Having built the table we then group the fixes together to form 616290075Sobrien tables that are as large as possible (subject to addressing 616390075Sobrien constraints) and emit each table of constants after the last 616490075Sobrien barrier that is within range of all the instructions in the group. 616590075Sobrien If a group does not contain a barrier, then we forcibly create one 616690075Sobrien by inserting a jump instruction into the flow. Once the table has 616790075Sobrien been inserted, the insns are then modified to reference the 616890075Sobrien relevant entry in the pool. 616990075Sobrien 617090075Sobrien Possible enhancements to the algorithm (not implemented) are: 617190075Sobrien 617290075Sobrien 1) For some processors and object formats, there may be benefit in 617390075Sobrien aligning the pools to the start of cache lines; this alignment 617490075Sobrien would need to be taken into account when calculating addressability 617590075Sobrien of a pool. */ 617690075Sobrien 617790075Sobrien/* These typedefs are located at the start of this file, so that 617890075Sobrien they can be used in the prototypes there. This comment is to 617990075Sobrien remind readers of that fact so that the following structures 618090075Sobrien can be understood more easily. 618190075Sobrien 618290075Sobrien typedef struct minipool_node Mnode; 618390075Sobrien typedef struct minipool_fixup Mfix; */ 618490075Sobrien 618590075Sobrienstruct minipool_node 618690075Sobrien{ 618790075Sobrien /* Doubly linked chain of entries. */ 618890075Sobrien Mnode * next; 618990075Sobrien Mnode * prev; 619090075Sobrien /* The maximum offset into the code that this entry can be placed. While 619190075Sobrien pushing fixes for forward references, all entries are sorted in order 619290075Sobrien of increasing max_address. */ 619390075Sobrien HOST_WIDE_INT max_address; 619490075Sobrien /* Similarly for an entry inserted for a backwards ref. */ 619590075Sobrien HOST_WIDE_INT min_address; 619690075Sobrien /* The number of fixes referencing this entry. This can become zero 619790075Sobrien if we "unpush" an entry. In this case we ignore the entry when we 619890075Sobrien come to emit the code. */ 619990075Sobrien int refcount; 620090075Sobrien /* The offset from the start of the minipool. */ 620190075Sobrien HOST_WIDE_INT offset; 620290075Sobrien /* The value in table. */ 620390075Sobrien rtx value; 620490075Sobrien /* The mode of value. */ 620590075Sobrien enum machine_mode mode; 6206132718Skan /* The size of the value. With iWMMXt enabled 6207132718Skan sizes > 4 also imply an alignment of 8-bytes. */ 620890075Sobrien int fix_size; 620990075Sobrien}; 621090075Sobrien 621190075Sobrienstruct minipool_fixup 621290075Sobrien{ 621390075Sobrien Mfix * next; 621490075Sobrien rtx insn; 621590075Sobrien HOST_WIDE_INT address; 621690075Sobrien rtx * loc; 621790075Sobrien enum machine_mode mode; 621890075Sobrien int fix_size; 621990075Sobrien rtx value; 622090075Sobrien Mnode * minipool; 622190075Sobrien HOST_WIDE_INT forwards; 622290075Sobrien HOST_WIDE_INT backwards; 622390075Sobrien}; 622490075Sobrien 622590075Sobrien/* Fixes less than a word need padding out to a word boundary. */ 622690075Sobrien#define MINIPOOL_FIX_SIZE(mode) \ 622790075Sobrien (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4) 622890075Sobrien 622990075Sobrienstatic Mnode * minipool_vector_head; 623090075Sobrienstatic Mnode * minipool_vector_tail; 623190075Sobrienstatic rtx minipool_vector_label; 623290075Sobrien 623390075Sobrien/* The linked list of all minipool fixes required for this function. */ 623490075SobrienMfix * minipool_fix_head; 623590075SobrienMfix * minipool_fix_tail; 623690075Sobrien/* The fix entry for the current minipool, once it has been placed. */ 623790075SobrienMfix * minipool_barrier; 623890075Sobrien 623990075Sobrien/* Determines if INSN is the start of a jump table. Returns the end 624090075Sobrien of the TABLE or NULL_RTX. */ 624190075Sobrienstatic rtx 6242132718Skanis_jump_table (rtx insn) 624390075Sobrien{ 624490075Sobrien rtx table; 624590075Sobrien 624690075Sobrien if (GET_CODE (insn) == JUMP_INSN 624790075Sobrien && JUMP_LABEL (insn) != NULL 624890075Sobrien && ((table = next_real_insn (JUMP_LABEL (insn))) 624990075Sobrien == next_real_insn (insn)) 625090075Sobrien && table != NULL 625190075Sobrien && GET_CODE (table) == JUMP_INSN 625290075Sobrien && (GET_CODE (PATTERN (table)) == ADDR_VEC 625390075Sobrien || GET_CODE (PATTERN (table)) == ADDR_DIFF_VEC)) 625490075Sobrien return table; 625590075Sobrien 625690075Sobrien return NULL_RTX; 625790075Sobrien} 625890075Sobrien 625996263Sobrien#ifndef JUMP_TABLES_IN_TEXT_SECTION 626096263Sobrien#define JUMP_TABLES_IN_TEXT_SECTION 0 626196263Sobrien#endif 626296263Sobrien 626390075Sobrienstatic HOST_WIDE_INT 6264132718Skanget_jump_table_size (rtx insn) 626590075Sobrien{ 626696263Sobrien /* ADDR_VECs only take room if read-only data does into the text 626796263Sobrien section. */ 626896263Sobrien if (JUMP_TABLES_IN_TEXT_SECTION 6269117395Skan#if !defined(READONLY_DATA_SECTION) && !defined(READONLY_DATA_SECTION_ASM_OP) 627096263Sobrien || 1 627196263Sobrien#endif 627296263Sobrien ) 627396263Sobrien { 627496263Sobrien rtx body = PATTERN (insn); 627596263Sobrien int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0; 627690075Sobrien 627796263Sobrien return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, elt); 627896263Sobrien } 627996263Sobrien 628096263Sobrien return 0; 628190075Sobrien} 628290075Sobrien 628390075Sobrien/* Move a minipool fix MP from its current location to before MAX_MP. 628490075Sobrien If MAX_MP is NULL, then MP doesn't need moving, but the addressing 6285132718Skan constraints may need updating. */ 628690075Sobrienstatic Mnode * 6287132718Skanmove_minipool_fix_forward_ref (Mnode *mp, Mnode *max_mp, 6288132718Skan HOST_WIDE_INT max_address) 628990075Sobrien{ 629090075Sobrien /* This should never be true and the code below assumes these are 629190075Sobrien different. */ 629290075Sobrien if (mp == max_mp) 629390075Sobrien abort (); 629490075Sobrien 629590075Sobrien if (max_mp == NULL) 629690075Sobrien { 629790075Sobrien if (max_address < mp->max_address) 629890075Sobrien mp->max_address = max_address; 629990075Sobrien } 630090075Sobrien else 630190075Sobrien { 630290075Sobrien if (max_address > max_mp->max_address - mp->fix_size) 630390075Sobrien mp->max_address = max_mp->max_address - mp->fix_size; 630490075Sobrien else 630590075Sobrien mp->max_address = max_address; 630690075Sobrien 630790075Sobrien /* Unlink MP from its current position. Since max_mp is non-null, 630890075Sobrien mp->prev must be non-null. */ 630990075Sobrien mp->prev->next = mp->next; 631090075Sobrien if (mp->next != NULL) 631190075Sobrien mp->next->prev = mp->prev; 631290075Sobrien else 631390075Sobrien minipool_vector_tail = mp->prev; 631490075Sobrien 631590075Sobrien /* Re-insert it before MAX_MP. */ 631690075Sobrien mp->next = max_mp; 631790075Sobrien mp->prev = max_mp->prev; 631890075Sobrien max_mp->prev = mp; 631990075Sobrien 632090075Sobrien if (mp->prev != NULL) 632190075Sobrien mp->prev->next = mp; 632290075Sobrien else 632390075Sobrien minipool_vector_head = mp; 632490075Sobrien } 632590075Sobrien 632690075Sobrien /* Save the new entry. */ 632790075Sobrien max_mp = mp; 632890075Sobrien 632990075Sobrien /* Scan over the preceding entries and adjust their addresses as 633090075Sobrien required. */ 633190075Sobrien while (mp->prev != NULL 633290075Sobrien && mp->prev->max_address > mp->max_address - mp->prev->fix_size) 633390075Sobrien { 633490075Sobrien mp->prev->max_address = mp->max_address - mp->prev->fix_size; 633590075Sobrien mp = mp->prev; 633690075Sobrien } 633790075Sobrien 633890075Sobrien return max_mp; 633990075Sobrien} 634090075Sobrien 634190075Sobrien/* Add a constant to the minipool for a forward reference. Returns the 634290075Sobrien node added or NULL if the constant will not fit in this pool. */ 634390075Sobrienstatic Mnode * 6344132718Skanadd_minipool_forward_ref (Mfix *fix) 634590075Sobrien{ 634690075Sobrien /* If set, max_mp is the first pool_entry that has a lower 634790075Sobrien constraint than the one we are trying to add. */ 634890075Sobrien Mnode * max_mp = NULL; 634990075Sobrien HOST_WIDE_INT max_address = fix->address + fix->forwards; 635090075Sobrien Mnode * mp; 635190075Sobrien 635290075Sobrien /* If this fix's address is greater than the address of the first 635390075Sobrien entry, then we can't put the fix in this pool. We subtract the 635490075Sobrien size of the current fix to ensure that if the table is fully 635590075Sobrien packed we still have enough room to insert this value by suffling 635690075Sobrien the other fixes forwards. */ 635790075Sobrien if (minipool_vector_head && 635890075Sobrien fix->address >= minipool_vector_head->max_address - fix->fix_size) 635990075Sobrien return NULL; 636090075Sobrien 636190075Sobrien /* Scan the pool to see if a constant with the same value has 636290075Sobrien already been added. While we are doing this, also note the 636390075Sobrien location where we must insert the constant if it doesn't already 636490075Sobrien exist. */ 636590075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 636690075Sobrien { 636790075Sobrien if (GET_CODE (fix->value) == GET_CODE (mp->value) 636890075Sobrien && fix->mode == mp->mode 636990075Sobrien && (GET_CODE (fix->value) != CODE_LABEL 637090075Sobrien || (CODE_LABEL_NUMBER (fix->value) 637190075Sobrien == CODE_LABEL_NUMBER (mp->value))) 637290075Sobrien && rtx_equal_p (fix->value, mp->value)) 637390075Sobrien { 637490075Sobrien /* More than one fix references this entry. */ 637590075Sobrien mp->refcount++; 637690075Sobrien return move_minipool_fix_forward_ref (mp, max_mp, max_address); 637790075Sobrien } 637890075Sobrien 637990075Sobrien /* Note the insertion point if necessary. */ 638090075Sobrien if (max_mp == NULL 638190075Sobrien && mp->max_address > max_address) 638290075Sobrien max_mp = mp; 6383132718Skan 6384132718Skan /* If we are inserting an 8-bytes aligned quantity and 6385132718Skan we have not already found an insertion point, then 6386132718Skan make sure that all such 8-byte aligned quantities are 6387132718Skan placed at the start of the pool. */ 6388132718Skan if (TARGET_REALLY_IWMMXT 6389132718Skan && max_mp == NULL 6390132718Skan && fix->fix_size == 8 6391132718Skan && mp->fix_size != 8) 6392132718Skan { 6393132718Skan max_mp = mp; 6394132718Skan max_address = mp->max_address; 6395132718Skan } 639690075Sobrien } 639790075Sobrien 639890075Sobrien /* The value is not currently in the minipool, so we need to create 639990075Sobrien a new entry for it. If MAX_MP is NULL, the entry will be put on 640090075Sobrien the end of the list since the placement is less constrained than 640190075Sobrien any existing entry. Otherwise, we insert the new fix before 6402132718Skan MAX_MP and, if necessary, adjust the constraints on the other 640390075Sobrien entries. */ 640490075Sobrien mp = xmalloc (sizeof (* mp)); 640590075Sobrien mp->fix_size = fix->fix_size; 640690075Sobrien mp->mode = fix->mode; 640790075Sobrien mp->value = fix->value; 640890075Sobrien mp->refcount = 1; 640990075Sobrien /* Not yet required for a backwards ref. */ 641090075Sobrien mp->min_address = -65536; 641190075Sobrien 641290075Sobrien if (max_mp == NULL) 641390075Sobrien { 641490075Sobrien mp->max_address = max_address; 641590075Sobrien mp->next = NULL; 641690075Sobrien mp->prev = minipool_vector_tail; 641790075Sobrien 641890075Sobrien if (mp->prev == NULL) 641990075Sobrien { 642090075Sobrien minipool_vector_head = mp; 642190075Sobrien minipool_vector_label = gen_label_rtx (); 642290075Sobrien } 642390075Sobrien else 642490075Sobrien mp->prev->next = mp; 642590075Sobrien 642690075Sobrien minipool_vector_tail = mp; 642790075Sobrien } 642890075Sobrien else 642990075Sobrien { 643090075Sobrien if (max_address > max_mp->max_address - mp->fix_size) 643190075Sobrien mp->max_address = max_mp->max_address - mp->fix_size; 643290075Sobrien else 643390075Sobrien mp->max_address = max_address; 643490075Sobrien 643590075Sobrien mp->next = max_mp; 643690075Sobrien mp->prev = max_mp->prev; 643790075Sobrien max_mp->prev = mp; 643890075Sobrien if (mp->prev != NULL) 643990075Sobrien mp->prev->next = mp; 644090075Sobrien else 644190075Sobrien minipool_vector_head = mp; 644290075Sobrien } 644390075Sobrien 644490075Sobrien /* Save the new entry. */ 644590075Sobrien max_mp = mp; 644690075Sobrien 644790075Sobrien /* Scan over the preceding entries and adjust their addresses as 644890075Sobrien required. */ 644990075Sobrien while (mp->prev != NULL 645090075Sobrien && mp->prev->max_address > mp->max_address - mp->prev->fix_size) 645190075Sobrien { 645290075Sobrien mp->prev->max_address = mp->max_address - mp->prev->fix_size; 645390075Sobrien mp = mp->prev; 645490075Sobrien } 645590075Sobrien 645690075Sobrien return max_mp; 645790075Sobrien} 645890075Sobrien 645990075Sobrienstatic Mnode * 6460132718Skanmove_minipool_fix_backward_ref (Mnode *mp, Mnode *min_mp, 6461132718Skan HOST_WIDE_INT min_address) 646290075Sobrien{ 646390075Sobrien HOST_WIDE_INT offset; 646490075Sobrien 646590075Sobrien /* This should never be true, and the code below assumes these are 646690075Sobrien different. */ 646790075Sobrien if (mp == min_mp) 646890075Sobrien abort (); 646990075Sobrien 647090075Sobrien if (min_mp == NULL) 647190075Sobrien { 647290075Sobrien if (min_address > mp->min_address) 647390075Sobrien mp->min_address = min_address; 647490075Sobrien } 647590075Sobrien else 647690075Sobrien { 647790075Sobrien /* We will adjust this below if it is too loose. */ 647890075Sobrien mp->min_address = min_address; 647990075Sobrien 648090075Sobrien /* Unlink MP from its current position. Since min_mp is non-null, 648190075Sobrien mp->next must be non-null. */ 648290075Sobrien mp->next->prev = mp->prev; 648390075Sobrien if (mp->prev != NULL) 648490075Sobrien mp->prev->next = mp->next; 648590075Sobrien else 648690075Sobrien minipool_vector_head = mp->next; 648790075Sobrien 648890075Sobrien /* Reinsert it after MIN_MP. */ 648990075Sobrien mp->prev = min_mp; 649090075Sobrien mp->next = min_mp->next; 649190075Sobrien min_mp->next = mp; 649290075Sobrien if (mp->next != NULL) 649390075Sobrien mp->next->prev = mp; 649490075Sobrien else 649590075Sobrien minipool_vector_tail = mp; 649690075Sobrien } 649790075Sobrien 649890075Sobrien min_mp = mp; 649990075Sobrien 650090075Sobrien offset = 0; 650190075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 650290075Sobrien { 650390075Sobrien mp->offset = offset; 650490075Sobrien if (mp->refcount > 0) 650590075Sobrien offset += mp->fix_size; 650690075Sobrien 650790075Sobrien if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size) 650890075Sobrien mp->next->min_address = mp->min_address + mp->fix_size; 650990075Sobrien } 651090075Sobrien 651190075Sobrien return min_mp; 651290075Sobrien} 651390075Sobrien 651490075Sobrien/* Add a constant to the minipool for a backward reference. Returns the 651590075Sobrien node added or NULL if the constant will not fit in this pool. 651690075Sobrien 651790075Sobrien Note that the code for insertion for a backwards reference can be 651890075Sobrien somewhat confusing because the calculated offsets for each fix do 651990075Sobrien not take into account the size of the pool (which is still under 652090075Sobrien construction. */ 652190075Sobrienstatic Mnode * 6522132718Skanadd_minipool_backward_ref (Mfix *fix) 652390075Sobrien{ 652490075Sobrien /* If set, min_mp is the last pool_entry that has a lower constraint 652590075Sobrien than the one we are trying to add. */ 6526132718Skan Mnode *min_mp = NULL; 652790075Sobrien /* This can be negative, since it is only a constraint. */ 652890075Sobrien HOST_WIDE_INT min_address = fix->address - fix->backwards; 6529132718Skan Mnode *mp; 653090075Sobrien 653190075Sobrien /* If we can't reach the current pool from this insn, or if we can't 653290075Sobrien insert this entry at the end of the pool without pushing other 653390075Sobrien fixes out of range, then we don't try. This ensures that we 653490075Sobrien can't fail later on. */ 653590075Sobrien if (min_address >= minipool_barrier->address 653690075Sobrien || (minipool_vector_tail->min_address + fix->fix_size 653790075Sobrien >= minipool_barrier->address)) 653890075Sobrien return NULL; 653990075Sobrien 654090075Sobrien /* Scan the pool to see if a constant with the same value has 654190075Sobrien already been added. While we are doing this, also note the 654290075Sobrien location where we must insert the constant if it doesn't already 654390075Sobrien exist. */ 654490075Sobrien for (mp = minipool_vector_tail; mp != NULL; mp = mp->prev) 654590075Sobrien { 654690075Sobrien if (GET_CODE (fix->value) == GET_CODE (mp->value) 654790075Sobrien && fix->mode == mp->mode 654890075Sobrien && (GET_CODE (fix->value) != CODE_LABEL 654990075Sobrien || (CODE_LABEL_NUMBER (fix->value) 655090075Sobrien == CODE_LABEL_NUMBER (mp->value))) 655190075Sobrien && rtx_equal_p (fix->value, mp->value) 655290075Sobrien /* Check that there is enough slack to move this entry to the 655390075Sobrien end of the table (this is conservative). */ 655490075Sobrien && (mp->max_address 655590075Sobrien > (minipool_barrier->address 655690075Sobrien + minipool_vector_tail->offset 655790075Sobrien + minipool_vector_tail->fix_size))) 655890075Sobrien { 655990075Sobrien mp->refcount++; 656090075Sobrien return move_minipool_fix_backward_ref (mp, min_mp, min_address); 656190075Sobrien } 656290075Sobrien 656390075Sobrien if (min_mp != NULL) 656490075Sobrien mp->min_address += fix->fix_size; 656590075Sobrien else 656690075Sobrien { 656790075Sobrien /* Note the insertion point if necessary. */ 656890075Sobrien if (mp->min_address < min_address) 6569132718Skan { 6570132718Skan /* For now, we do not allow the insertion of 8-byte alignment 6571132718Skan requiring nodes anywhere but at the start of the pool. */ 6572132718Skan if (TARGET_REALLY_IWMMXT && fix->fix_size == 8 && mp->fix_size != 8) 6573132718Skan return NULL; 6574132718Skan else 6575132718Skan min_mp = mp; 6576132718Skan } 657790075Sobrien else if (mp->max_address 657890075Sobrien < minipool_barrier->address + mp->offset + fix->fix_size) 657990075Sobrien { 658090075Sobrien /* Inserting before this entry would push the fix beyond 658190075Sobrien its maximum address (which can happen if we have 658290075Sobrien re-located a forwards fix); force the new fix to come 658390075Sobrien after it. */ 658490075Sobrien min_mp = mp; 658590075Sobrien min_address = mp->min_address + fix->fix_size; 658690075Sobrien } 6587132718Skan /* If we are inserting an 8-bytes aligned quantity and 6588132718Skan we have not already found an insertion point, then 6589132718Skan make sure that all such 8-byte aligned quantities are 6590132718Skan placed at the start of the pool. */ 6591132718Skan else if (TARGET_REALLY_IWMMXT 6592132718Skan && min_mp == NULL 6593132718Skan && fix->fix_size == 8 6594132718Skan && mp->fix_size < 8) 6595132718Skan { 6596132718Skan min_mp = mp; 6597132718Skan min_address = mp->min_address + fix->fix_size; 6598132718Skan } 659990075Sobrien } 660090075Sobrien } 660190075Sobrien 660290075Sobrien /* We need to create a new entry. */ 660390075Sobrien mp = xmalloc (sizeof (* mp)); 660490075Sobrien mp->fix_size = fix->fix_size; 660590075Sobrien mp->mode = fix->mode; 660690075Sobrien mp->value = fix->value; 660790075Sobrien mp->refcount = 1; 660890075Sobrien mp->max_address = minipool_barrier->address + 65536; 660990075Sobrien 661090075Sobrien mp->min_address = min_address; 661190075Sobrien 661290075Sobrien if (min_mp == NULL) 661390075Sobrien { 661490075Sobrien mp->prev = NULL; 661590075Sobrien mp->next = minipool_vector_head; 661690075Sobrien 661790075Sobrien if (mp->next == NULL) 661890075Sobrien { 661990075Sobrien minipool_vector_tail = mp; 662090075Sobrien minipool_vector_label = gen_label_rtx (); 662190075Sobrien } 662290075Sobrien else 662390075Sobrien mp->next->prev = mp; 662490075Sobrien 662590075Sobrien minipool_vector_head = mp; 662690075Sobrien } 662790075Sobrien else 662890075Sobrien { 662990075Sobrien mp->next = min_mp->next; 663090075Sobrien mp->prev = min_mp; 663190075Sobrien min_mp->next = mp; 663290075Sobrien 663390075Sobrien if (mp->next != NULL) 663490075Sobrien mp->next->prev = mp; 663590075Sobrien else 663690075Sobrien minipool_vector_tail = mp; 663790075Sobrien } 663890075Sobrien 663990075Sobrien /* Save the new entry. */ 664090075Sobrien min_mp = mp; 664190075Sobrien 664290075Sobrien if (mp->prev) 664390075Sobrien mp = mp->prev; 664490075Sobrien else 664590075Sobrien mp->offset = 0; 664690075Sobrien 664790075Sobrien /* Scan over the following entries and adjust their offsets. */ 664890075Sobrien while (mp->next != NULL) 664990075Sobrien { 665090075Sobrien if (mp->next->min_address < mp->min_address + mp->fix_size) 665190075Sobrien mp->next->min_address = mp->min_address + mp->fix_size; 665290075Sobrien 665390075Sobrien if (mp->refcount) 665490075Sobrien mp->next->offset = mp->offset + mp->fix_size; 665590075Sobrien else 665690075Sobrien mp->next->offset = mp->offset; 665790075Sobrien 665890075Sobrien mp = mp->next; 665990075Sobrien } 666090075Sobrien 666190075Sobrien return min_mp; 666290075Sobrien} 666390075Sobrien 666490075Sobrienstatic void 6665132718Skanassign_minipool_offsets (Mfix *barrier) 666690075Sobrien{ 666790075Sobrien HOST_WIDE_INT offset = 0; 6668132718Skan Mnode *mp; 666990075Sobrien 667090075Sobrien minipool_barrier = barrier; 667190075Sobrien 667290075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 667390075Sobrien { 667490075Sobrien mp->offset = offset; 667590075Sobrien 667690075Sobrien if (mp->refcount > 0) 667790075Sobrien offset += mp->fix_size; 667890075Sobrien } 667990075Sobrien} 668090075Sobrien 668190075Sobrien/* Output the literal table */ 668290075Sobrienstatic void 6683132718Skandump_minipool (rtx scan) 668490075Sobrien{ 668590075Sobrien Mnode * mp; 668690075Sobrien Mnode * nmp; 6687132718Skan int align64 = 0; 668890075Sobrien 6689132718Skan if (TARGET_REALLY_IWMMXT) 6690132718Skan for (mp = minipool_vector_head; mp != NULL; mp = mp->next) 6691132718Skan if (mp->refcount > 0 && mp->fix_size == 8) 6692132718Skan { 6693132718Skan align64 = 1; 6694132718Skan break; 6695132718Skan } 6696132718Skan 669790075Sobrien if (rtl_dump_file) 669890075Sobrien fprintf (rtl_dump_file, 6699132718Skan ";; Emitting minipool after insn %u; address %ld; align %d (bytes)\n", 6700132718Skan INSN_UID (scan), (unsigned long) minipool_barrier->address, align64 ? 8 : 4); 670190075Sobrien 670290075Sobrien scan = emit_label_after (gen_label_rtx (), scan); 6703132718Skan scan = emit_insn_after (align64 ? gen_align_8 () : gen_align_4 (), scan); 670490075Sobrien scan = emit_label_after (minipool_vector_label, scan); 670590075Sobrien 670690075Sobrien for (mp = minipool_vector_head; mp != NULL; mp = nmp) 670790075Sobrien { 670890075Sobrien if (mp->refcount > 0) 670990075Sobrien { 671090075Sobrien if (rtl_dump_file) 671190075Sobrien { 671290075Sobrien fprintf (rtl_dump_file, 671390075Sobrien ";; Offset %u, min %ld, max %ld ", 671490075Sobrien (unsigned) mp->offset, (unsigned long) mp->min_address, 671590075Sobrien (unsigned long) mp->max_address); 671690075Sobrien arm_print_value (rtl_dump_file, mp->value); 671790075Sobrien fputc ('\n', rtl_dump_file); 671890075Sobrien } 671990075Sobrien 672090075Sobrien switch (mp->fix_size) 672190075Sobrien { 672290075Sobrien#ifdef HAVE_consttable_1 672390075Sobrien case 1: 672490075Sobrien scan = emit_insn_after (gen_consttable_1 (mp->value), scan); 672590075Sobrien break; 672690075Sobrien 672790075Sobrien#endif 672890075Sobrien#ifdef HAVE_consttable_2 672990075Sobrien case 2: 673090075Sobrien scan = emit_insn_after (gen_consttable_2 (mp->value), scan); 673190075Sobrien break; 673290075Sobrien 673390075Sobrien#endif 673490075Sobrien#ifdef HAVE_consttable_4 673590075Sobrien case 4: 673690075Sobrien scan = emit_insn_after (gen_consttable_4 (mp->value), scan); 673790075Sobrien break; 673890075Sobrien 673990075Sobrien#endif 674090075Sobrien#ifdef HAVE_consttable_8 674190075Sobrien case 8: 674290075Sobrien scan = emit_insn_after (gen_consttable_8 (mp->value), scan); 674390075Sobrien break; 674490075Sobrien 674590075Sobrien#endif 674690075Sobrien default: 674790075Sobrien abort (); 674890075Sobrien break; 674990075Sobrien } 675090075Sobrien } 675190075Sobrien 675290075Sobrien nmp = mp->next; 675390075Sobrien free (mp); 675490075Sobrien } 675590075Sobrien 675690075Sobrien minipool_vector_head = minipool_vector_tail = NULL; 675790075Sobrien scan = emit_insn_after (gen_consttable_end (), scan); 675890075Sobrien scan = emit_barrier_after (scan); 675990075Sobrien} 676090075Sobrien 676190075Sobrien/* Return the cost of forcibly inserting a barrier after INSN. */ 676290075Sobrienstatic int 6763132718Skanarm_barrier_cost (rtx insn) 676490075Sobrien{ 676590075Sobrien /* Basing the location of the pool on the loop depth is preferable, 676690075Sobrien but at the moment, the basic block information seems to be 676790075Sobrien corrupt by this stage of the compilation. */ 676890075Sobrien int base_cost = 50; 676990075Sobrien rtx next = next_nonnote_insn (insn); 677090075Sobrien 677190075Sobrien if (next != NULL && GET_CODE (next) == CODE_LABEL) 677290075Sobrien base_cost -= 20; 677390075Sobrien 677490075Sobrien switch (GET_CODE (insn)) 677590075Sobrien { 677690075Sobrien case CODE_LABEL: 677790075Sobrien /* It will always be better to place the table before the label, rather 677890075Sobrien than after it. */ 677990075Sobrien return 50; 678090075Sobrien 678190075Sobrien case INSN: 678290075Sobrien case CALL_INSN: 678390075Sobrien return base_cost; 678490075Sobrien 678590075Sobrien case JUMP_INSN: 678690075Sobrien return base_cost - 10; 678790075Sobrien 678890075Sobrien default: 678990075Sobrien return base_cost + 10; 679090075Sobrien } 679190075Sobrien} 679290075Sobrien 679390075Sobrien/* Find the best place in the insn stream in the range 679490075Sobrien (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier. 679590075Sobrien Create the barrier by inserting a jump and add a new fix entry for 679690075Sobrien it. */ 679790075Sobrienstatic Mfix * 6798132718Skancreate_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address) 679990075Sobrien{ 680090075Sobrien HOST_WIDE_INT count = 0; 680190075Sobrien rtx barrier; 680290075Sobrien rtx from = fix->insn; 680390075Sobrien rtx selected = from; 680490075Sobrien int selected_cost; 680590075Sobrien HOST_WIDE_INT selected_address; 680690075Sobrien Mfix * new_fix; 680790075Sobrien HOST_WIDE_INT max_count = max_address - fix->address; 680890075Sobrien rtx label = gen_label_rtx (); 680990075Sobrien 681090075Sobrien selected_cost = arm_barrier_cost (from); 681190075Sobrien selected_address = fix->address; 681290075Sobrien 681390075Sobrien while (from && count < max_count) 681490075Sobrien { 681590075Sobrien rtx tmp; 681690075Sobrien int new_cost; 681790075Sobrien 681890075Sobrien /* This code shouldn't have been called if there was a natural barrier 681990075Sobrien within range. */ 682090075Sobrien if (GET_CODE (from) == BARRIER) 682190075Sobrien abort (); 682290075Sobrien 682390075Sobrien /* Count the length of this insn. */ 682490075Sobrien count += get_attr_length (from); 682590075Sobrien 682690075Sobrien /* If there is a jump table, add its length. */ 682790075Sobrien tmp = is_jump_table (from); 682890075Sobrien if (tmp != NULL) 682990075Sobrien { 683090075Sobrien count += get_jump_table_size (tmp); 683190075Sobrien 683290075Sobrien /* Jump tables aren't in a basic block, so base the cost on 683390075Sobrien the dispatch insn. If we select this location, we will 683490075Sobrien still put the pool after the table. */ 683590075Sobrien new_cost = arm_barrier_cost (from); 683690075Sobrien 683790075Sobrien if (count < max_count && new_cost <= selected_cost) 683890075Sobrien { 683990075Sobrien selected = tmp; 684090075Sobrien selected_cost = new_cost; 684190075Sobrien selected_address = fix->address + count; 684290075Sobrien } 684390075Sobrien 684490075Sobrien /* Continue after the dispatch table. */ 684590075Sobrien from = NEXT_INSN (tmp); 684690075Sobrien continue; 684790075Sobrien } 684890075Sobrien 684990075Sobrien new_cost = arm_barrier_cost (from); 685090075Sobrien 685190075Sobrien if (count < max_count && new_cost <= selected_cost) 685290075Sobrien { 685390075Sobrien selected = from; 685490075Sobrien selected_cost = new_cost; 685590075Sobrien selected_address = fix->address + count; 685690075Sobrien } 685790075Sobrien 685890075Sobrien from = NEXT_INSN (from); 685990075Sobrien } 686090075Sobrien 686190075Sobrien /* Create a new JUMP_INSN that branches around a barrier. */ 686290075Sobrien from = emit_jump_insn_after (gen_jump (label), selected); 686390075Sobrien JUMP_LABEL (from) = label; 686490075Sobrien barrier = emit_barrier_after (from); 686590075Sobrien emit_label_after (label, barrier); 686690075Sobrien 686790075Sobrien /* Create a minipool barrier entry for the new barrier. */ 686890075Sobrien new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix)); 686990075Sobrien new_fix->insn = barrier; 687090075Sobrien new_fix->address = selected_address; 687190075Sobrien new_fix->next = fix->next; 687290075Sobrien fix->next = new_fix; 687390075Sobrien 687490075Sobrien return new_fix; 687590075Sobrien} 687690075Sobrien 687790075Sobrien/* Record that there is a natural barrier in the insn stream at 687890075Sobrien ADDRESS. */ 687990075Sobrienstatic void 6880132718Skanpush_minipool_barrier (rtx insn, HOST_WIDE_INT address) 688190075Sobrien{ 688290075Sobrien Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); 688390075Sobrien 688490075Sobrien fix->insn = insn; 688590075Sobrien fix->address = address; 688690075Sobrien 688790075Sobrien fix->next = NULL; 688890075Sobrien if (minipool_fix_head != NULL) 688990075Sobrien minipool_fix_tail->next = fix; 689090075Sobrien else 689190075Sobrien minipool_fix_head = fix; 689290075Sobrien 689390075Sobrien minipool_fix_tail = fix; 689490075Sobrien} 689590075Sobrien 689690075Sobrien/* Record INSN, which will need fixing up to load a value from the 689790075Sobrien minipool. ADDRESS is the offset of the insn since the start of the 689890075Sobrien function; LOC is a pointer to the part of the insn which requires 689990075Sobrien fixing; VALUE is the constant that must be loaded, which is of type 690090075Sobrien MODE. */ 690190075Sobrienstatic void 6902132718Skanpush_minipool_fix (rtx insn, HOST_WIDE_INT address, rtx *loc, 6903132718Skan enum machine_mode mode, rtx value) 690490075Sobrien{ 690590075Sobrien Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix)); 690690075Sobrien 690790075Sobrien#ifdef AOF_ASSEMBLER 6908132718Skan /* PIC symbol references need to be converted into offsets into the 690990075Sobrien based area. */ 691090075Sobrien /* XXX This shouldn't be done here. */ 691190075Sobrien if (flag_pic && GET_CODE (value) == SYMBOL_REF) 691290075Sobrien value = aof_pic_entry (value); 691390075Sobrien#endif /* AOF_ASSEMBLER */ 691490075Sobrien 691590075Sobrien fix->insn = insn; 691690075Sobrien fix->address = address; 691790075Sobrien fix->loc = loc; 691890075Sobrien fix->mode = mode; 691990075Sobrien fix->fix_size = MINIPOOL_FIX_SIZE (mode); 692090075Sobrien fix->value = value; 692190075Sobrien fix->forwards = get_attr_pool_range (insn); 692290075Sobrien fix->backwards = get_attr_neg_pool_range (insn); 692390075Sobrien fix->minipool = NULL; 692490075Sobrien 692590075Sobrien /* If an insn doesn't have a range defined for it, then it isn't 692690075Sobrien expecting to be reworked by this code. Better to abort now than 692790075Sobrien to generate duff assembly code. */ 692890075Sobrien if (fix->forwards == 0 && fix->backwards == 0) 692990075Sobrien abort (); 693090075Sobrien 6931132718Skan /* With iWMMXt enabled, the pool is aligned to an 8-byte boundary. 6932132718Skan So there might be an empty word before the start of the pool. 6933132718Skan Hence we reduce the forward range by 4 to allow for this 6934132718Skan possibility. */ 6935132718Skan if (TARGET_REALLY_IWMMXT && fix->fix_size == 8) 6936132718Skan fix->forwards -= 4; 6937132718Skan 693890075Sobrien if (rtl_dump_file) 693990075Sobrien { 694090075Sobrien fprintf (rtl_dump_file, 694190075Sobrien ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ", 694290075Sobrien GET_MODE_NAME (mode), 694390075Sobrien INSN_UID (insn), (unsigned long) address, 694490075Sobrien -1 * (long)fix->backwards, (long)fix->forwards); 694590075Sobrien arm_print_value (rtl_dump_file, fix->value); 694690075Sobrien fprintf (rtl_dump_file, "\n"); 694790075Sobrien } 694890075Sobrien 694990075Sobrien /* Add it to the chain of fixes. */ 695090075Sobrien fix->next = NULL; 695190075Sobrien 695290075Sobrien if (minipool_fix_head != NULL) 695390075Sobrien minipool_fix_tail->next = fix; 695490075Sobrien else 695590075Sobrien minipool_fix_head = fix; 695690075Sobrien 695790075Sobrien minipool_fix_tail = fix; 695890075Sobrien} 695990075Sobrien 6960132718Skan/* Scan INSN and note any of its operands that need fixing. 6961132718Skan If DO_PUSHES is false we do not actually push any of the fixups 6962132718Skan needed. The function returns TRUE is any fixups were needed/pushed. 6963132718Skan This is used by arm_memory_load_p() which needs to know about loads 6964132718Skan of constants that will be converted into minipool loads. */ 6965132718Skanstatic bool 6966132718Skannote_invalid_constants (rtx insn, HOST_WIDE_INT address, int do_pushes) 696790075Sobrien{ 6968132718Skan bool result = false; 696990075Sobrien int opno; 697090075Sobrien 697190075Sobrien extract_insn (insn); 697290075Sobrien 697390075Sobrien if (!constrain_operands (1)) 697490075Sobrien fatal_insn_not_found (insn); 697590075Sobrien 6976132718Skan if (recog_data.n_alternatives == 0) 6977132718Skan return false; 6978132718Skan 6979132718Skan /* Fill in recog_op_alt with information about the constraints of this insn. */ 698090075Sobrien preprocess_constraints (); 698190075Sobrien 698290075Sobrien for (opno = 0; opno < recog_data.n_operands; opno++) 698390075Sobrien { 698490075Sobrien /* Things we need to fix can only occur in inputs. */ 698590075Sobrien if (recog_data.operand_type[opno] != OP_IN) 698690075Sobrien continue; 698790075Sobrien 698890075Sobrien /* If this alternative is a memory reference, then any mention 698990075Sobrien of constants in this alternative is really to fool reload 699090075Sobrien into allowing us to accept one there. We need to fix them up 699190075Sobrien now so that we output the right code. */ 699290075Sobrien if (recog_op_alt[opno][which_alternative].memory_ok) 699390075Sobrien { 699490075Sobrien rtx op = recog_data.operand[opno]; 699590075Sobrien 699690075Sobrien if (CONSTANT_P (op)) 6997132718Skan { 6998132718Skan if (do_pushes) 6999132718Skan push_minipool_fix (insn, address, recog_data.operand_loc[opno], 7000132718Skan recog_data.operand_mode[opno], op); 7001132718Skan result = true; 7002132718Skan } 700390075Sobrien else if (GET_CODE (op) == MEM 700490075Sobrien && GET_CODE (XEXP (op, 0)) == SYMBOL_REF 700590075Sobrien && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0))) 7006132718Skan { 7007132718Skan if (do_pushes) 7008132718Skan { 7009132718Skan rtx cop = avoid_constant_pool_reference (op); 7010132718Skan 7011132718Skan /* Casting the address of something to a mode narrower 7012132718Skan than a word can cause avoid_constant_pool_reference() 7013132718Skan to return the pool reference itself. That's no good to 7014132718Skan us here. Lets just hope that we can use the 7015132718Skan constant pool value directly. */ 7016132718Skan if (op == cop) 7017132718Skan cop = get_pool_constant (XEXP (op, 0)); 7018132718Skan 7019132718Skan push_minipool_fix (insn, address, 7020132718Skan recog_data.operand_loc[opno], 7021132718Skan recog_data.operand_mode[opno], cop); 7022132718Skan } 7023132718Skan 7024132718Skan result = true; 7025132718Skan } 702690075Sobrien } 702790075Sobrien } 7028132718Skan 7029132718Skan return result; 703090075Sobrien} 703190075Sobrien 7032132718Skan/* Gcc puts the pool in the wrong place for ARM, since we can only 7033132718Skan load addresses a limited distance around the pc. We do some 7034132718Skan special munging to move the constant pool values to the correct 7035132718Skan point in the code. */ 7036132718Skanstatic void 7037132718Skanarm_reorg (void) 703890075Sobrien{ 703990075Sobrien rtx insn; 704090075Sobrien HOST_WIDE_INT address = 0; 704190075Sobrien Mfix * fix; 704290075Sobrien 704390075Sobrien minipool_fix_head = minipool_fix_tail = NULL; 704490075Sobrien 704590075Sobrien /* The first insn must always be a note, or the code below won't 704690075Sobrien scan it properly. */ 7047132718Skan insn = get_insns (); 7048132718Skan if (GET_CODE (insn) != NOTE) 704990075Sobrien abort (); 705090075Sobrien 705190075Sobrien /* Scan all the insns and record the operands that will need fixing. */ 7052132718Skan for (insn = next_nonnote_insn (insn); insn; insn = next_nonnote_insn (insn)) 705390075Sobrien { 7054132718Skan if (TARGET_CIRRUS_FIX_INVALID_INSNS 7055132718Skan && (arm_cirrus_insn_p (insn) 7056132718Skan || GET_CODE (insn) == JUMP_INSN 7057132718Skan || arm_memory_load_p (insn))) 7058132718Skan cirrus_reorg (insn); 7059132718Skan 706090075Sobrien if (GET_CODE (insn) == BARRIER) 706190075Sobrien push_minipool_barrier (insn, address); 7062132718Skan else if (INSN_P (insn)) 706390075Sobrien { 706490075Sobrien rtx table; 706590075Sobrien 7066132718Skan note_invalid_constants (insn, address, true); 706790075Sobrien address += get_attr_length (insn); 706890075Sobrien 706990075Sobrien /* If the insn is a vector jump, add the size of the table 707090075Sobrien and skip the table. */ 707190075Sobrien if ((table = is_jump_table (insn)) != NULL) 707290075Sobrien { 707390075Sobrien address += get_jump_table_size (table); 707490075Sobrien insn = table; 707590075Sobrien } 707690075Sobrien } 707790075Sobrien } 707890075Sobrien 707990075Sobrien fix = minipool_fix_head; 708090075Sobrien 708190075Sobrien /* Now scan the fixups and perform the required changes. */ 708290075Sobrien while (fix) 708390075Sobrien { 708490075Sobrien Mfix * ftmp; 708590075Sobrien Mfix * fdel; 708690075Sobrien Mfix * last_added_fix; 708790075Sobrien Mfix * last_barrier = NULL; 708890075Sobrien Mfix * this_fix; 708990075Sobrien 709090075Sobrien /* Skip any further barriers before the next fix. */ 709190075Sobrien while (fix && GET_CODE (fix->insn) == BARRIER) 709290075Sobrien fix = fix->next; 709390075Sobrien 709490075Sobrien /* No more fixes. */ 709590075Sobrien if (fix == NULL) 709690075Sobrien break; 709790075Sobrien 709890075Sobrien last_added_fix = NULL; 709990075Sobrien 710090075Sobrien for (ftmp = fix; ftmp; ftmp = ftmp->next) 710190075Sobrien { 710290075Sobrien if (GET_CODE (ftmp->insn) == BARRIER) 710390075Sobrien { 710490075Sobrien if (ftmp->address >= minipool_vector_head->max_address) 710590075Sobrien break; 710690075Sobrien 710790075Sobrien last_barrier = ftmp; 710890075Sobrien } 710990075Sobrien else if ((ftmp->minipool = add_minipool_forward_ref (ftmp)) == NULL) 711090075Sobrien break; 711190075Sobrien 711290075Sobrien last_added_fix = ftmp; /* Keep track of the last fix added. */ 711390075Sobrien } 711490075Sobrien 711590075Sobrien /* If we found a barrier, drop back to that; any fixes that we 711690075Sobrien could have reached but come after the barrier will now go in 711790075Sobrien the next mini-pool. */ 711890075Sobrien if (last_barrier != NULL) 711990075Sobrien { 712090075Sobrien /* Reduce the refcount for those fixes that won't go into this 712190075Sobrien pool after all. */ 712290075Sobrien for (fdel = last_barrier->next; 712390075Sobrien fdel && fdel != ftmp; 712490075Sobrien fdel = fdel->next) 712590075Sobrien { 712690075Sobrien fdel->minipool->refcount--; 712790075Sobrien fdel->minipool = NULL; 712890075Sobrien } 712990075Sobrien 713090075Sobrien ftmp = last_barrier; 713190075Sobrien } 713290075Sobrien else 713390075Sobrien { 713490075Sobrien /* ftmp is first fix that we can't fit into this pool and 713590075Sobrien there no natural barriers that we could use. Insert a 713690075Sobrien new barrier in the code somewhere between the previous 713790075Sobrien fix and this one, and arrange to jump around it. */ 713890075Sobrien HOST_WIDE_INT max_address; 713990075Sobrien 714090075Sobrien /* The last item on the list of fixes must be a barrier, so 714190075Sobrien we can never run off the end of the list of fixes without 714290075Sobrien last_barrier being set. */ 714390075Sobrien if (ftmp == NULL) 714490075Sobrien abort (); 714590075Sobrien 714690075Sobrien max_address = minipool_vector_head->max_address; 714790075Sobrien /* Check that there isn't another fix that is in range that 714890075Sobrien we couldn't fit into this pool because the pool was 714990075Sobrien already too large: we need to put the pool before such an 715090075Sobrien instruction. */ 715190075Sobrien if (ftmp->address < max_address) 715290075Sobrien max_address = ftmp->address; 715390075Sobrien 715490075Sobrien last_barrier = create_fix_barrier (last_added_fix, max_address); 715590075Sobrien } 715690075Sobrien 715790075Sobrien assign_minipool_offsets (last_barrier); 715890075Sobrien 715990075Sobrien while (ftmp) 716090075Sobrien { 716190075Sobrien if (GET_CODE (ftmp->insn) != BARRIER 716290075Sobrien && ((ftmp->minipool = add_minipool_backward_ref (ftmp)) 716390075Sobrien == NULL)) 716490075Sobrien break; 716590075Sobrien 716690075Sobrien ftmp = ftmp->next; 716790075Sobrien } 716890075Sobrien 716990075Sobrien /* Scan over the fixes we have identified for this pool, fixing them 717090075Sobrien up and adding the constants to the pool itself. */ 717190075Sobrien for (this_fix = fix; this_fix && ftmp != this_fix; 717290075Sobrien this_fix = this_fix->next) 717390075Sobrien if (GET_CODE (this_fix->insn) != BARRIER) 717490075Sobrien { 717590075Sobrien rtx addr 717690075Sobrien = plus_constant (gen_rtx_LABEL_REF (VOIDmode, 717790075Sobrien minipool_vector_label), 717890075Sobrien this_fix->minipool->offset); 717990075Sobrien *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr); 718090075Sobrien } 718190075Sobrien 718290075Sobrien dump_minipool (last_barrier->insn); 718390075Sobrien fix = ftmp; 718490075Sobrien } 718590075Sobrien 718690075Sobrien /* From now on we must synthesize any constants that we can't handle 718790075Sobrien directly. This can happen if the RTL gets split during final 718890075Sobrien instruction generation. */ 718990075Sobrien after_arm_reorg = 1; 719090075Sobrien 719190075Sobrien /* Free the minipool memory. */ 719290075Sobrien obstack_free (&minipool_obstack, minipool_startobj); 719390075Sobrien} 719490075Sobrien 719590075Sobrien/* Routines to output assembly language. */ 719690075Sobrien 719790075Sobrien/* If the rtx is the correct value then return the string of the number. 719890075Sobrien In this way we can ensure that valid double constants are generated even 719990075Sobrien when cross compiling. */ 720090075Sobrienconst char * 7201132718Skanfp_immediate_constant (rtx x) 720290075Sobrien{ 720390075Sobrien REAL_VALUE_TYPE r; 720490075Sobrien int i; 720590075Sobrien 720690075Sobrien if (!fpa_consts_inited) 720790075Sobrien init_fpa_table (); 720890075Sobrien 720990075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 721090075Sobrien for (i = 0; i < 8; i++) 721190075Sobrien if (REAL_VALUES_EQUAL (r, values_fpa[i])) 721290075Sobrien return strings_fpa[i]; 721390075Sobrien 721490075Sobrien abort (); 721590075Sobrien} 721690075Sobrien 721790075Sobrien/* As for fp_immediate_constant, but value is passed directly, not in rtx. */ 721890075Sobrienstatic const char * 7219132718Skanfp_const_from_val (REAL_VALUE_TYPE *r) 722090075Sobrien{ 722190075Sobrien int i; 722290075Sobrien 722390075Sobrien if (!fpa_consts_inited) 722490075Sobrien init_fpa_table (); 722590075Sobrien 722690075Sobrien for (i = 0; i < 8; i++) 722790075Sobrien if (REAL_VALUES_EQUAL (*r, values_fpa[i])) 722890075Sobrien return strings_fpa[i]; 722990075Sobrien 723090075Sobrien abort (); 723190075Sobrien} 723290075Sobrien 723390075Sobrien/* Output the operands of a LDM/STM instruction to STREAM. 723490075Sobrien MASK is the ARM register set mask of which only bits 0-15 are important. 723590075Sobrien REG is the base register, either the frame pointer or the stack pointer, 723690075Sobrien INSTR is the possibly suffixed load or store instruction. */ 723790075Sobrienstatic void 7238132718Skanprint_multi_reg (FILE *stream, const char *instr, int reg, int mask) 723990075Sobrien{ 724090075Sobrien int i; 724190075Sobrien int not_first = FALSE; 724290075Sobrien 724390075Sobrien fputc ('\t', stream); 724490075Sobrien asm_fprintf (stream, instr, reg); 724590075Sobrien fputs (", {", stream); 724690075Sobrien 724790075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 724890075Sobrien if (mask & (1 << i)) 724990075Sobrien { 725090075Sobrien if (not_first) 725190075Sobrien fprintf (stream, ", "); 725290075Sobrien 725390075Sobrien asm_fprintf (stream, "%r", i); 725490075Sobrien not_first = TRUE; 725590075Sobrien } 725690075Sobrien 7257132718Skan fprintf (stream, "}"); 7258132718Skan 7259132718Skan /* Add a ^ character for the 26-bit ABI, but only if we were loading 7260132718Skan the PC. Otherwise we would generate an UNPREDICTABLE instruction. 7261132718Skan Strictly speaking the instruction would be unpredicatble only if 7262132718Skan we were writing back the base register as well, but since we never 7263132718Skan want to generate an LDM type 2 instruction (register bank switching) 7264132718Skan which is what you get if the PC is not being loaded, we do not need 7265132718Skan to check for writeback. */ 7266132718Skan if (! TARGET_APCS_32 7267132718Skan && ((mask & (1 << PC_REGNUM)) != 0)) 7268132718Skan fprintf (stream, "^"); 7269132718Skan 7270132718Skan fprintf (stream, "\n"); 727190075Sobrien} 727290075Sobrien 727390075Sobrien/* Output a 'call' insn. */ 727490075Sobrienconst char * 7275132718Skanoutput_call (rtx *operands) 727690075Sobrien{ 727790075Sobrien /* Handle calls to lr using ip (which may be clobbered in subr anyway). */ 727890075Sobrien 727990075Sobrien if (REGNO (operands[0]) == LR_REGNUM) 728090075Sobrien { 728190075Sobrien operands[0] = gen_rtx_REG (SImode, IP_REGNUM); 728290075Sobrien output_asm_insn ("mov%?\t%0, %|lr", operands); 728390075Sobrien } 728490075Sobrien 728590075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 728690075Sobrien 728790075Sobrien if (TARGET_INTERWORK) 728890075Sobrien output_asm_insn ("bx%?\t%0", operands); 728990075Sobrien else 729090075Sobrien output_asm_insn ("mov%?\t%|pc, %0", operands); 729190075Sobrien 729290075Sobrien return ""; 729390075Sobrien} 729490075Sobrien 729590075Sobrien/* Output a 'call' insn that is a reference in memory. */ 729690075Sobrienconst char * 7297132718Skanoutput_call_mem (rtx *operands) 729890075Sobrien{ 729990075Sobrien if (TARGET_INTERWORK) 730090075Sobrien { 730190075Sobrien output_asm_insn ("ldr%?\t%|ip, %0", operands); 730290075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 730390075Sobrien output_asm_insn ("bx%?\t%|ip", operands); 730490075Sobrien } 7305117395Skan else if (regno_use_in (LR_REGNUM, operands[0])) 7306117395Skan { 7307117395Skan /* LR is used in the memory address. We load the address in the 7308117395Skan first instruction. It's safe to use IP as the target of the 7309117395Skan load since the call will kill it anyway. */ 7310117395Skan output_asm_insn ("ldr%?\t%|ip, %0", operands); 7311117395Skan output_asm_insn ("mov%?\t%|lr, %|pc", operands); 7312117395Skan output_asm_insn ("mov%?\t%|pc, %|ip", operands); 7313117395Skan } 731490075Sobrien else 731590075Sobrien { 731690075Sobrien output_asm_insn ("mov%?\t%|lr, %|pc", operands); 731790075Sobrien output_asm_insn ("ldr%?\t%|pc, %0", operands); 731890075Sobrien } 731990075Sobrien 732090075Sobrien return ""; 732190075Sobrien} 732290075Sobrien 7323132718Skan/* Output a move from arm registers to an fpa registers. 7324132718Skan OPERANDS[0] is an fpa register. 732590075Sobrien OPERANDS[1] is the first registers of an arm register pair. */ 732690075Sobrienconst char * 7327132718Skanoutput_mov_long_double_fpa_from_arm (rtx *operands) 732890075Sobrien{ 732990075Sobrien int arm_reg0 = REGNO (operands[1]); 733090075Sobrien rtx ops[3]; 733190075Sobrien 733290075Sobrien if (arm_reg0 == IP_REGNUM) 733390075Sobrien abort (); 733490075Sobrien 733590075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 733690075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 733790075Sobrien ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); 733890075Sobrien 733990075Sobrien output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops); 734090075Sobrien output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands); 734190075Sobrien 734290075Sobrien return ""; 734390075Sobrien} 734490075Sobrien 7345132718Skan/* Output a move from an fpa register to arm registers. 734690075Sobrien OPERANDS[0] is the first registers of an arm register pair. 7347132718Skan OPERANDS[1] is an fpa register. */ 734890075Sobrienconst char * 7349132718Skanoutput_mov_long_double_arm_from_fpa (rtx *operands) 735090075Sobrien{ 735190075Sobrien int arm_reg0 = REGNO (operands[0]); 735290075Sobrien rtx ops[3]; 735390075Sobrien 735490075Sobrien if (arm_reg0 == IP_REGNUM) 735590075Sobrien abort (); 735690075Sobrien 735790075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 735890075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 735990075Sobrien ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0); 736090075Sobrien 736190075Sobrien output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands); 736290075Sobrien output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops); 736390075Sobrien return ""; 736490075Sobrien} 736590075Sobrien 736690075Sobrien/* Output a move from arm registers to arm registers of a long double 736790075Sobrien OPERANDS[0] is the destination. 736890075Sobrien OPERANDS[1] is the source. */ 736990075Sobrienconst char * 7370132718Skanoutput_mov_long_double_arm_from_arm (rtx *operands) 737190075Sobrien{ 737290075Sobrien /* We have to be careful here because the two might overlap. */ 737390075Sobrien int dest_start = REGNO (operands[0]); 737490075Sobrien int src_start = REGNO (operands[1]); 737590075Sobrien rtx ops[2]; 737690075Sobrien int i; 737790075Sobrien 737890075Sobrien if (dest_start < src_start) 737990075Sobrien { 738090075Sobrien for (i = 0; i < 3; i++) 738190075Sobrien { 738290075Sobrien ops[0] = gen_rtx_REG (SImode, dest_start + i); 738390075Sobrien ops[1] = gen_rtx_REG (SImode, src_start + i); 738490075Sobrien output_asm_insn ("mov%?\t%0, %1", ops); 738590075Sobrien } 738690075Sobrien } 738790075Sobrien else 738890075Sobrien { 738990075Sobrien for (i = 2; i >= 0; i--) 739090075Sobrien { 739190075Sobrien ops[0] = gen_rtx_REG (SImode, dest_start + i); 739290075Sobrien ops[1] = gen_rtx_REG (SImode, src_start + i); 739390075Sobrien output_asm_insn ("mov%?\t%0, %1", ops); 739490075Sobrien } 739590075Sobrien } 739690075Sobrien 739790075Sobrien return ""; 739890075Sobrien} 739990075Sobrien 740090075Sobrien 7401132718Skan/* Output a move from arm registers to an fpa registers. 7402132718Skan OPERANDS[0] is an fpa register. 740390075Sobrien OPERANDS[1] is the first registers of an arm register pair. */ 740490075Sobrienconst char * 7405132718Skanoutput_mov_double_fpa_from_arm (rtx *operands) 740690075Sobrien{ 740790075Sobrien int arm_reg0 = REGNO (operands[1]); 740890075Sobrien rtx ops[2]; 740990075Sobrien 741090075Sobrien if (arm_reg0 == IP_REGNUM) 741190075Sobrien abort (); 741290075Sobrien 741390075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 741490075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 741590075Sobrien output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops); 741690075Sobrien output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands); 741790075Sobrien return ""; 741890075Sobrien} 741990075Sobrien 7420132718Skan/* Output a move from an fpa register to arm registers. 742190075Sobrien OPERANDS[0] is the first registers of an arm register pair. 7422132718Skan OPERANDS[1] is an fpa register. */ 742390075Sobrienconst char * 7424132718Skanoutput_mov_double_arm_from_fpa (rtx *operands) 742590075Sobrien{ 742690075Sobrien int arm_reg0 = REGNO (operands[0]); 742790075Sobrien rtx ops[2]; 742890075Sobrien 742990075Sobrien if (arm_reg0 == IP_REGNUM) 743090075Sobrien abort (); 743190075Sobrien 743290075Sobrien ops[0] = gen_rtx_REG (SImode, arm_reg0); 743390075Sobrien ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0); 743490075Sobrien output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands); 743590075Sobrien output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops); 743690075Sobrien return ""; 743790075Sobrien} 743890075Sobrien 743990075Sobrien/* Output a move between double words. 744090075Sobrien It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM 744190075Sobrien or MEM<-REG and all MEMs must be offsettable addresses. */ 744290075Sobrienconst char * 7443132718Skanoutput_move_double (rtx *operands) 744490075Sobrien{ 744590075Sobrien enum rtx_code code0 = GET_CODE (operands[0]); 744690075Sobrien enum rtx_code code1 = GET_CODE (operands[1]); 744790075Sobrien rtx otherops[3]; 744890075Sobrien 744990075Sobrien if (code0 == REG) 745090075Sobrien { 745190075Sobrien int reg0 = REGNO (operands[0]); 745290075Sobrien 745390075Sobrien otherops[0] = gen_rtx_REG (SImode, 1 + reg0); 745490075Sobrien 745590075Sobrien if (code1 == REG) 745690075Sobrien { 745790075Sobrien int reg1 = REGNO (operands[1]); 745890075Sobrien if (reg1 == IP_REGNUM) 745990075Sobrien abort (); 746090075Sobrien 746190075Sobrien /* Ensure the second source is not overwritten. */ 746290075Sobrien if (reg1 == reg0 + (WORDS_BIG_ENDIAN ? -1 : 1)) 746390075Sobrien output_asm_insn ("mov%?\t%Q0, %Q1\n\tmov%?\t%R0, %R1", operands); 746490075Sobrien else 746590075Sobrien output_asm_insn ("mov%?\t%R0, %R1\n\tmov%?\t%Q0, %Q1", operands); 746690075Sobrien } 7467132718Skan else if (code1 == CONST_VECTOR) 7468132718Skan { 7469132718Skan HOST_WIDE_INT hint = 0; 7470132718Skan 7471132718Skan switch (GET_MODE (operands[1])) 7472132718Skan { 7473132718Skan case V2SImode: 7474132718Skan otherops[1] = GEN_INT (INTVAL (CONST_VECTOR_ELT (operands[1], 1))); 7475132718Skan operands[1] = GEN_INT (INTVAL (CONST_VECTOR_ELT (operands[1], 0))); 7476132718Skan break; 7477132718Skan 7478132718Skan case V4HImode: 7479132718Skan if (BYTES_BIG_ENDIAN) 7480132718Skan { 7481132718Skan hint = INTVAL (CONST_VECTOR_ELT (operands[1], 2)); 7482132718Skan hint <<= 16; 7483132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 3)); 7484132718Skan } 7485132718Skan else 7486132718Skan { 7487132718Skan hint = INTVAL (CONST_VECTOR_ELT (operands[1], 3)); 7488132718Skan hint <<= 16; 7489132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 2)); 7490132718Skan } 7491132718Skan 7492132718Skan otherops[1] = GEN_INT (hint); 7493132718Skan hint = 0; 7494132718Skan 7495132718Skan if (BYTES_BIG_ENDIAN) 7496132718Skan { 7497132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 0)); 7498132718Skan hint <<= 16; 7499132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 1)); 7500132718Skan } 7501132718Skan else 7502132718Skan { 7503132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 1)); 7504132718Skan hint <<= 16; 7505132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 0)); 7506132718Skan } 7507132718Skan 7508132718Skan operands[1] = GEN_INT (hint); 7509132718Skan break; 7510132718Skan 7511132718Skan case V8QImode: 7512132718Skan if (BYTES_BIG_ENDIAN) 7513132718Skan { 7514132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 4)); 7515132718Skan hint <<= 8; 7516132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 5)); 7517132718Skan hint <<= 8; 7518132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 6)); 7519132718Skan hint <<= 8; 7520132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 7)); 7521132718Skan } 7522132718Skan else 7523132718Skan { 7524132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 7)); 7525132718Skan hint <<= 8; 7526132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 6)); 7527132718Skan hint <<= 8; 7528132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 5)); 7529132718Skan hint <<= 8; 7530132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 4)); 7531132718Skan } 7532132718Skan 7533132718Skan otherops[1] = GEN_INT (hint); 7534132718Skan hint = 0; 7535132718Skan 7536132718Skan if (BYTES_BIG_ENDIAN) 7537132718Skan { 7538132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 0)); 7539132718Skan hint <<= 8; 7540132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 1)); 7541132718Skan hint <<= 8; 7542132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 2)); 7543132718Skan hint <<= 8; 7544132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 3)); 7545132718Skan } 7546132718Skan else 7547132718Skan { 7548132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 3)); 7549132718Skan hint <<= 8; 7550132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 2)); 7551132718Skan hint <<= 8; 7552132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 1)); 7553132718Skan hint <<= 8; 7554132718Skan hint |= INTVAL (CONST_VECTOR_ELT (operands[1], 0)); 7555132718Skan } 7556132718Skan 7557132718Skan operands[1] = GEN_INT (hint); 7558132718Skan break; 7559132718Skan 7560132718Skan default: 7561132718Skan abort (); 7562132718Skan } 7563132718Skan output_mov_immediate (operands); 7564132718Skan output_mov_immediate (otherops); 7565132718Skan } 756690075Sobrien else if (code1 == CONST_DOUBLE) 756790075Sobrien { 756890075Sobrien if (GET_MODE (operands[1]) == DFmode) 756990075Sobrien { 7570117395Skan REAL_VALUE_TYPE r; 757190075Sobrien long l[2]; 757290075Sobrien 7573117395Skan REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); 7574117395Skan REAL_VALUE_TO_TARGET_DOUBLE (r, l); 757590075Sobrien otherops[1] = GEN_INT (l[1]); 757690075Sobrien operands[1] = GEN_INT (l[0]); 757790075Sobrien } 757890075Sobrien else if (GET_MODE (operands[1]) != VOIDmode) 757990075Sobrien abort (); 758090075Sobrien else if (WORDS_BIG_ENDIAN) 758190075Sobrien { 758290075Sobrien otherops[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); 758390075Sobrien operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); 758490075Sobrien } 758590075Sobrien else 758690075Sobrien { 758790075Sobrien otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1])); 758890075Sobrien operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1])); 758990075Sobrien } 759090075Sobrien 759190075Sobrien output_mov_immediate (operands); 759290075Sobrien output_mov_immediate (otherops); 759390075Sobrien } 759490075Sobrien else if (code1 == CONST_INT) 759590075Sobrien { 759690075Sobrien#if HOST_BITS_PER_WIDE_INT > 32 759790075Sobrien /* If HOST_WIDE_INT is more than 32 bits, the intval tells us 759890075Sobrien what the upper word is. */ 759990075Sobrien if (WORDS_BIG_ENDIAN) 760090075Sobrien { 760190075Sobrien otherops[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); 760290075Sobrien operands[1] = GEN_INT (INTVAL (operands[1]) >> 32); 760390075Sobrien } 760490075Sobrien else 760590075Sobrien { 760690075Sobrien otherops[1] = GEN_INT (INTVAL (operands[1]) >> 32); 760790075Sobrien operands[1] = GEN_INT (ARM_SIGN_EXTEND (INTVAL (operands[1]))); 760890075Sobrien } 760990075Sobrien#else 761090075Sobrien /* Sign extend the intval into the high-order word. */ 761190075Sobrien if (WORDS_BIG_ENDIAN) 761290075Sobrien { 761390075Sobrien otherops[1] = operands[1]; 761490075Sobrien operands[1] = (INTVAL (operands[1]) < 0 761590075Sobrien ? constm1_rtx : const0_rtx); 761690075Sobrien } 761790075Sobrien else 761890075Sobrien otherops[1] = INTVAL (operands[1]) < 0 ? constm1_rtx : const0_rtx; 761990075Sobrien#endif 762090075Sobrien output_mov_immediate (otherops); 762190075Sobrien output_mov_immediate (operands); 762290075Sobrien } 762390075Sobrien else if (code1 == MEM) 762490075Sobrien { 762590075Sobrien switch (GET_CODE (XEXP (operands[1], 0))) 762690075Sobrien { 762790075Sobrien case REG: 762890075Sobrien output_asm_insn ("ldm%?ia\t%m1, %M0", operands); 762990075Sobrien break; 763090075Sobrien 763190075Sobrien case PRE_INC: 763290075Sobrien abort (); /* Should never happen now. */ 763390075Sobrien break; 763490075Sobrien 763590075Sobrien case PRE_DEC: 763690075Sobrien output_asm_insn ("ldm%?db\t%m1!, %M0", operands); 763790075Sobrien break; 763890075Sobrien 763990075Sobrien case POST_INC: 764090075Sobrien output_asm_insn ("ldm%?ia\t%m1!, %M0", operands); 764190075Sobrien break; 764290075Sobrien 764390075Sobrien case POST_DEC: 764490075Sobrien abort (); /* Should never happen now. */ 764590075Sobrien break; 764690075Sobrien 764790075Sobrien case LABEL_REF: 764890075Sobrien case CONST: 764990075Sobrien output_asm_insn ("adr%?\t%0, %1", operands); 765090075Sobrien output_asm_insn ("ldm%?ia\t%0, %M0", operands); 765190075Sobrien break; 765290075Sobrien 765390075Sobrien default: 765490075Sobrien if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1), 765590075Sobrien GET_MODE (XEXP (XEXP (operands[1], 0), 1)))) 765690075Sobrien { 765790075Sobrien otherops[0] = operands[0]; 765890075Sobrien otherops[1] = XEXP (XEXP (operands[1], 0), 0); 765990075Sobrien otherops[2] = XEXP (XEXP (operands[1], 0), 1); 766090075Sobrien 766190075Sobrien if (GET_CODE (XEXP (operands[1], 0)) == PLUS) 766290075Sobrien { 766390075Sobrien if (GET_CODE (otherops[2]) == CONST_INT) 766490075Sobrien { 7665132718Skan switch ((int) INTVAL (otherops[2])) 766690075Sobrien { 766790075Sobrien case -8: 766890075Sobrien output_asm_insn ("ldm%?db\t%1, %M0", otherops); 766990075Sobrien return ""; 767090075Sobrien case -4: 767190075Sobrien output_asm_insn ("ldm%?da\t%1, %M0", otherops); 767290075Sobrien return ""; 767390075Sobrien case 4: 767490075Sobrien output_asm_insn ("ldm%?ib\t%1, %M0", otherops); 767590075Sobrien return ""; 767690075Sobrien } 767790075Sobrien 767890075Sobrien if (!(const_ok_for_arm (INTVAL (otherops[2])))) 767990075Sobrien output_asm_insn ("sub%?\t%0, %1, #%n2", otherops); 768090075Sobrien else 768190075Sobrien output_asm_insn ("add%?\t%0, %1, %2", otherops); 768290075Sobrien } 768390075Sobrien else 768490075Sobrien output_asm_insn ("add%?\t%0, %1, %2", otherops); 768590075Sobrien } 768690075Sobrien else 768790075Sobrien output_asm_insn ("sub%?\t%0, %1, %2", otherops); 768890075Sobrien 768990075Sobrien return "ldm%?ia\t%0, %M0"; 769090075Sobrien } 769190075Sobrien else 769290075Sobrien { 7693117395Skan otherops[1] = adjust_address (operands[1], SImode, 4); 769490075Sobrien /* Take care of overlapping base/data reg. */ 769590075Sobrien if (reg_mentioned_p (operands[0], operands[1])) 769690075Sobrien { 769790075Sobrien output_asm_insn ("ldr%?\t%0, %1", otherops); 769890075Sobrien output_asm_insn ("ldr%?\t%0, %1", operands); 769990075Sobrien } 770090075Sobrien else 770190075Sobrien { 770290075Sobrien output_asm_insn ("ldr%?\t%0, %1", operands); 770390075Sobrien output_asm_insn ("ldr%?\t%0, %1", otherops); 770490075Sobrien } 770590075Sobrien } 770690075Sobrien } 770790075Sobrien } 770890075Sobrien else 770990075Sobrien abort (); /* Constraints should prevent this. */ 771090075Sobrien } 771190075Sobrien else if (code0 == MEM && code1 == REG) 771290075Sobrien { 771390075Sobrien if (REGNO (operands[1]) == IP_REGNUM) 771490075Sobrien abort (); 771590075Sobrien 771690075Sobrien switch (GET_CODE (XEXP (operands[0], 0))) 771790075Sobrien { 771890075Sobrien case REG: 771990075Sobrien output_asm_insn ("stm%?ia\t%m0, %M1", operands); 772090075Sobrien break; 772190075Sobrien 772290075Sobrien case PRE_INC: 772390075Sobrien abort (); /* Should never happen now. */ 772490075Sobrien break; 772590075Sobrien 772690075Sobrien case PRE_DEC: 772790075Sobrien output_asm_insn ("stm%?db\t%m0!, %M1", operands); 772890075Sobrien break; 772990075Sobrien 773090075Sobrien case POST_INC: 773190075Sobrien output_asm_insn ("stm%?ia\t%m0!, %M1", operands); 773290075Sobrien break; 773390075Sobrien 773490075Sobrien case POST_DEC: 773590075Sobrien abort (); /* Should never happen now. */ 773690075Sobrien break; 773790075Sobrien 773890075Sobrien case PLUS: 773990075Sobrien if (GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT) 774090075Sobrien { 7741132718Skan switch ((int) INTVAL (XEXP (XEXP (operands[0], 0), 1))) 774290075Sobrien { 774390075Sobrien case -8: 774490075Sobrien output_asm_insn ("stm%?db\t%m0, %M1", operands); 774590075Sobrien return ""; 774690075Sobrien 774790075Sobrien case -4: 774890075Sobrien output_asm_insn ("stm%?da\t%m0, %M1", operands); 774990075Sobrien return ""; 775090075Sobrien 775190075Sobrien case 4: 775290075Sobrien output_asm_insn ("stm%?ib\t%m0, %M1", operands); 775390075Sobrien return ""; 775490075Sobrien } 775590075Sobrien } 775690075Sobrien /* Fall through */ 775790075Sobrien 775890075Sobrien default: 7759117395Skan otherops[0] = adjust_address (operands[0], SImode, 4); 776090075Sobrien otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1])); 776190075Sobrien output_asm_insn ("str%?\t%1, %0", operands); 776290075Sobrien output_asm_insn ("str%?\t%1, %0", otherops); 776390075Sobrien } 776490075Sobrien } 776590075Sobrien else 776690075Sobrien /* Constraints should prevent this. */ 776790075Sobrien abort (); 776890075Sobrien 776990075Sobrien return ""; 777090075Sobrien} 777190075Sobrien 777290075Sobrien 777390075Sobrien/* Output an arbitrary MOV reg, #n. 777490075Sobrien OPERANDS[0] is a register. OPERANDS[1] is a const_int. */ 777590075Sobrienconst char * 7776132718Skanoutput_mov_immediate (rtx *operands) 777790075Sobrien{ 777890075Sobrien HOST_WIDE_INT n = INTVAL (operands[1]); 777990075Sobrien 778090075Sobrien /* Try to use one MOV. */ 778190075Sobrien if (const_ok_for_arm (n)) 778290075Sobrien output_asm_insn ("mov%?\t%0, %1", operands); 778390075Sobrien 778490075Sobrien /* Try to use one MVN. */ 778590075Sobrien else if (const_ok_for_arm (~n)) 778690075Sobrien { 778790075Sobrien operands[1] = GEN_INT (~n); 778890075Sobrien output_asm_insn ("mvn%?\t%0, %1", operands); 778990075Sobrien } 779090075Sobrien else 779190075Sobrien { 779290075Sobrien int n_ones = 0; 779390075Sobrien int i; 779490075Sobrien 779590075Sobrien /* If all else fails, make it out of ORRs or BICs as appropriate. */ 7796132718Skan for (i = 0; i < 32; i++) 779790075Sobrien if (n & 1 << i) 7798132718Skan n_ones++; 779990075Sobrien 780090075Sobrien if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */ 780190075Sobrien output_multi_immediate (operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, ~ n); 780290075Sobrien else 780390075Sobrien output_multi_immediate (operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, n); 780490075Sobrien } 780590075Sobrien 780690075Sobrien return ""; 780790075Sobrien} 780890075Sobrien 780990075Sobrien/* Output an ADD r, s, #n where n may be too big for one instruction. 781090075Sobrien If adding zero to one register, output nothing. */ 781190075Sobrienconst char * 7812132718Skanoutput_add_immediate (rtx *operands) 781390075Sobrien{ 781490075Sobrien HOST_WIDE_INT n = INTVAL (operands[2]); 781590075Sobrien 781690075Sobrien if (n != 0 || REGNO (operands[0]) != REGNO (operands[1])) 781790075Sobrien { 781890075Sobrien if (n < 0) 781990075Sobrien output_multi_immediate (operands, 782090075Sobrien "sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2, 782190075Sobrien -n); 782290075Sobrien else 782390075Sobrien output_multi_immediate (operands, 782490075Sobrien "add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2, 782590075Sobrien n); 782690075Sobrien } 782790075Sobrien 782890075Sobrien return ""; 782990075Sobrien} 783090075Sobrien 783190075Sobrien/* Output a multiple immediate operation. 783290075Sobrien OPERANDS is the vector of operands referred to in the output patterns. 783390075Sobrien INSTR1 is the output pattern to use for the first constant. 783490075Sobrien INSTR2 is the output pattern to use for subsequent constants. 783590075Sobrien IMMED_OP is the index of the constant slot in OPERANDS. 783690075Sobrien N is the constant value. */ 783790075Sobrienstatic const char * 7838132718Skanoutput_multi_immediate (rtx *operands, const char *instr1, const char *instr2, 7839132718Skan int immed_op, HOST_WIDE_INT n) 784090075Sobrien{ 784190075Sobrien#if HOST_BITS_PER_WIDE_INT > 32 784290075Sobrien n &= 0xffffffff; 784390075Sobrien#endif 784490075Sobrien 784590075Sobrien if (n == 0) 784690075Sobrien { 784790075Sobrien /* Quick and easy output. */ 784890075Sobrien operands[immed_op] = const0_rtx; 784990075Sobrien output_asm_insn (instr1, operands); 785090075Sobrien } 785190075Sobrien else 785290075Sobrien { 785390075Sobrien int i; 785490075Sobrien const char * instr = instr1; 785590075Sobrien 785690075Sobrien /* Note that n is never zero here (which would give no output). */ 785790075Sobrien for (i = 0; i < 32; i += 2) 785890075Sobrien { 785990075Sobrien if (n & (3 << i)) 786090075Sobrien { 786190075Sobrien operands[immed_op] = GEN_INT (n & (255 << i)); 786290075Sobrien output_asm_insn (instr, operands); 786390075Sobrien instr = instr2; 786490075Sobrien i += 6; 786590075Sobrien } 786690075Sobrien } 786790075Sobrien } 786890075Sobrien 786990075Sobrien return ""; 787090075Sobrien} 787190075Sobrien 787290075Sobrien/* Return the appropriate ARM instruction for the operation code. 787390075Sobrien The returned result should not be overwritten. OP is the rtx of the 787490075Sobrien operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator 787590075Sobrien was shifted. */ 787690075Sobrienconst char * 7877132718Skanarithmetic_instr (rtx op, int shift_first_arg) 787890075Sobrien{ 787990075Sobrien switch (GET_CODE (op)) 788090075Sobrien { 788190075Sobrien case PLUS: 788290075Sobrien return "add"; 788390075Sobrien 788490075Sobrien case MINUS: 788590075Sobrien return shift_first_arg ? "rsb" : "sub"; 788690075Sobrien 788790075Sobrien case IOR: 788890075Sobrien return "orr"; 788990075Sobrien 789090075Sobrien case XOR: 789190075Sobrien return "eor"; 789290075Sobrien 789390075Sobrien case AND: 789490075Sobrien return "and"; 789590075Sobrien 789690075Sobrien default: 789790075Sobrien abort (); 789890075Sobrien } 789990075Sobrien} 790090075Sobrien 790190075Sobrien/* Ensure valid constant shifts and return the appropriate shift mnemonic 790290075Sobrien for the operation code. The returned result should not be overwritten. 790390075Sobrien OP is the rtx code of the shift. 790490075Sobrien On exit, *AMOUNTP will be -1 if the shift is by a register, or a constant 790590075Sobrien shift. */ 790690075Sobrienstatic const char * 7907132718Skanshift_op (rtx op, HOST_WIDE_INT *amountp) 790890075Sobrien{ 790990075Sobrien const char * mnem; 791090075Sobrien enum rtx_code code = GET_CODE (op); 791190075Sobrien 791290075Sobrien if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG) 791390075Sobrien *amountp = -1; 791490075Sobrien else if (GET_CODE (XEXP (op, 1)) == CONST_INT) 791590075Sobrien *amountp = INTVAL (XEXP (op, 1)); 791690075Sobrien else 791790075Sobrien abort (); 791890075Sobrien 791990075Sobrien switch (code) 792090075Sobrien { 792190075Sobrien case ASHIFT: 792290075Sobrien mnem = "asl"; 792390075Sobrien break; 792490075Sobrien 792590075Sobrien case ASHIFTRT: 792690075Sobrien mnem = "asr"; 792790075Sobrien break; 792890075Sobrien 792990075Sobrien case LSHIFTRT: 793090075Sobrien mnem = "lsr"; 793190075Sobrien break; 793290075Sobrien 793390075Sobrien case ROTATERT: 793490075Sobrien mnem = "ror"; 793590075Sobrien break; 793690075Sobrien 793790075Sobrien case MULT: 793890075Sobrien /* We never have to worry about the amount being other than a 793990075Sobrien power of 2, since this case can never be reloaded from a reg. */ 794090075Sobrien if (*amountp != -1) 794190075Sobrien *amountp = int_log2 (*amountp); 794290075Sobrien else 794390075Sobrien abort (); 794490075Sobrien return "asl"; 794590075Sobrien 794690075Sobrien default: 794790075Sobrien abort (); 794890075Sobrien } 794990075Sobrien 795090075Sobrien if (*amountp != -1) 795190075Sobrien { 795290075Sobrien /* This is not 100% correct, but follows from the desire to merge 795390075Sobrien multiplication by a power of 2 with the recognizer for a 795490075Sobrien shift. >=32 is not a valid shift for "asl", so we must try and 795590075Sobrien output a shift that produces the correct arithmetical result. 795690075Sobrien Using lsr #32 is identical except for the fact that the carry bit 795790075Sobrien is not set correctly if we set the flags; but we never use the 795890075Sobrien carry bit from such an operation, so we can ignore that. */ 795990075Sobrien if (code == ROTATERT) 796090075Sobrien /* Rotate is just modulo 32. */ 796190075Sobrien *amountp &= 31; 796290075Sobrien else if (*amountp != (*amountp & 31)) 796390075Sobrien { 796490075Sobrien if (code == ASHIFT) 796590075Sobrien mnem = "lsr"; 796690075Sobrien *amountp = 32; 796790075Sobrien } 796890075Sobrien 796990075Sobrien /* Shifts of 0 are no-ops. */ 797090075Sobrien if (*amountp == 0) 797190075Sobrien return NULL; 797290075Sobrien } 797390075Sobrien 797490075Sobrien return mnem; 797590075Sobrien} 797690075Sobrien 797790075Sobrien/* Obtain the shift from the POWER of two. */ 797890075Sobrien 797990075Sobrienstatic HOST_WIDE_INT 7980132718Skanint_log2 (HOST_WIDE_INT power) 798190075Sobrien{ 798290075Sobrien HOST_WIDE_INT shift = 0; 798390075Sobrien 798490075Sobrien while ((((HOST_WIDE_INT) 1 << shift) & power) == 0) 798590075Sobrien { 798690075Sobrien if (shift > 31) 798790075Sobrien abort (); 7988132718Skan shift++; 798990075Sobrien } 799090075Sobrien 799190075Sobrien return shift; 799290075Sobrien} 799390075Sobrien 799490075Sobrien/* Output a .ascii pseudo-op, keeping track of lengths. This is because 799590075Sobrien /bin/as is horribly restrictive. */ 799690075Sobrien#define MAX_ASCII_LEN 51 799790075Sobrien 799890075Sobrienvoid 7999132718Skanoutput_ascii_pseudo_op (FILE *stream, const unsigned char *p, int len) 800090075Sobrien{ 800190075Sobrien int i; 800290075Sobrien int len_so_far = 0; 800390075Sobrien 800490075Sobrien fputs ("\t.ascii\t\"", stream); 800590075Sobrien 800690075Sobrien for (i = 0; i < len; i++) 800790075Sobrien { 800890075Sobrien int c = p[i]; 800990075Sobrien 801090075Sobrien if (len_so_far >= MAX_ASCII_LEN) 801190075Sobrien { 801290075Sobrien fputs ("\"\n\t.ascii\t\"", stream); 801390075Sobrien len_so_far = 0; 801490075Sobrien } 801590075Sobrien 801690075Sobrien switch (c) 801790075Sobrien { 801890075Sobrien case TARGET_TAB: 801990075Sobrien fputs ("\\t", stream); 802090075Sobrien len_so_far += 2; 802190075Sobrien break; 802290075Sobrien 802390075Sobrien case TARGET_FF: 802490075Sobrien fputs ("\\f", stream); 802590075Sobrien len_so_far += 2; 802690075Sobrien break; 802790075Sobrien 802890075Sobrien case TARGET_BS: 802990075Sobrien fputs ("\\b", stream); 803090075Sobrien len_so_far += 2; 803190075Sobrien break; 803290075Sobrien 803390075Sobrien case TARGET_CR: 803490075Sobrien fputs ("\\r", stream); 803590075Sobrien len_so_far += 2; 803690075Sobrien break; 803790075Sobrien 803890075Sobrien case TARGET_NEWLINE: 803990075Sobrien fputs ("\\n", stream); 804090075Sobrien c = p [i + 1]; 804190075Sobrien if ((c >= ' ' && c <= '~') 804290075Sobrien || c == TARGET_TAB) 804390075Sobrien /* This is a good place for a line break. */ 804490075Sobrien len_so_far = MAX_ASCII_LEN; 804590075Sobrien else 804690075Sobrien len_so_far += 2; 804790075Sobrien break; 804890075Sobrien 804990075Sobrien case '\"': 805090075Sobrien case '\\': 805190075Sobrien putc ('\\', stream); 805290075Sobrien len_so_far++; 8053132718Skan /* Drop through. */ 805490075Sobrien 805590075Sobrien default: 805690075Sobrien if (c >= ' ' && c <= '~') 805790075Sobrien { 805890075Sobrien putc (c, stream); 805990075Sobrien len_so_far++; 806090075Sobrien } 806190075Sobrien else 806290075Sobrien { 806390075Sobrien fprintf (stream, "\\%03o", c); 806490075Sobrien len_so_far += 4; 806590075Sobrien } 806690075Sobrien break; 806790075Sobrien } 806890075Sobrien } 806990075Sobrien 807090075Sobrien fputs ("\"\n", stream); 807190075Sobrien} 807290075Sobrien 807390075Sobrien/* Compute the register sabe mask for registers 0 through 12 807490075Sobrien inclusive. This code is used by both arm_compute_save_reg_mask 807590075Sobrien and arm_compute_initial_elimination_offset. */ 807690075Sobrienstatic unsigned long 8077132718Skanarm_compute_save_reg0_reg12_mask (void) 807890075Sobrien{ 807990075Sobrien unsigned long func_type = arm_current_func_type (); 808090075Sobrien unsigned int save_reg_mask = 0; 808190075Sobrien unsigned int reg; 808290075Sobrien 808390075Sobrien if (IS_INTERRUPT (func_type)) 808490075Sobrien { 808590075Sobrien unsigned int max_reg; 808690075Sobrien /* Interrupt functions must not corrupt any registers, 808790075Sobrien even call clobbered ones. If this is a leaf function 808890075Sobrien we can just examine the registers used by the RTL, but 808990075Sobrien otherwise we have to assume that whatever function is 809090075Sobrien called might clobber anything, and so we have to save 809190075Sobrien all the call-clobbered registers as well. */ 809290075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_FIQ) 809390075Sobrien /* FIQ handlers have registers r8 - r12 banked, so 809490075Sobrien we only need to check r0 - r7, Normal ISRs only 809590075Sobrien bank r14 and r15, so we must check up to r12. 809690075Sobrien r13 is the stack pointer which is always preserved, 809790075Sobrien so we do not need to consider it here. */ 809890075Sobrien max_reg = 7; 809990075Sobrien else 810090075Sobrien max_reg = 12; 810190075Sobrien 810290075Sobrien for (reg = 0; reg <= max_reg; reg++) 810390075Sobrien if (regs_ever_live[reg] 810490075Sobrien || (! current_function_is_leaf && call_used_regs [reg])) 810590075Sobrien save_reg_mask |= (1 << reg); 810690075Sobrien } 810790075Sobrien else 810890075Sobrien { 810990075Sobrien /* In the normal case we only need to save those registers 811090075Sobrien which are call saved and which are used by this function. */ 811190075Sobrien for (reg = 0; reg <= 10; reg++) 811290075Sobrien if (regs_ever_live[reg] && ! call_used_regs [reg]) 811390075Sobrien save_reg_mask |= (1 << reg); 811490075Sobrien 811590075Sobrien /* Handle the frame pointer as a special case. */ 811690075Sobrien if (! TARGET_APCS_FRAME 811790075Sobrien && ! frame_pointer_needed 811890075Sobrien && regs_ever_live[HARD_FRAME_POINTER_REGNUM] 811990075Sobrien && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) 812090075Sobrien save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM; 812190075Sobrien 812290075Sobrien /* If we aren't loading the PIC register, 812390075Sobrien don't stack it even though it may be live. */ 812490075Sobrien if (flag_pic 812590075Sobrien && ! TARGET_SINGLE_PIC_BASE 812690075Sobrien && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) 812790075Sobrien save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM; 812890075Sobrien } 812990075Sobrien 813090075Sobrien return save_reg_mask; 813190075Sobrien} 813290075Sobrien 813390075Sobrien/* Compute a bit mask of which registers need to be 813490075Sobrien saved on the stack for the current function. */ 813590075Sobrien 813690075Sobrienstatic unsigned long 8137132718Skanarm_compute_save_reg_mask (void) 813890075Sobrien{ 813990075Sobrien unsigned int save_reg_mask = 0; 814090075Sobrien unsigned long func_type = arm_current_func_type (); 814190075Sobrien 814290075Sobrien if (IS_NAKED (func_type)) 814390075Sobrien /* This should never really happen. */ 814490075Sobrien return 0; 814590075Sobrien 814690075Sobrien /* If we are creating a stack frame, then we must save the frame pointer, 814790075Sobrien IP (which will hold the old stack pointer), LR and the PC. */ 814890075Sobrien if (frame_pointer_needed) 814990075Sobrien save_reg_mask |= 815090075Sobrien (1 << ARM_HARD_FRAME_POINTER_REGNUM) 815190075Sobrien | (1 << IP_REGNUM) 815290075Sobrien | (1 << LR_REGNUM) 815390075Sobrien | (1 << PC_REGNUM); 815490075Sobrien 815590075Sobrien /* Volatile functions do not return, so there 815690075Sobrien is no need to save any other registers. */ 815790075Sobrien if (IS_VOLATILE (func_type)) 815890075Sobrien return save_reg_mask; 815990075Sobrien 816090075Sobrien save_reg_mask |= arm_compute_save_reg0_reg12_mask (); 816190075Sobrien 816290075Sobrien /* Decide if we need to save the link register. 816390075Sobrien Interrupt routines have their own banked link register, 816490075Sobrien so they never need to save it. 816596263Sobrien Otherwise if we do not use the link register we do not need to save 816690075Sobrien it. If we are pushing other registers onto the stack however, we 816790075Sobrien can save an instruction in the epilogue by pushing the link register 816890075Sobrien now and then popping it back into the PC. This incurs extra memory 8169132718Skan accesses though, so we only do it when optimizing for size, and only 817090075Sobrien if we know that we will not need a fancy return sequence. */ 817196263Sobrien if (regs_ever_live [LR_REGNUM] 817290075Sobrien || (save_reg_mask 817390075Sobrien && optimize_size 817496263Sobrien && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)) 817590075Sobrien save_reg_mask |= 1 << LR_REGNUM; 817690075Sobrien 817790075Sobrien if (cfun->machine->lr_save_eliminated) 817890075Sobrien save_reg_mask &= ~ (1 << LR_REGNUM); 817990075Sobrien 8180132718Skan if (TARGET_REALLY_IWMMXT 8181132718Skan && ((bit_count (save_reg_mask) 8182132718Skan + ARM_NUM_INTS (current_function_pretend_args_size)) % 2) != 0) 8183132718Skan { 8184132718Skan unsigned int reg; 8185132718Skan 8186132718Skan /* The total number of registers that are going to be pushed 8187132718Skan onto the stack is odd. We need to ensure that the stack 8188132718Skan is 64-bit aligned before we start to save iWMMXt registers, 8189132718Skan and also before we start to create locals. (A local variable 8190132718Skan might be a double or long long which we will load/store using 8191132718Skan an iWMMXt instruction). Therefore we need to push another 8192132718Skan ARM register, so that the stack will be 64-bit aligned. We 8193132718Skan try to avoid using the arg registers (r0 -r3) as they might be 8194132718Skan used to pass values in a tail call. */ 8195132718Skan for (reg = 4; reg <= 12; reg++) 8196132718Skan if ((save_reg_mask & (1 << reg)) == 0) 8197132718Skan break; 8198132718Skan 8199132718Skan if (reg <= 12) 8200132718Skan save_reg_mask |= (1 << reg); 8201132718Skan else 8202132718Skan { 8203132718Skan cfun->machine->sibcall_blocked = 1; 8204132718Skan save_reg_mask |= (1 << 3); 8205132718Skan } 8206132718Skan } 8207132718Skan 820890075Sobrien return save_reg_mask; 820990075Sobrien} 821090075Sobrien 8211132718Skan/* Generate a function exit sequence. If REALLY_RETURN is false, then do 821290075Sobrien everything bar the final return instruction. */ 821390075Sobrienconst char * 8214132718Skanoutput_return_instruction (rtx operand, int really_return, int reverse) 821590075Sobrien{ 821690075Sobrien char conditional[10]; 821790075Sobrien char instr[100]; 821890075Sobrien int reg; 821990075Sobrien unsigned long live_regs_mask; 822090075Sobrien unsigned long func_type; 8221117395Skan 822290075Sobrien func_type = arm_current_func_type (); 822390075Sobrien 822490075Sobrien if (IS_NAKED (func_type)) 822590075Sobrien return ""; 822690075Sobrien 822790075Sobrien if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) 822890075Sobrien { 8229132718Skan /* If this function was declared non-returning, and we have 8230132718Skan found a tail call, then we have to trust that the called 8231132718Skan function won't return. */ 823290075Sobrien if (really_return) 823390075Sobrien { 823490075Sobrien rtx ops[2]; 823590075Sobrien 823690075Sobrien /* Otherwise, trap an attempted return by aborting. */ 823790075Sobrien ops[0] = operand; 823890075Sobrien ops[1] = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" 823990075Sobrien : "abort"); 824090075Sobrien assemble_external_libcall (ops[1]); 824190075Sobrien output_asm_insn (reverse ? "bl%D0\t%a1" : "bl%d0\t%a1", ops); 824290075Sobrien } 824390075Sobrien 824490075Sobrien return ""; 824590075Sobrien } 824690075Sobrien 824790075Sobrien if (current_function_calls_alloca && !really_return) 824890075Sobrien abort (); 824990075Sobrien 825090075Sobrien sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd'); 825190075Sobrien 825290075Sobrien return_used_this_function = 1; 825390075Sobrien 825490075Sobrien live_regs_mask = arm_compute_save_reg_mask (); 825590075Sobrien 825696263Sobrien if (live_regs_mask) 825790075Sobrien { 825896263Sobrien const char * return_reg; 825996263Sobrien 826096263Sobrien /* If we do not have any special requirements for function exit 826196263Sobrien (eg interworking, or ISR) then we can load the return address 826296263Sobrien directly into the PC. Otherwise we must load it into LR. */ 826396263Sobrien if (really_return 826496263Sobrien && ! TARGET_INTERWORK) 826596263Sobrien return_reg = reg_names[PC_REGNUM]; 826690075Sobrien else 826796263Sobrien return_reg = reg_names[LR_REGNUM]; 826896263Sobrien 826990075Sobrien if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM)) 8270132718Skan { 8271132718Skan /* There are three possible reasons for the IP register 8272132718Skan being saved. 1) a stack frame was created, in which case 8273132718Skan IP contains the old stack pointer, or 2) an ISR routine 8274132718Skan corrupted it, or 3) it was saved to align the stack on 8275132718Skan iWMMXt. In case 1, restore IP into SP, otherwise just 8276132718Skan restore IP. */ 8277132718Skan if (frame_pointer_needed) 8278132718Skan { 8279132718Skan live_regs_mask &= ~ (1 << IP_REGNUM); 8280132718Skan live_regs_mask |= (1 << SP_REGNUM); 8281132718Skan } 8282132718Skan else 8283132718Skan { 8284132718Skan if (! IS_INTERRUPT (func_type) 8285132718Skan && ! TARGET_REALLY_IWMMXT) 8286132718Skan abort (); 8287132718Skan } 8288132718Skan } 828990075Sobrien 829096263Sobrien /* On some ARM architectures it is faster to use LDR rather than 829196263Sobrien LDM to load a single register. On other architectures, the 829296263Sobrien cost is the same. In 26 bit mode, or for exception handlers, 829396263Sobrien we have to use LDM to load the PC so that the CPSR is also 829496263Sobrien restored. */ 829596263Sobrien for (reg = 0; reg <= LAST_ARM_REGNUM; reg++) 829690075Sobrien { 829796263Sobrien if (live_regs_mask == (unsigned int)(1 << reg)) 829896263Sobrien break; 829990075Sobrien } 830096263Sobrien if (reg <= LAST_ARM_REGNUM 830196263Sobrien && (reg != LR_REGNUM 830296263Sobrien || ! really_return 830396263Sobrien || (TARGET_APCS_32 && ! IS_INTERRUPT (func_type)))) 830496263Sobrien { 830596263Sobrien sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional, 830696263Sobrien (reg == LR_REGNUM) ? return_reg : reg_names[reg]); 830796263Sobrien } 830890075Sobrien else 830990075Sobrien { 831096263Sobrien char *p; 831196263Sobrien int first = 1; 831290075Sobrien 8313132718Skan /* Generate the load multiple instruction to restore the 8314132718Skan registers. Note we can get here, even if 8315132718Skan frame_pointer_needed is true, but only if sp already 8316132718Skan points to the base of the saved core registers. */ 8317132718Skan if (live_regs_mask & (1 << SP_REGNUM)) 8318132718Skan { 8319132718Skan unsigned HOST_WIDE_INT stack_adjust = 8320132718Skan arm_get_frame_size () + current_function_outgoing_args_size; 8321132718Skan 8322132718Skan if (stack_adjust != 0 && stack_adjust != 4) 8323132718Skan abort (); 8324132718Skan 8325132718Skan if (stack_adjust && arm_arch5) 8326132718Skan sprintf (instr, "ldm%sib\t%%|sp, {", conditional); 8327132718Skan else 8328132718Skan { 8329132718Skan /* If we can't use ldmib (SA110 bug), then try to pop r3 8330132718Skan instead. */ 8331132718Skan if (stack_adjust) 8332132718Skan live_regs_mask |= 1 << 3; 8333132718Skan sprintf (instr, "ldm%sfd\t%%|sp, {", conditional); 8334132718Skan } 8335132718Skan } 833690075Sobrien else 833796263Sobrien sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional); 833890075Sobrien 833996263Sobrien p = instr + strlen (instr); 834090075Sobrien 834196263Sobrien for (reg = 0; reg <= SP_REGNUM; reg++) 834296263Sobrien if (live_regs_mask & (1 << reg)) 834396263Sobrien { 834496263Sobrien int l = strlen (reg_names[reg]); 834590075Sobrien 834696263Sobrien if (first) 834796263Sobrien first = 0; 834896263Sobrien else 834996263Sobrien { 835096263Sobrien memcpy (p, ", ", 2); 835196263Sobrien p += 2; 835296263Sobrien } 835390075Sobrien 835496263Sobrien memcpy (p, "%|", 2); 835596263Sobrien memcpy (p + 2, reg_names[reg], l); 835696263Sobrien p += l + 2; 835796263Sobrien } 835896263Sobrien 835996263Sobrien if (live_regs_mask & (1 << LR_REGNUM)) 836096263Sobrien { 8361132718Skan sprintf (p, "%s%%|%s}", first ? "" : ", ", return_reg); 8362132718Skan /* Decide if we need to add the ^ symbol to the end of the 8363132718Skan register list. This causes the saved condition codes 8364132718Skan register to be copied into the current condition codes 8365132718Skan register. We do the copy if we are conforming to the 32-bit 8366132718Skan ABI and this is an interrupt function, or if we are 8367132718Skan conforming to the 26-bit ABI. There is a special case for 8368132718Skan the 26-bit ABI however, which is if we are writing back the 8369132718Skan stack pointer but not loading the PC. In this case adding 8370132718Skan the ^ symbol would create a type 2 LDM instruction, where 8371132718Skan writeback is UNPREDICTABLE. We are safe in leaving the ^ 8372132718Skan character off in this case however, since the actual return 8373132718Skan instruction will be a MOVS which will restore the CPSR. */ 8374132718Skan if ((TARGET_APCS_32 && IS_INTERRUPT (func_type)) 8375132718Skan || (! TARGET_APCS_32 && really_return)) 8376132718Skan strcat (p, "^"); 837790075Sobrien } 837896263Sobrien else 837996263Sobrien strcpy (p, "}"); 838090075Sobrien } 838196263Sobrien 838296263Sobrien output_asm_insn (instr, & operand); 838396263Sobrien 838496263Sobrien /* See if we need to generate an extra instruction to 838596263Sobrien perform the actual function return. */ 838696263Sobrien if (really_return 838796263Sobrien && func_type != ARM_FT_INTERWORKED 838896263Sobrien && (live_regs_mask & (1 << LR_REGNUM)) != 0) 838996263Sobrien { 839096263Sobrien /* The return has already been handled 839196263Sobrien by loading the LR into the PC. */ 839296263Sobrien really_return = 0; 839396263Sobrien } 839490075Sobrien } 8395117395Skan 839696263Sobrien if (really_return) 839790075Sobrien { 839890075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 839990075Sobrien { 840090075Sobrien case ARM_FT_ISR: 840190075Sobrien case ARM_FT_FIQ: 840290075Sobrien sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional); 840390075Sobrien break; 840490075Sobrien 840590075Sobrien case ARM_FT_INTERWORKED: 840690075Sobrien sprintf (instr, "bx%s\t%%|lr", conditional); 840790075Sobrien break; 840890075Sobrien 840990075Sobrien case ARM_FT_EXCEPTION: 841090075Sobrien sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional); 841190075Sobrien break; 841290075Sobrien 841390075Sobrien default: 841496263Sobrien /* ARMv5 implementations always provide BX, so interworking 841596263Sobrien is the default unless APCS-26 is in use. */ 841696263Sobrien if ((insn_flags & FL_ARCH5) != 0 && TARGET_APCS_32) 841796263Sobrien sprintf (instr, "bx%s\t%%|lr", conditional); 841896263Sobrien else 841996263Sobrien sprintf (instr, "mov%s%s\t%%|pc, %%|lr", 842096263Sobrien conditional, TARGET_APCS_32 ? "" : "s"); 842190075Sobrien break; 842290075Sobrien } 842396263Sobrien 842496263Sobrien output_asm_insn (instr, & operand); 842590075Sobrien } 842690075Sobrien 842790075Sobrien return ""; 842890075Sobrien} 842990075Sobrien 843090075Sobrien/* Write the function name into the code section, directly preceding 843190075Sobrien the function prologue. 843290075Sobrien 843390075Sobrien Code will be output similar to this: 843490075Sobrien t0 843590075Sobrien .ascii "arm_poke_function_name", 0 843690075Sobrien .align 843790075Sobrien t1 843890075Sobrien .word 0xff000000 + (t1 - t0) 843990075Sobrien arm_poke_function_name 844090075Sobrien mov ip, sp 844190075Sobrien stmfd sp!, {fp, ip, lr, pc} 844290075Sobrien sub fp, ip, #4 844390075Sobrien 844490075Sobrien When performing a stack backtrace, code can inspect the value 844590075Sobrien of 'pc' stored at 'fp' + 0. If the trace function then looks 844690075Sobrien at location pc - 12 and the top 8 bits are set, then we know 844790075Sobrien that there is a function name embedded immediately preceding this 844890075Sobrien location and has length ((pc[-3]) & 0xff000000). 844990075Sobrien 845090075Sobrien We assume that pc is declared as a pointer to an unsigned long. 845190075Sobrien 845290075Sobrien It is of no benefit to output the function name if we are assembling 845390075Sobrien a leaf function. These function types will not contain a stack 845490075Sobrien backtrace structure, therefore it is not possible to determine the 845590075Sobrien function name. */ 845690075Sobrienvoid 8457132718Skanarm_poke_function_name (FILE *stream, const char *name) 845890075Sobrien{ 845990075Sobrien unsigned long alignlength; 846090075Sobrien unsigned long length; 846190075Sobrien rtx x; 846290075Sobrien 846390075Sobrien length = strlen (name) + 1; 8464132718Skan alignlength = ROUND_UP_WORD (length); 846590075Sobrien 846690075Sobrien ASM_OUTPUT_ASCII (stream, name, length); 846790075Sobrien ASM_OUTPUT_ALIGN (stream, 2); 846890075Sobrien x = GEN_INT ((unsigned HOST_WIDE_INT) 0xff000000 + alignlength); 846990075Sobrien assemble_aligned_integer (UNITS_PER_WORD, x); 847090075Sobrien} 847190075Sobrien 847290075Sobrien/* Place some comments into the assembler stream 847390075Sobrien describing the current function. */ 847490075Sobrienstatic void 8475132718Skanarm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size) 847690075Sobrien{ 847790075Sobrien unsigned long func_type; 847890075Sobrien 847990075Sobrien if (!TARGET_ARM) 848090075Sobrien { 848190075Sobrien thumb_output_function_prologue (f, frame_size); 848290075Sobrien return; 848390075Sobrien } 848490075Sobrien 848590075Sobrien /* Sanity check. */ 848690075Sobrien if (arm_ccfsm_state || arm_target_insn) 848790075Sobrien abort (); 848890075Sobrien 848990075Sobrien func_type = arm_current_func_type (); 849090075Sobrien 849190075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 849290075Sobrien { 849390075Sobrien default: 849490075Sobrien case ARM_FT_NORMAL: 849590075Sobrien break; 849690075Sobrien case ARM_FT_INTERWORKED: 849790075Sobrien asm_fprintf (f, "\t%@ Function supports interworking.\n"); 849890075Sobrien break; 849990075Sobrien case ARM_FT_EXCEPTION_HANDLER: 850090075Sobrien asm_fprintf (f, "\t%@ C++ Exception Handler.\n"); 850190075Sobrien break; 850290075Sobrien case ARM_FT_ISR: 850390075Sobrien asm_fprintf (f, "\t%@ Interrupt Service Routine.\n"); 850490075Sobrien break; 850590075Sobrien case ARM_FT_FIQ: 850690075Sobrien asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n"); 850790075Sobrien break; 850890075Sobrien case ARM_FT_EXCEPTION: 850990075Sobrien asm_fprintf (f, "\t%@ ARM Exception Handler.\n"); 851090075Sobrien break; 851190075Sobrien } 851290075Sobrien 851390075Sobrien if (IS_NAKED (func_type)) 851490075Sobrien asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n"); 851590075Sobrien 851690075Sobrien if (IS_VOLATILE (func_type)) 851790075Sobrien asm_fprintf (f, "\t%@ Volatile: function does not return.\n"); 851890075Sobrien 851990075Sobrien if (IS_NESTED (func_type)) 852090075Sobrien asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n"); 852190075Sobrien 8522132718Skan asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %wd\n", 852390075Sobrien current_function_args_size, 852490075Sobrien current_function_pretend_args_size, frame_size); 852590075Sobrien 852696263Sobrien asm_fprintf (f, "\t%@ frame_needed = %d, uses_anonymous_args = %d\n", 852790075Sobrien frame_pointer_needed, 852896263Sobrien cfun->machine->uses_anonymous_args); 852990075Sobrien 853090075Sobrien if (cfun->machine->lr_save_eliminated) 853190075Sobrien asm_fprintf (f, "\t%@ link register save eliminated.\n"); 853290075Sobrien 853390075Sobrien#ifdef AOF_ASSEMBLER 853490075Sobrien if (flag_pic) 853590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM); 853690075Sobrien#endif 853790075Sobrien 853890075Sobrien return_used_this_function = 0; 853990075Sobrien} 854090075Sobrien 854190075Sobrienconst char * 8542132718Skanarm_output_epilogue (rtx sibling) 854390075Sobrien{ 854490075Sobrien int reg; 854590075Sobrien unsigned long saved_regs_mask; 854690075Sobrien unsigned long func_type; 854796263Sobrien /* Floats_offset is the offset from the "virtual" frame. In an APCS 854896263Sobrien frame that is $fp + 4 for a non-variadic function. */ 854996263Sobrien int floats_offset = 0; 855090075Sobrien rtx operands[3]; 8551117395Skan int frame_size = arm_get_frame_size (); 855290075Sobrien FILE * f = asm_out_file; 855390075Sobrien rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; 8554132718Skan unsigned int lrm_count = 0; 8555132718Skan int really_return = (sibling == NULL); 855690075Sobrien 855790075Sobrien /* If we have already generated the return instruction 855890075Sobrien then it is futile to generate anything else. */ 8559132718Skan if (use_return_insn (FALSE, sibling) && return_used_this_function) 856090075Sobrien return ""; 856190075Sobrien 856290075Sobrien func_type = arm_current_func_type (); 856390075Sobrien 856490075Sobrien if (IS_NAKED (func_type)) 856590075Sobrien /* Naked functions don't have epilogues. */ 856690075Sobrien return ""; 856790075Sobrien 856890075Sobrien if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN) 856990075Sobrien { 857090075Sobrien rtx op; 857190075Sobrien 857290075Sobrien /* A volatile function should never return. Call abort. */ 857390075Sobrien op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort"); 857490075Sobrien assemble_external_libcall (op); 857590075Sobrien output_asm_insn ("bl\t%a0", &op); 857690075Sobrien 857790075Sobrien return ""; 857890075Sobrien } 857990075Sobrien 858090075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER 858190075Sobrien && ! really_return) 858290075Sobrien /* If we are throwing an exception, then we really must 858390075Sobrien be doing a return, so we can't tail-call. */ 858490075Sobrien abort (); 858590075Sobrien 858690075Sobrien saved_regs_mask = arm_compute_save_reg_mask (); 8587132718Skan 8588132718Skan if (TARGET_IWMMXT) 8589132718Skan lrm_count = bit_count (saved_regs_mask); 8590132718Skan 859196263Sobrien /* XXX We should adjust floats_offset for any anonymous args, and then 859296263Sobrien re-adjust vfp_offset below to compensate. */ 859396263Sobrien 859490075Sobrien /* Compute how far away the floats will be. */ 8595132718Skan for (reg = 0; reg <= LAST_ARM_REGNUM; reg++) 859690075Sobrien if (saved_regs_mask & (1 << reg)) 859790075Sobrien floats_offset += 4; 859890075Sobrien 859990075Sobrien if (frame_pointer_needed) 860090075Sobrien { 860196263Sobrien int vfp_offset = 4; 860296263Sobrien 8603132718Skan if (arm_fpu_arch == FPUTYPE_FPA_EMU2) 860490075Sobrien { 860590075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 860690075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 860790075Sobrien { 860890075Sobrien floats_offset += 12; 860990075Sobrien asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n", 861096263Sobrien reg, FP_REGNUM, floats_offset - vfp_offset); 861190075Sobrien } 861290075Sobrien } 861390075Sobrien else 861490075Sobrien { 861590075Sobrien int start_reg = LAST_ARM_FP_REGNUM; 861690075Sobrien 861790075Sobrien for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 861890075Sobrien { 861990075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 862090075Sobrien { 862190075Sobrien floats_offset += 12; 862290075Sobrien 862390075Sobrien /* We can't unstack more than four registers at once. */ 862490075Sobrien if (start_reg - reg == 3) 862590075Sobrien { 862690075Sobrien asm_fprintf (f, "\tlfm\t%r, 4, [%r, #-%d]\n", 862796263Sobrien reg, FP_REGNUM, floats_offset - vfp_offset); 862890075Sobrien start_reg = reg - 1; 862990075Sobrien } 863090075Sobrien } 863190075Sobrien else 863290075Sobrien { 863390075Sobrien if (reg != start_reg) 863490075Sobrien asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", 863590075Sobrien reg + 1, start_reg - reg, 863696263Sobrien FP_REGNUM, floats_offset - vfp_offset); 863790075Sobrien start_reg = reg - 1; 863890075Sobrien } 863990075Sobrien } 864090075Sobrien 864190075Sobrien /* Just in case the last register checked also needs unstacking. */ 864290075Sobrien if (reg != start_reg) 864390075Sobrien asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n", 864490075Sobrien reg + 1, start_reg - reg, 864596263Sobrien FP_REGNUM, floats_offset - vfp_offset); 864690075Sobrien } 864790075Sobrien 8648132718Skan if (TARGET_IWMMXT) 8649132718Skan { 8650132718Skan /* The frame pointer is guaranteed to be non-double-word aligned. 8651132718Skan This is because it is set to (old_stack_pointer - 4) and the 8652132718Skan old_stack_pointer was double word aligned. Thus the offset to 8653132718Skan the iWMMXt registers to be loaded must also be non-double-word 8654132718Skan sized, so that the resultant address *is* double-word aligned. 8655132718Skan We can ignore floats_offset since that was already included in 8656132718Skan the live_regs_mask. */ 8657132718Skan lrm_count += (lrm_count % 2 ? 2 : 1); 8658132718Skan 8659132718Skan for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) 8660132718Skan if (regs_ever_live[reg] && !call_used_regs[reg]) 8661132718Skan { 8662132718Skan asm_fprintf (f, "\twldrd\t%r, [%r, #-%d]\n", 8663132718Skan reg, FP_REGNUM, lrm_count * 4); 8664132718Skan lrm_count += 2; 8665132718Skan } 8666132718Skan } 8667132718Skan 866890075Sobrien /* saved_regs_mask should contain the IP, which at the time of stack 866990075Sobrien frame generation actually contains the old stack pointer. So a 867090075Sobrien quick way to unwind the stack is just pop the IP register directly 867190075Sobrien into the stack pointer. */ 867290075Sobrien if ((saved_regs_mask & (1 << IP_REGNUM)) == 0) 867390075Sobrien abort (); 867490075Sobrien saved_regs_mask &= ~ (1 << IP_REGNUM); 867590075Sobrien saved_regs_mask |= (1 << SP_REGNUM); 867690075Sobrien 867790075Sobrien /* There are two registers left in saved_regs_mask - LR and PC. We 867890075Sobrien only need to restore the LR register (the return address), but to 867990075Sobrien save time we can load it directly into the PC, unless we need a 868090075Sobrien special function exit sequence, or we are not really returning. */ 868190075Sobrien if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL) 868290075Sobrien /* Delete the LR from the register mask, so that the LR on 868390075Sobrien the stack is loaded into the PC in the register mask. */ 868490075Sobrien saved_regs_mask &= ~ (1 << LR_REGNUM); 868590075Sobrien else 868690075Sobrien saved_regs_mask &= ~ (1 << PC_REGNUM); 868790075Sobrien 8688132718Skan /* We must use SP as the base register, because SP is one of the 8689132718Skan registers being restored. If an interrupt or page fault 8690132718Skan happens in the ldm instruction, the SP might or might not 8691132718Skan have been restored. That would be bad, as then SP will no 8692132718Skan longer indicate the safe area of stack, and we can get stack 8693132718Skan corruption. Using SP as the base register means that it will 8694132718Skan be reset correctly to the original value, should an interrupt 8695132718Skan occur. If the stack pointer already points at the right 8696132718Skan place, then omit the subtraction. */ 8697132718Skan if (((frame_size + current_function_outgoing_args_size + floats_offset) 8698132718Skan != 4 * (1 + (int) bit_count (saved_regs_mask))) 8699132718Skan || current_function_calls_alloca) 8700132718Skan asm_fprintf (f, "\tsub\t%r, %r, #%d\n", SP_REGNUM, FP_REGNUM, 8701132718Skan 4 * bit_count (saved_regs_mask)); 8702132718Skan print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask); 8703132718Skan 870490075Sobrien if (IS_INTERRUPT (func_type)) 870590075Sobrien /* Interrupt handlers will have pushed the 870690075Sobrien IP onto the stack, so restore it now. */ 8707117395Skan print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, 1 << IP_REGNUM); 870890075Sobrien } 870990075Sobrien else 871090075Sobrien { 871190075Sobrien /* Restore stack pointer if necessary. */ 871290075Sobrien if (frame_size + current_function_outgoing_args_size != 0) 871390075Sobrien { 871490075Sobrien operands[0] = operands[1] = stack_pointer_rtx; 871590075Sobrien operands[2] = GEN_INT (frame_size 871690075Sobrien + current_function_outgoing_args_size); 871790075Sobrien output_add_immediate (operands); 871890075Sobrien } 871990075Sobrien 8720132718Skan if (arm_fpu_arch == FPUTYPE_FPA_EMU2) 872190075Sobrien { 872290075Sobrien for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) 872390075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 872490075Sobrien asm_fprintf (f, "\tldfe\t%r, [%r], #12\n", 872590075Sobrien reg, SP_REGNUM); 872690075Sobrien } 872790075Sobrien else 872890075Sobrien { 872990075Sobrien int start_reg = FIRST_ARM_FP_REGNUM; 873090075Sobrien 873190075Sobrien for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) 873290075Sobrien { 873390075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 873490075Sobrien { 873590075Sobrien if (reg - start_reg == 3) 873690075Sobrien { 873790075Sobrien asm_fprintf (f, "\tlfmfd\t%r, 4, [%r]!\n", 873890075Sobrien start_reg, SP_REGNUM); 873990075Sobrien start_reg = reg + 1; 874090075Sobrien } 874190075Sobrien } 874290075Sobrien else 874390075Sobrien { 874490075Sobrien if (reg != start_reg) 874590075Sobrien asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", 874690075Sobrien start_reg, reg - start_reg, 874790075Sobrien SP_REGNUM); 874890075Sobrien 874990075Sobrien start_reg = reg + 1; 875090075Sobrien } 875190075Sobrien } 875290075Sobrien 875390075Sobrien /* Just in case the last register checked also needs unstacking. */ 875490075Sobrien if (reg != start_reg) 875590075Sobrien asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n", 875690075Sobrien start_reg, reg - start_reg, SP_REGNUM); 875790075Sobrien } 875890075Sobrien 8759132718Skan if (TARGET_IWMMXT) 8760132718Skan for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) 8761132718Skan if (regs_ever_live[reg] && !call_used_regs[reg]) 8762132718Skan asm_fprintf (f, "\twldrd\t%r, [%r, #+8]!\n", reg, SP_REGNUM); 8763132718Skan 876490075Sobrien /* If we can, restore the LR into the PC. */ 876590075Sobrien if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL 876690075Sobrien && really_return 876790075Sobrien && current_function_pretend_args_size == 0 876890075Sobrien && saved_regs_mask & (1 << LR_REGNUM)) 876990075Sobrien { 877090075Sobrien saved_regs_mask &= ~ (1 << LR_REGNUM); 877190075Sobrien saved_regs_mask |= (1 << PC_REGNUM); 877290075Sobrien } 877390075Sobrien 877490075Sobrien /* Load the registers off the stack. If we only have one register 877590075Sobrien to load use the LDR instruction - it is faster. */ 877690075Sobrien if (saved_regs_mask == (1 << LR_REGNUM)) 877790075Sobrien { 877896263Sobrien /* The exception handler ignores the LR, so we do 877990075Sobrien not really need to load it off the stack. */ 878090075Sobrien if (eh_ofs) 878190075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM); 878290075Sobrien else 878390075Sobrien asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM); 878490075Sobrien } 878590075Sobrien else if (saved_regs_mask) 8786117395Skan { 8787117395Skan if (saved_regs_mask & (1 << SP_REGNUM)) 8788117395Skan /* Note - write back to the stack register is not enabled 8789117395Skan (ie "ldmfd sp!..."). We know that the stack pointer is 8790117395Skan in the list of registers and if we add writeback the 8791117395Skan instruction becomes UNPREDICTABLE. */ 8792117395Skan print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask); 8793117395Skan else 8794117395Skan print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask); 8795117395Skan } 879690075Sobrien 879790075Sobrien if (current_function_pretend_args_size) 879890075Sobrien { 879990075Sobrien /* Unwind the pre-pushed regs. */ 880090075Sobrien operands[0] = operands[1] = stack_pointer_rtx; 880190075Sobrien operands[2] = GEN_INT (current_function_pretend_args_size); 880290075Sobrien output_add_immediate (operands); 880390075Sobrien } 880490075Sobrien } 880590075Sobrien 880696263Sobrien if (! really_return 880796263Sobrien || (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL 880896263Sobrien && current_function_pretend_args_size == 0 880996263Sobrien && saved_regs_mask & (1 << PC_REGNUM))) 881090075Sobrien return ""; 881190075Sobrien 881290075Sobrien /* Generate the return instruction. */ 881390075Sobrien switch ((int) ARM_FUNC_TYPE (func_type)) 881490075Sobrien { 881590075Sobrien case ARM_FT_EXCEPTION_HANDLER: 881690075Sobrien /* Even in 26-bit mode we do a mov (rather than a movs) 881790075Sobrien because we don't have the PSR bits set in the address. */ 881890075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM); 881990075Sobrien break; 882090075Sobrien 882190075Sobrien case ARM_FT_ISR: 882290075Sobrien case ARM_FT_FIQ: 882390075Sobrien asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM); 882490075Sobrien break; 882590075Sobrien 882690075Sobrien case ARM_FT_EXCEPTION: 882790075Sobrien asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); 882890075Sobrien break; 882990075Sobrien 883090075Sobrien case ARM_FT_INTERWORKED: 883190075Sobrien asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM); 883290075Sobrien break; 883390075Sobrien 883490075Sobrien default: 883590075Sobrien if (frame_pointer_needed) 8836117395Skan /* If we used the frame pointer then the return address 883790075Sobrien will have been loaded off the stack directly into the 883890075Sobrien PC, so there is no need to issue a MOV instruction 883990075Sobrien here. */ 884090075Sobrien ; 884190075Sobrien else if (current_function_pretend_args_size == 0 884290075Sobrien && (saved_regs_mask & (1 << LR_REGNUM))) 884390075Sobrien /* Similarly we may have been able to load LR into the PC 884490075Sobrien even if we did not create a stack frame. */ 884590075Sobrien ; 884690075Sobrien else if (TARGET_APCS_32) 884790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM); 884890075Sobrien else 884990075Sobrien asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM); 885090075Sobrien break; 885190075Sobrien } 885290075Sobrien 885390075Sobrien return ""; 885490075Sobrien} 885590075Sobrien 885690075Sobrienstatic void 8857132718Skanarm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, 8858132718Skan HOST_WIDE_INT frame_size) 885990075Sobrien{ 886090075Sobrien if (TARGET_THUMB) 886190075Sobrien { 886290075Sobrien /* ??? Probably not safe to set this here, since it assumes that a 886390075Sobrien function will be emitted as assembly immediately after we generate 886490075Sobrien RTL for it. This does not happen for inline functions. */ 886590075Sobrien return_used_this_function = 0; 886690075Sobrien } 886790075Sobrien else 886890075Sobrien { 8869117395Skan /* We need to take into account any stack-frame rounding. */ 8870117395Skan frame_size = arm_get_frame_size (); 8871117395Skan 8872132718Skan if (use_return_insn (FALSE, NULL) 887390075Sobrien && return_used_this_function 887490075Sobrien && (frame_size + current_function_outgoing_args_size) != 0 887590075Sobrien && !frame_pointer_needed) 887690075Sobrien abort (); 887790075Sobrien 887890075Sobrien /* Reset the ARM-specific per-function variables. */ 887990075Sobrien after_arm_reorg = 0; 888090075Sobrien } 888190075Sobrien} 888290075Sobrien 888390075Sobrien/* Generate and emit an insn that we will recognize as a push_multi. 888490075Sobrien Unfortunately, since this insn does not reflect very well the actual 888590075Sobrien semantics of the operation, we need to annotate the insn for the benefit 888690075Sobrien of DWARF2 frame unwind information. */ 888790075Sobrienstatic rtx 8888132718Skanemit_multi_reg_push (int mask) 888990075Sobrien{ 889090075Sobrien int num_regs = 0; 889190075Sobrien int num_dwarf_regs; 889290075Sobrien int i, j; 889390075Sobrien rtx par; 889490075Sobrien rtx dwarf; 889590075Sobrien int dwarf_par_index; 889690075Sobrien rtx tmp, reg; 889790075Sobrien 889890075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 889990075Sobrien if (mask & (1 << i)) 890090075Sobrien num_regs++; 890190075Sobrien 890290075Sobrien if (num_regs == 0 || num_regs > 16) 890390075Sobrien abort (); 890490075Sobrien 890590075Sobrien /* We don't record the PC in the dwarf frame information. */ 890690075Sobrien num_dwarf_regs = num_regs; 890790075Sobrien if (mask & (1 << PC_REGNUM)) 890890075Sobrien num_dwarf_regs--; 890990075Sobrien 891090075Sobrien /* For the body of the insn we are going to generate an UNSPEC in 8911117395Skan parallel with several USEs. This allows the insn to be recognized 891290075Sobrien by the push_multi pattern in the arm.md file. The insn looks 891390075Sobrien something like this: 891490075Sobrien 891590075Sobrien (parallel [ 891690075Sobrien (set (mem:BLK (pre_dec:BLK (reg:SI sp))) 891790075Sobrien (unspec:BLK [(reg:SI r4)] UNSPEC_PUSH_MULT)) 891890075Sobrien (use (reg:SI 11 fp)) 891990075Sobrien (use (reg:SI 12 ip)) 892090075Sobrien (use (reg:SI 14 lr)) 892190075Sobrien (use (reg:SI 15 pc)) 892290075Sobrien ]) 892390075Sobrien 892490075Sobrien For the frame note however, we try to be more explicit and actually 892590075Sobrien show each register being stored into the stack frame, plus a (single) 892690075Sobrien decrement of the stack pointer. We do it this way in order to be 892790075Sobrien friendly to the stack unwinding code, which only wants to see a single 892890075Sobrien stack decrement per instruction. The RTL we generate for the note looks 892990075Sobrien something like this: 893090075Sobrien 893190075Sobrien (sequence [ 893290075Sobrien (set (reg:SI sp) (plus:SI (reg:SI sp) (const_int -20))) 893390075Sobrien (set (mem:SI (reg:SI sp)) (reg:SI r4)) 893490075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 4))) (reg:SI fp)) 893590075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 8))) (reg:SI ip)) 893690075Sobrien (set (mem:SI (plus:SI (reg:SI sp) (const_int 12))) (reg:SI lr)) 893790075Sobrien ]) 893890075Sobrien 893990075Sobrien This sequence is used both by the code to support stack unwinding for 894090075Sobrien exceptions handlers and the code to generate dwarf2 frame debugging. */ 894190075Sobrien 894290075Sobrien par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs)); 894390075Sobrien dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_dwarf_regs + 1)); 894490075Sobrien dwarf_par_index = 1; 894590075Sobrien 894690075Sobrien for (i = 0; i <= LAST_ARM_REGNUM; i++) 894790075Sobrien { 894890075Sobrien if (mask & (1 << i)) 894990075Sobrien { 895090075Sobrien reg = gen_rtx_REG (SImode, i); 895190075Sobrien 895290075Sobrien XVECEXP (par, 0, 0) 895390075Sobrien = gen_rtx_SET (VOIDmode, 895490075Sobrien gen_rtx_MEM (BLKmode, 895590075Sobrien gen_rtx_PRE_DEC (BLKmode, 895690075Sobrien stack_pointer_rtx)), 895790075Sobrien gen_rtx_UNSPEC (BLKmode, 895890075Sobrien gen_rtvec (1, reg), 895990075Sobrien UNSPEC_PUSH_MULT)); 896090075Sobrien 896190075Sobrien if (i != PC_REGNUM) 896290075Sobrien { 896390075Sobrien tmp = gen_rtx_SET (VOIDmode, 896490075Sobrien gen_rtx_MEM (SImode, stack_pointer_rtx), 896590075Sobrien reg); 896690075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 896790075Sobrien XVECEXP (dwarf, 0, dwarf_par_index) = tmp; 896890075Sobrien dwarf_par_index++; 896990075Sobrien } 897090075Sobrien 897190075Sobrien break; 897290075Sobrien } 897390075Sobrien } 897490075Sobrien 897590075Sobrien for (j = 1, i++; j < num_regs; i++) 897690075Sobrien { 897790075Sobrien if (mask & (1 << i)) 897890075Sobrien { 897990075Sobrien reg = gen_rtx_REG (SImode, i); 898090075Sobrien 898190075Sobrien XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg); 898290075Sobrien 898390075Sobrien if (i != PC_REGNUM) 898490075Sobrien { 898590075Sobrien tmp = gen_rtx_SET (VOIDmode, 898690075Sobrien gen_rtx_MEM (SImode, 898790075Sobrien plus_constant (stack_pointer_rtx, 898890075Sobrien 4 * j)), 898990075Sobrien reg); 899090075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 899190075Sobrien XVECEXP (dwarf, 0, dwarf_par_index++) = tmp; 899290075Sobrien } 899390075Sobrien 899490075Sobrien j++; 899590075Sobrien } 899690075Sobrien } 899790075Sobrien 899890075Sobrien par = emit_insn (par); 899990075Sobrien 900090075Sobrien tmp = gen_rtx_SET (SImode, 900190075Sobrien stack_pointer_rtx, 900290075Sobrien gen_rtx_PLUS (SImode, 900390075Sobrien stack_pointer_rtx, 900490075Sobrien GEN_INT (-4 * num_regs))); 900590075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 900690075Sobrien XVECEXP (dwarf, 0, 0) = tmp; 900790075Sobrien 900890075Sobrien REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 900990075Sobrien REG_NOTES (par)); 901090075Sobrien return par; 901190075Sobrien} 901290075Sobrien 901390075Sobrienstatic rtx 9014132718Skanemit_sfm (int base_reg, int count) 901590075Sobrien{ 901690075Sobrien rtx par; 901790075Sobrien rtx dwarf; 901890075Sobrien rtx tmp, reg; 901990075Sobrien int i; 902090075Sobrien 902190075Sobrien par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); 902290075Sobrien dwarf = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); 902390075Sobrien 902490075Sobrien reg = gen_rtx_REG (XFmode, base_reg++); 902590075Sobrien 902690075Sobrien XVECEXP (par, 0, 0) 902790075Sobrien = gen_rtx_SET (VOIDmode, 902890075Sobrien gen_rtx_MEM (BLKmode, 902990075Sobrien gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), 903090075Sobrien gen_rtx_UNSPEC (BLKmode, 903190075Sobrien gen_rtvec (1, reg), 903290075Sobrien UNSPEC_PUSH_MULT)); 903390075Sobrien tmp 903490075Sobrien = gen_rtx_SET (VOIDmode, 903590075Sobrien gen_rtx_MEM (XFmode, 903690075Sobrien gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)), 903790075Sobrien reg); 903890075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 903990075Sobrien XVECEXP (dwarf, 0, count - 1) = tmp; 904090075Sobrien 904190075Sobrien for (i = 1; i < count; i++) 904290075Sobrien { 904390075Sobrien reg = gen_rtx_REG (XFmode, base_reg++); 904490075Sobrien XVECEXP (par, 0, i) = gen_rtx_USE (VOIDmode, reg); 904590075Sobrien 904690075Sobrien tmp = gen_rtx_SET (VOIDmode, 904790075Sobrien gen_rtx_MEM (XFmode, 904890075Sobrien gen_rtx_PRE_DEC (BLKmode, 904990075Sobrien stack_pointer_rtx)), 905090075Sobrien reg); 905190075Sobrien RTX_FRAME_RELATED_P (tmp) = 1; 905290075Sobrien XVECEXP (dwarf, 0, count - i - 1) = tmp; 905390075Sobrien } 905490075Sobrien 905590075Sobrien par = emit_insn (par); 905690075Sobrien REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 905790075Sobrien REG_NOTES (par)); 905890075Sobrien return par; 905990075Sobrien} 906090075Sobrien 906190075Sobrien/* Compute the distance from register FROM to register TO. 906290075Sobrien These can be the arg pointer (26), the soft frame pointer (25), 906390075Sobrien the stack pointer (13) or the hard frame pointer (11). 906490075Sobrien Typical stack layout looks like this: 906590075Sobrien 906690075Sobrien old stack pointer -> | | 906790075Sobrien ---- 906890075Sobrien | | \ 906990075Sobrien | | saved arguments for 907090075Sobrien | | vararg functions 907190075Sobrien | | / 907290075Sobrien -- 907390075Sobrien hard FP & arg pointer -> | | \ 907490075Sobrien | | stack 907590075Sobrien | | frame 907690075Sobrien | | / 907790075Sobrien -- 907890075Sobrien | | \ 907990075Sobrien | | call saved 908090075Sobrien | | registers 908190075Sobrien soft frame pointer -> | | / 908290075Sobrien -- 908390075Sobrien | | \ 908490075Sobrien | | local 908590075Sobrien | | variables 908690075Sobrien | | / 908790075Sobrien -- 908890075Sobrien | | \ 908990075Sobrien | | outgoing 909090075Sobrien | | arguments 909190075Sobrien current stack pointer -> | | / 909290075Sobrien -- 909390075Sobrien 9094117395Skan For a given function some or all of these stack components 909590075Sobrien may not be needed, giving rise to the possibility of 909690075Sobrien eliminating some of the registers. 909790075Sobrien 9098117395Skan The values returned by this function must reflect the behavior 909990075Sobrien of arm_expand_prologue() and arm_compute_save_reg_mask(). 910090075Sobrien 910190075Sobrien The sign of the number returned reflects the direction of stack 910290075Sobrien growth, so the values are positive for all eliminations except 910390075Sobrien from the soft frame pointer to the hard frame pointer. */ 910490075Sobrienunsigned int 9105132718Skanarm_compute_initial_elimination_offset (unsigned int from, unsigned int to) 910690075Sobrien{ 9107117395Skan unsigned int local_vars = arm_get_frame_size (); 910890075Sobrien unsigned int outgoing_args = current_function_outgoing_args_size; 910990075Sobrien unsigned int stack_frame; 911090075Sobrien unsigned int call_saved_registers; 911190075Sobrien unsigned long func_type; 911290075Sobrien 911390075Sobrien func_type = arm_current_func_type (); 911490075Sobrien 911590075Sobrien /* Volatile functions never return, so there is 911690075Sobrien no need to save call saved registers. */ 911790075Sobrien call_saved_registers = 0; 911890075Sobrien if (! IS_VOLATILE (func_type)) 911990075Sobrien { 912090075Sobrien unsigned int reg_mask; 912190075Sobrien unsigned int reg; 912290075Sobrien 912390075Sobrien /* Make sure that we compute which registers will be saved 912490075Sobrien on the stack using the same algorithm that is used by 9125132718Skan the prologue creation code. */ 9126132718Skan reg_mask = arm_compute_save_reg_mask (); 912790075Sobrien 912890075Sobrien /* Now count the number of bits set in save_reg_mask. 9129132718Skan If we have already counted the registers in the stack 9130132718Skan frame, do not count them again. Non call-saved registers 9131132718Skan might be saved in the call-save area of the stack, if 9132132718Skan doing so will preserve the stack's alignment. Hence we 9133132718Skan must count them here. For each set bit we need 4 bytes 9134132718Skan of stack space. */ 9135132718Skan if (frame_pointer_needed) 9136132718Skan reg_mask &= 0x07ff; 9137132718Skan call_saved_registers += 4 * bit_count (reg_mask); 913890075Sobrien 913990075Sobrien /* If the hard floating point registers are going to be 914090075Sobrien used then they must be saved on the stack as well. 914190075Sobrien Each register occupies 12 bytes of stack space. */ 9142132718Skan for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++) 914390075Sobrien if (regs_ever_live[reg] && ! call_used_regs[reg]) 914490075Sobrien call_saved_registers += 12; 9145132718Skan 9146132718Skan if (TARGET_REALLY_IWMMXT) 9147132718Skan /* Check for the call-saved iWMMXt registers. */ 9148132718Skan for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) 9149132718Skan if (regs_ever_live[reg] && ! call_used_regs [reg]) 9150132718Skan call_saved_registers += 8; 915190075Sobrien } 915290075Sobrien 915390075Sobrien /* The stack frame contains 4 registers - the old frame pointer, 915490075Sobrien the old stack pointer, the return address and PC of the start 915590075Sobrien of the function. */ 915690075Sobrien stack_frame = frame_pointer_needed ? 16 : 0; 915790075Sobrien 915890075Sobrien /* OK, now we have enough information to compute the distances. 915990075Sobrien There must be an entry in these switch tables for each pair 916090075Sobrien of registers in ELIMINABLE_REGS, even if some of the entries 916190075Sobrien seem to be redundant or useless. */ 916290075Sobrien switch (from) 916390075Sobrien { 916490075Sobrien case ARG_POINTER_REGNUM: 916590075Sobrien switch (to) 916690075Sobrien { 916790075Sobrien case THUMB_HARD_FRAME_POINTER_REGNUM: 916890075Sobrien return 0; 916990075Sobrien 917090075Sobrien case FRAME_POINTER_REGNUM: 917190075Sobrien /* This is the reverse of the soft frame pointer 917290075Sobrien to hard frame pointer elimination below. */ 917390075Sobrien if (call_saved_registers == 0 && stack_frame == 0) 917490075Sobrien return 0; 917590075Sobrien return (call_saved_registers + stack_frame - 4); 917690075Sobrien 917790075Sobrien case ARM_HARD_FRAME_POINTER_REGNUM: 917890075Sobrien /* If there is no stack frame then the hard 917990075Sobrien frame pointer and the arg pointer coincide. */ 918090075Sobrien if (stack_frame == 0 && call_saved_registers != 0) 918190075Sobrien return 0; 918290075Sobrien /* FIXME: Not sure about this. Maybe we should always return 0 ? */ 918390075Sobrien return (frame_pointer_needed 918490075Sobrien && current_function_needs_context 918596263Sobrien && ! cfun->machine->uses_anonymous_args) ? 4 : 0; 918690075Sobrien 918790075Sobrien case STACK_POINTER_REGNUM: 918890075Sobrien /* If nothing has been pushed on the stack at all 918990075Sobrien then this will return -4. This *is* correct! */ 919090075Sobrien return call_saved_registers + stack_frame + local_vars + outgoing_args - 4; 919190075Sobrien 919290075Sobrien default: 919390075Sobrien abort (); 919490075Sobrien } 919590075Sobrien break; 919690075Sobrien 919790075Sobrien case FRAME_POINTER_REGNUM: 919890075Sobrien switch (to) 919990075Sobrien { 920090075Sobrien case THUMB_HARD_FRAME_POINTER_REGNUM: 920190075Sobrien return 0; 920290075Sobrien 920390075Sobrien case ARM_HARD_FRAME_POINTER_REGNUM: 920490075Sobrien /* The hard frame pointer points to the top entry in the 920590075Sobrien stack frame. The soft frame pointer to the bottom entry 920690075Sobrien in the stack frame. If there is no stack frame at all, 920790075Sobrien then they are identical. */ 920890075Sobrien if (call_saved_registers == 0 && stack_frame == 0) 920990075Sobrien return 0; 921090075Sobrien return - (call_saved_registers + stack_frame - 4); 921190075Sobrien 921290075Sobrien case STACK_POINTER_REGNUM: 921390075Sobrien return local_vars + outgoing_args; 921490075Sobrien 921590075Sobrien default: 921690075Sobrien abort (); 921790075Sobrien } 921890075Sobrien break; 921990075Sobrien 922090075Sobrien default: 922190075Sobrien /* You cannot eliminate from the stack pointer. 922290075Sobrien In theory you could eliminate from the hard frame 922390075Sobrien pointer to the stack pointer, but this will never 922490075Sobrien happen, since if a stack frame is not needed the 922590075Sobrien hard frame pointer will never be used. */ 922690075Sobrien abort (); 922790075Sobrien } 922890075Sobrien} 922990075Sobrien 9230117395Skan/* Calculate the size of the stack frame, taking into account any 9231117395Skan padding that is required to ensure stack-alignment. */ 9232117395SkanHOST_WIDE_INT 9233132718Skanarm_get_frame_size (void) 9234117395Skan{ 9235117395Skan int regno; 9236117395Skan 9237132718Skan int base_size = ROUND_UP_WORD (get_frame_size ()); 9238117395Skan int entry_size = 0; 9239117395Skan unsigned long func_type = arm_current_func_type (); 9240117395Skan int leaf; 9241117395Skan 9242117395Skan if (! TARGET_ARM) 9243117395Skan abort(); 9244117395Skan 9245117395Skan if (! TARGET_ATPCS) 9246117395Skan return base_size; 9247117395Skan 9248117395Skan /* We need to know if we are a leaf function. Unfortunately, it 9249117395Skan is possible to be called after start_sequence has been called, 9250117395Skan which causes get_insns to return the insns for the sequence, 9251117395Skan not the function, which will cause leaf_function_p to return 9252117395Skan the incorrect result. 9253117395Skan 9254117395Skan To work around this, we cache the computed frame size. This 9255117395Skan works because we will only be calling RTL expanders that need 9256117395Skan to know about leaf functions once reload has completed, and the 9257117395Skan frame size cannot be changed after that time, so we can safely 9258117395Skan use the cached value. */ 9259117395Skan 9260117395Skan if (reload_completed) 9261117395Skan return cfun->machine->frame_size; 9262117395Skan 9263117395Skan leaf = leaf_function_p (); 9264117395Skan 9265117395Skan /* A leaf function does not need any stack alignment if it has nothing 9266117395Skan on the stack. */ 9267117395Skan if (leaf && base_size == 0) 9268117395Skan { 9269117395Skan cfun->machine->frame_size = 0; 9270117395Skan return 0; 9271117395Skan } 9272117395Skan 9273117395Skan /* We know that SP will be word aligned on entry, and we must 9274117395Skan preserve that condition at any subroutine call. But those are 9275117395Skan the only constraints. */ 9276117395Skan 9277117395Skan /* Space for variadic functions. */ 9278117395Skan if (current_function_pretend_args_size) 9279117395Skan entry_size += current_function_pretend_args_size; 9280117395Skan 9281117395Skan /* Space for saved registers. */ 9282117395Skan entry_size += bit_count (arm_compute_save_reg_mask ()) * 4; 9283117395Skan 9284117395Skan /* Space for saved FPA registers. */ 9285117395Skan if (! IS_VOLATILE (func_type)) 9286117395Skan { 9287117395Skan for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++) 9288117395Skan if (regs_ever_live[regno] && ! call_used_regs[regno]) 9289117395Skan entry_size += 12; 9290117395Skan } 9291117395Skan 9292132718Skan if (TARGET_REALLY_IWMMXT) 9293132718Skan { 9294132718Skan /* Check for the call-saved iWMMXt registers. */ 9295132718Skan for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++) 9296132718Skan if (regs_ever_live [regno] && ! call_used_regs [regno]) 9297132718Skan entry_size += 8; 9298132718Skan } 9299132718Skan 9300117395Skan if ((entry_size + base_size + current_function_outgoing_args_size) & 7) 9301117395Skan base_size += 4; 9302117395Skan if ((entry_size + base_size + current_function_outgoing_args_size) & 7) 9303117395Skan abort (); 9304117395Skan 9305117395Skan cfun->machine->frame_size = base_size; 9306117395Skan 9307117395Skan return base_size; 9308117395Skan} 9309117395Skan 931090075Sobrien/* Generate the prologue instructions for entry into an ARM function. */ 931190075Sobrienvoid 9312132718Skanarm_expand_prologue (void) 931390075Sobrien{ 931490075Sobrien int reg; 931590075Sobrien rtx amount; 931690075Sobrien rtx insn; 931790075Sobrien rtx ip_rtx; 931890075Sobrien unsigned long live_regs_mask; 931990075Sobrien unsigned long func_type; 932090075Sobrien int fp_offset = 0; 932190075Sobrien int saved_pretend_args = 0; 932290075Sobrien unsigned int args_to_push; 932390075Sobrien 932490075Sobrien func_type = arm_current_func_type (); 932590075Sobrien 932690075Sobrien /* Naked functions don't have prologues. */ 932790075Sobrien if (IS_NAKED (func_type)) 932890075Sobrien return; 932990075Sobrien 933090075Sobrien /* Make a copy of c_f_p_a_s as we may need to modify it locally. */ 933190075Sobrien args_to_push = current_function_pretend_args_size; 933290075Sobrien 933390075Sobrien /* Compute which register we will have to save onto the stack. */ 933490075Sobrien live_regs_mask = arm_compute_save_reg_mask (); 933590075Sobrien 933690075Sobrien ip_rtx = gen_rtx_REG (SImode, IP_REGNUM); 933790075Sobrien 933890075Sobrien if (frame_pointer_needed) 933990075Sobrien { 934090075Sobrien if (IS_INTERRUPT (func_type)) 934190075Sobrien { 934290075Sobrien /* Interrupt functions must not corrupt any registers. 934390075Sobrien Creating a frame pointer however, corrupts the IP 934490075Sobrien register, so we must push it first. */ 934590075Sobrien insn = emit_multi_reg_push (1 << IP_REGNUM); 934690075Sobrien 934790075Sobrien /* Do not set RTX_FRAME_RELATED_P on this insn. 934890075Sobrien The dwarf stack unwinding code only wants to see one 934990075Sobrien stack decrement per function, and this is not it. If 935090075Sobrien this instruction is labeled as being part of the frame 935190075Sobrien creation sequence then dwarf2out_frame_debug_expr will 935290075Sobrien abort when it encounters the assignment of IP to FP 935390075Sobrien later on, since the use of SP here establishes SP as 935490075Sobrien the CFA register and not IP. 935590075Sobrien 935690075Sobrien Anyway this instruction is not really part of the stack 935790075Sobrien frame creation although it is part of the prologue. */ 935890075Sobrien } 935990075Sobrien else if (IS_NESTED (func_type)) 936090075Sobrien { 936190075Sobrien /* The Static chain register is the same as the IP register 936290075Sobrien used as a scratch register during stack frame creation. 936390075Sobrien To get around this need to find somewhere to store IP 936490075Sobrien whilst the frame is being created. We try the following 936590075Sobrien places in order: 936690075Sobrien 936790075Sobrien 1. The last argument register. 936890075Sobrien 2. A slot on the stack above the frame. (This only 936990075Sobrien works if the function is not a varargs function). 937090075Sobrien 3. Register r3, after pushing the argument registers 937190075Sobrien onto the stack. 937290075Sobrien 937390075Sobrien Note - we only need to tell the dwarf2 backend about the SP 937490075Sobrien adjustment in the second variant; the static chain register 937590075Sobrien doesn't need to be unwound, as it doesn't contain a value 937690075Sobrien inherited from the caller. */ 937790075Sobrien 937890075Sobrien if (regs_ever_live[3] == 0) 937990075Sobrien { 938090075Sobrien insn = gen_rtx_REG (SImode, 3); 938190075Sobrien insn = gen_rtx_SET (SImode, insn, ip_rtx); 938290075Sobrien insn = emit_insn (insn); 938390075Sobrien } 938490075Sobrien else if (args_to_push == 0) 938590075Sobrien { 938690075Sobrien rtx dwarf; 938790075Sobrien insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx); 938890075Sobrien insn = gen_rtx_MEM (SImode, insn); 938990075Sobrien insn = gen_rtx_SET (VOIDmode, insn, ip_rtx); 939090075Sobrien insn = emit_insn (insn); 939190075Sobrien 939290075Sobrien fp_offset = 4; 939390075Sobrien 939490075Sobrien /* Just tell the dwarf backend that we adjusted SP. */ 939590075Sobrien dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx, 939690075Sobrien gen_rtx_PLUS (SImode, stack_pointer_rtx, 939790075Sobrien GEN_INT (-fp_offset))); 939890075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 939990075Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, 940090075Sobrien dwarf, REG_NOTES (insn)); 940190075Sobrien } 940290075Sobrien else 940390075Sobrien { 940490075Sobrien /* Store the args on the stack. */ 940596263Sobrien if (cfun->machine->uses_anonymous_args) 940690075Sobrien insn = emit_multi_reg_push 940790075Sobrien ((0xf0 >> (args_to_push / 4)) & 0xf); 940890075Sobrien else 940990075Sobrien insn = emit_insn 941090075Sobrien (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 941190075Sobrien GEN_INT (- args_to_push))); 941290075Sobrien 941390075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 941490075Sobrien 941590075Sobrien saved_pretend_args = 1; 941690075Sobrien fp_offset = args_to_push; 941790075Sobrien args_to_push = 0; 941890075Sobrien 941990075Sobrien /* Now reuse r3 to preserve IP. */ 942090075Sobrien insn = gen_rtx_REG (SImode, 3); 942190075Sobrien insn = gen_rtx_SET (SImode, insn, ip_rtx); 942290075Sobrien (void) emit_insn (insn); 942390075Sobrien } 942490075Sobrien } 942590075Sobrien 942690075Sobrien if (fp_offset) 942790075Sobrien { 942890075Sobrien insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset)); 942990075Sobrien insn = gen_rtx_SET (SImode, ip_rtx, insn); 943090075Sobrien } 943190075Sobrien else 943290075Sobrien insn = gen_movsi (ip_rtx, stack_pointer_rtx); 943390075Sobrien 943490075Sobrien insn = emit_insn (insn); 943590075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 943690075Sobrien } 943790075Sobrien 943890075Sobrien if (args_to_push) 943990075Sobrien { 944090075Sobrien /* Push the argument registers, or reserve space for them. */ 944196263Sobrien if (cfun->machine->uses_anonymous_args) 944290075Sobrien insn = emit_multi_reg_push 944390075Sobrien ((0xf0 >> (args_to_push / 4)) & 0xf); 944490075Sobrien else 944590075Sobrien insn = emit_insn 944690075Sobrien (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 944790075Sobrien GEN_INT (- args_to_push))); 944890075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 944990075Sobrien } 945090075Sobrien 9451117395Skan /* If this is an interrupt service routine, and the link register 9452117395Skan is going to be pushed, and we are not creating a stack frame, 9453117395Skan (which would involve an extra push of IP and a pop in the epilogue) 9454117395Skan subtracting four from LR now will mean that the function return 9455117395Skan can be done with a single instruction. */ 945696263Sobrien if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ) 9457117395Skan && (live_regs_mask & (1 << LR_REGNUM)) != 0 9458117395Skan && ! frame_pointer_needed) 9459117395Skan emit_insn (gen_rtx_SET (SImode, 9460117395Skan gen_rtx_REG (SImode, LR_REGNUM), 9461117395Skan gen_rtx_PLUS (SImode, 9462117395Skan gen_rtx_REG (SImode, LR_REGNUM), 9463117395Skan GEN_INT (-4)))); 946496263Sobrien 946590075Sobrien if (live_regs_mask) 946690075Sobrien { 946790075Sobrien insn = emit_multi_reg_push (live_regs_mask); 946890075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 946990075Sobrien } 947090075Sobrien 9471132718Skan if (TARGET_IWMMXT) 9472132718Skan for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) 9473132718Skan if (regs_ever_live[reg] && ! call_used_regs [reg]) 9474132718Skan { 9475132718Skan insn = gen_rtx_PRE_DEC (V2SImode, stack_pointer_rtx); 9476132718Skan insn = gen_rtx_MEM (V2SImode, insn); 9477132718Skan insn = emit_insn (gen_rtx_SET (VOIDmode, insn, 9478132718Skan gen_rtx_REG (V2SImode, reg))); 9479132718Skan RTX_FRAME_RELATED_P (insn) = 1; 9480132718Skan } 9481132718Skan 948290075Sobrien if (! IS_VOLATILE (func_type)) 948390075Sobrien { 9484132718Skan /* Save any floating point call-saved registers used by this 9485132718Skan function. */ 9486132718Skan if (arm_fpu_arch == FPUTYPE_FPA_EMU2) 948790075Sobrien { 9488132718Skan for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 948990075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 949090075Sobrien { 949190075Sobrien insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx); 949290075Sobrien insn = gen_rtx_MEM (XFmode, insn); 949390075Sobrien insn = emit_insn (gen_rtx_SET (VOIDmode, insn, 949490075Sobrien gen_rtx_REG (XFmode, reg))); 949590075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 949690075Sobrien } 949790075Sobrien } 949890075Sobrien else 949990075Sobrien { 950090075Sobrien int start_reg = LAST_ARM_FP_REGNUM; 950190075Sobrien 9502132718Skan for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--) 950390075Sobrien { 950490075Sobrien if (regs_ever_live[reg] && !call_used_regs[reg]) 950590075Sobrien { 950690075Sobrien if (start_reg - reg == 3) 950790075Sobrien { 950890075Sobrien insn = emit_sfm (reg, 4); 950990075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 951090075Sobrien start_reg = reg - 1; 951190075Sobrien } 951290075Sobrien } 951390075Sobrien else 951490075Sobrien { 951590075Sobrien if (start_reg != reg) 951690075Sobrien { 951790075Sobrien insn = emit_sfm (reg + 1, start_reg - reg); 951890075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 951990075Sobrien } 952090075Sobrien start_reg = reg - 1; 952190075Sobrien } 952290075Sobrien } 952390075Sobrien 952490075Sobrien if (start_reg != reg) 952590075Sobrien { 952690075Sobrien insn = emit_sfm (reg + 1, start_reg - reg); 952790075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 952890075Sobrien } 952990075Sobrien } 953090075Sobrien } 953190075Sobrien 953290075Sobrien if (frame_pointer_needed) 953390075Sobrien { 953490075Sobrien /* Create the new frame pointer. */ 953590075Sobrien insn = GEN_INT (-(4 + args_to_push + fp_offset)); 953690075Sobrien insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn)); 953790075Sobrien RTX_FRAME_RELATED_P (insn) = 1; 953890075Sobrien 953990075Sobrien if (IS_NESTED (func_type)) 954090075Sobrien { 954190075Sobrien /* Recover the static chain register. */ 954290075Sobrien if (regs_ever_live [3] == 0 954390075Sobrien || saved_pretend_args) 954490075Sobrien insn = gen_rtx_REG (SImode, 3); 954590075Sobrien else /* if (current_function_pretend_args_size == 0) */ 954690075Sobrien { 9547132718Skan insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, 9548132718Skan GEN_INT (4)); 954990075Sobrien insn = gen_rtx_MEM (SImode, insn); 955090075Sobrien } 955190075Sobrien 955290075Sobrien emit_insn (gen_rtx_SET (SImode, ip_rtx, insn)); 955390075Sobrien /* Add a USE to stop propagate_one_insn() from barfing. */ 955490075Sobrien emit_insn (gen_prologue_use (ip_rtx)); 955590075Sobrien } 955690075Sobrien } 955790075Sobrien 9558117395Skan amount = GEN_INT (-(arm_get_frame_size () 955990075Sobrien + current_function_outgoing_args_size)); 956090075Sobrien 956190075Sobrien if (amount != const0_rtx) 956290075Sobrien { 956390075Sobrien /* This add can produce multiple insns for a large constant, so we 956490075Sobrien need to get tricky. */ 956590075Sobrien rtx last = get_last_insn (); 956690075Sobrien insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 956790075Sobrien amount)); 956890075Sobrien do 956990075Sobrien { 957090075Sobrien last = last ? NEXT_INSN (last) : get_insns (); 957190075Sobrien RTX_FRAME_RELATED_P (last) = 1; 957290075Sobrien } 957390075Sobrien while (last != insn); 957490075Sobrien 957590075Sobrien /* If the frame pointer is needed, emit a special barrier that 957690075Sobrien will prevent the scheduler from moving stores to the frame 957790075Sobrien before the stack adjustment. */ 957890075Sobrien if (frame_pointer_needed) 9579117395Skan insn = emit_insn (gen_stack_tie (stack_pointer_rtx, 9580117395Skan hard_frame_pointer_rtx)); 958190075Sobrien } 958290075Sobrien 958390075Sobrien /* If we are profiling, make sure no instructions are scheduled before 958490075Sobrien the call to mcount. Similarly if the user has requested no 958590075Sobrien scheduling in the prolog. */ 958690075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 958790075Sobrien emit_insn (gen_blockage ()); 958890075Sobrien 958990075Sobrien /* If the link register is being kept alive, with the return address in it, 959090075Sobrien then make sure that it does not get reused by the ce2 pass. */ 959190075Sobrien if ((live_regs_mask & (1 << LR_REGNUM)) == 0) 959290075Sobrien { 959390075Sobrien emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM))); 959490075Sobrien cfun->machine->lr_save_eliminated = 1; 959590075Sobrien } 959690075Sobrien} 959790075Sobrien 959890075Sobrien/* If CODE is 'd', then the X is a condition operand and the instruction 959990075Sobrien should only be executed if the condition is true. 960090075Sobrien if CODE is 'D', then the X is a condition operand and the instruction 960190075Sobrien should only be executed if the condition is false: however, if the mode 960290075Sobrien of the comparison is CCFPEmode, then always execute the instruction -- we 960390075Sobrien do this because in these circumstances !GE does not necessarily imply LT; 960490075Sobrien in these cases the instruction pattern will take care to make sure that 960590075Sobrien an instruction containing %d will follow, thereby undoing the effects of 960690075Sobrien doing this instruction unconditionally. 960790075Sobrien If CODE is 'N' then X is a floating point operand that must be negated 960890075Sobrien before output. 960990075Sobrien If CODE is 'B' then output a bitwise inverted value of X (a const int). 961090075Sobrien If X is a REG and CODE is `M', output a ldm/stm style multi-reg. */ 961190075Sobrienvoid 9612132718Skanarm_print_operand (FILE *stream, rtx x, int code) 961390075Sobrien{ 961490075Sobrien switch (code) 961590075Sobrien { 961690075Sobrien case '@': 961790075Sobrien fputs (ASM_COMMENT_START, stream); 961890075Sobrien return; 961990075Sobrien 962090075Sobrien case '_': 962190075Sobrien fputs (user_label_prefix, stream); 962290075Sobrien return; 962390075Sobrien 962490075Sobrien case '|': 962590075Sobrien fputs (REGISTER_PREFIX, stream); 962690075Sobrien return; 962790075Sobrien 962890075Sobrien case '?': 962990075Sobrien if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) 963090075Sobrien { 963190075Sobrien if (TARGET_THUMB || current_insn_predicate != NULL) 963290075Sobrien abort (); 963390075Sobrien 963490075Sobrien fputs (arm_condition_codes[arm_current_cc], stream); 963590075Sobrien } 963690075Sobrien else if (current_insn_predicate) 963790075Sobrien { 963890075Sobrien enum arm_cond_code code; 963990075Sobrien 964090075Sobrien if (TARGET_THUMB) 964190075Sobrien abort (); 964290075Sobrien 964390075Sobrien code = get_arm_condition_code (current_insn_predicate); 964490075Sobrien fputs (arm_condition_codes[code], stream); 964590075Sobrien } 964690075Sobrien return; 964790075Sobrien 964890075Sobrien case 'N': 964990075Sobrien { 965090075Sobrien REAL_VALUE_TYPE r; 965190075Sobrien REAL_VALUE_FROM_CONST_DOUBLE (r, x); 965290075Sobrien r = REAL_VALUE_NEGATE (r); 965390075Sobrien fprintf (stream, "%s", fp_const_from_val (&r)); 965490075Sobrien } 965590075Sobrien return; 965690075Sobrien 965790075Sobrien case 'B': 965890075Sobrien if (GET_CODE (x) == CONST_INT) 965990075Sobrien { 966090075Sobrien HOST_WIDE_INT val; 966190075Sobrien val = ARM_SIGN_EXTEND (~INTVAL (x)); 966290075Sobrien fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val); 966390075Sobrien } 966490075Sobrien else 966590075Sobrien { 966690075Sobrien putc ('~', stream); 966790075Sobrien output_addr_const (stream, x); 966890075Sobrien } 966990075Sobrien return; 967090075Sobrien 967190075Sobrien case 'i': 967290075Sobrien fprintf (stream, "%s", arithmetic_instr (x, 1)); 967390075Sobrien return; 967490075Sobrien 9675132718Skan /* Truncate Cirrus shift counts. */ 9676132718Skan case 's': 9677132718Skan if (GET_CODE (x) == CONST_INT) 9678132718Skan { 9679132718Skan fprintf (stream, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0x3f); 9680132718Skan return; 9681132718Skan } 9682132718Skan arm_print_operand (stream, x, 0); 9683132718Skan return; 9684132718Skan 968590075Sobrien case 'I': 968690075Sobrien fprintf (stream, "%s", arithmetic_instr (x, 0)); 968790075Sobrien return; 968890075Sobrien 968990075Sobrien case 'S': 969090075Sobrien { 969190075Sobrien HOST_WIDE_INT val; 969290075Sobrien const char * shift = shift_op (x, &val); 969390075Sobrien 969490075Sobrien if (shift) 969590075Sobrien { 969690075Sobrien fprintf (stream, ", %s ", shift_op (x, &val)); 969790075Sobrien if (val == -1) 969890075Sobrien arm_print_operand (stream, XEXP (x, 1), 0); 969990075Sobrien else 9700132718Skan fprintf (stream, "#" HOST_WIDE_INT_PRINT_DEC, val); 970190075Sobrien } 970290075Sobrien } 970390075Sobrien return; 970490075Sobrien 970590075Sobrien /* An explanation of the 'Q', 'R' and 'H' register operands: 970690075Sobrien 970790075Sobrien In a pair of registers containing a DI or DF value the 'Q' 970890075Sobrien operand returns the register number of the register containing 9709132718Skan the least significant part of the value. The 'R' operand returns 971090075Sobrien the register number of the register containing the most 971190075Sobrien significant part of the value. 971290075Sobrien 971390075Sobrien The 'H' operand returns the higher of the two register numbers. 971490075Sobrien On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the 9715132718Skan same as the 'Q' operand, since the most significant part of the 971690075Sobrien value is held in the lower number register. The reverse is true 971790075Sobrien on systems where WORDS_BIG_ENDIAN is false. 971890075Sobrien 971990075Sobrien The purpose of these operands is to distinguish between cases 972090075Sobrien where the endian-ness of the values is important (for example 972190075Sobrien when they are added together), and cases where the endian-ness 972290075Sobrien is irrelevant, but the order of register operations is important. 972390075Sobrien For example when loading a value from memory into a register 972490075Sobrien pair, the endian-ness does not matter. Provided that the value 972590075Sobrien from the lower memory address is put into the lower numbered 972690075Sobrien register, and the value from the higher address is put into the 972790075Sobrien higher numbered register, the load will work regardless of whether 972890075Sobrien the value being loaded is big-wordian or little-wordian. The 972990075Sobrien order of the two register loads can matter however, if the address 973090075Sobrien of the memory location is actually held in one of the registers 973190075Sobrien being overwritten by the load. */ 973290075Sobrien case 'Q': 973390075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 973490075Sobrien abort (); 973590075Sobrien asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)); 973690075Sobrien return; 973790075Sobrien 973890075Sobrien case 'R': 973990075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 974090075Sobrien abort (); 974190075Sobrien asm_fprintf (stream, "%r", REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)); 974290075Sobrien return; 974390075Sobrien 974490075Sobrien case 'H': 974590075Sobrien if (REGNO (x) > LAST_ARM_REGNUM) 974690075Sobrien abort (); 974790075Sobrien asm_fprintf (stream, "%r", REGNO (x) + 1); 974890075Sobrien return; 974990075Sobrien 975090075Sobrien case 'm': 975190075Sobrien asm_fprintf (stream, "%r", 975290075Sobrien GET_CODE (XEXP (x, 0)) == REG 975390075Sobrien ? REGNO (XEXP (x, 0)) : REGNO (XEXP (XEXP (x, 0), 0))); 975490075Sobrien return; 975590075Sobrien 975690075Sobrien case 'M': 975790075Sobrien asm_fprintf (stream, "{%r-%r}", 975890075Sobrien REGNO (x), 9759117395Skan REGNO (x) + ARM_NUM_REGS (GET_MODE (x)) - 1); 976090075Sobrien return; 976190075Sobrien 976290075Sobrien case 'd': 9763117395Skan /* CONST_TRUE_RTX means always -- that's the default. */ 9764117395Skan if (x == const_true_rtx) 976590075Sobrien return; 976690075Sobrien 9767132718Skan fputs (arm_condition_codes[get_arm_condition_code (x)], 9768132718Skan stream); 976990075Sobrien return; 977090075Sobrien 977190075Sobrien case 'D': 9772117395Skan /* CONST_TRUE_RTX means not always -- ie never. We shouldn't ever 9773117395Skan want to do that. */ 9774117395Skan if (x == const_true_rtx) 9775117395Skan abort (); 977690075Sobrien 9777132718Skan fputs (arm_condition_codes[ARM_INVERSE_CONDITION_CODE 9778132718Skan (get_arm_condition_code (x))], 9779132718Skan stream); 9780132718Skan return; 9781132718Skan 9782132718Skan /* Cirrus registers can be accessed in a variety of ways: 9783132718Skan single floating point (f) 9784132718Skan double floating point (d) 9785132718Skan 32bit integer (fx) 9786132718Skan 64bit integer (dx). */ 9787132718Skan case 'W': /* Cirrus register in F mode. */ 9788132718Skan case 'X': /* Cirrus register in D mode. */ 9789132718Skan case 'Y': /* Cirrus register in FX mode. */ 9790132718Skan case 'Z': /* Cirrus register in DX mode. */ 9791132718Skan if (GET_CODE (x) != REG || REGNO_REG_CLASS (REGNO (x)) != CIRRUS_REGS) 9792132718Skan abort (); 9793132718Skan 9794132718Skan fprintf (stream, "mv%s%s", 9795132718Skan code == 'W' ? "f" 9796132718Skan : code == 'X' ? "d" 9797132718Skan : code == 'Y' ? "fx" : "dx", reg_names[REGNO (x)] + 2); 9798132718Skan 9799132718Skan return; 9800132718Skan 9801132718Skan /* Print cirrus register in the mode specified by the register's mode. */ 9802132718Skan case 'V': 9803132718Skan { 9804132718Skan int mode = GET_MODE (x); 9805132718Skan 9806132718Skan if (GET_CODE (x) != REG || REGNO_REG_CLASS (REGNO (x)) != CIRRUS_REGS) 9807132718Skan abort (); 9808132718Skan 9809132718Skan fprintf (stream, "mv%s%s", 9810132718Skan mode == DFmode ? "d" 9811132718Skan : mode == SImode ? "fx" 9812132718Skan : mode == DImode ? "dx" 9813132718Skan : "f", reg_names[REGNO (x)] + 2); 9814132718Skan 9815132718Skan return; 9816132718Skan } 9817132718Skan 9818132718Skan case 'U': 9819132718Skan if (GET_CODE (x) != REG 9820132718Skan || REGNO (x) < FIRST_IWMMXT_GR_REGNUM 9821132718Skan || REGNO (x) > LAST_IWMMXT_GR_REGNUM) 9822132718Skan /* Bad value for wCG register number. */ 9823132718Skan abort (); 982490075Sobrien else 9825132718Skan fprintf (stream, "%d", REGNO (x) - FIRST_IWMMXT_GR_REGNUM); 982690075Sobrien return; 982790075Sobrien 9828132718Skan /* Print an iWMMXt control register name. */ 9829132718Skan case 'w': 9830132718Skan if (GET_CODE (x) != CONST_INT 9831132718Skan || INTVAL (x) < 0 9832132718Skan || INTVAL (x) >= 16) 9833132718Skan /* Bad value for wC register number. */ 9834132718Skan abort (); 9835132718Skan else 9836132718Skan { 9837132718Skan static const char * wc_reg_names [16] = 9838132718Skan { 9839132718Skan "wCID", "wCon", "wCSSF", "wCASF", 9840132718Skan "wC4", "wC5", "wC6", "wC7", 9841132718Skan "wCGR0", "wCGR1", "wCGR2", "wCGR3", 9842132718Skan "wC12", "wC13", "wC14", "wC15" 9843132718Skan }; 9844132718Skan 9845132718Skan fprintf (stream, wc_reg_names [INTVAL (x)]); 9846132718Skan } 9847132718Skan return; 9848132718Skan 984990075Sobrien default: 985090075Sobrien if (x == 0) 985190075Sobrien abort (); 985290075Sobrien 985390075Sobrien if (GET_CODE (x) == REG) 985490075Sobrien asm_fprintf (stream, "%r", REGNO (x)); 985590075Sobrien else if (GET_CODE (x) == MEM) 985690075Sobrien { 985790075Sobrien output_memory_reference_mode = GET_MODE (x); 985890075Sobrien output_address (XEXP (x, 0)); 985990075Sobrien } 986090075Sobrien else if (GET_CODE (x) == CONST_DOUBLE) 986190075Sobrien fprintf (stream, "#%s", fp_immediate_constant (x)); 986290075Sobrien else if (GET_CODE (x) == NEG) 986390075Sobrien abort (); /* This should never happen now. */ 986490075Sobrien else 986590075Sobrien { 986690075Sobrien fputc ('#', stream); 986790075Sobrien output_addr_const (stream, x); 986890075Sobrien } 986990075Sobrien } 987090075Sobrien} 987190075Sobrien 987290075Sobrien#ifndef AOF_ASSEMBLER 987390075Sobrien/* Target hook for assembling integer objects. The ARM version needs to 987490075Sobrien handle word-sized values specially. */ 987590075Sobrienstatic bool 9876132718Skanarm_assemble_integer (rtx x, unsigned int size, int aligned_p) 987790075Sobrien{ 987890075Sobrien if (size == UNITS_PER_WORD && aligned_p) 987990075Sobrien { 988090075Sobrien fputs ("\t.word\t", asm_out_file); 988190075Sobrien output_addr_const (asm_out_file, x); 988290075Sobrien 988390075Sobrien /* Mark symbols as position independent. We only do this in the 9884132718Skan .text segment, not in the .data segment. */ 988590075Sobrien if (NEED_GOT_RELOC && flag_pic && making_const_table && 988690075Sobrien (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)) 988790075Sobrien { 9888117395Skan if (GET_CODE (x) == SYMBOL_REF 988996263Sobrien && (CONSTANT_POOL_ADDRESS_P (x) 9890132718Skan || SYMBOL_REF_LOCAL_P (x))) 989190075Sobrien fputs ("(GOTOFF)", asm_out_file); 989290075Sobrien else if (GET_CODE (x) == LABEL_REF) 989390075Sobrien fputs ("(GOTOFF)", asm_out_file); 989490075Sobrien else 989590075Sobrien fputs ("(GOT)", asm_out_file); 989690075Sobrien } 989790075Sobrien fputc ('\n', asm_out_file); 989890075Sobrien return true; 989990075Sobrien } 990090075Sobrien 9901132718Skan if (VECTOR_MODE_SUPPORTED_P (GET_MODE (x))) 9902132718Skan { 9903132718Skan int i, units; 9904132718Skan 9905132718Skan if (GET_CODE (x) != CONST_VECTOR) 9906132718Skan abort (); 9907132718Skan 9908132718Skan units = CONST_VECTOR_NUNITS (x); 9909132718Skan 9910132718Skan switch (GET_MODE (x)) 9911132718Skan { 9912132718Skan case V2SImode: size = 4; break; 9913132718Skan case V4HImode: size = 2; break; 9914132718Skan case V8QImode: size = 1; break; 9915132718Skan default: 9916132718Skan abort (); 9917132718Skan } 9918132718Skan 9919132718Skan for (i = 0; i < units; i++) 9920132718Skan { 9921132718Skan rtx elt; 9922132718Skan 9923132718Skan elt = CONST_VECTOR_ELT (x, i); 9924132718Skan assemble_integer 9925132718Skan (elt, size, i == 0 ? BIGGEST_ALIGNMENT : size * BITS_PER_UNIT, 1); 9926132718Skan } 9927132718Skan 9928132718Skan return true; 9929132718Skan } 9930132718Skan 993190075Sobrien return default_assemble_integer (x, size, aligned_p); 993290075Sobrien} 993390075Sobrien#endif 993490075Sobrien 993590075Sobrien/* A finite state machine takes care of noticing whether or not instructions 993690075Sobrien can be conditionally executed, and thus decrease execution time and code 993790075Sobrien size by deleting branch instructions. The fsm is controlled by 993890075Sobrien final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. */ 993990075Sobrien 994090075Sobrien/* The state of the fsm controlling condition codes are: 994190075Sobrien 0: normal, do nothing special 994290075Sobrien 1: make ASM_OUTPUT_OPCODE not output this instruction 994390075Sobrien 2: make ASM_OUTPUT_OPCODE not output this instruction 994490075Sobrien 3: make instructions conditional 994590075Sobrien 4: make instructions conditional 994690075Sobrien 994790075Sobrien State transitions (state->state by whom under condition): 994890075Sobrien 0 -> 1 final_prescan_insn if the `target' is a label 994990075Sobrien 0 -> 2 final_prescan_insn if the `target' is an unconditional branch 995090075Sobrien 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch 995190075Sobrien 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch 9952132718Skan 3 -> 0 (*targetm.asm_out.internal_label) if the `target' label is reached 995390075Sobrien (the target label has CODE_LABEL_NUMBER equal to arm_target_label). 995490075Sobrien 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached 995590075Sobrien (the target insn is arm_target_insn). 995690075Sobrien 995790075Sobrien If the jump clobbers the conditions then we use states 2 and 4. 995890075Sobrien 995990075Sobrien A similar thing can be done with conditional return insns. 996090075Sobrien 996190075Sobrien XXX In case the `target' is an unconditional branch, this conditionalising 996290075Sobrien of the instructions always reduces code size, but not always execution 996390075Sobrien time. But then, I want to reduce the code size to somewhere near what 996490075Sobrien /bin/cc produces. */ 996590075Sobrien 996690075Sobrien/* Returns the index of the ARM condition code string in 996790075Sobrien `arm_condition_codes'. COMPARISON should be an rtx like 996890075Sobrien `(eq (...) (...))'. */ 996990075Sobrienstatic enum arm_cond_code 9970132718Skanget_arm_condition_code (rtx comparison) 997190075Sobrien{ 997290075Sobrien enum machine_mode mode = GET_MODE (XEXP (comparison, 0)); 997390075Sobrien int code; 997490075Sobrien enum rtx_code comp_code = GET_CODE (comparison); 997590075Sobrien 997690075Sobrien if (GET_MODE_CLASS (mode) != MODE_CC) 997790075Sobrien mode = SELECT_CC_MODE (comp_code, XEXP (comparison, 0), 997890075Sobrien XEXP (comparison, 1)); 997990075Sobrien 998090075Sobrien switch (mode) 998190075Sobrien { 998290075Sobrien case CC_DNEmode: code = ARM_NE; goto dominance; 998390075Sobrien case CC_DEQmode: code = ARM_EQ; goto dominance; 998490075Sobrien case CC_DGEmode: code = ARM_GE; goto dominance; 998590075Sobrien case CC_DGTmode: code = ARM_GT; goto dominance; 998690075Sobrien case CC_DLEmode: code = ARM_LE; goto dominance; 998790075Sobrien case CC_DLTmode: code = ARM_LT; goto dominance; 998890075Sobrien case CC_DGEUmode: code = ARM_CS; goto dominance; 998990075Sobrien case CC_DGTUmode: code = ARM_HI; goto dominance; 999090075Sobrien case CC_DLEUmode: code = ARM_LS; goto dominance; 999190075Sobrien case CC_DLTUmode: code = ARM_CC; 999290075Sobrien 999390075Sobrien dominance: 999490075Sobrien if (comp_code != EQ && comp_code != NE) 999590075Sobrien abort (); 999690075Sobrien 999790075Sobrien if (comp_code == EQ) 999890075Sobrien return ARM_INVERSE_CONDITION_CODE (code); 999990075Sobrien return code; 1000090075Sobrien 1000190075Sobrien case CC_NOOVmode: 1000290075Sobrien switch (comp_code) 1000390075Sobrien { 1000490075Sobrien case NE: return ARM_NE; 1000590075Sobrien case EQ: return ARM_EQ; 1000690075Sobrien case GE: return ARM_PL; 1000790075Sobrien case LT: return ARM_MI; 1000890075Sobrien default: abort (); 1000990075Sobrien } 1001090075Sobrien 1001190075Sobrien case CC_Zmode: 1001290075Sobrien switch (comp_code) 1001390075Sobrien { 1001490075Sobrien case NE: return ARM_NE; 1001590075Sobrien case EQ: return ARM_EQ; 1001690075Sobrien default: abort (); 1001790075Sobrien } 1001890075Sobrien 10019132718Skan case CC_Nmode: 10020132718Skan switch (comp_code) 10021132718Skan { 10022132718Skan case NE: return ARM_MI; 10023132718Skan case EQ: return ARM_PL; 10024132718Skan default: abort (); 10025132718Skan } 10026132718Skan 1002790075Sobrien case CCFPEmode: 1002890075Sobrien case CCFPmode: 1002990075Sobrien /* These encodings assume that AC=1 in the FPA system control 1003090075Sobrien byte. This allows us to handle all cases except UNEQ and 1003190075Sobrien LTGT. */ 1003290075Sobrien switch (comp_code) 1003390075Sobrien { 1003490075Sobrien case GE: return ARM_GE; 1003590075Sobrien case GT: return ARM_GT; 1003690075Sobrien case LE: return ARM_LS; 1003790075Sobrien case LT: return ARM_MI; 1003890075Sobrien case NE: return ARM_NE; 1003990075Sobrien case EQ: return ARM_EQ; 1004090075Sobrien case ORDERED: return ARM_VC; 1004190075Sobrien case UNORDERED: return ARM_VS; 1004290075Sobrien case UNLT: return ARM_LT; 1004390075Sobrien case UNLE: return ARM_LE; 1004490075Sobrien case UNGT: return ARM_HI; 1004590075Sobrien case UNGE: return ARM_PL; 1004690075Sobrien /* UNEQ and LTGT do not have a representation. */ 1004790075Sobrien case UNEQ: /* Fall through. */ 1004890075Sobrien case LTGT: /* Fall through. */ 1004990075Sobrien default: abort (); 1005090075Sobrien } 1005190075Sobrien 1005290075Sobrien case CC_SWPmode: 1005390075Sobrien switch (comp_code) 1005490075Sobrien { 1005590075Sobrien case NE: return ARM_NE; 1005690075Sobrien case EQ: return ARM_EQ; 1005790075Sobrien case GE: return ARM_LE; 1005890075Sobrien case GT: return ARM_LT; 1005990075Sobrien case LE: return ARM_GE; 1006090075Sobrien case LT: return ARM_GT; 1006190075Sobrien case GEU: return ARM_LS; 1006290075Sobrien case GTU: return ARM_CC; 1006390075Sobrien case LEU: return ARM_CS; 1006490075Sobrien case LTU: return ARM_HI; 1006590075Sobrien default: abort (); 1006690075Sobrien } 1006790075Sobrien 1006890075Sobrien case CC_Cmode: 1006990075Sobrien switch (comp_code) 1007090075Sobrien { 1007190075Sobrien case LTU: return ARM_CS; 1007290075Sobrien case GEU: return ARM_CC; 1007390075Sobrien default: abort (); 1007490075Sobrien } 1007590075Sobrien 1007690075Sobrien case CCmode: 1007790075Sobrien switch (comp_code) 1007890075Sobrien { 1007990075Sobrien case NE: return ARM_NE; 1008090075Sobrien case EQ: return ARM_EQ; 1008190075Sobrien case GE: return ARM_GE; 1008290075Sobrien case GT: return ARM_GT; 1008390075Sobrien case LE: return ARM_LE; 1008490075Sobrien case LT: return ARM_LT; 1008590075Sobrien case GEU: return ARM_CS; 1008690075Sobrien case GTU: return ARM_HI; 1008790075Sobrien case LEU: return ARM_LS; 1008890075Sobrien case LTU: return ARM_CC; 1008990075Sobrien default: abort (); 1009090075Sobrien } 1009190075Sobrien 1009290075Sobrien default: abort (); 1009390075Sobrien } 1009490075Sobrien 1009590075Sobrien abort (); 1009690075Sobrien} 1009790075Sobrien 1009890075Sobrienvoid 10099132718Skanarm_final_prescan_insn (rtx insn) 1010090075Sobrien{ 1010190075Sobrien /* BODY will hold the body of INSN. */ 1010290075Sobrien rtx body = PATTERN (insn); 1010390075Sobrien 1010490075Sobrien /* This will be 1 if trying to repeat the trick, and things need to be 1010590075Sobrien reversed if it appears to fail. */ 1010690075Sobrien int reverse = 0; 1010790075Sobrien 1010890075Sobrien /* JUMP_CLOBBERS will be one implies that the conditions if a branch is 1010990075Sobrien taken are clobbered, even if the rtl suggests otherwise. It also 1011090075Sobrien means that we have to grub around within the jump expression to find 1011190075Sobrien out what the conditions are when the jump isn't taken. */ 1011290075Sobrien int jump_clobbers = 0; 1011390075Sobrien 1011490075Sobrien /* If we start with a return insn, we only succeed if we find another one. */ 1011590075Sobrien int seeking_return = 0; 1011690075Sobrien 1011790075Sobrien /* START_INSN will hold the insn from where we start looking. This is the 1011890075Sobrien first insn after the following code_label if REVERSE is true. */ 1011990075Sobrien rtx start_insn = insn; 1012090075Sobrien 1012190075Sobrien /* If in state 4, check if the target branch is reached, in order to 1012290075Sobrien change back to state 0. */ 1012390075Sobrien if (arm_ccfsm_state == 4) 1012490075Sobrien { 1012590075Sobrien if (insn == arm_target_insn) 1012690075Sobrien { 1012790075Sobrien arm_target_insn = NULL; 1012890075Sobrien arm_ccfsm_state = 0; 1012990075Sobrien } 1013090075Sobrien return; 1013190075Sobrien } 1013290075Sobrien 1013390075Sobrien /* If in state 3, it is possible to repeat the trick, if this insn is an 1013490075Sobrien unconditional branch to a label, and immediately following this branch 1013590075Sobrien is the previous target label which is only used once, and the label this 1013690075Sobrien branch jumps to is not too far off. */ 1013790075Sobrien if (arm_ccfsm_state == 3) 1013890075Sobrien { 1013990075Sobrien if (simplejump_p (insn)) 1014090075Sobrien { 1014190075Sobrien start_insn = next_nonnote_insn (start_insn); 1014290075Sobrien if (GET_CODE (start_insn) == BARRIER) 1014390075Sobrien { 1014490075Sobrien /* XXX Isn't this always a barrier? */ 1014590075Sobrien start_insn = next_nonnote_insn (start_insn); 1014690075Sobrien } 1014790075Sobrien if (GET_CODE (start_insn) == CODE_LABEL 1014890075Sobrien && CODE_LABEL_NUMBER (start_insn) == arm_target_label 1014990075Sobrien && LABEL_NUSES (start_insn) == 1) 1015090075Sobrien reverse = TRUE; 1015190075Sobrien else 1015290075Sobrien return; 1015390075Sobrien } 1015490075Sobrien else if (GET_CODE (body) == RETURN) 1015590075Sobrien { 1015690075Sobrien start_insn = next_nonnote_insn (start_insn); 1015790075Sobrien if (GET_CODE (start_insn) == BARRIER) 1015890075Sobrien start_insn = next_nonnote_insn (start_insn); 1015990075Sobrien if (GET_CODE (start_insn) == CODE_LABEL 1016090075Sobrien && CODE_LABEL_NUMBER (start_insn) == arm_target_label 1016190075Sobrien && LABEL_NUSES (start_insn) == 1) 1016290075Sobrien { 1016390075Sobrien reverse = TRUE; 1016490075Sobrien seeking_return = 1; 1016590075Sobrien } 1016690075Sobrien else 1016790075Sobrien return; 1016890075Sobrien } 1016990075Sobrien else 1017090075Sobrien return; 1017190075Sobrien } 1017290075Sobrien 1017390075Sobrien if (arm_ccfsm_state != 0 && !reverse) 1017490075Sobrien abort (); 1017590075Sobrien if (GET_CODE (insn) != JUMP_INSN) 1017690075Sobrien return; 1017790075Sobrien 1017890075Sobrien /* This jump might be paralleled with a clobber of the condition codes 1017990075Sobrien the jump should always come first */ 1018090075Sobrien if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0) 1018190075Sobrien body = XVECEXP (body, 0, 0); 1018290075Sobrien 1018390075Sobrien if (reverse 1018490075Sobrien || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC 1018590075Sobrien && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) 1018690075Sobrien { 1018790075Sobrien int insns_skipped; 1018890075Sobrien int fail = FALSE, succeed = FALSE; 1018990075Sobrien /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ 1019090075Sobrien int then_not_else = TRUE; 1019190075Sobrien rtx this_insn = start_insn, label = 0; 1019290075Sobrien 1019390075Sobrien /* If the jump cannot be done with one instruction, we cannot 1019490075Sobrien conditionally execute the instruction in the inverse case. */ 1019590075Sobrien if (get_attr_conds (insn) == CONDS_JUMP_CLOB) 1019690075Sobrien { 1019790075Sobrien jump_clobbers = 1; 1019890075Sobrien return; 1019990075Sobrien } 1020090075Sobrien 1020190075Sobrien /* Register the insn jumped to. */ 1020290075Sobrien if (reverse) 1020390075Sobrien { 1020490075Sobrien if (!seeking_return) 1020590075Sobrien label = XEXP (SET_SRC (body), 0); 1020690075Sobrien } 1020790075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) 1020890075Sobrien label = XEXP (XEXP (SET_SRC (body), 1), 0); 1020990075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) 1021090075Sobrien { 1021190075Sobrien label = XEXP (XEXP (SET_SRC (body), 2), 0); 1021290075Sobrien then_not_else = FALSE; 1021390075Sobrien } 1021490075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN) 1021590075Sobrien seeking_return = 1; 1021690075Sobrien else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN) 1021790075Sobrien { 1021890075Sobrien seeking_return = 1; 1021990075Sobrien then_not_else = FALSE; 1022090075Sobrien } 1022190075Sobrien else 1022290075Sobrien abort (); 1022390075Sobrien 1022490075Sobrien /* See how many insns this branch skips, and what kind of insns. If all 1022590075Sobrien insns are okay, and the label or unconditional branch to the same 1022690075Sobrien label is not too far away, succeed. */ 1022790075Sobrien for (insns_skipped = 0; 1022890075Sobrien !fail && !succeed && insns_skipped++ < max_insns_skipped;) 1022990075Sobrien { 1023090075Sobrien rtx scanbody; 1023190075Sobrien 1023290075Sobrien this_insn = next_nonnote_insn (this_insn); 1023390075Sobrien if (!this_insn) 1023490075Sobrien break; 1023590075Sobrien 1023690075Sobrien switch (GET_CODE (this_insn)) 1023790075Sobrien { 1023890075Sobrien case CODE_LABEL: 1023990075Sobrien /* Succeed if it is the target label, otherwise fail since 1024090075Sobrien control falls in from somewhere else. */ 1024190075Sobrien if (this_insn == label) 1024290075Sobrien { 1024390075Sobrien if (jump_clobbers) 1024490075Sobrien { 1024590075Sobrien arm_ccfsm_state = 2; 1024690075Sobrien this_insn = next_nonnote_insn (this_insn); 1024790075Sobrien } 1024890075Sobrien else 1024990075Sobrien arm_ccfsm_state = 1; 1025090075Sobrien succeed = TRUE; 1025190075Sobrien } 1025290075Sobrien else 1025390075Sobrien fail = TRUE; 1025490075Sobrien break; 1025590075Sobrien 1025690075Sobrien case BARRIER: 1025790075Sobrien /* Succeed if the following insn is the target label. 1025890075Sobrien Otherwise fail. 1025990075Sobrien If return insns are used then the last insn in a function 1026090075Sobrien will be a barrier. */ 1026190075Sobrien this_insn = next_nonnote_insn (this_insn); 1026290075Sobrien if (this_insn && this_insn == label) 1026390075Sobrien { 1026490075Sobrien if (jump_clobbers) 1026590075Sobrien { 1026690075Sobrien arm_ccfsm_state = 2; 1026790075Sobrien this_insn = next_nonnote_insn (this_insn); 1026890075Sobrien } 1026990075Sobrien else 1027090075Sobrien arm_ccfsm_state = 1; 1027190075Sobrien succeed = TRUE; 1027290075Sobrien } 1027390075Sobrien else 1027490075Sobrien fail = TRUE; 1027590075Sobrien break; 1027690075Sobrien 1027790075Sobrien case CALL_INSN: 1027890075Sobrien /* If using 32-bit addresses the cc is not preserved over 1027990075Sobrien calls. */ 1028090075Sobrien if (TARGET_APCS_32) 1028190075Sobrien { 1028290075Sobrien /* Succeed if the following insn is the target label, 1028390075Sobrien or if the following two insns are a barrier and 1028490075Sobrien the target label. */ 1028590075Sobrien this_insn = next_nonnote_insn (this_insn); 1028690075Sobrien if (this_insn && GET_CODE (this_insn) == BARRIER) 1028790075Sobrien this_insn = next_nonnote_insn (this_insn); 1028890075Sobrien 1028990075Sobrien if (this_insn && this_insn == label 1029090075Sobrien && insns_skipped < max_insns_skipped) 1029190075Sobrien { 1029290075Sobrien if (jump_clobbers) 1029390075Sobrien { 1029490075Sobrien arm_ccfsm_state = 2; 1029590075Sobrien this_insn = next_nonnote_insn (this_insn); 1029690075Sobrien } 1029790075Sobrien else 1029890075Sobrien arm_ccfsm_state = 1; 1029990075Sobrien succeed = TRUE; 1030090075Sobrien } 1030190075Sobrien else 1030290075Sobrien fail = TRUE; 1030390075Sobrien } 1030490075Sobrien break; 1030590075Sobrien 1030690075Sobrien case JUMP_INSN: 1030790075Sobrien /* If this is an unconditional branch to the same label, succeed. 1030890075Sobrien If it is to another label, do nothing. If it is conditional, 1030990075Sobrien fail. */ 10310132718Skan /* XXX Probably, the tests for SET and the PC are 10311132718Skan unnecessary. */ 1031290075Sobrien 1031390075Sobrien scanbody = PATTERN (this_insn); 1031490075Sobrien if (GET_CODE (scanbody) == SET 1031590075Sobrien && GET_CODE (SET_DEST (scanbody)) == PC) 1031690075Sobrien { 1031790075Sobrien if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF 1031890075Sobrien && XEXP (SET_SRC (scanbody), 0) == label && !reverse) 1031990075Sobrien { 1032090075Sobrien arm_ccfsm_state = 2; 1032190075Sobrien succeed = TRUE; 1032290075Sobrien } 1032390075Sobrien else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) 1032490075Sobrien fail = TRUE; 1032590075Sobrien } 1032690075Sobrien /* Fail if a conditional return is undesirable (eg on a 1032790075Sobrien StrongARM), but still allow this if optimizing for size. */ 1032890075Sobrien else if (GET_CODE (scanbody) == RETURN 10329132718Skan && !use_return_insn (TRUE, NULL) 1033090075Sobrien && !optimize_size) 1033190075Sobrien fail = TRUE; 1033290075Sobrien else if (GET_CODE (scanbody) == RETURN 1033390075Sobrien && seeking_return) 1033490075Sobrien { 1033590075Sobrien arm_ccfsm_state = 2; 1033690075Sobrien succeed = TRUE; 1033790075Sobrien } 1033890075Sobrien else if (GET_CODE (scanbody) == PARALLEL) 1033990075Sobrien { 1034090075Sobrien switch (get_attr_conds (this_insn)) 1034190075Sobrien { 1034290075Sobrien case CONDS_NOCOND: 1034390075Sobrien break; 1034490075Sobrien default: 1034590075Sobrien fail = TRUE; 1034690075Sobrien break; 1034790075Sobrien } 1034890075Sobrien } 1034990075Sobrien else 1035090075Sobrien fail = TRUE; /* Unrecognized jump (eg epilogue). */ 1035190075Sobrien 1035290075Sobrien break; 1035390075Sobrien 1035490075Sobrien case INSN: 1035590075Sobrien /* Instructions using or affecting the condition codes make it 1035690075Sobrien fail. */ 1035790075Sobrien scanbody = PATTERN (this_insn); 1035890075Sobrien if (!(GET_CODE (scanbody) == SET 1035990075Sobrien || GET_CODE (scanbody) == PARALLEL) 1036090075Sobrien || get_attr_conds (this_insn) != CONDS_NOCOND) 1036190075Sobrien fail = TRUE; 10362132718Skan 10363132718Skan /* A conditional cirrus instruction must be followed by 10364132718Skan a non Cirrus instruction. However, since we 10365132718Skan conditionalize instructions in this function and by 10366132718Skan the time we get here we can't add instructions 10367132718Skan (nops), because shorten_branches() has already been 10368132718Skan called, we will disable conditionalizing Cirrus 10369132718Skan instructions to be safe. */ 10370132718Skan if (GET_CODE (scanbody) != USE 10371132718Skan && GET_CODE (scanbody) != CLOBBER 10372132718Skan && get_attr_cirrus (this_insn) != CIRRUS_NOT) 10373132718Skan fail = TRUE; 1037490075Sobrien break; 1037590075Sobrien 1037690075Sobrien default: 1037790075Sobrien break; 1037890075Sobrien } 1037990075Sobrien } 1038090075Sobrien if (succeed) 1038190075Sobrien { 1038290075Sobrien if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse)) 1038390075Sobrien arm_target_label = CODE_LABEL_NUMBER (label); 1038490075Sobrien else if (seeking_return || arm_ccfsm_state == 2) 1038590075Sobrien { 1038690075Sobrien while (this_insn && GET_CODE (PATTERN (this_insn)) == USE) 1038790075Sobrien { 1038890075Sobrien this_insn = next_nonnote_insn (this_insn); 1038990075Sobrien if (this_insn && (GET_CODE (this_insn) == BARRIER 1039090075Sobrien || GET_CODE (this_insn) == CODE_LABEL)) 1039190075Sobrien abort (); 1039290075Sobrien } 1039390075Sobrien if (!this_insn) 1039490075Sobrien { 10395132718Skan /* Oh, dear! we ran off the end.. give up. */ 1039690075Sobrien recog (PATTERN (insn), insn, NULL); 1039790075Sobrien arm_ccfsm_state = 0; 1039890075Sobrien arm_target_insn = NULL; 1039990075Sobrien return; 1040090075Sobrien } 1040190075Sobrien arm_target_insn = this_insn; 1040290075Sobrien } 1040390075Sobrien else 1040490075Sobrien abort (); 1040590075Sobrien if (jump_clobbers) 1040690075Sobrien { 1040790075Sobrien if (reverse) 1040890075Sobrien abort (); 1040990075Sobrien arm_current_cc = 1041090075Sobrien get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body), 1041190075Sobrien 0), 0), 1)); 1041290075Sobrien if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND) 1041390075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 1041490075Sobrien if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE) 1041590075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 1041690075Sobrien } 1041790075Sobrien else 1041890075Sobrien { 1041990075Sobrien /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from 1042090075Sobrien what it was. */ 1042190075Sobrien if (!reverse) 1042290075Sobrien arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 1042390075Sobrien 0)); 1042490075Sobrien } 1042590075Sobrien 1042690075Sobrien if (reverse || then_not_else) 1042790075Sobrien arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 1042890075Sobrien } 1042990075Sobrien 1043090075Sobrien /* Restore recog_data (getting the attributes of other insns can 1043190075Sobrien destroy this array, but final.c assumes that it remains intact 1043290075Sobrien across this call; since the insn has been recognized already we 1043390075Sobrien call recog direct). */ 1043490075Sobrien recog (PATTERN (insn), insn, NULL); 1043590075Sobrien } 1043690075Sobrien} 1043790075Sobrien 1043890075Sobrien/* Returns true if REGNO is a valid register 1043990075Sobrien for holding a quantity of tyoe MODE. */ 1044090075Sobrienint 10441132718Skanarm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) 1044290075Sobrien{ 1044390075Sobrien if (GET_MODE_CLASS (mode) == MODE_CC) 1044490075Sobrien return regno == CC_REGNUM; 1044590075Sobrien 1044690075Sobrien if (TARGET_THUMB) 1044790075Sobrien /* For the Thumb we only allow values bigger than SImode in 1044890075Sobrien registers 0 - 6, so that there is always a second low 1044990075Sobrien register available to hold the upper part of the value. 1045090075Sobrien We probably we ought to ensure that the register is the 1045190075Sobrien start of an even numbered register pair. */ 10452117395Skan return (ARM_NUM_REGS (mode) < 2) || (regno < LAST_LO_REGNUM); 1045390075Sobrien 10454132718Skan if (IS_CIRRUS_REGNUM (regno)) 10455132718Skan /* We have outlawed SI values in Cirrus registers because they 10456132718Skan reside in the lower 32 bits, but SF values reside in the 10457132718Skan upper 32 bits. This causes gcc all sorts of grief. We can't 10458132718Skan even split the registers into pairs because Cirrus SI values 10459132718Skan get sign extended to 64bits-- aldyh. */ 10460132718Skan return (GET_MODE_CLASS (mode) == MODE_FLOAT) || (mode == DImode); 10461132718Skan 10462132718Skan if (IS_IWMMXT_GR_REGNUM (regno)) 10463132718Skan return mode == SImode; 10464132718Skan 10465132718Skan if (IS_IWMMXT_REGNUM (regno)) 10466132718Skan return VALID_IWMMXT_REG_MODE (mode); 10467132718Skan 1046890075Sobrien if (regno <= LAST_ARM_REGNUM) 10469132718Skan /* We allow any value to be stored in the general registers. */ 1047096263Sobrien return 1; 1047190075Sobrien 1047290075Sobrien if ( regno == FRAME_POINTER_REGNUM 1047390075Sobrien || regno == ARG_POINTER_REGNUM) 1047490075Sobrien /* We only allow integers in the fake hard registers. */ 1047590075Sobrien return GET_MODE_CLASS (mode) == MODE_INT; 1047690075Sobrien 10477132718Skan /* The only registers left are the FPA registers 1047890075Sobrien which we only allow to hold FP values. */ 1047990075Sobrien return GET_MODE_CLASS (mode) == MODE_FLOAT 1048090075Sobrien && regno >= FIRST_ARM_FP_REGNUM 1048190075Sobrien && regno <= LAST_ARM_FP_REGNUM; 1048290075Sobrien} 1048390075Sobrien 1048490075Sobrienint 10485132718Skanarm_regno_class (int regno) 1048690075Sobrien{ 1048790075Sobrien if (TARGET_THUMB) 1048890075Sobrien { 1048990075Sobrien if (regno == STACK_POINTER_REGNUM) 1049090075Sobrien return STACK_REG; 1049190075Sobrien if (regno == CC_REGNUM) 1049290075Sobrien return CC_REG; 1049390075Sobrien if (regno < 8) 1049490075Sobrien return LO_REGS; 1049590075Sobrien return HI_REGS; 1049690075Sobrien } 1049790075Sobrien 1049890075Sobrien if ( regno <= LAST_ARM_REGNUM 1049990075Sobrien || regno == FRAME_POINTER_REGNUM 1050090075Sobrien || regno == ARG_POINTER_REGNUM) 1050190075Sobrien return GENERAL_REGS; 1050290075Sobrien 1050390075Sobrien if (regno == CC_REGNUM) 1050490075Sobrien return NO_REGS; 1050590075Sobrien 10506132718Skan if (IS_CIRRUS_REGNUM (regno)) 10507132718Skan return CIRRUS_REGS; 10508132718Skan 10509132718Skan if (IS_IWMMXT_REGNUM (regno)) 10510132718Skan return IWMMXT_REGS; 10511132718Skan 10512132718Skan if (IS_IWMMXT_GR_REGNUM (regno)) 10513132718Skan return IWMMXT_GR_REGS; 10514132718Skan 10515132718Skan return FPA_REGS; 1051690075Sobrien} 1051790075Sobrien 1051890075Sobrien/* Handle a special case when computing the offset 1051990075Sobrien of an argument from the frame pointer. */ 1052090075Sobrienint 10521132718Skanarm_debugger_arg_offset (int value, rtx addr) 1052290075Sobrien{ 1052390075Sobrien rtx insn; 1052490075Sobrien 1052590075Sobrien /* We are only interested if dbxout_parms() failed to compute the offset. */ 1052690075Sobrien if (value != 0) 1052790075Sobrien return 0; 1052890075Sobrien 1052990075Sobrien /* We can only cope with the case where the address is held in a register. */ 1053090075Sobrien if (GET_CODE (addr) != REG) 1053190075Sobrien return 0; 1053290075Sobrien 1053390075Sobrien /* If we are using the frame pointer to point at the argument, then 1053490075Sobrien an offset of 0 is correct. */ 1053590075Sobrien if (REGNO (addr) == (unsigned) HARD_FRAME_POINTER_REGNUM) 1053690075Sobrien return 0; 1053790075Sobrien 1053890075Sobrien /* If we are using the stack pointer to point at the 1053990075Sobrien argument, then an offset of 0 is correct. */ 1054090075Sobrien if ((TARGET_THUMB || !frame_pointer_needed) 1054190075Sobrien && REGNO (addr) == SP_REGNUM) 1054290075Sobrien return 0; 1054390075Sobrien 1054490075Sobrien /* Oh dear. The argument is pointed to by a register rather 1054590075Sobrien than being held in a register, or being stored at a known 1054690075Sobrien offset from the frame pointer. Since GDB only understands 1054790075Sobrien those two kinds of argument we must translate the address 1054890075Sobrien held in the register into an offset from the frame pointer. 1054990075Sobrien We do this by searching through the insns for the function 1055090075Sobrien looking to see where this register gets its value. If the 10551117395Skan register is initialized from the frame pointer plus an offset 1055290075Sobrien then we are in luck and we can continue, otherwise we give up. 1055390075Sobrien 1055490075Sobrien This code is exercised by producing debugging information 1055590075Sobrien for a function with arguments like this: 1055690075Sobrien 1055790075Sobrien double func (double a, double b, int c, double d) {return d;} 1055890075Sobrien 1055990075Sobrien Without this code the stab for parameter 'd' will be set to 1056090075Sobrien an offset of 0 from the frame pointer, rather than 8. */ 1056190075Sobrien 1056290075Sobrien /* The if() statement says: 1056390075Sobrien 1056490075Sobrien If the insn is a normal instruction 1056590075Sobrien and if the insn is setting the value in a register 1056690075Sobrien and if the register being set is the register holding the address of the argument 1056790075Sobrien and if the address is computing by an addition 1056890075Sobrien that involves adding to a register 1056990075Sobrien which is the frame pointer 1057090075Sobrien a constant integer 1057190075Sobrien 10572132718Skan then... */ 1057390075Sobrien 1057490075Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 1057590075Sobrien { 1057690075Sobrien if ( GET_CODE (insn) == INSN 1057790075Sobrien && GET_CODE (PATTERN (insn)) == SET 1057890075Sobrien && REGNO (XEXP (PATTERN (insn), 0)) == REGNO (addr) 1057990075Sobrien && GET_CODE (XEXP (PATTERN (insn), 1)) == PLUS 1058090075Sobrien && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 0)) == REG 1058190075Sobrien && REGNO (XEXP (XEXP (PATTERN (insn), 1), 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM 1058290075Sobrien && GET_CODE (XEXP (XEXP (PATTERN (insn), 1), 1)) == CONST_INT 1058390075Sobrien ) 1058490075Sobrien { 1058590075Sobrien value = INTVAL (XEXP (XEXP (PATTERN (insn), 1), 1)); 1058690075Sobrien 1058790075Sobrien break; 1058890075Sobrien } 1058990075Sobrien } 1059090075Sobrien 1059190075Sobrien if (value == 0) 1059290075Sobrien { 1059390075Sobrien debug_rtx (addr); 1059490075Sobrien warning ("unable to compute real location of stacked parameter"); 1059590075Sobrien value = 8; /* XXX magic hack */ 1059690075Sobrien } 1059790075Sobrien 1059890075Sobrien return value; 1059990075Sobrien} 10600132718Skan 10601132718Skan#define def_mbuiltin(MASK, NAME, TYPE, CODE) \ 10602132718Skan do \ 10603132718Skan { \ 10604132718Skan if ((MASK) & insn_flags) \ 10605132718Skan builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, NULL_TREE); \ 10606132718Skan } \ 10607132718Skan while (0) 1060890075Sobrien 10609132718Skanstruct builtin_description 10610132718Skan{ 10611132718Skan const unsigned int mask; 10612132718Skan const enum insn_code icode; 10613132718Skan const char * const name; 10614132718Skan const enum arm_builtins code; 10615132718Skan const enum rtx_code comparison; 10616132718Skan const unsigned int flag; 10617132718Skan}; 1061890075Sobrien 10619132718Skanstatic const struct builtin_description bdesc_2arg[] = 1062090075Sobrien{ 10621132718Skan#define IWMMXT_BUILTIN(code, string, builtin) \ 10622132718Skan { FL_IWMMXT, CODE_FOR_##code, "__builtin_arm_" string, \ 10623132718Skan ARM_BUILTIN_##builtin, 0, 0 }, 10624132718Skan 10625132718Skan IWMMXT_BUILTIN (addv8qi3, "waddb", WADDB) 10626132718Skan IWMMXT_BUILTIN (addv4hi3, "waddh", WADDH) 10627132718Skan IWMMXT_BUILTIN (addv2si3, "waddw", WADDW) 10628132718Skan IWMMXT_BUILTIN (subv8qi3, "wsubb", WSUBB) 10629132718Skan IWMMXT_BUILTIN (subv4hi3, "wsubh", WSUBH) 10630132718Skan IWMMXT_BUILTIN (subv2si3, "wsubw", WSUBW) 10631132718Skan IWMMXT_BUILTIN (ssaddv8qi3, "waddbss", WADDSSB) 10632132718Skan IWMMXT_BUILTIN (ssaddv4hi3, "waddhss", WADDSSH) 10633132718Skan IWMMXT_BUILTIN (ssaddv2si3, "waddwss", WADDSSW) 10634132718Skan IWMMXT_BUILTIN (sssubv8qi3, "wsubbss", WSUBSSB) 10635132718Skan IWMMXT_BUILTIN (sssubv4hi3, "wsubhss", WSUBSSH) 10636132718Skan IWMMXT_BUILTIN (sssubv2si3, "wsubwss", WSUBSSW) 10637132718Skan IWMMXT_BUILTIN (usaddv8qi3, "waddbus", WADDUSB) 10638132718Skan IWMMXT_BUILTIN (usaddv4hi3, "waddhus", WADDUSH) 10639132718Skan IWMMXT_BUILTIN (usaddv2si3, "waddwus", WADDUSW) 10640132718Skan IWMMXT_BUILTIN (ussubv8qi3, "wsubbus", WSUBUSB) 10641132718Skan IWMMXT_BUILTIN (ussubv4hi3, "wsubhus", WSUBUSH) 10642132718Skan IWMMXT_BUILTIN (ussubv2si3, "wsubwus", WSUBUSW) 10643132718Skan IWMMXT_BUILTIN (mulv4hi3, "wmulul", WMULUL) 10644132718Skan IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsh", WMULSH) 10645132718Skan IWMMXT_BUILTIN (umulv4hi3_highpart, "wmuluh", WMULUH) 10646132718Skan IWMMXT_BUILTIN (eqv8qi3, "wcmpeqb", WCMPEQB) 10647132718Skan IWMMXT_BUILTIN (eqv4hi3, "wcmpeqh", WCMPEQH) 10648132718Skan IWMMXT_BUILTIN (eqv2si3, "wcmpeqw", WCMPEQW) 10649132718Skan IWMMXT_BUILTIN (gtuv8qi3, "wcmpgtub", WCMPGTUB) 10650132718Skan IWMMXT_BUILTIN (gtuv4hi3, "wcmpgtuh", WCMPGTUH) 10651132718Skan IWMMXT_BUILTIN (gtuv2si3, "wcmpgtuw", WCMPGTUW) 10652132718Skan IWMMXT_BUILTIN (gtv8qi3, "wcmpgtsb", WCMPGTSB) 10653132718Skan IWMMXT_BUILTIN (gtv4hi3, "wcmpgtsh", WCMPGTSH) 10654132718Skan IWMMXT_BUILTIN (gtv2si3, "wcmpgtsw", WCMPGTSW) 10655132718Skan IWMMXT_BUILTIN (umaxv8qi3, "wmaxub", WMAXUB) 10656132718Skan IWMMXT_BUILTIN (smaxv8qi3, "wmaxsb", WMAXSB) 10657132718Skan IWMMXT_BUILTIN (umaxv4hi3, "wmaxuh", WMAXUH) 10658132718Skan IWMMXT_BUILTIN (smaxv4hi3, "wmaxsh", WMAXSH) 10659132718Skan IWMMXT_BUILTIN (umaxv2si3, "wmaxuw", WMAXUW) 10660132718Skan IWMMXT_BUILTIN (smaxv2si3, "wmaxsw", WMAXSW) 10661132718Skan IWMMXT_BUILTIN (uminv8qi3, "wminub", WMINUB) 10662132718Skan IWMMXT_BUILTIN (sminv8qi3, "wminsb", WMINSB) 10663132718Skan IWMMXT_BUILTIN (uminv4hi3, "wminuh", WMINUH) 10664132718Skan IWMMXT_BUILTIN (sminv4hi3, "wminsh", WMINSH) 10665132718Skan IWMMXT_BUILTIN (uminv2si3, "wminuw", WMINUW) 10666132718Skan IWMMXT_BUILTIN (sminv2si3, "wminsw", WMINSW) 10667132718Skan IWMMXT_BUILTIN (iwmmxt_anddi3, "wand", WAND) 10668132718Skan IWMMXT_BUILTIN (iwmmxt_nanddi3, "wandn", WANDN) 10669132718Skan IWMMXT_BUILTIN (iwmmxt_iordi3, "wor", WOR) 10670132718Skan IWMMXT_BUILTIN (iwmmxt_xordi3, "wxor", WXOR) 10671132718Skan IWMMXT_BUILTIN (iwmmxt_uavgv8qi3, "wavg2b", WAVG2B) 10672132718Skan IWMMXT_BUILTIN (iwmmxt_uavgv4hi3, "wavg2h", WAVG2H) 10673132718Skan IWMMXT_BUILTIN (iwmmxt_uavgrndv8qi3, "wavg2br", WAVG2BR) 10674132718Skan IWMMXT_BUILTIN (iwmmxt_uavgrndv4hi3, "wavg2hr", WAVG2HR) 10675132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckilb, "wunpckilb", WUNPCKILB) 10676132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckilh, "wunpckilh", WUNPCKILH) 10677132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckilw, "wunpckilw", WUNPCKILW) 10678132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckihb, "wunpckihb", WUNPCKIHB) 10679132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckihh, "wunpckihh", WUNPCKIHH) 10680132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckihw, "wunpckihw", WUNPCKIHW) 10681132718Skan IWMMXT_BUILTIN (iwmmxt_wmadds, "wmadds", WMADDS) 10682132718Skan IWMMXT_BUILTIN (iwmmxt_wmaddu, "wmaddu", WMADDU) 10683132718Skan 10684132718Skan#define IWMMXT_BUILTIN2(code, builtin) \ 10685132718Skan { FL_IWMMXT, CODE_FOR_##code, NULL, ARM_BUILTIN_##builtin, 0, 0 }, 10686132718Skan 10687132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackhss, WPACKHSS) 10688132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackwss, WPACKWSS) 10689132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackdss, WPACKDSS) 10690132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackhus, WPACKHUS) 10691132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackwus, WPACKWUS) 10692132718Skan IWMMXT_BUILTIN2 (iwmmxt_wpackdus, WPACKDUS) 10693132718Skan IWMMXT_BUILTIN2 (ashlv4hi3_di, WSLLH) 10694132718Skan IWMMXT_BUILTIN2 (ashlv4hi3, WSLLHI) 10695132718Skan IWMMXT_BUILTIN2 (ashlv2si3_di, WSLLW) 10696132718Skan IWMMXT_BUILTIN2 (ashlv2si3, WSLLWI) 10697132718Skan IWMMXT_BUILTIN2 (ashldi3_di, WSLLD) 10698132718Skan IWMMXT_BUILTIN2 (ashldi3_iwmmxt, WSLLDI) 10699132718Skan IWMMXT_BUILTIN2 (lshrv4hi3_di, WSRLH) 10700132718Skan IWMMXT_BUILTIN2 (lshrv4hi3, WSRLHI) 10701132718Skan IWMMXT_BUILTIN2 (lshrv2si3_di, WSRLW) 10702132718Skan IWMMXT_BUILTIN2 (lshrv2si3, WSRLWI) 10703132718Skan IWMMXT_BUILTIN2 (lshrdi3_di, WSRLD) 10704132718Skan IWMMXT_BUILTIN2 (lshrdi3, WSRLDI) 10705132718Skan IWMMXT_BUILTIN2 (ashrv4hi3_di, WSRAH) 10706132718Skan IWMMXT_BUILTIN2 (ashrv4hi3, WSRAHI) 10707132718Skan IWMMXT_BUILTIN2 (ashrv2si3_di, WSRAW) 10708132718Skan IWMMXT_BUILTIN2 (ashrv2si3, WSRAWI) 10709132718Skan IWMMXT_BUILTIN2 (ashrdi3_di, WSRAD) 10710132718Skan IWMMXT_BUILTIN2 (ashrdi3, WSRADI) 10711132718Skan IWMMXT_BUILTIN2 (rorv4hi3_di, WRORH) 10712132718Skan IWMMXT_BUILTIN2 (rorv4hi3, WRORHI) 10713132718Skan IWMMXT_BUILTIN2 (rorv2si3_di, WRORW) 10714132718Skan IWMMXT_BUILTIN2 (rorv2si3, WRORWI) 10715132718Skan IWMMXT_BUILTIN2 (rordi3_di, WRORD) 10716132718Skan IWMMXT_BUILTIN2 (rordi3, WRORDI) 10717132718Skan IWMMXT_BUILTIN2 (iwmmxt_wmacuz, WMACUZ) 10718132718Skan IWMMXT_BUILTIN2 (iwmmxt_wmacsz, WMACSZ) 10719132718Skan}; 10720132718Skan 10721132718Skanstatic const struct builtin_description bdesc_1arg[] = 10722132718Skan{ 10723132718Skan IWMMXT_BUILTIN (iwmmxt_tmovmskb, "tmovmskb", TMOVMSKB) 10724132718Skan IWMMXT_BUILTIN (iwmmxt_tmovmskh, "tmovmskh", TMOVMSKH) 10725132718Skan IWMMXT_BUILTIN (iwmmxt_tmovmskw, "tmovmskw", TMOVMSKW) 10726132718Skan IWMMXT_BUILTIN (iwmmxt_waccb, "waccb", WACCB) 10727132718Skan IWMMXT_BUILTIN (iwmmxt_wacch, "wacch", WACCH) 10728132718Skan IWMMXT_BUILTIN (iwmmxt_waccw, "waccw", WACCW) 10729132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehub, "wunpckehub", WUNPCKEHUB) 10730132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehuh, "wunpckehuh", WUNPCKEHUH) 10731132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehuw, "wunpckehuw", WUNPCKEHUW) 10732132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehsb, "wunpckehsb", WUNPCKEHSB) 10733132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehsh, "wunpckehsh", WUNPCKEHSH) 10734132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckehsw, "wunpckehsw", WUNPCKEHSW) 10735132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckelub, "wunpckelub", WUNPCKELUB) 10736132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckeluh, "wunpckeluh", WUNPCKELUH) 10737132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckeluw, "wunpckeluw", WUNPCKELUW) 10738132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckelsb, "wunpckelsb", WUNPCKELSB) 10739132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckelsh, "wunpckelsh", WUNPCKELSH) 10740132718Skan IWMMXT_BUILTIN (iwmmxt_wunpckelsw, "wunpckelsw", WUNPCKELSW) 10741132718Skan}; 10742132718Skan 10743132718Skan/* Set up all the iWMMXt builtins. This is 10744132718Skan not called if TARGET_IWMMXT is zero. */ 10745132718Skan 10746132718Skanstatic void 10747132718Skanarm_init_iwmmxt_builtins (void) 10748132718Skan{ 10749132718Skan const struct builtin_description * d; 10750132718Skan size_t i; 1075190075Sobrien tree endlink = void_list_node; 1075290075Sobrien 10753132718Skan tree int_ftype_int 10754132718Skan = build_function_type (integer_type_node, 10755132718Skan tree_cons (NULL_TREE, integer_type_node, endlink)); 10756132718Skan tree v8qi_ftype_v8qi_v8qi_int 10757132718Skan = build_function_type (V8QI_type_node, 10758132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10759132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10760132718Skan tree_cons (NULL_TREE, 10761132718Skan integer_type_node, 10762132718Skan endlink)))); 10763132718Skan tree v4hi_ftype_v4hi_int 10764132718Skan = build_function_type (V4HI_type_node, 10765132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10766132718Skan tree_cons (NULL_TREE, integer_type_node, 10767132718Skan endlink))); 10768132718Skan tree v2si_ftype_v2si_int 10769132718Skan = build_function_type (V2SI_type_node, 10770132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10771132718Skan tree_cons (NULL_TREE, integer_type_node, 10772132718Skan endlink))); 10773132718Skan tree v2si_ftype_di_di 10774132718Skan = build_function_type (V2SI_type_node, 10775132718Skan tree_cons (NULL_TREE, long_long_integer_type_node, 10776132718Skan tree_cons (NULL_TREE, long_long_integer_type_node, 10777132718Skan endlink))); 10778132718Skan tree di_ftype_di_int 10779132718Skan = build_function_type (long_long_integer_type_node, 10780132718Skan tree_cons (NULL_TREE, long_long_integer_type_node, 10781132718Skan tree_cons (NULL_TREE, integer_type_node, 10782132718Skan endlink))); 10783132718Skan tree di_ftype_di_int_int 10784132718Skan = build_function_type (long_long_integer_type_node, 10785132718Skan tree_cons (NULL_TREE, long_long_integer_type_node, 10786132718Skan tree_cons (NULL_TREE, integer_type_node, 10787132718Skan tree_cons (NULL_TREE, 10788132718Skan integer_type_node, 10789132718Skan endlink)))); 10790132718Skan tree int_ftype_v8qi 10791132718Skan = build_function_type (integer_type_node, 10792132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10793132718Skan endlink)); 10794132718Skan tree int_ftype_v4hi 10795132718Skan = build_function_type (integer_type_node, 10796132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10797132718Skan endlink)); 10798132718Skan tree int_ftype_v2si 10799132718Skan = build_function_type (integer_type_node, 10800132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10801132718Skan endlink)); 10802132718Skan tree int_ftype_v8qi_int 10803132718Skan = build_function_type (integer_type_node, 10804132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10805132718Skan tree_cons (NULL_TREE, integer_type_node, 10806132718Skan endlink))); 10807132718Skan tree int_ftype_v4hi_int 10808132718Skan = build_function_type (integer_type_node, 10809132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10810132718Skan tree_cons (NULL_TREE, integer_type_node, 10811132718Skan endlink))); 10812132718Skan tree int_ftype_v2si_int 10813132718Skan = build_function_type (integer_type_node, 10814132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10815132718Skan tree_cons (NULL_TREE, integer_type_node, 10816132718Skan endlink))); 10817132718Skan tree v8qi_ftype_v8qi_int_int 10818132718Skan = build_function_type (V8QI_type_node, 10819132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10820132718Skan tree_cons (NULL_TREE, integer_type_node, 10821132718Skan tree_cons (NULL_TREE, 10822132718Skan integer_type_node, 10823132718Skan endlink)))); 10824132718Skan tree v4hi_ftype_v4hi_int_int 10825132718Skan = build_function_type (V4HI_type_node, 10826132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10827132718Skan tree_cons (NULL_TREE, integer_type_node, 10828132718Skan tree_cons (NULL_TREE, 10829132718Skan integer_type_node, 10830132718Skan endlink)))); 10831132718Skan tree v2si_ftype_v2si_int_int 10832132718Skan = build_function_type (V2SI_type_node, 10833132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10834132718Skan tree_cons (NULL_TREE, integer_type_node, 10835132718Skan tree_cons (NULL_TREE, 10836132718Skan integer_type_node, 10837132718Skan endlink)))); 10838132718Skan /* Miscellaneous. */ 10839132718Skan tree v8qi_ftype_v4hi_v4hi 10840132718Skan = build_function_type (V8QI_type_node, 10841132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10842132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10843132718Skan endlink))); 10844132718Skan tree v4hi_ftype_v2si_v2si 10845132718Skan = build_function_type (V4HI_type_node, 10846132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10847132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10848132718Skan endlink))); 10849132718Skan tree v2si_ftype_v4hi_v4hi 10850132718Skan = build_function_type (V2SI_type_node, 10851132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10852132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10853132718Skan endlink))); 10854132718Skan tree v2si_ftype_v8qi_v8qi 10855132718Skan = build_function_type (V2SI_type_node, 10856132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10857132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10858132718Skan endlink))); 10859132718Skan tree v4hi_ftype_v4hi_di 10860132718Skan = build_function_type (V4HI_type_node, 10861132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10862132718Skan tree_cons (NULL_TREE, 10863132718Skan long_long_integer_type_node, 10864132718Skan endlink))); 10865132718Skan tree v2si_ftype_v2si_di 10866132718Skan = build_function_type (V2SI_type_node, 10867132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10868132718Skan tree_cons (NULL_TREE, 10869132718Skan long_long_integer_type_node, 10870132718Skan endlink))); 10871132718Skan tree void_ftype_int_int 10872132718Skan = build_function_type (void_type_node, 10873132718Skan tree_cons (NULL_TREE, integer_type_node, 10874132718Skan tree_cons (NULL_TREE, integer_type_node, 10875132718Skan endlink))); 10876132718Skan tree di_ftype_void 10877132718Skan = build_function_type (long_long_unsigned_type_node, endlink); 10878132718Skan tree di_ftype_v8qi 10879132718Skan = build_function_type (long_long_integer_type_node, 10880132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10881132718Skan endlink)); 10882132718Skan tree di_ftype_v4hi 10883132718Skan = build_function_type (long_long_integer_type_node, 10884132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10885132718Skan endlink)); 10886132718Skan tree di_ftype_v2si 10887132718Skan = build_function_type (long_long_integer_type_node, 10888132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10889132718Skan endlink)); 10890132718Skan tree v2si_ftype_v4hi 10891132718Skan = build_function_type (V2SI_type_node, 10892132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10893132718Skan endlink)); 10894132718Skan tree v4hi_ftype_v8qi 10895132718Skan = build_function_type (V4HI_type_node, 10896132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10897132718Skan endlink)); 1089890075Sobrien 10899132718Skan tree di_ftype_di_v4hi_v4hi 10900132718Skan = build_function_type (long_long_unsigned_type_node, 10901132718Skan tree_cons (NULL_TREE, 10902132718Skan long_long_unsigned_type_node, 10903132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10904132718Skan tree_cons (NULL_TREE, 10905132718Skan V4HI_type_node, 10906132718Skan endlink)))); 1090790075Sobrien 10908132718Skan tree di_ftype_v4hi_v4hi 10909132718Skan = build_function_type (long_long_unsigned_type_node, 10910132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10911132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10912132718Skan endlink))); 1091390075Sobrien 10914132718Skan /* Normal vector binops. */ 10915132718Skan tree v8qi_ftype_v8qi_v8qi 10916132718Skan = build_function_type (V8QI_type_node, 10917132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10918132718Skan tree_cons (NULL_TREE, V8QI_type_node, 10919132718Skan endlink))); 10920132718Skan tree v4hi_ftype_v4hi_v4hi 10921132718Skan = build_function_type (V4HI_type_node, 10922132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10923132718Skan tree_cons (NULL_TREE, V4HI_type_node, 10924132718Skan endlink))); 10925132718Skan tree v2si_ftype_v2si_v2si 10926132718Skan = build_function_type (V2SI_type_node, 10927132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10928132718Skan tree_cons (NULL_TREE, V2SI_type_node, 10929132718Skan endlink))); 10930132718Skan tree di_ftype_di_di 10931132718Skan = build_function_type (long_long_unsigned_type_node, 10932132718Skan tree_cons (NULL_TREE, long_long_unsigned_type_node, 10933132718Skan tree_cons (NULL_TREE, 10934132718Skan long_long_unsigned_type_node, 10935132718Skan endlink))); 10936132718Skan 10937132718Skan /* Add all builtins that are more or less simple operations on two 10938132718Skan operands. */ 10939132718Skan for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) 10940132718Skan { 10941132718Skan /* Use one of the operands; the target can have a different mode for 10942132718Skan mask-generating compares. */ 10943132718Skan enum machine_mode mode; 10944132718Skan tree type; 10945132718Skan 10946132718Skan if (d->name == 0) 10947132718Skan continue; 10948132718Skan 10949132718Skan mode = insn_data[d->icode].operand[1].mode; 10950132718Skan 10951132718Skan switch (mode) 10952132718Skan { 10953132718Skan case V8QImode: 10954132718Skan type = v8qi_ftype_v8qi_v8qi; 10955132718Skan break; 10956132718Skan case V4HImode: 10957132718Skan type = v4hi_ftype_v4hi_v4hi; 10958132718Skan break; 10959132718Skan case V2SImode: 10960132718Skan type = v2si_ftype_v2si_v2si; 10961132718Skan break; 10962132718Skan case DImode: 10963132718Skan type = di_ftype_di_di; 10964132718Skan break; 10965132718Skan 10966132718Skan default: 10967132718Skan abort (); 10968132718Skan } 10969132718Skan 10970132718Skan def_mbuiltin (d->mask, d->name, type, d->code); 10971132718Skan } 10972132718Skan 10973132718Skan /* Add the remaining MMX insns with somewhat more complicated types. */ 10974132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wzero", di_ftype_void, ARM_BUILTIN_WZERO); 10975132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_setwcx", void_ftype_int_int, ARM_BUILTIN_SETWCX); 10976132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_getwcx", int_ftype_int, ARM_BUILTIN_GETWCX); 10977132718Skan 10978132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSLLH); 10979132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllw", v2si_ftype_v2si_di, ARM_BUILTIN_WSLLW); 10980132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wslld", di_ftype_di_di, ARM_BUILTIN_WSLLD); 10981132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSLLHI); 10982132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllwi", v2si_ftype_v2si_int, ARM_BUILTIN_WSLLWI); 10983132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wslldi", di_ftype_di_int, ARM_BUILTIN_WSLLDI); 10984132718Skan 10985132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSRLH); 10986132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlw", v2si_ftype_v2si_di, ARM_BUILTIN_WSRLW); 10987132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrld", di_ftype_di_di, ARM_BUILTIN_WSRLD); 10988132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSRLHI); 10989132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlwi", v2si_ftype_v2si_int, ARM_BUILTIN_WSRLWI); 10990132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrldi", di_ftype_di_int, ARM_BUILTIN_WSRLDI); 10991132718Skan 10992132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrah", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSRAH); 10993132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsraw", v2si_ftype_v2si_di, ARM_BUILTIN_WSRAW); 10994132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrad", di_ftype_di_di, ARM_BUILTIN_WSRAD); 10995132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrahi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSRAHI); 10996132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrawi", v2si_ftype_v2si_int, ARM_BUILTIN_WSRAWI); 10997132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsradi", di_ftype_di_int, ARM_BUILTIN_WSRADI); 10998132718Skan 10999132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WRORH); 11000132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorw", v2si_ftype_v2si_di, ARM_BUILTIN_WRORW); 11001132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrord", di_ftype_di_di, ARM_BUILTIN_WRORD); 11002132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WRORHI); 11003132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorwi", v2si_ftype_v2si_int, ARM_BUILTIN_WRORWI); 11004132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrordi", di_ftype_di_int, ARM_BUILTIN_WRORDI); 11005132718Skan 11006132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wshufh", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSHUFH); 11007132718Skan 11008132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadb", v2si_ftype_v8qi_v8qi, ARM_BUILTIN_WSADB); 11009132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadh", v2si_ftype_v4hi_v4hi, ARM_BUILTIN_WSADH); 11010132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadbz", v2si_ftype_v8qi_v8qi, ARM_BUILTIN_WSADBZ); 11011132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadhz", v2si_ftype_v4hi_v4hi, ARM_BUILTIN_WSADHZ); 11012132718Skan 11013132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsb", int_ftype_v8qi_int, ARM_BUILTIN_TEXTRMSB); 11014132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsh", int_ftype_v4hi_int, ARM_BUILTIN_TEXTRMSH); 11015132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsw", int_ftype_v2si_int, ARM_BUILTIN_TEXTRMSW); 11016132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmub", int_ftype_v8qi_int, ARM_BUILTIN_TEXTRMUB); 11017132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmuh", int_ftype_v4hi_int, ARM_BUILTIN_TEXTRMUH); 11018132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmuw", int_ftype_v2si_int, ARM_BUILTIN_TEXTRMUW); 11019132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrb", v8qi_ftype_v8qi_int_int, ARM_BUILTIN_TINSRB); 11020132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrh", v4hi_ftype_v4hi_int_int, ARM_BUILTIN_TINSRH); 11021132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrw", v2si_ftype_v2si_int_int, ARM_BUILTIN_TINSRW); 11022132718Skan 11023132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_waccb", di_ftype_v8qi, ARM_BUILTIN_WACCB); 11024132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wacch", di_ftype_v4hi, ARM_BUILTIN_WACCH); 11025132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_waccw", di_ftype_v2si, ARM_BUILTIN_WACCW); 11026132718Skan 11027132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskb", int_ftype_v8qi, ARM_BUILTIN_TMOVMSKB); 11028132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskh", int_ftype_v4hi, ARM_BUILTIN_TMOVMSKH); 11029132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskw", int_ftype_v2si, ARM_BUILTIN_TMOVMSKW); 11030132718Skan 11031132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackhss", v8qi_ftype_v4hi_v4hi, ARM_BUILTIN_WPACKHSS); 11032132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackhus", v8qi_ftype_v4hi_v4hi, ARM_BUILTIN_WPACKHUS); 11033132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackwus", v4hi_ftype_v2si_v2si, ARM_BUILTIN_WPACKWUS); 11034132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackwss", v4hi_ftype_v2si_v2si, ARM_BUILTIN_WPACKWSS); 11035132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackdus", v2si_ftype_di_di, ARM_BUILTIN_WPACKDUS); 11036132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackdss", v2si_ftype_di_di, ARM_BUILTIN_WPACKDSS); 11037132718Skan 11038132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehub", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKEHUB); 11039132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehuh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKEHUH); 11040132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehuw", di_ftype_v2si, ARM_BUILTIN_WUNPCKEHUW); 11041132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsb", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKEHSB); 11042132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKEHSH); 11043132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsw", di_ftype_v2si, ARM_BUILTIN_WUNPCKEHSW); 11044132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelub", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKELUB); 11045132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckeluh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKELUH); 11046132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckeluw", di_ftype_v2si, ARM_BUILTIN_WUNPCKELUW); 11047132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsb", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKELSB); 11048132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKELSH); 11049132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsw", di_ftype_v2si, ARM_BUILTIN_WUNPCKELSW); 11050132718Skan 11051132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacs", di_ftype_di_v4hi_v4hi, ARM_BUILTIN_WMACS); 11052132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacsz", di_ftype_v4hi_v4hi, ARM_BUILTIN_WMACSZ); 11053132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacu", di_ftype_di_v4hi_v4hi, ARM_BUILTIN_WMACU); 11054132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacuz", di_ftype_v4hi_v4hi, ARM_BUILTIN_WMACUZ); 11055132718Skan 11056132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_walign", v8qi_ftype_v8qi_v8qi_int, ARM_BUILTIN_WALIGN); 11057132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmia", di_ftype_di_int_int, ARM_BUILTIN_TMIA); 11058132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiaph", di_ftype_di_int_int, ARM_BUILTIN_TMIAPH); 11059132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiabb", di_ftype_di_int_int, ARM_BUILTIN_TMIABB); 11060132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiabt", di_ftype_di_int_int, ARM_BUILTIN_TMIABT); 11061132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiatb", di_ftype_di_int_int, ARM_BUILTIN_TMIATB); 11062132718Skan def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiatt", di_ftype_di_int_int, ARM_BUILTIN_TMIATT); 1106390075Sobrien} 1106490075Sobrien 11065132718Skanstatic void 11066132718Skanarm_init_builtins (void) 11067132718Skan{ 11068132718Skan if (TARGET_REALLY_IWMMXT) 11069132718Skan arm_init_iwmmxt_builtins (); 11070132718Skan} 11071132718Skan 11072132718Skan/* Errors in the source file can cause expand_expr to return const0_rtx 11073132718Skan where we expect a vector. To avoid crashing, use one of the vector 11074132718Skan clear instructions. */ 11075132718Skan 11076132718Skanstatic rtx 11077132718Skansafe_vector_operand (rtx x, enum machine_mode mode) 11078132718Skan{ 11079132718Skan if (x != const0_rtx) 11080132718Skan return x; 11081132718Skan x = gen_reg_rtx (mode); 11082132718Skan 11083132718Skan emit_insn (gen_iwmmxt_clrdi (mode == DImode ? x 11084132718Skan : gen_rtx_SUBREG (DImode, x, 0))); 11085132718Skan return x; 11086132718Skan} 11087132718Skan 11088132718Skan/* Subroutine of arm_expand_builtin to take care of binop insns. */ 11089132718Skan 11090132718Skanstatic rtx 11091132718Skanarm_expand_binop_builtin (enum insn_code icode, 11092132718Skan tree arglist, rtx target) 11093132718Skan{ 11094132718Skan rtx pat; 11095132718Skan tree arg0 = TREE_VALUE (arglist); 11096132718Skan tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 11097132718Skan rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11098132718Skan rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 11099132718Skan enum machine_mode tmode = insn_data[icode].operand[0].mode; 11100132718Skan enum machine_mode mode0 = insn_data[icode].operand[1].mode; 11101132718Skan enum machine_mode mode1 = insn_data[icode].operand[2].mode; 11102132718Skan 11103132718Skan if (VECTOR_MODE_P (mode0)) 11104132718Skan op0 = safe_vector_operand (op0, mode0); 11105132718Skan if (VECTOR_MODE_P (mode1)) 11106132718Skan op1 = safe_vector_operand (op1, mode1); 11107132718Skan 11108132718Skan if (! target 11109132718Skan || GET_MODE (target) != tmode 11110132718Skan || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 11111132718Skan target = gen_reg_rtx (tmode); 11112132718Skan 11113132718Skan /* In case the insn wants input operands in modes different from 11114132718Skan the result, abort. */ 11115132718Skan if (GET_MODE (op0) != mode0 || GET_MODE (op1) != mode1) 11116132718Skan abort (); 11117132718Skan 11118132718Skan if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 11119132718Skan op0 = copy_to_mode_reg (mode0, op0); 11120132718Skan if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) 11121132718Skan op1 = copy_to_mode_reg (mode1, op1); 11122132718Skan 11123132718Skan pat = GEN_FCN (icode) (target, op0, op1); 11124132718Skan if (! pat) 11125132718Skan return 0; 11126132718Skan emit_insn (pat); 11127132718Skan return target; 11128132718Skan} 11129132718Skan 11130132718Skan/* Subroutine of arm_expand_builtin to take care of unop insns. */ 11131132718Skan 11132132718Skanstatic rtx 11133132718Skanarm_expand_unop_builtin (enum insn_code icode, 11134132718Skan tree arglist, rtx target, int do_load) 11135132718Skan{ 11136132718Skan rtx pat; 11137132718Skan tree arg0 = TREE_VALUE (arglist); 11138132718Skan rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11139132718Skan enum machine_mode tmode = insn_data[icode].operand[0].mode; 11140132718Skan enum machine_mode mode0 = insn_data[icode].operand[1].mode; 11141132718Skan 11142132718Skan if (! target 11143132718Skan || GET_MODE (target) != tmode 11144132718Skan || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 11145132718Skan target = gen_reg_rtx (tmode); 11146132718Skan if (do_load) 11147132718Skan op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); 11148132718Skan else 11149132718Skan { 11150132718Skan if (VECTOR_MODE_P (mode0)) 11151132718Skan op0 = safe_vector_operand (op0, mode0); 11152132718Skan 11153132718Skan if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 11154132718Skan op0 = copy_to_mode_reg (mode0, op0); 11155132718Skan } 11156132718Skan 11157132718Skan pat = GEN_FCN (icode) (target, op0); 11158132718Skan if (! pat) 11159132718Skan return 0; 11160132718Skan emit_insn (pat); 11161132718Skan return target; 11162132718Skan} 11163132718Skan 1116490075Sobrien/* Expand an expression EXP that calls a built-in function, 1116590075Sobrien with result going to TARGET if that's convenient 1116690075Sobrien (and in mode MODE if that's convenient). 1116790075Sobrien SUBTARGET may be used as the target for computing one of EXP's operands. 1116890075Sobrien IGNORE is nonzero if the value is to be ignored. */ 1116990075Sobrien 11170132718Skanstatic rtx 11171132718Skanarm_expand_builtin (tree exp, 11172132718Skan rtx target, 11173132718Skan rtx subtarget ATTRIBUTE_UNUSED, 11174132718Skan enum machine_mode mode ATTRIBUTE_UNUSED, 11175132718Skan int ignore ATTRIBUTE_UNUSED) 1117690075Sobrien{ 11177132718Skan const struct builtin_description * d; 11178132718Skan enum insn_code icode; 11179132718Skan tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); 11180132718Skan tree arglist = TREE_OPERAND (exp, 1); 11181132718Skan tree arg0; 11182132718Skan tree arg1; 11183132718Skan tree arg2; 11184132718Skan rtx op0; 11185132718Skan rtx op1; 11186132718Skan rtx op2; 11187132718Skan rtx pat; 11188132718Skan int fcode = DECL_FUNCTION_CODE (fndecl); 11189132718Skan size_t i; 11190132718Skan enum machine_mode tmode; 11191132718Skan enum machine_mode mode0; 11192132718Skan enum machine_mode mode1; 11193132718Skan enum machine_mode mode2; 1119490075Sobrien 1119590075Sobrien switch (fcode) 1119690075Sobrien { 11197132718Skan case ARM_BUILTIN_TEXTRMSB: 11198132718Skan case ARM_BUILTIN_TEXTRMUB: 11199132718Skan case ARM_BUILTIN_TEXTRMSH: 11200132718Skan case ARM_BUILTIN_TEXTRMUH: 11201132718Skan case ARM_BUILTIN_TEXTRMSW: 11202132718Skan case ARM_BUILTIN_TEXTRMUW: 11203132718Skan icode = (fcode == ARM_BUILTIN_TEXTRMSB ? CODE_FOR_iwmmxt_textrmsb 11204132718Skan : fcode == ARM_BUILTIN_TEXTRMUB ? CODE_FOR_iwmmxt_textrmub 11205132718Skan : fcode == ARM_BUILTIN_TEXTRMSH ? CODE_FOR_iwmmxt_textrmsh 11206132718Skan : fcode == ARM_BUILTIN_TEXTRMUH ? CODE_FOR_iwmmxt_textrmuh 11207132718Skan : CODE_FOR_iwmmxt_textrmw); 11208132718Skan 1120990075Sobrien arg0 = TREE_VALUE (arglist); 11210132718Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 1121190075Sobrien op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11212132718Skan op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 1121390075Sobrien tmode = insn_data[icode].operand[0].mode; 1121490075Sobrien mode0 = insn_data[icode].operand[1].mode; 11215132718Skan mode1 = insn_data[icode].operand[2].mode; 1121690075Sobrien 1121790075Sobrien if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 1121890075Sobrien op0 = copy_to_mode_reg (mode0, op0); 11219132718Skan if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) 11220132718Skan { 11221132718Skan /* @@@ better error message */ 11222132718Skan error ("selector must be an immediate"); 11223132718Skan return gen_reg_rtx (tmode); 11224132718Skan } 1122590075Sobrien if (target == 0 1122690075Sobrien || GET_MODE (target) != tmode 1122790075Sobrien || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 1122890075Sobrien target = gen_reg_rtx (tmode); 11229132718Skan pat = GEN_FCN (icode) (target, op0, op1); 1123090075Sobrien if (! pat) 1123190075Sobrien return 0; 1123290075Sobrien emit_insn (pat); 1123390075Sobrien return target; 11234132718Skan 11235132718Skan case ARM_BUILTIN_TINSRB: 11236132718Skan case ARM_BUILTIN_TINSRH: 11237132718Skan case ARM_BUILTIN_TINSRW: 11238132718Skan icode = (fcode == ARM_BUILTIN_TINSRB ? CODE_FOR_iwmmxt_tinsrb 11239132718Skan : fcode == ARM_BUILTIN_TINSRH ? CODE_FOR_iwmmxt_tinsrh 11240132718Skan : CODE_FOR_iwmmxt_tinsrw); 11241132718Skan arg0 = TREE_VALUE (arglist); 11242132718Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 11243132718Skan arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 11244132718Skan op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11245132718Skan op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 11246132718Skan op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); 11247132718Skan tmode = insn_data[icode].operand[0].mode; 11248132718Skan mode0 = insn_data[icode].operand[1].mode; 11249132718Skan mode1 = insn_data[icode].operand[2].mode; 11250132718Skan mode2 = insn_data[icode].operand[3].mode; 11251132718Skan 11252132718Skan if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 11253132718Skan op0 = copy_to_mode_reg (mode0, op0); 11254132718Skan if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) 11255132718Skan op1 = copy_to_mode_reg (mode1, op1); 11256132718Skan if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) 11257132718Skan { 11258132718Skan /* @@@ better error message */ 11259132718Skan error ("selector must be an immediate"); 11260132718Skan return const0_rtx; 11261132718Skan } 11262132718Skan if (target == 0 11263132718Skan || GET_MODE (target) != tmode 11264132718Skan || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 11265132718Skan target = gen_reg_rtx (tmode); 11266132718Skan pat = GEN_FCN (icode) (target, op0, op1, op2); 11267132718Skan if (! pat) 11268132718Skan return 0; 11269132718Skan emit_insn (pat); 11270132718Skan return target; 11271132718Skan 11272132718Skan case ARM_BUILTIN_SETWCX: 11273132718Skan arg0 = TREE_VALUE (arglist); 11274132718Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 11275132718Skan op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11276132718Skan op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 11277132718Skan emit_insn (gen_iwmmxt_tmcr (op0, op1)); 11278132718Skan return 0; 11279132718Skan 11280132718Skan case ARM_BUILTIN_GETWCX: 11281132718Skan arg0 = TREE_VALUE (arglist); 11282132718Skan op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11283132718Skan target = gen_reg_rtx (SImode); 11284132718Skan emit_insn (gen_iwmmxt_tmrc (target, op0)); 11285132718Skan return target; 11286132718Skan 11287132718Skan case ARM_BUILTIN_WSHUFH: 11288132718Skan icode = CODE_FOR_iwmmxt_wshufh; 11289132718Skan arg0 = TREE_VALUE (arglist); 11290132718Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 11291132718Skan op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11292132718Skan op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 11293132718Skan tmode = insn_data[icode].operand[0].mode; 11294132718Skan mode1 = insn_data[icode].operand[1].mode; 11295132718Skan mode2 = insn_data[icode].operand[2].mode; 11296132718Skan 11297132718Skan if (! (*insn_data[icode].operand[1].predicate) (op0, mode1)) 11298132718Skan op0 = copy_to_mode_reg (mode1, op0); 11299132718Skan if (! (*insn_data[icode].operand[2].predicate) (op1, mode2)) 11300132718Skan { 11301132718Skan /* @@@ better error message */ 11302132718Skan error ("mask must be an immediate"); 11303132718Skan return const0_rtx; 11304132718Skan } 11305132718Skan if (target == 0 11306132718Skan || GET_MODE (target) != tmode 11307132718Skan || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 11308132718Skan target = gen_reg_rtx (tmode); 11309132718Skan pat = GEN_FCN (icode) (target, op0, op1); 11310132718Skan if (! pat) 11311132718Skan return 0; 11312132718Skan emit_insn (pat); 11313132718Skan return target; 11314132718Skan 11315132718Skan case ARM_BUILTIN_WSADB: 11316132718Skan return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadb, arglist, target); 11317132718Skan case ARM_BUILTIN_WSADH: 11318132718Skan return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadh, arglist, target); 11319132718Skan case ARM_BUILTIN_WSADBZ: 11320132718Skan return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadbz, arglist, target); 11321132718Skan case ARM_BUILTIN_WSADHZ: 11322132718Skan return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadhz, arglist, target); 11323132718Skan 11324132718Skan /* Several three-argument builtins. */ 11325132718Skan case ARM_BUILTIN_WMACS: 11326132718Skan case ARM_BUILTIN_WMACU: 11327132718Skan case ARM_BUILTIN_WALIGN: 11328132718Skan case ARM_BUILTIN_TMIA: 11329132718Skan case ARM_BUILTIN_TMIAPH: 11330132718Skan case ARM_BUILTIN_TMIATT: 11331132718Skan case ARM_BUILTIN_TMIATB: 11332132718Skan case ARM_BUILTIN_TMIABT: 11333132718Skan case ARM_BUILTIN_TMIABB: 11334132718Skan icode = (fcode == ARM_BUILTIN_WMACS ? CODE_FOR_iwmmxt_wmacs 11335132718Skan : fcode == ARM_BUILTIN_WMACU ? CODE_FOR_iwmmxt_wmacu 11336132718Skan : fcode == ARM_BUILTIN_TMIA ? CODE_FOR_iwmmxt_tmia 11337132718Skan : fcode == ARM_BUILTIN_TMIAPH ? CODE_FOR_iwmmxt_tmiaph 11338132718Skan : fcode == ARM_BUILTIN_TMIABB ? CODE_FOR_iwmmxt_tmiabb 11339132718Skan : fcode == ARM_BUILTIN_TMIABT ? CODE_FOR_iwmmxt_tmiabt 11340132718Skan : fcode == ARM_BUILTIN_TMIATB ? CODE_FOR_iwmmxt_tmiatb 11341132718Skan : fcode == ARM_BUILTIN_TMIATT ? CODE_FOR_iwmmxt_tmiatt 11342132718Skan : CODE_FOR_iwmmxt_walign); 11343132718Skan arg0 = TREE_VALUE (arglist); 11344132718Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 11345132718Skan arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 11346132718Skan op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); 11347132718Skan op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); 11348132718Skan op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); 11349132718Skan tmode = insn_data[icode].operand[0].mode; 11350132718Skan mode0 = insn_data[icode].operand[1].mode; 11351132718Skan mode1 = insn_data[icode].operand[2].mode; 11352132718Skan mode2 = insn_data[icode].operand[3].mode; 11353132718Skan 11354132718Skan if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) 11355132718Skan op0 = copy_to_mode_reg (mode0, op0); 11356132718Skan if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) 11357132718Skan op1 = copy_to_mode_reg (mode1, op1); 11358132718Skan if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) 11359132718Skan op2 = copy_to_mode_reg (mode2, op2); 11360132718Skan if (target == 0 11361132718Skan || GET_MODE (target) != tmode 11362132718Skan || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) 11363132718Skan target = gen_reg_rtx (tmode); 11364132718Skan pat = GEN_FCN (icode) (target, op0, op1, op2); 11365132718Skan if (! pat) 11366132718Skan return 0; 11367132718Skan emit_insn (pat); 11368132718Skan return target; 11369132718Skan 11370132718Skan case ARM_BUILTIN_WZERO: 11371132718Skan target = gen_reg_rtx (DImode); 11372132718Skan emit_insn (gen_iwmmxt_clrdi (target)); 11373132718Skan return target; 11374132718Skan 11375132718Skan default: 11376132718Skan break; 1137790075Sobrien } 11378117395Skan 11379132718Skan for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) 11380132718Skan if (d->code == (const enum arm_builtins) fcode) 11381132718Skan return arm_expand_binop_builtin (d->icode, arglist, target); 11382132718Skan 11383132718Skan for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) 11384132718Skan if (d->code == (const enum arm_builtins) fcode) 11385132718Skan return arm_expand_unop_builtin (d->icode, arglist, target, 0); 11386132718Skan 1138790075Sobrien /* @@@ Should really do something sensible here. */ 1138890075Sobrien return NULL_RTX; 1138990075Sobrien} 1139090075Sobrien 1139190075Sobrien/* Recursively search through all of the blocks in a function 1139290075Sobrien checking to see if any of the variables created in that 1139390075Sobrien function match the RTX called 'orig'. If they do then 1139490075Sobrien replace them with the RTX called 'new'. */ 1139590075Sobrienstatic void 11396132718Skanreplace_symbols_in_block (tree block, rtx orig, rtx new) 1139790075Sobrien{ 1139890075Sobrien for (; block; block = BLOCK_CHAIN (block)) 1139990075Sobrien { 1140090075Sobrien tree sym; 1140190075Sobrien 1140290075Sobrien if (!TREE_USED (block)) 1140390075Sobrien continue; 1140490075Sobrien 1140590075Sobrien for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym)) 1140690075Sobrien { 1140790075Sobrien if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL) 1140890075Sobrien || DECL_IGNORED_P (sym) 1140990075Sobrien || TREE_CODE (sym) != VAR_DECL 1141090075Sobrien || DECL_EXTERNAL (sym) 1141190075Sobrien || !rtx_equal_p (DECL_RTL (sym), orig) 1141290075Sobrien ) 1141390075Sobrien continue; 1141490075Sobrien 1141590075Sobrien SET_DECL_RTL (sym, new); 1141690075Sobrien } 1141790075Sobrien 1141890075Sobrien replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new); 1141990075Sobrien } 1142090075Sobrien} 1142190075Sobrien 1142290075Sobrien/* Return the number (counting from 0) of 1142390075Sobrien the least significant set bit in MASK. */ 1142490075Sobrien 11425132718Skaninline static int 11426132718Skannumber_of_first_bit_set (int mask) 1142790075Sobrien{ 1142890075Sobrien int bit; 1142990075Sobrien 1143090075Sobrien for (bit = 0; 1143190075Sobrien (mask & (1 << bit)) == 0; 1143290075Sobrien ++bit) 1143390075Sobrien continue; 1143490075Sobrien 1143590075Sobrien return bit; 1143690075Sobrien} 1143790075Sobrien 1143890075Sobrien/* Generate code to return from a thumb function. 1143990075Sobrien If 'reg_containing_return_addr' is -1, then the return address is 1144090075Sobrien actually on the stack, at the stack pointer. */ 1144190075Sobrienstatic void 11442132718Skanthumb_exit (FILE *f, int reg_containing_return_addr, rtx eh_ofs) 1144390075Sobrien{ 1144490075Sobrien unsigned regs_available_for_popping; 1144590075Sobrien unsigned regs_to_pop; 1144690075Sobrien int pops_needed; 1144790075Sobrien unsigned available; 1144890075Sobrien unsigned required; 1144990075Sobrien int mode; 1145090075Sobrien int size; 1145190075Sobrien int restore_a4 = FALSE; 1145290075Sobrien 1145390075Sobrien /* Compute the registers we need to pop. */ 1145490075Sobrien regs_to_pop = 0; 1145590075Sobrien pops_needed = 0; 1145690075Sobrien 1145790075Sobrien /* There is an assumption here, that if eh_ofs is not NULL, the 1145890075Sobrien normal return address will have been pushed. */ 1145990075Sobrien if (reg_containing_return_addr == -1 || eh_ofs) 1146090075Sobrien { 1146190075Sobrien /* When we are generating a return for __builtin_eh_return, 1146290075Sobrien reg_containing_return_addr must specify the return regno. */ 1146390075Sobrien if (eh_ofs && reg_containing_return_addr == -1) 1146490075Sobrien abort (); 1146590075Sobrien 1146690075Sobrien regs_to_pop |= 1 << LR_REGNUM; 1146790075Sobrien ++pops_needed; 1146890075Sobrien } 1146990075Sobrien 1147090075Sobrien if (TARGET_BACKTRACE) 1147190075Sobrien { 1147290075Sobrien /* Restore the (ARM) frame pointer and stack pointer. */ 1147390075Sobrien regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM); 1147490075Sobrien pops_needed += 2; 1147590075Sobrien } 1147690075Sobrien 1147790075Sobrien /* If there is nothing to pop then just emit the BX instruction and 1147890075Sobrien return. */ 1147990075Sobrien if (pops_needed == 0) 1148090075Sobrien { 1148190075Sobrien if (eh_ofs) 1148290075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 1148390075Sobrien 1148490075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 1148590075Sobrien return; 1148690075Sobrien } 1148790075Sobrien /* Otherwise if we are not supporting interworking and we have not created 1148890075Sobrien a backtrace structure and the function was not entered in ARM mode then 1148990075Sobrien just pop the return address straight into the PC. */ 1149090075Sobrien else if (!TARGET_INTERWORK 1149190075Sobrien && !TARGET_BACKTRACE 1149290075Sobrien && !is_called_in_ARM_mode (current_function_decl)) 1149390075Sobrien { 1149490075Sobrien if (eh_ofs) 1149590075Sobrien { 1149690075Sobrien asm_fprintf (f, "\tadd\t%r, #4\n", SP_REGNUM); 1149790075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 1149890075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 1149990075Sobrien } 1150090075Sobrien else 1150190075Sobrien asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM); 1150290075Sobrien 1150390075Sobrien return; 1150490075Sobrien } 1150590075Sobrien 1150690075Sobrien /* Find out how many of the (return) argument registers we can corrupt. */ 1150790075Sobrien regs_available_for_popping = 0; 1150890075Sobrien 1150990075Sobrien /* If returning via __builtin_eh_return, the bottom three registers 1151090075Sobrien all contain information needed for the return. */ 1151190075Sobrien if (eh_ofs) 1151290075Sobrien size = 12; 1151390075Sobrien else 1151490075Sobrien { 1151590075Sobrien#ifdef RTX_CODE 1151690075Sobrien /* If we can deduce the registers used from the function's 1151790075Sobrien return value. This is more reliable that examining 1151890075Sobrien regs_ever_live[] because that will be set if the register is 1151990075Sobrien ever used in the function, not just if the register is used 1152090075Sobrien to hold a return value. */ 1152190075Sobrien 1152290075Sobrien if (current_function_return_rtx != 0) 1152390075Sobrien mode = GET_MODE (current_function_return_rtx); 1152490075Sobrien else 1152590075Sobrien#endif 1152690075Sobrien mode = DECL_MODE (DECL_RESULT (current_function_decl)); 1152790075Sobrien 1152890075Sobrien size = GET_MODE_SIZE (mode); 1152990075Sobrien 1153090075Sobrien if (size == 0) 1153190075Sobrien { 1153290075Sobrien /* In a void function we can use any argument register. 1153390075Sobrien In a function that returns a structure on the stack 1153490075Sobrien we can use the second and third argument registers. */ 1153590075Sobrien if (mode == VOIDmode) 1153690075Sobrien regs_available_for_popping = 1153790075Sobrien (1 << ARG_REGISTER (1)) 1153890075Sobrien | (1 << ARG_REGISTER (2)) 1153990075Sobrien | (1 << ARG_REGISTER (3)); 1154090075Sobrien else 1154190075Sobrien regs_available_for_popping = 1154290075Sobrien (1 << ARG_REGISTER (2)) 1154390075Sobrien | (1 << ARG_REGISTER (3)); 1154490075Sobrien } 1154590075Sobrien else if (size <= 4) 1154690075Sobrien regs_available_for_popping = 1154790075Sobrien (1 << ARG_REGISTER (2)) 1154890075Sobrien | (1 << ARG_REGISTER (3)); 1154990075Sobrien else if (size <= 8) 1155090075Sobrien regs_available_for_popping = 1155190075Sobrien (1 << ARG_REGISTER (3)); 1155290075Sobrien } 1155390075Sobrien 1155490075Sobrien /* Match registers to be popped with registers into which we pop them. */ 1155590075Sobrien for (available = regs_available_for_popping, 1155690075Sobrien required = regs_to_pop; 1155790075Sobrien required != 0 && available != 0; 1155890075Sobrien available &= ~(available & - available), 1155990075Sobrien required &= ~(required & - required)) 1156090075Sobrien -- pops_needed; 1156190075Sobrien 1156290075Sobrien /* If we have any popping registers left over, remove them. */ 1156390075Sobrien if (available > 0) 1156490075Sobrien regs_available_for_popping &= ~available; 1156590075Sobrien 1156690075Sobrien /* Otherwise if we need another popping register we can use 1156790075Sobrien the fourth argument register. */ 1156890075Sobrien else if (pops_needed) 1156990075Sobrien { 1157090075Sobrien /* If we have not found any free argument registers and 1157190075Sobrien reg a4 contains the return address, we must move it. */ 1157290075Sobrien if (regs_available_for_popping == 0 1157390075Sobrien && reg_containing_return_addr == LAST_ARG_REGNUM) 1157490075Sobrien { 1157590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); 1157690075Sobrien reg_containing_return_addr = LR_REGNUM; 1157790075Sobrien } 1157890075Sobrien else if (size > 12) 1157990075Sobrien { 1158090075Sobrien /* Register a4 is being used to hold part of the return value, 1158190075Sobrien but we have dire need of a free, low register. */ 1158290075Sobrien restore_a4 = TRUE; 1158390075Sobrien 1158490075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM); 1158590075Sobrien } 1158690075Sobrien 1158790075Sobrien if (reg_containing_return_addr != LAST_ARG_REGNUM) 1158890075Sobrien { 1158990075Sobrien /* The fourth argument register is available. */ 1159090075Sobrien regs_available_for_popping |= 1 << LAST_ARG_REGNUM; 1159190075Sobrien 1159290075Sobrien --pops_needed; 1159390075Sobrien } 1159490075Sobrien } 1159590075Sobrien 1159690075Sobrien /* Pop as many registers as we can. */ 11597132718Skan thumb_pushpop (f, regs_available_for_popping, FALSE, NULL, 11598132718Skan regs_available_for_popping); 1159990075Sobrien 1160090075Sobrien /* Process the registers we popped. */ 1160190075Sobrien if (reg_containing_return_addr == -1) 1160290075Sobrien { 1160390075Sobrien /* The return address was popped into the lowest numbered register. */ 1160490075Sobrien regs_to_pop &= ~(1 << LR_REGNUM); 1160590075Sobrien 1160690075Sobrien reg_containing_return_addr = 1160790075Sobrien number_of_first_bit_set (regs_available_for_popping); 1160890075Sobrien 1160990075Sobrien /* Remove this register for the mask of available registers, so that 11610132718Skan the return address will not be corrupted by further pops. */ 1161190075Sobrien regs_available_for_popping &= ~(1 << reg_containing_return_addr); 1161290075Sobrien } 1161390075Sobrien 1161490075Sobrien /* If we popped other registers then handle them here. */ 1161590075Sobrien if (regs_available_for_popping) 1161690075Sobrien { 1161790075Sobrien int frame_pointer; 1161890075Sobrien 1161990075Sobrien /* Work out which register currently contains the frame pointer. */ 1162090075Sobrien frame_pointer = number_of_first_bit_set (regs_available_for_popping); 1162190075Sobrien 1162290075Sobrien /* Move it into the correct place. */ 1162390075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", 1162490075Sobrien ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer); 1162590075Sobrien 1162690075Sobrien /* (Temporarily) remove it from the mask of popped registers. */ 1162790075Sobrien regs_available_for_popping &= ~(1 << frame_pointer); 1162890075Sobrien regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM); 1162990075Sobrien 1163090075Sobrien if (regs_available_for_popping) 1163190075Sobrien { 1163290075Sobrien int stack_pointer; 1163390075Sobrien 1163490075Sobrien /* We popped the stack pointer as well, 1163590075Sobrien find the register that contains it. */ 1163690075Sobrien stack_pointer = number_of_first_bit_set (regs_available_for_popping); 1163790075Sobrien 1163890075Sobrien /* Move it into the stack register. */ 1163990075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer); 1164090075Sobrien 1164190075Sobrien /* At this point we have popped all necessary registers, so 1164290075Sobrien do not worry about restoring regs_available_for_popping 1164390075Sobrien to its correct value: 1164490075Sobrien 1164590075Sobrien assert (pops_needed == 0) 1164690075Sobrien assert (regs_available_for_popping == (1 << frame_pointer)) 1164790075Sobrien assert (regs_to_pop == (1 << STACK_POINTER)) */ 1164890075Sobrien } 1164990075Sobrien else 1165090075Sobrien { 1165190075Sobrien /* Since we have just move the popped value into the frame 1165290075Sobrien pointer, the popping register is available for reuse, and 1165390075Sobrien we know that we still have the stack pointer left to pop. */ 1165490075Sobrien regs_available_for_popping |= (1 << frame_pointer); 1165590075Sobrien } 1165690075Sobrien } 1165790075Sobrien 1165890075Sobrien /* If we still have registers left on the stack, but we no longer have 1165990075Sobrien any registers into which we can pop them, then we must move the return 1166090075Sobrien address into the link register and make available the register that 1166190075Sobrien contained it. */ 1166290075Sobrien if (regs_available_for_popping == 0 && pops_needed > 0) 1166390075Sobrien { 1166490075Sobrien regs_available_for_popping |= 1 << reg_containing_return_addr; 1166590075Sobrien 1166690075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, 1166790075Sobrien reg_containing_return_addr); 1166890075Sobrien 1166990075Sobrien reg_containing_return_addr = LR_REGNUM; 1167090075Sobrien } 1167190075Sobrien 1167290075Sobrien /* If we have registers left on the stack then pop some more. 1167390075Sobrien We know that at most we will want to pop FP and SP. */ 1167490075Sobrien if (pops_needed > 0) 1167590075Sobrien { 1167690075Sobrien int popped_into; 1167790075Sobrien int move_to; 1167890075Sobrien 11679132718Skan thumb_pushpop (f, regs_available_for_popping, FALSE, NULL, 11680132718Skan regs_available_for_popping); 1168190075Sobrien 1168290075Sobrien /* We have popped either FP or SP. 1168390075Sobrien Move whichever one it is into the correct register. */ 1168490075Sobrien popped_into = number_of_first_bit_set (regs_available_for_popping); 1168590075Sobrien move_to = number_of_first_bit_set (regs_to_pop); 1168690075Sobrien 1168790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into); 1168890075Sobrien 1168990075Sobrien regs_to_pop &= ~(1 << move_to); 1169090075Sobrien 1169190075Sobrien --pops_needed; 1169290075Sobrien } 1169390075Sobrien 1169490075Sobrien /* If we still have not popped everything then we must have only 1169590075Sobrien had one register available to us and we are now popping the SP. */ 1169690075Sobrien if (pops_needed > 0) 1169790075Sobrien { 1169890075Sobrien int popped_into; 1169990075Sobrien 11700132718Skan thumb_pushpop (f, regs_available_for_popping, FALSE, NULL, 11701132718Skan regs_available_for_popping); 1170290075Sobrien 1170390075Sobrien popped_into = number_of_first_bit_set (regs_available_for_popping); 1170490075Sobrien 1170590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into); 1170690075Sobrien /* 1170790075Sobrien assert (regs_to_pop == (1 << STACK_POINTER)) 1170890075Sobrien assert (pops_needed == 1) 1170990075Sobrien */ 1171090075Sobrien } 1171190075Sobrien 1171290075Sobrien /* If necessary restore the a4 register. */ 1171390075Sobrien if (restore_a4) 1171490075Sobrien { 1171590075Sobrien if (reg_containing_return_addr != LR_REGNUM) 1171690075Sobrien { 1171790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM); 1171890075Sobrien reg_containing_return_addr = LR_REGNUM; 1171990075Sobrien } 1172090075Sobrien 1172190075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); 1172290075Sobrien } 1172390075Sobrien 1172490075Sobrien if (eh_ofs) 1172590075Sobrien asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, REGNO (eh_ofs)); 1172690075Sobrien 1172790075Sobrien /* Return to caller. */ 1172890075Sobrien asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); 1172990075Sobrien} 1173090075Sobrien 11731132718Skan/* Emit code to push or pop registers to or from the stack. F is the 11732132718Skan assembly file. MASK is the registers to push or pop. PUSH is 11733132718Skan non-zero if we should push, and zero if we should pop. For debugging 11734132718Skan output, if pushing, adjust CFA_OFFSET by the amount of space added 11735132718Skan to the stack. REAL_REGS should have the same number of bits set as 11736132718Skan MASK, and will be used instead (in the same order) to describe which 11737132718Skan registers were saved - this is used to mark the save slots when we 11738132718Skan push high registers after moving them to low registers. */ 1173990075Sobrienstatic void 11740132718Skanthumb_pushpop (FILE *f, int mask, int push, int *cfa_offset, int real_regs) 1174190075Sobrien{ 1174290075Sobrien int regno; 1174390075Sobrien int lo_mask = mask & 0xFF; 11744132718Skan int pushed_words = 0; 1174590075Sobrien 1174690075Sobrien if (lo_mask == 0 && !push && (mask & (1 << 15))) 1174790075Sobrien { 1174890075Sobrien /* Special case. Do not generate a POP PC statement here, do it in 1174990075Sobrien thumb_exit() */ 1175090075Sobrien thumb_exit (f, -1, NULL_RTX); 1175190075Sobrien return; 1175290075Sobrien } 1175390075Sobrien 1175490075Sobrien fprintf (f, "\t%s\t{", push ? "push" : "pop"); 1175590075Sobrien 1175690075Sobrien /* Look at the low registers first. */ 1175790075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1) 1175890075Sobrien { 1175990075Sobrien if (lo_mask & 1) 1176090075Sobrien { 1176190075Sobrien asm_fprintf (f, "%r", regno); 1176290075Sobrien 1176390075Sobrien if ((lo_mask & ~1) != 0) 1176490075Sobrien fprintf (f, ", "); 11765132718Skan 11766132718Skan pushed_words++; 1176790075Sobrien } 1176890075Sobrien } 1176990075Sobrien 1177090075Sobrien if (push && (mask & (1 << LR_REGNUM))) 1177190075Sobrien { 1177290075Sobrien /* Catch pushing the LR. */ 1177390075Sobrien if (mask & 0xFF) 1177490075Sobrien fprintf (f, ", "); 1177590075Sobrien 1177690075Sobrien asm_fprintf (f, "%r", LR_REGNUM); 11777132718Skan 11778132718Skan pushed_words++; 1177990075Sobrien } 1178090075Sobrien else if (!push && (mask & (1 << PC_REGNUM))) 1178190075Sobrien { 1178290075Sobrien /* Catch popping the PC. */ 1178390075Sobrien if (TARGET_INTERWORK || TARGET_BACKTRACE) 1178490075Sobrien { 1178590075Sobrien /* The PC is never poped directly, instead 1178690075Sobrien it is popped into r3 and then BX is used. */ 1178790075Sobrien fprintf (f, "}\n"); 1178890075Sobrien 1178990075Sobrien thumb_exit (f, -1, NULL_RTX); 1179090075Sobrien 1179190075Sobrien return; 1179290075Sobrien } 1179390075Sobrien else 1179490075Sobrien { 1179590075Sobrien if (mask & 0xFF) 1179690075Sobrien fprintf (f, ", "); 1179790075Sobrien 1179890075Sobrien asm_fprintf (f, "%r", PC_REGNUM); 1179990075Sobrien } 1180090075Sobrien } 1180190075Sobrien 1180290075Sobrien fprintf (f, "}\n"); 11803132718Skan 11804132718Skan if (push && pushed_words && dwarf2out_do_frame ()) 11805132718Skan { 11806132718Skan char *l = dwarf2out_cfi_label (); 11807132718Skan int pushed_mask = real_regs; 11808132718Skan 11809132718Skan *cfa_offset += pushed_words * 4; 11810132718Skan dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset); 11811132718Skan 11812132718Skan pushed_words = 0; 11813132718Skan pushed_mask = real_regs; 11814132718Skan for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1) 11815132718Skan { 11816132718Skan if (pushed_mask & 1) 11817132718Skan dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset); 11818132718Skan } 11819132718Skan } 1182090075Sobrien} 1182190075Sobrien 1182290075Sobrienvoid 11823132718Skanthumb_final_prescan_insn (rtx insn) 1182490075Sobrien{ 1182590075Sobrien if (flag_print_asm_name) 1182690075Sobrien asm_fprintf (asm_out_file, "%@ 0x%04x\n", 1182790075Sobrien INSN_ADDRESSES (INSN_UID (insn))); 1182890075Sobrien} 1182990075Sobrien 1183090075Sobrienint 11831132718Skanthumb_shiftable_const (unsigned HOST_WIDE_INT val) 1183290075Sobrien{ 1183390075Sobrien unsigned HOST_WIDE_INT mask = 0xff; 1183490075Sobrien int i; 1183590075Sobrien 1183690075Sobrien if (val == 0) /* XXX */ 1183790075Sobrien return 0; 1183890075Sobrien 1183990075Sobrien for (i = 0; i < 25; i++) 1184090075Sobrien if ((val & (mask << i)) == val) 1184190075Sobrien return 1; 1184290075Sobrien 1184390075Sobrien return 0; 1184490075Sobrien} 1184590075Sobrien 11846117395Skan/* Returns nonzero if the current function contains, 1184790075Sobrien or might contain a far jump. */ 1184890075Sobrienint 11849132718Skanthumb_far_jump_used_p (int in_prologue) 1185090075Sobrien{ 1185190075Sobrien rtx insn; 1185290075Sobrien 1185390075Sobrien /* This test is only important for leaf functions. */ 1185490075Sobrien /* assert (!leaf_function_p ()); */ 1185590075Sobrien 1185690075Sobrien /* If we have already decided that far jumps may be used, 1185790075Sobrien do not bother checking again, and always return true even if 1185890075Sobrien it turns out that they are not being used. Once we have made 1185990075Sobrien the decision that far jumps are present (and that hence the link 1186090075Sobrien register will be pushed onto the stack) we cannot go back on it. */ 1186190075Sobrien if (cfun->machine->far_jump_used) 1186290075Sobrien return 1; 1186390075Sobrien 1186490075Sobrien /* If this function is not being called from the prologue/epilogue 1186590075Sobrien generation code then it must be being called from the 1186690075Sobrien INITIAL_ELIMINATION_OFFSET macro. */ 1186790075Sobrien if (!in_prologue) 1186890075Sobrien { 1186990075Sobrien /* In this case we know that we are being asked about the elimination 1187090075Sobrien of the arg pointer register. If that register is not being used, 1187190075Sobrien then there are no arguments on the stack, and we do not have to 1187290075Sobrien worry that a far jump might force the prologue to push the link 1187390075Sobrien register, changing the stack offsets. In this case we can just 1187490075Sobrien return false, since the presence of far jumps in the function will 1187590075Sobrien not affect stack offsets. 1187690075Sobrien 1187790075Sobrien If the arg pointer is live (or if it was live, but has now been 1187890075Sobrien eliminated and so set to dead) then we do have to test to see if 1187990075Sobrien the function might contain a far jump. This test can lead to some 1188090075Sobrien false negatives, since before reload is completed, then length of 1188190075Sobrien branch instructions is not known, so gcc defaults to returning their 1188290075Sobrien longest length, which in turn sets the far jump attribute to true. 1188390075Sobrien 1188490075Sobrien A false negative will not result in bad code being generated, but it 1188590075Sobrien will result in a needless push and pop of the link register. We 1188690075Sobrien hope that this does not occur too often. */ 1188790075Sobrien if (regs_ever_live [ARG_POINTER_REGNUM]) 1188890075Sobrien cfun->machine->arg_pointer_live = 1; 1188990075Sobrien else if (!cfun->machine->arg_pointer_live) 1189090075Sobrien return 0; 1189190075Sobrien } 1189290075Sobrien 1189390075Sobrien /* Check to see if the function contains a branch 1189490075Sobrien insn with the far jump attribute set. */ 1189590075Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 1189690075Sobrien { 1189790075Sobrien if (GET_CODE (insn) == JUMP_INSN 1189890075Sobrien /* Ignore tablejump patterns. */ 1189990075Sobrien && GET_CODE (PATTERN (insn)) != ADDR_VEC 1190090075Sobrien && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC 1190190075Sobrien && get_attr_far_jump (insn) == FAR_JUMP_YES 1190290075Sobrien ) 1190390075Sobrien { 11904132718Skan /* Record the fact that we have decided that 1190590075Sobrien the function does use far jumps. */ 1190690075Sobrien cfun->machine->far_jump_used = 1; 1190790075Sobrien return 1; 1190890075Sobrien } 1190990075Sobrien } 1191090075Sobrien 1191190075Sobrien return 0; 1191290075Sobrien} 1191390075Sobrien 11914117395Skan/* Return nonzero if FUNC must be entered in ARM mode. */ 1191590075Sobrienint 11916132718Skanis_called_in_ARM_mode (tree func) 1191790075Sobrien{ 1191890075Sobrien if (TREE_CODE (func) != FUNCTION_DECL) 1191990075Sobrien abort (); 1192090075Sobrien 1192190075Sobrien /* Ignore the problem about functions whoes address is taken. */ 1192290075Sobrien if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func)) 1192390075Sobrien return TRUE; 1192490075Sobrien 1192590075Sobrien#ifdef ARM_PE 1192690075Sobrien return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE; 1192790075Sobrien#else 1192890075Sobrien return FALSE; 1192990075Sobrien#endif 1193090075Sobrien} 1193190075Sobrien 11932132718Skan/* The bits which aren't usefully expanded as rtl. */ 1193390075Sobrienconst char * 11934132718Skanthumb_unexpanded_epilogue (void) 1193590075Sobrien{ 1193690075Sobrien int regno; 1193790075Sobrien int live_regs_mask = 0; 1193890075Sobrien int high_regs_pushed = 0; 1193990075Sobrien int leaf_function = leaf_function_p (); 1194090075Sobrien int had_to_push_lr; 1194190075Sobrien rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; 1194290075Sobrien 1194390075Sobrien if (return_used_this_function) 1194490075Sobrien return ""; 1194590075Sobrien 11946117395Skan if (IS_NAKED (arm_current_func_type ())) 11947117395Skan return ""; 11948117395Skan 1194990075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 11950117395Skan if (THUMB_REG_PUSHED_P (regno)) 1195190075Sobrien live_regs_mask |= 1 << regno; 1195290075Sobrien 1195390075Sobrien for (regno = 8; regno < 13; regno++) 11954117395Skan if (THUMB_REG_PUSHED_P (regno)) 11955117395Skan high_regs_pushed++; 1195690075Sobrien 1195790075Sobrien /* The prolog may have pushed some high registers to use as 11958132718Skan work registers. eg the testsuite file: 1195990075Sobrien gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c 1196090075Sobrien compiles to produce: 1196190075Sobrien push {r4, r5, r6, r7, lr} 1196290075Sobrien mov r7, r9 1196390075Sobrien mov r6, r8 1196490075Sobrien push {r6, r7} 1196590075Sobrien as part of the prolog. We have to undo that pushing here. */ 1196690075Sobrien 1196790075Sobrien if (high_regs_pushed) 1196890075Sobrien { 1196990075Sobrien int mask = live_regs_mask; 1197090075Sobrien int next_hi_reg; 1197190075Sobrien int size; 1197290075Sobrien int mode; 1197390075Sobrien 1197490075Sobrien#ifdef RTX_CODE 1197590075Sobrien /* If we can deduce the registers used from the function's return value. 1197690075Sobrien This is more reliable that examining regs_ever_live[] because that 1197790075Sobrien will be set if the register is ever used in the function, not just if 1197890075Sobrien the register is used to hold a return value. */ 1197990075Sobrien 1198090075Sobrien if (current_function_return_rtx != 0) 1198190075Sobrien mode = GET_MODE (current_function_return_rtx); 1198290075Sobrien else 1198390075Sobrien#endif 1198490075Sobrien mode = DECL_MODE (DECL_RESULT (current_function_decl)); 1198590075Sobrien 1198690075Sobrien size = GET_MODE_SIZE (mode); 1198790075Sobrien 1198890075Sobrien /* Unless we are returning a type of size > 12 register r3 is 1198990075Sobrien available. */ 1199090075Sobrien if (size < 13) 1199190075Sobrien mask |= 1 << 3; 1199290075Sobrien 1199390075Sobrien if (mask == 0) 1199490075Sobrien /* Oh dear! We have no low registers into which we can pop 1199590075Sobrien high registers! */ 1199690075Sobrien internal_error 1199790075Sobrien ("no low registers available for popping high registers"); 1199890075Sobrien 1199990075Sobrien for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++) 12000117395Skan if (THUMB_REG_PUSHED_P (next_hi_reg)) 1200190075Sobrien break; 1200290075Sobrien 1200390075Sobrien while (high_regs_pushed) 1200490075Sobrien { 1200590075Sobrien /* Find lo register(s) into which the high register(s) can 1200690075Sobrien be popped. */ 1200790075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 1200890075Sobrien { 1200990075Sobrien if (mask & (1 << regno)) 1201090075Sobrien high_regs_pushed--; 1201190075Sobrien if (high_regs_pushed == 0) 1201290075Sobrien break; 1201390075Sobrien } 1201490075Sobrien 1201590075Sobrien mask &= (2 << regno) - 1; /* A noop if regno == 8 */ 1201690075Sobrien 12017132718Skan /* Pop the values into the low register(s). */ 12018132718Skan thumb_pushpop (asm_out_file, mask, 0, NULL, mask); 1201990075Sobrien 1202090075Sobrien /* Move the value(s) into the high registers. */ 1202190075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 1202290075Sobrien { 1202390075Sobrien if (mask & (1 << regno)) 1202490075Sobrien { 1202590075Sobrien asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg, 1202690075Sobrien regno); 1202790075Sobrien 1202890075Sobrien for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++) 12029117395Skan if (THUMB_REG_PUSHED_P (next_hi_reg)) 1203090075Sobrien break; 1203190075Sobrien } 1203290075Sobrien } 1203390075Sobrien } 1203490075Sobrien } 1203590075Sobrien 1203690075Sobrien had_to_push_lr = (live_regs_mask || !leaf_function 1203790075Sobrien || thumb_far_jump_used_p (1)); 1203890075Sobrien 1203990075Sobrien if (TARGET_BACKTRACE 1204090075Sobrien && ((live_regs_mask & 0xFF) == 0) 1204190075Sobrien && regs_ever_live [LAST_ARG_REGNUM] != 0) 1204290075Sobrien { 1204390075Sobrien /* The stack backtrace structure creation code had to 1204490075Sobrien push R7 in order to get a work register, so we pop 12045132718Skan it now. */ 1204690075Sobrien live_regs_mask |= (1 << LAST_LO_REGNUM); 1204790075Sobrien } 1204890075Sobrien 1204990075Sobrien if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE) 1205090075Sobrien { 1205190075Sobrien if (had_to_push_lr 1205290075Sobrien && !is_called_in_ARM_mode (current_function_decl) 1205390075Sobrien && !eh_ofs) 1205490075Sobrien live_regs_mask |= 1 << PC_REGNUM; 1205590075Sobrien 1205690075Sobrien /* Either no argument registers were pushed or a backtrace 1205790075Sobrien structure was created which includes an adjusted stack 1205890075Sobrien pointer, so just pop everything. */ 1205990075Sobrien if (live_regs_mask) 12060132718Skan thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL, 12061132718Skan live_regs_mask); 1206290075Sobrien 1206390075Sobrien if (eh_ofs) 1206490075Sobrien thumb_exit (asm_out_file, 2, eh_ofs); 1206590075Sobrien /* We have either just popped the return address into the 1206690075Sobrien PC or it is was kept in LR for the entire function or 1206790075Sobrien it is still on the stack because we do not want to 1206890075Sobrien return by doing a pop {pc}. */ 1206990075Sobrien else if ((live_regs_mask & (1 << PC_REGNUM)) == 0) 1207090075Sobrien thumb_exit (asm_out_file, 1207190075Sobrien (had_to_push_lr 1207290075Sobrien && is_called_in_ARM_mode (current_function_decl)) ? 1207390075Sobrien -1 : LR_REGNUM, NULL_RTX); 1207490075Sobrien } 1207590075Sobrien else 1207690075Sobrien { 1207790075Sobrien /* Pop everything but the return address. */ 1207890075Sobrien live_regs_mask &= ~(1 << PC_REGNUM); 1207990075Sobrien 1208090075Sobrien if (live_regs_mask) 12081132718Skan thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL, 12082132718Skan live_regs_mask); 1208390075Sobrien 1208490075Sobrien if (had_to_push_lr) 1208590075Sobrien /* Get the return address into a temporary register. */ 12086132718Skan thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL, 12087132718Skan 1 << LAST_ARG_REGNUM); 1208890075Sobrien 1208990075Sobrien /* Remove the argument registers that were pushed onto the stack. */ 1209090075Sobrien asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n", 1209190075Sobrien SP_REGNUM, SP_REGNUM, 1209290075Sobrien current_function_pretend_args_size); 1209390075Sobrien 1209490075Sobrien if (eh_ofs) 1209590075Sobrien thumb_exit (asm_out_file, 2, eh_ofs); 1209690075Sobrien else 1209790075Sobrien thumb_exit (asm_out_file, 1209890075Sobrien had_to_push_lr ? LAST_ARG_REGNUM : LR_REGNUM, NULL_RTX); 1209990075Sobrien } 1210090075Sobrien 1210190075Sobrien return ""; 1210290075Sobrien} 1210390075Sobrien 1210490075Sobrien/* Functions to save and restore machine-specific function data. */ 12105117395Skanstatic struct machine_function * 12106132718Skanarm_init_machine_status (void) 1210790075Sobrien{ 12108117395Skan struct machine_function *machine; 12109117395Skan machine = (machine_function *) ggc_alloc_cleared (sizeof (machine_function)); 1211090075Sobrien 12111117395Skan#if ARM_FT_UNKNOWN != 0 12112117395Skan machine->func_type = ARM_FT_UNKNOWN; 1211390075Sobrien#endif 12114117395Skan return machine; 1211590075Sobrien} 1211690075Sobrien 1211790075Sobrien/* Return an RTX indicating where the return address to the 1211890075Sobrien calling function can be found. */ 1211990075Sobrienrtx 12120132718Skanarm_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) 1212190075Sobrien{ 1212290075Sobrien if (count != 0) 1212390075Sobrien return NULL_RTX; 1212490075Sobrien 1212590075Sobrien if (TARGET_APCS_32) 1212690075Sobrien return get_hard_reg_initial_val (Pmode, LR_REGNUM); 1212790075Sobrien else 1212890075Sobrien { 1212990075Sobrien rtx lr = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM), 1213090075Sobrien GEN_INT (RETURN_ADDR_MASK26)); 1213190075Sobrien return get_func_hard_reg_initial_val (cfun, lr); 1213290075Sobrien } 1213390075Sobrien} 1213490075Sobrien 1213590075Sobrien/* Do anything needed before RTL is emitted for each function. */ 1213690075Sobrienvoid 12137132718Skanarm_init_expanders (void) 1213890075Sobrien{ 1213990075Sobrien /* Arrange to initialize and mark the machine per-function status. */ 1214090075Sobrien init_machine_status = arm_init_machine_status; 1214190075Sobrien} 1214290075Sobrien 12143117395SkanHOST_WIDE_INT 12144132718Skanthumb_get_frame_size (void) 12145117395Skan{ 12146117395Skan int regno; 12147117395Skan 12148132718Skan int base_size = ROUND_UP_WORD (get_frame_size ()); 12149117395Skan int count_regs = 0; 12150117395Skan int entry_size = 0; 12151117395Skan int leaf; 12152117395Skan 12153117395Skan if (! TARGET_THUMB) 12154117395Skan abort (); 12155117395Skan 12156117395Skan if (! TARGET_ATPCS) 12157117395Skan return base_size; 12158117395Skan 12159117395Skan /* We need to know if we are a leaf function. Unfortunately, it 12160117395Skan is possible to be called after start_sequence has been called, 12161117395Skan which causes get_insns to return the insns for the sequence, 12162117395Skan not the function, which will cause leaf_function_p to return 12163117395Skan the incorrect result. 12164117395Skan 12165117395Skan To work around this, we cache the computed frame size. This 12166117395Skan works because we will only be calling RTL expanders that need 12167117395Skan to know about leaf functions once reload has completed, and the 12168117395Skan frame size cannot be changed after that time, so we can safely 12169117395Skan use the cached value. */ 12170117395Skan 12171117395Skan if (reload_completed) 12172117395Skan return cfun->machine->frame_size; 12173117395Skan 12174117395Skan leaf = leaf_function_p (); 12175117395Skan 12176117395Skan /* A leaf function does not need any stack alignment if it has nothing 12177117395Skan on the stack. */ 12178117395Skan if (leaf && base_size == 0) 12179117395Skan { 12180117395Skan cfun->machine->frame_size = 0; 12181117395Skan return 0; 12182117395Skan } 12183117395Skan 12184117395Skan /* We know that SP will be word aligned on entry, and we must 12185117395Skan preserve that condition at any subroutine call. But those are 12186117395Skan the only constraints. */ 12187117395Skan 12188117395Skan /* Space for variadic functions. */ 12189117395Skan if (current_function_pretend_args_size) 12190117395Skan entry_size += current_function_pretend_args_size; 12191117395Skan 12192117395Skan /* Space for pushed lo registers. */ 12193117395Skan for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 12194117395Skan if (THUMB_REG_PUSHED_P (regno)) 12195117395Skan count_regs++; 12196117395Skan 12197117395Skan /* Space for backtrace structure. */ 12198117395Skan if (TARGET_BACKTRACE) 12199117395Skan { 12200117395Skan if (count_regs == 0 && regs_ever_live[LAST_ARG_REGNUM] != 0) 12201117395Skan entry_size += 20; 12202117395Skan else 12203117395Skan entry_size += 16; 12204117395Skan } 12205117395Skan 12206117395Skan if (count_regs || !leaf || thumb_far_jump_used_p (1)) 12207117395Skan count_regs++; /* LR */ 12208117395Skan 12209117395Skan entry_size += count_regs * 4; 12210117395Skan count_regs = 0; 12211117395Skan 12212117395Skan /* Space for pushed hi regs. */ 12213117395Skan for (regno = 8; regno < 13; regno++) 12214117395Skan if (THUMB_REG_PUSHED_P (regno)) 12215117395Skan count_regs++; 12216117395Skan 12217117395Skan entry_size += count_regs * 4; 12218117395Skan 12219117395Skan if ((entry_size + base_size + current_function_outgoing_args_size) & 7) 12220117395Skan base_size += 4; 12221117395Skan if ((entry_size + base_size + current_function_outgoing_args_size) & 7) 12222117395Skan abort (); 12223117395Skan 12224117395Skan cfun->machine->frame_size = base_size; 12225117395Skan 12226117395Skan return base_size; 12227117395Skan} 12228117395Skan 1222990075Sobrien/* Generate the rest of a function's prologue. */ 1223090075Sobrienvoid 12231132718Skanthumb_expand_prologue (void) 1223290075Sobrien{ 12233132718Skan rtx insn, dwarf; 12234132718Skan 12235117395Skan HOST_WIDE_INT amount = (thumb_get_frame_size () 1223690075Sobrien + current_function_outgoing_args_size); 1223790075Sobrien unsigned long func_type; 1223890075Sobrien 1223990075Sobrien func_type = arm_current_func_type (); 1224090075Sobrien 1224190075Sobrien /* Naked functions don't have prologues. */ 1224290075Sobrien if (IS_NAKED (func_type)) 1224390075Sobrien return; 1224490075Sobrien 1224590075Sobrien if (IS_INTERRUPT (func_type)) 1224690075Sobrien { 1224790075Sobrien error ("interrupt Service Routines cannot be coded in Thumb mode"); 1224890075Sobrien return; 1224990075Sobrien } 1225090075Sobrien 1225190075Sobrien if (frame_pointer_needed) 12252132718Skan { 12253132718Skan insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); 12254132718Skan RTX_FRAME_RELATED_P (insn) = 1; 12255132718Skan } 1225690075Sobrien 1225790075Sobrien if (amount) 1225890075Sobrien { 12259132718Skan amount = ROUND_UP_WORD (amount); 1226090075Sobrien 1226190075Sobrien if (amount < 512) 12262132718Skan { 12263132718Skan insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 12264132718Skan GEN_INT (- amount))); 12265132718Skan RTX_FRAME_RELATED_P (insn) = 1; 12266132718Skan } 1226790075Sobrien else 1226890075Sobrien { 1226990075Sobrien int regno; 1227090075Sobrien rtx reg; 1227190075Sobrien 1227290075Sobrien /* The stack decrement is too big for an immediate value in a single 1227390075Sobrien insn. In theory we could issue multiple subtracts, but after 1227490075Sobrien three of them it becomes more space efficient to place the full 1227590075Sobrien value in the constant pool and load into a register. (Also the 1227690075Sobrien ARM debugger really likes to see only one stack decrement per 1227790075Sobrien function). So instead we look for a scratch register into which 1227890075Sobrien we can load the decrement, and then we subtract this from the 1227990075Sobrien stack pointer. Unfortunately on the thumb the only available 1228090075Sobrien scratch registers are the argument registers, and we cannot use 1228190075Sobrien these as they may hold arguments to the function. Instead we 1228290075Sobrien attempt to locate a call preserved register which is used by this 1228390075Sobrien function. If we can find one, then we know that it will have 1228490075Sobrien been pushed at the start of the prologue and so we can corrupt 1228590075Sobrien it now. */ 1228690075Sobrien for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++) 12287117395Skan if (THUMB_REG_PUSHED_P (regno) 1228890075Sobrien && !(frame_pointer_needed 1228990075Sobrien && (regno == THUMB_HARD_FRAME_POINTER_REGNUM))) 1229090075Sobrien break; 1229190075Sobrien 12292117395Skan if (regno > LAST_LO_REGNUM) /* Very unlikely. */ 1229390075Sobrien { 1229490075Sobrien rtx spare = gen_rtx (REG, SImode, IP_REGNUM); 1229590075Sobrien 12296132718Skan /* Choose an arbitrary, non-argument low register. */ 1229790075Sobrien reg = gen_rtx (REG, SImode, LAST_LO_REGNUM); 1229890075Sobrien 1229990075Sobrien /* Save it by copying it into a high, scratch register. */ 1230090075Sobrien emit_insn (gen_movsi (spare, reg)); 1230190075Sobrien /* Add a USE to stop propagate_one_insn() from barfing. */ 1230290075Sobrien emit_insn (gen_prologue_use (spare)); 1230390075Sobrien 1230490075Sobrien /* Decrement the stack. */ 1230590075Sobrien emit_insn (gen_movsi (reg, GEN_INT (- amount))); 12306132718Skan insn = emit_insn (gen_addsi3 (stack_pointer_rtx, 12307132718Skan stack_pointer_rtx, reg)); 12308132718Skan RTX_FRAME_RELATED_P (insn) = 1; 12309132718Skan dwarf = gen_rtx_SET (SImode, stack_pointer_rtx, 12310132718Skan plus_constant (stack_pointer_rtx, 12311132718Skan GEN_INT (- amount))); 12312132718Skan RTX_FRAME_RELATED_P (dwarf) = 1; 12313132718Skan REG_NOTES (insn) 12314132718Skan = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 12315132718Skan REG_NOTES (insn)); 1231690075Sobrien 1231790075Sobrien /* Restore the low register's original value. */ 1231890075Sobrien emit_insn (gen_movsi (reg, spare)); 1231990075Sobrien 1232090075Sobrien /* Emit a USE of the restored scratch register, so that flow 1232190075Sobrien analysis will not consider the restore redundant. The 1232290075Sobrien register won't be used again in this function and isn't 1232390075Sobrien restored by the epilogue. */ 1232490075Sobrien emit_insn (gen_prologue_use (reg)); 1232590075Sobrien } 1232690075Sobrien else 1232790075Sobrien { 1232890075Sobrien reg = gen_rtx (REG, SImode, regno); 1232990075Sobrien 1233090075Sobrien emit_insn (gen_movsi (reg, GEN_INT (- amount))); 12331132718Skan 12332132718Skan insn = emit_insn (gen_addsi3 (stack_pointer_rtx, 12333132718Skan stack_pointer_rtx, reg)); 12334132718Skan RTX_FRAME_RELATED_P (insn) = 1; 12335132718Skan dwarf = gen_rtx_SET (SImode, stack_pointer_rtx, 12336132718Skan plus_constant (stack_pointer_rtx, 12337132718Skan GEN_INT (- amount))); 12338132718Skan RTX_FRAME_RELATED_P (dwarf) = 1; 12339132718Skan REG_NOTES (insn) 12340132718Skan = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf, 12341132718Skan REG_NOTES (insn)); 1234290075Sobrien } 1234390075Sobrien } 1234490075Sobrien } 1234590075Sobrien 1234690075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 1234790075Sobrien emit_insn (gen_blockage ()); 1234890075Sobrien} 1234990075Sobrien 1235090075Sobrienvoid 12351132718Skanthumb_expand_epilogue (void) 1235290075Sobrien{ 12353117395Skan HOST_WIDE_INT amount = (thumb_get_frame_size () 1235490075Sobrien + current_function_outgoing_args_size); 12355132718Skan int regno; 12356132718Skan 1235790075Sobrien /* Naked functions don't have prologues. */ 1235890075Sobrien if (IS_NAKED (arm_current_func_type ())) 1235990075Sobrien return; 1236090075Sobrien 1236190075Sobrien if (frame_pointer_needed) 1236290075Sobrien emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); 1236390075Sobrien else if (amount) 1236490075Sobrien { 12365132718Skan amount = ROUND_UP_WORD (amount); 1236690075Sobrien 1236790075Sobrien if (amount < 512) 1236890075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 1236990075Sobrien GEN_INT (amount))); 1237090075Sobrien else 1237190075Sobrien { 1237290075Sobrien /* r3 is always free in the epilogue. */ 1237390075Sobrien rtx reg = gen_rtx (REG, SImode, LAST_ARG_REGNUM); 1237490075Sobrien 1237590075Sobrien emit_insn (gen_movsi (reg, GEN_INT (amount))); 1237690075Sobrien emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); 1237790075Sobrien } 1237890075Sobrien } 1237990075Sobrien 1238090075Sobrien /* Emit a USE (stack_pointer_rtx), so that 1238190075Sobrien the stack adjustment will not be deleted. */ 1238290075Sobrien emit_insn (gen_prologue_use (stack_pointer_rtx)); 1238390075Sobrien 1238490075Sobrien if (current_function_profile || TARGET_NO_SCHED_PRO) 1238590075Sobrien emit_insn (gen_blockage ()); 12386132718Skan 12387132718Skan /* Emit a clobber for each insn that will be restored in the epilogue, 12388132718Skan so that flow2 will get register lifetimes correct. */ 12389132718Skan for (regno = 0; regno < 13; regno++) 12390132718Skan if (regs_ever_live[regno] && !call_used_regs[regno]) 12391132718Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, regno))); 12392132718Skan 12393132718Skan if (! regs_ever_live[LR_REGNUM]) 12394132718Skan emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, LR_REGNUM))); 1239590075Sobrien} 1239690075Sobrien 1239790075Sobrienstatic void 12398132718Skanthumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) 1239990075Sobrien{ 1240090075Sobrien int live_regs_mask = 0; 1240190075Sobrien int high_regs_pushed = 0; 12402132718Skan int cfa_offset = 0; 1240390075Sobrien int regno; 1240490075Sobrien 1240590075Sobrien if (IS_NAKED (arm_current_func_type ())) 1240690075Sobrien return; 1240790075Sobrien 1240890075Sobrien if (is_called_in_ARM_mode (current_function_decl)) 1240990075Sobrien { 1241090075Sobrien const char * name; 1241190075Sobrien 1241290075Sobrien if (GET_CODE (DECL_RTL (current_function_decl)) != MEM) 1241390075Sobrien abort (); 1241490075Sobrien if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF) 1241590075Sobrien abort (); 1241690075Sobrien name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); 1241790075Sobrien 1241890075Sobrien /* Generate code sequence to switch us into Thumb mode. */ 1241990075Sobrien /* The .code 32 directive has already been emitted by 1242090075Sobrien ASM_DECLARE_FUNCTION_NAME. */ 1242190075Sobrien asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM); 1242290075Sobrien asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM); 1242390075Sobrien 1242490075Sobrien /* Generate a label, so that the debugger will notice the 1242590075Sobrien change in instruction sets. This label is also used by 1242690075Sobrien the assembler to bypass the ARM code when this function 1242790075Sobrien is called from a Thumb encoded function elsewhere in the 1242890075Sobrien same file. Hence the definition of STUB_NAME here must 12429132718Skan agree with the definition in gas/config/tc-arm.c. */ 1243090075Sobrien 1243190075Sobrien#define STUB_NAME ".real_start_of" 1243290075Sobrien 12433117395Skan fprintf (f, "\t.code\t16\n"); 1243490075Sobrien#ifdef ARM_PE 1243590075Sobrien if (arm_dllexport_name_p (name)) 1243690075Sobrien name = arm_strip_name_encoding (name); 1243790075Sobrien#endif 1243890075Sobrien asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name); 12439117395Skan fprintf (f, "\t.thumb_func\n"); 1244090075Sobrien asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name); 1244190075Sobrien } 1244290075Sobrien 1244390075Sobrien if (current_function_pretend_args_size) 1244490075Sobrien { 1244596263Sobrien if (cfun->machine->uses_anonymous_args) 1244690075Sobrien { 1244790075Sobrien int num_pushes; 1244890075Sobrien 12449117395Skan fprintf (f, "\tpush\t{"); 1245090075Sobrien 12451117395Skan num_pushes = ARM_NUM_INTS (current_function_pretend_args_size); 1245290075Sobrien 1245390075Sobrien for (regno = LAST_ARG_REGNUM + 1 - num_pushes; 1245490075Sobrien regno <= LAST_ARG_REGNUM; 1245590075Sobrien regno++) 1245690075Sobrien asm_fprintf (f, "%r%s", regno, 1245790075Sobrien regno == LAST_ARG_REGNUM ? "" : ", "); 1245890075Sobrien 12459117395Skan fprintf (f, "}\n"); 1246090075Sobrien } 1246190075Sobrien else 1246290075Sobrien asm_fprintf (f, "\tsub\t%r, %r, #%d\n", 1246390075Sobrien SP_REGNUM, SP_REGNUM, 1246490075Sobrien current_function_pretend_args_size); 12465132718Skan 12466132718Skan /* We don't need to record the stores for unwinding (would it 12467132718Skan help the debugger any if we did?), but record the change in 12468132718Skan the stack pointer. */ 12469132718Skan if (dwarf2out_do_frame ()) 12470132718Skan { 12471132718Skan char *l = dwarf2out_cfi_label (); 12472132718Skan cfa_offset = cfa_offset + current_function_pretend_args_size; 12473132718Skan dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset); 12474132718Skan } 1247590075Sobrien } 1247690075Sobrien 1247790075Sobrien for (regno = 0; regno <= LAST_LO_REGNUM; regno++) 12478117395Skan if (THUMB_REG_PUSHED_P (regno)) 1247990075Sobrien live_regs_mask |= 1 << regno; 1248090075Sobrien 1248190075Sobrien if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1)) 1248290075Sobrien live_regs_mask |= 1 << LR_REGNUM; 1248390075Sobrien 1248490075Sobrien if (TARGET_BACKTRACE) 1248590075Sobrien { 1248690075Sobrien int offset; 1248790075Sobrien int work_register = 0; 1248890075Sobrien int wr; 1248990075Sobrien 1249090075Sobrien /* We have been asked to create a stack backtrace structure. 1249190075Sobrien The code looks like this: 1249290075Sobrien 1249390075Sobrien 0 .align 2 1249490075Sobrien 0 func: 1249590075Sobrien 0 sub SP, #16 Reserve space for 4 registers. 1249690075Sobrien 2 push {R7} Get a work register. 1249790075Sobrien 4 add R7, SP, #20 Get the stack pointer before the push. 1249890075Sobrien 6 str R7, [SP, #8] Store the stack pointer (before reserving the space). 1249990075Sobrien 8 mov R7, PC Get hold of the start of this code plus 12. 1250090075Sobrien 10 str R7, [SP, #16] Store it. 1250190075Sobrien 12 mov R7, FP Get hold of the current frame pointer. 1250290075Sobrien 14 str R7, [SP, #4] Store it. 1250390075Sobrien 16 mov R7, LR Get hold of the current return address. 1250490075Sobrien 18 str R7, [SP, #12] Store it. 1250590075Sobrien 20 add R7, SP, #16 Point at the start of the backtrace structure. 1250690075Sobrien 22 mov FP, R7 Put this value into the frame pointer. */ 1250790075Sobrien 1250890075Sobrien if ((live_regs_mask & 0xFF) == 0) 1250990075Sobrien { 1251090075Sobrien /* See if the a4 register is free. */ 1251190075Sobrien 1251290075Sobrien if (regs_ever_live [LAST_ARG_REGNUM] == 0) 1251390075Sobrien work_register = LAST_ARG_REGNUM; 12514132718Skan else /* We must push a register of our own. */ 1251590075Sobrien live_regs_mask |= (1 << LAST_LO_REGNUM); 1251690075Sobrien } 1251790075Sobrien 1251890075Sobrien if (work_register == 0) 1251990075Sobrien { 1252090075Sobrien /* Select a register from the list that will be pushed to 1252190075Sobrien use as our work register. */ 1252290075Sobrien for (work_register = (LAST_LO_REGNUM + 1); work_register--;) 1252390075Sobrien if ((1 << work_register) & live_regs_mask) 1252490075Sobrien break; 1252590075Sobrien } 1252690075Sobrien 1252790075Sobrien asm_fprintf 1252890075Sobrien (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n", 1252990075Sobrien SP_REGNUM, SP_REGNUM); 12530132718Skan 12531132718Skan if (dwarf2out_do_frame ()) 12532132718Skan { 12533132718Skan char *l = dwarf2out_cfi_label (); 12534132718Skan cfa_offset = cfa_offset + 16; 12535132718Skan dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset); 12536132718Skan } 12537132718Skan 1253890075Sobrien if (live_regs_mask) 12539132718Skan thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask); 1254090075Sobrien 1254190075Sobrien for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1) 1254290075Sobrien if (wr & live_regs_mask) 1254390075Sobrien offset += 4; 1254490075Sobrien 1254590075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, 1254690075Sobrien offset + 16 + current_function_pretend_args_size); 1254790075Sobrien 1254890075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1254990075Sobrien offset + 4); 1255090075Sobrien 1255190075Sobrien /* Make sure that the instruction fetching the PC is in the right place 1255290075Sobrien to calculate "start of backtrace creation code + 12". */ 1255390075Sobrien if (live_regs_mask) 1255490075Sobrien { 1255590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); 1255690075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1255790075Sobrien offset + 12); 1255890075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, 1255990075Sobrien ARM_HARD_FRAME_POINTER_REGNUM); 1256090075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1256190075Sobrien offset); 1256290075Sobrien } 1256390075Sobrien else 1256490075Sobrien { 1256590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, 1256690075Sobrien ARM_HARD_FRAME_POINTER_REGNUM); 1256790075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1256890075Sobrien offset); 1256990075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM); 1257090075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1257190075Sobrien offset + 12); 1257290075Sobrien } 1257390075Sobrien 1257490075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", work_register, LR_REGNUM); 1257590075Sobrien asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM, 1257690075Sobrien offset + 8); 1257790075Sobrien asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM, 1257890075Sobrien offset + 12); 1257990075Sobrien asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n", 1258090075Sobrien ARM_HARD_FRAME_POINTER_REGNUM, work_register); 1258190075Sobrien } 1258290075Sobrien else if (live_regs_mask) 12583132718Skan thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask); 1258490075Sobrien 1258590075Sobrien for (regno = 8; regno < 13; regno++) 12586117395Skan if (THUMB_REG_PUSHED_P (regno)) 12587117395Skan high_regs_pushed++; 1258890075Sobrien 1258990075Sobrien if (high_regs_pushed) 1259090075Sobrien { 1259190075Sobrien int pushable_regs = 0; 1259290075Sobrien int mask = live_regs_mask & 0xff; 1259390075Sobrien int next_hi_reg; 1259490075Sobrien 1259590075Sobrien for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--) 12596117395Skan if (THUMB_REG_PUSHED_P (next_hi_reg)) 12597117395Skan break; 1259890075Sobrien 1259990075Sobrien pushable_regs = mask; 1260090075Sobrien 1260190075Sobrien if (pushable_regs == 0) 1260290075Sobrien { 1260390075Sobrien /* Desperation time -- this probably will never happen. */ 12604117395Skan if (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM)) 1260590075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM); 1260690075Sobrien mask = 1 << LAST_ARG_REGNUM; 1260790075Sobrien } 1260890075Sobrien 1260990075Sobrien while (high_regs_pushed > 0) 1261090075Sobrien { 12611132718Skan int real_regs_mask = 0; 12612132718Skan 1261390075Sobrien for (regno = LAST_LO_REGNUM; regno >= 0; regno--) 1261490075Sobrien { 1261590075Sobrien if (mask & (1 << regno)) 1261690075Sobrien { 1261790075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg); 1261890075Sobrien 1261990075Sobrien high_regs_pushed--; 12620132718Skan real_regs_mask |= (1 << next_hi_reg); 1262190075Sobrien 1262290075Sobrien if (high_regs_pushed) 12623117395Skan { 12624117395Skan for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM; 12625117395Skan next_hi_reg--) 12626117395Skan if (THUMB_REG_PUSHED_P (next_hi_reg)) 1262790075Sobrien break; 12628117395Skan } 1262990075Sobrien else 1263090075Sobrien { 1263190075Sobrien mask &= ~((1 << regno) - 1); 1263290075Sobrien break; 1263390075Sobrien } 1263490075Sobrien } 1263590075Sobrien } 12636132718Skan 12637132718Skan thumb_pushpop (f, mask, 1, &cfa_offset, real_regs_mask); 1263890075Sobrien } 1263990075Sobrien 1264090075Sobrien if (pushable_regs == 0 12641117395Skan && (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM))) 1264290075Sobrien asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM); 1264390075Sobrien } 1264490075Sobrien} 1264590075Sobrien 1264690075Sobrien/* Handle the case of a double word load into a low register from 1264790075Sobrien a computed memory address. The computed address may involve a 1264890075Sobrien register which is overwritten by the load. */ 1264990075Sobrienconst char * 12650132718Skanthumb_load_double_from_address (rtx *operands) 1265190075Sobrien{ 1265290075Sobrien rtx addr; 1265390075Sobrien rtx base; 1265490075Sobrien rtx offset; 1265590075Sobrien rtx arg1; 1265690075Sobrien rtx arg2; 1265790075Sobrien 1265890075Sobrien if (GET_CODE (operands[0]) != REG) 1265990075Sobrien abort (); 1266090075Sobrien 1266190075Sobrien if (GET_CODE (operands[1]) != MEM) 1266290075Sobrien abort (); 1266390075Sobrien 1266490075Sobrien /* Get the memory address. */ 1266590075Sobrien addr = XEXP (operands[1], 0); 1266690075Sobrien 1266790075Sobrien /* Work out how the memory address is computed. */ 1266890075Sobrien switch (GET_CODE (addr)) 1266990075Sobrien { 1267090075Sobrien case REG: 1267190075Sobrien operands[2] = gen_rtx (MEM, SImode, 1267290075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1267390075Sobrien 1267490075Sobrien if (REGNO (operands[0]) == REGNO (addr)) 1267590075Sobrien { 1267690075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1267790075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1267890075Sobrien } 1267990075Sobrien else 1268090075Sobrien { 1268190075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1268290075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1268390075Sobrien } 1268490075Sobrien break; 1268590075Sobrien 1268690075Sobrien case CONST: 1268790075Sobrien /* Compute <address> + 4 for the high order load. */ 1268890075Sobrien operands[2] = gen_rtx (MEM, SImode, 1268990075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1269090075Sobrien 1269190075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1269290075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1269390075Sobrien break; 1269490075Sobrien 1269590075Sobrien case PLUS: 1269690075Sobrien arg1 = XEXP (addr, 0); 1269790075Sobrien arg2 = XEXP (addr, 1); 1269890075Sobrien 1269990075Sobrien if (CONSTANT_P (arg1)) 1270090075Sobrien base = arg2, offset = arg1; 1270190075Sobrien else 1270290075Sobrien base = arg1, offset = arg2; 1270390075Sobrien 1270490075Sobrien if (GET_CODE (base) != REG) 1270590075Sobrien abort (); 1270690075Sobrien 1270790075Sobrien /* Catch the case of <address> = <reg> + <reg> */ 1270890075Sobrien if (GET_CODE (offset) == REG) 1270990075Sobrien { 1271090075Sobrien int reg_offset = REGNO (offset); 1271190075Sobrien int reg_base = REGNO (base); 1271290075Sobrien int reg_dest = REGNO (operands[0]); 1271390075Sobrien 1271490075Sobrien /* Add the base and offset registers together into the 1271590075Sobrien higher destination register. */ 1271690075Sobrien asm_fprintf (asm_out_file, "\tadd\t%r, %r, %r", 1271790075Sobrien reg_dest + 1, reg_base, reg_offset); 1271890075Sobrien 1271990075Sobrien /* Load the lower destination register from the address in 1272090075Sobrien the higher destination register. */ 1272190075Sobrien asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #0]", 1272290075Sobrien reg_dest, reg_dest + 1); 1272390075Sobrien 1272490075Sobrien /* Load the higher destination register from its own address 1272590075Sobrien plus 4. */ 1272690075Sobrien asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #4]", 1272790075Sobrien reg_dest + 1, reg_dest + 1); 1272890075Sobrien } 1272990075Sobrien else 1273090075Sobrien { 1273190075Sobrien /* Compute <address> + 4 for the high order load. */ 1273290075Sobrien operands[2] = gen_rtx (MEM, SImode, 1273390075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1273490075Sobrien 1273590075Sobrien /* If the computed address is held in the low order register 1273690075Sobrien then load the high order register first, otherwise always 1273790075Sobrien load the low order register first. */ 1273890075Sobrien if (REGNO (operands[0]) == REGNO (base)) 1273990075Sobrien { 1274090075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1274190075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1274290075Sobrien } 1274390075Sobrien else 1274490075Sobrien { 1274590075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1274690075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1274790075Sobrien } 1274890075Sobrien } 1274990075Sobrien break; 1275090075Sobrien 1275190075Sobrien case LABEL_REF: 1275290075Sobrien /* With no registers to worry about we can just load the value 1275390075Sobrien directly. */ 1275490075Sobrien operands[2] = gen_rtx (MEM, SImode, 1275590075Sobrien plus_constant (XEXP (operands[1], 0), 4)); 1275690075Sobrien 1275790075Sobrien output_asm_insn ("ldr\t%H0, %2", operands); 1275890075Sobrien output_asm_insn ("ldr\t%0, %1", operands); 1275990075Sobrien break; 1276090075Sobrien 1276190075Sobrien default: 1276290075Sobrien abort (); 1276390075Sobrien break; 1276490075Sobrien } 1276590075Sobrien 1276690075Sobrien return ""; 1276790075Sobrien} 1276890075Sobrien 1276990075Sobrienconst char * 12770132718Skanthumb_output_move_mem_multiple (int n, rtx *operands) 1277190075Sobrien{ 1277290075Sobrien rtx tmp; 1277390075Sobrien 1277490075Sobrien switch (n) 1277590075Sobrien { 1277690075Sobrien case 2: 1277790075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1277890075Sobrien { 1277990075Sobrien tmp = operands[4]; 1278090075Sobrien operands[4] = operands[5]; 1278190075Sobrien operands[5] = tmp; 1278290075Sobrien } 1278390075Sobrien output_asm_insn ("ldmia\t%1!, {%4, %5}", operands); 1278490075Sobrien output_asm_insn ("stmia\t%0!, {%4, %5}", operands); 1278590075Sobrien break; 1278690075Sobrien 1278790075Sobrien case 3: 1278890075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1278990075Sobrien { 1279090075Sobrien tmp = operands[4]; 1279190075Sobrien operands[4] = operands[5]; 1279290075Sobrien operands[5] = tmp; 1279390075Sobrien } 1279490075Sobrien if (REGNO (operands[5]) > REGNO (operands[6])) 1279590075Sobrien { 1279690075Sobrien tmp = operands[5]; 1279790075Sobrien operands[5] = operands[6]; 1279890075Sobrien operands[6] = tmp; 1279990075Sobrien } 1280090075Sobrien if (REGNO (operands[4]) > REGNO (operands[5])) 1280190075Sobrien { 1280290075Sobrien tmp = operands[4]; 1280390075Sobrien operands[4] = operands[5]; 1280490075Sobrien operands[5] = tmp; 1280590075Sobrien } 1280690075Sobrien 1280790075Sobrien output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands); 1280890075Sobrien output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands); 1280990075Sobrien break; 1281090075Sobrien 1281190075Sobrien default: 1281290075Sobrien abort (); 1281390075Sobrien } 1281490075Sobrien 1281590075Sobrien return ""; 1281690075Sobrien} 1281790075Sobrien 1281890075Sobrien/* Routines for generating rtl. */ 1281990075Sobrienvoid 12820132718Skanthumb_expand_movstrqi (rtx *operands) 1282190075Sobrien{ 1282290075Sobrien rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); 1282390075Sobrien rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); 1282490075Sobrien HOST_WIDE_INT len = INTVAL (operands[2]); 1282590075Sobrien HOST_WIDE_INT offset = 0; 1282690075Sobrien 1282790075Sobrien while (len >= 12) 1282890075Sobrien { 1282990075Sobrien emit_insn (gen_movmem12b (out, in, out, in)); 1283090075Sobrien len -= 12; 1283190075Sobrien } 1283290075Sobrien 1283390075Sobrien if (len >= 8) 1283490075Sobrien { 1283590075Sobrien emit_insn (gen_movmem8b (out, in, out, in)); 1283690075Sobrien len -= 8; 1283790075Sobrien } 1283890075Sobrien 1283990075Sobrien if (len >= 4) 1284090075Sobrien { 1284190075Sobrien rtx reg = gen_reg_rtx (SImode); 1284290075Sobrien emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in))); 1284390075Sobrien emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg)); 1284490075Sobrien len -= 4; 1284590075Sobrien offset += 4; 1284690075Sobrien } 1284790075Sobrien 1284890075Sobrien if (len >= 2) 1284990075Sobrien { 1285090075Sobrien rtx reg = gen_reg_rtx (HImode); 1285190075Sobrien emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, 1285290075Sobrien plus_constant (in, offset)))); 1285390075Sobrien emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)), 1285490075Sobrien reg)); 1285590075Sobrien len -= 2; 1285690075Sobrien offset += 2; 1285790075Sobrien } 1285890075Sobrien 1285990075Sobrien if (len) 1286090075Sobrien { 1286190075Sobrien rtx reg = gen_reg_rtx (QImode); 1286290075Sobrien emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode, 1286390075Sobrien plus_constant (in, offset)))); 1286490075Sobrien emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)), 1286590075Sobrien reg)); 1286690075Sobrien } 1286790075Sobrien} 1286890075Sobrien 1286990075Sobrienint 12870132718Skanthumb_cmp_operand (rtx op, enum machine_mode mode) 1287190075Sobrien{ 1287290075Sobrien return ((GET_CODE (op) == CONST_INT 12873132718Skan && INTVAL (op) < 256 12874132718Skan && INTVAL (op) >= 0) 12875132718Skan || s_register_operand (op, mode)); 1287690075Sobrien} 1287790075Sobrien 12878132718Skanint 12879132718Skanthumb_cmpneg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) 1288090075Sobrien{ 12881132718Skan return (GET_CODE (op) == CONST_INT 12882132718Skan && INTVAL (op) < 0 12883132718Skan && INTVAL (op) > -256); 12884132718Skan} 1288590075Sobrien 12886132718Skan/* Return TRUE if a result can be stored in OP without clobbering the 12887132718Skan condition code register. Prior to reload we only accept a 12888132718Skan register. After reload we have to be able to handle memory as 12889132718Skan well, since a pseudo may not get a hard reg and reload cannot 12890132718Skan handle output-reloads on jump insns. 1289190075Sobrien 12892132718Skan We could possibly handle mem before reload as well, but that might 12893132718Skan complicate things with the need to handle increment 12894132718Skan side-effects. */ 12895132718Skan 12896132718Skanint 12897132718Skanthumb_cbrch_target_operand (rtx op, enum machine_mode mode) 12898132718Skan{ 12899132718Skan return (s_register_operand (op, mode) 12900132718Skan || ((reload_in_progress || reload_completed) 12901132718Skan && memory_operand (op, mode))); 1290290075Sobrien} 1290390075Sobrien 1290490075Sobrien/* Handle storing a half-word to memory during reload. */ 1290590075Sobrienvoid 12906132718Skanthumb_reload_out_hi (rtx *operands) 1290790075Sobrien{ 1290890075Sobrien emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2])); 1290990075Sobrien} 1291090075Sobrien 12911132718Skan/* Handle reading a half-word from memory during reload. */ 1291290075Sobrienvoid 12913132718Skanthumb_reload_in_hi (rtx *operands ATTRIBUTE_UNUSED) 1291490075Sobrien{ 1291590075Sobrien abort (); 1291690075Sobrien} 1291790075Sobrien 1291890075Sobrien/* Return the length of a function name prefix 1291990075Sobrien that starts with the character 'c'. */ 1292090075Sobrienstatic int 12921132718Skanarm_get_strip_length (int c) 1292290075Sobrien{ 1292390075Sobrien switch (c) 1292490075Sobrien { 1292590075Sobrien ARM_NAME_ENCODING_LENGTHS 1292690075Sobrien default: return 0; 1292790075Sobrien } 1292890075Sobrien} 1292990075Sobrien 1293090075Sobrien/* Return a pointer to a function's name with any 1293190075Sobrien and all prefix encodings stripped from it. */ 1293290075Sobrienconst char * 12933132718Skanarm_strip_name_encoding (const char *name) 1293490075Sobrien{ 1293590075Sobrien int skip; 1293690075Sobrien 1293790075Sobrien while ((skip = arm_get_strip_length (* name))) 1293890075Sobrien name += skip; 1293990075Sobrien 1294090075Sobrien return name; 1294190075Sobrien} 1294290075Sobrien 12943117395Skan/* If there is a '*' anywhere in the name's prefix, then 12944117395Skan emit the stripped name verbatim, otherwise prepend an 12945117395Skan underscore if leading underscores are being used. */ 12946117395Skanvoid 12947132718Skanarm_asm_output_labelref (FILE *stream, const char *name) 12948117395Skan{ 12949117395Skan int skip; 12950117395Skan int verbatim = 0; 12951117395Skan 12952117395Skan while ((skip = arm_get_strip_length (* name))) 12953117395Skan { 12954117395Skan verbatim |= (*name == '*'); 12955117395Skan name += skip; 12956117395Skan } 12957117395Skan 12958117395Skan if (verbatim) 12959117395Skan fputs (name, stream); 12960117395Skan else 12961117395Skan asm_fprintf (stream, "%U%s", name); 12962117395Skan} 12963117395Skan 12964117395Skanrtx aof_pic_label; 12965117395Skan 1296690075Sobrien#ifdef AOF_ASSEMBLER 1296790075Sobrien/* Special functions only needed when producing AOF syntax assembler. */ 1296890075Sobrien 1296990075Sobrienstruct pic_chain 1297090075Sobrien{ 1297190075Sobrien struct pic_chain * next; 1297290075Sobrien const char * symname; 1297390075Sobrien}; 1297490075Sobrien 1297590075Sobrienstatic struct pic_chain * aof_pic_chain = NULL; 1297690075Sobrien 1297790075Sobrienrtx 12978132718Skanaof_pic_entry (rtx x) 1297990075Sobrien{ 1298090075Sobrien struct pic_chain ** chainp; 1298190075Sobrien int offset; 1298290075Sobrien 1298390075Sobrien if (aof_pic_label == NULL_RTX) 1298490075Sobrien { 1298590075Sobrien aof_pic_label = gen_rtx_SYMBOL_REF (Pmode, "x$adcons"); 1298690075Sobrien } 1298790075Sobrien 1298890075Sobrien for (offset = 0, chainp = &aof_pic_chain; *chainp; 1298990075Sobrien offset += 4, chainp = &(*chainp)->next) 1299090075Sobrien if ((*chainp)->symname == XSTR (x, 0)) 1299190075Sobrien return plus_constant (aof_pic_label, offset); 1299290075Sobrien 1299390075Sobrien *chainp = (struct pic_chain *) xmalloc (sizeof (struct pic_chain)); 1299490075Sobrien (*chainp)->next = NULL; 1299590075Sobrien (*chainp)->symname = XSTR (x, 0); 1299690075Sobrien return plus_constant (aof_pic_label, offset); 1299790075Sobrien} 1299890075Sobrien 1299990075Sobrienvoid 13000132718Skanaof_dump_pic_table (FILE *f) 1300190075Sobrien{ 1300290075Sobrien struct pic_chain * chain; 1300390075Sobrien 1300490075Sobrien if (aof_pic_chain == NULL) 1300590075Sobrien return; 1300690075Sobrien 1300790075Sobrien asm_fprintf (f, "\tAREA |%r$$adcons|, BASED %r\n", 1300890075Sobrien PIC_OFFSET_TABLE_REGNUM, 1300990075Sobrien PIC_OFFSET_TABLE_REGNUM); 1301090075Sobrien fputs ("|x$adcons|\n", f); 1301190075Sobrien 1301290075Sobrien for (chain = aof_pic_chain; chain; chain = chain->next) 1301390075Sobrien { 1301490075Sobrien fputs ("\tDCD\t", f); 1301590075Sobrien assemble_name (f, chain->symname); 1301690075Sobrien fputs ("\n", f); 1301790075Sobrien } 1301890075Sobrien} 1301990075Sobrien 1302090075Sobrienint arm_text_section_count = 1; 1302190075Sobrien 1302290075Sobrienchar * 13023132718Skanaof_text_section (void ) 1302490075Sobrien{ 1302590075Sobrien static char buf[100]; 1302690075Sobrien sprintf (buf, "\tAREA |C$$code%d|, CODE, READONLY", 1302790075Sobrien arm_text_section_count++); 1302890075Sobrien if (flag_pic) 1302990075Sobrien strcat (buf, ", PIC, REENTRANT"); 1303090075Sobrien return buf; 1303190075Sobrien} 1303290075Sobrien 1303390075Sobrienstatic int arm_data_section_count = 1; 1303490075Sobrien 1303590075Sobrienchar * 13036132718Skanaof_data_section (void) 1303790075Sobrien{ 1303890075Sobrien static char buf[100]; 1303990075Sobrien sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++); 1304090075Sobrien return buf; 1304190075Sobrien} 1304290075Sobrien 1304390075Sobrien/* The AOF assembler is religiously strict about declarations of 1304490075Sobrien imported and exported symbols, so that it is impossible to declare 1304590075Sobrien a function as imported near the beginning of the file, and then to 1304690075Sobrien export it later on. It is, however, possible to delay the decision 1304790075Sobrien until all the functions in the file have been compiled. To get 1304890075Sobrien around this, we maintain a list of the imports and exports, and 1304990075Sobrien delete from it any that are subsequently defined. At the end of 1305090075Sobrien compilation we spit the remainder of the list out before the END 1305190075Sobrien directive. */ 1305290075Sobrien 1305390075Sobrienstruct import 1305490075Sobrien{ 1305590075Sobrien struct import * next; 1305690075Sobrien const char * name; 1305790075Sobrien}; 1305890075Sobrien 1305990075Sobrienstatic struct import * imports_list = NULL; 1306090075Sobrien 1306190075Sobrienvoid 13062132718Skanaof_add_import (const char *name) 1306390075Sobrien{ 1306490075Sobrien struct import * new; 1306590075Sobrien 1306690075Sobrien for (new = imports_list; new; new = new->next) 1306790075Sobrien if (new->name == name) 1306890075Sobrien return; 1306990075Sobrien 1307090075Sobrien new = (struct import *) xmalloc (sizeof (struct import)); 1307190075Sobrien new->next = imports_list; 1307290075Sobrien imports_list = new; 1307390075Sobrien new->name = name; 1307490075Sobrien} 1307590075Sobrien 1307690075Sobrienvoid 13077132718Skanaof_delete_import (const char *name) 1307890075Sobrien{ 1307990075Sobrien struct import ** old; 1308090075Sobrien 1308190075Sobrien for (old = &imports_list; *old; old = & (*old)->next) 1308290075Sobrien { 1308390075Sobrien if ((*old)->name == name) 1308490075Sobrien { 1308590075Sobrien *old = (*old)->next; 1308690075Sobrien return; 1308790075Sobrien } 1308890075Sobrien } 1308990075Sobrien} 1309090075Sobrien 1309190075Sobrienint arm_main_function = 0; 1309290075Sobrien 13093132718Skanstatic void 13094132718Skanaof_dump_imports (FILE *f) 1309590075Sobrien{ 1309690075Sobrien /* The AOF assembler needs this to cause the startup code to be extracted 1309790075Sobrien from the library. Brining in __main causes the whole thing to work 1309890075Sobrien automagically. */ 1309990075Sobrien if (arm_main_function) 1310090075Sobrien { 1310190075Sobrien text_section (); 1310290075Sobrien fputs ("\tIMPORT __main\n", f); 1310390075Sobrien fputs ("\tDCD __main\n", f); 1310490075Sobrien } 1310590075Sobrien 1310690075Sobrien /* Now dump the remaining imports. */ 1310790075Sobrien while (imports_list) 1310890075Sobrien { 1310990075Sobrien fprintf (f, "\tIMPORT\t"); 1311090075Sobrien assemble_name (f, imports_list->name); 1311190075Sobrien fputc ('\n', f); 1311290075Sobrien imports_list = imports_list->next; 1311390075Sobrien } 1311490075Sobrien} 13115117395Skan 13116117395Skanstatic void 13117132718Skanaof_globalize_label (FILE *stream, const char *name) 13118117395Skan{ 13119117395Skan default_globalize_label (stream, name); 13120117395Skan if (! strcmp (name, "main")) 13121117395Skan arm_main_function = 1; 13122117395Skan} 13123132718Skan 13124132718Skanstatic void 13125132718Skanaof_file_start (void) 13126132718Skan{ 13127132718Skan fputs ("__r0\tRN\t0\n", asm_out_file); 13128132718Skan fputs ("__a1\tRN\t0\n", asm_out_file); 13129132718Skan fputs ("__a2\tRN\t1\n", asm_out_file); 13130132718Skan fputs ("__a3\tRN\t2\n", asm_out_file); 13131132718Skan fputs ("__a4\tRN\t3\n", asm_out_file); 13132132718Skan fputs ("__v1\tRN\t4\n", asm_out_file); 13133132718Skan fputs ("__v2\tRN\t5\n", asm_out_file); 13134132718Skan fputs ("__v3\tRN\t6\n", asm_out_file); 13135132718Skan fputs ("__v4\tRN\t7\n", asm_out_file); 13136132718Skan fputs ("__v5\tRN\t8\n", asm_out_file); 13137132718Skan fputs ("__v6\tRN\t9\n", asm_out_file); 13138132718Skan fputs ("__sl\tRN\t10\n", asm_out_file); 13139132718Skan fputs ("__fp\tRN\t11\n", asm_out_file); 13140132718Skan fputs ("__ip\tRN\t12\n", asm_out_file); 13141132718Skan fputs ("__sp\tRN\t13\n", asm_out_file); 13142132718Skan fputs ("__lr\tRN\t14\n", asm_out_file); 13143132718Skan fputs ("__pc\tRN\t15\n", asm_out_file); 13144132718Skan fputs ("__f0\tFN\t0\n", asm_out_file); 13145132718Skan fputs ("__f1\tFN\t1\n", asm_out_file); 13146132718Skan fputs ("__f2\tFN\t2\n", asm_out_file); 13147132718Skan fputs ("__f3\tFN\t3\n", asm_out_file); 13148132718Skan fputs ("__f4\tFN\t4\n", asm_out_file); 13149132718Skan fputs ("__f5\tFN\t5\n", asm_out_file); 13150132718Skan fputs ("__f6\tFN\t6\n", asm_out_file); 13151132718Skan fputs ("__f7\tFN\t7\n", asm_out_file); 13152132718Skan text_section (); 13153132718Skan} 13154132718Skan 13155132718Skanstatic void 13156132718Skanaof_file_end (void) 13157132718Skan{ 13158132718Skan if (flag_pic) 13159132718Skan aof_dump_pic_table (asm_out_file); 13160132718Skan aof_dump_imports (asm_out_file); 13161132718Skan fputs ("\tEND\n", asm_out_file); 13162132718Skan} 1316390075Sobrien#endif /* AOF_ASSEMBLER */ 1316490075Sobrien 1316590075Sobrien#ifdef OBJECT_FORMAT_ELF 1316690075Sobrien/* Switch to an arbitrary section NAME with attributes as specified 1316790075Sobrien by FLAGS. ALIGN specifies any known alignment requirements for 1316890075Sobrien the section; 0 if the default should be used. 1316990075Sobrien 1317090075Sobrien Differs from the default elf version only in the prefix character 1317190075Sobrien used before the section type. */ 1317290075Sobrien 1317390075Sobrienstatic void 13174132718Skanarm_elf_asm_named_section (const char *name, unsigned int flags) 1317590075Sobrien{ 13176117395Skan char flagchars[10], *f = flagchars; 1317790075Sobrien 13178117395Skan if (! named_section_first_declaration (name)) 13179117395Skan { 13180117395Skan fprintf (asm_out_file, "\t.section\t%s\n", name); 13181117395Skan return; 13182117395Skan } 13183117395Skan 1318490075Sobrien if (!(flags & SECTION_DEBUG)) 1318590075Sobrien *f++ = 'a'; 1318690075Sobrien if (flags & SECTION_WRITE) 1318790075Sobrien *f++ = 'w'; 1318890075Sobrien if (flags & SECTION_CODE) 1318990075Sobrien *f++ = 'x'; 1319090075Sobrien if (flags & SECTION_SMALL) 1319190075Sobrien *f++ = 's'; 1319290075Sobrien if (flags & SECTION_MERGE) 1319390075Sobrien *f++ = 'M'; 1319490075Sobrien if (flags & SECTION_STRINGS) 1319590075Sobrien *f++ = 'S'; 13196117395Skan if (flags & SECTION_TLS) 13197117395Skan *f++ = 'T'; 1319890075Sobrien *f = '\0'; 1319990075Sobrien 13200117395Skan fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars); 1320190075Sobrien 13202117395Skan if (!(flags & SECTION_NOTYPE)) 13203117395Skan { 13204117395Skan const char *type; 13205117395Skan 13206117395Skan if (flags & SECTION_BSS) 13207117395Skan type = "nobits"; 13208117395Skan else 13209117395Skan type = "progbits"; 13210117395Skan 13211117395Skan fprintf (asm_out_file, ",%%%s", type); 13212117395Skan 13213117395Skan if (flags & SECTION_ENTSIZE) 13214117395Skan fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE); 13215117395Skan } 13216117395Skan 13217117395Skan putc ('\n', asm_out_file); 1321890075Sobrien} 1321990075Sobrien#endif 13220117395Skan 13221117395Skan#ifndef ARM_PE 13222117395Skan/* Symbols in the text segment can be accessed without indirecting via the 13223117395Skan constant pool; it may take an extra binary operation, but this is still 13224117395Skan faster than indirecting via memory. Don't do this when not optimizing, 13225117395Skan since we won't be calculating al of the offsets necessary to do this 13226117395Skan simplification. */ 13227117395Skan 13228117395Skanstatic void 13229132718Skanarm_encode_section_info (tree decl, rtx rtl, int first) 13230117395Skan{ 13231117395Skan /* This doesn't work with AOF syntax, since the string table may be in 13232117395Skan a different AREA. */ 13233117395Skan#ifndef AOF_ASSEMBLER 13234117395Skan if (optimize > 0 && TREE_CONSTANT (decl) 13235117395Skan && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST)) 13236132718Skan SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1; 13237117395Skan#endif 13238117395Skan 13239117395Skan /* If we are referencing a function that is weak then encode a long call 13240117395Skan flag in the function name, otherwise if the function is static or 13241117395Skan or known to be defined in this file then encode a short call flag. */ 13242117395Skan if (first && TREE_CODE_CLASS (TREE_CODE (decl)) == 'd') 13243117395Skan { 13244117395Skan if (TREE_CODE (decl) == FUNCTION_DECL && DECL_WEAK (decl)) 13245117395Skan arm_encode_call_attribute (decl, LONG_CALL_FLAG_CHAR); 13246117395Skan else if (! TREE_PUBLIC (decl)) 13247117395Skan arm_encode_call_attribute (decl, SHORT_CALL_FLAG_CHAR); 13248117395Skan } 13249117395Skan} 13250117395Skan#endif /* !ARM_PE */ 13251117395Skan 13252132718Skanstatic void 13253132718Skanarm_internal_label (FILE *stream, const char *prefix, unsigned long labelno) 13254132718Skan{ 13255132718Skan if (arm_ccfsm_state == 3 && (unsigned) arm_target_label == labelno 13256132718Skan && !strcmp (prefix, "L")) 13257132718Skan { 13258132718Skan arm_ccfsm_state = 0; 13259132718Skan arm_target_insn = NULL; 13260132718Skan } 13261132718Skan default_internal_label (stream, prefix, labelno); 13262132718Skan} 13263132718Skan 13264117395Skan/* Output code to add DELTA to the first argument, and then jump 13265117395Skan to FUNCTION. Used for C++ multiple inheritance. */ 13266117395Skanstatic void 13267132718Skanarm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, 13268132718Skan HOST_WIDE_INT delta, 13269132718Skan HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, 13270132718Skan tree function) 13271117395Skan{ 13272132718Skan static int thunk_label = 0; 13273132718Skan char label[256]; 13274117395Skan int mi_delta = delta; 13275117395Skan const char *const mi_op = mi_delta < 0 ? "sub" : "add"; 13276117395Skan int shift = 0; 13277132718Skan int this_regno = (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function) 13278117395Skan ? 1 : 0); 13279117395Skan if (mi_delta < 0) 13280117395Skan mi_delta = - mi_delta; 13281132718Skan if (TARGET_THUMB) 13282132718Skan { 13283132718Skan int labelno = thunk_label++; 13284132718Skan ASM_GENERATE_INTERNAL_LABEL (label, "LTHUMBFUNC", labelno); 13285132718Skan fputs ("\tldr\tr12, ", file); 13286132718Skan assemble_name (file, label); 13287132718Skan fputc ('\n', file); 13288132718Skan } 13289117395Skan while (mi_delta != 0) 13290117395Skan { 13291117395Skan if ((mi_delta & (3 << shift)) == 0) 13292117395Skan shift += 2; 13293117395Skan else 13294117395Skan { 13295117395Skan asm_fprintf (file, "\t%s\t%r, %r, #%d\n", 13296117395Skan mi_op, this_regno, this_regno, 13297117395Skan mi_delta & (0xff << shift)); 13298117395Skan mi_delta &= ~(0xff << shift); 13299117395Skan shift += 8; 13300117395Skan } 13301117395Skan } 13302132718Skan if (TARGET_THUMB) 13303132718Skan { 13304132718Skan fprintf (file, "\tbx\tr12\n"); 13305132718Skan ASM_OUTPUT_ALIGN (file, 2); 13306132718Skan assemble_name (file, label); 13307132718Skan fputs (":\n", file); 13308132718Skan assemble_integer (XEXP (DECL_RTL (function), 0), 4, BITS_PER_WORD, 1); 13309132718Skan } 13310132718Skan else 13311132718Skan { 13312132718Skan fputs ("\tb\t", file); 13313132718Skan assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); 13314132718Skan if (NEED_PLT_RELOC) 13315132718Skan fputs ("(PLT)", file); 13316132718Skan fputc ('\n', file); 13317132718Skan } 13318117395Skan} 13319117395Skan 13320132718Skanint 13321132718Skanarm_emit_vector_const (FILE *file, rtx x) 13322132718Skan{ 13323132718Skan int i; 13324132718Skan const char * pattern; 13325132718Skan 13326132718Skan if (GET_CODE (x) != CONST_VECTOR) 13327132718Skan abort (); 13328132718Skan 13329132718Skan switch (GET_MODE (x)) 13330132718Skan { 13331132718Skan case V2SImode: pattern = "%08x"; break; 13332132718Skan case V4HImode: pattern = "%04x"; break; 13333132718Skan case V8QImode: pattern = "%02x"; break; 13334132718Skan default: abort (); 13335132718Skan } 13336132718Skan 13337132718Skan fprintf (file, "0x"); 13338132718Skan for (i = CONST_VECTOR_NUNITS (x); i--;) 13339132718Skan { 13340132718Skan rtx element; 13341132718Skan 13342132718Skan element = CONST_VECTOR_ELT (x, i); 13343132718Skan fprintf (file, pattern, INTVAL (element)); 13344132718Skan } 13345132718Skan 13346132718Skan return 1; 13347132718Skan} 13348132718Skan 13349132718Skanconst char * 13350132718Skanarm_output_load_gr (rtx *operands) 13351132718Skan{ 13352132718Skan rtx reg; 13353132718Skan rtx offset; 13354132718Skan rtx wcgr; 13355132718Skan rtx sum; 13356132718Skan 13357132718Skan if (GET_CODE (operands [1]) != MEM 13358132718Skan || GET_CODE (sum = XEXP (operands [1], 0)) != PLUS 13359132718Skan || GET_CODE (reg = XEXP (sum, 0)) != REG 13360132718Skan || GET_CODE (offset = XEXP (sum, 1)) != CONST_INT 13361132718Skan || ((INTVAL (offset) < 1024) && (INTVAL (offset) > -1024))) 13362132718Skan return "wldrw%?\t%0, %1"; 13363132718Skan 13364132718Skan /* Fix up an out-of-range load of a GR register. */ 13365132718Skan output_asm_insn ("str%?\t%0, [sp, #-4]!\t@ Start of GR load expansion", & reg); 13366132718Skan wcgr = operands[0]; 13367132718Skan operands[0] = reg; 13368132718Skan output_asm_insn ("ldr%?\t%0, %1", operands); 13369132718Skan 13370132718Skan operands[0] = wcgr; 13371132718Skan operands[1] = reg; 13372132718Skan output_asm_insn ("tmcr%?\t%0, %1", operands); 13373132718Skan output_asm_insn ("ldr%?\t%0, [sp], #4\t@ End of GR load expansion", & reg); 13374132718Skan 13375132718Skan return ""; 13376132718Skan} 13377