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