Commit d5b7b3ae by Richard Earnshaw Committed by Richard Earnshaw

Merge changes from merged-arm-thumb-backend-branch onto trunk.

From-SVN: r33028
parent 002e89d3
2000-04-08 Richard Earnshaw (rearnsha@arm.com)
Merge changes from merged-arm-thumb-backend-branch onto trunk.
2000-04-08 Richard Earnshaw (rearnsha@arm.com)
* Merge trunk code from tag merged-arm-thumb-backend-merge_20000408
into branch.
* arm.md (nop): Use the standard RTL expression. Don't code as a
define_expand.
(*arm_nop, *thumb_nop): Delete.
* arm.c (const_ok_for_arm): Don't use ANSI extensions for defining
constants. Use casts instead.
(arm_gen_constant, arm_rtx_costs, arm_reload_in_hi, arm_reload_out_hi,
output_multi_immediate, arm_poke_function_name): Likewise.
* arm.h (ARM_LEGITIIMIZE_RELOAD_ADDRESS, ARM_SIGN_EXTEND,
MASK_RETURN_ADDDR): Likewise.
2000-03-31 Richard Earnshaw (rearnsha@arm.com)
* Merge trunk code from tag merged-arm-thumb-backend-merge_20000325
into branch.
* arm.md (eh_epilogue): New function.
* arm.h (struct machine_function): Move to here ...
* arm.c: ... from here.
(arm_output_epilogue): Support epilogues for __builtin_eh_return.
(thumb_exit): Extra parameter eh_ofs. All callers changed.
Handle epilogues for __builtin_eh_return. Make bit-fields unsigned.
2000-03-30 Nick Clifton <nickc@cygnus.com>
* config/arm/semi.h (ASM_SPEC): Restore definition.
(SUBTARGET_EXTRA_ASM_SPEC): Define if not already defined.
* config/arm/elf.h (ASM_SPEC): Restore definition.
(SUBTARGET_EXTRA_ASM_SPEC): Define if not already defined.
* config/arm/arm.h (ASM_SPEC): Remove definition.
(SUBTARGET_EXTRA_ASM_SPEC): Remove definition.
2000-03-26 Bernd Schmidt <bernds@cygnus.co.uk>
* config/arm/arm.c: Disable -fschedule-insns for Thumb.
2000-03-24 Nick Clifton <nickc@cygnus.com>
Various formating tidyups, elimination of compile time
warnings and synchronisation with internal sources:
* config/arm/arm-protos.h (assemble_align): Add prototype.
(gen_rotated_half_load): Rename to arm_gen_rotated_half_load.
(gen_comapre_reg): Rename to arm_gen_compare_reg.
(arm_return_addr_rtx): Rename to arm_return_addr.
* config/arm/arm.c: Include except.h.
Define shorter typenames for strict minipool_node and struct
minipool_fix.
(arm_return_in_memory): For WinCE return all structures <= 32 bits
in memory.
(gen_rotated_half_load): Rename to arm_gen_rotated_half_load.
(gen_comapre_reg): Rename to arm_gen_compare_reg.
* config/arm/arm.h (SUBTARGET_EXTRA_ASM_SPEC): Define if not
already defined.
(ASM_SPEC): Define if not already defined.
(ASM_OUTPUT_DEF_FROM_DECLS): Define.
* config/arm/arm.md: Rename references to gen_rotated_half_load to
arm_gen_rotated_half_load, and references to gen_comapre_reg to
arm_gen_compare_reg.
(indirect_jump): Only accept register operands.
(load_indirect_jump): Keep this pattern since combine can generate
it.
* config/arm/coff.h: Include aout.h.
(MULTILIB_DEFAULTS): Only define if not already defined.
* config/arm/elf.h (ASM_OUTPUT_DEF_FROM_DECLS): Move definition
into arm.h (so that COFF ports can support thumb based aliases).
(ASM_SPEC): Move definition into arm.h
* config/arm/linux-elf.h (SUBTARGET_EXTRA_ASM_SPEC): Remove
redundant apcs spec.
(ASM_SPEC): Move definition to arm.h
* config/arm/semi.h (ASM_SPEC): Move definition to arm.h
* config/arm/unknown-elf.h (ASM_OUTPUT_SECTION_NAME): Add support
for entries in the .bss section
2000-03-23 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.h (THUMB_INITIAL_ELIMINATION_OFFSET): Pass 0
to thumb_far_jump_used_p.
* config/arm/arm-protos.h (thumb_far_jump_used_p): Take a
single integer parameter.
* config/arm/arm.c (struct machine_function): Add two new
fields, 'far_jump_used' and 'arg_pointer_live'.
(thumb_far_jump_used_p): Once the decision has been made that
far jumps might be used, always return true.
If being called from the initial elimination offset macro then
do not bother to perform the test if the arg pointer is not
being used.
(thumb_unexpand_epilogue): Pass 1 to thumb_far_jump_used_p().
(output_thumb_prologue): Pass 1 to thumb_far_jump_used_p().
2000-03-23 Richard Earnshaw (rearnsha@arm.com)
* arm.c (output_return_instruction): Handle more cases where we can
return from a function with an ldr instruction.
(arm_output_epilogue): Likewise.
* arm.c (thumb_expand_prologue): Don't clobber the frame pointer
if we need to push a large stack frame and there are no callee-saved
registers.
* arm.c (arm_debugger_arg_offset): An offset of 0 is also valid
in ARM code if the frame pointer has been eliminated.
* arm.md (epilogue, *epilogue_insns, consttable_*, align_4): Renumber
unspec_volatile arguments to avoid duplicates.
(consttable_1, consttable_2): Fixes for big-endian mode.
* arm.md (all ARM-mode load insns): Add neg_pool_range attribute
as appropriate.
Re-write constant pool code.
* arm.c (minipool_node, minipool_fix): New types.
(minifix): Delete type.
(arm_add_minipool_constant): Delete.
(arm_compute_minipool_offsets, arm_find_barrier,
arm_find_minipool_constant, fixup_compare, sort_fixups): Likewise.
(get_jump_table_size): Now returns HOST_WIDE_INT.
(move_minipool_fix_forward_ref, add_minipool_forward_ref): New
functions.
(move_minipool_fix_backward_ref, add_minipool_backward_ref): Likewise.
(assign_minipool_offsets, arm_print_value): Likewise.
(dump_minipool): Rewrite.
(arm_barrier_cost): New function.
(create_fix_barrier): New function.
(push_minipool_barrier): New function.
(push_minipool_fix): Record additional information about the fixup
required.
(note_invalid_constants): Remove push for (UNSPEC 3). Don't
check the mode of what needs fixing.
(arm_reorg): Rewrite.
2000-03-08 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.md (indirect_jump): Force constant addresses into
a register.
2000-03-01 Bernd Schmidt <bernds@cygnus.co.uk>
* config/arm/arm.md: Add splitter to turn SF moves into SI moves.
2000-02-24 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (thumb_expand_prologue): Make sure that stack
adjust is word aligned.
(thumb_expand_epilogue): Make sure that stack adjust is word
aligned.
* config/arm/elf.h (ASM_FILE_START): Fix type of version string.
2000-02-24 Bernd Schmidt <bernds@cygnus.com>
* config/arm/arm.h (THUMB_GO_IF_LEGITIMATE_ADDRESS): Allow frame
pointer relative addresses.
2000-02-10 Richard Earnshaw <rearnsha@arm.com>
* arm.c (find_barrier): Find the last barrier within the allowed
range.
2000-02-09 Nick Clifton <nickc@cygnus.com>
* config/arm/arm-protos.h: Replace PROTO with PARAMS.
2000-02-09 Nick Clifton <nickc@cygnus.com>
* config/arm/linux-elf.h (FUNCTION_PROFILER): Rename to
ARM_FUNCTION_PROFILER.
* config/arm/netbsd.h (FUNCTION_PROFILER): Rename to
ARM_FUNCTION_PROFILER.
* config/arm/linux-oldld.h: Imported from mainline sources.
2000-02-07 Nick Clifton <nickc@cygnus.com>
* config/arm/lib1funcs.asm: Merge in thumb functions from
lib1thumb.asm.
* config/arm/lib1thumb.asm: Delete.
2000-02-04 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c: Sychronised with current Red hat local
sources.
* config/arm/arm.h: Ditto.
* config/arm/arm-protos.h: Ditto.
2000-02-02 Bernd Schmidt <bernds@cygnus.co.uk>
* arm.c (soft_df_operand): Reject SUBREGs containing a constant.
* arm.c (arm_gen_movstrqi): Generate halfword stores rather than two
byte stores.
2000-02-02 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.c (thumb_exit): Additional fix for same bug.
* config/arm/arm.md (epilogue): Fix rtl checking abort because
(return) was emited using an emit_insn() call.
2000-01-31 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (output_thumb_prologue): Fix bug generating
thumb stack frame.
(thumb_exit): Move frame pointer back into hard frame pointer
register if backtracing is being used.
2000-01-13 Richard Earnshaw (rearnsha@arm.com)
* Merge trunk code from tag merged-arm-thumb-backend-merge_20000113
into branch.
1999-12-15 Bernd Schmidt <bernds@cygnus.co.uk>
* config/arm/arm.md (neg_pool_offset): Provide default for new
attribute.
(arm_movsi_insn): Add neg_pool_offset attribute.
* config/arm/arm.c (arm_find_barrier): Replace arg FIX with an
rtx for the insn that starts the scan and an unsigned long for its
address. Add MIN_OFFSET and PINSERTED args. All callers changed.
Change scan to ignore insns before MIN_OFFSET. Store size of inserted
instructions in *PINSERTED.
(struct minipool_fixup): Add MIN_ADDRESS elt.
(sort_fixups): Compute it.
(arm_reorg): Changes to support inserting pools before the insn to be
fixed up.
1999-12-08 Bernd Schmidt <bernds@cygnus.co.uk>
* config/arm/elf.h (MULTILIB_DEFAULTS): Add "marm".
* config/arm/linux-elf.h (MULTILIB_DEFAULTS): Add "marm".
* config/arm/coff.h (MULTILIB_DEFAULTS): Add "marm".
Wed Nov 3 10:04:07 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/semi.h (TARGET_VERSION): Do not define if already
defined.
(TARGET_DEFAULT): Do not define if already defined.
Tue Nov 2 10:37:25 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.md (anddi3): Allow disjoint source operands.
(iordi3): Allow disjoint source operands.
(xordi3): Allow disjoint source operands.
(negdi2): Permit construction for ARM and Thumb.
(arm_negdi2): Renamed version of old negdi2 pattern.
(thumb_negdi2): New pattern: Only permit non-overlapping
source and destination.
Fri Oct 29 18:52:38 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (thumb_expand_prologue): Emit a USE of the
scratch low register so that it will not be deleted.
Fri Oct 29 15:23:48 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/pe.h (ARM_PE): Define.
* config/arm/arm.c (arm_valid_machine_decl_attribute): Allow
interfacearm attribute if this is a PE toolchain.
(output_return_instruction): Do not emit anything if the function
has the naked attribute set.
(is_called_in_ARM_mode): If the function has the interfacearm
attribute then return true.
(thumb_expand_prologue): Do not generate a prologue for naked
functions.
(thumb_expand_epilogue): Do not generate an epilogie for a naked
function.
(output_thumb_prologue): Do not bother if the function is naked.
Strip PE encoding from function name before emitting.
Thu Oct 28 11:05:13 1999 Richard Earnshaw <rearnsha@arm.com>
* arm.md (pic_load_addr): Fix constraints.
* arm.md (casesi_insn): Fix mis-applied patch.
* arm.md ("core" function unit): Add rules for single- and
multi-cycle insns.
(All TARGET_THUMB patterns): Add "type" attribute information
where needed.
* arm.c (arm_adjust_cost): Reduce the cost of a data dependency if
the following insn is a CALL.
* arm.c (thumb_expand_epilogue): Add a use of the adjusted stack
pointer.
* arm.h (CONDITIONAL_REGISTER_USAGE): Nail down the
ARM_HARD_FRAME_POINTER_REGNUM when TARGET_APCS_FRAME.
(FRAME_POINTER_REQUIRED): Only check TARGET_APCS_FRAME if TARGET_ARM.
Wed Oct 27 14:40:48 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/linux-gas.h: oops - this file was missed out when the
branch was created...
Tue Oct 26 17:07:38 1999 Richard Earnshaw <rearnsha@arm.com>
* thumb.c: Deleted. Move contents to ...
* arm.c: ... here.
* t-arm-aout, t-arm-coff, t-arm-elf, t-linux, t-netbsd, t-semi: Remove
rule for thumb.o
* configure.in (arm*-*-*): Remove thumb.o from extra_objs list.
* configure: Regen.
* arm-protos.h: Use RTX_CODE and tidy up long lines. Don't
declare a prototype for asm_output_align();
* arm.c (thumb_condition_code): Delete.
(arm_print_operand): Always use arm_condition_code array.
* arm.c (thumb_return_addr_rtx): Delete.
(arm_save_machine_status, arm_restore_machine_status): Delete.
(arm_init_machine_status, arm_mark_machine_status): New functions.
(arm_init_expanders): Update accordingly.
(arm_return_addr): Renamed from arm_return_addr_rtx. Rewrite.
(thumb_expand_prologue): Delete code referencing thumb_return_addr_rtx.
* arm.h (RETURN_ADDR_RTX): Call arm_return_addr().
Tue Oct 26 13:24:16 1999 Nick Clifton <nickc@cygnus.com>
* configure.in : Fix arm-*-aout target to use new t-arm-aout
script.
* config/arm/t-arm-aout: New file: Makefile script for arm-*-aout
targets.
Tue Oct 26 11:27:12 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.c (output_thumb_prologue): Fix bug creating
stack backtrace structure.
* config/arm/arm-protos.h: Add prototypes for functions defined in
arm/pe.h.
* config/arm/arm.c (arm_override_options): Fix selection of
attributes of default processor.
* config/arm/coff.h: Delete needless #include.
* config/arm/pe.c: Delete unused code.
* config/arm/pe.h (CPP_DEFINES): Delete
(SUBTARGET_CPP_SPEC): Define.
(ASM_DECLARE_FUNCTION_NAME): Use ARM_DECLARE_FUNCTION_NAME.
* config/arm/t-pe: Add -DInhibit_libc to allof libgcc.a to build.
Mon Oct 25 15:42:09 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.h (TARGET_APCS_FRAME): Renamed from TARGET_APCS -- all uses
updated.
(CAN_DEBUG_WITHOUT_FP): Define.
(FIXED_REGISTERS): Make r11 call-saved.
(CALL_USED_REGISTERS): Likewise.
(CONDITIONAL_REGISTER_USAGE): Fix r11 if TARGET_APCS_FRAME.
(FRAME_POINTER_REQUIRED): Correct logic for determining when a
frame-pointer is required.
(TARGET_DEFAULT): Make default setting include ARM_FLAG_APCS_FRAME.
* arm.c (arm_override_options): Warn about -mno-apcs-frame and -g
if the target normally needs a stack frame in non-leaf functions.
(use_return_insn): Correct logic for determining when a return
instruction can be used.
(output_return_instruction): Handle the frame-pointer register as
a normal register when not TARGET_APCS_FRAME.
(arm_output_prologue): Likewise.
(arm_output_epilogue): Likewise.
(output_func_epilogue): Likewise.
(arm_expand_prologue): Likewise.
* netbsd.h semi.h (TARGET_DEFAULT): Add ARM_FLAG_APCS_FRAME.
* arm.c (use_return_insn): No need to check floating point regs if
TARGET_SOFT_FLOAT.
(arm_find_minipool_constant): Correct typo; use GET_CODE to get
the code of value stored in the minipool array.
(arm_add_minipool_constant): Likewise.
2000-04-08 Kaveh R. Ghazi <ghazi@caip.rutgers.edu> 2000-04-08 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* basic-block.h (conflict_graph_enum_fn): K&R fix. * basic-block.h (conflict_graph_enum_fn): K&R fix.
......
/* Definitions of target machine for GNU compiler, for ARM with a.out /* Definitions of target machine for GNU compiler, for ARM with a.out
Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. Copyright (C) 1995 - 1999 Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rearnsha@armltd.co.uk). Contributed by Richard Earnshaw (rearnsha@armltd.co.uk).
This file is part of GNU CC. This file is part of GNU CC.
...@@ -27,18 +27,22 @@ Boston, MA 02111-1307, USA. */ ...@@ -27,18 +27,22 @@ Boston, MA 02111-1307, USA. */
#ifndef ASM_FILE_START #ifndef ASM_FILE_START
#define ASM_FILE_START(STREAM) \ #define ASM_FILE_START(STREAM) \
{ \ { \
fprintf (STREAM,"%srfp\t.req\t%sr9\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rrfp\t.req\t%Rr9\n"); \
fprintf (STREAM,"%ssl\t.req\t%sr10\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rsl\t.req\t%Rr10\n"); \
fprintf (STREAM,"%sfp\t.req\t%sr11\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rfp\t.req\t%Rr11\n"); \
fprintf (STREAM,"%sip\t.req\t%sr12\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rip\t.req\t%Rr12\n"); \
fprintf (STREAM,"%ssp\t.req\t%sr13\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rsp\t.req\t%Rr13\n"); \
fprintf (STREAM,"%slr\t.req\t%sr14\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rlr\t.req\t%Rr14\n"); \
fprintf (STREAM,"%spc\t.req\t%sr15\n", REGISTER_PREFIX, REGISTER_PREFIX); \ asm_fprintf (STREAM,"%Rpc\t.req\t%Rr15\n"); \
} }
#endif #endif
#define ASM_APP_ON " " #ifndef ASM_APP_ON
#define ASM_APP_OFF " " #define ASM_APP_ON ""
#endif
#ifndef ASM_APP_OFF
#define ASM_APP_OFF ""
#endif
/* Switch to the text or data segment. */ /* Switch to the text or data segment. */
#define TEXT_SECTION_ASM_OP ".text" #define TEXT_SECTION_ASM_OP ".text"
...@@ -192,8 +196,9 @@ Boston, MA 02111-1307, USA. */ ...@@ -192,8 +196,9 @@ Boston, MA 02111-1307, USA. */
long l[3]; \ long l[3]; \
REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \ REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \ REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.long 0x%lx,0x%lx,0x%lx\t%s long double %s\n", \ asm_fprintf (STREAM, \
l[0], l[1], l[2], ASM_COMMENT_START, dstr); \ "\t.long 0x%lx,0x%lx,0x%lx\t%@ long double %s\n", \
l[0], l[1], l[2], dstr); \
} \ } \
while (0) while (0)
...@@ -204,8 +209,8 @@ Boston, MA 02111-1307, USA. */ ...@@ -204,8 +209,8 @@ Boston, MA 02111-1307, USA. */
long l[2]; \ long l[2]; \
REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \ REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.14g", dstr); \ REAL_VALUE_TO_DECIMAL (VALUE, "%.14g", dstr); \
fprintf (STREAM, "\t.long 0x%lx, 0x%lx\t%s double %s\n", l[0], \ asm_fprintf (STREAM, "\t.long 0x%lx, 0x%lx\t%@ double %s\n", l[0],\
l[1], ASM_COMMENT_START, dstr); \ l[1], dstr); \
} \ } \
while (0) while (0)
...@@ -216,8 +221,8 @@ Boston, MA 02111-1307, USA. */ ...@@ -216,8 +221,8 @@ Boston, MA 02111-1307, USA. */
long l; \ long l; \
REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \ REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.7g", dstr); \ REAL_VALUE_TO_DECIMAL (VALUE, "%.7g", dstr); \
fprintf (STREAM, "\t.word 0x%lx\t%s float %s\n", l, \ asm_fprintf (STREAM, "\t.word 0x%lx\t%@ float %s\n", l, \
ASM_COMMENT_START, dstr); \ dstr); \
} \ } \
while (0) while (0)
...@@ -280,8 +285,8 @@ Boston, MA 02111-1307, USA. */ ...@@ -280,8 +285,8 @@ Boston, MA 02111-1307, USA. */
{ \ { \
fprintf (STREAM, "\t.comm\t"); \ fprintf (STREAM, "\t.comm\t"); \
assemble_name (STREAM, NAME); \ assemble_name (STREAM, NAME); \
fprintf (STREAM, ", %d\t%s %d\n", ROUNDED, \ asm_fprintf (STREAM, ", %d\t%@ %d\n", \
ASM_COMMENT_START, SIZE); \ ROUNDED, SIZE); \
} \ } \
while (0) while (0)
#endif #endif
...@@ -314,7 +319,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -314,7 +319,7 @@ Boston, MA 02111-1307, USA. */
/* Output a #ident directive. */ /* Output a #ident directive. */
#ifndef ASM_OUTPUT_IDENT #ifndef ASM_OUTPUT_IDENT
#define ASM_OUTPUT_IDENT(STREAM,STRING) \ #define ASM_OUTPUT_IDENT(STREAM,STRING) \
fprintf (STREAM, "%s - - - ident %s\n", ASM_COMMENT_START, STRING) asm_fprintf (STREAM, "%@ - - - ident %s\n", STRING)
#endif #endif
/* The assembler's parentheses characters. */ /* The assembler's parentheses characters. */
...@@ -328,4 +333,4 @@ Boston, MA 02111-1307, USA. */ ...@@ -328,4 +333,4 @@ Boston, MA 02111-1307, USA. */
/* This works for GAS and some other assemblers. */ /* This works for GAS and some other assemblers. */
#define SET_ASM_OP ".set" #define SET_ASM_OP ".set"
#include "arm/arm.h" #include "arm.h"
...@@ -20,142 +20,176 @@ along with GNU CC; see the file COPYING. If not, write to ...@@ -20,142 +20,176 @@ along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330, the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
extern void arm_expand_prologue PARAMS ((void));
extern void arm_finalize_pic PARAMS ((void));
extern char * arm_output_epilogue PARAMS ((void));
extern void arm_override_options PARAMS ((void)); extern void arm_override_options PARAMS ((void));
extern void arm_poke_function_name PARAMS ((FILE *, char *)); extern int use_return_insn PARAMS ((int));
extern int arm_process_pragma PARAMS ((int (*)(void), void (*) (int), char *));
extern int arm_regno_class PARAMS ((int)); extern int arm_regno_class PARAMS ((int));
extern int arm_process_pragma PARAMS ((int (*)(void), void (*) (int),
char *));
extern void arm_finalize_pic PARAMS ((void));
extern int arm_volatile_func PARAMS ((void)); extern int arm_volatile_func PARAMS ((void));
extern int const_ok_for_arm PARAMS ((HOST_WIDE_INT)); extern char * arm_output_epilogue PARAMS ((void));
extern void output_arm_prologue PARAMS ((FILE *, int));
extern void output_ascii_pseudo_op PARAMS ((FILE *, const unsigned char *, int));
extern void output_func_epilogue PARAMS ((int)); extern void output_func_epilogue PARAMS ((int));
extern void output_func_prologue PARAMS ((FILE *, int)); extern void arm_expand_prologue PARAMS ((void));
extern int use_return_insn PARAMS ((int)); /* Used in arm.md, but defined in output.c. */
extern void assemble_align PARAMS ((int));
extern const char * arm_strip_name_encoding PARAMS ((const char *)); extern const char * arm_strip_name_encoding PARAMS ((const char *));
#if defined AOF_ASSEMBLER
extern void aof_add_import PARAMS ((char *));
extern char * aof_data_section PARAMS ((void));
extern void aof_delete_import PARAMS ((char *));
extern void aof_dump_imports PARAMS ((FILE *));
extern void aof_dump_pic_table PARAMS ((FILE *));
extern char * aof_text_section PARAMS ((void));
#endif /* AOF_ASSEMBLER */
/* Defined in pe.c */
extern int arm_dllexport_name_p PARAMS ((const char *));
extern int arm_dllimport_name_p PARAMS ((const char *));
#define Mmode enum machine_mode
#ifdef TREE_CODE #ifdef TREE_CODE
extern int arm_comp_type_attributes PARAMS ((tree, tree));
extern int arm_return_in_memory PARAMS ((tree)); extern int arm_return_in_memory PARAMS ((tree));
extern int arm_valid_machine_decl_attribute PARAMS ((tree, tree, tree)); extern int arm_valid_machine_decl_attribute PARAMS ((tree, tree, tree));
extern int arm_comp_type_attributes PARAMS ((tree, tree));
extern int arm_valid_type_attribute_p PARAMS ((tree, tree, tree, tree)); extern int arm_valid_type_attribute_p PARAMS ((tree, tree, tree, tree));
/* Defined in pe.c */
extern int arm_dllexport_p PARAMS ((tree));
extern int arm_dllimport_p PARAMS ((tree));
extern void arm_mark_dllexport PARAMS ((tree));
extern void arm_mark_dllimport PARAMS ((tree));
extern void arm_pe_encode_section_info PARAMS ((tree));
extern tree arm_pe_merge_machine_decl_attributes PARAMS ((tree, tree));
extern void arm_pe_unique_section PARAMS ((tree, int));
extern int arm_pe_valid_machine_decl_attribute PARAMS ((tree, tree, tree, tree));
extern void arm_set_default_type_attributes PARAMS ((tree)); extern void arm_set_default_type_attributes PARAMS ((tree));
extern void arm_encode_call_attribute PARAMS ((tree, char)); extern void arm_encode_call_attribute PARAMS ((tree, char));
extern int arm_pe_return_in_memory PARAMS ((tree));
#endif #endif
#ifdef RTX_CODE #ifdef RTX_CODE
extern int adjacent_mem_locations PARAMS ((rtx, rtx)); extern int const_ok_for_arm PARAMS ((HOST_WIDE_INT));
extern char * arithmetic_instr PARAMS ((rtx, int)); extern int arm_split_constant PARAMS ((RTX_CODE, enum machine_mode,
extern int arm_adjust_cost PARAMS ((rtx, rtx, rtx, int)); HOST_WIDE_INT, rtx, rtx, int));
extern RTX_CODE arm_canonicalize_comparison PARAMS ((RTX_CODE, rtx *)); extern RTX_CODE arm_canonicalize_comparison PARAMS ((RTX_CODE, rtx *));
extern int arm_debugger_arg_offset PARAMS ((int, rtx)); extern int legitimate_pic_operand_p PARAMS ((rtx));
extern void arm_final_prescan_insn PARAMS ((rtx)); extern rtx legitimize_pic_address PARAMS ((rtx, enum machine_mode, rtx));
extern rtx arm_gen_load_multiple PARAMS ((int, int, rtx, int, int, int, int, int)); extern int is_pic PARAMS ((rtx));
extern int arm_rtx_costs PARAMS ((rtx, RTX_CODE, RTX_CODE));
extern int arm_adjust_cost PARAMS ((rtx, rtx, rtx, int));
extern int const_double_rtx_ok_for_fpu PARAMS ((rtx));
extern int neg_const_double_rtx_ok_for_fpu PARAMS ((rtx));
/* Predicates. */
extern int s_register_operand PARAMS ((rtx, enum machine_mode));
extern int f_register_operand PARAMS ((rtx, enum machine_mode));
extern int reg_or_int_operand PARAMS ((rtx, enum machine_mode));
extern int arm_reload_memory_operand PARAMS ((rtx, enum machine_mode));
extern int arm_rhs_operand PARAMS ((rtx, enum machine_mode));
extern int arm_rhsm_operand PARAMS ((rtx, enum machine_mode));
extern int arm_add_operand PARAMS ((rtx, enum machine_mode));
extern int arm_not_operand PARAMS ((rtx, enum machine_mode));
extern int offsettable_memory_operand PARAMS ((rtx, enum machine_mode));
extern int alignable_memory_operand PARAMS ((rtx, enum machine_mode));
extern int bad_signed_byte_operand PARAMS ((rtx, enum machine_mode));
extern int fpu_rhs_operand PARAMS ((rtx, enum machine_mode));
extern int fpu_add_operand PARAMS ((rtx, enum machine_mode));
extern int power_of_two_operand PARAMS ((rtx, enum machine_mode));
extern int nonimmediate_di_operand PARAMS ((rtx, enum machine_mode));
extern int di_operand PARAMS ((rtx, enum machine_mode));
extern int nonimmediate_soft_df_operand PARAMS ((rtx, enum machine_mode));
extern int soft_df_operand PARAMS ((rtx, enum machine_mode));
extern int index_operand PARAMS ((rtx, enum machine_mode));
extern int const_shift_operand PARAMS ((rtx, enum machine_mode));
extern int shiftable_operator PARAMS ((rtx, enum machine_mode));
extern int shift_operator PARAMS ((rtx, enum machine_mode));
extern int equality_operator PARAMS ((rtx, enum machine_mode));
extern int minmax_operator PARAMS ((rtx, enum machine_mode));
extern int cc_register PARAMS ((rtx, enum machine_mode));
extern int dominant_cc_register PARAMS ((rtx, enum machine_mode));
extern int logical_binary_operator PARAMS ((rtx, enum machine_mode));
extern int multi_register_push PARAMS ((rtx, enum machine_mode));
extern int load_multiple_operation PARAMS ((rtx, enum machine_mode));
extern int store_multiple_operation PARAMS ((rtx, enum machine_mode));
extern int symbol_mentioned_p PARAMS ((rtx));
extern int label_mentioned_p PARAMS ((rtx));
extern RTX_CODE minmax_code PARAMS ((rtx));
extern int adjacent_mem_locations PARAMS ((rtx, rtx));
extern int load_multiple_sequence PARAMS ((rtx *, int, int *, int *,
HOST_WIDE_INT *));
extern char * emit_ldm_seq PARAMS ((rtx *, int));
extern int store_multiple_sequence PARAMS ((rtx *, int, int *, int *,
HOST_WIDE_INT *));
extern char * emit_stm_seq PARAMS ((rtx *, int));
extern rtx arm_gen_load_multiple PARAMS ((int, int, rtx, int, int, int,
int, int));
extern rtx arm_gen_store_multiple PARAMS ((int, int, rtx, int, int, int,
int, int));
extern int arm_gen_movstrqi PARAMS ((rtx *)); extern int arm_gen_movstrqi PARAMS ((rtx *));
extern rtx arm_gen_store_multiple PARAMS ((int, int, rtx, int, int, int, int, int)); extern rtx arm_gen_rotated_half_load PARAMS ((rtx));
extern void arm_print_operand PARAMS ((FILE *, rtx, int)); extern enum machine_mode arm_select_cc_mode PARAMS ((RTX_CODE, rtx, rtx));
extern rtx arm_gen_compare_reg PARAMS ((RTX_CODE, rtx, rtx));
extern void arm_reload_in_hi PARAMS ((rtx *)); extern void arm_reload_in_hi PARAMS ((rtx *));
extern void arm_reload_out_hi PARAMS ((rtx *)); extern void arm_reload_out_hi PARAMS ((rtx *));
extern void arm_reorg PARAMS ((rtx)); extern void arm_reorg PARAMS ((rtx));
extern int arm_rtx_costs PARAMS ((rtx, RTX_CODE));
extern Mmode arm_select_cc_mode PARAMS ((RTX_CODE, rtx, rtx));
extern int const_double_rtx_ok_for_fpu PARAMS ((rtx));
extern int const_ok_for_arm PARAMS ((HOST_WIDE_INT));
extern char * emit_ldm_seq PARAMS ((rtx *, int));
extern char * emit_stm_seq PARAMS ((rtx *, int));
extern char * fp_immediate_constant PARAMS ((rtx)); extern char * fp_immediate_constant PARAMS ((rtx));
extern rtx gen_compare_reg PARAMS ((RTX_CODE, rtx, rtx));
extern rtx gen_rotated_half_load PARAMS ((rtx));
extern int is_pic PARAMS ((rtx));
extern int label_mentioned_p PARAMS ((rtx));
extern int legitimate_pic_operand_p PARAMS ((rtx));
extern int load_multiple_sequence PARAMS ((rtx *, int, int *, int *, HOST_WIDE_INT *));
extern RTX_CODE minmax_code PARAMS ((rtx));
extern int neg_const_double_rtx_ok_for_fpu PARAMS ((rtx));
extern char * output_add_immediate PARAMS ((rtx *));
extern char * output_call PARAMS ((rtx *)); extern char * output_call PARAMS ((rtx *));
extern char * output_call_mem PARAMS ((rtx *)); extern char * output_call_mem PARAMS ((rtx *));
extern char * output_mov_double_arm_from_fpu PARAMS ((rtx *));
extern char * output_mov_double_fpu_from_arm PARAMS ((rtx *));
extern char * output_mov_immediate PARAMS ((rtx *));
extern char * output_mov_long_double_arm_from_arm PARAMS ((rtx *));
extern char * output_mov_long_double_arm_from_fpu PARAMS ((rtx *));
extern char * output_mov_long_double_fpu_from_arm PARAMS ((rtx *)); extern char * output_mov_long_double_fpu_from_arm PARAMS ((rtx *));
extern char * output_mov_long_double_arm_from_fpu PARAMS ((rtx *));
extern char * output_mov_long_double_arm_from_arm PARAMS ((rtx *));
extern char * output_mov_double_fpu_from_arm PARAMS ((rtx *));
extern char * output_mov_double_arm_from_fpu PARAMS ((rtx *));
extern char * output_move_double PARAMS ((rtx *)); extern char * output_move_double PARAMS ((rtx *));
extern char * output_mov_immediate PARAMS ((rtx *));
extern char * output_add_immediate PARAMS ((rtx *));
extern char * arithmetic_instr PARAMS ((rtx, int));
extern void output_ascii_pseudo_op PARAMS ((FILE *, const unsigned char *, int));
extern char * output_return_instruction PARAMS ((rtx, int, int)); extern char * output_return_instruction PARAMS ((rtx, int, int));
extern int store_multiple_sequence PARAMS ((rtx *, int, int *, int *, HOST_WIDE_INT *)); extern void arm_poke_function_name PARAMS ((FILE *, char *));
extern int symbol_mentioned_p PARAMS ((rtx)); extern void output_arm_prologue PARAMS ((FILE *, int));
extern void arm_print_operand PARAMS ((FILE *, rtx, int));
extern void arm_final_prescan_insn PARAMS ((rtx));
extern int arm_go_if_legitimate_address PARAMS ((enum machine_mode, rtx));
extern int arm_debugger_arg_offset PARAMS ((int, rtx));
extern int arm_is_longcall_p PARAMS ((rtx, int, int)); extern int arm_is_longcall_p PARAMS ((rtx, int, int));
#if defined TREE_CODE
extern rtx arm_function_arg PARAMS ((CUMULATIVE_ARGS *,
enum machine_mode, tree, int));
extern void arm_init_cumulative_args PARAMS ((CUMULATIVE_ARGS *, tree, rtx,
int));
#endif
#if defined AOF_ASSEMBLER #if defined AOF_ASSEMBLER
extern rtx aof_pic_entry PARAMS ((rtx)); extern rtx aof_pic_entry PARAMS ((rtx));
extern void aof_dump_pic_table PARAMS ((FILE *));
extern char * aof_text_section PARAMS ((void));
extern char * aof_data_section PARAMS ((void));
extern void aof_add_import PARAMS ((char *));
extern void aof_delete_import PARAMS ((char *));
extern void aof_dump_imports PARAMS ((FILE *));
#endif /* AOF_ASSEMBLER */ #endif /* AOF_ASSEMBLER */
#ifdef HAVE_MACHINE_MODES
extern int alignable_memory_operand PARAMS ((rtx, Mmode));
extern int arm_add_operand PARAMS ((rtx, Mmode));
extern int arm_go_if_legitimate_address PARAMS ((Mmode, rtx));
extern int arm_not_operand PARAMS ((rtx, Mmode));
extern int arm_reload_memory_operand PARAMS ((rtx, Mmode));
extern int arm_rhs_operand PARAMS ((rtx, Mmode));
extern int arm_rhsm_operand PARAMS ((rtx, Mmode));
extern Mmode arm_select_cc_mode PARAMS ((RTX_CODE, rtx, rtx));
extern int arm_split_constant PARAMS ((RTX_CODE, Mmode, HOST_WIDE_INT, rtx, rtx, int));
extern int bad_signed_byte_operand PARAMS ((rtx, Mmode));
extern int cc_register PARAMS ((rtx, Mmode));
extern int const_shift_operand PARAMS ((rtx, Mmode));
extern int di_operand PARAMS ((rtx, Mmode));
extern int dominant_cc_register PARAMS ((rtx, Mmode));
extern int equality_operator PARAMS ((rtx, Mmode));
extern int f_register_operand PARAMS ((rtx, Mmode));
extern int fpu_add_operand PARAMS ((rtx, Mmode));
extern int fpu_rhs_operand PARAMS ((rtx, Mmode));
extern int index_operand PARAMS ((rtx, Mmode));
extern rtx legitimize_pic_address PARAMS ((rtx, Mmode, rtx));
extern int load_multiple_operation PARAMS ((rtx, Mmode));
extern int logical_binary_operator PARAMS ((rtx, Mmode));
extern int minmax_operator PARAMS ((rtx, Mmode));
extern int multi_register_push PARAMS ((rtx, Mmode));
extern int nonimmediate_di_operand PARAMS ((rtx, Mmode));
extern int nonimmediate_soft_df_operand PARAMS ((rtx, Mmode));
extern int offsettable_memory_operand PARAMS ((rtx, Mmode));
extern int power_of_two_operand PARAMS ((rtx, Mmode));
extern int reg_or_int_operand PARAMS ((rtx, Mmode));
extern int s_register_operand PARAMS ((rtx, Mmode));
extern int shift_operator PARAMS ((rtx, Mmode));
extern int shiftable_operator PARAMS ((rtx, Mmode));
extern int soft_df_operand PARAMS ((rtx, Mmode));
extern int store_multiple_operation PARAMS ((rtx, Mmode));
#if defined TREE_CODE
extern rtx arm_function_arg PARAMS ((CUMULATIVE_ARGS *, Mmode, tree, int));
extern void arm_init_cumulative_args PARAMS ((CUMULATIVE_ARGS *, tree, rtx, int));
#endif /* TREE_CODE */
#endif /* HAVE_MACHINE_MODES */
#endif /* RTX_CODE */ #endif /* RTX_CODE */
#undef Mmode /* Thumb functions. */
extern void arm_init_expanders PARAMS ((void));
extern int thumb_far_jump_used_p PARAMS ((int));
extern char * thumb_unexpanded_epilogue PARAMS ((void));
extern void thumb_expand_prologue PARAMS ((void));
extern void thumb_expand_epilogue PARAMS ((void));
#ifdef TREE_CODE
extern int is_called_in_ARM_mode PARAMS ((tree));
#endif
#ifdef RTX_CODE
extern int thumb_shiftable_const PARAMS ((unsigned HOST_WIDE_INT));
extern void thumb_final_prescan_insn PARAMS ((rtx));
extern char * thumb_load_double_from_address
PARAMS ((rtx *));
extern void output_thumb_prologue PARAMS ((FILE *));
extern char * thumb_output_move_mem_multiple
PARAMS ((int, rtx *));
extern void thumb_expand_movstrqi PARAMS ((rtx *));
extern int thumb_cmp_operand PARAMS ((rtx, enum machine_mode));
extern rtx * thumb_legitimize_pic_address
PARAMS ((rtx, enum machine_mode, rtx));
extern int thumb_go_if_legitimate_address
PARAMS ((enum machine_mode, rtx));
extern rtx arm_return_addr PARAMS ((int, rtx));
extern void thumb_reload_out_hi PARAMS ((rtx *));
extern void thumb_reload_in_hi PARAMS ((rtx *));
#endif
/* Defined in pe.c. */
extern int arm_dllexport_name_p PARAMS ((char *));
extern int arm_dllimport_name_p PARAMS ((char *));
#ifdef TREE_CODE
extern int arm_pe_valid_machine_decl_attribute
PARAMS ((tree, tree, tree, tree));
extern tree arm_pe_merge_machine_decl_attributes
PARAMS ((tree, tree));
extern void arm_pe_unique_section PARAMS ((tree, int));
extern void arm_pe_encode_section_info PARAMS ((tree));
extern int arm_dllexport_p PARAMS ((tree));
extern int arm_dllimport_p PARAMS ((tree));
extern void arm_mark_dllexport PARAMS ((tree));
extern void arm_mark_dllimport PARAMS ((tree));
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -51,16 +51,17 @@ Boston, MA 02111-1307, USA. */ ...@@ -51,16 +51,17 @@ Boston, MA 02111-1307, USA. */
/* Configure didn't specify. */ /* Configure didn't specify. */
#define TARGET_CPU_generic 0x8000 #define TARGET_CPU_generic 0x8000
enum arm_cond_code typedef enum arm_cond_code
{ {
ARM_EQ = 0, ARM_NE, ARM_CS, ARM_CC, ARM_MI, ARM_PL, ARM_VS, ARM_VC, ARM_EQ = 0, ARM_NE, ARM_CS, ARM_CC, ARM_MI, ARM_PL, ARM_VS, ARM_VC,
ARM_HI, ARM_LS, ARM_GE, ARM_LT, ARM_GT, ARM_LE, ARM_AL, ARM_NV ARM_HI, ARM_LS, ARM_GE, ARM_LT, ARM_GT, ARM_LE, ARM_AL, ARM_NV
}; }
arm_cc;
extern enum arm_cond_code arm_current_cc; extern arm_cc arm_current_cc;
extern char * arm_condition_codes[]; extern char * arm_condition_codes[];
#define ARM_INVERSE_CONDITION_CODE(X) ((enum arm_cond_code) (((int)X) ^ 1)) #define ARM_INVERSE_CONDITION_CODE(X) ((arm_cc) (((int)X) ^ 1))
extern int arm_target_label; extern int arm_target_label;
extern int arm_ccfsm_state; extern int arm_ccfsm_state;
...@@ -72,7 +73,18 @@ extern int frame_pointer_needed; ...@@ -72,7 +73,18 @@ extern int frame_pointer_needed;
extern int target_flags; extern int target_flags;
/* The floating point instruction architecture, can be 2 or 3 */ /* The floating point instruction architecture, can be 2 or 3 */
extern const char * target_fp_name; extern const char * target_fp_name;
/* Define the information needed to generate branch insns. This is
stored from the compare operation. Note that we can't use "rtx" here
since it hasn't been defined! */
extern struct rtx_def * arm_compare_op0;
extern struct rtx_def * arm_compare_op1;
/* The label of the current constant pool. */
extern struct rtx_def * pool_vector_label;
/* Set to 1 when a return insn is output, this means that the epilogue
is not needed. */
extern int return_used_this_function;
/* Nonzero if the prologue must setup `fp'. */
extern int current_function_anonymous_args;
/* Just in case configure has failed to define anything. */ /* Just in case configure has failed to define anything. */
#ifndef TARGET_CPU_DEFAULT #ifndef TARGET_CPU_DEFAULT
...@@ -113,12 +125,14 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -113,12 +125,14 @@ Unrecognized value in TARGET_CPU_DEFAULT.
#endif #endif
#ifndef CPP_PREDEFINES #ifndef CPP_PREDEFINES
#define CPP_PREDEFINES "-Darm -Acpu(arm) -Amachine(arm)" #define CPP_PREDEFINES "-Acpu(arm) -Amachine(arm)"
#endif #endif
#define CPP_SPEC "\ #define CPP_SPEC "\
%(cpp_cpu_arch) %(cpp_apcs_pc) %(cpp_float) \ %(cpp_cpu_arch) %(cpp_apcs_pc) %(cpp_float) \
%(cpp_endian) %(subtarget_cpp_spec)" %(cpp_endian) %(subtarget_cpp_spec) %(cpp_isa)"
#define CPP_ISA_SPEC "%{mthumb:-Dthumb -D__thumb__} %{!mthumb:-Darm -D__arm__}"
/* Set the architecture define -- if -march= is set, then it overrides /* Set the architecture define -- if -march= is set, then it overrides
the -mcpu= setting. */ the -mcpu= setting. */
...@@ -156,6 +170,9 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -156,6 +170,9 @@ Unrecognized value in TARGET_CPU_DEFAULT.
%{march=armv4:-D__ARM_ARCH_4__} \ %{march=armv4:-D__ARM_ARCH_4__} \
%{march=armv4t:-D__ARM_ARCH_4T__} \ %{march=armv4t:-D__ARM_ARCH_4T__} \
%{march=armv5:-D__ARM_ARCH_5__} \ %{march=armv5:-D__ARM_ARCH_5__} \
%{march=armv5t:-D__ARM_ARCH_5T__} \
%{march=armv5e:-D__ARM_ARCH_5E__} \
%{march=armv5te:-D__ARM_ARCH_5TE__} \
%{!march=*: \ %{!march=*: \
%{mcpu=arm2:-D__ARM_ARCH_2__} \ %{mcpu=arm2:-D__ARM_ARCH_2__} \
%{mcpu=arm250:-D__ARM_ARCH_2__} \ %{mcpu=arm250:-D__ARM_ARCH_2__} \
...@@ -212,12 +229,13 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -212,12 +229,13 @@ Unrecognized value in TARGET_CPU_DEFAULT.
%{mbig-endian: \ %{mbig-endian: \
%{mlittle-endian: \ %{mlittle-endian: \
%e-mbig-endian and -mlittle-endian may not be used together} \ %e-mbig-endian and -mlittle-endian may not be used together} \
-D__ARMEB__ %{mwords-little-endian:-D__ARMWEL__}} \ -D__ARMEB__ %{mwords-little-endian:-D__ARMWEL__} %{mthumb:-D__THUMBEB__}}\
%{mlittle-endian:-D__ARMEL__ %{mthumb:-D__THUMBEL__}} \
%{!mlittle-endian:%{!mbig-endian:%(cpp_endian_default)}} \ %{!mlittle-endian:%{!mbig-endian:%(cpp_endian_default)}} \
" "
/* Default is little endian, which doesn't define anything. */ /* Default is little endian. */
#define CPP_ENDIAN_DEFAULT_SPEC "-D__ARMEL__" #define CPP_ENDIAN_DEFAULT_SPEC "-D__ARMEL__ %{mthumb:-D__THUMBEL__}"
#define CC1_SPEC "" #define CC1_SPEC ""
...@@ -239,6 +257,7 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -239,6 +257,7 @@ Unrecognized value in TARGET_CPU_DEFAULT.
{ "cpp_float_default", CPP_FLOAT_DEFAULT_SPEC }, \ { "cpp_float_default", CPP_FLOAT_DEFAULT_SPEC }, \
{ "cpp_endian", CPP_ENDIAN_SPEC }, \ { "cpp_endian", CPP_ENDIAN_SPEC }, \
{ "cpp_endian_default", CPP_ENDIAN_DEFAULT_SPEC }, \ { "cpp_endian_default", CPP_ENDIAN_DEFAULT_SPEC }, \
{ "cpp_isa", CPP_ISA_SPEC }, \
{ "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \ { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \
SUBTARGET_EXTRA_SPECS SUBTARGET_EXTRA_SPECS
...@@ -320,7 +339,26 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -320,7 +339,26 @@ Unrecognized value in TARGET_CPU_DEFAULT.
/* Nonzero if all call instructions should be indirect. */ /* Nonzero if all call instructions should be indirect. */
#define ARM_FLAG_LONG_CALLS (1 << 15) #define ARM_FLAG_LONG_CALLS (1 << 15)
#define TARGET_APCS (target_flags & ARM_FLAG_APCS_FRAME) /* Nonzero means that the target ISA is the THUMB, not the ARM. */
#define ARM_FLAG_THUMB (1 << 16)
/* Set if a TPCS style stack frame should be generated, for non-leaf
functions, even if they do not need one. */
#define THUMB_FLAG_BACKTRACE (1 << 17)
/* Set if a TPCS style stack frame should be generated, for leaf
functions, even if they do not need one. */
#define THUMB_FLAG_LEAF_BACKTRACE (1 << 18)
/* Set if externally visible functions should assume that they
might be called in ARM mode, from a non-thumb aware code. */
#define THUMB_FLAG_CALLEE_SUPER_INTERWORKING (1 << 19)
/* Set if calls via function pointers should assume that their
destination is non-Thumb aware. */
#define THUMB_FLAG_CALLER_SUPER_INTERWORKING (1 << 20)
#define TARGET_APCS_FRAME (target_flags & ARM_FLAG_APCS_FRAME)
#define TARGET_POKE_FUNCTION_NAME (target_flags & ARM_FLAG_POKE) #define TARGET_POKE_FUNCTION_NAME (target_flags & ARM_FLAG_POKE)
#define TARGET_FPE (target_flags & ARM_FLAG_FPE) #define TARGET_FPE (target_flags & ARM_FLAG_FPE)
#define TARGET_APCS_32 (target_flags & ARM_FLAG_APCS_32) #define TARGET_APCS_32 (target_flags & ARM_FLAG_APCS_32)
...@@ -337,6 +375,14 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -337,6 +375,14 @@ Unrecognized value in TARGET_CPU_DEFAULT.
#define TARGET_ABORT_NORETURN (target_flags & ARM_FLAG_ABORT_NORETURN) #define TARGET_ABORT_NORETURN (target_flags & ARM_FLAG_ABORT_NORETURN)
#define TARGET_SINGLE_PIC_BASE (target_flags & ARM_FLAG_SINGLE_PIC_BASE) #define TARGET_SINGLE_PIC_BASE (target_flags & ARM_FLAG_SINGLE_PIC_BASE)
#define TARGET_LONG_CALLS (target_flags & ARM_FLAG_LONG_CALLS) #define TARGET_LONG_CALLS (target_flags & ARM_FLAG_LONG_CALLS)
#define TARGET_THUMB (target_flags & ARM_FLAG_THUMB)
#define TARGET_ARM (! TARGET_THUMB)
#define TARGET_EITHER 1 /* (TARGET_ARM | TARGET_THUMB) */
#define TARGET_CALLEE_INTERWORKING (target_flags & THUMB_FLAG_CALLEE_SUPER_INTERWORKING)
#define TARGET_CALLER_INTERWORKING (target_flags & THUMB_FLAG_CALLER_SUPER_INTERWORKING)
#define TARGET_BACKTRACE (leaf_function_p () \
? (target_flags & THUMB_FLAG_LEAF_BACKTRACE) \
: (target_flags & THUMB_FLAG_BACKTRACE))
/* SUBTARGET_SWITCHES is used to add flags on a per-config basis. /* SUBTARGET_SWITCHES is used to add flags on a per-config basis.
Bit 31 is reserved. See riscix.h. */ Bit 31 is reserved. See riscix.h. */
...@@ -388,7 +434,7 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -388,7 +434,7 @@ Unrecognized value in TARGET_CPU_DEFAULT.
{"no-thumb-interwork", -ARM_FLAG_INTERWORK, "" }, \ {"no-thumb-interwork", -ARM_FLAG_INTERWORK, "" }, \
{"abort-on-noreturn", ARM_FLAG_ABORT_NORETURN, \ {"abort-on-noreturn", ARM_FLAG_ABORT_NORETURN, \
"Generate a call to abort if a noreturn function returns"},\ "Generate a call to abort if a noreturn function returns"},\
{"no-abort-on-noreturn", -ARM_FLAG_ABORT_NORETURN, ""}, \ {"no-abort-on-noreturn", -ARM_FLAG_ABORT_NORETURN, "" }, \
{"sched-prolog", -ARM_FLAG_NO_SCHED_PRO, \ {"sched-prolog", -ARM_FLAG_NO_SCHED_PRO, \
"Do not move instructions into a function's prologue" }, \ "Do not move instructions into a function's prologue" }, \
{"no-sched-prolog", ARM_FLAG_NO_SCHED_PRO, "" }, \ {"no-sched-prolog", ARM_FLAG_NO_SCHED_PRO, "" }, \
...@@ -396,8 +442,26 @@ Unrecognized value in TARGET_CPU_DEFAULT. ...@@ -396,8 +442,26 @@ Unrecognized value in TARGET_CPU_DEFAULT.
"Do not load the PIC register in function prologues" }, \ "Do not load the PIC register in function prologues" }, \
{"no-single-pic-base", -ARM_FLAG_SINGLE_PIC_BASE, "" }, \ {"no-single-pic-base", -ARM_FLAG_SINGLE_PIC_BASE, "" }, \
{"long-calls", ARM_FLAG_LONG_CALLS, \ {"long-calls", ARM_FLAG_LONG_CALLS, \
"Generate call insns as indirect calls, if necessary"}, \ "Generate call insns as indirect calls, if necessary" }, \
{"no-long-calls", -ARM_FLAG_LONG_CALLS, ""}, \ {"no-long-calls", -ARM_FLAG_LONG_CALLS, "" }, \
{"thumb", ARM_FLAG_THUMB, \
"Compile for the Thumb not the ARM" }, \
{"no-thumb", -ARM_FLAG_THUMB, "" }, \
{"arm", -ARM_FLAG_THUMB, "" }, \
{"tpcs-frame", THUMB_FLAG_BACKTRACE, \
"Thumb: Generate (non-leaf) stack frames even if not needed" }, \
{"no-tpcs-frame", -THUMB_FLAG_BACKTRACE, "" }, \
{"tpcs-leaf-frame", THUMB_FLAG_LEAF_BACKTRACE, \
"Thumb: Generate (leaf) stack frames even if not needed" }, \
{"no-tpcs-leaf-frame", -THUMB_FLAG_LEAF_BACKTRACE, "" }, \
{"callee-super-interworking", THUMB_FLAG_CALLEE_SUPER_INTERWORKING, \
"Thumb: Assume non-static functions may be called from ARM code" }, \
{"no-callee-super-interworking", -THUMB_FLAG_CALLEE_SUPER_INTERWORKING, \
""}, \
{"caller-super-interworking", THUMB_FLAG_CALLER_SUPER_INTERWORKING, \
"Thumb: Assume function pointers may go to non-Thumb aware code" }, \
{"no-caller-super-interworking", -THUMB_FLAG_CALLER_SUPER_INTERWORKING, \
"" }, \
SUBTARGET_SWITCHES \ SUBTARGET_SWITCHES \
{"", TARGET_DEFAULT, "" } \ {"", TARGET_DEFAULT, "" } \
} }
...@@ -483,13 +547,12 @@ extern int arm_is_strong; ...@@ -483,13 +547,12 @@ extern int arm_is_strong;
extern int arm_is_6_or_7; extern int arm_is_6_or_7;
#ifndef TARGET_DEFAULT #ifndef TARGET_DEFAULT
#define TARGET_DEFAULT 0 #define TARGET_DEFAULT (ARM_FLAG_APCS_FRAME)
#endif #endif
/* The frame pointer register used in gcc has nothing to do with debugging; /* The frame pointer register used in gcc has nothing to do with debugging;
that is controlled by the APCS-FRAME option. */ that is controlled by the APCS-FRAME option. */
/* Not fully implemented yet. */ #define CAN_DEBUG_WITHOUT_FP
/* #define CAN_DEBUG_WITHOUT_FP 1 */
#define TARGET_MEM_FUNCTIONS 1 #define TARGET_MEM_FUNCTIONS 1
...@@ -705,15 +768,12 @@ extern const char * structure_size_string; ...@@ -705,15 +768,12 @@ extern const char * structure_size_string;
[| saved f4 value |] three words [| saved f4 value |] three words
r0-r3 are not normally saved in a C function. */ r0-r3 are not normally saved in a C function. */
/* The number of hard registers is 16 ARM + 8 FPU + 1 CC + 1 SFP. */
#define FIRST_PSEUDO_REGISTER 27
/* 1 for registers that have pervasive standard uses /* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */ and are not available for the register allocator. */
#define FIXED_REGISTERS \ #define FIXED_REGISTERS \
{ \ { \
0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0, \
0,0,0,1,0,1,0,1, \ 0,0,0,0,0,1,0,1, \
0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0, \
1,1,1 \ 1,1,1 \
} }
...@@ -729,7 +789,7 @@ extern const char * structure_size_string; ...@@ -729,7 +789,7 @@ extern const char * structure_size_string;
#define CALL_USED_REGISTERS \ #define CALL_USED_REGISTERS \
{ \ { \
1,1,1,1,0,0,0,0, \ 1,1,1,1,0,0,0,0, \
0,0,0,1,1,1,1,1, \ 0,0,0,0,1,1,1,1, \
1,1,1,1,0,0,0,0, \ 1,1,1,1,0,0,0,0, \
1,1,1 \ 1,1,1 \
} }
...@@ -740,10 +800,11 @@ extern const char * structure_size_string; ...@@ -740,10 +800,11 @@ extern const char * structure_size_string;
#define CONDITIONAL_REGISTER_USAGE \ #define CONDITIONAL_REGISTER_USAGE \
{ \ { \
if (TARGET_SOFT_FLOAT) \ if (TARGET_SOFT_FLOAT || TARGET_THUMB) \
{ \ { \
int regno; \ int regno; \
for (regno = 16; regno < 24; ++regno) \ for (regno = FIRST_ARM_FP_REGNUM; \
regno <= LAST_ARM_FP_REGNUM; ++regno) \
fixed_regs[regno] = call_used_regs[regno] = 1; \ fixed_regs[regno] = call_used_regs[regno] = 1; \
} \ } \
if (flag_pic) \ if (flag_pic) \
...@@ -756,6 +817,11 @@ extern const char * structure_size_string; ...@@ -756,6 +817,11 @@ extern const char * structure_size_string;
fixed_regs[10] = 1; \ fixed_regs[10] = 1; \
call_used_regs[10] = 1; \ call_used_regs[10] = 1; \
} \ } \
if (TARGET_APCS_FRAME) \
{ \
fixed_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \
call_used_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \
} \
SUBTARGET_CONDITIONAL_REGISTER_USAGE \ SUBTARGET_CONDITIONAL_REGISTER_USAGE \
} }
...@@ -773,6 +839,9 @@ extern const char * structure_size_string; ...@@ -773,6 +839,9 @@ extern const char * structure_size_string;
fputs (reg_names [va_arg (ARGS, int)], FILE); \ fputs (reg_names [va_arg (ARGS, int)], FILE); \
break; break;
/* Round X up to the nearest word. */
#define ROUND_UP(X) (((X) + 3) & ~3)
/* Convert fron bytes to ints. */ /* Convert fron bytes to ints. */
#define NUM_INTS(X) (((X) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) #define NUM_INTS(X) (((X) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
...@@ -782,7 +851,8 @@ extern const char * structure_size_string; ...@@ -782,7 +851,8 @@ extern const char * structure_size_string;
/* The number of (integer) registers required to hold a quantity of TYPE MODE. */ /* The number of (integer) registers required to hold a quantity of TYPE MODE. */
#define NUM_REGS2(MODE, TYPE) \ #define NUM_REGS2(MODE, TYPE) \
NUM_INTS ((MODE) == BLKmode ? int_size_in_bytes (TYPE) : GET_MODE_SIZE (MODE)) NUM_INTS ((MODE) == BLKmode ? \
int_size_in_bytes (TYPE) : GET_MODE_SIZE (MODE))
/* The number of (integer) argument register available. */ /* The number of (integer) argument register available. */
#define NUM_ARG_REGS 4 #define NUM_ARG_REGS 4
...@@ -790,63 +860,76 @@ extern const char * structure_size_string; ...@@ -790,63 +860,76 @@ extern const char * structure_size_string;
/* Return the regiser number of the N'th (integer) argument. */ /* Return the regiser number of the N'th (integer) argument. */
#define ARG_REGISTER(N) (N - 1) #define ARG_REGISTER(N) (N - 1)
/* Register in which address to store a structure value
is passed to a function. */
#define STRUCT_VALUE_REGNUM ARG_REGISTER (1)
/* Specify the registers used for certain standard purposes.
The values of these macros are register numbers. */
/* The number of the last argument register. */ /* The number of the last argument register. */
#define LAST_ARG_REGNUM ARG_REGISTER (NUM_ARG_REGS) #define LAST_ARG_REGNUM ARG_REGISTER (NUM_ARG_REGS)
/* Return number of consecutive hard regs needed starting at reg REGNO /* The number of the last "lo" register (thumb). */
to hold something of mode MODE. #define LAST_LO_REGNUM 7
This is ordinarily the length in words of a value of mode MODE
but can be less for certain modes in special long registers.
On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP /* The native (Norcroft) Pascal compiler for the ARM passes the static chain
mode. */ as an invisible last argument (possible since varargs don't exist in
#define HARD_REGNO_NREGS(REGNO, MODE) \ Pascal), so the following is not true. */
(( REGNO >= 16 \ #define STATIC_CHAIN_REGNUM (TARGET_ARM ? 8 : 9)
&& REGNO != FRAME_POINTER_REGNUM \
&& REGNO != ARG_POINTER_REGNUM) \
? 1 : NUM_REGS (MODE))
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. /* Define this to be where the real frame pointer is if it is not possible to
This is TRUE for ARM regs since they can hold anything, and TRUE for FPU work out the offset between the frame pointer and the automatic variables
regs holding FP. */ until after register allocation has taken place. FRAME_POINTER_REGNUM
#define HARD_REGNO_MODE_OK(REGNO, MODE) \ should point to a special register that we will make sure is eliminated.
((GET_MODE_CLASS (MODE) == MODE_CC) ? (REGNO == CC_REGNUM) : \
((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \ For the Thumb we have another problem. The TPCS defines the frame pointer
|| REGNO == ARG_POINTER_REGNUM \ as r11, and GCC belives that it is always possible to use the frame pointer
|| GET_MODE_CLASS (MODE) == MODE_FLOAT)) as base register for addressing purposes. (See comments in
find_reloads_address()). But - the Thumb does not allow high registers,
including r11, to be used as base address registers. Hence our problem.
The solution used here, and in the old thumb port is to use r7 instead of
r11 as the hard frame pointer and to have special code to generate
backtrace structures on the stack (if required to do so via a command line
option) using r11. This is the only 'user visable' use of r11 as a frame
pointer. */
#define ARM_HARD_FRAME_POINTER_REGNUM 11
#define THUMB_HARD_FRAME_POINTER_REGNUM 7
#define HARD_FRAME_POINTER_REGNUM (TARGET_ARM ? ARM_HARD_FRAME_POINTER_REGNUM : THUMB_HARD_FRAME_POINTER_REGNUM)
#define FP_REGNUM HARD_FRAME_POINTER_REGNUM
/* Value is 1 if it is a good idea to tie two pseudo registers /* Scratch register - used in all kinds of places, eg trampolines. */
when one has mode MODE1 and one has mode MODE2. #define IP_REGNUM 12
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
for any hard reg, then this must be 0 for correct output. */
#define MODES_TIEABLE_P(MODE1, MODE2) \
(GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2))
/* Specify the registers used for certain standard purposes. /* Register to use for pushing function arguments. */
The values of these macros are register numbers. */ #define STACK_POINTER_REGNUM 13
#define SP_REGNUM STACK_POINTER_REGNUM
/* Register which holds return address from a subroutine call. */
#define LR_REGNUM 14
/* Define this if the program counter is overloaded on a register. */ /* Define this if the program counter is overloaded on a register. */
#define PC_REGNUM 15 #define PC_REGNUM 15
/* Register to use for pushing function arguments. */ /* The number of the last ARM (integer) register. */
#define STACK_POINTER_REGNUM 13 #define LAST_ARM_REGNUM 15
#define SP_REGNUM STACK_POINTER_REGNUM
/* ARM floating pointer registers. */
#define FIRST_ARM_FP_REGNUM 16
#define LAST_ARM_FP_REGNUM 23
/* Internal, so that we don't need to refer to a raw number */
#define CC_REGNUM 24
/* Base register for access to local variables of the function. */ /* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM 25 #define FRAME_POINTER_REGNUM 25
/* Define this to be where the real frame pointer is if it is not possible to /* Base register for access to arguments of the function. */
work out the offset between the frame pointer and the automatic variables #define ARG_POINTER_REGNUM 26
until after register allocation has taken place. FRAME_POINTER_REGNUM
should point to a special register that we will make sure is eliminated. */
#define HARD_FRAME_POINTER_REGNUM 11
#define FP_REGNUM HARD_FRAME_POINTER_REGNUM
/* Register which holds return address from a subroutine call. */
#define LR_REGNUM 14
/* Scratch register - used in all kinds of places, eg trampolines. */ /* The number of hard registers is 16 ARM + 8 FPU + 1 CC + 1 SFP. */
#define IP_REGNUM 12 #define FIRST_PSEUDO_REGISTER 27
/* Value should be nonzero if functions must have frame pointers. /* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms may be accessed Zero means the frame pointer need not be set up (and parms may be accessed
...@@ -855,30 +938,54 @@ extern const char * structure_size_string; ...@@ -855,30 +938,54 @@ extern const char * structure_size_string;
APCS says that the frame pointer does not need to be pushed in leaf APCS says that the frame pointer does not need to be pushed in leaf
functions, or simple tail call functions. */ functions, or simple tail call functions. */
#define FRAME_POINTER_REQUIRED \ #define FRAME_POINTER_REQUIRED \
(current_function_has_nonlocal_label || (TARGET_APCS && !leaf_function_p ())) (current_function_has_nonlocal_label \
|| (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ()))
/* Base register for access to arguments of the function. */ /* Return number of consecutive hard regs needed starting at reg REGNO
#define ARG_POINTER_REGNUM 26 to hold something of mode MODE.
This is ordinarily the length in words of a value of mode MODE
but can be less for certain modes in special long registers.
/* The native (Norcroft) Pascal compiler for the ARM passes the static chain On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP
as an invisible last argument (possible since varargs don't exist in mode. */
Pascal), so the following is not true. */ #define HARD_REGNO_NREGS(REGNO, MODE) \
#define STATIC_CHAIN_REGNUM 8 ((TARGET_ARM \
&& REGNO >= FIRST_ARM_FP_REGNUM \
&& REGNO != FRAME_POINTER_REGNUM \
&& REGNO != ARG_POINTER_REGNUM) \
? 1 : NUM_REGS (MODE))
/* Register in which address to store a structure value /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
is passed to a function. */ This is TRUE for ARM regs since they can hold anything, and TRUE for FPU
#define STRUCT_VALUE_REGNUM 0 regs holding FP.
For the Thumb we only allow values bigger than SImode in registers 0 - 6,
so that there is always a second lo register available to hold the upper
part of the value. Probably we ought to ensure that the register is the
start of an even numbered register pair. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
(TARGET_ARM ? \
((GET_MODE_CLASS (MODE) == MODE_CC) ? (REGNO == CC_REGNUM) : \
( REGNO <= LAST_ARM_REGNUM \
|| REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM \
|| GET_MODE_CLASS (MODE) == MODE_FLOAT)) \
: \
((GET_MODE_CLASS (MODE) == MODE_CC) ? (REGNO == CC_REGNUM) : \
(NUM_REGS (MODE) < 2 || REGNO < LAST_LO_REGNUM)))
/* Internal, so that we don't need to refer to a raw number */ /* Value is 1 if it is a good idea to tie two pseudo registers
#define CC_REGNUM 24 when one has mode MODE1 and one has mode MODE2.
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
for any hard reg, then this must be 0 for correct output. */
#define MODES_TIEABLE_P(MODE1, MODE2) \
(GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2))
/* The order in which register should be allocated. It is good to use ip /* The order in which register should be allocated. It is good to use ip
since no saving is required (though calls clobber it) and it never contains since no saving is required (though calls clobber it) and it never contains
function parameters. It is quite good to use lr since other calls may function parameters. It is quite good to use lr since other calls may
clobber it anyway. Allocate r0 through r3 in reverse order since r3 is clobber it anyway. Allocate r0 through r3 in reverse order since r3 is
least likely to contain a function parameter; in addition results are least likely to contain a function parameter; in addition results are
returned in r0. returned in r0. */
*/
#define REG_ALLOC_ORDER \ #define REG_ALLOC_ORDER \
{ \ { \
3, 2, 1, 0, 12, 14, 4, 5, \ 3, 2, 1, 0, 12, 14, 4, 5, \
...@@ -889,11 +996,17 @@ extern const char * structure_size_string; ...@@ -889,11 +996,17 @@ extern const char * structure_size_string;
/* Register and constant classes. */ /* Register and constant classes. */
/* Register classes: all ARM regs or all FPU regs---simple! */ /* Register classes: used to be simple, just all ARM regs or all FPU regs
Now that the Thumb is involved it has become more compilcated. */
enum reg_class enum reg_class
{ {
NO_REGS, NO_REGS,
FPU_REGS, FPU_REGS,
LO_REGS,
STACK_REG,
BASE_REGS,
HI_REGS,
CC_REG,
GENERAL_REGS, GENERAL_REGS,
ALL_REGS, ALL_REGS,
LIM_REG_CLASSES LIM_REG_CLASSES
...@@ -906,6 +1019,11 @@ enum reg_class ...@@ -906,6 +1019,11 @@ enum reg_class
{ \ { \
"NO_REGS", \ "NO_REGS", \
"FPU_REGS", \ "FPU_REGS", \
"LO_REGS", \
"STACK_REG", \
"BASE_REGS", \
"HI_REGS", \
"CC_REG", \
"GENERAL_REGS", \ "GENERAL_REGS", \
"ALL_REGS", \ "ALL_REGS", \
} }
...@@ -917,6 +1035,11 @@ enum reg_class ...@@ -917,6 +1035,11 @@ enum reg_class
{ \ { \
{ 0x0000000 }, /* NO_REGS */ \ { 0x0000000 }, /* NO_REGS */ \
{ 0x0FF0000 }, /* FPU_REGS */ \ { 0x0FF0000 }, /* FPU_REGS */ \
{ 0x00000FF }, /* LO_REGS */ \
{ 0x0002000 }, /* STACK_REG */ \
{ 0x00020FF }, /* BASE_REGS */ \
{ 0x000FF00 }, /* HI_REGS */ \
{ 0x1000000 }, /* CC_REG */ \
{ 0x200FFFF }, /* GENERAL_REGS */ \ { 0x200FFFF }, /* GENERAL_REGS */ \
{ 0x2FFFFFF } /* ALL_REGS */ \ { 0x2FFFFFF } /* ALL_REGS */ \
} }
...@@ -925,20 +1048,30 @@ enum reg_class ...@@ -925,20 +1048,30 @@ enum reg_class
Return the class number of the smallest class containing Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression reg number REGNO. This could be a conditional expression
or could index an array. */ or could index an array. */
#define REGNO_REG_CLASS(REGNO) \ #define REGNO_REG_CLASS(REGNO) arm_regno_class (REGNO)
(((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM) \
? GENERAL_REGS : (REGNO) == CC_REGNUM \
? NO_REGS : FPU_REGS)
/* The class value for index registers, and the one for base regs. */ /* The class value for index registers, and the one for base regs. */
#define INDEX_REG_CLASS GENERAL_REGS #define INDEX_REG_CLASS (TARGET_THUMB ? LO_REGS : GENERAL_REGS)
#define BASE_REG_CLASS GENERAL_REGS #define BASE_REG_CLASS (TARGET_THUMB ? BASE_REGS : GENERAL_REGS)
/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows
registers explicitly used in the rtl to be used as spill registers
but prevents the compiler from extending the lifetime of these
registers. */
#define SMALL_REGISTER_CLASSES TARGET_THUMB
/* Get reg_class from a letter such as appears in the machine description. /* Get reg_class from a letter such as appears in the machine description.
We only need constraint `f' for FPU_REGS (`r' == GENERAL_REGS). */ We only need constraint `f' for FPU_REGS (`r' == GENERAL_REGS) for the
ARM, but several more letters for the Thumb. */
#define REG_CLASS_FROM_LETTER(C) \ #define REG_CLASS_FROM_LETTER(C) \
((C)=='f' ? FPU_REGS : NO_REGS) ( (C) == 'f' ? FPU_REGS \
: (C) == 'l' ? (TARGET_ARM ? GENERAL_REGS : LO_REGS) \
: TARGET_ARM ? NO_REGS \
: (C) == 'h' ? HI_REGS \
: (C) == 'b' ? BASE_REGS \
: (C) == 'k' ? STACK_REG \
: (C) == 'c' ? CC_REG \
: NO_REGS)
/* The letters I, J, K, L and M in a register constraint string /* The letters I, J, K, L and M in a register constraint string
can be used to stand for particular ranges of immediate operands. can be used to stand for particular ranges of immediate operands.
...@@ -950,7 +1083,7 @@ enum reg_class ...@@ -950,7 +1083,7 @@ enum reg_class
K: ~value ok in rhs argument of data operand. K: ~value ok in rhs argument of data operand.
L: -value ok in rhs argument of data operand. L: -value ok in rhs argument of data operand.
M: 0..32, or a power of 2 (for shifts, or mult done by shift). */ M: 0..32, or a power of 2 (for shifts, or mult done by shift). */
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ #define CONST_OK_FOR_ARM_LETTER(VALUE, C) \
((C) == 'I' ? const_ok_for_arm (VALUE) : \ ((C) == 'I' ? const_ok_for_arm (VALUE) : \
(C) == 'J' ? ((VALUE) < 4096 && (VALUE) > -4096) : \ (C) == 'J' ? ((VALUE) < 4096 && (VALUE) > -4096) : \
(C) == 'K' ? (const_ok_for_arm (~(VALUE))) : \ (C) == 'K' ? (const_ok_for_arm (~(VALUE))) : \
...@@ -959,46 +1092,94 @@ enum reg_class ...@@ -959,46 +1092,94 @@ enum reg_class
|| (((VALUE) & ((VALUE) - 1)) == 0)) \ || (((VALUE) & ((VALUE) - 1)) == 0)) \
: 0) : 0)
#define CONST_OK_FOR_THUMB_LETTER(VAL, C) \
((C) == 'I' ? (unsigned HOST_WIDE_INT) (VAL) < 256 : \
(C) == 'J' ? (VAL) > -256 && (VAL) < 0 : \
(C) == 'K' ? thumb_shiftable_const (VAL) : \
(C) == 'L' ? (VAL) > -8 && (VAL) < 8 : \
(C) == 'M' ? ((unsigned HOST_WIDE_INT) (VAL) < 1024 \
&& ((VAL) & 3) == 0) : \
(C) == 'N' ? ((unsigned HOST_WIDE_INT) (VAL) < 32) : \
(C) == 'O' ? ((VAL) >= -508 && (VAL) <= 508) \
: 0)
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
(TARGET_ARM ? \
CONST_OK_FOR_ARM_LETTER (VALUE, C) : CONST_OK_FOR_THUMB_LETTER (VALUE, C))
/* Constant letter 'G' for the FPU immediate constants.
'H' means the same constant negated. */
#define CONST_DOUBLE_OK_FOR_ARM_LETTER(X, C) \
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) : \
(C) == 'H' ? neg_const_double_rtx_ok_for_fpu (X) : 0)
#define CONST_DOUBLE_OK_FOR_LETTER_P(X, C) \
(TARGET_ARM ? \
CONST_DOUBLE_OK_FOR_ARM_LETTER (X, C) : 0)
/* For the ARM, `Q' means that this is a memory operand that is just /* For the ARM, `Q' means that this is a memory operand that is just
an offset from a register. an offset from a register.
`S' means any symbol that has the SYMBOL_REF_FLAG set or a CONSTANT_POOL `S' means any symbol that has the SYMBOL_REF_FLAG set or a CONSTANT_POOL
address. This means that the symbol is in the text segment and can be address. This means that the symbol is in the text segment and can be
accessed without using a load. */ accessed without using a load. */
#define EXTRA_CONSTRAINT(OP, C) \ #define EXTRA_CONSTRAINT_ARM(OP, C) \
((C) == 'Q' ? GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG \ ((C) == 'Q' ? GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG : \
: (C) == 'R' ? (GET_CODE (OP) == MEM \ (C) == 'R' ? (GET_CODE (OP) == MEM \
&& GET_CODE (XEXP (OP, 0)) == SYMBOL_REF \ && GET_CODE (XEXP (OP, 0)) == SYMBOL_REF \
&& CONSTANT_POOL_ADDRESS_P (XEXP (OP, 0))) \ && CONSTANT_POOL_ADDRESS_P (XEXP (OP, 0))) : \
: (C) == 'S' ? (optimize > 0 && CONSTANT_ADDRESS_P (OP)) \ (C) == 'S' ? (optimize > 0 && CONSTANT_ADDRESS_P (OP)) \
: 0) : 0)
/* Constant letter 'G' for the FPU immediate constants. #define EXTRA_CONSTRAINT_THUMB(X, C) \
'H' means the same constant negated. */ ((C) == 'Q' ? (GET_CODE (X) == MEM \
#define CONST_DOUBLE_OK_FOR_LETTER_P(X,C) \ && GET_CODE (XEXP (X, 0)) == LABEL_REF) : 0)
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) \
: (C) == 'H' ? neg_const_double_rtx_ok_for_fpu (X) : 0) #define EXTRA_CONSTRAINT(X, C) \
(TARGET_ARM ? \
EXTRA_CONSTRAINT_ARM (X, C) : EXTRA_CONSTRAINT_THUMB (X, C))
/* Given an rtx X being reloaded into a reg required to be /* Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use. in class CLASS, return the class of reg to actually use.
In general this is just CLASS; but on some machines In general this is just CLASS, but for the Thumb we prefer
in some cases it is preferable to use a more restrictive class. */ a LO_REGS class or a subset. */
#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS) #define PREFERRED_RELOAD_CLASS(X, CLASS) \
(TARGET_ARM ? (CLASS) : \
((CLASS) == BASE_REGS ? (CLASS) : LO_REGS))
/* Must leave BASE_REGS reloads alone */
#define THUMB_SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
((CLASS) != LO_REGS && (CLASS) != BASE_REGS \
? ((true_regnum (X) == -1 ? LO_REGS \
: (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \
: NO_REGS)) \
: NO_REGS)
#define THUMB_SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
((CLASS) != LO_REGS \
? ((true_regnum (X) == -1 ? LO_REGS \
: (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \
: NO_REGS)) \
: NO_REGS)
/* Return the register class of a scratch register needed to copy IN into /* Return the register class of a scratch register needed to copy IN into
or out of a register in CLASS in MODE. If it can be done directly, or out of a register in CLASS in MODE. If it can be done directly,
NO_REGS is returned. */ NO_REGS is returned. */
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS,MODE,X) \ #define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
(TARGET_ARM ? \
(((MODE) == HImode && ! arm_arch4 && true_regnum (X) == -1) \ (((MODE) == HImode && ! arm_arch4 && true_regnum (X) == -1) \
? GENERAL_REGS : NO_REGS) ? GENERAL_REGS : NO_REGS) \
: THUMB_SECONDARY_OUTPUT_RELOAD_CLASS (CLASS, MODE, X))
/* If we need to load shorts byte-at-a-time, then we need a scratch. */ /* If we need to load shorts byte-at-a-time, then we need a scratch. */
#define SECONDARY_INPUT_RELOAD_CLASS(CLASS,MODE,X) \ #define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
(TARGET_ARM ? \
(((MODE) == HImode && ! arm_arch4 && TARGET_MMU_TRAPS \ (((MODE) == HImode && ! arm_arch4 && TARGET_MMU_TRAPS \
&& (GET_CODE (X) == MEM \ && (GET_CODE (X) == MEM \
|| ((GET_CODE (X) == REG || GET_CODE (X) == SUBREG) \ || ((GET_CODE (X) == REG || GET_CODE (X) == SUBREG) \
&& true_regnum (X) == -1))) \ && true_regnum (X) == -1))) \
? GENERAL_REGS : NO_REGS) ? GENERAL_REGS : NO_REGS) \
: THUMB_SECONDARY_INPUT_RELOAD_CLASS (CLASS, MODE, X))
/* Try a machine-dependent way of reloading an illegitimate address /* Try a machine-dependent way of reloading an illegitimate address
operand. If we find one, push the reload and jump to WIN. This operand. If we find one, push the reload and jump to WIN. This
...@@ -1007,7 +1188,7 @@ enum reg_class ...@@ -1007,7 +1188,7 @@ enum reg_class
For the ARM, we wish to handle large displacements off a base For the ARM, we wish to handle large displacements off a base
register by splitting the addend across a MOV and the mem insn. register by splitting the addend across a MOV and the mem insn.
This can cut the number of reloads needed. */ This can cut the number of reloads needed. */
#define LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \ #define ARM_LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND, WIN) \
do \ do \
{ \ { \
if (GET_CODE (X) == PLUS \ if (GET_CODE (X) == PLUS \
...@@ -1024,19 +1205,21 @@ enum reg_class ...@@ -1024,19 +1205,21 @@ enum reg_class
else if (MODE == SImode \ else if (MODE == SImode \
|| (MODE == SFmode && TARGET_SOFT_FLOAT) \ || (MODE == SFmode && TARGET_SOFT_FLOAT) \
|| ((MODE == HImode || MODE == QImode) && ! arm_arch4)) \ || ((MODE == HImode || MODE == QImode) && ! arm_arch4)) \
/* Need to be careful, -4096 is not a valid offset */ \ /* Need to be careful, -4096 is not a valid offset. */ \
low = val >= 0 ? (val & 0xfff) : -((-val) & 0xfff); \ low = val >= 0 ? (val & 0xfff) : -((-val) & 0xfff); \
else if ((MODE == HImode || MODE == QImode) && arm_arch4) \ else if ((MODE == HImode || MODE == QImode) && arm_arch4) \
/* Need to be careful, -256 is not a valid offset */ \ /* Need to be careful, -256 is not a valid offset. */ \
low = val >= 0 ? (val & 0xff) : -((-val) & 0xff); \ low = val >= 0 ? (val & 0xff) : -((-val) & 0xff); \
else if (GET_MODE_CLASS (MODE) == MODE_FLOAT \ else if (GET_MODE_CLASS (MODE) == MODE_FLOAT \
&& TARGET_HARD_FLOAT) \ && TARGET_HARD_FLOAT) \
/* Need to be careful, -1024 is not a valid offset */ \ /* Need to be careful, -1024 is not a valid offset. */ \
low = val >= 0 ? (val & 0x3ff) : -((-val) & 0x3ff); \ low = val >= 0 ? (val & 0x3ff) : -((-val) & 0x3ff); \
else \ else \
break; \ break; \
\ \
high = ((((val - low) & 0xffffffffUL) ^ 0x80000000UL) - 0x80000000UL);\ high = ((((val - low) & (unsigned long)0xffffffff) \
^ (unsigned long)0x80000000) \
- (unsigned long)0x80000000); \
/* Check for overflow or zero */ \ /* Check for overflow or zero */ \
if (low == 0 || high == 0 || (high + low != val)) \ if (low == 0 || high == 0 || (high + low != val)) \
break; \ break; \
...@@ -1055,6 +1238,38 @@ enum reg_class ...@@ -1055,6 +1238,38 @@ enum reg_class
} \ } \
while (0) while (0)
/* ??? If an HImode FP+large_offset address is converted to an HImode
SP+large_offset address, then reload won't know how to fix it. It sees
only that SP isn't valid for HImode, and so reloads the SP into an index
register, but the resulting address is still invalid because the offset
is too big. We fix it here instead by reloading the entire address. */
/* We could probably achieve better results by defining PROMOTE_MODE to help
cope with the variances between the Thumb's signed and unsigned byte and
halfword load instructions. */
#define THUMB_LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \
{ \
if (GET_CODE (X) == PLUS \
&& GET_MODE_SIZE (MODE) < 4 \
&& GET_CODE (XEXP (X, 0)) == REG \
&& XEXP (X, 0) == stack_pointer_rtx \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& ! LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \
{ \
rtx orig_X = X; \
X = copy_rtx (X); \
push_reload (orig_X, NULL_RTX, &X, NULL_PTR, \
BASE_REG_CLASS, \
Pmode, VOIDmode, 0, 0, OPNUM, TYPE); \
goto WIN; \
} \
}
#define LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \
if (TARGET_ARM) \
ARM_LEGITIMIZE_RELOAD_ADDRESS (X, MODE, OPNUM, TYPE, IND_LEVELS, WIN); \
else \
THUMB_LEGITIMIZE_RELOAD_ADDRESS (X, MODE, OPNUM, TYPE, IND_LEVELS, WIN)
/* Return the maximum number of consecutive registers /* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. needed to represent mode MODE in a register of class CLASS.
ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */ ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */
...@@ -1062,10 +1277,12 @@ enum reg_class ...@@ -1062,10 +1277,12 @@ enum reg_class
((CLASS) == FPU_REGS ? 1 : NUM_REGS (MODE)) ((CLASS) == FPU_REGS ? 1 : NUM_REGS (MODE))
/* Moves between FPU_REGS and GENERAL_REGS are two memory insns. */ /* Moves between FPU_REGS and GENERAL_REGS are two memory insns. */
#define REGISTER_MOVE_COST(CLASS1, CLASS2) \ #define REGISTER_MOVE_COST(FROM, TO) \
((((CLASS1) == FPU_REGS && (CLASS2) != FPU_REGS) \ (TARGET_ARM ? \
|| ((CLASS2) == FPU_REGS && (CLASS1) != FPU_REGS)) \ ((FROM) == FPU_REGS && (TO) != FPU_REGS ? 20 : \
? 20 : 2) (FROM) != FPU_REGS && (TO) == FPU_REGS ? 20 : 2) \
: \
((FROM) == HI_REGS || (TO) == HI_REGS) ? 4 : 2)
/* Stack layout; function entry, exit and calling. */ /* Stack layout; function entry, exit and calling. */
...@@ -1087,8 +1304,9 @@ enum reg_class ...@@ -1087,8 +1304,9 @@ enum reg_class
/* If we generate an insn to push BYTES bytes, /* If we generate an insn to push BYTES bytes,
this says how many the stack pointer really advances by. */ this says how many the stack pointer really advances by. */
/* The push insns do not do this rounding implicitly. So don't define this. */ /* The push insns do not do this rounding implicitly.
/* #define PUSH_ROUNDING(NPUSHED) (((NPUSHED) + 3) & ~3) */ So don't define this. */
/* #define PUSH_ROUNDING(NPUSHED) ROUND_UP (NPUSHED) */
/* Define this if the maximum size of all the outgoing args is to be /* Define this if the maximum size of all the outgoing args is to be
accumulated and pushed during the prologue. The amount can be accumulated and pushed during the prologue. The amount can be
...@@ -1096,7 +1314,7 @@ enum reg_class ...@@ -1096,7 +1314,7 @@ enum reg_class
#define ACCUMULATE_OUTGOING_ARGS 1 #define ACCUMULATE_OUTGOING_ARGS 1
/* Offset of first parameter from the argument pointer register value. */ /* Offset of first parameter from the argument pointer register value. */
#define FIRST_PARM_OFFSET(FNDECL) 4 #define FIRST_PARM_OFFSET(FNDECL) (TARGET_ARM ? 4 : 0)
/* Value is the number of byte of arguments automatically /* Value is the number of byte of arguments automatically
popped when returning from a subroutine call. popped when returning from a subroutine call.
...@@ -1112,9 +1330,9 @@ enum reg_class ...@@ -1112,9 +1330,9 @@ enum reg_class
/* Define how to find the value returned by a library function /* Define how to find the value returned by a library function
assuming the value has mode MODE. */ assuming the value has mode MODE. */
#define LIBCALL_VALUE(MODE) \ #define LIBCALL_VALUE(MODE) \
(GET_MODE_CLASS (MODE) == MODE_FLOAT && TARGET_HARD_FLOAT \ (TARGET_ARM && TARGET_HARD_FLOAT && GET_MODE_CLASS (MODE) == MODE_FLOAT \
? gen_rtx_REG (MODE, 16) \ ? gen_rtx_REG (MODE, FIRST_ARM_FP_REGNUM) \
: gen_rtx_REG (MODE, 0)) : gen_rtx_REG (MODE, ARG_REGISTER (1)))
/* Define how to find the value returned by a function. /* Define how to find the value returned by a function.
VALTYPE is the data type of the value (as a tree). VALTYPE is the data type of the value (as a tree).
...@@ -1126,7 +1344,8 @@ enum reg_class ...@@ -1126,7 +1344,8 @@ enum reg_class
/* 1 if N is a possible register number for a function value. /* 1 if N is a possible register number for a function value.
On the ARM, only r0 and f0 can return results. */ On the ARM, only r0 and f0 can return results. */
#define FUNCTION_VALUE_REGNO_P(REGNO) \ #define FUNCTION_VALUE_REGNO_P(REGNO) \
((REGNO) == 0 || (((REGNO) == 16) && TARGET_HARD_FLOAT)) ((REGNO) == ARG_REGISTER (1) \
|| (TARGET_ARM && ((REGNO) == FIRST_ARM_FP_REGNUM) && TARGET_HARD_FLOAT))
/* How large values are returned */ /* How large values are returned */
/* A C expression which can inhibit the returning of certain function values /* A C expression which can inhibit the returning of certain function values
...@@ -1138,22 +1357,35 @@ enum reg_class ...@@ -1138,22 +1357,35 @@ enum reg_class
than a word, or if they contain elements offset from zero in the struct. */ than a word, or if they contain elements offset from zero in the struct. */
#define DEFAULT_PCC_STRUCT_RETURN 0 #define DEFAULT_PCC_STRUCT_RETURN 0
/* Flags for the call/call_value rtl operations set up by function_arg. */
#define CALL_NORMAL 0x00000000 /* No special processing. */
#define CALL_LONG 0x00000001 /* Always call indirect. */
#define CALL_SHORT 0x00000002 /* Never call indirect. */
/* A C structure for machine-specific, per-function data. This is added
to the cfun structure. */
struct machine_function
{
/* Records __builtin_return address. */
struct rtx_def *ra_rtx;
/* Additionsl stack adjustment in __builtin_eh_throw. */
struct rtx_def *eh_epilogue_sp_ofs;
/* Records if LR has to be saved for far jumps. */
int far_jump_used;
/* Records if ARG_POINTER was ever live. */
int arg_pointer_live;
};
/* A C type for declaring a variable that is used as the first argument of /* A C type for declaring a variable that is used as the first argument of
`FUNCTION_ARG' and other related values. For some target machines, the `FUNCTION_ARG' and other related values. For some target machines, the
type `int' suffices and can hold the number of bytes of argument so far. */ type `int' suffices and can hold the number of bytes of argument so far. */
typedef struct typedef struct
{ {
/* This is the number of argument registers scanned so far. */ /* This is the number of registers of arguments scanned so far. */
int nregs; int nregs;
/* instructions on how to process this call. */ /* One of CALL_NORMAL, CALL_LONG or CALL_SHORT . */
int call_cookie; int call_cookie;
} } CUMULATIVE_ARGS;
CUMULATIVE_ARGS;
/* Flags for the call_cookie field of CUMULATIVE_ARGS. */
#define CALL_NORMAL 0 /* No special processing. */
#define CALL_LONG 1 /* Always call indirect. */
#define CALL_SHORT 2 /* Never call indirect. */
/* Define where to put the arguments to a function. /* Define where to put the arguments to a function.
Value is zero to push the argument on the stack, Value is zero to push the argument on the stack,
...@@ -1225,7 +1457,14 @@ CUMULATIVE_ARGS; ...@@ -1225,7 +1457,14 @@ CUMULATIVE_ARGS;
/* Generate assembly output for the start of a function. */ /* Generate assembly output for the start of a function. */
#define FUNCTION_PROLOGUE(STREAM, SIZE) \ #define FUNCTION_PROLOGUE(STREAM, SIZE) \
output_func_prologue ((STREAM), (SIZE)) do \
{ \
if (TARGET_ARM) \
output_arm_prologue (STREAM, SIZE); \
else \
output_thumb_prologue (STREAM); \
} \
while (0)
/* If your target environment doesn't prefix user functions with an /* If your target environment doesn't prefix user functions with an
underscore, you may wish to re-define this to prevent any conflicts. underscore, you may wish to re-define this to prevent any conflicts.
...@@ -1250,7 +1489,7 @@ CUMULATIVE_ARGS; ...@@ -1250,7 +1489,7 @@ CUMULATIVE_ARGS;
The ``mov ip,lr'' seems like a good idea to stick with cc convention. The ``mov ip,lr'' seems like a good idea to stick with cc convention.
``prof'' doesn't seem to mind about this! */ ``prof'' doesn't seem to mind about this! */
#define FUNCTION_PROFILER(STREAM, LABELNO) \ #define ARM_FUNCTION_PROFILER(STREAM, LABELNO) \
{ \ { \
char temp[20]; \ char temp[20]; \
rtx sym; \ rtx sym; \
...@@ -1264,6 +1503,19 @@ CUMULATIVE_ARGS; ...@@ -1264,6 +1503,19 @@ CUMULATIVE_ARGS;
ASM_OUTPUT_INT (STREAM, sym); \ ASM_OUTPUT_INT (STREAM, sym); \
} }
#define THUMB_FUNCTION_PROFILER(STREAM, LABELNO) \
{ \
fprintf (STREAM, "\tmov\\tip, lr\n"); \
fprintf (STREAM, "\tbl\tmcount\n"); \
fprintf (STREAM, "\t.word\tLP%d\n", LABELNO); \
}
#define FUNCTION_PROFILER(STREAM, LABELNO) \
if (TARGET_ARM) \
ARM_FUNCTION_PROFILER (STREAM, LABELNO) \
else \
THUMB_FUNCTION_PROFILER (STREAM, LABELNO)
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, /* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in the stack pointer does not matter. The value is tested only in
functions that have frame pointers. functions that have frame pointers.
...@@ -1279,7 +1531,8 @@ CUMULATIVE_ARGS; ...@@ -1279,7 +1531,8 @@ CUMULATIVE_ARGS;
/* Determine if the epilogue should be output as RTL. /* Determine if the epilogue should be output as RTL.
You should override this if you define FUNCTION_EXTRA_EPILOGUE. */ You should override this if you define FUNCTION_EXTRA_EPILOGUE. */
#define USE_RETURN_INSN(ISCOND) use_return_insn (ISCOND) #define USE_RETURN_INSN(ISCOND) \
(TARGET_ARM ? use_return_insn (ISCOND) : 0)
/* Definitions for register eliminations. /* Definitions for register eliminations.
...@@ -1292,27 +1545,36 @@ CUMULATIVE_ARGS; ...@@ -1292,27 +1545,36 @@ CUMULATIVE_ARGS;
arg pointer register can often be eliminated in favor of the stack arg pointer register can often be eliminated in favor of the stack
pointer register. Secondly, the pseudo frame pointer register can always pointer register. Secondly, the pseudo frame pointer register can always
be eliminated; it is replaced with either the stack or the real frame be eliminated; it is replaced with either the stack or the real frame
pointer. */ pointer. Note we have to use {ARM|THUMB}_HARD_FRAME_POINTER_REGNUM
because the defintion of HARD_FRAME_POINTER_REGNUM is not a constant. */
#define ELIMINABLE_REGS \ #define ELIMINABLE_REGS \
{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM },\
{ ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM },\
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ { ARG_POINTER_REGNUM, ARM_HARD_FRAME_POINTER_REGNUM },\
{ FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }} { ARG_POINTER_REGNUM, THUMB_HARD_FRAME_POINTER_REGNUM },\
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM },\
{ FRAME_POINTER_REGNUM, ARM_HARD_FRAME_POINTER_REGNUM },\
{ FRAME_POINTER_REGNUM, THUMB_HARD_FRAME_POINTER_REGNUM }}
/* Given FROM and TO register numbers, say whether this elimination is allowed. /* Given FROM and TO register numbers, say whether this elimination is
Frame pointer elimination is automatically handled. allowed. Frame pointer elimination is automatically handled.
All eliminations are permissible. Note that ARG_POINTER_REGNUM and All eliminations are permissible. Note that ARG_POINTER_REGNUM and
HARD_FRAME_POINTER_REGNUM are in fact the same thing. If we need a frame HARD_FRAME_POINTER_REGNUM are in fact the same thing. If we need a frame
pointer, we must eliminate FRAME_POINTER_REGNUM into pointer, we must eliminate FRAME_POINTER_REGNUM into
HARD_FRAME_POINTER_REGNUM and not into STACK_POINTER_REGNUM. */ HARD_FRAME_POINTER_REGNUM and not into STACK_POINTER_REGNUM or
ARG_POINTER_REGNUM. */
#define CAN_ELIMINATE(FROM, TO) \ #define CAN_ELIMINATE(FROM, TO) \
(((TO) == STACK_POINTER_REGNUM && frame_pointer_needed) ? 0 : 1) (((TO) == FRAME_POINTER_REGNUM && (FROM) == ARG_POINTER_REGNUM) ? 0 : \
((TO) == STACK_POINTER_REGNUM && frame_pointer_needed) ? 0 : \
/* Define the offset between two registers, one to be eliminated, and the other ((TO) == ARM_HARD_FRAME_POINTER_REGNUM && TARGET_THUMB) ? 0 : \
its replacement, at the start of a routine. */ ((TO) == THUMB_HARD_FRAME_POINTER_REGNUM && TARGET_ARM) ? 0 : \
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ 1)
/* Define the offset between two registers, one to be eliminated, and the
other its replacement, at the start of a routine. */
#define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \ { \
int volatile_func = arm_volatile_func (); \ int volatile_func = arm_volatile_func (); \
if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\ if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
...@@ -1320,7 +1582,7 @@ CUMULATIVE_ARGS; ...@@ -1320,7 +1582,7 @@ CUMULATIVE_ARGS;
else if ((FROM) == FRAME_POINTER_REGNUM \ else if ((FROM) == FRAME_POINTER_REGNUM \
&& (TO) == STACK_POINTER_REGNUM) \ && (TO) == STACK_POINTER_REGNUM) \
(OFFSET) = current_function_outgoing_args_size \ (OFFSET) = current_function_outgoing_args_size \
+ ((get_frame_size () + 3) & ~3); \ + ROUND_UP (get_frame_size ()); \
else \ else \
{ \ { \
int regno; \ int regno; \
...@@ -1332,15 +1594,21 @@ CUMULATIVE_ARGS; ...@@ -1332,15 +1594,21 @@ CUMULATIVE_ARGS;
for (regno = 0; regno <= 10; regno++) \ for (regno = 0; regno <= 10; regno++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \ if (regs_ever_live[regno] && ! call_used_regs[regno]) \
saved_hard_reg = 1, offset += 4; \ saved_hard_reg = 1, offset += 4; \
if (! TARGET_APCS_FRAME \
&& ! frame_pointer_needed \
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM] \
&& ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) \
saved_hard_reg = 1, offset += 4; \
/* PIC register is a fixed reg, so call_used_regs set. */ \ /* PIC register is a fixed reg, so call_used_regs set. */ \
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) \ if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) \
saved_hard_reg = 1, offset += 4; \ saved_hard_reg = 1, offset += 4; \
for (regno = 16; regno <=23; regno++) \ for (regno = FIRST_ARM_FP_REGNUM; \
regno <= LAST_ARM_FP_REGNUM; regno++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \ if (regs_ever_live[regno] && ! call_used_regs[regno]) \
offset += 12; \ offset += 12; \
} \ } \
if ((FROM) == FRAME_POINTER_REGNUM) \ if ((FROM) == FRAME_POINTER_REGNUM) \
(OFFSET) = -offset; \ (OFFSET) = - offset; \
else \ else \
{ \ { \
if (! frame_pointer_needed) \ if (! frame_pointer_needed) \
...@@ -1349,11 +1617,58 @@ CUMULATIVE_ARGS; ...@@ -1349,11 +1617,58 @@ CUMULATIVE_ARGS;
&& (regs_ever_live[LR_REGNUM] || saved_hard_reg)) \ && (regs_ever_live[LR_REGNUM] || saved_hard_reg)) \
offset += 4; \ offset += 4; \
offset += current_function_outgoing_args_size; \ offset += current_function_outgoing_args_size; \
(OFFSET) = ((get_frame_size () + 3) & ~3) + offset; \ (OFFSET) = ROUND_UP (get_frame_size ()) + offset; \
} \
} \
}
/* Note: This macro must match the code in thumb_function_prologue(). */
#define THUMB_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \
(OFFSET) = 0; \
if ((FROM) == ARG_POINTER_REGNUM) \
{ \
int count_regs = 0; \
int regno; \
for (regno = 8; regno < 13; regno ++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
count_regs ++; \
if (count_regs) \
(OFFSET) += 4 * count_regs; \
count_regs = 0; \
for (regno = 0; regno <= LAST_LO_REGNUM; regno ++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
count_regs ++; \
if (count_regs || ! leaf_function_p () || thumb_far_jump_used_p (0))\
(OFFSET) += 4 * (count_regs + 1); \
if (TARGET_BACKTRACE) \
{ \
if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) \
(OFFSET) += 20; \
else \
(OFFSET) += 16; \
} \
} \ } \
if ((TO) == STACK_POINTER_REGNUM) \
{ \
(OFFSET) += current_function_outgoing_args_size; \
(OFFSET) += ROUND_UP (get_frame_size ()); \
} \ } \
} }
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
if (TARGET_ARM) \
ARM_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET) \
else \
THUMB_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET)
/* Special case handling of the location of arguments passed on the stack. */
#define DEBUGGER_ARG_OFFSET(value, addr) value ? value : arm_debugger_arg_offset (value, addr)
/* Initialize data used by insn expanders. This is called from insn_emit,
once for every function before code is generated. */
#define INIT_EXPANDERS arm_init_expanders ()
/* Output assembler code for a block containing the constant parts /* Output assembler code for a block containing the constant parts
of a trampoline, leaving space for the variable parts. of a trampoline, leaving space for the variable parts.
...@@ -1364,20 +1679,47 @@ CUMULATIVE_ARGS; ...@@ -1364,20 +1679,47 @@ CUMULATIVE_ARGS;
.word static chain value .word static chain value
.word function's address .word function's address
??? FIXME: When the trampoline returns, r8 will be clobbered. */ ??? FIXME: When the trampoline returns, r8 will be clobbered. */
#define TRAMPOLINE_TEMPLATE(FILE) \ #define ARM_TRAMPOLINE_TEMPLATE(FILE) \
{ \
asm_fprintf (FILE, "\tldr\t%r, [%r, #0]\n", \
STATIC_CHAIN_REGNUM, PC_REGNUM); \
asm_fprintf (FILE, "\tldr\t%r, [%r, #0]\n", \
PC_REGNUM, PC_REGNUM); \
ASM_OUTPUT_INT (FILE, const0_rtx); \
ASM_OUTPUT_INT (FILE, const0_rtx); \
}
/* On the Thumb we always switch into ARM mode to execute the trampoline.
Why - because it is easier. This code will always be branched to via
a BX instruction and since the compiler magically generates the address
of the function the linker has no opportunity to ensure that the
bottom bit is set. Thus the processor will be in ARM mode when it
reaches this code. So we duplicate the ARM trampoline code and add
a switch into Thumb mode as well. */
#define THUMB_TRAMPOLINE_TEMPLATE(FILE) \
{ \ { \
fprintf ((FILE), "\tldr\t%s%s, [%s%s, #0]\n", \ fprintf (FILE, "\t.code 32\n"); \
REGISTER_PREFIX, reg_names[STATIC_CHAIN_REGNUM], \ fprintf (FILE, ".Ltrampoline_start:\n"); \
REGISTER_PREFIX, reg_names[PC_REGNUM]); \ asm_fprintf (FILE, "\tldr\t%r, [%r, #8]\n", \
fprintf ((FILE), "\tldr\t%s%s, [%s%s, #0]\n", \ STATIC_CHAIN_REGNUM, PC_REGNUM); \
REGISTER_PREFIX, reg_names[PC_REGNUM], \ asm_fprintf (FILE, "\tldr\t%r, [%r, #8]\n", \
REGISTER_PREFIX, reg_names[PC_REGNUM]); \ IP_REGNUM, PC_REGNUM); \
ASM_OUTPUT_INT ((FILE), const0_rtx); \ asm_fprintf (FILE, "\torr\t%r, %r, #1\n", \
ASM_OUTPUT_INT ((FILE), const0_rtx); \ IP_REGNUM, IP_REGNUM); \
asm_fprintf (FILE, "\tbx\t%r\n", IP_REGNUM); \
fprintf (FILE, "\t.word\t0\n"); \
fprintf (FILE, "\t.word\t0\n"); \
fprintf (FILE, "\t.code 16\n"); \
} }
#define TRAMPOLINE_TEMPLATE(FILE) \
if (TARGET_ARM) \
ARM_TRAMPOLINE_TEMPLATE (FILE) \
else \
THUMB_TRAMPOLINE_TEMPLATE (FILE)
/* Length in units of the trampoline for entering a nested function. */ /* Length in units of the trampoline for entering a nested function. */
#define TRAMPOLINE_SIZE 16 #define TRAMPOLINE_SIZE (TARGET_ARM ? 16 : 24)
/* Alignment required for a trampoline in units. */ /* Alignment required for a trampoline in units. */
#define TRAMPOLINE_ALIGN 4 #define TRAMPOLINE_ALIGN 4
...@@ -1387,19 +1729,18 @@ CUMULATIVE_ARGS; ...@@ -1387,19 +1729,18 @@ CUMULATIVE_ARGS;
CXT is an RTX for the static chain value for the function. */ CXT is an RTX for the static chain value for the function. */
#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ #define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
{ \ { \
emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 8)), \ emit_move_insn \
(CXT)); \ (gen_rtx_MEM (SImode, plus_constant (TRAMP, TARGET_ARM ? 8 : 16)), CXT); \
emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 12)), \ emit_move_insn \
(FNADDR)); \ (gen_rtx_MEM (SImode, plus_constant (TRAMP, TARGET_ARM ? 12 : 20)), FNADDR); \
} }
/* Addressing modes, and classification of registers for them. */ /* Addressing modes, and classification of registers for them. */
#define HAVE_POST_INCREMENT 1 #define HAVE_POST_INCREMENT 1
#define HAVE_PRE_INCREMENT 1 #define HAVE_PRE_INCREMENT TARGET_ARM
#define HAVE_POST_DECREMENT 1 #define HAVE_POST_DECREMENT TARGET_ARM
#define HAVE_PRE_DECREMENT 1 #define HAVE_PRE_DECREMENT TARGET_ARM
/* Macros to check register numbers against specific register classes. */ /* Macros to check register numbers against specific register classes. */
...@@ -1407,21 +1748,33 @@ CUMULATIVE_ARGS; ...@@ -1407,21 +1748,33 @@ CUMULATIVE_ARGS;
They give nonzero only if REGNO is a hard reg of the suitable class They give nonzero only if REGNO is a hard reg of the suitable class
or a pseudo reg currently allocated to a suitable hard reg. or a pseudo reg currently allocated to a suitable hard reg.
Since they use reg_renumber, they are safe only once reg_renumber Since they use reg_renumber, they are safe only once reg_renumber
has been allocated, which happens in local-alloc.c. has been allocated, which happens in local-alloc.c. */
#define TEST_REGNO(R, TEST, VALUE) \
On the ARM, don't allow the pc to be used. */ ((R TEST VALUE) || ((unsigned) reg_renumber[R] TEST VALUE))
/* On the ARM, don't allow the pc to be used. */
#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \
(TARGET_THUMB ? \
( TEST_REGNO (REGNO, <=, LAST_LO_REGNUM) \
|| (GET_MODE_SIZE (MODE) >= 4 \
&& TEST_REGNO (REGNO, ==, STACK_POINTER_REGNUM))) \
:( \
TEST_REGNO (REGNO, <, PC_REGNUM) \
|| TEST_REGNO (REGNO, ==, FRAME_POINTER_REGNUM) \
|| TEST_REGNO (REGNO, ==, ARG_POINTER_REGNUM)))
/* This is like REGNO_MODE_OF_FOR_BASE_P, except that in Thumb mode
the stack pointer is always acceptable, hence the passing of SImode */
#define REGNO_OK_FOR_BASE_P(REGNO) \ #define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < 15 || (REGNO) == FRAME_POINTER_REGNUM \ REGNO_MODE_OK_FOR_BASE_P (REGNO, SImode)
|| (REGNO) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[(REGNO)] < 15 \ /* We play tricks with REGNO_MODE_OK... here, so that for ARM the macros
|| (unsigned) reg_renumber[(REGNO)] == FRAME_POINTER_REGNUM \ are the same, but for Thumb only registers 0 - 7 are OK. */
|| (unsigned) reg_renumber[(REGNO)] == ARG_POINTER_REGNUM)
#define REGNO_OK_FOR_INDEX_P(REGNO) \ #define REGNO_OK_FOR_INDEX_P(REGNO) \
REGNO_OK_FOR_BASE_P(REGNO) REGNO_MODE_OK_FOR_BASE_P (REGNO, QImode)
/* Maximum number of registers that can appear in a valid memory address. /* Maximum number of registers that can appear in a valid memory address.
Shifts in addresses can't be by a register. */ Shifts in addresses can't be by a register. */
#define MAX_REGS_PER_ADDRESS 2 #define MAX_REGS_PER_ADDRESS 2
/* Recognize any constant value that is a valid address. */ /* Recognize any constant value that is a valid address. */
...@@ -1430,15 +1783,14 @@ CUMULATIVE_ARGS; ...@@ -1430,15 +1783,14 @@ CUMULATIVE_ARGS;
#ifdef AOF_ASSEMBLER #ifdef AOF_ASSEMBLER
#define CONSTANT_ADDRESS_P(X) \ #define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == SYMBOL_REF \ (GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X))
&& CONSTANT_POOL_ADDRESS_P (X))
#else #else
#define CONSTANT_ADDRESS_P(X) \ #define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == SYMBOL_REF \ (GET_CODE (X) == SYMBOL_REF \
&& (CONSTANT_POOL_ADDRESS_P (X) \ && (CONSTANT_POOL_ADDRESS_P (X) \
|| (optimize > 0 && SYMBOL_REF_FLAG (X)))) || (TARGET_ARM && optimize > 0 && SYMBOL_REF_FLAG (X))))
#endif /* AOF_ASSEMBLER */ #endif /* AOF_ASSEMBLER */
...@@ -1450,7 +1802,15 @@ CUMULATIVE_ARGS; ...@@ -1450,7 +1802,15 @@ CUMULATIVE_ARGS;
constant pool XXX. constant pool XXX.
When generating pic allow anything. */ When generating pic allow anything. */
#define LEGITIMATE_CONSTANT_P(X) (flag_pic || ! label_mentioned_p (X)) #define ARM_LEGITIMATE_CONSTANT_P(X) (flag_pic || ! label_mentioned_p (X))
#define THUMB_LEGITIMATE_CONSTANT_P(X) \
( GET_CODE (X) == CONST_INT \
|| GET_CODE (X) == CONST_DOUBLE \
|| CONSTANT_ADDRESS_P (X))
#define LEGITIMATE_CONSTANT_P(X) \
(TARGET_ARM ? ARM_LEGITIMATE_CONSTANT_P (X) : THUMB_LEGITIMATE_CONSTANT_P (X))
/* Special characters prefixed to function names /* Special characters prefixed to function names
in order to encode attribute like information. in order to encode attribute like information.
...@@ -1475,7 +1835,6 @@ CUMULATIVE_ARGS; ...@@ -1475,7 +1835,6 @@ CUMULATIVE_ARGS;
#define ARM_NAME_ENCODING_LENGTHS \ #define ARM_NAME_ENCODING_LENGTHS \
case SHORT_CALL_FLAG_CHAR: return 1; \ case SHORT_CALL_FLAG_CHAR: return 1; \
case LONG_CALL_FLAG_CHAR: return 1; \ case LONG_CALL_FLAG_CHAR: return 1; \
case '*': return 1; \
SUBTARGET_NAME_ENCODING_LENGTHS SUBTARGET_NAME_ENCODING_LENGTHS
/* This has to be handled by a function because more than part of the /* This has to be handled by a function because more than part of the
...@@ -1538,22 +1897,36 @@ CUMULATIVE_ARGS; ...@@ -1538,22 +1897,36 @@ CUMULATIVE_ARGS;
The symbol REG_OK_STRICT causes the latter definition to be used. */ The symbol REG_OK_STRICT causes the latter definition to be used. */
#ifndef REG_OK_STRICT #ifndef REG_OK_STRICT
#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
(TARGET_THUMB ? \
( REGNO (X) <= LAST_LO_REGNUM \
|| REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| (GET_MODE_SIZE (MODE) >= 4 \
&& (REGNO (X) == STACK_POINTER_REGNUM \
|| (X) == hard_frame_pointer_rtx \
|| (X) == arg_pointer_rtx))) \
:( \
REGNO (X) <= LAST_ARM_REGNUM \
|| REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| REGNO (X) == FRAME_POINTER_REGNUM \
|| REGNO (X) == ARG_POINTER_REGNUM))
/* Nonzero if X is a hard reg that can be used as a base reg /* Nonzero if X is a hard reg that can be used as a base reg
or if it is a pseudo reg. */ or if it is a pseudo reg. */
#define REG_OK_FOR_BASE_P(X) \ #define REG_OK_FOR_BASE_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \ REG_MODE_OK_FOR_BASE_P (X, SImode)
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
/* Nonzero if X is a hard reg that can be used as an index /* Nonzero if X is a hard reg that can be used as an index
or if it is a pseudo reg. */ or if it is a pseudo reg. On the Thumb, the stack pointer
is not suitable. */
#define REG_OK_FOR_INDEX_P(X) \ #define REG_OK_FOR_INDEX_P(X) \
REG_OK_FOR_BASE_P(X) REG_MODE_OK_FOR_BASE_P (X, QImode)
/* Just like REG_OK_FOR_BASE_P except that we also allow the PC. */
#define REG_OK_FOR_PRE_POST_P(X) \ #define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \ (REG_OK_FOR_BASE_P (X) || REGNO(X) == PC_REGNUM)
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
#else #else /* REG_OK_STRICT */
/* Nonzero if X is a hard reg that can be used as a base reg. */ /* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) #define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
...@@ -1561,13 +1934,11 @@ CUMULATIVE_ARGS; ...@@ -1561,13 +1934,11 @@ CUMULATIVE_ARGS;
/* Nonzero if X is a hard reg that can be used as an index. */ /* Nonzero if X is a hard reg that can be used as an index. */
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) #define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
/* Just like REG_OK_FOR_BASE_P except that we also allow the PC. */
#define REG_OK_FOR_PRE_POST_P(X) \ #define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || (unsigned) reg_renumber[REGNO (X)] < 16 \ (REG_OK_FOR_BASE_P (X) || TEST_REGNO (REGNO (X), ==, PC_REGNUM))
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == FRAME_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == ARG_POINTER_REGNUM)
#endif #endif /* REG_OK_STRICT */
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
that is a valid memory address for an instruction. that is a valid memory address for an instruction.
...@@ -1575,6 +1946,8 @@ CUMULATIVE_ARGS; ...@@ -1575,6 +1946,8 @@ CUMULATIVE_ARGS;
that wants to use this address. that wants to use this address.
The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS. */ The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS. */
/* --------------------------------arm version----------------------------- */
#define BASE_REGISTER_RTX_P(X) \ #define BASE_REGISTER_RTX_P(X) \
(GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X))
...@@ -1640,7 +2013,7 @@ CUMULATIVE_ARGS; ...@@ -1640,7 +2013,7 @@ CUMULATIVE_ARGS;
INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool. INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool.
Allow REG-only and AUTINC-REG if handling TImode or HImode. Other symbol Allow REG-only and AUTINC-REG if handling TImode or HImode. Other symbol
refs must be forced though a static cell to ensure addressability. */ refs must be forced though a static cell to ensure addressability. */
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ #define ARM_GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \
{ \ { \
if (BASE_REGISTER_RTX_P (X)) \ if (BASE_REGISTER_RTX_P (X)) \
goto LABEL; \ goto LABEL; \
...@@ -1669,8 +2042,8 @@ CUMULATIVE_ARGS; ...@@ -1669,8 +2042,8 @@ CUMULATIVE_ARGS;
} \ } \
else if (GET_CODE (X) == PLUS) \ else if (GET_CODE (X) == PLUS) \
{ \ { \
rtx xop0 = XEXP(X,0); \ rtx xop0 = XEXP (X, 0); \
rtx xop1 = XEXP(X,1); \ rtx xop1 = XEXP (X, 1); \
\ \
if (BASE_REGISTER_RTX_P (xop0)) \ if (BASE_REGISTER_RTX_P (xop0)) \
GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop0), xop1, LABEL); \ GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop0), xop1, LABEL); \
...@@ -1699,6 +2072,119 @@ CUMULATIVE_ARGS; ...@@ -1699,6 +2072,119 @@ CUMULATIVE_ARGS;
goto LABEL; \ goto LABEL; \
} }
/* ---------------------thumb version----------------------------------*/
#define LEGITIMATE_OFFSET(MODE, VAL) \
(GET_MODE_SIZE (MODE) == 1 ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \
: GET_MODE_SIZE (MODE) == 2 ? ((unsigned HOST_WIDE_INT) (VAL) < 64 \
&& ((VAL) & 1) == 0) \
: ((VAL) >= 0 && ((VAL) + GET_MODE_SIZE (MODE)) <= 128 \
&& ((VAL) & 3) == 0))
/* The AP may be eliminated to either the SP or the FP, so we use the
least common denominator, e.g. SImode, and offsets from 0 to 64. */
/* ??? Verify whether the above is the right approach. */
/* ??? Also, the FP may be eliminated to the SP, so perhaps that
needs special handling also. */
/* ??? Look at how the mips16 port solves this problem. It probably uses
better ways to solve some of these problems. */
/* Although it is not incorrect, we don't accept QImode and HImode
addresses based on the frame pointer or arg pointer until the reload pass starts.
This is so that eliminating such addresses into stack based ones
won't produce impossible code. */
#define THUMB_GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \
{ \
/* ??? Not clear if this is right. Experiment. */ \
if (GET_MODE_SIZE (MODE) < 4 \
&& ! (reload_in_progress || reload_completed) \
&& ( reg_mentioned_p (frame_pointer_rtx, X) \
|| reg_mentioned_p (arg_pointer_rtx, X) \
|| reg_mentioned_p (virtual_incoming_args_rtx, X) \
|| reg_mentioned_p (virtual_outgoing_args_rtx, X) \
|| reg_mentioned_p (virtual_stack_dynamic_rtx, X) \
|| reg_mentioned_p (virtual_stack_vars_rtx, X))) \
; \
/* Accept any base register. SP only in SImode or larger. */ \
else if (GET_CODE (X) == REG && REG_MODE_OK_FOR_BASE_P (X, MODE)) \
goto WIN; \
/* This is PC relative data before MACHINE_DEPENDENT_REORG runs. */ \
else if (GET_MODE_SIZE (MODE) >= 4 && CONSTANT_P (X) \
&& CONSTANT_POOL_ADDRESS_P (X) && ! flag_pic) \
goto WIN; \
/* This is PC relative data after MACHINE_DEPENDENT_REORG runs. */ \
else if (GET_MODE_SIZE (MODE) >= 4 && reload_completed \
&& (GET_CODE (X) == LABEL_REF \
|| (GET_CODE (X) == CONST \
&& GET_CODE (XEXP (X, 0)) == PLUS \
&& GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF \
&& GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT))) \
goto WIN; \
/* Post-inc indexing only supported for SImode and larger. */ \
else if (GET_CODE (X) == POST_INC && GET_MODE_SIZE (MODE) >= 4 \
&& GET_CODE (XEXP (X, 0)) == REG \
&& REG_OK_FOR_INDEX_P (XEXP (X, 0))) \
goto WIN; \
else if (GET_CODE (X) == PLUS) \
{ \
/* REG+REG address can be any two index registers. */ \
/* We disallow FRAME+REG addressing since we know that FRAME \
will be replaced with STACK, and SP relative addressing only \
permits SP+OFFSET. */ \
if (GET_MODE_SIZE (MODE) <= 4 \
&& GET_CODE (XEXP (X, 0)) == REG \
&& GET_CODE (XEXP (X, 1)) == REG \
&& XEXP (X, 0) != frame_pointer_rtx \
&& XEXP (X, 1) != frame_pointer_rtx \
&& XEXP (X, 0) != virtual_stack_vars_rtx \
&& XEXP (X, 1) != virtual_stack_vars_rtx \
&& REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
&& REG_OK_FOR_INDEX_P (XEXP (X, 1))) \
goto WIN; \
/* REG+const has 5-7 bit offset for non-SP registers. */ \
else if (GET_CODE (XEXP (X, 0)) == REG \
&& (REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
|| XEXP (X, 0) == arg_pointer_rtx) \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \
goto WIN; \
/* REG+const has 10 bit offset for SP, but only SImode and \
larger is supported. */ \
/* ??? Should probably check for DI/DFmode overflow here \
just like GO_IF_LEGITIMATE_OFFSET does. */ \
else if (GET_CODE (XEXP (X, 0)) == REG \
&& REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \
&& GET_MODE_SIZE (MODE) >= 4 \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& ((unsigned HOST_WIDE_INT) INTVAL (XEXP (X, 1)) \
+ GET_MODE_SIZE (MODE)) <= 1024 \
&& (INTVAL (XEXP (X, 1)) & 3) == 0) \
goto WIN; \
else if (GET_CODE (XEXP (X, 0)) == REG \
&& REGNO (XEXP (X, 0)) == FRAME_POINTER_REGNUM \
&& GET_MODE_SIZE (MODE) >= 4 \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (INTVAL (XEXP (X, 1)) & 3) == 0) \
goto WIN; \
} \
else if (GET_MODE_CLASS (MODE) != MODE_FLOAT \
&& GET_CODE (X) == SYMBOL_REF \
&& CONSTANT_POOL_ADDRESS_P (X) \
&& ! (flag_pic \
&& symbol_mentioned_p (get_pool_constant (X)))) \
goto WIN; \
}
/* ------------------------------------------------------------------- */
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \
if (TARGET_ARM) \
ARM_GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN) \
else /* if (TARGET_THUMB) */ \
THUMB_GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN)
/* ------------------------------------------------------------------- */
/* Try machine-dependent ways of modifying an illegitimate address /* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address. to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c. This macro is used in only one place: `memory_address' in explow.c.
...@@ -1715,7 +2201,7 @@ CUMULATIVE_ARGS; ...@@ -1715,7 +2201,7 @@ CUMULATIVE_ARGS;
On the ARM, try to convert [REG, #BIGCONST] On the ARM, try to convert [REG, #BIGCONST]
into ADD BASE, REG, #UPPERCONST and [BASE, #VALIDCONST], into ADD BASE, REG, #UPPERCONST and [BASE, #VALIDCONST],
where VALIDCONST == 0 in case of TImode. */ where VALIDCONST == 0 in case of TImode. */
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ #define ARM_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
{ \ { \
if (GET_CODE (X) == PLUS) \ if (GET_CODE (X) == PLUS) \
{ \ { \
...@@ -1776,18 +2262,34 @@ CUMULATIVE_ARGS; ...@@ -1776,18 +2262,34 @@ CUMULATIVE_ARGS;
goto WIN; \ goto WIN; \
} }
#define THUMB_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
if (flag_pic) \
(X) = legitimize_pic_address (OLDX, MODE, NULL_RTX);
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
if (TARGET_ARM) \
ARM_LEGITIMIZE_ADDRESS (X, OLDX, MODE, WIN) \
else \
THUMB_LEGITIMIZE_ADDRESS (X, OLDX, MODE, WIN)
/* Go to LABEL if ADDR (a legitimate address expression) /* Go to LABEL if ADDR (a legitimate address expression)
has an effect that depends on the machine mode it is used for. */ has an effect that depends on the machine mode it is used for. */
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \ #define ARM_GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \
{ \ { \
if (GET_CODE(ADDR) == PRE_DEC || GET_CODE(ADDR) == POST_DEC \ if ( GET_CODE (ADDR) == PRE_DEC || GET_CODE (ADDR) == POST_DEC \
|| GET_CODE(ADDR) == PRE_INC || GET_CODE(ADDR) == POST_INC) \ || GET_CODE (ADDR) == PRE_INC || GET_CODE (ADDR) == POST_INC) \
goto LABEL; \ goto LABEL; \
} }
/* Nothing helpful to do for the Thumb */
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \
if (TARGET_ARM) \
ARM_GO_IF_MODE_DEPENDENT_ADDRESS (ADDR, LABEL)
/* Specify the machine mode that this machine uses /* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */ for the index in the tablejump instruction. */
#define CASE_VECTOR_MODE SImode #define CASE_VECTOR_MODE Pmode
/* Define as C expression which evaluates to nonzero if the tablejump /* Define as C expression which evaluates to nonzero if the tablejump
instruction expects the table to contain offsets from the address of the instruction expects the table to contain offsets from the address of the
...@@ -1823,8 +2325,9 @@ CUMULATIVE_ARGS; ...@@ -1823,8 +2325,9 @@ CUMULATIVE_ARGS;
be the code that says which one of the two operations is implicitly be the code that says which one of the two operations is implicitly
done, NIL if none. */ done, NIL if none. */
#define LOAD_EXTEND_OP(MODE) \ #define LOAD_EXTEND_OP(MODE) \
(TARGET_THUMB ? ZERO_EXTEND : \
((arm_arch4 || (MODE) == QImode) ? ZERO_EXTEND \ ((arm_arch4 || (MODE) == QImode) ? ZERO_EXTEND \
: ((BYTES_BIG_ENDIAN && (MODE) == HImode) ? SIGN_EXTEND : NIL)) : ((BYTES_BIG_ENDIAN && (MODE) == HImode) ? SIGN_EXTEND : NIL)))
/* Define this if zero-extension is slow (more than one real instruction). /* Define this if zero-extension is slow (more than one real instruction).
On the ARM, it is more than one instruction only if not fetching from On the ARM, it is more than one instruction only if not fetching from
...@@ -1834,6 +2337,8 @@ CUMULATIVE_ARGS; ...@@ -1834,6 +2337,8 @@ CUMULATIVE_ARGS;
/* Nonzero if access to memory by bytes is slow and undesirable. */ /* Nonzero if access to memory by bytes is slow and undesirable. */
#define SLOW_BYTE_ACCESS 0 #define SLOW_BYTE_ACCESS 0
#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) 1
/* Immediate shift counts are truncated by the output routines (or was it /* Immediate shift counts are truncated by the output routines (or was it
the assembler?). Shift counts in a register are truncated by ARM. Note the assembler?). Shift counts in a register are truncated by ARM. Note
that the native compiler puts too large (> 32) immediate shift counts that the native compiler puts too large (> 32) immediate shift counts
...@@ -1846,7 +2351,7 @@ CUMULATIVE_ARGS; ...@@ -1846,7 +2351,7 @@ CUMULATIVE_ARGS;
/* #define SHIFT_COUNT_TRUNCATED 1 */ /* #define SHIFT_COUNT_TRUNCATED 1 */
/* All integers have the same format so truncation is easy. */ /* All integers have the same format so truncation is easy. */
#define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) 1 #define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
/* Calling from registers is a massive pain. */ /* Calling from registers is a massive pain. */
#define NO_FUNCTION_CSE 1 #define NO_FUNCTION_CSE 1
...@@ -1858,52 +2363,24 @@ CUMULATIVE_ARGS; ...@@ -1858,52 +2363,24 @@ CUMULATIVE_ARGS;
#define Pmode SImode #define Pmode SImode
#define FUNCTION_MODE Pmode #define FUNCTION_MODE Pmode
/* The structure type of the machine dependent info field of insns
No uses for this yet. */
/* #define INSN_MACHINE_INFO struct machine_info */
/* The relative costs of various types of constants. Note that cse.c defines
REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */
#define CONST_COSTS(RTX, CODE, OUTER_CODE) \
case CONST_INT: \
if (const_ok_for_arm (INTVAL (RTX))) \
return (OUTER_CODE) == SET ? 2 : -1; \
else if (OUTER_CODE == AND \
&& const_ok_for_arm (~INTVAL (RTX))) \
return -1; \
else if ((OUTER_CODE == COMPARE \
|| OUTER_CODE == PLUS || OUTER_CODE == MINUS) \
&& const_ok_for_arm (-INTVAL (RTX))) \
return -1; \
else \
return 5; \
case CONST: \
case LABEL_REF: \
case SYMBOL_REF: \
return 6; \
case CONST_DOUBLE: \
if (const_double_rtx_ok_for_fpu (RTX)) \
return (OUTER_CODE) == SET ? 2 : -1; \
else if (((OUTER_CODE) == COMPARE || (OUTER_CODE) == PLUS) \
&& neg_const_double_rtx_ok_for_fpu (RTX)) \
return -1; \
return(7);
#define ARM_FRAME_RTX(X) \ #define ARM_FRAME_RTX(X) \
((X) == frame_pointer_rtx || (X) == stack_pointer_rtx \ ( (X) == frame_pointer_rtx || (X) == stack_pointer_rtx \
|| (X) == arg_pointer_rtx) || (X) == arg_pointer_rtx)
#define DEFAULT_RTX_COSTS(X, CODE, OUTER_CODE) \ #define DEFAULT_RTX_COSTS(X, CODE, OUTER_CODE) \
return arm_rtx_costs (X, CODE); return arm_rtx_costs (X, CODE, OUTER_CODE);
/* Moves to and from memory are quite expensive */ /* Moves to and from memory are quite expensive */
#define MEMORY_MOVE_COST(MODE,CLASS,IN) 10 #define MEMORY_MOVE_COST(M, CLASS, IN) \
(TARGET_ARM ? 10 : \
((GET_MODE_SIZE (M) < 4 ? 8 : 2 * GET_MODE_SIZE (M)) \
* (CLASS == LO_REGS ? 1 : 2)))
/* All address computations that can be done are free, but rtx cost returns /* All address computations that can be done are free, but rtx cost returns
the same for practically all of them. So we weight the different types the same for practically all of them. So we weight the different types
of address here in the order (most pref first): of address here in the order (most pref first):
PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL. */ PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL. */
#define ADDRESS_COST(X) \ #define ARM_ADDRESS_COST(X) \
(10 - ((GET_CODE (X) == MEM || GET_CODE (X) == LABEL_REF \ (10 - ((GET_CODE (X) == MEM || GET_CODE (X) == LABEL_REF \
|| GET_CODE (X) == SYMBOL_REF) \ || GET_CODE (X) == SYMBOL_REF) \
? 0 \ ? 0 \
...@@ -1919,9 +2396,19 @@ CUMULATIVE_ARGS; ...@@ -1919,9 +2396,19 @@ CUMULATIVE_ARGS;
? 1 : 0)) \ ? 1 : 0)) \
: 4))))) : 4)))))
#define THUMB_ADDRESS_COST(X) \
((GET_CODE (X) == REG \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
&& GET_CODE (XEXP (X, 1)) == CONST_INT)) \
? 1 : 2)
#define ADDRESS_COST(X) \
(TARGET_ARM ? ARM_ADDRESS_COST (X) : THUMB_ADDRESS_COST (X))
/* Try to generate sequences that don't involve branches, we can then use /* Try to generate sequences that don't involve branches, we can then use
conditional instructions */ conditional instructions */
#define BRANCH_COST 4 #define BRANCH_COST \
(TARGET_ARM ? 4 : (optimize > 1 ? 1 : 0))
/* A C statement to update the variable COST based on the relationship /* A C statement to update the variable COST based on the relationship
between INSN that is dependent on DEP through dependence LINK. */ between INSN that is dependent on DEP through dependence LINK. */
...@@ -2005,7 +2492,7 @@ extern int making_const_table; ...@@ -2005,7 +2492,7 @@ extern int making_const_table;
CC(CC_DGTUmode, "CC_DGTU") \ CC(CC_DGTUmode, "CC_DGTU") \
CC(CC_Cmode, "CC_C") CC(CC_Cmode, "CC_C")
#define SELECT_CC_MODE(OP,X,Y) arm_select_cc_mode ((OP), (X), (Y)) #define SELECT_CC_MODE(OP, X, Y) arm_select_cc_mode (OP, X, Y)
#define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) #define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode)
...@@ -2025,49 +2512,17 @@ extern int making_const_table; ...@@ -2025,49 +2512,17 @@ extern int making_const_table;
#define STORE_FLAG_VALUE 1 #define STORE_FLAG_VALUE 1
/* Define the information needed to generate branch insns. This is
stored from the compare operation. Note that we can't use "rtx" here
since it hasn't been defined! */
extern struct rtx_def * arm_compare_op0;
extern struct rtx_def * arm_compare_op1;
/* Define the codes that are matched by predicates in arm.c */
#define PREDICATE_CODES \
{"s_register_operand", {SUBREG, REG}}, \
{"f_register_operand", {SUBREG, REG}}, \
{"arm_add_operand", {SUBREG, REG, CONST_INT}}, \
{"fpu_add_operand", {SUBREG, REG, CONST_DOUBLE}}, \
{"arm_rhs_operand", {SUBREG, REG, CONST_INT}}, \
{"fpu_rhs_operand", {SUBREG, REG, CONST_DOUBLE}}, \
{"arm_not_operand", {SUBREG, REG, CONST_INT}}, \
{"offsettable_memory_operand", {MEM}}, \
{"bad_signed_byte_operand", {MEM}}, \
{"alignable_memory_operand", {MEM}}, \
{"shiftable_operator", {PLUS, MINUS, AND, IOR, XOR}}, \
{"minmax_operator", {SMIN, SMAX, UMIN, UMAX}}, \
{"shift_operator", {ASHIFT, ASHIFTRT, LSHIFTRT, ROTATERT, MULT}}, \
{"di_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE, MEM}}, \
{"soft_df_operand", {SUBREG, REG, CONST_DOUBLE, MEM}}, \
{"load_multiple_operation", {PARALLEL}}, \
{"store_multiple_operation", {PARALLEL}}, \
{"equality_operator", {EQ, NE}}, \
{"arm_rhsm_operand", {SUBREG, REG, CONST_INT, MEM}}, \
{"const_shift_operand", {CONST_INT}}, \
{"index_operand", {SUBREG, REG, CONST_INT}}, \
{"reg_or_int_operand", {SUBREG, REG, CONST_INT}}, \
{"multi_register_push", {PARALLEL}}, \
{"cc_register", {REG}}, \
{"logical_binary_operator", {AND, IOR, XOR}}, \
{"dominant_cc_register", {REG}},
/* Gcc puts the pool in the wrong place for ARM, since we can only /* Gcc puts the pool in the wrong place for ARM, since we can only
load addresses a limited distance around the pc. We do some load addresses a limited distance around the pc. We do some
special munging to move the constant pool values to the correct special munging to move the constant pool values to the correct
point in the code. */ point in the code. */
#define MACHINE_DEPENDENT_REORG(INSN) arm_reorg ((INSN)) #define MACHINE_DEPENDENT_REORG(INSN) \
arm_reorg (INSN); \
#undef ASM_APP_OFF
#define ASM_APP_OFF (TARGET_THUMB ? "\t.code\t16\n" : "")
/* Output an internal label definition. */ /* Output an internal label definition. */
#ifndef ASM_OUTPUT_INTERNAL_LABEL #ifndef ASM_OUTPUT_INTERNAL_LABEL
...@@ -2090,19 +2545,66 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2090,19 +2545,66 @@ extern struct rtx_def * arm_compare_op1;
/* Output a push or a pop instruction (only used when profiling). */ /* Output a push or a pop instruction (only used when profiling). */
#define ASM_OUTPUT_REG_PUSH(STREAM, REGNO) \ #define ASM_OUTPUT_REG_PUSH(STREAM, REGNO) \
asm_fprintf (STREAM,"\tstmfd\t%r!,{%r}\n", SP_REGNUM, REGNO) if (TARGET_ARM) \
asm_fprintf (STREAM,"\tstmfd\t%r!,{%r}\n", \
STACK_POINTER_REGNUM, REGNO); \
else \
asm_fprintf (STREAM, "\tpush {%r}\n", REGNO)
#define ASM_OUTPUT_REG_POP(STREAM, REGNO) \ #define ASM_OUTPUT_REG_POP(STREAM, REGNO) \
asm_fprintf (STREAM,"\tldmfd\t%r!,{%r}\n", SP_REGNUM, REGNO) if (TARGET_ARM) \
asm_fprintf (STREAM, "\tldmfd\t%r!,{%r}\n", \
STACK_POINTER_REGNUM, REGNO); \
else \
asm_fprintf (STREAM, "\tpop {%r}\n", REGNO)
/* This is how to output a label which precedes a jumptable. Since
Thumb instructions are 2 bytes, we may need explicit alignment here. */
#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE) \
do \
{ \
if (TARGET_THUMB) \
ASM_OUTPUT_ALIGN (FILE, 2); \
ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \
} \
while (0)
#define ARM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ #define ARM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \
do \ do \
{ \ { \
if (TARGET_THUMB) \
{ \
if (is_called_in_ARM_mode (DECL)) \
fprintf (STREAM, "\t.code 32\n") ; \
else \
fprintf (STREAM, "\t.thumb_func\n") ; \
} \
if (TARGET_POKE_FUNCTION_NAME) \ if (TARGET_POKE_FUNCTION_NAME) \
arm_poke_function_name (STREAM, (char *) NAME); \ arm_poke_function_name (STREAM, (char *) NAME); \
} \ } \
while (0) while (0)
/* For aliases of functions we use .thumb_set instead. */
#define ASM_OUTPUT_DEF_FROM_DECLS(FILE, DECL1, DECL2) \
do \
{ \
char * LABEL1 = XSTR (XEXP (DECL_RTL (decl), 0), 0); \
char * LABEL2 = IDENTIFIER_POINTER (DECL2); \
\
if (TARGET_THUMB && TREE_CODE (DECL1) == FUNCTION_DECL) \
{ \
fprintf (FILE, "\t.thumb_set "); \
assemble_name (FILE, LABEL1); \
fprintf (FILE, ","); \
assemble_name (FILE, LABEL2); \
fprintf (FILE, "\n"); \
} \
else \
ASM_OUTPUT_DEF (FILE, LABEL1, LABEL2); \
} \
while (0)
/* Target characters. */ /* Target characters. */
#define TARGET_BELL 007 #define TARGET_BELL 007
#define TARGET_BS 010 #define TARGET_BS 010
...@@ -2115,11 +2617,16 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2115,11 +2617,16 @@ extern struct rtx_def * arm_compare_op1;
/* Only perform branch elimination (by making instructions conditional) if /* Only perform branch elimination (by making instructions conditional) if
we're optimising. Otherwise it's of no use anyway. */ we're optimising. Otherwise it's of no use anyway. */
#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ #define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
if (optimize) \ if (TARGET_ARM && optimize) \
arm_final_prescan_insn (INSN) arm_final_prescan_insn (INSN); \
else if (TARGET_THUMB) \
thumb_final_prescan_insn (INSN)
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \ #define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
((CODE) == '?' || (CODE) == '|' || (CODE) == '@') (CODE == '@' || CODE == '|' \
|| (TARGET_ARM && (CODE == '?')) \
|| (TARGET_THUMB && (CODE == '_')))
/* Output an operand of an instruction. */ /* Output an operand of an instruction. */
#define PRINT_OPERAND(STREAM, X, CODE) \ #define PRINT_OPERAND(STREAM, X, CODE) \
...@@ -2127,14 +2634,14 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2127,14 +2634,14 @@ extern struct rtx_def * arm_compare_op1;
#define ARM_SIGN_EXTEND(x) ((HOST_WIDE_INT) \ #define ARM_SIGN_EXTEND(x) ((HOST_WIDE_INT) \
(HOST_BITS_PER_WIDE_INT <= 32 ? (x) \ (HOST_BITS_PER_WIDE_INT <= 32 ? (x) \
: (((x) & (unsigned HOST_WIDE_INT) 0xffffffffUL) | \ : (((x) & (unsigned HOST_WIDE_INT) 0xffffffff) | \
(((x) & (unsigned HOST_WIDE_INT) 0x80000000UL) \ (((x) & (unsigned HOST_WIDE_INT) 0x80000000) \
? ((~ (HOST_WIDE_INT) 0) \ ? ((~ (HOST_WIDE_INT) 0) \
& ~ (unsigned HOST_WIDE_INT) 0xffffffffUL) \ & ~ (unsigned HOST_WIDE_INT) 0xffffffff) \
: 0)))) : 0))))
/* Output the address of an operand. */ /* Output the address of an operand. */
#define PRINT_OPERAND_ADDRESS(STREAM,X) \ #define ARM_PRINT_OPERAND_ADDRESS(STREAM, X) \
{ \ { \
int is_minus = GET_CODE (X) == MINUS; \ int is_minus = GET_CODE (X) == MINUS; \
\ \
...@@ -2144,28 +2651,29 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2144,28 +2651,29 @@ extern struct rtx_def * arm_compare_op1;
{ \ { \
rtx base = XEXP (X, 0); \ rtx base = XEXP (X, 0); \
rtx index = XEXP (X, 1); \ rtx index = XEXP (X, 1); \
int base_reg; \
HOST_WIDE_INT offset = 0; \ HOST_WIDE_INT offset = 0; \
if (GET_CODE (base) != REG) \ if (GET_CODE (base) != REG) \
{ \ { \
/* Ensure that BASE is a register (one of them must be). */ \ /* Ensure that BASE is a register */ \
/* (one of them must be). */ \
rtx temp = base; \ rtx temp = base; \
base = index; \ base = index; \
index = temp; \ index = temp; \
} \ } \
base_reg = REGNO (base); \
switch (GET_CODE (index)) \ switch (GET_CODE (index)) \
{ \ { \
case CONST_INT: \ case CONST_INT: \
offset = INTVAL (index); \ offset = INTVAL (index); \
if (is_minus) \ if (is_minus) \
offset = -offset; \ offset = -offset; \
asm_fprintf (STREAM, "[%r, #%d]", base_reg, offset); \ asm_fprintf (STREAM, "[%r, #%d]", \
REGNO (base), offset); \
break; \ break; \
\ \
case REG: \ case REG: \
asm_fprintf (STREAM, "[%r, %s%r]", base_reg, \ asm_fprintf (STREAM, "[%r, %s%r]", \
is_minus ? "-" : "", REGNO (index)); \ REGNO (base), is_minus ? "-" : "", \
REGNO (index)); \
break; \ break; \
\ \
case MULT: \ case MULT: \
...@@ -2174,8 +2682,9 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2174,8 +2682,9 @@ extern struct rtx_def * arm_compare_op1;
case ASHIFT: \ case ASHIFT: \
case ROTATERT: \ case ROTATERT: \
{ \ { \
asm_fprintf (STREAM, "[%r, %s%r", base_reg, \ asm_fprintf (STREAM, "[%r, %s%r", \
is_minus ? "-" : "", REGNO (XEXP (index, 0))); \ REGNO (base), is_minus ? "-" : "", \
REGNO (XEXP (index, 0))); \
arm_print_operand (STREAM, index, 'S'); \ arm_print_operand (STREAM, index, 'S'); \
fputs ("]", STREAM); \ fputs ("]", STREAM); \
break; \ break; \
...@@ -2185,8 +2694,8 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2185,8 +2694,8 @@ extern struct rtx_def * arm_compare_op1;
abort(); \ abort(); \
} \ } \
} \ } \
else if (GET_CODE (X) == PRE_INC || GET_CODE (X) == POST_INC \ else if ( GET_CODE (X) == PRE_INC || GET_CODE (X) == POST_INC\
|| GET_CODE (X) == PRE_DEC || GET_CODE (X) == POST_DEC) \ || GET_CODE (X) == PRE_DEC || GET_CODE (X) == POST_DEC)\
{ \ { \
extern int output_memory_reference_mode; \ extern int output_memory_reference_mode; \
\ \
...@@ -2197,18 +2706,45 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2197,18 +2706,45 @@ extern struct rtx_def * arm_compare_op1;
asm_fprintf (STREAM, "[%r, #%s%d]!", \ asm_fprintf (STREAM, "[%r, #%s%d]!", \
REGNO (XEXP (X, 0)), \ REGNO (XEXP (X, 0)), \
GET_CODE (X) == PRE_DEC ? "-" : "", \ GET_CODE (X) == PRE_DEC ? "-" : "", \
GET_MODE_SIZE (output_memory_reference_mode)); \ GET_MODE_SIZE (output_memory_reference_mode));\
else \ else \
asm_fprintf (STREAM, "[%r], #%s%d", \ asm_fprintf (STREAM, "[%r], #%s%d", \
REGNO (XEXP (X, 0)), \ REGNO (XEXP (X, 0)), \
GET_CODE (X) == POST_DEC ? "-" : "", \ GET_CODE (X) == POST_DEC ? "-" : "", \
GET_MODE_SIZE (output_memory_reference_mode)); \ GET_MODE_SIZE (output_memory_reference_mode));\
} \ } \
else output_addr_const (STREAM, X); \ else output_addr_const (STREAM, X); \
} }
#define THUMB_PRINT_OPERAND_ADDRESS(STREAM, X) \
{ \
if (GET_CODE (X) == REG) \
asm_fprintf (STREAM, "[%r]", REGNO (X)); \
else if (GET_CODE (X) == POST_INC) \
asm_fprintf (STREAM, "%r!", REGNO (XEXP (X, 0))); \
else if (GET_CODE (X) == PLUS) \
{ \
if (GET_CODE (XEXP (X, 1)) == CONST_INT) \
asm_fprintf (STREAM, "[%r, #%d]", \
REGNO (XEXP (X, 0)), \
(int) INTVAL (XEXP (X, 1))); \
else \
asm_fprintf (STREAM, "[%r, %r]", \
REGNO (XEXP (X, 0)), \
REGNO (XEXP (X, 1))); \
} \
else \
output_addr_const (STREAM, X); \
}
#define PRINT_OPERAND_ADDRESS(STREAM, X) \
if (TARGET_ARM) \
ARM_PRINT_OPERAND_ADDRESS (STREAM, X) \
else \
THUMB_PRINT_OPERAND_ADDRESS (STREAM, X)
/* Handles PIC addr specially */ /* Handles PIC addr specially */
#define OUTPUT_INT_ADDR_CONST(STREAM,X) \ #define OUTPUT_INT_ADDR_CONST(STREAM, X) \
{ \ { \
if (flag_pic && GET_CODE (X) == CONST && is_pic (X)) \ if (flag_pic && GET_CODE (X) == CONST && is_pic (X)) \
{ \ { \
...@@ -2217,7 +2753,8 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2217,7 +2753,8 @@ extern struct rtx_def * arm_compare_op1;
output_addr_const (STREAM, XEXP (XEXP (XEXP (X, 0), 1), 0)); \ output_addr_const (STREAM, XEXP (XEXP (XEXP (X, 0), 1), 0)); \
fputs (")", STREAM); \ fputs (")", STREAM); \
} \ } \
else output_addr_const (STREAM, X); \ else \
output_addr_const (STREAM, X); \
\ \
/* Mark symbols as position independent. We only do this in the \ /* Mark symbols as position independent. We only do this in the \
.text segment, not in the .data segment. */ \ .text segment, not in the .data segment. */ \
...@@ -2270,9 +2807,11 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2270,9 +2807,11 @@ extern struct rtx_def * arm_compare_op1;
address for the frame COUNT steps up from the current frame. */ address for the frame COUNT steps up from the current frame. */
#define RETURN_ADDR_RTX(COUNT, FRAME) \ #define RETURN_ADDR_RTX(COUNT, FRAME) \
((COUNT == 0) \ arm_return_addr (COUNT, FRAME)
? gen_rtx_MEM (Pmode, plus_constant (FRAME, -4)) \
: NULL_RTX) /* Mask of the bits in the PC that contain the real return address
when running in 26-bit mode. */
#define RETURN_ADDR_MASK26 (0x03fffffc)
/* Pick up the return address upon entry to a procedure. Used for /* Pick up the return address upon entry to a procedure. Used for
dwarf2 unwind information. This also enables the table driven dwarf2 unwind information. This also enables the table driven
...@@ -2288,6 +2827,40 @@ extern struct rtx_def * arm_compare_op1; ...@@ -2288,6 +2827,40 @@ extern struct rtx_def * arm_compare_op1;
in 26 bit mode, the condition codes must be masked out of the \ in 26 bit mode, the condition codes must be masked out of the \
return address. This does not apply to ARM6 and later processors \ return address. This does not apply to ARM6 and later processors \
when running in 32 bit mode. */ \ when running in 32 bit mode. */ \
((!TARGET_APCS_32) ? (GEN_INT (0x03fffffc)) : (GEN_INT (0xffffffff))) ((!TARGET_APCS_32) ? (GEN_INT (RETURN_ADDR_MASK26)) \
: (GEN_INT ((unsigned long)0xffffffff)))
/* Define the codes that are matched by predicates in arm.c */
#define PREDICATE_CODES \
{"s_register_operand", {SUBREG, REG}}, \
{"f_register_operand", {SUBREG, REG}}, \
{"arm_add_operand", {SUBREG, REG, CONST_INT}}, \
{"fpu_add_operand", {SUBREG, REG, CONST_DOUBLE}}, \
{"fpu_rhs_operand", {SUBREG, REG, CONST_DOUBLE}}, \
{"arm_rhs_operand", {SUBREG, REG, CONST_INT}}, \
{"arm_not_operand", {SUBREG, REG, CONST_INT}}, \
{"reg_or_int_operand", {SUBREG, REG, CONST_INT}}, \
{"index_operand", {SUBREG, REG, CONST_INT}}, \
{"thumb_cmp_operand", {SUBREG, REG, CONST_INT}}, \
{"offsettable_memory_operand", {MEM}}, \
{"bad_signed_byte_operand", {MEM}}, \
{"alignable_memory_operand", {MEM}}, \
{"shiftable_operator", {PLUS, MINUS, AND, IOR, XOR}}, \
{"minmax_operator", {SMIN, SMAX, UMIN, UMAX}}, \
{"shift_operator", {ASHIFT, ASHIFTRT, LSHIFTRT, ROTATERT, MULT}}, \
{"di_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE, MEM}}, \
{"nonimmediate_di_operand", {SUBREG, REG, MEM}}, \
{"soft_df_operand", {SUBREG, REG, CONST_DOUBLE, MEM}}, \
{"nonimmediate_soft_df_operand", {SUBREG, REG, MEM}}, \
{"load_multiple_operation", {PARALLEL}}, \
{"store_multiple_operation", {PARALLEL}}, \
{"equality_operator", {EQ, NE}}, \
{"arm_rhsm_operand", {SUBREG, REG, CONST_INT, MEM}}, \
{"const_shift_operand", {CONST_INT}}, \
{"multi_register_push", {PARALLEL}}, \
{"cc_register", {REG}}, \
{"logical_binary_operator", {AND, IOR, XOR}}, \
{"dominant_cc_register", {REG}},
#endif /* __ARM_H__ */ #endif /* __ARM_H__ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Definitions of target machine for GNU compiler, /* Definitions of target machine for GNU compiler.
for ARM with COFF obj format. For ARM with COFF object format.
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. Copyright (C) 1995 - 1999 Free Software Foundation, Inc.
Contributed by Doug Evans (dje@cygnus.com). Contributed by Doug Evans (devans@cygnus.com).
This file is part of GNU CC. This file is part of GNU CC.
...@@ -20,8 +20,8 @@ along with GNU CC; see the file COPYING. If not, write to ...@@ -20,8 +20,8 @@ along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330, the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
#include "arm/semi.h" #include "semi.h"
#include "arm/aout.h" #include "aout.h"
/* Note - it is important that this definition matches the one in tcoff.h */ /* Note - it is important that this definition matches the one in tcoff.h */
#undef USER_LABEL_PREFIX #undef USER_LABEL_PREFIX
...@@ -33,9 +33,12 @@ Boston, MA 02111-1307, USA. */ ...@@ -33,9 +33,12 @@ Boston, MA 02111-1307, USA. */
#define TARGET_VERSION fputs (" (ARM/coff)", stderr) #define TARGET_VERSION fputs (" (ARM/coff)", stderr)
#undef TARGET_DEFAULT #undef TARGET_DEFAULT
#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32) #define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME)
#define MULTILIB_DEFAULTS { "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork" } #ifndef MULTILIB_DEFAULTS
#define MULTILIB_DEFAULTS \
{ "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork" }
#endif
/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS /* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
is a valid machine specific attribute for DECL. is a valid machine specific attribute for DECL.
...@@ -70,16 +73,13 @@ Boston, MA 02111-1307, USA. */ ...@@ -70,16 +73,13 @@ Boston, MA 02111-1307, USA. */
fprintf (STREAM, "%s Generated by gcc %s for ARM/coff\n", \ fprintf (STREAM, "%s Generated by gcc %s for ARM/coff\n", \
ASM_COMMENT_START, version_string); \ ASM_COMMENT_START, version_string); \
fprintf (STREAM, ASM_APP_OFF); \ fprintf (STREAM, ASM_APP_OFF); \
if (write_symbols == SDB_DEBUG) \
output_file_directive (STREAM, main_input_filename); \
} \ } \
while (0) while (0)
/* A C statement to output something to the assembler file to switch to section /* A C statement to output something to the assembler file to switch to
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or section NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
NULL_TREE. Some target formats do not support arbitrary sections. Do not NULL_TREE. Some target formats do not support arbitrary sections. Do not
define this macro in such cases. */ define this macro in such cases. */
#undef ASM_OUTPUT_SECTION_NAME
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \ #define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do \ do \
{ \ { \
...@@ -198,6 +198,14 @@ dtors_section () \ ...@@ -198,6 +198,14 @@ dtors_section () \
#undef DO_GLOBAL_CTORS_BODY #undef DO_GLOBAL_CTORS_BODY
#undef DO_GLOBAL_DTORS_BODY #undef DO_GLOBAL_DTORS_BODY
/* If you don't define HAVE_ATEXIT, and the object file format/OS/whatever
does not support constructors/destructors, then gcc implements destructors
by defining its own exit function, which calls the destructors. This gcc
exit function overrides the C library's exit function, and this can cause
all kinds of havoc if the C library has a non-trivial exit function. You
really don't want to use the exit function in libgcc2.c. */
#define HAVE_ATEXIT
/* The ARM development system defines __main. */ /* The ARM development system defines __main. */
#define NAME__MAIN "__gccmain" #define NAME__MAIN "__gccmain"
#define SYMBOL__MAIN __gccmain #define SYMBOL__MAIN __gccmain
/* Definitions of target machine for GNU compiler, /* Definitions of target machine for GNU compiler.
for ARM with ELF obj format. For ARM with ELF obj format.
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. Copyright (C) 1995 - 1999 Free Software Foundation, Inc.
Contributed by Philip Blundell <philb@gnu.org> and Contributed by Philip Blundell <philb@gnu.org> and
Catherine Moore <clm@cygnus.com> Catherine Moore <clm@cygnus.com>
...@@ -36,6 +36,22 @@ Boston, MA 02111-1307, USA. */ ...@@ -36,6 +36,22 @@ Boston, MA 02111-1307, USA. */
#define SUBTARGET_CPP_SPEC "-Darm_elf -D__ELF__" #define SUBTARGET_CPP_SPEC "-Darm_elf -D__ELF__"
#endif #endif
#ifndef SUBTARGET_EXTRA_ASM_SPEC
#define SUBTARGET_EXTRA_ASM_SPEC
#endif
#ifndef ASM_SPEC
#define ASM_SPEC "\
%{mbig-endian:-EB} \
%{mcpu=*:-m%*} \
%{march=*:-m%*} \
%{mapcs-*:-mapcs-%*} \
%{mapcs-float:-mfloat} \
%{msoft-float:-mno-fpu} \
%{mthumb-interwork:-mthumb-interwork} \
" SUBTARGET_EXTRA_ASM_SPEC
#endif
/* The following macro defines the format used to output the second /* The following macro defines the format used to output the second
operand of the .type assembler directive. Different svr4 assemblers operand of the .type assembler directive. Different svr4 assemblers
expect various different forms for this operand. The one given here expect various different forms for this operand. The one given here
...@@ -151,11 +167,6 @@ Boston, MA 02111-1307, USA. */ ...@@ -151,11 +167,6 @@ Boston, MA 02111-1307, USA. */
Otherwise, the readonly data section is used. */ Otherwise, the readonly data section is used. */
#define JUMP_TABLES_IN_TEXT_SECTION 1 #define JUMP_TABLES_IN_TEXT_SECTION 1
#ifndef ASM_SPEC
#define ASM_SPEC "%{mbig-endian:-EB} %{mcpu=*:-m%*} %{march=*:-m%*} \
%{mapcs-*:-mapcs-%*} %{mthumb-interwork:-mthumb-interwork} %{mapcs-float:mfloat}"
#endif
#ifndef LINK_SPEC #ifndef LINK_SPEC
#define LINK_SPEC "%{mbig-endian:-EB} -X" #define LINK_SPEC "%{mbig-endian:-EB} -X"
#endif #endif
...@@ -166,11 +177,12 @@ Boston, MA 02111-1307, USA. */ ...@@ -166,11 +177,12 @@ Boston, MA 02111-1307, USA. */
#endif #endif
#ifndef TARGET_DEFAULT #ifndef TARGET_DEFAULT
#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32) #define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME)
#endif #endif
#ifndef MULTILIB_DEFAULTS #ifndef MULTILIB_DEFAULTS
#define MULTILIB_DEFAULTS { "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork" } #define MULTILIB_DEFAULTS \
{ "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork", "fno-leading-underscore" }
#endif #endif
/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS /* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
...@@ -300,6 +312,24 @@ dtors_section () \ ...@@ -300,6 +312,24 @@ dtors_section () \
} }
#endif #endif
/* A C statement to output something to the assembler file to switch to
section NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL
or NULL_TREE. */
#undef ASM_OUTPUT_SECTION_NAME
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do \
{ \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"ax\",%%progbits\n", NAME); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"a\"\n", NAME); \
else if (! strncmp (NAME, ".bss", 4)) \
fprintf (STREAM, "\t.section %s,\"aw\",%%nobits\n", NAME); \
else \
fprintf (STREAM, "\t.section %s,\"aw\"\n", NAME); \
} \
while (0)
/* Support the ctors/dtors sections for g++. */ /* Support the ctors/dtors sections for g++. */
#ifndef INT_ASM_OP #ifndef INT_ASM_OP
#define INT_ASM_OP ".word" #define INT_ASM_OP ".word"
...@@ -367,4 +397,4 @@ dtors_section () \ ...@@ -367,4 +397,4 @@ dtors_section () \
} \ } \
while (0) while (0)
#include "arm/aout.h" #include "aout.h"
@ libgcc1 routines for ARM cpu. @ libgcc1 routines for ARM cpu.
@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) @ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk)
/* Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc. /* Copyright (C) 1995, 1996, 1998, 1999, 2000 Free Software Foundation, Inc.
This file is free software; you can redistribute it and/or modify it This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the under the terms of the GNU General Public License as published by the
...@@ -34,12 +34,12 @@ Boston, MA 02111-1307, USA. */ ...@@ -34,12 +34,12 @@ Boston, MA 02111-1307, USA. */
the executable file might be covered by the GNU General Public License. */ the executable file might be covered by the GNU General Public License. */
#ifdef __APCS_26__ #ifdef __APCS_26__
#define RET movs #define RET movs pc, lr
#define RETc(x) mov##x##s #define RETc(x) mov##x##s pc, lr
#define RETCOND ^ #define RETCOND ^
#else #else
#define RET mov #define RET mov pc, lr
#define RETc(x) mov##x #define RETc(x) mov##x pc, lr
#define RETCOND #define RETCOND
#endif #endif
...@@ -57,7 +57,11 @@ Boston, MA 02111-1307, USA. */ ...@@ -57,7 +57,11 @@ Boston, MA 02111-1307, USA. */
#define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x) #define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
#ifdef __ELF__ #ifdef __ELF__
#ifdef __thumb__
#define __PLT__ /* Not supported in thumb assembler (for now). */
#else
#define __PLT__ (PLT) #define __PLT__ (PLT)
#endif
#define TYPE(x) .type SYM(x),function #define TYPE(x) .type SYM(x),function
#define SIZE(x) .size SYM(x), . - SYM(x) #define SIZE(x) .size SYM(x), . - SYM(x)
#else #else
...@@ -66,6 +70,28 @@ Boston, MA 02111-1307, USA. */ ...@@ -66,6 +70,28 @@ Boston, MA 02111-1307, USA. */
#define SIZE(x) #define SIZE(x)
#endif #endif
#ifdef __thumb__
#define THUMB_FUNC .thumb_func
#define THUMB_CODE .force_thumb
#else
#define THUMB_FUNC
#define THUMB_CODE
#endif
.macro FUNC_START name
.text
.globl SYM (__\name)
TYPE (__\name)
.align 0
THUMB_CODE
THUMB_FUNC
SYM (__\name):
.endm
/* Used for Thumb code. */
work .req r4 @ XXXX is this safe ?
#ifdef L_udivsi3 #ifdef L_udivsi3
dividend .req r0 dividend .req r0
...@@ -77,12 +103,99 @@ sp .req r13 ...@@ -77,12 +103,99 @@ sp .req r13
lr .req r14 lr .req r14
pc .req r15 pc .req r15
.text FUNC_START udivsi3
.globl SYM (__udivsi3)
TYPE (__udivsi3) #ifdef __thumb__
.align 0
cmp divisor, #0
beq Ldiv0
mov curbit, #1
mov result, #0
push { work }
cmp dividend, divisor
bcc Lgot_result
@ Load the constant 0x10000000 into our work register
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions, and note which bits
@ are done in the result. On the final pass, this may subtract
@ too much from the dividend, but the result will be ok, since the
@ "bit" will have been shifted out at the bottom.
cmp dividend, divisor
bcc Over1
sub dividend, dividend, divisor
orr result, result, curbit
Over1:
lsr work, divisor, #1
cmp dividend, work
bcc Over2
sub dividend, dividend, work
lsr work, curbit, #1
orr result, work
Over2:
lsr work, divisor, #2
cmp dividend, work
bcc Over3
sub dividend, dividend, work
lsr work, curbit, #2
orr result, work
Over3:
lsr work, divisor, #3
cmp dividend, work
bcc Over4
sub dividend, dividend, work
lsr work, curbit, #3
orr result, work
Over4:
cmp dividend, #0 @ Early termination?
beq Lgot_result
lsr curbit, #4 @ No, any more bits to do?
beq Lgot_result
lsr divisor, #4
b Loop3
Lgot_result:
mov r0, result
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
#else /* arm version */
SYM (__udivsi3):
cmp divisor, #0 cmp divisor, #0
beq Ldiv0 beq Ldiv0
mov curbit, #1 mov curbit, #1
...@@ -132,7 +245,7 @@ Loop3: ...@@ -132,7 +245,7 @@ Loop3:
bne Loop3 bne Loop3
Lgot_result: Lgot_result:
mov r0, result mov r0, result
RET pc, lr RET
Ldiv0: Ldiv0:
str lr, [sp, #-4]! str lr, [sp, #-4]!
...@@ -140,6 +253,8 @@ Ldiv0: ...@@ -140,6 +253,8 @@ Ldiv0:
mov r0, #0 @ about as wrong as it could be mov r0, #0 @ about as wrong as it could be
ldmia sp!, {pc}RETCOND ldmia sp!, {pc}RETCOND
#endif /* arm version */
SIZE (__udivsi3) SIZE (__udivsi3)
#endif /* L_udivsi3 */ #endif /* L_udivsi3 */
...@@ -155,17 +270,147 @@ sp .req r13 ...@@ -155,17 +270,147 @@ sp .req r13
lr .req r14 lr .req r14
pc .req r15 pc .req r15
.text FUNC_START umodsi3
.globl SYM (__umodsi3)
TYPE (__umodsi3) #ifdef __thumb__
.align 0
SYM (__umodsi3):
cmp divisor, #0 cmp divisor, #0
beq Ldiv0 beq Ldiv0
mov curbit, #1 mov curbit, #1
cmp dividend, divisor cmp dividend, divisor
RETc(cc) pc, lr bcs Over1
RET
Over1:
@ Load the constant 0x10000000 into our work register
push { work }
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions. On the final pass, this may
@ subtract too much from the dividend, so keep track of which
@ subtractions are done, we can fix them up afterwards...
mov overdone, #0
cmp dividend, divisor
bcc Over2
sub dividend, dividend, divisor
Over2:
lsr work, divisor, #1
cmp dividend, work
bcc Over3
sub dividend, dividend, work
mov ip, curbit
mov work, #1
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over3:
lsr work, divisor, #2
cmp dividend, work
bcc Over4
sub dividend, dividend, work
mov ip, curbit
mov work, #2
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over4:
lsr work, divisor, #3
cmp dividend, work
bcc Over5
sub dividend, dividend, work
mov ip, curbit
mov work, #3
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over5:
mov ip, curbit
cmp dividend, #0 @ Early termination?
beq Over6
lsr curbit, #4 @ No, any more bits to do?
beq Over6
lsr divisor, #4
b Loop3
Over6:
@ Any subtractions that we should not have done will be recorded in
@ the top three bits of "overdone". Exactly which were not needed
@ are governed by the position of the bit, stored in ip.
@ If we terminated early, because dividend became zero,
@ then none of the below will match, since the bit in ip will not be
@ in the bottom nibble.
mov work, #0xe
lsl work, #28
and overdone, work
bne Over7
pop { work }
RET @ No fixups needed
Over7:
mov curbit, ip
mov work, #3
ror curbit, work
tst overdone, curbit
beq Over8
lsr work, divisor, #3
add dividend, dividend, work
Over8:
mov curbit, ip
mov work, #2
ror curbit, work
tst overdone, curbit
beq Over9
lsr work, divisor, #2
add dividend, dividend, work
Over9:
mov curbit, ip
mov work, #1
ror curbit, work
tst overdone, curbit
beq Over10
lsr work, divisor, #1
add dividend, dividend, work
Over10:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
#else /* arm version */
cmp divisor, #0
beq Ldiv0
mov curbit, #1
cmp dividend, divisor
RETc(cc)
Loop1: Loop1:
@ Unless the divisor is very big, shift it up in multiples of @ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main @ four bits, since this is the amount of unwinding in the main
...@@ -215,14 +460,14 @@ Loop3: ...@@ -215,14 +460,14 @@ Loop3:
@ then none of the below will match, since the bit in ip will not be @ then none of the below will match, since the bit in ip will not be
@ in the bottom nibble. @ in the bottom nibble.
ands overdone, overdone, #0xe0000000 ands overdone, overdone, #0xe0000000
RETc(eq) pc, lr @ No fixups needed RETc(eq) @ No fixups needed
tst overdone, ip, ror #3 tst overdone, ip, ror #3
addne dividend, dividend, divisor, lsr #3 addne dividend, dividend, divisor, lsr #3
tst overdone, ip, ror #2 tst overdone, ip, ror #2
addne dividend, dividend, divisor, lsr #2 addne dividend, dividend, divisor, lsr #2
tst overdone, ip, ror #1 tst overdone, ip, ror #1
addne dividend, dividend, divisor, lsr #1 addne dividend, dividend, divisor, lsr #1
RET pc, lr RET
Ldiv0: Ldiv0:
str lr, [sp, #-4]! str lr, [sp, #-4]!
...@@ -230,6 +475,8 @@ Ldiv0: ...@@ -230,6 +475,8 @@ Ldiv0:
mov r0, #0 @ about as wrong as it could be mov r0, #0 @ about as wrong as it could be
ldmia sp!, {pc}RETCOND ldmia sp!, {pc}RETCOND
#endif /* arm version */
SIZE (__umodsi3) SIZE (__umodsi3)
#endif /* L_umodsi3 */ #endif /* L_umodsi3 */
...@@ -245,12 +492,113 @@ sp .req r13 ...@@ -245,12 +492,113 @@ sp .req r13
lr .req r14 lr .req r14
pc .req r15 pc .req r15
.text FUNC_START divsi3
.globl SYM (__divsi3)
TYPE (__divsi3) #ifdef __thumb__
.align 0 cmp divisor, #0
beq Ldiv0
push { work }
mov work, dividend
eor work, divisor @ Save the sign of the result.
mov ip, work
mov curbit, #1
mov result, #0
cmp divisor, #0
bpl Over1
neg divisor, divisor @ Loops below use unsigned.
Over1:
cmp dividend, #0
bpl Over2
neg dividend, dividend
Over2:
cmp dividend, divisor
bcc Lgot_result
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
Bcs Lbignum
cmp divisor, dividend
Bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
lsl work, #3
Loop2:
cmp divisor, work
Bcs Loop3
cmp divisor, dividend
Bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions, and note which bits
@ are done in the result. On the final pass, this may subtract
@ too much from the dividend, but the result will be ok, since the
@ "bit" will have been shifted out at the bottom.
cmp dividend, divisor
Bcc Over3
sub dividend, dividend, divisor
orr result, result, curbit
Over3:
lsr work, divisor, #1
cmp dividend, work
Bcc Over4
sub dividend, dividend, work
lsr work, curbit, #1
orr result, work
Over4:
lsr work, divisor, #2
cmp dividend, work
Bcc Over5
sub dividend, dividend, work
lsr work, curbit, #2
orr result, result, work
Over5:
lsr work, divisor, #3
cmp dividend, work
Bcc Over6
sub dividend, dividend, work
lsr work, curbit, #3
orr result, result, work
Over6:
cmp dividend, #0 @ Early termination?
Beq Lgot_result
lsr curbit, #4 @ No, any more bits to do?
Beq Lgot_result
lsr divisor, #4
b Loop3
Lgot_result:
mov r0, result
mov work, ip
cmp work, #0
Bpl Over7
neg r0, r0
Over7:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
#else /* arm version */
SYM (__divsi3):
eor ip, dividend, divisor @ Save the sign of the result. eor ip, dividend, divisor @ Save the sign of the result.
mov curbit, #1 mov curbit, #1
mov result, #0 mov result, #0
...@@ -307,7 +655,7 @@ Lgot_result: ...@@ -307,7 +655,7 @@ Lgot_result:
mov r0, result mov r0, result
cmp ip, #0 cmp ip, #0
rsbmi r0, r0, #0 rsbmi r0, r0, #0
RET pc, lr RET
Ldiv0: Ldiv0:
str lr, [sp, #-4]! str lr, [sp, #-4]!
...@@ -315,6 +663,8 @@ Ldiv0: ...@@ -315,6 +663,8 @@ Ldiv0:
mov r0, #0 @ about as wrong as it could be mov r0, #0 @ about as wrong as it could be
ldmia sp!, {pc}RETCOND ldmia sp!, {pc}RETCOND
#endif /* arm version */
SIZE (__divsi3) SIZE (__divsi3)
#endif /* L_divsi3 */ #endif /* L_divsi3 */
...@@ -330,12 +680,155 @@ sp .req r13 ...@@ -330,12 +680,155 @@ sp .req r13
lr .req r14 lr .req r14
pc .req r15 pc .req r15
.text FUNC_START modsi3
.globl SYM (__modsi3)
TYPE (__modsi3) #ifdef __thumb__
.align 0
mov curbit, #1
cmp divisor, #0
beq Ldiv0
Bpl Over1
neg divisor, divisor @ Loops below use unsigned.
Over1:
push { work }
@ Need to save the sign of the dividend, unfortunately, we need
@ ip later on. Must do this after saving the original value of
@ the work register, because we will pop this value off first.
push { dividend }
cmp dividend, #0
Bpl Over2
neg dividend, dividend
Over2:
cmp dividend, divisor
bcc Lgot_result
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions. On the final pass, this may
@ subtract too much from the dividend, so keep track of which
@ subtractions are done, we can fix them up afterwards...
mov overdone, #0
cmp dividend, divisor
bcc Over3
sub dividend, dividend, divisor
Over3:
lsr work, divisor, #1
cmp dividend, work
bcc Over4
sub dividend, dividend, work
mov ip, curbit
mov work, #1
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over4:
lsr work, divisor, #2
cmp dividend, work
bcc Over5
sub dividend, dividend, work
mov ip, curbit
mov work, #2
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over5:
lsr work, divisor, #3
cmp dividend, work
bcc Over6
sub dividend, dividend, work
mov ip, curbit
mov work, #3
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over6:
mov ip, curbit
cmp dividend, #0 @ Early termination?
beq Over7
lsr curbit, #4 @ No, any more bits to do?
beq Over7
lsr divisor, #4
b Loop3
Over7:
@ Any subtractions that we should not have done will be recorded in
@ the top three bits of "overdone". Exactly which were not needed
@ are governed by the position of the bit, stored in ip.
@ If we terminated early, because dividend became zero,
@ then none of the below will match, since the bit in ip will not be
@ in the bottom nibble.
mov work, #0xe
lsl work, #28
and overdone, work
beq Lgot_result
mov curbit, ip
mov work, #3
ror curbit, work
tst overdone, curbit
beq Over8
lsr work, divisor, #3
add dividend, dividend, work
Over8:
mov curbit, ip
mov work, #2
ror curbit, work
tst overdone, curbit
beq Over9
lsr work, divisor, #2
add dividend, dividend, work
Over9:
mov curbit, ip
mov work, #1
ror curbit, work
tst overdone, curbit
beq Lgot_result
lsr work, divisor, #1
add dividend, dividend, work
Lgot_result:
pop { work }
cmp work, #0
bpl Over10
neg dividend, dividend
Over10:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
#else /* arm version */
SYM (__modsi3):
mov curbit, #1 mov curbit, #1
cmp divisor, #0 cmp divisor, #0
rsbmi divisor, divisor, #0 @ Loops below use unsigned. rsbmi divisor, divisor, #0 @ Loops below use unsigned.
...@@ -408,7 +901,7 @@ Lgot_result: ...@@ -408,7 +901,7 @@ Lgot_result:
ldr ip, [sp], #4 ldr ip, [sp], #4
cmp ip, #0 cmp ip, #0
rsbmi dividend, dividend, #0 rsbmi dividend, dividend, #0
RET pc, lr RET
Ldiv0: Ldiv0:
str lr, [sp, #-4]! str lr, [sp, #-4]!
...@@ -416,17 +909,17 @@ Ldiv0: ...@@ -416,17 +909,17 @@ Ldiv0:
mov r0, #0 @ about as wrong as it could be mov r0, #0 @ about as wrong as it could be
ldmia sp!, {pc}RETCOND ldmia sp!, {pc}RETCOND
#endif /* arm version */
SIZE (__modsi3) SIZE (__modsi3)
#endif /* L_modsi3 */ #endif /* L_modsi3 */
#ifdef L_dvmd_tls #ifdef L_dvmd_tls
.globl SYM (__div0) FUNC_START div0
TYPE (__div0)
.align 0 RET
SYM (__div0):
RET pc, lr
SIZE (__div0) SIZE (__div0)
...@@ -439,10 +932,8 @@ SYM (__div0): ...@@ -439,10 +932,8 @@ SYM (__div0):
#define SIGFPE 8 @ cant use <asm/signal.h> as it #define SIGFPE 8 @ cant use <asm/signal.h> as it
@ contains too much C rubbish @ contains too much C rubbish
.globl SYM (__div0) FUNC_START div0
TYPE (__div0)
.align 0
SYM (__div0):
stmfd sp!, {r1, lr} stmfd sp!, {r1, lr}
swi __NR_getpid swi __NR_getpid
cmn r0, #1000 cmn r0, #1000
...@@ -459,7 +950,10 @@ SYM (__div0): ...@@ -459,7 +950,10 @@ SYM (__div0):
assembler because their presence allows interworked code to be linked even assembler because their presence allows interworked code to be linked even
when the GCC library is this one. */ when the GCC library is this one. */
#ifdef L_call_via_rX /* Do not build the interworking functions when the target cpu
is the arm v3 architecture. (This is one of the multilib
options). */
#if defined L_call_via_rX && ! defined __ARM_ARCH_3__
/* These labels & instructions are used by the Arm/Thumb interworking code. /* These labels & instructions are used by the Arm/Thumb interworking code.
The address of function to be called is loaded into a register and then The address of function to be called is loaded into a register and then
...@@ -499,7 +993,10 @@ SYM (_call_via_\register): ...@@ -499,7 +993,10 @@ SYM (_call_via_\register):
#endif /* L_call_via_rX */ #endif /* L_call_via_rX */
#ifdef L_interwork_call_via_rX /* Do not build the interworking functions when the target cpu
is the arm v3 architecture. (This is one of the multilib
options). */
#if defined L_interwork_call_via_rX && ! defined __ARM_ARCH_3__
/* These labels & instructions are used by the Arm/Thumb interworking code, /* These labels & instructions are used by the Arm/Thumb interworking code,
when the target address is in an unknown instruction set. The address when the target address is in an unknown instruction set. The address
......
@ libgcc1 routines for ARM cpu.
@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk)
/* Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file. (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
.code 16
#ifndef __USER_LABEL_PREFIX__
#error __USER_LABEL_PREFIX__ not defined
#endif
#ifdef __elf__
#define __PLT__ (PLT)
#define TYPE(x) .type SYM(x),function
#define SIZE(x) .size SYM(x), . - SYM(x)
#else
#define __PLT__
#define TYPE(x)
#define SIZE(x)
#endif
#define RET mov pc, lr
/* ANSI concatenation macros. */
#define CONCAT1(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a ## b
/* Use the right prefix for global labels. */
#define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
work .req r4 @ XXXX is this safe ?
#ifdef L_udivsi3
dividend .req r0
divisor .req r1
result .req r2
curbit .req r3
ip .req r12
sp .req r13
lr .req r14
pc .req r15
.text
.globl SYM (__udivsi3)
TYPE (__udivsi3)
.align 0
.thumb_func
SYM (__udivsi3):
cmp divisor, #0
beq Ldiv0
mov curbit, #1
mov result, #0
push { work }
cmp dividend, divisor
bcc Lgot_result
@ Load the constant 0x10000000 into our work register
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions, and note which bits
@ are done in the result. On the final pass, this may subtract
@ too much from the dividend, but the result will be ok, since the
@ "bit" will have been shifted out at the bottom.
cmp dividend, divisor
bcc Over1
sub dividend, dividend, divisor
orr result, result, curbit
Over1:
lsr work, divisor, #1
cmp dividend, work
bcc Over2
sub dividend, dividend, work
lsr work, curbit, #1
orr result, work
Over2:
lsr work, divisor, #2
cmp dividend, work
bcc Over3
sub dividend, dividend, work
lsr work, curbit, #2
orr result, work
Over3:
lsr work, divisor, #3
cmp dividend, work
bcc Over4
sub dividend, dividend, work
lsr work, curbit, #3
orr result, work
Over4:
cmp dividend, #0 @ Early termination?
beq Lgot_result
lsr curbit, #4 @ No, any more bits to do?
beq Lgot_result
lsr divisor, #4
b Loop3
Lgot_result:
mov r0, result
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
SIZE (__udivsi3)
#endif /* L_udivsi3 */
#ifdef L_umodsi3
dividend .req r0
divisor .req r1
overdone .req r2
curbit .req r3
ip .req r12
sp .req r13
lr .req r14
pc .req r15
.text
.globl SYM (__umodsi3)
TYPE (__umodsi3)
.align 0
.thumb_func
SYM (__umodsi3):
cmp divisor, #0
beq Ldiv0
mov curbit, #1
cmp dividend, divisor
bcs Over1
RET
Over1:
@ Load the constant 0x10000000 into our work register
push { work }
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions. On the final pass, this may
@ subtract too much from the dividend, so keep track of which
@ subtractions are done, we can fix them up afterwards...
mov overdone, #0
cmp dividend, divisor
bcc Over2
sub dividend, dividend, divisor
Over2:
lsr work, divisor, #1
cmp dividend, work
bcc Over3
sub dividend, dividend, work
mov ip, curbit
mov work, #1
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over3:
lsr work, divisor, #2
cmp dividend, work
bcc Over4
sub dividend, dividend, work
mov ip, curbit
mov work, #2
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over4:
lsr work, divisor, #3
cmp dividend, work
bcc Over5
sub dividend, dividend, work
mov ip, curbit
mov work, #3
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over5:
mov ip, curbit
cmp dividend, #0 @ Early termination?
beq Over6
lsr curbit, #4 @ No, any more bits to do?
beq Over6
lsr divisor, #4
b Loop3
Over6:
@ Any subtractions that we should not have done will be recorded in
@ the top three bits of "overdone". Exactly which were not needed
@ are governed by the position of the bit, stored in ip.
@ If we terminated early, because dividend became zero,
@ then none of the below will match, since the bit in ip will not be
@ in the bottom nibble.
mov work, #0xe
lsl work, #28
and overdone, work
bne Over7
pop { work }
RET @ No fixups needed
Over7:
mov curbit, ip
mov work, #3
ror curbit, work
tst overdone, curbit
beq Over8
lsr work, divisor, #3
add dividend, dividend, work
Over8:
mov curbit, ip
mov work, #2
ror curbit, work
tst overdone, curbit
beq Over9
lsr work, divisor, #2
add dividend, dividend, work
Over9:
mov curbit, ip
mov work, #1
ror curbit, work
tst overdone, curbit
beq Over10
lsr work, divisor, #1
add dividend, dividend, work
Over10:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
SIZE (__umodsi3)
#endif /* L_umodsi3 */
#ifdef L_divsi3
dividend .req r0
divisor .req r1
result .req r2
curbit .req r3
ip .req r12
sp .req r13
lr .req r14
pc .req r15
.text
.globl SYM (__divsi3)
TYPE (__divsi3)
.align 0
.thumb_func
SYM (__divsi3):
cmp divisor, #0
beq Ldiv0
push { work }
mov work, dividend
eor work, divisor @ Save the sign of the result.
mov ip, work
mov curbit, #1
mov result, #0
cmp divisor, #0
bpl Over1
neg divisor, divisor @ Loops below use unsigned.
Over1:
cmp dividend, #0
bpl Over2
neg dividend, dividend
Over2:
cmp dividend, divisor
bcc Lgot_result
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
Bcs Lbignum
cmp divisor, dividend
Bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
lsl work, #3
Loop2:
cmp divisor, work
Bcs Loop3
cmp divisor, dividend
Bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions, and note which bits
@ are done in the result. On the final pass, this may subtract
@ too much from the dividend, but the result will be ok, since the
@ "bit" will have been shifted out at the bottom.
cmp dividend, divisor
Bcc Over3
sub dividend, dividend, divisor
orr result, result, curbit
Over3:
lsr work, divisor, #1
cmp dividend, work
Bcc Over4
sub dividend, dividend, work
lsr work, curbit, #1
orr result, work
Over4:
lsr work, divisor, #2
cmp dividend, work
Bcc Over5
sub dividend, dividend, work
lsr work, curbit, #2
orr result, result, work
Over5:
lsr work, divisor, #3
cmp dividend, work
Bcc Over6
sub dividend, dividend, work
lsr work, curbit, #3
orr result, result, work
Over6:
cmp dividend, #0 @ Early termination?
Beq Lgot_result
lsr curbit, #4 @ No, any more bits to do?
Beq Lgot_result
lsr divisor, #4
b Loop3
Lgot_result:
mov r0, result
mov work, ip
cmp work, #0
Bpl Over7
neg r0, r0
Over7:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
SIZE (__divsi3)
#endif /* L_divsi3 */
#ifdef L_modsi3
dividend .req r0
divisor .req r1
overdone .req r2
curbit .req r3
ip .req r12
sp .req r13
lr .req r14
pc .req r15
.text
.globl SYM (__modsi3)
TYPE (__modsi3)
.align 0
.thumb_func
SYM (__modsi3):
mov curbit, #1
cmp divisor, #0
beq Ldiv0
Bpl Over1
neg divisor, divisor @ Loops below use unsigned.
Over1:
push { work }
@ Need to save the sign of the dividend, unfortunately, we need
@ ip later on. Must do this after saving the original value of
@ the work register, because we will pop this value off first.
push { dividend }
cmp dividend, #0
Bpl Over2
neg dividend, dividend
Over2:
cmp dividend, divisor
bcc Lgot_result
mov work, #1
lsl work, #28
Loop1:
@ Unless the divisor is very big, shift it up in multiples of
@ four bits, since this is the amount of unwinding in the main
@ division loop. Continue shifting until the divisor is
@ larger than the dividend.
cmp divisor, work
bcs Lbignum
cmp divisor, dividend
bcs Lbignum
lsl divisor, #4
lsl curbit, #4
b Loop1
Lbignum:
@ Set work to 0x80000000
lsl work, #3
Loop2:
@ For very big divisors, we must shift it a bit at a time, or
@ we will be in danger of overflowing.
cmp divisor, work
bcs Loop3
cmp divisor, dividend
bcs Loop3
lsl divisor, #1
lsl curbit, #1
b Loop2
Loop3:
@ Test for possible subtractions. On the final pass, this may
@ subtract too much from the dividend, so keep track of which
@ subtractions are done, we can fix them up afterwards...
mov overdone, #0
cmp dividend, divisor
bcc Over3
sub dividend, dividend, divisor
Over3:
lsr work, divisor, #1
cmp dividend, work
bcc Over4
sub dividend, dividend, work
mov ip, curbit
mov work, #1
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over4:
lsr work, divisor, #2
cmp dividend, work
bcc Over5
sub dividend, dividend, work
mov ip, curbit
mov work, #2
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over5:
lsr work, divisor, #3
cmp dividend, work
bcc Over6
sub dividend, dividend, work
mov ip, curbit
mov work, #3
ror curbit, work
orr overdone, curbit
mov curbit, ip
Over6:
mov ip, curbit
cmp dividend, #0 @ Early termination?
beq Over7
lsr curbit, #4 @ No, any more bits to do?
beq Over7
lsr divisor, #4
b Loop3
Over7:
@ Any subtractions that we should not have done will be recorded in
@ the top three bits of "overdone". Exactly which were not needed
@ are governed by the position of the bit, stored in ip.
@ If we terminated early, because dividend became zero,
@ then none of the below will match, since the bit in ip will not be
@ in the bottom nibble.
mov work, #0xe
lsl work, #28
and overdone, work
beq Lgot_result
mov curbit, ip
mov work, #3
ror curbit, work
tst overdone, curbit
beq Over8
lsr work, divisor, #3
add dividend, dividend, work
Over8:
mov curbit, ip
mov work, #2
ror curbit, work
tst overdone, curbit
beq Over9
lsr work, divisor, #2
add dividend, dividend, work
Over9:
mov curbit, ip
mov work, #1
ror curbit, work
tst overdone, curbit
beq Lgot_result
lsr work, divisor, #1
add dividend, dividend, work
Lgot_result:
pop { work }
cmp work, #0
bpl Over10
neg dividend, dividend
Over10:
pop { work }
RET
Ldiv0:
push { lr }
bl SYM (__div0) __PLT__
mov r0, #0 @ about as wrong as it could be
pop { pc }
SIZE (__modsi3)
#endif /* L_modsi3 */
#ifdef L_dvmd_tls
.globl SYM (__div0)
TYPE (__div0)
.align 0
.thumb_func
SYM (__div0):
RET
SIZE (__div0)
#endif /* L_divmodsi_tools */
#ifdef L_call_via_rX
/* These labels & instructions are used by the Arm/Thumb interworking code.
The address of function to be called is loaded into a register and then
one of these labels is called via a BL instruction. This puts the
return address into the link register with the bottom bit set, and the
code here switches to the correct mode before executing the function. */
.text
.align 0
.macro call_via register
.globl SYM (_call_via_\register)
TYPE (_call_via_\register)
.thumb_func
SYM (_call_via_\register):
bx \register
nop
SIZE (_call_via_\register)
.endm
call_via r0
call_via r1
call_via r2
call_via r3
call_via r4
call_via r5
call_via r6
call_via r7
call_via r8
call_via r9
call_via sl
call_via fp
call_via ip
call_via sp
call_via lr
#endif /* L_call_via_rX */
#ifdef L_interwork_call_via_rX
/* These labels & instructions are used by the Arm/Thumb interworking code,
when the target address is in an unknown instruction set. The address
of function to be called is loaded into a register and then one of these
labels is called via a BL instruction. This puts the return address
into the link register with the bottom bit set, and the code here
switches to the correct mode before executing the function. Unfortunately
the target code cannot be relied upon to return via a BX instruction, so
instead we have to store the resturn address on the stack and allow the
called function to return here instead. Upon return we recover the real
return address and use a BX to get back to Thumb mode. */
.text
.align 0
.code 32
.globl _arm_return
_arm_return:
ldmia r13!, {r12}
bx r12
.macro interwork register
.code 16
.globl SYM (_interwork_call_via_\register)
TYPE (_interwork_call_via_\register)
.thumb_func
SYM (_interwork_call_via_\register):
bx pc
nop
.code 32
.globl .Lchange_\register
.Lchange_\register:
tst \register, #1
stmeqdb r13!, {lr}
adreq lr, _arm_return
bx \register
SIZE (_interwork_call_via_\register)
.endm
interwork r0
interwork r1
interwork r2
interwork r3
interwork r4
interwork r5
interwork r6
interwork r7
interwork r8
interwork r9
interwork sl
interwork fp
interwork ip
interwork sp
/* The lr case has to be handled a little differently...*/
.code 16
.globl SYM (_interwork_call_via_lr)
TYPE (_interwork_call_via_lr)
.thumb_func
SYM (_interwork_call_via_lr):
bx pc
nop
.code 32
.globl .Lchange_lr
.Lchange_lr:
tst lr, #1
stmeqdb r13!, {lr}
mov ip, lr
adreq lr, _arm_return
bx ip
SIZE (_interwork_call_via_lr)
#endif /* L_interwork_call_via_rX */
...@@ -38,7 +38,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -38,7 +38,7 @@ Boston, MA 02111-1307, USA. */
# define SUBTARGET_EXTRA_ASM_SPEC \ # define SUBTARGET_EXTRA_ASM_SPEC \
" %{mapcs-26:-mapcs-26} %{!mapcs-26:-mapcs-32}" " %{mapcs-26:-mapcs-26} %{!mapcs-26:-mapcs-32}"
# define MULTILIB_DEFAULTS \ # define MULTILIB_DEFAULTS \
{ "mlittle-endian", "mhard-float", "mapcs-32", "mno-thumb-interwork" } { "marm", "mlittle-endian", "mhard-float", "mapcs-32", "mno-thumb-interwork" }
# define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__" # define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__"
#else /* default is APCS-26 */ #else /* default is APCS-26 */
# define TARGET_DEFAULT (ARM_FLAG_MMU_TRAPS) # define TARGET_DEFAULT (ARM_FLAG_MMU_TRAPS)
...@@ -52,7 +52,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -52,7 +52,7 @@ Boston, MA 02111-1307, USA. */
# define SUBTARGET_EXTRA_ASM_SPEC \ # define SUBTARGET_EXTRA_ASM_SPEC \
" %{mapcs-32:-mapcs-32} %{!mapcs-32:-mapcs-26}" " %{mapcs-32:-mapcs-32} %{!mapcs-32:-mapcs-26}"
# define MULTILIB_DEFAULTS \ # define MULTILIB_DEFAULTS \
{ "mlittle-endian", "mhard-float", "mapcs-26", "mno-thumb-interwork" } { "marm", "mlittle-endian", "mhard-float", "mapcs-26", "mno-thumb-interwork" }
#endif #endif
/* This was defined in linux.h. Define it here also. */ /* This was defined in linux.h. Define it here also. */
...@@ -103,13 +103,6 @@ Boston, MA 02111-1307, USA. */ ...@@ -103,13 +103,6 @@ Boston, MA 02111-1307, USA. */
%{mbig-endian:-EB}" \ %{mbig-endian:-EB}" \
SUBTARGET_EXTRA_LINK_SPEC SUBTARGET_EXTRA_LINK_SPEC
#define ASM_SPEC "%{mbig-endian:-EB} \
%{mcpu=*:-m%*} %{march=*:-m%*} \
%{mthumb-interwork:-mthumb-interwork} \
%{msoft-float:-mno-fpu} \
%{mapcs-float:-mfloat}" \
SUBTARGET_EXTRA_ASM_SPEC
#undef CPP_PREDEFINES #undef CPP_PREDEFINES
#define CPP_PREDEFINES \ #define CPP_PREDEFINES \
"-Dunix -D__arm__ -Dlinux -D__ELF__ \ "-Dunix -D__arm__ -Dlinux -D__ELF__ \
...@@ -166,6 +159,8 @@ do { \ ...@@ -166,6 +159,8 @@ do { \
definition in the target-specific file which includes this file. */ definition in the target-specific file which includes this file. */
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS CONST_SECTION_FUNCTION #define SUBTARGET_EXTRA_SECTION_FUNCTIONS CONST_SECTION_FUNCTION
extern void text_section ();
#define CONST_SECTION_ASM_OP ".section\t.rodata" #define CONST_SECTION_ASM_OP ".section\t.rodata"
#define CONST_SECTION_FUNCTION \ #define CONST_SECTION_FUNCTION \
...@@ -302,8 +297,8 @@ const_section () \ ...@@ -302,8 +297,8 @@ const_section () \
#define FP_DEFAULT FP_SOFT3 #define FP_DEFAULT FP_SOFT3
/* Call the function profiler with a given profile label. */ /* Call the function profiler with a given profile label. */
#undef FUNCTION_PROFILER #undef ARM_FUNCTION_PROFILER
#define FUNCTION_PROFILER(STREAM, LABELNO) \ #define ARM_FUNCTION_PROFILER(STREAM, LABELNO) \
{ \ { \
fprintf (STREAM, "\tbl\tmcount%s\n", NEED_PLT_RELOC ? "(PLT)" : ""); \ fprintf (STREAM, "\tbl\tmcount%s\n", NEED_PLT_RELOC ? "(PLT)" : ""); \
} }
......
/* Definitions for Thumb running Linux-based GNU systems using ELF
Copyright (C) 1999 Free Software Foundation, Inc.
Contributed by Philip Blundell <philb@gnu.org>
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define SUBTARGET_EXTRA_ASM_SPEC \
" %{fPIC:-k} %{fpic:-k}"
/* This was defined in linux.h. Define it here also. */
#undef DEFAULT_VTABLE_THUNKS
#define DEFAULT_VTABLE_THUNKS 1
/* Handle #pragma weak and #pragma pack. */
#define HANDLE_SYSV_PRAGMA
/* Now we define the strings used to build the spec file. */
#define LIB_SPEC \
"%{shared: -lc} \
%{!shared: %{pthread:-lpthread} \
%{profile:-lc_p} %{!profile: -lc}}"
/* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add
the GNU/Linux magical crtbegin.o file (see crtstuff.c) which
provides part of the support for getting C++ file-scope static
object constructed before entering `main'. */
#define STARTFILE_SPEC \
"%{!shared: \
%{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} \
%{!p:%{profile:gcrt1.o%s} \
%{!profile:crt1.o%s}}}} \
crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}"
/* Provide a ENDFILE_SPEC appropriate for GNU/Linux. Here we tack on
the GNU/Linux magical crtend.o file (see crtstuff.c) which
provides part of the support for getting C++ file-scope static
object constructed before entering `main', followed by a normal
GNU/Linux "finalizer" file, `crtn.o'. */
#define ENDFILE_SPEC \
"%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s"
#define LINK_SPEC "%{h*} %{version:-v} \
%{b} %{Wl,*:%*} \
%{static:-Bstatic} \
%{shared:-shared} \
%{symbolic:-Bsymbolic} \
%{rdynamic:-export-dynamic} \
%{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2} \
-X -mno-fpu -p \
%{mbig-endian:-EB} \
-m armelf_linux"
#define CPP_PREDEFINES \
"-Dunix -Dthumb -D__thumb -Dlinux \
-Asystem(unix) -Asystem(posix) -Acpu(arm) \
-Amachine(arm) -D__ELF__"
/* Allow #sccs in preprocessor. */
#define SCCS_DIRECTIVE
#define USER_LABEL_PREFIX "" /* For ELF the default is no underscores */
#define LOCAL_LABEL_PREFIX "."
/* Attach a special .ident directive to the end of the file to identify
the version of GCC which compiled this code. */
#define IDENT_ASM_OP ".ident"
/* Output #ident as a .ident. */
#define ASM_OUTPUT_IDENT(FILE, NAME) \
fprintf (FILE, "\t%s\t\"%s\"\n", IDENT_ASM_OP, NAME);
#ifdef IDENTIFY_WITH_IDENT
#define ASM_IDENTIFY_GCC(FILE) /* nothing */
#define ASM_IDENTIFY_LANGUAGE(FILE) \
fprintf (FILE, "\t%s \"GCC (%s) %s\"\n", IDENT_ASM_OP, \
lang_identify (), version_string)
#else
#define ASM_FILE_END(FILE) \
do \
{ \
if (!flag_no_ident) \
fprintf (FILE, "\t%s\t\"GCC: (GNU) %s\"\n", \
IDENT_ASM_OP, version_string); \
} \
while (0)
#endif
/* Support const sections and the ctors and dtors sections for g++.
Note that there appears to be two different ways to support const
sections at the moment. You can either #define the symbol
READONLY_DATA_SECTION (giving it some code which switches to the
readonly data section) or else you can #define the symbols
EXTRA_SECTIONS, EXTRA_SECTION_FUNCTIONS, SELECT_SECTION, and
SELECT_RTX_SECTION. We do both here just to be on the safe side. */
#define USE_CONST_SECTION 1
/* Support for Constructors and Destructors. */
#define READONLY_DATA_SECTION() const_section ()
/* A default list of other sections which we might be "in" at any given
time. For targets that use additional sections (e.g. .tdesc) you
should override this definition in the target-specific file which
includes this file. */
#define SUBTARGET_EXTRA_SECTIONS in_const,
/* A default list of extra section function definitions. For targets
that use additional sections (e.g. .tdesc) you should override this
definition in the target-specific file which includes this file. */
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS CONST_SECTION_FUNCTION
#define CONST_SECTION_ASM_OP ".section\t.rodata"
#define CONST_SECTION_FUNCTION \
void \
const_section () \
{ \
if (!USE_CONST_SECTION) \
text_section (); \
else if (in_section != in_const) \
{ \
fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \
in_section = in_const; \
} \
}
/* A C statement or statements to switch to the appropriate
section for output of DECL. DECL is either a `VAR_DECL' node
or a constant of some sort. RELOC indicates whether forming
the initial value of DECL requires link-time relocations. */
#define SELECT_SECTION(DECL, RELOC) \
{ \
if (TREE_CODE (DECL) == STRING_CST) \
{ \
if (! flag_writable_strings) \
const_section (); \
else \
data_section (); \
} \
else if (TREE_CODE (DECL) == VAR_DECL) \
{ \
if ((flag_pic && RELOC) \
|| !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \
|| !DECL_INITIAL (DECL) \
|| (DECL_INITIAL (DECL) != error_mark_node \
&& !TREE_CONSTANT (DECL_INITIAL (DECL)))) \
data_section (); \
else \
const_section (); \
} \
else \
const_section (); \
}
/* A C statement or statements to switch to the appropriate
section for output of RTX in mode MODE. RTX is some kind
of constant in RTL. The argument MODE is redundant except
in the case of a `const_int' rtx. Currently, these always
go into the const section. */
#define SELECT_RTX_SECTION(MODE, RTX) const_section ()
/* On svr4, we *do* have support for the .init and .fini sections, and we
can put stuff in there to be executed before and after `main'. We let
crtstuff.c and other files know this by defining the following symbols.
The definitions say how to change sections to the .init and .fini
sections. This is the same for all known svr4 assemblers. */
#define INIT_SECTION_ASM_OP ".section\t.init"
#define FINI_SECTION_ASM_OP ".section\t.fini"
/* This is how we tell the assembler that a symbol is weak. */
#define ASM_WEAKEN_LABEL(FILE, NAME) \
do \
{ \
fputs ("\t.weak\t", FILE); \
assemble_name (FILE, NAME); \
fputc ('\n', FILE); \
} \
while (0)
#include "arm/telf.h"
#include "arm/linux-tgas.h"
/* Run-time Target Specification. */
#undef TARGET_VERSION
#define TARGET_VERSION fputs (" (Thumb GNU/Linux)", stderr);
/* Definitions of target machine for GNU compiler.
Thumb Linux-based GNU systems version.
Copyright (C) 1999 Free Software Foundation, Inc.
Contributed by Russell King <rmk92@ecs.soton.ac.uk>.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* We are using GAS, so stabs should work. */
#ifndef DBX_DEBUGGING_INFO
#define DBX_DEBUGGING_INFO 1
#endif
/* This is how we tell the assembler that a symbol is weak.
GAS always supports weak symbols. */
#define ASM_WEAKEN_LABEL(FILE, NAME) \
do \
{ \
fputs ("\t.weak\t", FILE); \
assemble_name (FILE, NAME); \
fputc ('\n', FILE); \
} \
while (0)
/* This is used in ASM_FILE_START */
#undef ARM_OS_NAME
#define ARM_OS_NAME "Linux"
/* Unsigned chars produces much better code than signed. */
#define DEFAULT_SIGNED_CHAR 0
#undef SUBTARGET_CPP_SPEC
#define SUBTARGET_CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__}"
#undef SIZE_TYPE
#define SIZE_TYPE "unsigned int"
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"
#undef WCHAR_TYPE
#define WCHAR_TYPE "long int"
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE BITS_PER_WORD
/* Emit code to set up a trampoline and synchronise the caches. */
#undef INITIALIZE_TRAMPOLINE
#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
{ \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 8)), \
CXT); \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 12)), \
FNADDR); \
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__clear_cache"), \
0, VOIDmode, 2, TRAMP, Pmode, \
plus_constant (TRAMP, TRAMPOLINE_SIZE), Pmode); \
}
#if 0
/* Clear the instruction cache from `beg' to `end'. This makes an
inline system call to SYS_cacheflush. */
#define CLEAR_INSN_CACHE(BEG, END) \
{ \
register unsigned long _beg __asm ("a1") = (unsigned long) (BEG); \
register unsigned long _end __asm ("a2") = (unsigned long) (END); \
register unsigned long _flg __asm ("a3") = 0; \
__asm __volatile ("swi 0x9f0002"); \
}
#endif
...@@ -37,7 +37,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -37,7 +37,7 @@ Boston, MA 02111-1307, USA. */
#define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm6 #define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm6
/* Default is to use APCS-32 mode. */ /* Default is to use APCS-32 mode. */
#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_SOFT_FLOAT) #define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_FRAME)
#include "arm/aout.h" #include "arm/aout.h"
...@@ -105,8 +105,8 @@ Boston, MA 02111-1307, USA. */ ...@@ -105,8 +105,8 @@ Boston, MA 02111-1307, USA. */
compiling the profiling functions. Since we break Acorn CC compiling the profiling functions. Since we break Acorn CC
compatibility below a little more won't hurt. */ compatibility below a little more won't hurt. */
#undef FUNCTION_PROFILER #undef ARM_FUNCTION_PROFILER
#define FUNCTION_PROFILER(STREAM,LABELNO) \ #define ARM_FUNCTION_PROFILER(STREAM,LABELNO) \
{ \ { \
fprintf(STREAM, "\tmov\t%sip, %slr\n", REGISTER_PREFIX, REGISTER_PREFIX); \ fprintf(STREAM, "\tmov\t%sip, %slr\n", REGISTER_PREFIX, REGISTER_PREFIX); \
fprintf(STREAM, "\tbl\tmcount\n"); \ fprintf(STREAM, "\tbl\tmcount\n"); \
......
...@@ -27,7 +27,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -27,7 +27,7 @@ Boston, MA 02111-1307, USA. */
#include "tree.h" #include "tree.h"
#include "expr.h" #include "expr.h"
#include "toplev.h" #include "toplev.h"
#include "arm-protos.h" #include "tm_p.h"
extern int current_function_anonymous_args; extern int current_function_anonymous_args;
...@@ -59,6 +59,7 @@ arm_pe_valid_machine_decl_attribute (decl, attributes, attr, args) ...@@ -59,6 +59,7 @@ arm_pe_valid_machine_decl_attribute (decl, attributes, attr, args)
if (is_attribute_p ("dllexport", attr)) if (is_attribute_p ("dllexport", attr))
return 1; return 1;
if (is_attribute_p ("dllimport", attr)) if (is_attribute_p ("dllimport", attr))
return 1; return 1;
...@@ -118,45 +119,6 @@ arm_pe_merge_machine_decl_attributes (old, new) ...@@ -118,45 +119,6 @@ arm_pe_merge_machine_decl_attributes (old, new)
return a; return a;
} }
#if 0
/* Check a type that has a virtual table, and see if any virtual methods are
marked for import or export, and if so, arrange for the vtable to
be imported or exported. */
static int
arm_check_vtable_importexport (type)
tree type;
{
tree methods = TYPE_METHODS (type);
tree fndecl;
if (TREE_CODE (methods) == FUNCTION_DECL)
fndecl = methods;
else if (TREE_VEC_ELT (methods, 0) != NULL_TREE)
fndecl = TREE_VEC_ELT (methods, 0);
else
fndecl = TREE_VEC_ELT (methods, 1);
while (fndecl)
{
if (DECL_VIRTUAL_P (fndecl) || DECL_VINDEX (fndecl) != NULL_TREE)
{
tree exp = lookup_attribute ("dllimport",
DECL_MACHINE_ATTRIBUTES (fndecl));
if (exp == 0)
exp = lookup_attribute ("dllexport",
DECL_MACHINE_ATTRIBUTES (fndecl));
if (exp)
return 1;
}
fndecl = TREE_CHAIN (fndecl);
}
return 0;
}
#endif
/* Return non-zero if DECL is a dllexport'd object. */ /* Return non-zero if DECL is a dllexport'd object. */
tree current_class_type; /* FIXME */ tree current_class_type; /* FIXME */
...@@ -174,23 +136,6 @@ arm_dllexport_p (decl) ...@@ -174,23 +136,6 @@ arm_dllexport_p (decl)
if (exp) if (exp)
return 1; return 1;
#if 0 /* This was a hack to get vtable's exported or imported since only one
copy of them is ever output. Disabled pending better solution. */
/* For C++, the vtables might have to be marked. */
if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
{
if (TREE_PUBLIC (decl)
&& DECL_EXTERNAL (decl) == 0
&& (DECL_CONTEXT (decl)
? arm_check_vtable_importexport (DECL_CONTEXT (decl))
: current_class_type
? arm_check_vtable_importexport (current_class_type)
: 0)
)
return 1;
}
#endif
return 0; return 0;
} }
...@@ -213,23 +158,6 @@ arm_dllimport_p (decl) ...@@ -213,23 +158,6 @@ arm_dllimport_p (decl)
if (imp) if (imp)
return 1; return 1;
#if 0 /* This was a hack to get vtable's exported or imported since only one
copy of them is ever output. Disabled pending better solution. */
/* For C++, the vtables might have to be marked. */
if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
{
if (TREE_PUBLIC (decl)
&& DECL_EXTERNAL (decl)
&& (DECL_CONTEXT (decl)
? arm_check_vtable_importexport (DECL_CONTEXT (decl))
: current_class_type
? arm_check_vtable_importexport (current_class_type)
: 0)
)
return 1;
}
#endif
return 0; return 0;
} }
...@@ -437,70 +365,3 @@ arm_pe_unique_section (decl, reloc) ...@@ -437,70 +365,3 @@ arm_pe_unique_section (decl, reloc)
DECL_SECTION_NAME (decl) = build_string (len, string); DECL_SECTION_NAME (decl) = build_string (len, string);
} }
/* This is to better conform to the ARM PCS.
Richard Earnshaw hasn't put this into FSF sources yet so it's here. */
int
arm_pe_return_in_memory (type)
tree type;
{
if (TREE_CODE (type) == RECORD_TYPE)
{
tree field;
int num_fields = 0;
/* For a record containing just a single element, we can be a little
less restrictive. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL && ! TREE_STATIC (field))
{
if ((AGGREGATE_TYPE_P (TREE_TYPE (field))
&& RETURN_IN_MEMORY (TREE_TYPE (field)))
|| FLOAT_TYPE_P (TREE_TYPE (field)))
return 1;
num_fields++;
}
}
if (num_fields == 1)
return 0;
/* For a struct, we can return in a register if every element was a
bit-field and it all fits in one word. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL
&& ! TREE_STATIC (field)
&& (! DECL_BIT_FIELD_TYPE (field)
|| (host_integerp (DECL_SIZE (field), 1)
&& host_integerp (bit_position (field), 1)
&& 32 < (int_bit_position (field)
+ tree_low_cst (DECL_SIZE (field), 1)))))
return 1;
}
return 0;
}
else if (TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE)
{
tree field;
/* Unions can be returned in registers if every element is
integral, or can be returned in an integer register. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL
&& ! TREE_STATIC (field)
&& ((AGGREGATE_TYPE_P (TREE_TYPE (field))
&& RETURN_IN_MEMORY (TREE_TYPE (field)))
|| FLOAT_TYPE_P (TREE_TYPE (field))))
return 1;
}
return 0;
}
/* XXX Not sure what should be done for other aggregates, so put them in
memory. */
return 1;
}
...@@ -19,6 +19,9 @@ along with GNU CC; see the file COPYING. If not, write to ...@@ -19,6 +19,9 @@ along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330, the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
/* Enable PE specific code. */
#define ARM_PE 1
#define ARM_PE_FLAG_CHAR '@' #define ARM_PE_FLAG_CHAR '@'
/* Ensure that @x. will be stripped from the function name. */ /* Ensure that @x. will be stripped from the function name. */
...@@ -42,11 +45,9 @@ Boston, MA 02111-1307, USA. */ ...@@ -42,11 +45,9 @@ Boston, MA 02111-1307, USA. */
say __declspec__, and passing args to it. The problem with that approach say __declspec__, and passing args to it. The problem with that approach
is that args are not accumulated: each new appearance would clobber any is that args are not accumulated: each new appearance would clobber any
existing args. */ existing args. */
#undef CPP_PREDEFINES #undef SUBTARGET_CPP_SPEC
#define CPP_PREDEFINES "\ #define SUBTARGET_CPP_SPEC "-D__pe__ -D__declspec(x)=__attribute__((x))"
-Darm -D__pe__ -Acpu(arm) -Amachine(arm) \
-D__declspec(x)=__attribute__((x)) \
"
/* Experimental addition for pr 7885. /* Experimental addition for pr 7885.
Ignore dllimport for functions. */ Ignore dllimport for functions. */
...@@ -89,13 +90,6 @@ Boston, MA 02111-1307, USA. */ ...@@ -89,13 +90,6 @@ Boston, MA 02111-1307, USA. */
1,1,1 \ 1,1,1 \
} }
/* This is to better conform to the ARM PCS.
Richard Earnshaw hasn't put this into FSF sources yet so it's here. */
#undef RETURN_IN_MEMORY
#define RETURN_IN_MEMORY(TYPE) \
((TYPE_MODE ((TYPE)) == BLKmode && ! TYPE_NO_FORCE_BLK (TYPE)) \
|| (AGGREGATE_TYPE_P ((TYPE)) && arm_pe_return_in_memory ((TYPE))))
/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS /* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
is a valid machine specific attribute for DECL. is a valid machine specific attribute for DECL.
The attributes in ATTRIBUTES have previously been assigned to DECL. */ The attributes in ATTRIBUTES have previously been assigned to DECL. */
...@@ -121,13 +115,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -121,13 +115,7 @@ Boston, MA 02111-1307, USA. */
to handle vtables - the first pass won't do anything because to handle vtables - the first pass won't do anything because
DECL_CONTEXT (DECL) will be 0 so arm_dll{ex,im}port_p will return 0. DECL_CONTEXT (DECL) will be 0 so arm_dll{ex,im}port_p will return 0.
It's also used to handle dllimport override semantics. */ It's also used to handle dllimport override semantics. */
#if 0
#define REDO_SECTION_INFO_P(DECL) \
((DECL_MACHINE_ATTRIBUTES (DECL) != NULL_TREE) \
|| (TREE_CODE (DECL) == VAR_DECL && DECL_VIRTUAL_P (DECL)))
#else
#define REDO_SECTION_INFO_P(DECL) 1 #define REDO_SECTION_INFO_P(DECL) 1
#endif
/* Define this macro if in some cases global symbols from one translation /* Define this macro if in some cases global symbols from one translation
unit may not be bound to undefined symbols in another translation unit unit may not be bound to undefined symbols in another translation unit
...@@ -180,7 +168,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -180,7 +168,7 @@ Boston, MA 02111-1307, USA. */
/* Output a reference to a label. */ /* Output a reference to a label. */
#undef ASM_OUTPUT_LABELREF #undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ #define ASM_OUTPUT_LABELREF(STREAM, NAME) \
fprintf (STREAM, "%s%s", USER_LABEL_PREFIX, arm_strip_name_encoding (NAME)) asm_fprintf (STREAM, "%U%s", arm_strip_name_encoding (NAME))
/* Output a function definition label. */ /* Output a function definition label. */
#undef ASM_DECLARE_FUNCTION_NAME #undef ASM_DECLARE_FUNCTION_NAME
...@@ -190,13 +178,14 @@ Boston, MA 02111-1307, USA. */ ...@@ -190,13 +178,14 @@ Boston, MA 02111-1307, USA. */
if (arm_dllexport_name_p (NAME)) \ if (arm_dllexport_name_p (NAME)) \
{ \ { \
drectve_section (); \ drectve_section (); \
fprintf (STREAM, "\t.ascii \" -export:%s\"\n",\ fprintf (STREAM, "\t.ascii \" -export:%s\"\n", \
arm_strip_name_encoding (NAME)); \ arm_strip_name_encoding (NAME)); \
function_section (DECL); \ function_section (DECL); \
} \ } \
if (TARGET_POKE_FUNCTION_NAME) \ ARM_DECLARE_FUNCTION_NAME (STREAM, NAME, DECL); \
arm_poke_function_name ((STREAM), (NAME)); \ if (TARGET_THUMB) \
ASM_OUTPUT_LABEL ((STREAM), (NAME)); \ fprintf (STREAM, "\t.code 16\n"); \
ASM_OUTPUT_LABEL (STREAM, NAME); \
} \ } \
while (0) while (0)
......
...@@ -23,10 +23,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -23,10 +23,7 @@ Boston, MA 02111-1307, USA. */
#define LIB_SPEC "-lc" #define LIB_SPEC "-lc"
#define CPP_PREDEFINES \ #define SUBTARGET_CPP_SPEC "-D__semi__"
"-Darm -D__semi__ -Acpu(arm) -Amachine(arm)"
#define ASM_SPEC "%{mbig-endian:-EB}"
#define LINK_SPEC "%{mbig-endian:-EB} -X" #define LINK_SPEC "%{mbig-endian:-EB} -X"
...@@ -35,7 +32,23 @@ Boston, MA 02111-1307, USA. */ ...@@ -35,7 +32,23 @@ Boston, MA 02111-1307, USA. */
#endif #endif
#ifndef TARGET_DEFAULT #ifndef TARGET_DEFAULT
#define TARGET_DEFAULT ARM_FLAG_APCS_32 #define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME)
#endif
#ifndef SUBTARGET_EXTRA_ASM_SPEC
#define SUBTARGET_EXTRA_ASM_SPEC
#endif
#ifndef ASM_SPEC
#define ASM_SPEC "\
%{mbig-endian:-EB} \
%{mcpu=*:-m%*} \
%{march=*:-m%*} \
%{mapcs-*:-mapcs-%*} \
%{mapcs-float:-mfloat} \
%{msoft-float:-mno-fpu} \
%{mthumb-interwork:-mthumb-interwork} \
" SUBTARGET_EXTRA_ASM_SPEC
#endif #endif
#include "arm/aout.h" #include "arm/aout.h"
......
/* Definitions for Thumb running ucLinux using ELF /* Definitions for StrongARM systems using COFF
Copyright (C) 1999 Free Software Foundation, Inc. Copyright (C) 1999 Free Software Foundation, Inc.
Contributed by Philip Blundell <pb@nexus.co.uk> Contributed by Catherine Moore <clm@cygnus.com>
This file is part of GNU CC. This file is part of GNU CC.
...@@ -19,22 +19,13 @@ along with this program; see the file COPYING. If not, write to ...@@ -19,22 +19,13 @@ along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330, the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
#include "arm/linux-telf.h" /* Run-time Target Specification. */
#ifndef SUBTARGET_CPU_DEFAULT
#define SUBTARGET_CPU_DEFAULT TARGET_CPU_strongarm
#endif
#include "coff.h"
#undef TARGET_VERSION #undef TARGET_VERSION
#define TARGET_VERSION fputs (" (Thumb/ELF ucLinux)", stderr); #define TARGET_VERSION fputs (" (StrongARM/COFF)", stderr);
#undef TARGET_DEFAULT
#define TARGET_DEFAULT (THUMB_FLAG_SINGLE_PIC_BASE)
/* We don't want a PLT. */
#undef NEED_PLT_RELOC
#define NEED_PLT_RELOC 0
/* On svr4, we *do* have support for the .init and .fini sections, and we
can put stuff in there to be executed before and after `main'. We let
crtstuff.c and other files know this by defining the following symbols.
The definitions say how to change sections to the .init and .fini
sections. This is the same for all known svr4 assemblers. */
#define INIT_SECTION_ASM_OP ".section\t.init"
#define FINI_SECTION_ASM_OP ".section\t.fini"
/* Definitions for non-Linux based StrongARM systems using ELF
Copyright (C) 1999 Free Software Foundation, Inc.
Contributed by Catherine Moore <clm@cygnus.com>
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Run-time Target Specification. */
#ifndef TARGET_VERSION
#define TARGET_VERSION fputs (" (StrongARM/ELF non-Linux)", stderr);
#endif
#ifndef SUBTARGET_CPU_DEFAULT
#define SUBTARGET_CPU_DEFAULT TARGET_CPU_strongarm
#endif
#include "unknown-elf.h"
CROSS_LIBGCC1 = libgcc1-asm.a CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1funcs.asm LIB1ASMSRC = arm/lib1funcs.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX
# We want fine grained libraries, so use the new code to build the # We want fine grained libraries, so use the new code to build the
# floating point emulation libraries. # floating point emulation libraries.
...@@ -21,12 +21,10 @@ dp-bit.c: $(srcdir)/config/fp-bit.c ...@@ -21,12 +21,10 @@ dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#endif' >> dp-bit.c echo '#endif' >> dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c cat $(srcdir)/config/fp-bit.c >> dp-bit.c
# Avoid building a duplicate set of libraries for the default endian-ness. # MULTILIB_OPTIONS = mhard-float/msoft-float mapcs-32/mapcs-26 mno-thumb-interwork/mthumb-interwork arm/thumb
MULTILIB_OPTIONS = mlittle-endian/mbig-endian mhard-float # MULTILIB_DIRNAMES = le be fpu soft 32bit 26bit normal interwork arm thumb
MULTILIB_DIRNAMES = le be fpu # MULTILIB_MATCHES =
MULTILIB_MATCHES = # MULTILIB_EXCEPTIONS = *mapcs-26/*mthumb-interwork* *mpacs-26/*mthumb*
LIBGCC = stmp-multilib LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib INSTALL_LIBGCC = install-multilib
CROSS_LIBGCC1 = libgcc1-asm.a CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1thumb.asm LIB1ASMSRC = arm/lib1funcs.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX
# adddi3/subdi3 added to machine description
# We want fine grained libraries, so use the new code to build the # We want fine grained libraries, so use the new code to build the
# floating point emulation libraries. # floating point emulation libraries.
...@@ -22,13 +21,15 @@ dp-bit.c: $(srcdir)/config/fp-bit.c ...@@ -22,13 +21,15 @@ dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#endif' >> dp-bit.c echo '#endif' >> dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c cat $(srcdir)/config/fp-bit.c >> dp-bit.c
# Avoid building a duplicate set of libraries for the default endian-ness. MULTILIB_OPTIONS = mlittle-endian/mbig-endian mhard-float/msoft-float marm/mthumb mno-thumb-interwork/mthumb-interwork
MULTILIB_OPTIONS = mlittle-endian/mbig-endian mno-thumb-interwork/mthumb-interwork fno-leading-underscore/fleading-underscore MULTILIB_DIRNAMES = le be fpu soft arm thumb normal interwork
MULTILIB_DIRNAMES = le be normal interwork elf under MULTILIB_MATCHES =
MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle
EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o
LIBGCC = stmp-multilib LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib INSTALL_LIBGCC = install-multilib
TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc # Currently there is a bug somwehere in GCC's alias analysis
# or scheduling code that is breaking _fpmul_parts in libgcc1.c.
# Disabling function inlining is a workaround for this problem.
TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline
CROSS_LIBGCC1 = libgcc1-asm.a CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1funcs.asm LIB1ASMSRC = arm/lib1funcs.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func _call_via_rX _interwork_call_via_rX
# We want fine grained libraries, so use the new code to build the # We want fine grained libraries, so use the new code to build the
# floating point emulation libraries. # floating point emulation libraries.
...@@ -67,7 +67,16 @@ dp-bit.c: $(srcdir)/config/fp-bit.c ...@@ -67,7 +67,16 @@ dp-bit.c: $(srcdir)/config/fp-bit.c
# LIBGCC = stmp-multilib # LIBGCC = stmp-multilib
# INSTALL_LIBGCC = install-multilib # INSTALL_LIBGCC = install-multilib
MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle
EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o
# If EXTRA_MULTILIB_PARTS is not defined above then define EXTRA_PARTS here # If EXTRA_MULTILIB_PARTS is not defined above then define EXTRA_PARTS here
EXTRA_PARTS = crtbegin.o crtend.o # EXTRA_PARTS = crtbegin.o crtend.o
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc # Currently there is a bug somewhere in GCC's alias analysis
# or scheduling code that is breaking _fpmul_parts in libgcc1.c.
# Disabling function inlining is a workaround for this problem.
TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc -fno-inline
...@@ -5,3 +5,5 @@ LIBGCC2_DEBUG_CFLAGS = -g0 ...@@ -5,3 +5,5 @@ LIBGCC2_DEBUG_CFLAGS = -g0
# Don't build enquire # Don't build enquire
ENQUIRE= ENQUIRE=
...@@ -24,8 +24,9 @@ dp-bit.c: $(srcdir)/config/fp-bit.c ...@@ -24,8 +24,9 @@ dp-bit.c: $(srcdir)/config/fp-bit.c
pe.o: $(srcdir)/config/arm/pe.c pe.o: $(srcdir)/config/arm/pe.c
$(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/arm/pe.c $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/arm/pe.c
MULTILIB_OPTIONS = mhard-float MULTILIB_OPTIONS = mhard-float mthumb
MULTILIB_DIRNAMES = fpu MULTILIB_DIRNAMES = fpu thumb
LIBGCC = stmp-multilib LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib INSTALL_LIBGCC = install-multilib
TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc
# Makefile fragment
# Copyright (c) 1998 Free Software Foundation
CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1thumb.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
echo '#ifndef __ARMEB__' >> fp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c
echo '#endif' >> fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#ifndef __ARMEB__' > dp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c
echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c
echo '#endif' >> dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c
# Rule to build Psion specific GCC functions.
pe.o: $(srcdir)/config/arm/pe.c
$(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/arm/pe.c
# Avoid building a duplicate set of libraries for the default endian-ness.
MULTILIB_OPTIONS = mthumb-interwork
MULTILIB_DIRNAMES = interwork
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
...@@ -64,3 +64,4 @@ libgcc1-atest: libgcc1-test.o native $(GCC_PARTS) $(EXTRA_PARTS) ...@@ -64,3 +64,4 @@ libgcc1-atest: libgcc1-test.o native $(GCC_PARTS) $(EXTRA_PARTS)
@echo "Testing libgcc1. Ignore linker warning messages." @echo "Testing libgcc1. Ignore linker warning messages."
$(GCC_FOR_TARGET) $(GCC_CFLAGS) libgcc1-test.o -o libgcc1-test \ $(GCC_FOR_TARGET) $(GCC_CFLAGS) libgcc1-test.o -o libgcc1-test \
-v -v
CROSS_LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1thumb.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX
# adddi3/subdi3 added to machine description
#LIB1ASMFUNCS = _adddi3 _subdi3 _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
echo '#ifndef __ARMEB__' >> fp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c
echo '#endif' >> fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#ifndef __ARMEB__' > dp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c
echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c
echo '#endif' >> dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c
# Avoid building a duplicate set of libraries for the default endian-ness.
MULTILIB_OPTIONS = mlittle-endian/mbig-endian mno-thumb-interwork/mthumb-interwork
MULTILIB_DIRNAMES = le be normal interwork
MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
# Just for these, we omit the frame pointer since it makes such a big
# difference. It is then pointless adding debugging.
TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC
LIBGCC2_DEBUG_CFLAGS = -g0
# Don't build enquire
ENQUIRE=
# Since libgcc1 is an assembler file, we can build it automatically for the
# cross-compiler.
CROSS_LIBGCC1 = libgcc1-asm.a
LIBGCC1 = libgcc1-asm.a
LIB1ASMSRC = arm/lib1thumb.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _call_via_rX _interwork_call_via_rX
MULTILIB_OPTIONS = mlittle-endian/mbig-endian
MULTILIB_DIRNAMES = le be
MULTILIB_MATCHES = mbig-endian=mbe mlittle-endian=mle
EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
echo '#ifndef __ARMEB__' >> fp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c
echo '#endif' >> fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
dp-bit.c: $(srcdir)/config/fp-bit.c
echo '#ifndef __ARMEB__' > dp-bit.c
echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c
echo '#define FLOAT_WORD_ORDER_MISMATCH' >> dp-bit.c
echo '#endif' >> dp-bit.c
cat $(srcdir)/config/fp-bit.c >> dp-bit.c
/* Definitions of target machine for GNU compiler,
for Thumb with COFF obj format.
Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
Derived from arm/coff.h originally by Doug Evans (dje@cygnus.com).
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "arm/thumb.h"
/* Run-time Target Specification. */
#undef TARGET_VERSION
#define TARGET_VERSION fputs (" (Thumb/coff)", stderr)
#define MULTILIB_DEFAULTS { "mlittle-endian" }
/* Setting this to 32 produces more efficient code, but the value set in previous
versions of this toolchain was 8, which produces more compact structures. The
command line option -mstructure_size_boundary=<n> can be used to change this
value. */
#undef STRUCTURE_SIZE_BOUNDARY
#define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary
extern int arm_structure_size_boundary;
/* This is COFF, but prefer stabs. */
#define SDB_DEBUGGING_INFO
#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG
#include "dbxcoff.h"
#undef LOCAL_LABEL_PREFIX
#define LOCAL_LABEL_PREFIX "."
/* Note - it is important that this definition matches the one in coff.h */
#undef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX "_"
/* A C statement to output assembler commands which will identify the
object file as having been compiled with GNU CC (or another GNU
compiler). */
#define ASM_IDENTIFY_GCC(STREAM) \
fprintf (STREAM, "%sgcc2_compiled.:\n", LOCAL_LABEL_PREFIX )
#undef ASM_FILE_START
#define ASM_FILE_START(STREAM) \
do { \
fprintf ((STREAM), "%s Generated by gcc %s for Thumb/coff\n", \
ASM_COMMENT_START, version_string); \
fprintf ((STREAM), ASM_APP_OFF); \
} while (0)
/* A C statement to output something to the assembler file to switch to section
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
NULL_TREE. Some target formats do not support arbitrary sections. Do not
define this macro in such cases. */
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do { \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"x\"\n", (NAME)); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"\"\n", (NAME)); \
else \
fprintf (STREAM, "\t.section %s,\"w\"\n", (NAME)); \
} while (0)
/* Support the ctors/dtors and other sections. */
#undef INIT_SECTION_ASM_OP
/* Define this macro if jump tables (for `tablejump' insns) should be
output in the text section, along with the assembler instructions.
Otherwise, the readonly data section is used. */
#define JUMP_TABLES_IN_TEXT_SECTION 1
#undef READONLY_DATA_SECTION
#define READONLY_DATA_SECTION rdata_section
#undef RDATA_SECTION_ASM_OP
#define RDATA_SECTION_ASM_OP "\t.section .rdata"
#undef CTORS_SECTION_ASM_OP
#define CTORS_SECTION_ASM_OP "\t.section .ctors,\"x\""
#undef DTORS_SECTION_ASM_OP
#define DTORS_SECTION_ASM_OP "\t.section .dtors,\"x\""
/* A list of other sections which the compiler might be "in" at any
given time. */
#undef EXTRA_SECTIONS
#define EXTRA_SECTIONS SUBTARGET_EXTRA_SECTIONS in_rdata, in_ctors, in_dtors
#define SUBTARGET_EXTRA_SECTIONS
/* A list of extra section function definitions. */
#undef EXTRA_SECTION_FUNCTIONS
#define EXTRA_SECTION_FUNCTIONS \
RDATA_SECTION_FUNCTION \
CTORS_SECTION_FUNCTION \
DTORS_SECTION_FUNCTION \
SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define RDATA_SECTION_FUNCTION \
void \
rdata_section () \
{ \
if (in_section != in_rdata) \
{ \
fprintf (asm_out_file, "%s\n", RDATA_SECTION_ASM_OP); \
in_section = in_rdata; \
} \
}
#define CTORS_SECTION_FUNCTION \
void \
ctors_section () \
{ \
if (in_section != in_ctors) \
{ \
fprintf (asm_out_file, "%s\n", CTORS_SECTION_ASM_OP); \
in_section = in_ctors; \
} \
}
#define DTORS_SECTION_FUNCTION \
void \
dtors_section () \
{ \
if (in_section != in_dtors) \
{ \
fprintf (asm_out_file, "%s\n", DTORS_SECTION_ASM_OP); \
in_section = in_dtors; \
} \
}
/* Support the ctors/dtors sections for g++. */
#define INT_ASM_OP ".word"
/* A C statement (sans semicolon) to output an element in the table of
global constructors. */
#undef ASM_OUTPUT_CONSTRUCTOR
#define ASM_OUTPUT_CONSTRUCTOR(STREAM,NAME) \
do { \
ctors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} while (0)
/* A C statement (sans semicolon) to output an element in the table of
global destructors. */
#undef ASM_OUTPUT_DESTRUCTOR
#define ASM_OUTPUT_DESTRUCTOR(STREAM,NAME) \
do { \
dtors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} while (0)
/* __CTOR_LIST__ and __DTOR_LIST__ must be defined by the linker script. */
#define CTOR_LISTS_DEFINED_EXTERNALLY
#undef DO_GLOBAL_CTORS_BODY
#undef DO_GLOBAL_DTORS_BODY
/* The ARM development system defines __main. */
#define NAME__MAIN "__gccmain"
#define SYMBOL__MAIN __gccmain
/* Definitions of target machine for GNU compiler,
for Thumb with ELF obj format.
Copyright (C) 1995, 1996, 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define OBJECT_FORMAT_ELF
#ifndef CPP_PREDEFINES
#define CPP_PREDEFINES "-Dthumb -Dthumbelf -D__thumb -Acpu(arm) -Amachine(arm)"
#endif
#include "arm/thumb.h"
/* Run-time Target Specification. */
#undef TARGET_VERSION
#define TARGET_VERSION fputs (" (Thumb/elf)", stderr)
#define MULTILIB_DEFAULTS { "mlittle-endian" }
/* Setting this to 32 produces more efficient code, but the value set in previous
versions of this toolchain was 8, which produces more compact structures. The
command line option -mstructure_size_boundary=<n> can be used to change this
value. */
#undef STRUCTURE_SIZE_BOUNDARY
#define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary
extern int arm_structure_size_boundary;
/* Debug */
#define DWARF_DEBUGGING_INFO
#define DWARF2_DEBUGGING_INFO
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
/* Get the standard ELF stabs definitions. */
#include "dbxelf.h"
/* Note - it is important that these definitions match those in semi.h for the ARM port. */
#undef LOCAL_LABEL_PREFIX
#define LOCAL_LABEL_PREFIX "."
/* A C statement to output assembler commands which will identify the
object file as having been compiled with GNU CC (or another GNU
compiler). */
#ifndef ASM_IDENTIFY_GCC
#define ASM_IDENTIFY_GCC(STREAM) \
fprintf (STREAM, "%sgcc2_compiled.:\n", LOCAL_LABEL_PREFIX )
#endif
#undef ASM_FILE_START
#define ASM_FILE_START(STREAM) \
do \
{ \
fprintf ((STREAM), "%s Generated by gcc %s for Thumb/elf\n", \
ASM_COMMENT_START, version_string); \
fprintf ((STREAM), ASM_APP_OFF); \
} \
while (0)
/* A C statement to output something to the assembler file to switch to section
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
NULL_TREE. Some target formats do not support arbitrary sections. Do not
define this macro in such cases. */
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do \
{ \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"ax\",%%progbits\n", NAME); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"a\"\n", NAME); \
else if (! strncmp (NAME, ".bss", 4)) \
fprintf (STREAM, "\t.section %s,\"aw\",%%nobits\n", NAME); \
else \
fprintf (STREAM, "\t.section %s,\"aw\"\n", NAME); \
} \
while (0)
/* Support the ctors/dtors and other sections. */
#undef INIT_SECTION_ASM_OP
/* Define this macro if jump tables (for `tablejump' insns) should be
output in the text section, along with the assembler instructions.
Otherwise, the readonly data section is used. */
#define JUMP_TABLES_IN_TEXT_SECTION 1
#undef READONLY_DATA_SECTION
#define READONLY_DATA_SECTION rdata_section
#undef RDATA_SECTION_ASM_OP
#define RDATA_SECTION_ASM_OP "\t.section .rodata"
#undef CTORS_SECTION_ASM_OP
#define CTORS_SECTION_ASM_OP "\t.section .ctors,\"aw\""
#undef DTORS_SECTION_ASM_OP
#define DTORS_SECTION_ASM_OP "\t.section .dtors,\"aw\""
#define USER_LABEL_PREFIX ""
/* If defined, a C expression whose value is a string containing the
assembler operation to identify the following data as
uninitialized global data. If not defined, and neither
`ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined,
uninitialized global data will be output in the data section if
`-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be
used. */
#ifndef BSS_SECTION_ASM_OP
#define BSS_SECTION_ASM_OP ".section\t.bss"
#endif
/* Like `ASM_OUTPUT_BSS' except takes the required alignment as a
separate, explicit argument. If you define this macro, it is used
in place of `ASM_OUTPUT_BSS', and gives you more flexibility in
handling the required alignment of the variable. The alignment is
specified as the number of bits.
Try to use function `asm_output_aligned_bss' defined in file
`varasm.c' when defining this macro. */
#ifndef ASM_OUTPUT_ALIGNED_BSS
#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN)
#endif
/* Don't know how to order these. UNALIGNED_WORD_ASM_OP is in
dwarf2.out. */
#define UNALIGNED_WORD_ASM_OP ".4byte"
#define ASM_OUTPUT_DWARF2_ADDR_CONST(FILE,ADDR) \
if (((ADDR)[0] == '.') && ((ADDR)[1] == 'L')) \
fprintf ((FILE), "\t%s\t%s", UNALIGNED_WORD_ASM_OP, (ADDR)); \
else \
fprintf ((FILE), "\t%s\t%s", \
UNALIGNED_WORD_ASM_OP, (ADDR))
#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \
do \
{ \
fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \
output_addr_const ((FILE), (RTX)); \
fputc ('\n', (FILE)); \
} \
while (0)
/* This is how to equate one symbol to another symbol. The syntax used is
`SYM1=SYM2'. Note that this is different from the way equates are done
with most svr4 assemblers, where the syntax is `.set SYM1,SYM2'. */
#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \
do \
{ \
fprintf ((FILE), "\t"); \
assemble_name (FILE, LABEL1); \
fprintf (FILE, " = "); \
assemble_name (FILE, LABEL2); \
fprintf (FILE, "\n"); \
} \
while (0)
/* For aliases of functions we use .thumb_set instead. */
#define ASM_OUTPUT_DEF_FROM_DECLS(FILE,DECL1,DECL2) \
do \
{ \
const char * LABEL1 = XSTR (XEXP (DECL_RTL (decl), 0), 0);\
const char * LABEL2 = IDENTIFIER_POINTER (DECL2); \
\
if (TREE_CODE (DECL1) == FUNCTION_DECL) \
{ \
fprintf (FILE, "\t.thumb_set "); \
assemble_name (FILE, LABEL1); \
fprintf (FILE, ","); \
assemble_name (FILE, LABEL2); \
fprintf (FILE, "\n"); \
} \
else \
ASM_OUTPUT_DEF (FILE, LABEL1, LABEL2); \
} \
while (0)
/* A list of other sections which the compiler might be "in" at any
given time. */
#undef EXTRA_SECTIONS
#define EXTRA_SECTIONS SUBTARGET_EXTRA_SECTIONS in_rdata, in_ctors, in_dtors
#ifndef SUBTARGET_EXTRA_SECTIONS
#define SUBTARGET_EXTRA_SECTIONS
#endif
/* A list of extra section function definitions. */
#undef EXTRA_SECTION_FUNCTIONS
#define EXTRA_SECTION_FUNCTIONS \
RDATA_SECTION_FUNCTION \
CTORS_SECTION_FUNCTION \
DTORS_SECTION_FUNCTION \
SUBTARGET_EXTRA_SECTION_FUNCTIONS
#ifndef SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS
#endif
#define RDATA_SECTION_FUNCTION \
void \
rdata_section () \
{ \
if (in_section != in_rdata) \
{ \
fprintf (asm_out_file, "%s\n", RDATA_SECTION_ASM_OP); \
in_section = in_rdata; \
} \
}
#define CTOR_LIST_BEGIN \
asm (CTORS_SECTION_ASM_OP); \
func_ptr __CTOR_LIST__[1] = { (func_ptr) (-1) }
#define CTOR_LIST_END \
asm (CTORS_SECTION_ASM_OP); \
func_ptr __CTOR_END__[1] = { (func_ptr) 0 };
#define DTOR_LIST_BEGIN \
asm (DTORS_SECTION_ASM_OP); \
func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) }
#define DTOR_LIST_END \
asm (DTORS_SECTION_ASM_OP); \
func_ptr __DTOR_END__[1] = { (func_ptr) 0 };
#define CTORS_SECTION_FUNCTION \
void \
ctors_section () \
{ \
if (in_section != in_ctors) \
{ \
fprintf (asm_out_file, "%s\n", CTORS_SECTION_ASM_OP); \
in_section = in_ctors; \
} \
}
#define DTORS_SECTION_FUNCTION \
void \
dtors_section () \
{ \
if (in_section != in_dtors) \
{ \
fprintf (asm_out_file, "%s\n", DTORS_SECTION_ASM_OP); \
in_section = in_dtors; \
} \
}
/* Support the ctors/dtors sections for g++. */
#define INT_ASM_OP ".word"
#define INVOKE__main
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "crtbegin%O%s crt0%O%s"
#undef ENDFILE_SPEC
#define ENDFILE_SPEC "crtend%O%s"
/* A C statement (sans semicolon) to output an element in the table of
global constructors. */
#undef ASM_OUTPUT_CONSTRUCTOR
#define ASM_OUTPUT_CONSTRUCTOR(STREAM,NAME) \
do \
{ \
ctors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} \
while (0)
/* A C statement (sans semicolon) to output an element in the table of
global destructors. */
#undef ASM_OUTPUT_DESTRUCTOR
#define ASM_OUTPUT_DESTRUCTOR(STREAM,NAME) \
do \
{ \
dtors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} \
while (0)
/* The ARM development system defines __main. */
#define NAME__MAIN "__gccmain"
#define SYMBOL__MAIN __gccmain
#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1)
#define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL))
#define UNIQUE_SECTION(DECL,RELOC) \
do \
{ \
int len; \
char * name, * string, * prefix; \
\
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \
\
if (! DECL_ONE_ONLY (DECL)) \
{ \
prefix = "."; \
if (TREE_CODE (DECL) == FUNCTION_DECL) \
prefix = ".text."; \
else if (DECL_READONLY_SECTION (DECL, RELOC)) \
prefix = ".rodata."; \
else \
prefix = ".data."; \
} \
else if (TREE_CODE (DECL) == FUNCTION_DECL) \
prefix = ".gnu.linkonce.t."; \
else if (DECL_READONLY_SECTION (DECL, RELOC)) \
prefix = ".gnu.linkonce.r."; \
else \
prefix = ".gnu.linkonce.d."; \
\
len = strlen (name) + strlen (prefix); \
string = alloca (len + 1); \
sprintf (string, "%s%s", prefix, name); \
\
DECL_SECTION_NAME (DECL) = build_string (len, string); \
} \
while (0)
/* This is how we tell the assembler that a symbol is weak. */
#ifndef ASM_WEAKEN_LABEL
#define ASM_WEAKEN_LABEL(FILE, NAME) \
do \
{ \
fputs ("\t.weak\t", FILE); \
assemble_name (FILE, NAME); \
fputc ('\n', FILE); \
} \
while (0)
#endif
#ifndef TYPE_ASM_OP
/* These macros generate the special .type and .size directives which
are used to set the corresponding fields of the linker symbol table
entries in an ELF object file under SVR4. These macros also output
the starting labels for the relevant functions/objects. */
#define TYPE_ASM_OP ".type"
#define SIZE_ASM_OP ".size"
/* The following macro defines the format used to output the second
operand of the .type assembler directive. Different svr4 assemblers
expect various different forms for this operand. The one given here
is just a default. You may need to override it in your machine-
specific tm.h file (depending upon the particulars of your assembler). */
#define TYPE_OPERAND_FMT "%s"
/* Write the extra assembler code needed to declare a function's result.
Most svr4 assemblers don't require any special declaration of the
result value, but there are exceptions. */
#ifndef ASM_DECLARE_RESULT
#define ASM_DECLARE_RESULT(FILE, RESULT)
#endif
/* Write the extra assembler code needed to declare a function properly.
Some svr4 assemblers need to also have something extra said about the
function's return value. We allow for that here. */
#undef ASM_DECLARE_FUNCTION_NAME
#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
do \
{ \
fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
putc (',', FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
putc ('\n', FILE); \
ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \
if (! is_called_in_ARM_mode (decl)) \
fprintf (FILE, "\t.thumb_func\n") ; \
else \
fprintf (FILE, "\t.code\t32\n") ; \
ASM_OUTPUT_LABEL(FILE, NAME); \
} \
while (0)
/* Write the extra assembler code needed to declare an object properly. */
#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
do \
{ \
fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
putc (',', FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "object"); \
putc ('\n', FILE); \
size_directive_output = 0; \
if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \
{ \
size_directive_output = 1; \
fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
assemble_name (FILE, NAME); \
putc (',', FILE); \
fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, \
int_size_in_bytes (TREE_TYPE (DECL))); \
fputc ('\n', FILE); \
} \
ASM_OUTPUT_LABEL(FILE, NAME); \
} \
while (0)
/* Output the size directive for a decl in rest_of_decl_compilation
in the case where we did not do so before the initializer.
Once we find the error_mark_node, we know that the value of
size_directive_output was set
by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */
#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END)\
do \
{ \
const char * name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \
if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \
&& ! AT_END && TOP_LEVEL \
&& DECL_INITIAL (DECL) == error_mark_node \
&& !size_directive_output) \
{ \
size_directive_output = 1; \
fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
assemble_name (FILE, name); \
putc (',', FILE); \
fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, \
int_size_in_bytes (TREE_TYPE (DECL))); \
fputc ('\n', FILE); \
} \
} \
while (0)
/* This is how to declare the size of a function. */
#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
do \
{ \
if (!flag_inhibit_size_directive) \
{ \
char label[256]; \
static int labelno; \
labelno ++; \
ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \
ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \
fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
assemble_name (FILE, (FNAME)); \
fprintf (FILE, ","); \
assemble_name (FILE, label); \
fprintf (FILE, "-"); \
assemble_name (FILE, (FNAME)); \
putc ('\n', FILE); \
} \
} \
while (0)
#endif /* TYPE_ASM_OP */
/* Output routines for GCC for ARM/Thumb
Copyright (C) 1996, 2000 Cygnus Software Technologies Ltd
The basis of this contribution was generated by
Richard Earnshaw, Advanced RISC Machines Ltd
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "regs.h"
#include "output.h"
#include "insn-flags.h"
#include "insn-attr.h"
#include "flags.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
#include "insn-config.h"
#include "recog.h"
#include "toplev.h"
#include "thumb-protos.h"
int current_function_anonymous_args = 0;
/* Used to parse -mstructure_size_boundary command line option. */
const char * structure_size_string = NULL;
int arm_structure_size_boundary = 32; /* Used to be 8 */
/* The register number to be used for the PIC offset register. */
const char * thumb_pic_register_string = NULL;
int thumb_pic_register = 10;
/* True if we are currently building a constant table. */
int making_const_table;
/* Predicates */
int
reload_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED ;
{
int regno = true_regnum (op);
return (! CONSTANT_P (op)
&& (regno == -1
|| (GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)));
}
/* Return nonzero if op is suitable for the RHS of a cmp instruction. */
int
thumb_cmp_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return ((GET_CODE (op) == CONST_INT
&& (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256)
|| register_operand (op, mode));
}
int
thumb_shiftable_const (val)
HOST_WIDE_INT val;
{
HOST_WIDE_INT mask = 0xff;
int i;
for (i = 0; i < 25; i++)
if ((val & (mask << i)) == val)
return 1;
return 0;
}
int
thumb_trivial_epilogue ()
{
/* ??? If this function ever returns 1, we get a function without any
epilogue at all. It appears that the intent was to cause a "return"
insn to be emitted, but that does not happen. */
return 0;
#if 0
if (get_frame_size ()
|| current_function_outgoing_args_size
|| current_function_pretend_args_size)
return 0;
for (regno = 8; regno < 13; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
return 0;
return 1;
#endif
}
/* Return TRUE if X references a SYMBOL_REF. */
int
thumb_symbol_mentioned_p (x)
rtx x;
{
register const char * fmt;
register int i;
if (GET_CODE (x) == SYMBOL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (thumb_symbol_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && thumb_symbol_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
/* Return TRUE if X references a LABEL_REF. */
int
label_mentioned_p (x)
rtx x;
{
register const char * fmt;
register int i;
if (GET_CODE (x) == LABEL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (label_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
rtx
legitimize_pic_address (orig, mode, reg)
rtx orig;
enum machine_mode mode;
rtx reg;
{
if (GET_CODE (orig) == SYMBOL_REF)
{
rtx pic_ref, address;
rtx insn;
int subregs = 0;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
subregs = 1;
}
#ifdef AOF_ASSEMBLER
/* The AOF assembler can generate relocations for these directly, and
understands that the PIC register has to be added into the offset.
*/
insn = emit_insn (gen_pic_load_addr_based (reg, orig));
#else
if (subregs)
address = gen_reg_rtx (Pmode);
else
address = reg;
emit_insn (gen_pic_load_addr (address, orig));
pic_ref = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
address));
RTX_UNCHANGING_P (pic_ref) = 1;
insn = emit_move_insn (reg, pic_ref);
#endif
current_function_uses_pic_offset_table = 1;
/* Put a REG_EQUAL note on this insn, so that it can be optimized
by loop. */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig,
REG_NOTES (insn));
return reg;
}
else if (GET_CODE (orig) == CONST)
{
rtx base, offset;
if (GET_CODE (XEXP (orig, 0)) == PLUS
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
else
abort ();
if (GET_CODE (offset) == CONST_INT)
{
/* The base register doesn't really matter, we only want to
test the index for the appropriate mode. */
if (INDEX_REGISTER_RTX_P (offset) && GET_MODE_SIZE (mode) <= 4)
goto win;
if (! reload_in_progress && ! reload_completed)
offset = force_reg (Pmode, offset);
else
abort ();
win:
if (GET_CODE (offset) == CONST_INT)
return plus_constant_for_output (base, INTVAL (offset));
}
if (GET_MODE_SIZE (mode) > 4)
{
emit_insn (gen_addsi3 (reg, base, offset));
return reg;
}
return gen_rtx_PLUS (Pmode, base, offset);
}
else if (GET_CODE (orig) == LABEL_REF)
current_function_uses_pic_offset_table = 1;
return orig;
}
static rtx pic_rtx;
int
is_pic (x)
rtx x;
{
if (x == pic_rtx)
return 1;
return 0;
}
void
thumb_finalize_pic ()
{
#ifndef AOF_ASSEMBLER
rtx l1, pic_tmp, pic_tmp2, seq;
rtx global_offset_table;
if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE)
return;
if (! flag_pic)
abort ();
start_sequence ();
l1 = gen_label_rtx ();
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
/* On the Thumb the PC register contains 'dot + 4' at the time of the
addition. XXX Is this true? */
pic_tmp = plus_constant (gen_rtx_LABEL_REF (Pmode, l1), 4);
if (GOT_PCREL)
pic_tmp2 = gen_rtx_CONST (VOIDmode,
gen_rtx_PLUS (Pmode, global_offset_table, pc_rtx));
else
pic_tmp2 = gen_rtx_CONST (VOIDmode, global_offset_table);
pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp));
emit_insn (gen_pic_load_addr (pic_offset_table_rtx, pic_rtx));
emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1));
seq = gen_sequence ();
end_sequence ();
emit_insn_after (seq, get_insns ());
/* Need to emit this whether or not we obey regdecls,
since setjmp/longjmp can cause life info to screw up. */
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
#endif /* AOF_ASSEMBLER */
}
/* Routines for handling the constant pool */
/* This is unashamedly hacked from the version in sh.c, since the problem is
extremely similar. */
/* Thumb instructions cannot load a large constant into a register,
constants have to come from a pc relative load. The reference of a pc
relative load instruction must be less than 1k infront of the instruction.
This means that we often have to dump a constant inside a function, and
generate code to branch around it.
It is important to minimize this, since the branches will slow things
down and make things bigger.
Worst case code looks like:
ldr rn, L1
b L2
align
L1: .long value
L2:
..
ldr rn, L3
b L4
align
L3: .long value
L4:
..
We fix this by performing a scan before scheduling, which notices which
instructions need to have their operands fetched from the constant table
and builds the table.
The algorithm is:
scan, find an instruction which needs a pcrel move. Look forward, find the
last barrier which is within MAX_COUNT bytes of the requirement.
If there isn't one, make one. Process all the instructions between
the find and the barrier.
In the above example, we can tell that L3 is within 1k of L1, so
the first move can be shrunk from the 2 insn+constant sequence into
just 1 insn, and the constant moved to L3 to make:
ldr rn, L1
..
ldr rn, L3
b L4
align
L1: .long value
L3: .long value
L4:
Then the second move becomes the target for the shortening process.
*/
typedef struct
{
rtx value; /* Value in table */
HOST_WIDE_INT next_offset;
enum machine_mode mode; /* Mode of value */
} pool_node;
/* The maximum number of constants that can fit into one pool, since
the pc relative range is 0...1020 bytes and constants are at least 4
bytes long */
#define MAX_POOL_SIZE (1020/4)
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
static rtx pool_vector_label;
/* Add a constant to the pool and return its label. */
static HOST_WIDE_INT
add_constant (x, mode)
rtx x;
enum machine_mode mode;
{
int i;
HOST_WIDE_INT offset;
if (mode == SImode && GET_CODE (x) == MEM && CONSTANT_P (XEXP (x, 0))
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
x = get_pool_constant (XEXP (x, 0));
#ifndef AOF_ASSEMBLER
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == 3)
x = XVECEXP (x, 0, 0);
#endif
/* First see if we've already got it */
for (i = 0; i < pool_size; i++)
{
if (x->code == pool_vector[i].value->code
&& mode == pool_vector[i].mode)
{
if (x->code == CODE_LABEL)
{
if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
continue;
}
if (rtx_equal_p (x, pool_vector[i].value))
return pool_vector[i].next_offset - GET_MODE_SIZE (mode);
}
}
/* Need a new one */
pool_vector[pool_size].next_offset = GET_MODE_SIZE (mode);
offset = 0;
if (pool_size == 0)
pool_vector_label = gen_label_rtx ();
else
pool_vector[pool_size].next_offset
+= (offset = pool_vector[pool_size - 1].next_offset);
pool_vector[pool_size].value = x;
pool_vector[pool_size].mode = mode;
pool_size++;
return offset;
}
/* Output the literal table */
static void
dump_table (scan)
rtx scan;
{
int i;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
scan = emit_label_after (pool_vector_label, scan);
for (i = 0; i < pool_size; i++)
{
pool_node *p = pool_vector + i;
switch (GET_MODE_SIZE (p->mode))
{
case 4:
scan = emit_insn_after (gen_consttable_4 (p->value), scan);
break;
case 8:
scan = emit_insn_after (gen_consttable_8 (p->value), scan);
break;
default:
abort ();
break;
}
}
scan = emit_insn_after (gen_consttable_end (), scan);
scan = emit_barrier_after (scan);
pool_size = 0;
}
/* Non zero if the src operand needs to be fixed up */
static int
fixit (src, mode)
rtx src;
enum machine_mode mode;
{
#ifndef AOF_ASSEMBLER
if (GET_CODE (src) == UNSPEC && XINT (src, 1) == 3)
return 1;
#endif
return ((CONSTANT_P (src)
&& (GET_CODE (src) != CONST_INT
|| ! (CONST_OK_FOR_LETTER_P (INTVAL (src), 'I')
|| CONST_OK_FOR_LETTER_P (INTVAL (src), 'J')
|| (mode != DImode
&& CONST_OK_FOR_LETTER_P (INTVAL (src), 'K')))))
|| (mode == SImode && GET_CODE (src) == MEM
&& GET_CODE (XEXP (src, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (src, 0))));
}
/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. */
#define MAX_COUNT_SI 1000
static rtx
find_barrier (from)
rtx from;
{
int count = 0;
rtx found_barrier = 0;
rtx label;
while (from && count < MAX_COUNT_SI)
{
if (GET_CODE (from) == BARRIER)
return from;
/* Count the length of this insn. */
if (GET_CODE (from) == INSN
&& GET_CODE (PATTERN (from)) == SET
&& CONSTANT_P (SET_SRC (PATTERN (from)))
&& CONSTANT_POOL_ADDRESS_P (SET_SRC (PATTERN (from))))
count += 2;
else
count += get_attr_length (from);
from = NEXT_INSN (from);
}
/* We didn't find a barrier in time to
dump our stuff, so we'll make one. */
label = gen_label_rtx ();
if (from)
from = PREV_INSN (from);
else
from = get_last_insn ();
/* Walk back to be just before any jump. */
while (GET_CODE (from) == JUMP_INSN
|| GET_CODE (from) == NOTE
|| GET_CODE (from) == CODE_LABEL)
from = PREV_INSN (from);
from = emit_jump_insn_after (gen_jump (label), from);
JUMP_LABEL (from) = label;
found_barrier = emit_barrier_after (from);
emit_label_after (label, found_barrier);
return found_barrier;
}
/* Non zero if the insn is a move instruction which needs to be fixed. */
static int
broken_move (insn)
rtx insn;
{
if (!INSN_DELETED_P (insn)
&& GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
enum machine_mode mode = GET_MODE (dst);
if (dst == pc_rtx)
return 0;
return fixit (src, mode);
}
return 0;
}
/* Recursively search through all of the blocks in a function
checking to see if any of the variables created in that
function match the RTX called 'orig'. If they do then
replace them with the RTX called 'new'. */
static void
replace_symbols_in_block (tree block, rtx orig, rtx new)
{
for (; block; block = BLOCK_CHAIN (block))
{
tree sym;
if (! TREE_USED (block))
continue;
for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym))
{
if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL)
|| DECL_IGNORED_P (sym)
|| TREE_CODE (sym) != VAR_DECL
|| DECL_EXTERNAL (sym)
|| ! rtx_equal_p (DECL_RTL (sym), orig)
)
continue;
DECL_RTL (sym) = new;
}
replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new);
}
}
void
thumb_reorg (first)
rtx first;
{
rtx insn;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (broken_move (insn))
{
/* This is a broken move instruction, scan ahead looking for
a barrier to stick the constant table behind. */
rtx scan;
rtx barrier = find_barrier (insn);
/* Now find all the moves between the points and modify them. */
for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
{
if (broken_move (scan))
{
/* This is a broken move instruction, add it to the pool. */
rtx pat = PATTERN (scan);
rtx src = SET_SRC (pat);
rtx dst = SET_DEST (pat);
enum machine_mode mode = GET_MODE (dst);
HOST_WIDE_INT offset;
rtx newinsn;
rtx newsrc;
/* If this is an HImode constant load, convert it into
an SImode constant load. Since the register is always
32 bits this is safe. We have to do this, since the
load pc-relative instruction only does a 32-bit load. */
if (mode == HImode)
{
mode = SImode;
if (GET_CODE (dst) != REG)
abort ();
PUT_MODE (dst, SImode);
}
offset = add_constant (src, mode);
newsrc = gen_rtx (MEM, mode,
plus_constant (gen_rtx (LABEL_REF,
VOIDmode,
pool_vector_label),
offset));
/* Build a jump insn wrapper around the move instead
of an ordinary insn, because we want to have room for
the target label rtx in fld[7], which an ordinary
insn doesn't have. */
newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode,
dst, newsrc), scan);
JUMP_LABEL (newinsn) = pool_vector_label;
/* But it's still an ordinary insn. */
PUT_CODE (newinsn, INSN);
/* If debugging information is going to be emitted
then we must make sure that any refences to
symbols which are removed by the above code are
also removed in the descriptions of the
function's variables. Failure to do this means
that the debugging information emitted could
refer to symbols which are not emited by
output_constant_pool() because
mark_constant_pool() never sees them as being
used. */
/* These are the tests used in
output_constant_pool() to decide if the constant
pool will be marked. Only necessary if debugging
info is being emitted. Only necessary for
references to memory whose address is given by a
symbol. */
if (optimize > 0
&& flag_expensive_optimizations
&& write_symbols != NO_DEBUG
&& GET_CODE (src) == MEM
&& GET_CODE (XEXP (src, 0)) == SYMBOL_REF)
replace_symbols_in_block
(DECL_INITIAL (current_function_decl), src, newsrc);
/* Kill old insn. */
delete_insn (scan);
scan = newinsn;
}
}
dump_table (barrier);
}
}
}
/* Routines for generating rtl. */
void
thumb_expand_movstrqi (operands)
rtx *operands;
{
rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
HOST_WIDE_INT len = INTVAL (operands[2]);
HOST_WIDE_INT offset = 0;
while (len >= 12)
{
emit_insn (gen_movmem12b (out, in));
len -= 12;
}
if (len >= 8)
{
emit_insn (gen_movmem8b (out, in));
len -= 8;
}
if (len >= 4)
{
rtx reg = gen_reg_rtx (SImode);
emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in)));
emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg));
len -= 4;
offset += 4;
}
if (len >= 2)
{
rtx reg = gen_reg_rtx (HImode);
emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode,
plus_constant (in, offset))));
emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)),
reg));
len -= 2;
offset += 2;
}
if (len)
{
rtx reg = gen_reg_rtx (QImode);
emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode,
plus_constant (in, offset))));
emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)),
reg));
}
}
/* Routines for reloading */
void
thumb_reload_out_si (operands)
rtx operands ATTRIBUTE_UNUSED;
{
abort ();
}
#ifdef THUMB_PE
/* Return non-zero if FUNC is a naked function. */
static int
arm_naked_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
abort ();
a = lookup_attribute ("naked", DECL_MACHINE_ATTRIBUTES (func));
return a != NULL_TREE;
}
#endif
/* Return non-zero if FUNC must be entered in ARM mode. */
int
is_called_in_ARM_mode (func)
tree func;
{
if (TREE_CODE (func) != FUNCTION_DECL)
abort ();
/* Ignore the problem about functions whoes address is taken. */
if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func))
return TRUE;
#ifdef THUMB_PE
return lookup_attribute ("interfacearm", DECL_MACHINE_ATTRIBUTES (func)) != NULL_TREE;
#else
return FALSE;
#endif
}
/* Routines for emitting code */
void
thumb_final_prescan_insn (insn)
rtx insn;
{
extern int * insn_addresses;
if (flag_print_asm_name)
fprintf (asm_out_file, "%s 0x%04x\n", ASM_COMMENT_START,
insn_addresses[INSN_UID (insn)]);
}
static void thumb_pushpop ( FILE *, int, int ); /* Forward declaration. */
#ifdef __GNUC__
inline
#endif
static int
number_of_first_bit_set (mask)
int mask;
{
int bit;
for (bit = 0;
(mask & (1 << bit)) == 0;
++ bit)
continue;
return bit;
}
#define ARG_1_REGISTER 0
#define ARG_2_REGISTER 1
#define ARG_3_REGISTER 2
#define ARG_4_REGISTER 3
#define WORK_REGISTER 7
#define FRAME_POINTER 11
#define IP_REGISTER 12
#define STACK_POINTER STACK_POINTER_REGNUM
#define LINK_REGISTER 14
#define PROGRAM_COUNTER 15
/* Generate code to return from a thumb function. If
'reg_containing_return_addr' is -1, then the return address is
actually on the stack, at the stack pointer. */
static void
thumb_exit (f, reg_containing_return_addr)
FILE * f;
int reg_containing_return_addr;
{
int regs_available_for_popping;
int regs_to_pop;
int pops_needed;
int available;
int required;
int mode;
int size;
int restore_a4 = FALSE;
/* Compute the registers we need to pop. */
regs_to_pop = 0;
pops_needed = 0;
if (reg_containing_return_addr == -1)
{
regs_to_pop |= 1 << LINK_REGISTER;
++ pops_needed;
}
if (TARGET_BACKTRACE)
{
/* Restore frame pointer and stack pointer. */
regs_to_pop |= (1 << FRAME_POINTER) | (1 << STACK_POINTER);
pops_needed += 2;
}
/* If there is nothing to pop then just emit the BX instruction and return. */
if (pops_needed == 0)
{
asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]);
return;
}
/* Otherwise if we are not supporting interworking and we have not created
a backtrace structure and the function was not entered in ARM mode then
just pop the return address straight into the PC. */
else if ( ! TARGET_THUMB_INTERWORK
&& ! TARGET_BACKTRACE
&& ! is_called_in_ARM_mode (current_function_decl))
{
asm_fprintf (f, "\tpop\t{pc}\n" );
return;
}
/* Find out how many of the (return) argument registers we can corrupt. */
regs_available_for_popping = 0;
#ifdef RTX_CODE
/* If we can deduce the registers used from the function's return value.
This is more reliable that examining regs_ever_live[] because that
will be set if the register is ever used in the function, not just if
the register is used to hold a return value. */
if (current_function_return_rtx != 0)
mode = GET_MODE (current_function_return_rtx);
else
#endif
mode = DECL_MODE (DECL_RESULT (current_function_decl));
size = GET_MODE_SIZE (mode);
if (size == 0)
{
/* In a void function we can use any argument register.
In a function that returns a structure on the stack
we can use the second and third argument registers. */
if (mode == VOIDmode)
regs_available_for_popping =
(1 << ARG_1_REGISTER)
| (1 << ARG_2_REGISTER)
| (1 << ARG_3_REGISTER);
else
regs_available_for_popping =
(1 << ARG_2_REGISTER)
| (1 << ARG_3_REGISTER);
}
else if (size <= 4) regs_available_for_popping =
(1 << ARG_2_REGISTER)
| (1 << ARG_3_REGISTER);
else if (size <= 8) regs_available_for_popping =
(1 << ARG_3_REGISTER);
/* Match registers to be popped with registers into which we pop them. */
for (available = regs_available_for_popping,
required = regs_to_pop;
required != 0 && available != 0;
available &= ~(available & - available),
required &= ~(required & - required))
-- pops_needed;
/* If we have any popping registers left over, remove them. */
if (available > 0)
regs_available_for_popping &= ~ available;
/* Otherwise if we need another popping register we can use
the fourth argument register. */
else if (pops_needed)
{
/* If we have not found any free argument registers and
reg a4 contains the return address, we must move it. */
if (regs_available_for_popping == 0
&& reg_containing_return_addr == ARG_4_REGISTER)
{
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]);
reg_containing_return_addr = LINK_REGISTER;
}
else if (size > 12)
{
/* Register a4 is being used to hold part of the return value,
but we have dire need of a free, low register. */
restore_a4 = TRUE;
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [IP_REGISTER], reg_names [ARG_4_REGISTER]);
}
if (reg_containing_return_addr != ARG_4_REGISTER)
{
/* The fourth argument register is available. */
regs_available_for_popping |= 1 << ARG_4_REGISTER;
-- pops_needed;
}
}
/* Pop as many registers as we can. */
thumb_pushpop (f, regs_available_for_popping, FALSE);
/* Process the registers we popped. */
if (reg_containing_return_addr == -1)
{
/* The return address was popped into the lowest numbered register. */
regs_to_pop &= ~ (1 << LINK_REGISTER);
reg_containing_return_addr =
number_of_first_bit_set (regs_available_for_popping);
/* Remove this register for the mask of available registers, so that
the return address will not be corrupted by futher pops. */
regs_available_for_popping &= ~ (1 << reg_containing_return_addr);
}
/* If we popped other registers then handle them here. */
if (regs_available_for_popping)
{
int frame_pointer;
/* Work out which register currently contains the frame pointer. */
frame_pointer = number_of_first_bit_set (regs_available_for_popping);
/* Move it into the correct place. */
asm_fprintf (f, "\tmov\tfp, %s\n", reg_names [frame_pointer]);
/* (Temporarily) remove it from the mask of popped registers. */
regs_available_for_popping &= ~ (1 << frame_pointer);
regs_to_pop &= ~ (1 << FRAME_POINTER);
if (regs_available_for_popping)
{
int stack_pointer;
/* We popped the stack pointer as well, find the register that
contains it.*/
stack_pointer = number_of_first_bit_set (regs_available_for_popping);
/* Move it into the stack register. */
asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [stack_pointer]);
/* At this point we have popped all necessary registers, so
do not worry about restoring regs_available_for_popping
to its correct value:
assert (pops_needed == 0)
assert (regs_available_for_popping == (1 << frame_pointer))
assert (regs_to_pop == (1 << STACK_POINTER)) */
}
else
{
/* Since we have just move the popped value into the frame
pointer, the popping register is available for reuse, and
we know that we still have the stack pointer left to pop. */
regs_available_for_popping |= (1 << frame_pointer);
}
}
/* If we still have registers left on the stack, but we no longer have
any registers into which we can pop them, then we must move the return
address into the link register and make available the register that
contained it. */
if (regs_available_for_popping == 0 && pops_needed > 0)
{
regs_available_for_popping |= 1 << reg_containing_return_addr;
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [LINK_REGISTER],
reg_names [reg_containing_return_addr]);
reg_containing_return_addr = LINK_REGISTER;
}
/* If we have registers left on the stack then pop some more.
We know that at most we will want to pop FP and SP. */
if (pops_needed > 0)
{
int popped_into;
int move_to;
thumb_pushpop (f, regs_available_for_popping, FALSE);
/* We have popped either FP or SP.
Move whichever one it is into the correct register. */
popped_into = number_of_first_bit_set (regs_available_for_popping);
move_to = number_of_first_bit_set (regs_to_pop);
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [move_to], reg_names [popped_into]);
regs_to_pop &= ~ (1 << move_to);
-- pops_needed;
}
/* If we still have not popped everything then we must have only
had one register available to us and we are now popping the SP. */
if (pops_needed > 0)
{
int popped_into;
thumb_pushpop (f, regs_available_for_popping, FALSE);
popped_into = number_of_first_bit_set (regs_available_for_popping);
asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [popped_into]);
/*
assert (regs_to_pop == (1 << STACK_POINTER))
assert (pops_needed == 1)
*/
}
/* If necessary restore the a4 register. */
if (restore_a4)
{
if (reg_containing_return_addr != LINK_REGISTER)
{
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]);
reg_containing_return_addr = LINK_REGISTER;
}
asm_fprintf (f, "\tmov\t%s, %s\n",
reg_names [ARG_4_REGISTER], reg_names [IP_REGISTER]);
}
/* Return to caller. */
asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]);
}
/* Emit code to push or pop registers to or from the stack. */
static void
thumb_pushpop (f, mask, push)
FILE * f;
int mask;
int push;
{
int regno;
int lo_mask = mask & 0xFF;
if (lo_mask == 0 && ! push && (mask & (1 << 15)))
{
/* Special case. Do not generate a POP PC statement here, do it in
thumb_exit() */
thumb_exit (f, -1);
return;
}
asm_fprintf (f, "\t%s\t{", push ? "push" : "pop");
/* Look at the low registers first. */
for (regno = 0; regno < 8; regno ++, lo_mask >>= 1)
{
if (lo_mask & 1)
{
asm_fprintf (f, reg_names[regno]);
if ((lo_mask & ~1) != 0)
asm_fprintf (f, ", ");
}
}
if (push && (mask & (1 << 14)))
{
/* Catch pushing the LR. */
if (mask & 0xFF)
asm_fprintf (f, ", ");
asm_fprintf (f, reg_names[14]);
}
else if (!push && (mask & (1 << 15)))
{
/* Catch popping the PC. */
if (TARGET_THUMB_INTERWORK || TARGET_BACKTRACE)
{
/* The PC is never poped directly, instead
it is popped into r3 and then BX is used. */
asm_fprintf (f, "}\n");
thumb_exit (f, -1);
return;
}
else
{
if (mask & 0xFF)
asm_fprintf (f, ", ");
asm_fprintf (f, reg_names[15]);
}
}
asm_fprintf (f, "}\n");
}
/* Returns non-zero if the current function contains a far jump */
int
far_jump_used_p (void)
{
rtx insn;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN
/* Ignore tablejump patterns. */
&& GET_CODE (PATTERN (insn)) != ADDR_VEC
&& GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
&& get_attr_far_jump (insn) == FAR_JUMP_YES)
return 1;
}
return 0;
}
static int return_used_this_function = 0;
char *
output_return ()
{
int regno;
int live_regs_mask = 0;
#ifdef THUMB_PE
/* If a function is naked, don't use the "return" insn. */
if (arm_naked_function_p (current_function_decl))
return "";
#endif
return_used_this_function = 1;
for (regno = 0; regno < 8; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno]
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
live_regs_mask |= 1 << regno;
if (live_regs_mask == 0)
{
if (leaf_function_p () && ! far_jump_used_p())
{
thumb_exit (asm_out_file, 14);
}
else if ( TARGET_THUMB_INTERWORK
|| TARGET_BACKTRACE
|| is_called_in_ARM_mode (current_function_decl))
{
thumb_exit (asm_out_file, -1);
}
else
asm_fprintf (asm_out_file, "\tpop\t{pc}\n");
}
else
{
asm_fprintf (asm_out_file, "\tpop\t{");
for (regno = 0; live_regs_mask; regno ++, live_regs_mask >>= 1)
if (live_regs_mask & 1)
{
asm_fprintf (asm_out_file, reg_names[regno]);
if (live_regs_mask & ~1)
asm_fprintf (asm_out_file, ", ");
}
if ( TARGET_THUMB_INTERWORK
|| TARGET_BACKTRACE
|| is_called_in_ARM_mode (current_function_decl))
{
asm_fprintf (asm_out_file, "}\n");
thumb_exit (asm_out_file, -1);
}
else
asm_fprintf (asm_out_file, ", pc}\n");
}
return "";
}
void
thumb_function_prologue (f, frame_size)
FILE * f;
int frame_size ATTRIBUTE_UNUSED;
{
int live_regs_mask = 0;
int high_regs_pushed = 0;
int store_arg_regs = 0;
int regno;
#ifdef THUMB_PE
if (arm_naked_function_p (current_function_decl))
return;
#endif
if (is_called_in_ARM_mode (current_function_decl))
{
const char * name;
if (GET_CODE (DECL_RTL (current_function_decl)) != MEM)
abort();
if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF)
abort();
name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
/* Generate code sequence to switch us into Thumb mode. */
/* The .code 32 directive has already been emitted by
ASM_DECLARE_FUNCITON_NAME */
asm_fprintf (f, "\torr\tr12, pc, #1\n");
asm_fprintf (f, "\tbx\tr12\n");
/* Generate a label, so that the debugger will notice the
change in instruction sets. This label is also used by
the assembler to bypass the ARM code when this function
is called from a Thumb encoded function elsewhere in the
same file. Hence the definition of STUB_NAME here must
agree with the definition in gas/config/tc-arm.c */
#define STUB_NAME ".real_start_of"
asm_fprintf (f, "\t.code\t16\n");
#ifdef THUMB_PE
if (arm_dllexport_name_p (name))
name = ARM_STRIP_NAME_ENCODING (name);
#endif
asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name);
asm_fprintf (f, "\t.thumb_func\n");
asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
}
if (current_function_anonymous_args && current_function_pretend_args_size)
store_arg_regs = 1;
if (current_function_pretend_args_size)
{
if (store_arg_regs)
{
asm_fprintf (f, "\tpush\t{");
for (regno = 4 - current_function_pretend_args_size / 4 ; regno < 4;
regno++)
asm_fprintf (f, "%s%s", reg_names[regno], regno == 3 ? "" : ", ");
asm_fprintf (f, "}\n");
}
else
asm_fprintf (f, "\tsub\t%Rsp, %Rsp, #%d\n",
current_function_pretend_args_size);
}
for (regno = 0; regno < 8; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno]
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
live_regs_mask |= 1 << regno;
if (live_regs_mask || ! leaf_function_p () || far_jump_used_p ())
live_regs_mask |= 1 << 14;
if (TARGET_BACKTRACE)
{
const char * name;
int offset;
int work_register = 0;
/* We have been asked to create a stack backtrace structure.
The code looks like this:
0 .align 2
0 func:
0 sub SP, #16 Reserve space for 4 registers.
2 push {R7} Get a work register.
4 add R7, SP, #20 Get the stack pointer before the push.
6 str R7, [SP, #8] Store the stack pointer (before reserving the space).
8 mov R7, PC Get hold of the start of this code plus 12.
10 str R7, [SP, #16] Store it.
12 mov R7, FP Get hold of the current frame pointer.
14 str R7, [SP, #4] Store it.
16 mov R7, LR Get hold of the current return address.
18 str R7, [SP, #12] Store it.
20 add R7, SP, #16 Point at the start of the backtrace structure.
22 mov FP, R7 Put this value into the frame pointer. */
if ((live_regs_mask & 0xFF) == 0)
{
/* See if the a4 register is free. */
if (regs_ever_live [3] == 0)
work_register = 3;
else /* We must push a register of our own */
live_regs_mask |= (1 << 7);
}
if (work_register == 0)
{
/* Select a register from the list that will be pushed to use as our work register. */
for (work_register = 8; work_register--;)
if ((1 << work_register) & live_regs_mask)
break;
}
name = reg_names [work_register];
asm_fprintf (f, "\tsub\tsp, sp, #16\t@ Create stack backtrace structure\n");
if (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1);
for (offset = 0, work_register = 1 << 15; work_register; work_register >>= 1)
if (work_register & live_regs_mask)
offset += 4;
asm_fprintf (f, "\tadd\t%s, sp, #%d\n",
name, offset + 16 + current_function_pretend_args_size);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 4);
/* Make sure that the instruction fetching the PC is in the right place
to calculate "start of backtrace creation code + 12". */
if (live_regs_mask)
{
asm_fprintf (f, "\tmov\t%s, pc\n", name);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 12);
asm_fprintf (f, "\tmov\t%s, fp\n", name);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset);
}
else
{
asm_fprintf (f, "\tmov\t%s, fp\n", name);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset);
asm_fprintf (f, "\tmov\t%s, pc\n", name);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 12);
}
asm_fprintf (f, "\tmov\t%s, lr\n", name);
asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 8);
asm_fprintf (f, "\tadd\t%s, sp, #%d\n", name, offset + 12);
asm_fprintf (f, "\tmov\tfp, %s\t\t@ Backtrace structure created\n", name);
}
else if (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1);
for (regno = 8; regno < 13; regno++)
{
if (regs_ever_live[regno] && ! call_used_regs[regno]
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
high_regs_pushed++;
}
if (high_regs_pushed)
{
int pushable_regs = 0;
int mask = live_regs_mask & 0xff;
int next_hi_reg;
for (next_hi_reg = 12; next_hi_reg > 7; next_hi_reg--)
{
if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]
&& ! (TARGET_SINGLE_PIC_BASE && (next_hi_reg == thumb_pic_register)))
break;
}
pushable_regs = mask;
if (pushable_regs == 0)
{
/* desperation time -- this probably will never happen */
if (regs_ever_live[3] || ! call_used_regs[3])
asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[12], reg_names[3]);
mask = 1 << 3;
}
while (high_regs_pushed > 0)
{
for (regno = 7; regno >= 0; regno--)
{
if (mask & (1 << regno))
{
asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[regno],
reg_names[next_hi_reg]);
high_regs_pushed--;
if (high_regs_pushed)
for (next_hi_reg--; next_hi_reg > 7; next_hi_reg--)
{
if (regs_ever_live[next_hi_reg]
&& ! call_used_regs[next_hi_reg]
&& ! (TARGET_SINGLE_PIC_BASE
&& (next_hi_reg == thumb_pic_register)))
break;
}
else
{
mask &= ~ ((1 << regno) - 1);
break;
}
}
}
thumb_pushpop (f, mask, 1);
}
if (pushable_regs == 0 && (regs_ever_live[3] || ! call_used_regs[3]))
asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[3], reg_names[12]);
}
}
/* Functions to save and restore thumb_return_addr_rtx. */
static rtx thumb_return_addr_rtx = NULL_RTX;
struct machine_function
{
rtx ra_rtx;
};
static void
thumb_save_machine_status (p)
struct function * p;
{
struct machine_function * machine =
(struct machine_function *) xmalloc (sizeof (* machine));
p->machine = machine;
machine->ra_rtx = thumb_return_addr_rtx;
}
static void
thumb_restore_machine_status (p)
struct function * p;
{
struct machine_function * machine = p->machine;
thumb_return_addr_rtx = machine->ra_rtx;
free (machine);
p->machine = (struct machine_function *) NULL;
}
/* Return an RTX indicating where the return address to the
calling function can be found. */
rtx
thumb_return_addr (count)
int count;
{
if (count != 0)
return NULL_RTX;
if (thumb_return_addr_rtx == NULL_RTX)
{
rtx init;
thumb_return_addr_rtx = gen_reg_rtx (Pmode);
init = gen_rtx_REG (Pmode, 14);
init = gen_rtx_SET (VOIDmode, thumb_return_addr_rtx, init);
/* Emit the insn to the prologue with the other argument copies. */
push_topmost_sequence ();
emit_insn_after (init, get_insns ());
pop_topmost_sequence ();
}
return thumb_return_addr_rtx;
}
/* Do anything needed before RTL is emitted for each function. */
void
thumb_init_expanders ()
{
thumb_return_addr_rtx = NULL_RTX;
/* Arrange to save and restore machine status around nested functions. */
save_machine_status = thumb_save_machine_status;
restore_machine_status = thumb_restore_machine_status;
}
void
thumb_expand_prologue ()
{
HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size);
#ifdef THUMB_PE
/* Naked functions don't have prologues. */
if (arm_naked_function_p (current_function_decl))
return;
#endif
if (amount)
{
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (- amount)));
else
{
int regno;
rtx reg;
/* The stack decrement is too big for an immediate value in a single
insn. In theory we could issue multiple subtracts, but after
three of them it becomes more space efficient to place the full
value in the constant pool and load into a register. (Also the
ARM debugger really likes to see only one stack decrement per
function). So instead we look for a scratch register into which
we can load the decrement, and then we subtract this from the
stack pointer. Unfortunately on the thumb the only available
scratch registers are the argument registers, and we cannot use
these as they may hold arguments to the function. Instead we
attempt to locate a call preserved register which is used by this
function. If we can find one, then we know that it will have
been pushed at the start of the prologue and so we can corrupt
it now. */
for (regno = 4; regno < 8; regno++)
if (regs_ever_live[regno]
&& ! call_used_regs[regno] /* Paranoia */
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
break;
if (regno == 8) /* Very unlikely */
{
rtx spare = gen_rtx (REG, SImode, 12);
/* Choose an arbitary, non-argument low register. */
reg = gen_rtx (REG, SImode, 4);
/* Save it by copying it into a high, scratch register. */
emit_insn (gen_movsi (spare, reg));
/* Decrement the stack. */
emit_insn (gen_movsi (reg, GEN_INT (- amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
/* Restore the low register's original value. */
emit_insn (gen_movsi (reg, spare));
/* Emit a USE of the restored scratch register, so that flow
analysis will not consider the restore redundant. The
register won't be used again in this function and isn't
restored by the epilogue. */
emit_insn (gen_rtx_USE (VOIDmode, reg));
}
else
{
reg = gen_rtx (REG, SImode, regno);
emit_insn (gen_movsi (reg, GEN_INT (- amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
}
}
}
/* This should only happen with optimisation disabled. Emit the copy
*after* the stack adjust, as the unoptimised code will attempt to store
local variables at positive offsets from the frame pointer. */
if (frame_pointer_needed)
{
if (current_function_outgoing_args_size)
{
rtx offset = GEN_INT (current_function_outgoing_args_size);
if (current_function_outgoing_args_size < 1024)
emit_insn (gen_addsi3 (frame_pointer_rtx, stack_pointer_rtx,
offset));
else
{
emit_insn (gen_movsi (frame_pointer_rtx, offset));
emit_insn (gen_addsi3 (frame_pointer_rtx, frame_pointer_rtx,
stack_pointer_rtx));
}
}
else
emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
}
if (profile_flag || profile_block_flag)
emit_insn (gen_blockage ());
}
void
thumb_expand_epilogue ()
{
HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size);
#ifdef THUMB_PE
/* Naked functions don't have epilogues. */
if (arm_naked_function_p (current_function_decl))
return;
#endif
if (amount)
{
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (amount)));
else
{
rtx reg = gen_rtx (REG, SImode, 3); /* Always free in the epilogue */
emit_insn (gen_movsi (reg, GEN_INT (amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
}
}
/* Emit a USE (stack_pointer_rtx), so that
the stack adjustment will not be deleted. */
emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
if (profile_flag || profile_block_flag)
emit_insn (gen_blockage ());
}
void
thumb_function_epilogue (f, frame_size)
FILE * f ATTRIBUTE_UNUSED;
int frame_size ATTRIBUTE_UNUSED;
{
/* ??? Probably not safe to set this here, since it assumes that a
function will be emitted as assembly immediately after we generate
RTL for it. This does not happen for inline functions. */
return_used_this_function = 0;
#if 0 /* TODO : comment not really needed */
fprintf (f, "%s THUMB Epilogue\n", ASM_COMMENT_START);
#endif
}
/* The bits which aren't usefully expanded as rtl. */
char *
thumb_unexpanded_epilogue ()
{
int regno;
int live_regs_mask = 0;
int high_regs_pushed = 0;
int leaf_function = leaf_function_p ();
int had_to_push_lr;
if (return_used_this_function)
return "";
for (regno = 0; regno < 8; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno]
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
live_regs_mask |= 1 << regno;
for (regno = 8; regno < 13; regno++)
{
if (regs_ever_live[regno] && ! call_used_regs[regno]
&& ! (TARGET_SINGLE_PIC_BASE && (regno == thumb_pic_register)))
high_regs_pushed ++;
}
/* The prolog may have pushed some high registers to use as
work registers. eg the testuite file:
gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c
compiles to produce:
push {r4, r5, r6, r7, lr}
mov r7, r9
mov r6, r8
push {r6, r7}
as part of the prolog. We have to undo that pushing here. */
if (high_regs_pushed)
{
int mask = live_regs_mask;
int next_hi_reg;
int size;
int mode;
#ifdef RTX_CODE
/* If we can deduce the registers used from the function's return value.
This is more reliable that examining regs_ever_live[] because that
will be set if the register is ever used in the function, not just if
the register is used to hold a return value. */
if (current_function_return_rtx != 0)
mode = GET_MODE (current_function_return_rtx);
else
#endif
mode = DECL_MODE (DECL_RESULT (current_function_decl));
size = GET_MODE_SIZE (mode);
/* Unless we are returning a type of size > 12 register r3 is available. */
if (size < 13)
mask |= 1 << 3;
if (mask == 0)
/* Oh dear! We have no low registers into which we can pop high registers! */
fatal ("No low registers available for popping high registers");
for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]
&& ! (TARGET_SINGLE_PIC_BASE && (next_hi_reg == thumb_pic_register)))
break;
while (high_regs_pushed)
{
/* Find low register(s) into which the high register(s) can be popped. */
for (regno = 0; regno < 8; regno++)
{
if (mask & (1 << regno))
high_regs_pushed--;
if (high_regs_pushed == 0)
break;
}
mask &= (2 << regno) - 1; /* A noop if regno == 8 */
/* Pop the values into the low register(s). */
thumb_pushpop (asm_out_file, mask, 0);
/* Move the value(s) into the high registers. */
for (regno = 0; regno < 8; regno++)
{
if (mask & (1 << regno))
{
asm_fprintf (asm_out_file, "\tmov\t%s, %s\n",
reg_names[next_hi_reg], reg_names[regno]);
for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
if (regs_ever_live[next_hi_reg] &&
! call_used_regs[next_hi_reg]
&& ! (TARGET_SINGLE_PIC_BASE
&& (next_hi_reg == thumb_pic_register)))
break;
}
}
}
}
had_to_push_lr = (live_regs_mask || ! leaf_function || far_jump_used_p());
if (TARGET_BACKTRACE && ((live_regs_mask & 0xFF) == 0) && regs_ever_live[ ARG_4_REGISTER ] != 0)
/* The stack backtrace structure creation code had to
push R7 in order to get a work register, so we pop
it now. */
live_regs_mask |= (1 << WORK_REGISTER);
if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE)
{
if (had_to_push_lr
&& ! is_called_in_ARM_mode (current_function_decl))
live_regs_mask |= 1 << PROGRAM_COUNTER;
/* Either no argument registers were pushed or a backtrace
structure was created which includes an adjusted stack
pointer, so just pop everything. */
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
/* We have either just popped the return address into the
PC or it is was kept in LR for the entire function or
it is still on the stack because we do not want to
return by doing a pop {pc}. */
if ((live_regs_mask & (1 << PROGRAM_COUNTER)) == 0)
thumb_exit (asm_out_file,
(had_to_push_lr
&& is_called_in_ARM_mode (current_function_decl)) ?
-1 : LINK_REGISTER);
}
else
{
/* Pop everything but the return address. */
live_regs_mask &= ~ (1 << PROGRAM_COUNTER);
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
if (had_to_push_lr)
/* Get the return address into a temporary register. */
thumb_pushpop (asm_out_file, 1 << ARG_4_REGISTER, 0);
/* Remove the argument registers that were pushed onto the stack. */
asm_fprintf (asm_out_file, "\tadd\t%s, %s, #%d\n",
reg_names [STACK_POINTER],
reg_names [STACK_POINTER],
current_function_pretend_args_size);
thumb_exit (asm_out_file, had_to_push_lr ? ARG_4_REGISTER : LINK_REGISTER);
}
return "";
}
/* Handle the case of a double word load into a low register from
a computed memory address. The computed address may involve a
register which is overwritten by the load. */
char *
thumb_load_double_from_address (operands)
rtx * operands;
{
rtx addr;
rtx base;
rtx offset;
rtx arg1;
rtx arg2;
if (GET_CODE (operands[0]) != REG)
fatal ("thumb_load_double_from_address: destination is not a register");
if (GET_CODE (operands[1]) != MEM)
fatal ("thumb_load_double_from_address: source is not a computed memory address");
/* Get the memory address. */
addr = XEXP (operands[1], 0);
/* Work out how the memory address is computed. */
switch (GET_CODE (addr))
{
case REG:
operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4));
if (REGNO (operands[0]) == REGNO (addr))
{
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
}
else
{
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
}
break;
case CONST:
/* Compute <address> + 4 for the high order load. */
operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4));
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
break;
case PLUS:
arg1 = XEXP (addr, 0);
arg2 = XEXP (addr, 1);
if (CONSTANT_P (arg1))
base = arg2, offset = arg1;
else
base = arg1, offset = arg2;
if (GET_CODE (base) != REG)
fatal ("thumb_load_double_from_address: base is not a register");
/* Catch the case of <address> = <reg> + <reg> */
if (GET_CODE (offset) == REG)
{
int reg_offset = REGNO (offset);
int reg_base = REGNO (base);
int reg_dest = REGNO (operands[0]);
/* Add the base and offset registers together into the higher destination register. */
fprintf (asm_out_file, "\tadd\t%s, %s, %s\t\t%s created by thumb_load_double_from_address",
reg_names[ reg_dest + 1 ],
reg_names[ reg_base ],
reg_names[ reg_offset ],
ASM_COMMENT_START);
/* Load the lower destination register from the address in the higher destination register. */
fprintf (asm_out_file, "\tldr\t%s, [%s, #0]\t\t%s created by thumb_load_double_from_address",
reg_names[ reg_dest ],
reg_names[ reg_dest + 1],
ASM_COMMENT_START);
/* Load the higher destination register from its own address plus 4. */
fprintf (asm_out_file, "\tldr\t%s, [%s, #4]\t\t%s created by thumb_load_double_from_address",
reg_names[ reg_dest + 1 ],
reg_names[ reg_dest + 1 ],
ASM_COMMENT_START);
}
else
{
/* Compute <address> + 4 for the high order load. */
operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4));
/* If the computed address is held in the low order register
then load the high order register first, otherwise always
load the low order register first. */
if (REGNO (operands[0]) == REGNO (base))
{
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
}
else
{
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
}
}
break;
case LABEL_REF:
/* With no registers to worry about we can just load the value directly. */
operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4));
output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands);
output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands);
break;
default:
debug_rtx (operands[1]);
fatal ("thumb_load_double_from_address: Unhandled address calculation");
break;
}
return "";
}
char *
output_move_mem_multiple (n, operands)
int n;
rtx *operands;
{
rtx tmp;
switch (n)
{
case 2:
if (REGNO (operands[2]) > REGNO (operands[3]))
{
tmp = operands[2];
operands[2] = operands[3];
operands[3] = tmp;
}
output_asm_insn ("ldmia\t%1!, {%2, %3}", operands);
output_asm_insn ("stmia\t%0!, {%2, %3}", operands);
break;
case 3:
if (REGNO (operands[2]) > REGNO (operands[3]))
{
tmp = operands[2];
operands[2] = operands[3];
operands[3] = tmp;
}
if (REGNO (operands[3]) > REGNO (operands[4]))
{
tmp = operands[3];
operands[3] = operands[4];
operands[4] = tmp;
}
if (REGNO (operands[2]) > REGNO (operands[3]))
{
tmp = operands[2];
operands[2] = operands[3];
operands[3] = tmp;
}
output_asm_insn ("ldmia\t%1!, {%2, %3, %4}", operands);
output_asm_insn ("stmia\t%0!, {%2, %3, %4}", operands);
break;
default:
abort ();
}
return "";
}
int
thumb_epilogue_size ()
{
return 42; /* The answer to .... */
}
static char *conds[] =
{
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le"
};
static char *
thumb_condition_code (x, invert)
rtx x;
int invert;
{
int val;
switch (GET_CODE (x))
{
case EQ: val = 0; break;
case NE: val = 1; break;
case GEU: val = 2; break;
case LTU: val = 3; break;
case GTU: val = 8; break;
case LEU: val = 9; break;
case GE: val = 10; break;
case LT: val = 11; break;
case GT: val = 12; break;
case LE: val = 13; break;
default:
abort ();
}
return conds[val ^ invert];
}
void
thumb_print_operand (f, x, code)
FILE *f;
rtx x;
int code;
{
if (code)
{
switch (code)
{
case '@':
fputs (ASM_COMMENT_START, f);
return;
case '|':
/* fputs (REGISTER_PREFIX, f); */
return;
case '_':
fputs (user_label_prefix, f);
return;
case 'D':
if (x)
fputs (thumb_condition_code (x, 1), f);
return;
case 'd':
if (x)
fputs (thumb_condition_code (x, 0), f);
return;
/* An explanation of the 'Q', 'R' and 'H' register operands:
In a pair of registers containing a DI or DF value the 'Q'
operand returns the register number of the register containing
the least signficant part of the value. The 'R' operand returns
the register number of the register containing the most
significant part of the value.
The 'H' operand returns the higher of the two register numbers.
On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the
same as the 'Q' operand, since the most signficant part of the
value is held in the lower number register. The reverse is true
on systems where WORDS_BIG_ENDIAN is false.
The purpose of these operands is to distinguish between cases
where the endian-ness of the values is important (for example
when they are added together), and cases where the endian-ness
is irrelevant, but the order of register operations is important.
For example when loading a value from memory into a register
pair, the endian-ness does not matter. Provided that the value
from the lower memory address is put into the lower numbered
register, and the value from the higher address is put into the
higher numbered register, the load will work regardless of whether
the value being loaded is big-wordian or little-wordian. The
order of the two register loads can matter however, if the address
of the memory location is actually held in one of the registers
being overwritten by the load. */
case 'Q':
if (REGNO (x) > 15)
abort ();
fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)], f);
return;
case 'R':
if (REGNO (x) > 15)
abort ();
fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)], f);
return;
case 'H':
if (REGNO (x) > 15)
abort ();
fputs (reg_names[REGNO (x) + 1], f);
return;
default:
abort ();
}
}
if (GET_CODE (x) == REG)
fputs (reg_names[REGNO (x)], f);
else if (GET_CODE (x) == MEM)
output_address (XEXP (x, 0));
else if (GET_CODE (x) == CONST_INT)
{
fputc ('#', f);
output_addr_const (f, x);
}
else
abort ();
}
#ifdef AOF_ASSEMBLER
int arm_text_section_count = 1;
char *
aof_text_section (in_readonly)
int in_readonly;
{
static char buf[100];
if (in_readonly)
return "";
sprintf (buf, "\tCODE16\n\tAREA |C$$code%d|, CODE, READONLY",
arm_text_section_count++);
return buf;
}
static int arm_data_section_count = 1;
char *
aof_data_section ()
{
static char buf[100];
sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++);
return buf;
}
/* The AOF thumb assembler is religiously strict about declarations of
imported and exported symbols, so that it is impossible to declare a
function as imported near the begining of the file, and then to export
it later on. It is, however, possible to delay the decision until all
the functions in the file have been compiled. To get around this, we
maintain a list of the imports and exports, and delete from it any that
are subsequently defined. At the end of compilation we spit the
remainder of the list out before the END directive. */
struct import
{
struct import *next;
char *name;
};
static struct import *imports_list = NULL;
void
thumb_aof_add_import (name)
char *name;
{
struct import *new;
for (new = imports_list; new; new = new->next)
if (new->name == name)
return;
new = (struct import *) xmalloc (sizeof (struct import));
new->next = imports_list;
imports_list = new;
new->name = name;
}
void
thumb_aof_delete_import (name)
char *name;
{
struct import **old;
for (old = &imports_list; *old; old = & (*old)->next)
{
if ((*old)->name == name)
{
*old = (*old)->next;
return;
}
}
}
void
thumb_aof_dump_imports (f)
FILE *f;
{
while (imports_list)
{
fprintf (f, "\tIMPORT\t");
assemble_name (f, imports_list->name);
fputc ('\n', f);
imports_list = imports_list->next;
}
}
#endif
/* Decide whether a type should be returned in memory (true)
or in a register (false). This is called by the macro
RETURN_IN_MEMORY. */
int
thumb_return_in_memory (type)
tree type;
{
if (! AGGREGATE_TYPE_P (type))
{
/* All simple types are returned in registers. */
return 0;
}
else if (int_size_in_bytes (type) > 4)
{
/* All structures/unions bigger than one word are returned in memory. */
return 1;
}
else if (TREE_CODE (type) == RECORD_TYPE)
{
tree field;
/* For a struct the APCS says that we must return in a register if
every addressable element has an offset of zero. For practical
purposes this means that the structure can have at most one non-
bit-field element and that this element must be the first one in
the structure. */
/* Find the first field, ignoring non FIELD_DECL things which will
have been created by C++. */
for (field = TYPE_FIELDS (type);
field && TREE_CODE (field) != FIELD_DECL;
field = TREE_CHAIN (field))
continue;
if (field == NULL)
return 0; /* An empty structure. Allowed by an extension to ANSI C. */
/* Now check the remaining fields, if any. */
for (field = TREE_CHAIN (field); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (! DECL_BIT_FIELD_TYPE (field))
return 1;
}
return 0;
}
else if (TREE_CODE (type) == UNION_TYPE)
{
tree field;
/* Unions can be returned in registers if every element is
integral, or can be returned in an integer register. */
for (field = TYPE_FIELDS (type);
field;
field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (RETURN_IN_MEMORY (TREE_TYPE (field)))
return 1;
}
return 0;
}
/* XXX Not sure what should be done for other aggregates, so put them in
memory. */
return 1;
}
void
thumb_override_options ()
{
if (structure_size_string != NULL)
{
int size = strtol (structure_size_string, NULL, 0);
if (size == 8 || size == 32)
arm_structure_size_boundary = size;
else
warning ("Structure size boundary can only be set to 8 or 32");
}
if (thumb_pic_register_string != NULL)
{
int pic_register;
if (! flag_pic)
warning ("-mpic-register= is useless without -fpic");
pic_register = decode_reg_name (thumb_pic_register_string);
/* Prevent the user from choosing an obviously stupid PIC register. */
if (pic_register < 0 || call_used_regs[pic_register]
|| pic_register == HARD_FRAME_POINTER_REGNUM
|| pic_register == STACK_POINTER_REGNUM
|| pic_register >= PC_REGNUM)
error ("Unable to use '%s' for PIC register", thumb_pic_register_string);
else
thumb_pic_register = pic_register;
}
}
#ifdef THUMB_PE
/* Return nonzero if ATTR is a valid attribute for DECL.
ATTRIBUTES are any existing attributes and ARGS are the arguments
supplied with ATTR.
Supported attributes:
naked: don't output any prologue or epilogue code, the user is assumed
to do the right thing.
interfacearm: Always assume that this function will be entered in ARM
mode, not Thumb mode, and that the caller wishes to be returned to in
ARM mode. */
int
arm_valid_machine_decl_attribute (decl, attr, args)
tree decl;
tree attr;
tree args;
{
if (args != NULL_TREE)
return 0;
if (is_attribute_p ("naked", attr))
if (TREE_CODE (decl) == FUNCTION_DECL)
return 1;
if (is_attribute_p ("interfacearm", attr))
return TREE_CODE (decl) == FUNCTION_DECL;
return 0;
}
#endif /* THUMB_PE */
/* Definitions of target machine for GNU compiler, for ARM/Thumb.
Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
The basis of this contribution was generated by
Richard Earnshaw, Advanced RISC Machines Ltd
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* ??? The files thumb.{c,h,md} are all seriously lacking comments. */
/* ??? The files thumb.{c,h,md} need to be reviewed by an experienced
gcc hacker in their entirety. */
/* ??? The files thumb.{c,h,md} and tcoff.h are all separate from the arm
files, which will lead to many maintenance problems. These files are
likely missing all bug fixes made to the arm port since they diverged. */
/* ??? Many patterns in the md file accept operands that will require a
reload. These should be eliminated if possible by tightening the
predicates and/or constraints. This will give faster/smaller code. */
/* ??? There is no pattern for the TST instuction. Check for other unsupported
instructions. */
/* Run Time Target Specifications */
#ifndef CPP_PREDEFINES
#define CPP_PREDEFINES "-Dthumb -D__thumb -Acpu(arm) -Amachine(arm)"
#endif
#ifndef CPP_SPEC
#define CPP_SPEC "\
%{mbig-endian:-D__ARMEB__ -D__THUMBEB__} \
%{mbe:-D__ARMEB__ -D__THUMBEB__} \
%{!mbe: %{!mbig-endian:-D__ARMEL__ -D__THUMBEL__}} \
"
#endif
#define ASM_SPEC "-marm7tdmi %{mthumb-interwork:-mthumb-interwork} %{mbig-endian:-EB}"
#ifndef LINK_SPEC
#define LINK_SPEC "%{mbig-endian:-EB} -X"
#endif
#define TARGET_VERSION fputs (" (ARM/THUMB:generic)", stderr);
/* Nonzero if we should compile with BYTES_BIG_ENDIAN set to 1. */
#define THUMB_FLAG_BIG_END 0x0001
#define THUMB_FLAG_BACKTRACE 0x0002
#define THUMB_FLAG_LEAF_BACKTRACE 0x0004
#define ARM_FLAG_THUMB 0x1000 /* same as in arm.h */
#define THUMB_FLAG_SINGLE_PIC_BASE 0x4000 /* same as in arm.h */
#define THUMB_FLAG_CALLEE_SUPER_INTERWORKING 0x40000
#define THUMB_FLAG_CALLER_SUPER_INTERWORKING 0x80000
/* Run-time compilation parameters selecting different hardware/software subsets. */
extern int target_flags;
#define TARGET_DEFAULT 0 /* ARM_FLAG_THUMB */
#define TARGET_BIG_END (target_flags & THUMB_FLAG_BIG_END)
#define TARGET_THUMB_INTERWORK (target_flags & ARM_FLAG_THUMB)
#define TARGET_BACKTRACE (leaf_function_p() \
? (target_flags & THUMB_FLAG_LEAF_BACKTRACE) \
: (target_flags & THUMB_FLAG_BACKTRACE))
#define TARGET_SINGLE_PIC_BASE (target_flags & THUMB_FLAG_SINGLE_PIC_BASE)
#ifndef GOT_PCREL
#define GOT_PCREL 0
#endif
#ifndef NEED_GOT_RELOC
#define NEED_GOT_RELOC 1
#endif
/* Set if externally visible functions should assume that they
might be called in ARM mode, from a non-thumb aware code. */
#define TARGET_CALLEE_INTERWORKING \
(target_flags & THUMB_FLAG_CALLEE_SUPER_INTERWORKING)
/* Set if calls via function pointers should assume that their
destination is non-Thumb aware. */
#define TARGET_CALLER_INTERWORKING \
(target_flags & THUMB_FLAG_CALLER_SUPER_INTERWORKING)
/* SUBTARGET_SWITCHES is used to add flags on a per-config basis. */
#ifndef SUBTARGET_SWITCHES
#define SUBTARGET_SWITCHES
#endif
#define TARGET_SWITCHES \
{ \
{"big-endian", THUMB_FLAG_BIG_END, ""}, \
{"little-endian", -THUMB_FLAG_BIG_END, ""}, \
{"thumb-interwork", ARM_FLAG_THUMB, ""}, \
{"no-thumb-interwork", -ARM_FLAG_THUMB, ""}, \
{"tpcs-frame", THUMB_FLAG_BACKTRACE, ""}, \
{"no-tpcs-frame", -THUMB_FLAG_BACKTRACE, ""}, \
{"tpcs-leaf-frame", THUMB_FLAG_LEAF_BACKTRACE, ""}, \
{"no-tpcs-leaf-frame", -THUMB_FLAG_LEAF_BACKTRACE, ""}, \
{"callee-super-interworking", THUMB_FLAG_CALLEE_SUPER_INTERWORKING, ""}, \
{"no-callee-super-interworking", -THUMB_FLAG_CALLEE_SUPER_INTERWORKING, ""}, \
{"caller-super-interworking", THUMB_FLAG_CALLER_SUPER_INTERWORKING, ""}, \
{"no-caller-super-interworking", -THUMB_FLAG_CALLER_SUPER_INTERWORKING, ""}, \
{"single-pic-base", THUMB_FLAG_SINGLE_PIC_BASE, \
"Do not load the PIC register in function prologues" }, \
{"no-single-pic-base", -THUMB_FLAG_SINGLE_PIC_BASE, ""}, \
SUBTARGET_SWITCHES \
{"", TARGET_DEFAULT, ""} \
}
#define TARGET_OPTIONS \
{ \
{ "structure-size-boundary=", & structure_size_string, \
"Specify the structure aligment: 8 or 32 bits" }, \
{ "pic-register=", & thumb_pic_register_string, \
"Specify the register to be used for PIC addressing" } \
}
#define REGISTER_PREFIX ""
#define CAN_DEBUG_WITHOUT_FP 1
#define ASM_APP_ON ""
#define ASM_APP_OFF "\t.code\t16\n"
/* Output a gap. In fact we fill it with nulls. */
#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \
fprintf ((STREAM), "\t.space\t%u\n", (NBYTES))
/* This is how to output an assembler line that says to advance the
location counter to a multiple of 2**LOG bytes. Advancing to the
nearest 1 byte boundary is redundant, and anyway the assembler would
treat it as meaning "advance to nearest 4 byte boundary", which we do
not want. */
#define ASM_OUTPUT_ALIGN(STREAM,LOG) \
do \
{ \
if ((LOG) > 0) \
fprintf (STREAM, "\t.align\t%d\n", LOG); \
} \
while (0)
/* Output a common block */
#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \
(fprintf ((STREAM), "\t.comm\t"), \
assemble_name ((STREAM), (NAME)), \
fprintf((STREAM), ", %d\t%s %d\n", (ROUNDED), (ASM_COMMENT_START), (SIZE)))
#define ASM_GENERATE_INTERNAL_LABEL(STRING,PREFIX,NUM) \
sprintf ((STRING), "*%s%s%d", (LOCAL_LABEL_PREFIX), (PREFIX), (NUM))
/* This is how to output an internal numbered label where
PREFIX is the class of label and NUM is the number within the class. */
#define ASM_OUTPUT_INTERNAL_LABEL(STREAM,PREFIX,NUM) \
fprintf ((STREAM), "%s%s%d:\n", (LOCAL_LABEL_PREFIX), (PREFIX), (NUM))
/* This is how to output a label which precedes a jumptable. Since
instructions are 2 bytes, we need explicit alignment here. */
#define ASM_OUTPUT_CASE_LABEL(FILE,PREFIX,NUM,JUMPTABLE) \
do { \
ASM_OUTPUT_ALIGN (FILE, 2); \
ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \
} while (0)
/* This says how to define a local common symbol (ie, not visible to
linker). */
#define ASM_OUTPUT_LOCAL(STREAM, NAME, SIZE, ROUNDED) \
(fprintf((STREAM),"\n\t.lcomm\t"), \
assemble_name((STREAM),(NAME)), \
fprintf((STREAM),",%u\n",(SIZE)))
/* Output a reference to a label. */
#define ASM_OUTPUT_LABELREF(STREAM,NAME) \
fprintf ((STREAM), "%s%s", user_label_prefix, (NAME))
/* This is how to output an assembler line for a numeric constant byte. */
#define ASM_OUTPUT_BYTE(STREAM,VALUE) \
fprintf ((STREAM), "\t.byte\t0x%x\n", (VALUE))
#define ASM_OUTPUT_INT(STREAM,VALUE) \
{ \
fprintf (STREAM, "\t.word\t"); \
OUTPUT_INT_ADDR_CONST (STREAM, (VALUE)); \
fprintf (STREAM, "\n"); \
}
#define ASM_OUTPUT_SHORT(STREAM,VALUE) \
{ \
fprintf (STREAM, "\t.short\t"); \
output_addr_const (STREAM, (VALUE)); \
fprintf (STREAM, "\n"); \
}
#define ASM_OUTPUT_CHAR(STREAM,VALUE) \
{ \
fprintf (STREAM, "\t.byte\t"); \
output_addr_const (STREAM, (VALUE)); \
fprintf (STREAM, "\n"); \
}
#define ASM_OUTPUT_LONG_DOUBLE(STREAM,VALUE) \
do { char dstr[30]; \
long l[3]; \
REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.long 0x%lx,0x%lx,0x%lx\t%s long double %s\n", \
l[0], l[1], l[2], ASM_COMMENT_START, dstr); \
} while (0)
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \
do { char dstr[30]; \
long l[2]; \
REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.14g", dstr); \
fprintf (STREAM, "\t.long 0x%lx, 0x%lx\t%s double %s\n", l[0], \
l[1], ASM_COMMENT_START, dstr); \
} while (0)
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \
do { char dstr[30]; \
long l; \
REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.7g", dstr); \
fprintf (STREAM, "\t.word 0x%lx\t%s float %s\n", l, \
ASM_COMMENT_START, dstr); \
} while (0);
/* Define results of standard character escape sequences. */
#define TARGET_BELL 007
#define TARGET_BS 010
#define TARGET_TAB 011
#define TARGET_NEWLINE 012
#define TARGET_VT 013
#define TARGET_FF 014
#define TARGET_CR 015
/* This is how to output a string. */
#define ASM_OUTPUT_ASCII(STREAM, STRING, LEN) \
do { \
register int i, c, len = (LEN), cur_pos = 17; \
register const unsigned char *string = (const unsigned char *)(STRING); \
fprintf ((STREAM), "\t.ascii\t\""); \
for (i = 0; i < len; i++) \
{ \
register int c = string[i]; \
\
switch (c) \
{ \
case '\"': \
case '\\': \
putc ('\\', (STREAM)); \
putc (c, (STREAM)); \
cur_pos += 2; \
break; \
\
case TARGET_NEWLINE: \
fputs ("\\n", (STREAM)); \
if (i+1 < len \
&& (((c = string[i+1]) >= '\040' && c <= '~') \
|| c == TARGET_TAB)) \
cur_pos = 32767; /* break right here */ \
else \
cur_pos += 2; \
break; \
\
case TARGET_TAB: \
fputs ("\\t", (STREAM)); \
cur_pos += 2; \
break; \
\
case TARGET_FF: \
fputs ("\\f", (STREAM)); \
cur_pos += 2; \
break; \
\
case TARGET_BS: \
fputs ("\\b", (STREAM)); \
cur_pos += 2; \
break; \
\
case TARGET_CR: \
fputs ("\\r", (STREAM)); \
cur_pos += 2; \
break; \
\
default: \
if (c >= ' ' && c < 0177) \
{ \
putc (c, (STREAM)); \
cur_pos++; \
} \
else \
{ \
fprintf ((STREAM), "\\%03o", c); \
cur_pos += 4; \
} \
} \
\
if (cur_pos > 72 && i+1 < len) \
{ \
cur_pos = 17; \
fprintf ((STREAM), "\"\n\t.ascii\t\""); \
} \
} \
fprintf ((STREAM), "\"\n"); \
} while (0)
/* Output and Generation of Labels */
#define ASM_OUTPUT_LABEL(STREAM,NAME) \
(assemble_name ((STREAM), (NAME)), \
fprintf ((STREAM), ":\n"))
#define ASM_GLOBALIZE_LABEL(STREAM,NAME) \
(fprintf ((STREAM), "\t.globl\t"), \
assemble_name ((STREAM), (NAME)), \
fputc ('\n', (STREAM)))
/* Construct a private name. */
#define ASM_FORMAT_PRIVATE_NAME(OUTVAR,NAME,NUMBER) \
((OUTVAR) = (char *) alloca (strlen (NAME) + 10), \
sprintf ((OUTVAR), "%s.%d", (NAME), (NUMBER)))
/* Switch to the text or data segment. */
#define TEXT_SECTION_ASM_OP ".text"
#define DATA_SECTION_ASM_OP ".data"
#define BSS_SECTION_ASM_OP ".bss"
/* The assembler's names for the registers. */
#ifndef REGISTER_NAMES
#define REGISTER_NAMES \
{ \
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
"r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc", "ap" \
}
#endif
#ifndef ADDITIONAL_REGISTER_NAMES
#define ADDITIONAL_REGISTER_NAMES \
{ \
{"a1", 0}, \
{"a2", 1}, \
{"a3", 2}, \
{"a4", 3}, \
{"v1", 4}, \
{"v2", 5}, \
{"v3", 6}, \
{"v4", 7}, \
{"v5", 8}, \
{"v6", 9}, \
{"sb", 9}, \
{"v7", 10}, \
{"r10", 10}, /* sl */ \
{"r11", 11}, /* fp */ \
{"r12", 12}, /* ip */ \
{"r13", 13}, /* sp */ \
{"r14", 14}, /* lr */ \
{"r15", 15} /* pc */ \
}
#endif
/* The assembler's parentheses characters. */
#define ASM_OPEN_PAREN "("
#define ASM_CLOSE_PAREN ")"
#ifndef ASM_COMMENT_START
#define ASM_COMMENT_START "@"
#endif
/* Output an element of a dispatch table. */
#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \
fprintf (STREAM, "\t.word\t%sL%d\n", (LOCAL_LABEL_PREFIX), (VALUE))
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,BODY,VALUE,REL) \
fprintf (STREAM, "\tb\t%sL%d\n", (LOCAL_LABEL_PREFIX), (VALUE))
/* Storage Layout */
/* Define this is most significant bit is lowest numbered in
instructions that operate on numbered bit-fields. */
#define BITS_BIG_ENDIAN 0
/* Define this if most significant byte of a word is the lowest
numbered. */
#define BYTES_BIG_ENDIAN (TARGET_BIG_END != 0)
#define WORDS_BIG_ENDIAN (BYTES_BIG_ENDIAN)
/* LIBGCC2_WORDS_BIG_ENDIAN has to be a constant, so we define this based
on processor pre-defineds when compiling libgcc2.c. */
#if defined(__THUMBEB__) && !defined(__THUMBEL__)
#define LIBGCC2_WORDS_BIG_ENDIAN 1
#else
#define LIBGCC2_WORDS_BIG_ENDIAN 0
#endif
#define FLOAT_WORDS_BIG_ENDIAN 1
#define BITS_PER_UNIT 8
#define BITS_PER_WORD 32
#define UNITS_PER_WORD 4
#define POINTER_SIZE 32
#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \
{ \
if (GET_MODE_CLASS (MODE) == MODE_INT \
&& GET_MODE_SIZE (MODE) < 4) \
{ \
(UNSIGNEDP) = 1; \
(MODE) = SImode; \
} \
}
#define PARM_BOUNDARY 32
#define STACK_BOUNDARY 32
#define FUNCTION_BOUNDARY 32
#define BIGGEST_ALIGNMENT 32
/* Make strings word-aligned so strcpy from constants will be faster. */
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
(TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
#define EMPTY_FIELD_BOUNDARY 32
#define STRUCTURE_SIZE_BOUNDARY 32
/* Used when parsing command line option -mstructure_size_boundary. */
extern const char * structure_size_string;
#define STRICT_ALIGNMENT 1
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
/* Layout of Source Language Data Types */
#define DEFAULT_SIGNED_CHAR 0
#define TARGET_BELL 007
#define TARGET_BS 010
#define TARGET_TAB 011
#define TARGET_NEWLINE 012
#define TARGET_VT 013
#define TARGET_FF 014
#define TARGET_CR 015
/* Register Usage */
/* Note there are 16 hard registers on the Thumb. We invent a 17th register
which is assigned to ARG_POINTER_REGNUM, but this is later removed by
elimination passes in the compiler. */
#define FIRST_PSEUDO_REGISTER 17
/* ??? This is questionable. */
#define FIXED_REGISTERS \
{ \
0,0,0,0, \
0,0,0,0, \
0,0,0,1, \
0,1,1,1,1 \
}
/* ??? This is questionable. */
#define CALL_USED_REGISTERS \
{ \
1,1,1,1, \
0,0,0,0, \
0,0,0,1, \
1,1,1,1,1 \
}
#define HARD_REGNO_NREGS(REGNO,MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
/ UNITS_PER_WORD)
/* ??? Probably should only allow DImode/DFmode in even numbered registers. */
#define HARD_REGNO_MODE_OK(REGNO,MODE) ((GET_MODE_SIZE (MODE) > UNITS_PER_WORD) ? (REGNO < 7) : 1)
#define MODES_TIEABLE_P(MODE1,MODE2) 1
/* The NOARG_LO_REGS class is the set of LO_REGS that are not used for passing
arguments to functions. These are the registers that are available for
spilling during reload. The code in reload1.c:init_reload() will detect this
class and place it into 'reload_address_base_reg_class'. */
enum reg_class
{
NO_REGS,
NONARG_LO_REGS,
LO_REGS,
STACK_REG,
BASE_REGS,
HI_REGS,
ALL_REGS,
LIM_REG_CLASSES
};
#define GENERAL_REGS ALL_REGS
#define N_REG_CLASSES (int) LIM_REG_CLASSES
#define REG_CLASS_NAMES \
{ \
"NO_REGS", \
"NONARG_LO_REGS", \
"LO_REGS", \
"STACK_REG", \
"BASE_REGS", \
"HI_REGS", \
"ALL_REGS" \
}
#define REG_CLASS_CONTENTS \
{ \
{ 0x00000 }, \
{ 0x000f0 }, \
{ 0x000ff }, \
{ 0x02000 }, \
{ 0x020ff }, \
{ 0x0ff00 }, \
{ 0x1ffff }, \
}
#define REGNO_REG_CLASS(REGNO) \
((REGNO) == STACK_POINTER_REGNUM ? STACK_REG \
: (REGNO) < 8 ? ((REGNO) < 4 ? LO_REGS \
: NONARG_LO_REGS) \
: HI_REGS)
#define BASE_REG_CLASS BASE_REGS
#define INDEX_REG_CLASS LO_REGS
/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows
registers explicitly used in the rtl to be used as spill registers
but prevents the compiler from extending the lifetime of these
registers. */
#define SMALL_REGISTER_CLASSES 1
#define REG_CLASS_FROM_LETTER(C) \
((C) == 'l' ? LO_REGS \
: (C) == 'h' ? HI_REGS \
: (C) == 'b' ? BASE_REGS \
: (C) == 'k' ? STACK_REG \
: NO_REGS)
#define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < 8 \
|| (REGNO) == STACK_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO] < 8 \
|| (unsigned) reg_renumber[REGNO] == STACK_POINTER_REGNUM)
#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \
((REGNO) < 8 \
|| (unsigned) reg_renumber[REGNO] < 8 \
|| (GET_MODE_SIZE (MODE) >= 4 \
&& ((REGNO) == STACK_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO] == STACK_POINTER_REGNUM)))
#define REGNO_OK_FOR_INDEX_P(REGNO) \
((REGNO) < 8 \
|| (unsigned) reg_renumber[REGNO] < 8)
#define INDEX_REGISTER_RTX_P(X) \
(GET_CODE (X) == REG && REG_OK_FOR_INDEX_P (X))
/* ??? This looks suspiciously wrong. */
/* We need to leave BASE_REGS reloads alone, in order to avoid caller_save
lossage. Caller_saves requests a BASE_REGS reload (caller_save_spill_class)
and then later we verify that one was allocated. If PREFERRED_RELOAD_CLASS
says to allocate a LO_REGS spill instead, then this mismatch gives an
abort. Alternatively, this could be fixed by modifying BASE_REG_CLASS
to be LO_REGS instead of BASE_REGS. It is not clear what affect this
change would have. */
/* ??? This looks even more suspiciously wrong. PREFERRED_RELOAD_CLASS
must always return a strict subset of the input class. Just blindly
returning LO_REGS is safe only if the input class is a superset of LO_REGS,
but there is no check for this. Added another exception for NONARG_LO_REGS
because it is not a superset of LO_REGS. */
/* ??? We now use NONARG_LO_REGS for caller_save_spill_class, so the
comments about BASE_REGS are now obsolete. */
#define PREFERRED_RELOAD_CLASS(X,CLASS) \
((CLASS) == BASE_REGS || (CLASS) == NONARG_LO_REGS ? (CLASS) \
: LO_REGS)
/*
((CONSTANT_P ((X)) && GET_CODE ((X)) != CONST_INT \
&& ! CONSTANT_POOL_ADDRESS_P((X))) ? NO_REGS \
: (GET_CODE ((X)) == CONST_INT \
&& (unsigned HOST_WIDE_INT) INTVAL ((X)) > 255) ? NO_REGS \
: LO_REGS) */
/* Must leave BASE_REGS and NONARG_LO_REGS reloads alone, see comment
above. */
#define SECONDARY_RELOAD_CLASS(CLASS,MODE,X) \
((CLASS) != LO_REGS && (CLASS) != BASE_REGS && (CLASS) != NONARG_LO_REGS \
? ((true_regnum (X) == -1 ? LO_REGS \
: (true_regnum (X) + HARD_REGNO_NREGS (0, MODE) > 8) ? LO_REGS \
: NO_REGS)) \
: NO_REGS)
#define CLASS_MAX_NREGS(CLASS,MODE) HARD_REGNO_NREGS(0,(MODE))
int thumb_shiftable_const ();
#define CONST_OK_FOR_LETTER_P(VAL,C) \
((C) == 'I' ? (unsigned HOST_WIDE_INT) (VAL) < 256 \
: (C) == 'J' ? (VAL) > -256 && (VAL) <= 0 \
: (C) == 'K' ? thumb_shiftable_const (VAL) \
: (C) == 'L' ? (VAL) > -8 && (VAL) < 8 \
: (C) == 'M' ? ((unsigned HOST_WIDE_INT) (VAL) < 1024 \
&& ((VAL) & 3) == 0) \
: (C) == 'N' ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \
: (C) == 'O' ? ((VAL) >= -508 && (VAL) <= 508) \
: 0)
#define CONST_DOUBLE_OK_FOR_LETTER_P(VAL,C) 0
#define EXTRA_CONSTRAINT(X,C) \
((C) == 'Q' ? (GET_CODE (X) == MEM \
&& GET_CODE (XEXP (X, 0)) == LABEL_REF) : 0)
/* Stack Layout and Calling Conventions */
#define STACK_GROWS_DOWNWARD 1
/* #define FRAME_GROWS_DOWNWARD 1 */
/* #define ARGS_GROW_DOWNWARD 1 */
#define STARTING_FRAME_OFFSET 0
#define FIRST_PARM_OFFSET(FNDECL) 0
/* Registers that address the stack frame */
#define STACK_POINTER_REGNUM 13 /* Defined by the TPCS. */
#define FRAME_POINTER_REGNUM 7 /* TPCS defines this as 11 but it does not really mean it. */
#define ARG_POINTER_REGNUM 16 /* A fake hard register that is eliminated later on. */
#define STATIC_CHAIN_REGNUM 9
/* Define this if the program counter is overloaded on a register. */
#define PC_REGNUM 15
#define FRAME_POINTER_REQUIRED 0
#define ELIMINABLE_REGS \
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
/* On the Thumb we always want to perform the eliminations as we
actually only have one real register pointing to the stashed
variables: the stack pointer, and we never use the frame pointer. */
#define CAN_ELIMINATE(FROM,TO) 1
/* Note: This macro must match the code in thumb_function_prologue() in thumb.c. */
#define INITIAL_ELIMINATION_OFFSET(FROM,TO,OFFSET) \
{ \
(OFFSET) = 0; \
if ((FROM) == ARG_POINTER_REGNUM) \
{ \
int count_regs = 0; \
int regno; \
(OFFSET) += get_frame_size (); \
for (regno = 8; regno < 13; regno++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
count_regs++; \
if (count_regs) \
(OFFSET) += 4 * count_regs; \
count_regs = 0; \
for (regno = 0; regno < 8; regno++) \
if (regs_ever_live[regno] && ! call_used_regs[regno]) \
count_regs++; \
if (count_regs || ! leaf_function_p () || far_jump_used_p()) \
(OFFSET) += 4 * (count_regs + 1); \
if (TARGET_BACKTRACE) { \
if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) \
(OFFSET) += 20; \
else \
(OFFSET) += 16; } \
} \
if ((TO) == STACK_POINTER_REGNUM) \
(OFFSET) += current_function_outgoing_args_size; \
}
/* A C expression whose value is RTL representing the value of the return
address for the frame COUNT steps up from the current frame. */
#define RETURN_ADDR_RTX(COUNT, FRAME) \
thumb_return_addr (COUNT)
/* Passing Arguments on the stack */
/* Initialize data used by insn expanders. This is called from insn_emit,
once for every function before code is generated. */
#define INIT_EXPANDERS thumb_init_expanders ()
#define PROMOTE_PROTOTYPES 1
#define ACCUMULATE_OUTGOING_ARGS 1
#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
#define FUNCTION_ARG(CUM,MODE,TYPE,NAMED) \
((NAMED) ? ((CUM) >= 16 ? 0 : gen_rtx (REG, (MODE), (CUM) / 4)) \
: 0)
#define FUNCTION_ARG_PARTIAL_NREGS(CUM,MODE,TYPE,NAMED) \
(((CUM) < 16 && (CUM) + (((MODE) == BLKmode) \
? int_size_in_bytes (TYPE) \
: HARD_REGNO_NREGS (0, (MODE)) * 4) > 16) \
? 4 - (CUM) / 4 : 0)
#define CUMULATIVE_ARGS int
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \
((CUM) = ((FNTYPE) && aggregate_value_p (TREE_TYPE (FNTYPE))) ? 4 : 0)
#define FUNCTION_ARG_ADVANCE(CUM,MODE,TYPE,NAMED) \
(CUM) += ((((MODE) == BLKmode) \
? int_size_in_bytes (TYPE) \
: GET_MODE_SIZE (MODE)) + 3) & ~3
#define FUNCTION_ARG_REGNO_P(REGNO) \
((REGNO) >=0 && (REGNO) <= 3)
#define FUNCTION_VALUE(VALTYPE,FUNC) gen_rtx (REG, TYPE_MODE (VALTYPE), 0)
#define LIBCALL_VALUE(MODE) gen_rtx (REG, (MODE), 0)
#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == 0)
/* How large values are returned */
/* A C expression which can inhibit the returning of certain function values
in registers, based on the type of value. */
#define RETURN_IN_MEMORY(TYPE) thumb_return_in_memory (TYPE)
/* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return
values must be in memory. On the ARM, they need only do so if larger
than a word, or if they contain elements offset from zero in the struct. */
#define DEFAULT_PCC_STRUCT_RETURN 0
#define STRUCT_VALUE_REGNUM 0
#define FUNCTION_PROLOGUE(FILE,SIZE) thumb_function_prologue((FILE),(SIZE))
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
/* Generating code for profiling */
#define FUNCTION_PROFILER(STREAM,LABELNO) \
{ \
fprintf ((STREAM), "\tmov\\tip, lr\n"); \
fprintf ((STREAM), "\tbl\tmcount\n"); \
fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
}
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
{ \
extern int current_function_anonymous_args; \
current_function_anonymous_args = 1; \
if ((CUM) < 16) \
(PRETEND_SIZE) = 16 - (CUM); \
}
/* Trampolines for nested functions */
/* Output assembler code for a block containing the constant parts of
a trampoline, leaving space for the variable parts.
On the Thumb we always switch into ARM mode to execute the trampoline.
Why - because it is easier. This code will always be branched to via
a BX instruction and since the compiler magically generates the address
of the function the linker has no opportunity to ensure that the
bottom bit is set. Thus the processor will be in ARM mode when it
reaches this code. So we duplicate the ARM trampoline code and add
a switch into Thumb mode as well.
On the ARM, (if r8 is the static chain regnum, and remembering that
referencing pc adds an offset of 8) the trampoline looks like:
ldr r8, [pc, #0]
ldr pc, [pc]
.word static chain value
.word function's address
??? FIXME: When the trampoline returns, r8 will be clobbered. */
#define TRAMPOLINE_TEMPLATE(FILE) \
{ \
fprintf ((FILE), "\t.code 32\n"); \
fprintf ((FILE), ".Ltrampoline_start:\n"); \
fprintf ((FILE), "\tldr\t%s, [%spc, #8]\n", \
reg_names[STATIC_CHAIN_REGNUM], REGISTER_PREFIX); \
fprintf ((FILE), "\tldr\t%sip, [%spc, #8]\n", \
REGISTER_PREFIX, REGISTER_PREFIX); \
fprintf ((FILE), "\torr\t%sip, %sip, #1\n", \
REGISTER_PREFIX, REGISTER_PREFIX); \
fprintf ((FILE), "\tbx\t%sip\n", REGISTER_PREFIX); \
fprintf ((FILE), "\t.word\t0\n"); \
fprintf ((FILE), "\t.word\t0\n"); \
fprintf ((FILE), "\t.code 16\n"); \
}
/* Length in units of the trampoline for entering a nested function. */
#define TRAMPOLINE_SIZE 24
/* Alignment required for a trampoline in units. */
#define TRAMPOLINE_ALIGN 4
#define INITIALIZE_TRAMPOLINE(ADDR,FNADDR,CHAIN) \
{ \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((ADDR), 16)), \
(CHAIN)); \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((ADDR), 20)), \
(FNADDR)); \
}
/* Position Independent Code. */
/* We decide which register to use based on the compilation options and
the assembler in use. @@@ Actually, we don't currently for Thumb. */
extern int thumb_pic_register;
/* The register number of the register used to address a table of static
data addresses in memory. */
#define PIC_OFFSET_TABLE_REGNUM thumb_pic_register
#define FINALIZE_PIC thumb_finalize_pic ()
/* We can't directly access anything that contains a symbol,
nor can we indirect via the constant pool. */
#define LEGITIMATE_PIC_OPERAND_P(X) \
(! thumb_symbol_mentioned_p (X) \
&& (! CONSTANT_POOL_ADDRESS_P (X) \
|| ! thumb_symbol_mentioned_p (get_pool_constant (X))))
/* We need to know when we are making a constant pool; this determines
whether data needs to be in the GOT or can be referenced via a GOT
offset. */
extern int making_const_table;
#define CONDITIONAL_REGISTER_USAGE \
{ \
if (flag_pic) \
{ \
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 0; \
} \
}
/* Implicit Calls to Library Routines */
#define TARGET_MEM_FUNCTIONS 1
#define OVERRIDE_OPTIONS thumb_override_options ()
/* Addressing Modes */
#define HAVE_POST_INCREMENT 1
#define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X))
#define MAX_REGS_PER_ADDRESS 2
#ifdef REG_OK_STRICT
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_MODE_OK_FOR_BASE_P(X,MODE) \
REGNO_MODE_OK_FOR_BASE_P (REGNO (X), MODE)
#else /* REG_OK_STRICT */
#define REG_OK_FOR_BASE_P(X) \
(REGNO (X) < 8 || REGNO (X) == STACK_POINTER_REGNUM \
|| (X) == arg_pointer_rtx \
|| REGNO (X) >= FIRST_PSEUDO_REGISTER)
#define REG_MODE_OK_FOR_BASE_P(X,MODE) \
(REGNO (X) < 8 \
|| REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| (GET_MODE_SIZE (MODE) >= 4 \
&& (REGNO (X) == STACK_POINTER_REGNUM \
|| (X) == arg_pointer_rtx)))
#define REG_OK_FOR_INDEX_P(X) \
(REGNO (X) < 8 \
|| REGNO (X) >= FIRST_PSEUDO_REGISTER)
#endif /* REG_OK_STRICT */
/* In a REG+REG address, both must be INDEX registers. */
#define REG_OK_FOR_INDEXED_BASE_P(X) REG_OK_FOR_INDEX_P(X)
#define LEGITIMATE_OFFSET(MODE,VAL) \
(GET_MODE_SIZE (MODE) == 1 ? ((unsigned HOST_WIDE_INT) (VAL) < 32) \
: GET_MODE_SIZE (MODE) == 2 ? ((unsigned HOST_WIDE_INT) (VAL) < 64 \
&& ((VAL) & 1) == 0) \
: ((VAL) >= 0 && ((VAL) + GET_MODE_SIZE (MODE)) <= 128 \
&& ((VAL) & 3) == 0))
/* The AP may be eliminated to either the SP or the FP, so we use the
least common denominator, e.g. SImode, and offsets from 0 to 64. */
/* ??? Verify whether the above is the right approach. */
/* ??? Also, the FP may be eliminated to the SP, so perhaps that
needs special handling also. */
/* ??? Look at how the mips16 port solves this problem. It probably uses
better ways to solve some of these problems. */
/* Although it is not incorrect, we don't accept QImode and HImode
addresses based on the frame pointer or arg pointer until the reload pass starts.
This is so that eliminating such addresses into stack based ones
won't produce impossible code. */
#define GO_IF_LEGITIMATE_ADDRESS(MODE,X,WIN) \
{ \
/* ??? Not clear if this is right. Experiment. */ \
if (GET_MODE_SIZE (MODE) < 4 \
&& ! (reload_in_progress || reload_completed) \
&& (reg_mentioned_p (frame_pointer_rtx, X) \
|| reg_mentioned_p (arg_pointer_rtx, X) \
|| reg_mentioned_p (virtual_incoming_args_rtx, X) \
|| reg_mentioned_p (virtual_outgoing_args_rtx, X) \
|| reg_mentioned_p (virtual_stack_dynamic_rtx, X) \
|| reg_mentioned_p (virtual_stack_vars_rtx, X))) \
; \
/* Accept any base register. SP only in SImode or larger. */ \
else if (GET_CODE (X) == REG && REG_MODE_OK_FOR_BASE_P(X, MODE)) \
goto WIN; \
/* This is PC relative data before MACHINE_DEPENDENT_REORG runs. */ \
else if (GET_MODE_SIZE (MODE) >= 4 && CONSTANT_P (X) \
&& CONSTANT_POOL_ADDRESS_P (X) && ! flag_pic) \
goto WIN; \
/* This is PC relative data after MACHINE_DEPENDENT_REORG runs. */ \
else if (GET_MODE_SIZE (MODE) >= 4 && reload_completed \
&& (GET_CODE (X) == LABEL_REF \
|| (GET_CODE (X) == CONST \
&& GET_CODE (XEXP (X, 0)) == PLUS \
&& GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF \
&& GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT))) \
goto WIN; \
/* Post-inc indexing only supported for SImode and larger. */ \
else if (GET_CODE (X) == POST_INC && GET_MODE_SIZE (MODE) >= 4 \
&& GET_CODE (XEXP (X, 0)) == REG \
&& REG_OK_FOR_INDEX_P (XEXP (X, 0))) \
goto WIN; \
else if (GET_CODE (X) == PLUS) \
{ \
/* REG+REG address can be any two index registers. */ \
/* ??? REG+REG addresses have been completely disabled before \
reload completes, because we do not have enough available \
reload registers. We only have 3 guaranteed reload registers \
(NONARG_LO_REGS - the frame pointer), but we need at least 4 \
to support REG+REG addresses. We have left them enabled after \
reload completes, in the hope that reload_cse_regs and related \
routines will be able to create them after the fact. It is \
probably possible to support REG+REG addresses with additional \
reload work, but I do not not have enough time to attempt such \
a change at this time. */ \
/* ??? Normally checking the mode here is wrong, since it isn't \
impossible to use REG+REG with DFmode. However, the movdf \
pattern requires offsettable addresses, and REG+REG is not \
offsettable, so it must be rejected somehow. Trying to use \
'o' fails, because offsettable_address_p does a QImode check. \
QImode is not valid for stack addresses, and has a smaller \
range for non-stack bases, and this causes valid addresses \
to be rejected. So we just eliminate REG+REG here by checking \
the mode. */ \
/* We also disallow FRAME+REG addressing since we know that FRAME \
will be replaced with STACK, and SP relative addressing only \
permits SP+OFFSET. */ \
if (GET_MODE_SIZE (MODE) <= 4 \
/* ??? See comment above. */ \
&& reload_completed \
&& GET_CODE (XEXP (X, 0)) == REG \
&& GET_CODE (XEXP (X, 1)) == REG \
&& XEXP (X, 0) != frame_pointer_rtx \
&& XEXP (X, 1) != frame_pointer_rtx \
&& XEXP (X, 0) != virtual_stack_vars_rtx \
&& XEXP (X, 1) != virtual_stack_vars_rtx \
&& REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
&& REG_OK_FOR_INDEX_P (XEXP (X, 1))) \
goto WIN; \
/* REG+const has 5-7 bit offset for non-SP registers. */ \
else if (GET_CODE (XEXP (X, 0)) == REG \
&& (REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
|| XEXP (X, 0) == arg_pointer_rtx) \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \
goto WIN; \
/* REG+const has 10 bit offset for SP, but only SImode and \
larger is supported. */ \
/* ??? Should probably check for DI/DFmode overflow here \
just like GO_IF_LEGITIMATE_OFFSET does. */ \
else if (GET_CODE (XEXP (X, 0)) == REG \
&& REGNO (XEXP (X, 0)) == STACK_POINTER_REGNUM \
&& GET_MODE_SIZE (MODE) >= 4 \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& ((unsigned HOST_WIDE_INT) INTVAL (XEXP (X, 1)) \
+ GET_MODE_SIZE (MODE)) <= 1024 \
&& (INTVAL (XEXP (X, 1)) & 3) == 0) \
goto WIN; \
} \
else if (GET_MODE_CLASS (MODE) != MODE_FLOAT \
&& GET_CODE (X) == SYMBOL_REF \
&& CONSTANT_POOL_ADDRESS_P (X) \
&& ! (flag_pic \
&& thumb_symbol_mentioned_p (get_pool_constant (X)))) \
goto WIN; \
}
/* ??? If an HImode FP+large_offset address is converted to an HImode
SP+large_offset address, then reload won't know how to fix it. It sees
only that SP isn't valid for HImode, and so reloads the SP into an index
register, but the resulting address is still invalid because the offset
is too big. We fix it here instead by reloading the entire address. */
/* We could probably achieve better results by defining PROMOTE_MODE to help
cope with the variances between the Thumb's signed and unsigned byte and
halfword load instructions. */
#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN) \
{ \
if (GET_CODE (X) == PLUS \
&& GET_MODE_SIZE (MODE) < 4 \
&& GET_CODE (XEXP (X, 0)) == REG \
&& XEXP (X, 0) == stack_pointer_rtx \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& ! LEGITIMATE_OFFSET (MODE, INTVAL (XEXP (X, 1)))) \
{ \
rtx orig_X = X; \
X = copy_rtx (X); \
push_reload (orig_X, NULL_RTX, &X, NULL_PTR, \
BASE_REG_CLASS, \
Pmode, VOIDmode, 0, 0, OPNUM, TYPE); \
goto WIN; \
} \
}
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)
extern struct rtx_def * legitimize_pic_address ();
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
if (flag_pic) \
(X) = legitimize_pic_address (OLDX, MODE, NULL_RTX);
#define LEGITIMATE_CONSTANT_P(X) \
(GET_CODE (X) == CONST_INT \
|| GET_CODE (X) == CONST_DOUBLE \
|| CONSTANT_ADDRESS_P (X))
/* Condition Code Status */
#define NOTICE_UPDATE_CC(EXP,INSN) \
{ \
if (get_attr_conds ((INSN)) != CONDS_UNCHANGED) \
CC_STATUS_INIT; \
}
/* Describing Relative Costs of Operations */
#define SLOW_BYTE_ACCESS 0
#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) 1
#define NO_FUNCTION_CSE 1
#define NO_RECURSIVE_FUNCTION_CSE 1
#define REGISTER_MOVE_COST(FROM,TO) \
(((FROM) == HI_REGS || (TO) == HI_REGS) ? 4 : 2)
#define MEMORY_MOVE_COST(M,CLASS,IN) \
((GET_MODE_SIZE(M) < 4 ? 8 : 2 * GET_MODE_SIZE(M)) * (CLASS == LO_REGS ? 1 : 2))
/* This will allow better space optimization when compiling with -O */
#define BRANCH_COST (optimize > 1 ? 1 : 0)
#define RTX_COSTS(X,CODE,OUTER) \
case MULT: \
if (GET_CODE (XEXP (X, 1)) == CONST_INT) \
{ \
int cycles = 0; \
unsigned HOST_WIDE_INT i = INTVAL (XEXP (X, 1)); \
while (i) \
{ \
i >>= 2; \
cycles++; \
} \
return COSTS_N_INSNS (2) + cycles; \
} \
return COSTS_N_INSNS (1) + 16; \
case ASHIFT: case ASHIFTRT: case LSHIFTRT: case ROTATERT: \
case PLUS: case MINUS: case COMPARE: case NEG: case NOT: \
return COSTS_N_INSNS (1); \
case SET: \
return (COSTS_N_INSNS (1) \
+ 4 * ((GET_CODE (SET_SRC (X)) == MEM) \
+ GET_CODE (SET_DEST (X)) == MEM))
#define CONST_COSTS(X,CODE,OUTER) \
case CONST_INT: \
if ((OUTER) == SET) \
{ \
if ((unsigned HOST_WIDE_INT) INTVAL (X) < 256) \
return 0; \
if (thumb_shiftable_const (INTVAL (X))) \
return COSTS_N_INSNS (2); \
return COSTS_N_INSNS (3); \
} \
else if (OUTER == PLUS \
&& INTVAL (X) < 256 && INTVAL (X) > -256) \
return 0; \
else if (OUTER == COMPARE \
&& (unsigned HOST_WIDE_INT) INTVAL (X) < 256) \
return 0; \
else if (OUTER == ASHIFT || OUTER == ASHIFTRT \
|| OUTER == LSHIFTRT) \
return 0; \
return COSTS_N_INSNS (2); \
case CONST: \
case CONST_DOUBLE: \
case LABEL_REF: \
case SYMBOL_REF: \
return COSTS_N_INSNS(3);
#define ADDRESS_COST(X) \
((GET_CODE (X) == REG \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG \
&& GET_CODE (XEXP (X, 1)) == CONST_INT)) \
? 1 : 2)
/* Position Independent Code */
extern const char * thumb_pic_register_string;
extern int thumb_pic_register;
/* The register number of the register used to address a table of static
data addresses in memory. */
#define PIC_OFFSET_TABLE_REGNUM thumb_pic_register
#define FINALIZE_PIC thumb_finalize_pic ()
/* We can't directly access anything that contains a symbol,
nor can we indirect via the constant pool. */
#define LEGITIMATE_PIC_OPERAND_P(X) \
(! thumb_symbol_mentioned_p (X) \
&& (! CONSTANT_POOL_ADDRESS_P (X) \
|| ! thumb_symbol_mentioned_p (get_pool_constant (X))))
/* We need to know when we are making a constant pool; this determines
whether data needs to be in the GOT or can be referenced via a GOT
offset. */
extern int making_const_table;
#define PRINT_OPERAND(STREAM,X,CODE) \
thumb_print_operand((STREAM), (X), (CODE))
#define PRINT_OPERAND_ADDRESS(STREAM,X) \
{ \
if (GET_CODE ((X)) == REG) \
fprintf ((STREAM), "[%s]", reg_names[REGNO ((X))]); \
else if (GET_CODE ((X)) == POST_INC) \
fprintf ((STREAM), "%s!", reg_names[REGNO (XEXP (X, 0))]); \
else if (GET_CODE ((X)) == PLUS) \
{ \
if (GET_CODE (XEXP ((X), 1)) == CONST_INT) \
fprintf ((STREAM), "[%s, #%d]", \
reg_names[REGNO (XEXP ((X), 0))], \
(int) INTVAL (XEXP ((X), 1))); \
else \
fprintf ((STREAM), "[%s, %s]", \
reg_names[REGNO (XEXP ((X), 0))], \
reg_names[REGNO (XEXP ((X), 1))]); \
} \
else \
output_addr_const ((STREAM), (X)); \
}
/* Handles PIC addr specially */
#define OUTPUT_INT_ADDR_CONST(STREAM,X) \
{ \
if (flag_pic && GET_CODE(X) == CONST && is_pic(X)) \
{ \
output_addr_const(STREAM, XEXP (XEXP (XEXP (X, 0), 0), 0)); \
fputs(" - (", STREAM); \
output_addr_const(STREAM, XEXP (XEXP (XEXP (X, 0), 1), 0)); \
fputs(")", STREAM); \
} \
else output_addr_const(STREAM, X); \
\
/* Mark symbols as position independent. We only do this in the \
.text segment, not in the .data segment. */ \
if (NEED_GOT_RELOC && flag_pic && making_const_table && \
(GET_CODE(X) == SYMBOL_REF || GET_CODE(X) == LABEL_REF)) \
{ \
if (GET_CODE(X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P(X)) \
fprintf(STREAM, "(GOTOFF)"); \
else if (GET_CODE (X) == LABEL_REF) \
fprintf(STREAM, "(GOTOFF)"); \
else \
fprintf(STREAM, "(GOT)"); \
} \
}
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '@' || ((CODE) == '_') || ((CODE) == '|'))
/* Emit a special directive when defining a function name.
This is used by the assembler to assit with interworking. */
#define ASM_DECLARE_FUNCTION_NAME(file, name, decl) \
if (! is_called_in_ARM_mode (decl)) \
fprintf (file, "\t.thumb_func\n") ; \
else \
fprintf (file, "\t.code\t32\n") ; \
ASM_OUTPUT_LABEL (file, name)
#define ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \
asm_fprintf ((STREAM), "\tpush {%R%s}\n", reg_names[(REGNO)])
#define ASM_OUTPUT_REG_POP(STREAM,REGNO) \
asm_fprintf ((STREAM), "\tpop {%R%s}\n", reg_names[(REGNO)])
#define FINAL_PRESCAN_INSN(INSN,OPVEC,NOPERANDS) \
thumb_final_prescan_insn (INSN)
/* Controlling Debugging Information Format */
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
/* Specific options for DBX Output */
#define DEFAULT_GDB_EXTENSIONS 1
/* Cross Compilation and Floating Point */
#define REAL_ARITHMETIC
/* Miscellaneous Parameters */
#define PREDICATE_CODES \
{"thumb_cmp_operand", {SUBREG, REG, CONST_INT}},
#define CASE_VECTOR_MODE Pmode
#define WORD_REGISTER_OPERATIONS
#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND
#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
#define EASY_DIV_EXPR TRUNC_DIV_EXPR
#define MOVE_MAX 4
#define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) 1
#define STORE_FLAG_VALUE 1
#define Pmode SImode
#define FUNCTION_MODE SImode
#define DOLLARS_IN_IDENTIFIERS 0
#define NO_DOLLAR_IN_LABEL 1
/* The literal pool needs to reside in the text area due to the
limited PC addressing range: */
#define MACHINE_DEPENDENT_REORG(INSN) thumb_reorg (INSN)
/* Options specific to Thumb */
/* True if a return instruction can be used in this function. */
#define USE_RETURN (reload_completed && thumb_trivial_epilogue ())
/* Definitions of target machine for GNU compiler,
for Thumb with PE object format.
Copyright (C) 1998, 1999 Free Software Foundation, Inc.
Derived from arm/coff.h and arm/pe.h originally by Doug Evans (evans@cygnus.com).
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "arm/thumb.h"
#define THUMB_PE 1
/* Run-time Target Specification. */
#undef TARGET_VERSION
#define TARGET_VERSION fputs (" (Thumb/pe)", stderr)
/* Support the __declspec keyword by turning them into attributes.
We currently only support: naked, dllimport, and dllexport.
Note that the current way we do this may result in a collision with
predefined attributes later on. This can be solved by using one attribute,
say __declspec__, and passing args to it. The problem with that approach
is that args are not accumulated: each new appearance would clobber any
existing args. */
#undef CPP_PREDEFINES
#define CPP_PREDEFINES "\
-Dthumb -D__thumb -D__pe__ -Acpu(arm) -Amachine(arm) \
-D__declspec(x)=__attribute__((x)) \
"
/* Experimental addition for pr 7885.
Ignore dllimport for functions. */
#define ARM_FLAG_NOP_FUN_IMPORT 0x20000
#define TARGET_NOP_FUN_DLLIMPORT (target_flags & ARM_FLAG_NOP_FUN_IMPORT)
#undef SUBTARGET_SWITCHES
#define SUBTARGET_SWITCHES \
{ "nop-fun-dllimport", ARM_FLAG_NOP_FUN_IMPORT, "Ignore dllimport attribute for functions" }, \
{ "no-nop-fun-dllimport", -ARM_FLAG_NOP_FUN_IMPORT, "" },
#undef TARGET_DEFAULT
#define TARGET_DEFAULT ARM_FLAG_NOP_FUN_IMPORT
#undef WCHAR_TYPE
#define WCHAR_TYPE "short unsigned int"
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE 16
/* Setting this to 32 produces more efficient code, but the value set in previous
versions of this toolchain was 8, which produces more compact structures. The
command line option -mstructure_size_boundary=<n> can be used to change this
value. */
#undef STRUCTURE_SIZE_BOUNDARY
#define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary
extern int arm_structure_size_boundary;
/* This is COFF, but prefer stabs. */
#define SDB_DEBUGGING_INFO
#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG
#include "dbxcoff.h"
/* Note - it is important that these definitions match those in semi.h for the ARM port. */
#undef LOCAL_LABEL_PREFIX
#define LOCAL_LABEL_PREFIX "."
#undef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX "_"
/* A C statement to output assembler commands which will identify the
object file as having been compiled with GNU CC (or another GNU
compiler). */
#define ASM_IDENTIFY_GCC(STREAM) \
fprintf (STREAM, "%sgcc2_compiled.:\n%s", LOCAL_LABEL_PREFIX, ASM_APP_OFF )
#undef ASM_FILE_START
#define ASM_FILE_START(STREAM) \
do { \
fprintf ((STREAM), "%s Generated by gcc %s for Thumb/coff\n", \
ASM_COMMENT_START, version_string); \
fprintf ((STREAM), ASM_APP_OFF); \
} while (0)
/* A C statement to output something to the assembler file to switch to section
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
NULL_TREE. Some target formats do not support arbitrary sections. Do not
define this macro in such cases. */
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do { \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"x\"\n", (NAME)); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"\"\n", (NAME)); \
else \
fprintf (STREAM, "\t.section %s,\"w\"\n", (NAME)); \
} while (0)
/* Support the ctors/dtors and other sections. */
#undef INIT_SECTION_ASM_OP
/* Define this macro if jump tables (for `tablejump' insns) should be
output in the text section, along with the assembler instructions.
Otherwise, the readonly data section is used. */
#define JUMP_TABLES_IN_TEXT_SECTION 1
#undef READONLY_DATA_SECTION
#define READONLY_DATA_SECTION rdata_section
#undef RDATA_SECTION_ASM_OP
#define RDATA_SECTION_ASM_OP "\t.section .rdata"
#undef CTORS_SECTION_ASM_OP
#define CTORS_SECTION_ASM_OP "\t.section .ctors,\"x\""
#undef DTORS_SECTION_ASM_OP
#define DTORS_SECTION_ASM_OP "\t.section .dtors,\"x\""
/* A list of other sections which the compiler might be "in" at any
given time. */
#undef EXTRA_SECTIONS
#define EXTRA_SECTIONS SUBTARGET_EXTRA_SECTIONS in_rdata, in_ctors, in_dtors
#define SUBTARGET_EXTRA_SECTIONS
/* A list of extra section function definitions. */
#undef EXTRA_SECTION_FUNCTIONS
#define EXTRA_SECTION_FUNCTIONS \
RDATA_SECTION_FUNCTION \
CTORS_SECTION_FUNCTION \
DTORS_SECTION_FUNCTION \
SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define RDATA_SECTION_FUNCTION \
void \
rdata_section () \
{ \
if (in_section != in_rdata) \
{ \
fprintf (asm_out_file, "%s\n", RDATA_SECTION_ASM_OP); \
in_section = in_rdata; \
} \
}
#define CTORS_SECTION_FUNCTION \
void \
ctors_section () \
{ \
if (in_section != in_ctors) \
{ \
fprintf (asm_out_file, "%s\n", CTORS_SECTION_ASM_OP); \
in_section = in_ctors; \
} \
}
#define DTORS_SECTION_FUNCTION \
void \
dtors_section () \
{ \
if (in_section != in_dtors) \
{ \
fprintf (asm_out_file, "%s\n", DTORS_SECTION_ASM_OP); \
in_section = in_dtors; \
} \
}
/* Support the ctors/dtors sections for g++. */
#define INT_ASM_OP ".word"
/* A C statement (sans semicolon) to output an element in the table of
global constructors. */
#undef ASM_OUTPUT_CONSTRUCTOR
#define ASM_OUTPUT_CONSTRUCTOR(STREAM,NAME) \
do { \
ctors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} while (0)
/* A C statement (sans semicolon) to output an element in the table of
global destructors. */
#undef ASM_OUTPUT_DESTRUCTOR
#define ASM_OUTPUT_DESTRUCTOR(STREAM,NAME) \
do { \
dtors_section (); \
fprintf (STREAM, "\t%s\t ", INT_ASM_OP); \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\n"); \
} while (0)
/* __CTOR_LIST__ and __DTOR_LIST__ must be defined by the linker script. */
#define CTOR_LISTS_DEFINED_EXTERNALLY
#undef DO_GLOBAL_CTORS_BODY
#undef DO_GLOBAL_DTORS_BODY
/* The ARM development system defines __main. */
#define NAME__MAIN "__gccmain"
#define SYMBOL__MAIN __gccmain
/* This is to better conform to the ARM PCS.
Richard Earnshaw hasn't put this into FSF sources yet so it's here. */
#undef RETURN_IN_MEMORY
#define RETURN_IN_MEMORY(TYPE) \
((TYPE_MODE ((TYPE)) == BLKmode && ! TYPE_NO_FORCE_BLK (TYPE)) \
|| (AGGREGATE_TYPE_P ((TYPE)) && arm_pe_return_in_memory ((TYPE))))
extern int arm_pe_return_in_memory ();
/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
is a valid machine specific attribute for DECL.
The attributes in ATTRIBUTES have previously been assigned to DECL. */
extern int arm_pe_valid_machine_decl_attribute ();
#undef VALID_MACHINE_DECL_ATTRIBUTE
#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRIBUTES, IDENTIFIER, ARGS) \
arm_pe_valid_machine_decl_attribute (DECL, ATTRIBUTES, IDENTIFIER, ARGS)
extern union tree_node * arm_pe_merge_machine_decl_attributes ();
#define MERGE_MACHINE_DECL_ATTRIBUTES(OLD, NEW) \
arm_pe_merge_machine_decl_attributes ((OLD), (NEW))
/* In addition to the stuff done in arm.h, we must mark dll symbols specially.
Definitions of dllexport'd objects install some info in the .drectve
section. References to dllimport'd objects are fetched indirectly via
__imp_. If both are declared, dllexport overrides.
This is also needed to implement one-only vtables: they go into their own
section and we need to set DECL_SECTION_NAME so we do that here.
Note that we can be called twice on the same decl. */
extern void arm_pe_encode_section_info ();
#undef ENCODE_SECTION_INFO
#define ENCODE_SECTION_INFO(DECL) \
arm_pe_encode_section_info (DECL)
#define REDO_SECTION_INFO_P(DECL) 1
/* Utility used only in this file. */
#define ARM_STRIP_NAME_ENCODING(SYM_NAME) \
((SYM_NAME) + ((SYM_NAME)[0] == '@' ? 3 : 0))
/* Strip any text from SYM_NAME added by ENCODE_SECTION_INFO and store
the result in VAR. */
#undef STRIP_NAME_ENCODING
#define STRIP_NAME_ENCODING(VAR, SYM_NAME) \
(VAR) = ARM_STRIP_NAME_ENCODING (SYM_NAME)
/* Define this macro if in some cases global symbols from one translation
unit may not be bound to undefined symbols in another translation unit
without user intervention. For instance, under Microsoft Windows
symbols must be explicitly imported from shared libraries (DLLs). */
#define MULTIPLE_SYMBOL_SPACES
#define UNIQUE_SECTION_P(DECL) DECL_ONE_ONLY (DECL)
extern void arm_pe_unique_section ();
#define UNIQUE_SECTION(DECL,RELOC) arm_pe_unique_section (DECL, RELOC)
#define SUPPORTS_ONE_ONLY 1
/* A C statement to output something to the assembler file to switch to section
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
NULL_TREE. Some target formats do not support arbitrary sections. Do not
define this macro in such cases. */
#undef ASM_OUTPUT_SECTION_NAME
#define ASM_OUTPUT_SECTION_NAME(STREAM, DECL, NAME, RELOC) \
do { \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"x\"\n", (NAME)); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"\"\n", (NAME)); \
else \
fprintf (STREAM, "\t.section %s,\"w\"\n", (NAME)); \
/* Functions may have been compiled at various levels of \
optimization so we can't use `same_size' here. Instead, \
have the linker pick one. */ \
if ((DECL) && DECL_ONE_ONLY (DECL)) \
fprintf (STREAM, "\t.linkonce %s\n", \
TREE_CODE (DECL) == FUNCTION_DECL \
? "discard" : "same_size"); \
} while (0)
/* This outputs a lot of .req's to define alias for various registers.
Let's try to avoid this. */
#undef ASM_FILE_START
#define ASM_FILE_START(STREAM) \
do { \
fprintf (STREAM, "%s Generated by gcc %s for ARM/pe\n", \
ASM_COMMENT_START, version_string); \
output_file_directive ((STREAM), main_input_filename); \
} while (0)
/* Output a reference to a label. */
#undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(STREAM, NAME) \
fprintf (STREAM, "%s%s", USER_LABEL_PREFIX, ARM_STRIP_NAME_ENCODING (NAME))
/* Output a function definition label. */
#undef ASM_DECLARE_FUNCTION_NAME
#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \
do { \
if (arm_dllexport_name_p (NAME)) \
{ \
drectve_section (); \
fprintf (STREAM, "\t.ascii \" -export:%s\"\n", \
ARM_STRIP_NAME_ENCODING (NAME)); \
function_section (DECL); \
} \
if (! is_called_in_ARM_mode (decl)) \
fprintf (STREAM, "\t.thumb_func\n") ; \
else \
fprintf (STREAM, "\t.code\t32\n") ; \
ASM_OUTPUT_LABEL ((STREAM), (NAME)); \
} while (0)
/* Output a common block. */
#undef ASM_OUTPUT_COMMON
#define ASM_OUTPUT_COMMON(STREAM, NAME, SIZE, ROUNDED) \
do { \
if (arm_dllexport_name_p (NAME)) \
{ \
drectve_section (); \
fprintf ((STREAM), "\t.ascii \" -export:%s\"\n", \
ARM_STRIP_NAME_ENCODING (NAME)); \
} \
if (! arm_dllimport_name_p (NAME)) \
{ \
fprintf ((STREAM), "\t.comm\t"); \
assemble_name ((STREAM), (NAME)); \
fprintf ((STREAM), ", %d\t%s %d\n", \
(ROUNDED), ASM_COMMENT_START, (SIZE)); \
} \
} while (0)
/* Output the label for an initialized variable. */
#undef ASM_DECLARE_OBJECT_NAME
#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \
do { \
if (arm_dllexport_name_p (NAME)) \
{ \
enum in_section save_section = in_section; \
drectve_section (); \
fprintf (STREAM, "\t.ascii \" -export:%s\"\n", \
ARM_STRIP_NAME_ENCODING (NAME)); \
switch_to_section (save_section, (DECL)); \
} \
ASM_OUTPUT_LABEL ((STREAM), (NAME)); \
} while (0)
/* Support the ctors/dtors and other sections. */
#define DRECTVE_SECTION_ASM_OP "\t.section .drectve"
/* A list of other sections which the compiler might be "in" at any
given time. */
#undef SUBTARGET_EXTRA_SECTIONS
#define SUBTARGET_EXTRA_SECTIONS in_drectve,
/* A list of extra section function definitions. */
#undef SUBTARGET_EXTRA_SECTION_FUNCTIONS
#define SUBTARGET_EXTRA_SECTION_FUNCTIONS \
DRECTVE_SECTION_FUNCTION \
SWITCH_TO_SECTION_FUNCTION
#define DRECTVE_SECTION_FUNCTION \
void \
drectve_section () \
{ \
if (in_section != in_drectve) \
{ \
fprintf (asm_out_file, "%s\n", DRECTVE_SECTION_ASM_OP); \
in_section = in_drectve; \
} \
}
/* Switch to SECTION (an `enum in_section').
??? This facility should be provided by GCC proper.
The problem is that we want to temporarily switch sections in
ASM_DECLARE_OBJECT_NAME and then switch back to the original section
afterwards. */
#define SWITCH_TO_SECTION_FUNCTION \
void \
switch_to_section (section, decl) \
enum in_section section; \
tree decl; \
{ \
switch (section) \
{ \
case in_text: text_section (); break; \
case in_data: data_section (); break; \
case in_named: named_section (decl, NULL, 0); break; \
case in_rdata: rdata_section (); break; \
case in_ctors: ctors_section (); break; \
case in_dtors: dtors_section (); break; \
case in_drectve: drectve_section (); break; \
default: abort (); break; \
} \
}
extern int thumb_pe_valid_machine_decl_attribute ();
extern int arm_dllexport_p ();
extern int arm_dllimport_p ();
extern int arm_dllexport_name_p ();
extern int arm_dllimport_name_p ();
extern int arm_pe_return_in_memory ();
...@@ -24,9 +24,17 @@ Boston, MA 02111-1307, USA. */ ...@@ -24,9 +24,17 @@ Boston, MA 02111-1307, USA. */
#define TARGET_VERSION fputs (" (ARM/ELF non-Linux)", stderr); #define TARGET_VERSION fputs (" (ARM/ELF non-Linux)", stderr);
#endif #endif
/* If you don't define HAVE_ATEXIT, and the object file format/OS/whatever
does not support constructors/destructors, then gcc implements destructors
by defining its own exit function, which calls the destructors. This gcc
exit function overrides the C library's exit function, and this can cause
all kinds of havoc if the C library has a non-trivial exit function. You
really don't want to use the exit function in libgcc2.c. */
#define HAVE_ATEXIT
/* Default to using APCS-32 and software floating point. */ /* Default to using APCS-32 and software floating point. */
#ifndef TARGET_DEFAULT #ifndef TARGET_DEFAULT
#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32) #define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME)
#endif #endif
/* Now we define the strings used to build the spec file. */ /* Now we define the strings used to build the spec file. */
...@@ -70,20 +78,20 @@ rdata_section () \ ...@@ -70,20 +78,20 @@ rdata_section () \
} }
#define CTOR_LIST_BEGIN \ #define CTOR_LIST_BEGIN \
asm (CTORS_SECTION_ASM_OP); \ asm (CTORS_SECTION_ASM_OP); \
func_ptr __CTOR_LIST__[1] = { (func_ptr) (-1) } func_ptr __CTOR_LIST__[1] = { (func_ptr) (-1) }
#define CTOR_LIST_END \ #define CTOR_LIST_END \
asm (CTORS_SECTION_ASM_OP); \ asm (CTORS_SECTION_ASM_OP); \
func_ptr __CTOR_END__[1] = { (func_ptr) 0 }; func_ptr __CTOR_END__[1] = { (func_ptr) 0 };
#define DTOR_LIST_BEGIN \ #define DTOR_LIST_BEGIN \
asm (DTORS_SECTION_ASM_OP); \ asm (DTORS_SECTION_ASM_OP); \
func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) } func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) }
#define DTOR_LIST_END \ #define DTOR_LIST_END \
asm (DTORS_SECTION_ASM_OP); \ asm (DTORS_SECTION_ASM_OP); \
func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; func_ptr __DTOR_END__[1] = { (func_ptr) 0 };
/* A C statement to output something to the assembler file to switch to section /* A C statement to output something to the assembler file to switch to section
NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or NAME for object DECL which is either a FUNCTION_DECL, a VAR_DECL or
...@@ -93,13 +101,13 @@ func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; ...@@ -93,13 +101,13 @@ func_ptr __DTOR_END__[1] = { (func_ptr) 0 };
do \ do \
{ \ { \
if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \ if ((DECL) && TREE_CODE (DECL) == FUNCTION_DECL) \
fprintf (STREAM, "\t.section %s,\"ax\",%%progbits\n", NAME); \ fprintf (STREAM, "\t.section %s,\"ax\",@progbits\n", (NAME)); \
else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \ else if ((DECL) && DECL_READONLY_SECTION (DECL, RELOC)) \
fprintf (STREAM, "\t.section %s,\"a\"\n", NAME); \ fprintf (STREAM, "\t.section %s,\"a\"\n", (NAME)); \
else if (! strncmp (NAME, ".bss", 4)) \ else if (! strncmp (NAME, ".bss", 4)) \
fprintf (STREAM, "\t.section %s,\"aw\",%%nobits\n", NAME); \ fprintf (STREAM, "\t.section %s,\"aw\",@nobits\n", (NAME)); \
else \ else \
fprintf (STREAM, "\t.section %s,\"aw\"\n", NAME); \ fprintf (STREAM, "\t.section %s,\"aw\"\n", (NAME)); \
} \ } \
while (0) while (0)
...@@ -111,11 +119,13 @@ func_ptr __DTOR_END__[1] = { (func_ptr) 0 }; ...@@ -111,11 +119,13 @@ func_ptr __DTOR_END__[1] = { (func_ptr) 0 };
fprintf ((FILE), "\t%s\t%s", UNALIGNED_WORD_ASM_OP, ADDR) fprintf ((FILE), "\t%s\t%s", UNALIGNED_WORD_ASM_OP, ADDR)
#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \ #define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \
do { \ do \
{ \
fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \ fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \
output_addr_const ((FILE), (RTX)); \ output_addr_const ((FILE), (RTX)); \
fputc ('\n', (FILE)); \ fputc ('\n', (FILE)); \
} while (0) } \
while (0)
/* The ARM development system defines __main. */ /* The ARM development system defines __main. */
...@@ -127,7 +137,6 @@ do { \ ...@@ -127,7 +137,6 @@ do { \
((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \ ((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
&& DECL_SECTION_NAME (DECL) != NULL_TREE) && DECL_SECTION_NAME (DECL) != NULL_TREE)
#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1) #define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1)
#define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL) || flag_data_sections) #define UNIQUE_SECTION_P(DECL) (DECL_ONE_ONLY (DECL) || flag_data_sections)
...@@ -137,10 +146,10 @@ do { \ ...@@ -137,10 +146,10 @@ do { \
{ \ { \
int len; \ int len; \
int sec; \ int sec; \
char *name; \ const char * name; \
char *string; \ char * string; \
char *prefix; \ char * prefix; \
static char *prefixes[4][2] = \ static char * prefixes[4][2] = \
{ \ { \
{ ".text.", ".gnu.linkonce.t." }, \ { ".text.", ".gnu.linkonce.t." }, \
{ ".rodata.", ".gnu.linkonce.r." }, \ { ".rodata.", ".gnu.linkonce.r." }, \
...@@ -216,4 +225,4 @@ do { \ ...@@ -216,4 +225,4 @@ do { \
#endif #endif
/* Now get the routine arm-elf definitions. */ /* Now get the routine arm-elf definitions. */
#include "arm/elf.h" #include "elf.h"
...@@ -3452,11 +3452,11 @@ for machine in $build $host $target; do ...@@ -3452,11 +3452,11 @@ for machine in $build $host $target; do
;; ;;
arm-*-coff* | armel-*-coff*) arm-*-coff* | armel-*-coff*)
tm_file=arm/coff.h tm_file=arm/coff.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-coff
;; ;;
arm-*-vxworks*) arm-*-vxworks*)
tm_file=arm/vxarm.h tm_file=arm/vxarm.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-coff
thread_file='vxworks' thread_file='vxworks'
;; ;;
arm-*-riscix1.[01]*) # Acorn RISC machine (early versions) arm-*-riscix1.[01]*) # Acorn RISC machine (early versions)
...@@ -3536,11 +3536,11 @@ for machine in $build $host $target; do ...@@ -3536,11 +3536,11 @@ for machine in $build $host $target; do
;; ;;
arm*-*-aout) arm*-*-aout)
tm_file=arm/aout.h tm_file=arm/aout.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-aout
;; ;;
arm*-*-ecos-elf) arm*-*-ecos-elf)
tm_file=arm/ecos-elf.h tm_file=arm/ecos-elf.h
tmake_file=arm/t-elf tmake_file=arm/t-arm-elf
;; ;;
arm*-*-elf) arm*-*-elf)
tm_file=arm/unknown-elf.h tm_file=arm/unknown-elf.h
...@@ -6039,56 +6039,13 @@ for machine in $build $host $target; do ...@@ -6039,56 +6039,13 @@ for machine in $build $host $target; do
# ;; # ;;
# tahoe-*-bsd*) # tahoe running BSD # tahoe-*-bsd*) # tahoe running BSD
# ;; # ;;
thumb-*-coff* | thumbel-*-coff*) thumb*-*-*)
tm_file=arm/tcoff.h { echo "configure: error:
out_file=arm/thumb.c *** The Thumb targets have been depreciated. The equivalent
xm_file=arm/xm-thumb.h *** ARM based toolchain can now generated Thumb instructions
md_file=arm/thumb.md *** when the -mthumb switch is given to the compiler." 1>&2; exit 1; }
tmake_file=arm/t-thumb
tm_p_file=arm/thumb-protos.h
;;
thumb-*-elf* | thumbel-*-elf*)
tm_file=arm/telf.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb-elf
tm_p_file=arm/thumb-protos.h
;;
thumb-*-linux-gnu*)
tm_file=arm/linux-telf.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb-linux
tm_p_file=arm/thumb-protos.h
;;
thumb-*-uclinux*)
tm_file=arm/uclinux-telf.h
out_file=arm/thumb.c
md_file=arm/thumb.md
tmake_file=arm/t-thumb-linux
xm_file=arm/xm-thumb.h
tm_p_file=arm/thumb-protos.h
;;
thumb-wrs-vxworks)
tm_file=arm/tcoff.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb
thread_file='vxworks'
tm_p_file=arm/thumb-protos.h
;;
thumb-*-pe)
tm_file=arm/tpe.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-pe-thumb
extra_objs=pe.o
tm_p_file=arm/thumb-protos.h
;; ;;
# This hasn't been upgraded to GCC 2. # This hasn't been upgraded to GCC 2.
# tron-*-*) # tron-*-*)
# cpu_type=gmicro # cpu_type=gmicro
......
...@@ -727,11 +727,11 @@ changequote([,])dnl ...@@ -727,11 +727,11 @@ changequote([,])dnl
;; ;;
arm-*-coff* | armel-*-coff*) arm-*-coff* | armel-*-coff*)
tm_file=arm/coff.h tm_file=arm/coff.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-coff
;; ;;
arm-*-vxworks*) arm-*-vxworks*)
tm_file=arm/vxarm.h tm_file=arm/vxarm.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-coff
thread_file='vxworks' thread_file='vxworks'
;; ;;
changequote(,)dnl changequote(,)dnl
...@@ -813,11 +813,11 @@ changequote([,])dnl ...@@ -813,11 +813,11 @@ changequote([,])dnl
;; ;;
arm*-*-aout) arm*-*-aout)
tm_file=arm/aout.h tm_file=arm/aout.h
tmake_file=arm/t-bare tmake_file=arm/t-arm-aout
;; ;;
arm*-*-ecos-elf) arm*-*-ecos-elf)
tm_file=arm/ecos-elf.h tm_file=arm/ecos-elf.h
tmake_file=arm/t-elf tmake_file=arm/t-arm-elf
;; ;;
arm*-*-elf) arm*-*-elf)
tm_file=arm/unknown-elf.h tm_file=arm/unknown-elf.h
...@@ -834,7 +834,7 @@ changequote([,])dnl ...@@ -834,7 +834,7 @@ changequote([,])dnl
arm-*-pe*) arm-*-pe*)
tm_file=arm/pe.h tm_file=arm/pe.h
tmake_file=arm/t-pe tmake_file=arm/t-pe
extra_objs=pe.o extra_objs="pe.o"
;; ;;
avr-*-*) avr-*-*)
;; ;;
...@@ -3126,6 +3126,12 @@ changequote([,])dnl ...@@ -3126,6 +3126,12 @@ changequote([,])dnl
then then
tmake_file=rs6000/t-xnewas tmake_file=rs6000/t-xnewas
else else
tmake_file=rs6000/t-newas
fi
if test "$gnu_ld" = yes
then
xmake_file=rs6000/x-aix41-gld
else
tmake_file="rs6000/t-newas rs6000/t-aix41" tmake_file="rs6000/t-newas rs6000/t-aix41"
fi fi
xmake_file=rs6000/x-aix41 xmake_file=rs6000/x-aix41
...@@ -3450,55 +3456,12 @@ changequote([,])dnl ...@@ -3450,55 +3456,12 @@ changequote([,])dnl
# ;; # ;;
# tahoe-*-bsd*) # tahoe running BSD # tahoe-*-bsd*) # tahoe running BSD
# ;; # ;;
thumb-*-coff* | thumbel-*-coff*)
tm_file=arm/tcoff.h thumb*-*-*)
out_file=arm/thumb.c AC_MSG_ERROR([
xm_file=arm/xm-thumb.h *** The Thumb targets have been depreciated. The equivalent
md_file=arm/thumb.md *** ARM based toolchain can now generated Thumb instructions
tmake_file=arm/t-thumb *** when the -mthumb switch is given to the compiler.])
tm_p_file=arm/thumb-protos.h
;;
thumb-*-elf* | thumbel-*-elf*)
tm_file=arm/telf.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb-elf
tm_p_file=arm/thumb-protos.h
;;
thumb-*-linux-gnu*)
tm_file=arm/linux-telf.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb-linux
tm_p_file=arm/thumb-protos.h
;;
thumb-*-uclinux*)
tm_file=arm/uclinux-telf.h
out_file=arm/thumb.c
md_file=arm/thumb.md
tmake_file=arm/t-thumb-linux
xm_file=arm/xm-thumb.h
tm_p_file=arm/thumb-protos.h
;;
thumb-wrs-vxworks)
tm_file=arm/tcoff.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb
thread_file='vxworks'
tm_p_file=arm/thumb-protos.h
;;
thumb-*-pe)
tm_file=arm/tpe.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-pe-thumb
extra_objs=pe.o
tm_p_file=arm/thumb-protos.h
;; ;;
# This hasn't been upgraded to GCC 2. # This hasn't been upgraded to GCC 2.
# tron-*-*) # tron-*-*)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment