summaryrefslogtreecommitdiff
path: root/gcc/config/sh/sh.c
diff options
context:
space:
mode:
authoraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>2002-02-09 03:08:08 +0000
committeraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>2002-02-09 03:08:08 +0000
commit87e19636a559963421ed4b38ab15416642cd741a (patch)
treed0a27ffe9a822025fe0fa1929bfd170fabd4723e /gcc/config/sh/sh.c
parente0c8d7fd2d498dd4d3a2d09b6f97248d794d3c9d (diff)
downloadgcc-87e19636a559963421ed4b38ab15416642cd741a.tar.gz
Contribute sh64-elf.
2002-02-09 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (TARGET_CANNOT_MODIFY_JUMPS_P): Define to... (sh_cannot_modify_jumps_p): New function. 2002-02-05 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (TARGET_MS_BITFIELD_LAYOUT_P): Define to... (sh_ms_bitfield_layout_p): New function. 2002-02-04 Alexandre Oliva <aoliva@redhat.com> Zack Weinberg <zack@codesourcery.com> * config/sh/sh.h (TRAMPOLINE_ADJUST_ADDRESS): Use expand_simple_binop instead of expand_binop. 2002-02-03 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (OVERRIDE_OPTIONS) [! TARGET_SH5]: Disable use of .quad and .uaquad. * config/sh/sh.c (TARGET_ASM_UNALIGNED_DI_OP, TARGET_ASM_ALIGNED_DI_OP): Add comment pointing to the above. 2002-01-24 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (movdi_const, movdi_const_32bit, movdi_const_16bit): Make sure all CONSTs have modes. (sym2PIC): Ditto, but by adjusting all callers. * config/sh/sh.c (calc_live_regs) [TARGET_SHCOMPACT]: Set pr_live if the prologue calls the SHmedia argument decoder or register saver. 2002-01-24 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (TARGET_ASM_UNALIGNED_DI_OP): Define. (TARGET_ASM_ALIGNED_DI_OP): Likewise. (sh_expand_epilogue): Don't emit USE of return target register. (prepare_move_operands): Legitimize DImode PIC addresses. (sh_media_register_for_return): Skip tr0, used to initialize the PIC register. (sh_expand_prologue): Remove explicit USE of return register. (nonpic_symbol_mentioned_p): PC is non-PIC. Don't recurse in CONST_DOUBLEs. UNSPEC_GOTPLT is PIC. * config/sh/sh.h (ASM_OUTPUT_DOUBLE_INT): Removed, obsolete. (OVERRIDE_OPTIONS): Don't disable PIC on SH5. (EXTRA_CONSTRAINT_S): Use MOVI_SHORI_BASE_OPERAND_P instead of EXTRA_CONSTRAINT_T. (GOT_ENTRY_P, GOTPLT_ENTRY_P, GOTOFF_P, PIC_ADDR_P): New. (MOVI_SHORI_BASE_OPERAND_P): New. (NON_PIC_REFERENCE_P, PIC_REFERENCE_P): New. (EXTRA_CONSTRAINT_T): Define in terms of them. (OUTPUT_ADDR_CONST_EXTRA): Handle UNSPEC_GOTPLT. * config/sh/sh.md (movsi_media, movsi_media_nofpu, movdi_media, movdi_media_nofpu): Add SIBCALL_REGS class to alternatives supporting TARGET_REGS. (UNSPEC_GOTPLT): New constant. (movdi split): Move incrementing of LABEL_NUSES... (movdi_const, movdi_const_32bit): Here. Use MOVI_SHORI_BASE_OPERAND_P instead of EXTRA_CONSTRAINT_T. (movdi_const_16bit): New. (call, call_value) [flag_pic]: Use GOTPLT. (call_pop, call_value_pop): New expands. (call_pop_compact, call_pop_rettramp): New insns. (call_value_pop_compact, call_value_pop_rettramp): New insns. (sibcall) [flag_pic]: Use GOT. (builtint_setjmp_receiver): Remove bogus, unused expand. (GOTaddr2picreg): Implement for SHcompact and SHmedia. (*pt, *ptb, ptrel): New insns. (sym2GOT): Handle DImode GOT. (sym2GOTPLT, symGOTPLT2reg): New expands. (sym2PIC): New expand. (shcompact_return_tramp): Use GOTPLT to return trampoline. (shcompact_return_tramp_i): Use return register explicitly. * config/sh/sh.h (OVERRIDE_OPTIONS) [TARGET_SHMEDIA]: Don't disable flag_reorder_blocks. 2002-01-19 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (sibcall_compact): Reorder return, uses and clobbers, for clarity. (sibcall_epilogue) [TARGET_SHCOMPACT]: Mark saving and restoring of r0 in macl as MAYBE_DEAD. 2002-01-18 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (LONG_DOUBLE_TYPE_SIZE): Define. * config/sh/sh.md (movv4sf_i, movv16sf_i): Fix uses of alter_subreg all over. (jump) [TARGET_SHMEDIA]: FAIL to create new jumps after reload, instead of emitting instructions that would require reloading. (casesi_load_media): Add missing modes. 2001-11-09 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (sh_expand_prologue): Mark the PIC register as used if the argument decoder is called. 2001-08-28 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (udivsi3, divsi3): Load libcall symbol name in Pmode, then extend it to DImode if necessary. 2001-08-28 Stephen Clarke <Stephen.Clarke@st.com> * config/sh/sh.h (LEGITIMATE_CONSTANT_P): Don't accept DFmode constants in FPU-enabled SHmedia, let them be loaded from memory. 2001-08-28 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (cmpeqdi_media, cmpgtdi_media, cmpgtudi_media): Adjust whitespace in assembly output templates. 2001-08-28 Stephen Clarke <Stephen.Clarke@st.com> * config/sh/sh.md (movdicc_false, movdicc_true, movdicc): Adjust mode of if_then_else. 2001-08-04 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh64.h (CPP_DEFAULT_CPU_SPEC): Override definition in sh.h. 2001-07-26 Andrew Haley <aph@cambridge.redhat.com> Joern Rennecke <amylaar@redhat.com> * config/sh/sh64.h (CPP_DEFAULT_CPU_SPEC): New. (SUBTARGET_CPP_PTR_SPEC): New. (SUBTARGET_CPP_SPEC): Remove. 2001-07-06 Chandrakala Chavva <cchavva@redhat.com> * config/sh/sh.md (movsf_media_nofpu+1, movdf_media_nofpu+1): Fix typo in previous checkin. 2001-07-11 Chandrakala Chavva <cchavva@redhat.com> * config/sh/sh.h (MODES_TIEABLE_P): Fix redact indentations. 2001-07-10 Chandrakala Chavva <cchavva@cygnus.com> Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (MODES_TIEABLE_P): Don't tie modes wider than what single FP register can hold for SHmedia target. 2001-07-06 Chandrakala Chavva <cchavva@redhat.com> Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (movsf_media_nofpu+1, movdf_media_nofpu+1): Do not split into SUBREG. 2001-06-14 Alexandre Oliva <aoliva@redhat.com> * config/sh/ushmedia.h, config/sh/sshmedia.h: Updated signatures and added new functions as specified in SH5 ABI r9. 2001-06-04 Alexandre Oliva <aoliva@redhat.com> * config/sh/lib1funcs.asm (GCC_nested_trampoline): Align to an 8-byte boundary. 2001-06-03 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (dump_table): Add const0_rtx in calls of gen_consttable_4 and gen_consttable_8. Emit multiple labels and consttable_window_ends. 2001-06-03 Graham Stott <grahams@redhat,com> * config/sh/sh.md (movdi split): Remove unused variable last_insn. 2001-05-16 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (print_operand): Handle floating-point pair, vector and matrix registers. * config/sh/sh.h (REGISTER_MOVE_COST): Take floating-pointer vector modes into account. * config/sh/sh.md (movv2sf): Split move between registers into movdf. (movv4sf, movv16sf): Introduce insns that get split only after reload. * config/sh/shmedia.h: Fix Copyright dates. * config/sh/ushmedia.h: Likewise. Move loop counter declarations into conditionals that uses them. (sh_media_FVADD_S, sh_media_FVSUB_S): Fix off-by-one error in loop boundary. * config/sh/sshmedia.h: Fix Copyright dates. (sh_media_PUTCFG): Fix constraints. 2001-05-12 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (TARGET_PTRMEMFUNC_VBIT_LOCATION): Define to ptrmemfunc_vbit_in_delta for SH5. 2001-05-08 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (TARGET_SWITCHES): Document -m5-*. * invoke.texi: Likewise. 2001-04-14 Alexandre Oliva <aoliva@redhat.com> * config/sh/lib1funcs.asm (GCC_push_shmedia_regs, GCC_push_shmedia_regs_nofpu, GCC_pop_shmedia_regs, GCC_pop_shmedia_regs_nofpu): New global symbols. * config/sh/t-sh64 (LIB1ASMFUNCS): Add them. * config/sh/sh.h (SHMEDIA_REGS_STACK_ADJUST): New macro. * config/sh/sh.c (calc_live_regs): Account for PR's saving in compact function with nonlocal labels. (sh_expand_prologue) [SHcompact]: Push SHmedia regs if needed. (sh_expand_epilogue) [SHcompact]: Pop them when appropriate. (initial_elimination_offset): Account for their stack space. * config/sh/sh.md (shmedia_save_restore_regs_compact): New insn. * config/sh/sh.md (movsi_media, movsi_media_nofpu, movqi_media, movhi_media, movdi_media, movdi_media_nofpu, movdf_media, movdf_media_nofpu, movsf_media, movsf_media_nofpu): Require at least one of the operands to be a register. (movv2sf): Likewise. Renamed to movv2sf_i. (movdi, movdf, movv2sf, movv4sf, movv16sf, movsf): prepare_move_operands() before emitting SHmedia insns. 2001-04-03 Alexandre Oliva <aoliva@redhat.com> * config/sh/crti.asm (init, fini) [__SH5__ && ! __SHMEDIA__]: Don't save nor initialize r12. Don't mis-align the stack. Pad the code with a nop. * config/sh/crti.asm: Don't restore r12. Don't mis-align the stack. 2001-03-13 Alexandre Oliva <aoliva@redhat.com> * gcc/longlong.h (__umulsidi3, count_leading_zeros) [__SHMEDIA__]: Implement. 2001-03-11 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md: Set latency of `pt' closer to reality. (movsi_media, movsi_media_nofpu, movdi_media, movdi_media_nofpu, movdf_media, movdf_media_nofpu, movsf_media, movsf_media_nofpu): Set move, load and store type attributes. * config/sh/sh.c (sh_loop_align) [TARGET_SH5]: Set to 3. * config/sh/sh.h (OVERRIDE_OPTIONS) [TARGET_SH5]: Disable profiling. * config/sh/sh.h (PROMOTE_MODE): Sign-extend SImode to DImode. * config/sh/sh-protos.h (sh_media_register_for_return): Declare. * config/sh/sh.c (sh_media_register_for_return): New function. (sh_expand_prologue) [TARGET_SHMEDIA]: Copy r18 to an available branch-target register. (sh_expand_epilogue) [TARGET_SHMEDIA]: Explicitly USE it. * config/sh/sh.md (return_media_i): Use any call-clobbered branch-target register. (return_media): If r18 wasn't copied in the prologue, copy it here. * config/sh/sh.h (CONDITIONAL_REGISTER_USAGE) [TARGET_SHMEDIA]: Clear class FP0_REGS. * config/sh/sh64.h (LINK_SPEC): Removed incorrect default copied from elf.h. 2001-03-08 DJ Delorie <dj@redhat.com> * config/sh/sh.h (OVERRIDE_OPTIONS): Disable relaxing for SHMEDIA. 2001-02-09 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (sibcall_compact): Set fp_mode to single. 2001-02-07 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (INT_ASM_OP) [SHMEDIA64]: Use `.quad'. 2001-02-03 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (INIT_CUMULATIVE_ARGS): Compute size of BLKmode return value correctly for call_cookie. 2001-02-01 Alexandre Oliva <aoliva@redhat.com> * config/sh/crt1.asm (start): Modified so as to call ___setup_argv_and_call_main. 2001-01-26 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (FUNCTION_ARG_ADVANCE): Don't count stack_regs in SHmedia mode. 2001-01-20 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (STRIP_DATALABEL_ENCODING): New macro. (STRIP_NAME_ENCODING): Use it. (ASM_OUTPUT_LABELREF): Likewise. Don't call assemble_name(). 2001-01-19 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (sgeu) [! SHMEDIA]: Fix invocation of prepare_scc_operands(). * config/sh/sh.h (SH_DATALABEL_ENCODING): Change to "#"... (DATALABEL_SYMNAME_P): ... so that we don't need memcmp here. 2001-01-17 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (STRIP_NAME_ENCODING): Strip leading `*'. 2001-01-13 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (shcompact_incoming_args): Use R0_REG. * config/sh/sh.md (R7_REG, R8_REG, R9_REG): Define as constants, used in shcompact_incoming_args. * config/sh/sh.c (sh_expand_epilogue): Fix thinko in previous change. * config/sh/crt1.asm (start) [SH5]: Switch to single-precision mode. * config/sh/lib1funcs.asm (sdivsi3_i4, udivsi3_i4, set_fpscr): Adjust accordingly. * config/sh/sh.c (sh_expand_prologue, sh_expand_epilogue): Simplify. Adjust. Add sanity check. * config/sh/sh.h (TARGET_SWITCHES) [5-compact]: Set FPU_SINGLE_BIT. * config/sh/sh.md (udivsi3_i4_single, divsi3_i4_single): Match TARGET_SHCOMPACT. (udivsi3, divsi3): Use them. (force_mode_for_call): New insn. (call, call_value, sibcall_value): Emit it before SHcompact calls. 2001-01-11 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (call, call_value, sibcall): Make sure the call cookie is non-NULL before taking its value. 2001-01-10 Alexandre Oliva <aoliva@redhat.com> * config.gcc (sh64): Set target_requires_64bit_host_wide_int. 2001-01-09 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (shcompact_incoming_args): Set argument memory block. * config/sh/sh.h (STATIC_CHAIN_REGNUM) [SH5]: Use r1. * config/sh/sh.c (sh_expand_prologue) [SH5]: Use r0 as temporary for stack adjusts. Use MACL and MACH to pass arguments to shcompact_incoming_args. * config/sh/sh.md (shcompact_incoming_args): Adjust. Don't clobber r1. * config/sh/lib1funcs.asm (shcompact_incoming_args): Likewise. (nested_trampoline): Load static chain address into r1. * config/sh/sh.md (movdi_media splits): Fix sign-extension. 2001-01-07 Alexandre Oliva <aoliva@redhat.com * config/sh/sh.c (fpul_operand) [SHMEDIA]: Just call fp_arith_reg_operand(). 2001-01-06 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (casesi): Sign-extend the first two operands, and use signed compares for them. * config/sh/sh.c (dump_table): Don't emit 8-byte constants after 4-byte ones. Instead, inter-leave them, maintaining the 8-byte ones properly aligned. (find_barrier): Account for extra alignment needed for 8-byte wide constants. (machine_dependent_reorg): Require a label for the second 4-byte constant after an 8-byte one. * config/sh/lib1funcs.asm (sdivsi3): Fix typo in yesterday's change. 2001-01-05 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (machine_dependent_reorg) [SHCOMPACT]: Reset last_float when switching float modes. * config/sh/sh.md (movdf) [SH5]: Don't use stack-pointer auto-increment for general-purpose registers. * config/sh/lib1funcs.asm (sdivsi3) [SHMEDIA]: Sign-extend the result. * config/sh/sh.c (sh_expand_prologue) [SH5]: Use r1 as temporary for stack adjust. * config/sh/sh.c (sh_builtin_saveregs): Support using all registers for varargs. 2001-01-01 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (FUNCTION_ARG_ADVANCE): Simplify. * config/sh/sh.h (CALL_COOKIE_STACKSEQ, CALL_COOKIE_STACKSEQ_SHIFT, CALL_COOKIE_STACKSEQ_GET): New macros. (CALL_COOKIE_INT_REG_SHIFT): Adjust. (FUNCTION_ARG_ADVANCE): Use SHCOMPACT_FORCE_ON_STACK. Adjust call_cookie accordingly. (FUNCTION_ARG): Test SHCOMPACT_FORCE_ON_STACK. (SHCOMPACT_BYREF): Likewise. (SHCOMPACT_FORCE_ON_STACK): New macro. * config/sh/sh.c (sh_expand_prologue): Use new call_cookie format. (sh_builtin_saveregs): Likewise. * config/sh/lib1funcs.asm (shcompact_call_trampoline, shcompact_incoming_args): Use new shift values. Support sequences of consecutive and non-consecutive pushes/pops. * config/sh/sh.md (return): Don't explicitly use PR_REG. 2001-01-05 Hans-Peter Nilsson <hpn@cygnus.com> * config/sh/sh.h (TEXT_SECTION): Define. * config/sh/elf.h (ASM_FILE_START): Output TEXT_SECTION_ASM_OP. 2001-01-05 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (INIT_CUMULATIVE_LIBCALL_ARGS): New macro. * config/sh/sh.h (BASE_RETURN_VALUE_REG): Use FP regs for return values on FPU-enabled SHmedia. (FUNCTION_VALUE_REGNO_P): Mark FIRST_FP_RET_REG as used on FPU-enabled SHmedia. (INIT_CUMULATIVE_ARGS): Set up return trampoline only if value is returned in a non-FP reg and is not returned by reference. * config/sh/sh.md (shcompact_return_tramp_i): Change type to jump_ind. 2000-01-04 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (SH_MIN_ALIGN_FOR_CALLEE_COPY): New. (FUNCTION_ARG_CALLEE_COPIES): Require argument to be quad-aligned to be passed by callee-copy reference. 2001-01-03 Alexandre Oliva <aoliva@redhat.com> * config/sh/elf.h (MAX_WCHAR_TYPE_SIZE): Define. * config/sh/sh64.h (MAX_WCHAR_TYPE_SIZE): Undefine. 2001-01-02 Alexandre Oliva <aoliva@redhat.com> * config/sh/lib1funcs.asm (shcompact_call_trampoline): Fix error in copying low-numbered FP regs to r7 and r8. * config/sh/sh.h (FUNCTION_ARG_ADVANCE): Don't request copying of FP regs to general-purpose regs only if the copy was passed on the stack. * config/sh/lib1funcs.asm (shcompact_call_trampoline): Fix typo in copying FP reg to r9. * config/sh/sh.h (FUNCTION_ARG_ADVANCE): Use trampoline to copy FP regs to general-purpose regs only in outgoing calls. * config/sh/sh.md (movdf_media, movsf_media): Revert incorrect change from 2000-10-30. Adjust for 64-bit (or 32-bit) HOST_WIDE_INT. * config/sh/sh.h (struct sh_args): Document all fields. (FUNCTION_OK_FOR_SIBCALL): Functions that receive arguments passed partially on the stack should not consider making sibcalls. * config/sh/sh.h (FUNCTION_ARG_ADVANCE): Add byref regs to stack_regs only for incoming calls. When passing FP args, make sure there are FP regs available before modifying call_cookie. (SHCOMPACT_BYREF): Pass double args in general-purpose registers by reference. 2000-12-30 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (FUNCTION_OK_FOR_SIBCALL) [SHCOMPACT]: Don't attempt to generate sibcalls if the caller got any arguments by reference. * config/sh/lib1funcs.asm (set_fpscr) [SH5]: Default to double. * config/sh/sh.c (dump_table) [SHCOMPACT]: Align DImode and DFmode to 8-byte boundaries. * config/sh/sh.md (shcompact_preserve_incoming_args): New insn. * config/sh/sh.h (CALL_COOKIE_INT_REG_GET): New macro. * config/sh/sh.c (sh_expand_prologue): Preserve args that will be stored in the stack. * config/sh/lib1funcs.asm (ct_main_table, ia_main_table): Arrange for the offsets to have the ISA bit set. (shcompact_call_trampoline): Document. Swap r0 and r1, to match invocation. Use beq instead of bgt to mark end of sequence of loads. (shcompact_incoming_args): Fix store of r2. Use beq instead of bgt to mark end of sequence of stores. * config/sh/sh.c (arith_operand): Don't check whether CONST_OK_FOR_J for now. * config/sh/sh.md (movdf_media, movsf_media): Use HOST_WIDE_INT instead of long for conversion. 2000-12-29 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.c (print_operand_address): Convert INTVAL to int before passing it to fprintf. 2000-12-28 Alexandre Oliva <aoliva@redhat.com> * config/sh/crt1.asm (start): Reset SR.FD, to enable the FP unit. Call set_fpscr before reading/writing SR. * config/sh/crt1.asm (start): Set SR.SZ and SR.PR, but not SR.FR. Call set_fpscr. * config/sh/lib1funcs.asm: Add `.align 2' directives before SHmedia code. (FMOVD_WORKS): Define on SH5 with FPU. (set_fpscr): Define on SH5. Remove separate _fpscr_values setting. * config/sh/t-sh64 (LIB1ASMFUNCS): Add _set_fpscr instead of _fpscr_values. 2000-12-28 Hans-Peter Nilsson <hpn@cygnus.com> * config/sh/lib1funcs.asm (ct_main_table): Align contents to even address. (ia_main_table): Ditto. 2000-12-27 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.h (MAX_WCHAR_TYPE_SIZE): Don't define. * config/sh/sh64.h (WCHAR_TYPE, WCHAR_TYPE_SIZE): Reinstate the definitions from sh.h. * config/sh/sh.h (PTRDIFF_TYPE): Define as conditional on TARGET_SH5. (SUBTARGET_CPP_SPEC): Arrange for __PTRDIFF_TYPE__ to be defined. * config/sh/elf.h (PTRDIFF_TYPE): Likewise. * config/sh/sh64.h (SUBTARGET_CPP_SPEC): Likewise. 2000-12-26 Alexandre Oliva <aoliva@redhat.com> * config/sh/sh.md (movdi_media split): Don't add REG_LABEL notes. Increment LABEL_NUSES. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@49630 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r--gcc/config/sh/sh.c1191
1 files changed, 1095 insertions, 96 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index f83a1e13f76..c408caaabe1 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -47,6 +47,13 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
+/* These are some macros to abstract register modes. */
+#define CONST_OK_FOR_ADD(size) \
+ (TARGET_SHMEDIA ? CONST_OK_FOR_P (size) : CONST_OK_FOR_I (size))
+#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
+#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
+#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
+
/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
int current_function_interrupt;
@@ -100,31 +107,56 @@ int regno_reg_class[FIRST_PSEUDO_REGISTER] =
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, PR_REGS, T_REGS, NO_REGS,
- MAC_REGS, MAC_REGS, FPUL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
DF_REGS, DF_REGS, DF_REGS, DF_REGS,
DF_REGS, DF_REGS, DF_REGS, DF_REGS,
- FPSCR_REGS,
+ NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
+ MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
+ GENERAL_REGS,
};
-char fp_reg_names[][5] =
-{
- "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",
- "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
- "fpul",
- "xd0","xd2","xd4", "xd6", "xd8", "xd10", "xd12", "xd14",
-};
+char sh_register_names[FIRST_PSEUDO_REGISTER] \
+ [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
+
+char sh_additional_register_names[ADDREGNAMES_SIZE] \
+ [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
+ = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
/* Provide reg_class from a letter such as appears in the machine
description. */
const enum reg_class reg_class_from_letter[] =
{
- /* a */ ALL_REGS, /* b */ NO_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,
+ /* a */ ALL_REGS, /* b */ TARGET_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,
/* e */ NO_REGS, /* f */ FP_REGS, /* g */ NO_REGS, /* h */ NO_REGS,
/* i */ NO_REGS, /* j */ NO_REGS, /* k */ SIBCALL_REGS, /* l */ PR_REGS,
/* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS,
@@ -150,8 +182,8 @@ static rtx gen_block_redirect PARAMS ((rtx, int, int));
static void output_stack_adjust PARAMS ((int, rtx, int));
static void push PARAMS ((int));
static void pop PARAMS ((int));
-static void push_regs PARAMS ((int, int));
-static int calc_live_regs PARAMS ((int *, int *));
+static void push_regs PARAMS ((HOST_WIDE_INT *));
+static void calc_live_regs PARAMS ((int *, HOST_WIDE_INT *));
static void mark_use PARAMS ((rtx, rtx *));
static HOST_WIDE_INT rounded_frame_size PARAMS ((int));
static rtx mark_constant_pool_use PARAMS ((rtx));
@@ -165,6 +197,9 @@ static void sh_insert_attributes PARAMS ((tree, tree *));
static void sh_asm_named_section PARAMS ((const char *, unsigned int));
#endif
static int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int));
+static bool sh_cannot_modify_jumps_p PARAMS ((void));
+
+static bool sh_ms_bitfield_layout_p PARAMS ((tree));
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
@@ -176,6 +211,12 @@ static int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int));
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
+/* These are NULLed out on non-SH5 in OVERRIDE_OPTIONS. */
+#undef TARGET_ASM_UNALIGNED_DI_OP
+#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
@@ -185,6 +226,12 @@ static int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int));
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
+#undef TARGET_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
+
+#undef TARGET_MS_BITFIELD_LAYOUT_P
+#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Print the operand address in x to the stream. */
@@ -257,6 +304,9 @@ print_operand_address (stream, x)
'R' print the LSW of a dp value - changes if in little endian
'S' print the MSW of a dp value - changes if in little endian
'T' print the next word of a dp value - same as 'R' in big endian mode.
+ 'M' print an `x' if `m' will print `base,index'.
+ 'm' print a pair `base,offset' or `base,index', for LD and ST.
+ 'u' prints the lowest 16 bits of CONST_INT, as an unsigned value.
'o' output an operator. */
void
@@ -338,21 +388,120 @@ print_operand (stream, x, code)
break;
}
break;
+ case 'M':
+ if (GET_CODE (x) == MEM
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && (GET_CODE (XEXP (XEXP (x, 0), 1)) == REG
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
+ fputc ('x', stream);
+ break;
+
+ case 'm':
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ print_operand (stream, x, 0);
+ fputs (", 0", stream);
+ break;
+
+ case PLUS:
+ print_operand (stream, XEXP (x, 0), 0);
+ fputs (", ", stream);
+ print_operand (stream, XEXP (x, 1), 0);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case 'u':
+ if (GET_CODE (x) == CONST_INT)
+ {
+ fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
+ break;
+ }
+ /* Fall through. */
+
default:
switch (GET_CODE (x))
{
+ /* FIXME: We need this on SHmedia32 because reload generates
+ some sign-extended HI or QI loads into DImode registers
+ but, because Pmode is SImode, the address ends up with a
+ subreg:SI of the DImode register. Maybe reload should be
+ fixed so as to apply alter_subreg to such loads? */
+ case SUBREG:
+ if (SUBREG_BYTE (x) != 0
+ || GET_CODE (SUBREG_REG (x)) != REG)
+ abort ();
+
+ x = SUBREG_REG (x);
+ /* Fall through. */
+
case REG:
if (FP_REGISTER_P (REGNO (x))
- && GET_MODE_SIZE (GET_MODE (x)) > 4)
- fprintf ((stream), "d%s", reg_names[REGNO (x)]+1);
+ && GET_MODE (x) == V16SFmode)
+ fprintf ((stream), "mtrx%s", reg_names[REGNO (x)] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && GET_MODE (x) == V4SFmode)
+ fprintf ((stream), "fv%s", reg_names[REGNO (x)] + 2);
+ else if (GET_CODE (x) == REG
+ && GET_MODE (x) == V2SFmode)
+ fprintf ((stream), "fp%s", reg_names[REGNO (x)] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && GET_MODE_SIZE (GET_MODE (x)) > 4)
+ fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
else
fputs (reg_names[REGNO (x)], (stream));
break;
+
case MEM:
output_address (XEXP (x, 0));
break;
+
+ case CONST:
+ if (TARGET_SHMEDIA
+ && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+ && GET_MODE (XEXP (x, 0)) == DImode
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == TRUNCATE
+ && GET_MODE (XEXP (XEXP (x, 0), 0)) == HImode)
+ {
+ rtx val = XEXP (XEXP (XEXP (x, 0), 0), 0);
+
+ fputc ('(', stream);
+ if (GET_CODE (val) == ASHIFTRT)
+ {
+ fputc ('(', stream);
+ if (GET_CODE (XEXP (val, 0)) == CONST)
+ fputc ('(', stream);
+ output_addr_const (stream, XEXP (val, 0));
+ if (GET_CODE (XEXP (val, 0)) == CONST)
+ fputc (')', stream);
+ fputs (" >> ", stream);
+ output_addr_const (stream, XEXP (val, 1));
+ fputc (')', stream);
+ }
+ else
+ {
+ if (GET_CODE (val) == CONST)
+ fputc ('(', stream);
+ output_addr_const (stream, val);
+ if (GET_CODE (val) == CONST)
+ fputc (')', stream);
+ }
+ fputs (" & 65535)", stream);
+ break;
+ }
+
+ /* Fall through. */
default:
- fputc ('#', stream);
+ if (TARGET_SH1)
+ fputc ('#', stream);
output_addr_const (stream, x);
break;
}
@@ -498,17 +647,20 @@ prepare_move_operands (operands, mode)
rtx operands[];
enum machine_mode mode;
{
- if (mode == SImode && flag_pic)
+ if ((mode == SImode || mode == DImode) && flag_pic)
{
rtx temp;
if (SYMBOLIC_CONST_P (operands[1]))
{
if (GET_CODE (operands[0]) == MEM)
operands[1] = force_reg (Pmode, operands[1]);
+ else if (GET_CODE (operands[1]) == LABEL_REF
+ && target_reg_operand (operands[0], mode))
+ /* It's ok. */;
else
{
temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
- operands[1] = legitimize_pic_address (operands[1], SImode, temp);
+ operands[1] = legitimize_pic_address (operands[1], mode, temp);
}
}
else if (GET_CODE (operands[1]) == CONST
@@ -517,8 +669,8 @@ prepare_move_operands (operands, mode)
{
temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
- SImode, temp);
- operands[1] = expand_binop (SImode, add_optab, temp,
+ mode, temp);
+ operands[1] = expand_binop (mode, add_optab, temp,
XEXP (XEXP (operands[1], 0), 1),
no_new_pseudos ? temp
: gen_reg_rtx (Pmode),
@@ -802,9 +954,19 @@ output_far_jump (insn, op)
print_slot (final_sequence);
this.reg = gen_rtx_REG (SImode, 13);
- output_asm_insn ("mov.l r13,@-r15", 0);
+ /* We must keep the stack aligned to 8-byte boundaries on SH5.
+ Fortunately, MACL is fixed and call-clobbered, and we never
+ need its value across jumps, so save r13 in it instead of in
+ the stack. */
+ if (TARGET_SH5)
+ output_asm_insn ("lds r13, macl", 0);
+ else
+ output_asm_insn ("mov.l r13,@-r15", 0);
output_asm_insn (jump, &this.lab);
- output_asm_insn ("mov.l @r15+,r13", 0);
+ if (TARGET_SH5)
+ output_asm_insn ("sts macl, r13", 0);
+ else
+ output_asm_insn ("mov.l @r15+,r13", 0);
}
if (far && flag_pic && TARGET_SH2)
{
@@ -954,6 +1116,12 @@ output_file_start (file)
if (TARGET_LITTLE_ENDIAN)
fprintf (file, "\t.little\n");
+
+ if (TARGET_SHCOMPACT)
+ fprintf (file, "\t.mode\tSHcompact\n");
+ else if (TARGET_SHMEDIA)
+ fprintf (file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
+ TARGET_SHMEDIA64 ? 64 : 32);
}
/* Actual number of instructions used to make a shift by N. */
@@ -1031,6 +1199,9 @@ shiftcosts (x)
{
int value;
+ if (TARGET_SHMEDIA)
+ return 1;
+
if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
{
if (GET_MODE (x) == DImode
@@ -1073,6 +1244,17 @@ andcosts (x)
return 1;
i = INTVAL (XEXP (x, 1));
+
+ if (TARGET_SHMEDIA)
+ {
+ if ((GET_CODE (XEXP (x, 1)) == CONST_INT
+ && CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
+ || EXTRA_CONSTRAINT_S (XEXP (x, 1)))
+ return 1;
+ else
+ return 2;
+ }
+
/* These constants are single cycle extu.[bw] instructions. */
if (i == 0xff || i == 0xffff)
return 1;
@@ -1102,9 +1284,30 @@ addsubcosts (x)
/* Likewise for small constants. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
- && CONST_OK_FOR_I (INTVAL (XEXP (x, 1))))
+ && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
return 1;
+ if (TARGET_SHMEDIA)
+ switch (GET_CODE (XEXP (x, 1)))
+ {
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ return TARGET_SHMEDIA64 ? 5 : 3;
+
+ case CONST_INT:
+ if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
+ return 2;
+ else if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1)) >> 16))
+ return 3;
+ else if (CONST_OK_FOR_J ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
+ return 4;
+
+ /* Fall through. */
+ default:
+ return 5;
+ }
+
/* Any other constant requires a 2 cycle pc-relative load plus an
addition. */
return 3;
@@ -1115,6 +1318,9 @@ int
multcosts (x)
rtx x ATTRIBUTE_UNUSED;
{
+ if (TARGET_SHMEDIA)
+ return 3;
+
if (TARGET_SH2)
{
/* We have a mul insn, so we can never take more than the mul and the
@@ -1916,6 +2122,27 @@ gen_shl_sext (dest, left_rtx, size_rtx, source)
}
return 0;
}
+
+/* Prefix a symbol_ref name with "datalabel". */
+
+rtx
+gen_datalabel_ref (sym)
+ rtx sym;
+{
+ if (GET_CODE (sym) == LABEL_REF)
+ return gen_rtx_CONST (GET_MODE (sym),
+ gen_rtx_UNSPEC (GET_MODE (sym),
+ gen_rtvec (1, sym),
+ UNSPEC_DATALABEL));
+
+ if (GET_CODE (sym) != SYMBOL_REF)
+ abort ();
+
+ XSTR (sym, 0) = concat (SH_DATALABEL_ENCODING, XSTR (sym, 0), NULL);
+
+ return sym;
+}
+
/* The SH cannot load a large constant into a register, constants have to
come from a pc relative load. The reference of a pc relative load
@@ -2078,6 +2305,7 @@ dump_table (scan)
int i;
int need_align = 1;
rtx lab, ref;
+ int have_di = 0;
/* Do two passes, first time dump out the HI sized constants. */
@@ -2102,10 +2330,87 @@ dump_table (scan)
scan = emit_insn_after (gen_consttable_window_end (lab), scan);
}
}
+ else if (p->mode == DImode || p->mode == DFmode)
+ have_di = 1;
}
need_align = 1;
+ if (TARGET_SHCOMPACT && have_di)
+ {
+ rtx align_insn = NULL_RTX;
+
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ need_align = 0;
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ switch (p->mode)
+ {
+ case HImode:
+ break;
+ case SImode:
+ case SFmode:
+ if (align_insn)
+ {
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ emit_label_before (lab, align_insn);
+ emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
+ align_insn);
+ for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+ {
+ lab = XEXP (ref, 0);
+ emit_insn_before (gen_consttable_window_end (lab),
+ align_insn);
+ }
+ delete_insn (align_insn);
+ align_insn = NULL_RTX;
+ continue;
+ }
+ else
+ {
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value,
+ const0_rtx), scan);
+ need_align = ! need_align;
+ }
+ break;
+ case DFmode:
+ case DImode:
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ align_insn = scan;
+ need_align = 0;
+ }
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+ {
+ lab = XEXP (ref, 0);
+ scan = emit_insn_after (gen_consttable_window_end (lab),
+ scan);
+ }
+ }
+ }
+
+ pool_size = 0;
+ }
+
for (i = 0; i < pool_size; i++)
{
pool_node *p = &pool_vector[i];
@@ -2240,6 +2545,7 @@ find_barrier (num_mova, mova, from)
int count_hi = 0;
int found_hi = 0;
int found_si = 0;
+ int found_di = 0;
int hi_align = 2;
int si_align = 2;
int leading_mova = num_mova;
@@ -2321,6 +2627,18 @@ find_barrier (num_mova, mova, from)
}
else
{
+ /* We dump DF/DI constants before SF/SI ones, because
+ the limit is the same, but the alignment requirements
+ are higher. We may waste up to 4 additional bytes
+ for alignment, and the DF/DI constant may have
+ another SF/SI constant placed before it. */
+ if (TARGET_SHCOMPACT
+ && ! found_di
+ && (mode == DFmode || mode == DImode))
+ {
+ found_di = 1;
+ si_limit -= 8;
+ }
while (si_align > 2 && found_si + si_align - 2 > count_si)
si_align >>= 1;
if (found_si > count_si)
@@ -2928,7 +3246,7 @@ barrier_align (barrier_or_label)
return ((TARGET_SMALLCODE
|| (XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
<= (unsigned)1 << (CACHE_LOG - 2)))
- ? 1 : CACHE_LOG);
+ ? 1 << TARGET_SHMEDIA : CACHE_LOG);
}
if (TARGET_SMALLCODE)
@@ -3035,6 +3353,10 @@ sh_loop_align (label)
|| GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC
|| recog_memoized (next) == CODE_FOR_consttable_2)
return 0;
+
+ if (TARGET_SH5)
+ return 3;
+
return 2;
}
@@ -3058,6 +3380,9 @@ machine_dependent_reorg (first)
if (! optimize)
split_all_insns_noflow ();
+ if (TARGET_SHMEDIA)
+ return;
+
/* If relaxing, generate pseudo-ops to associate function calls with
the symbols they call. It does no harm to not generate these
pseudo-ops. However, when we can generate them, it enables to
@@ -3340,6 +3665,7 @@ machine_dependent_reorg (first)
behind. */
rtx barrier = find_barrier (num_mova, mova, insn);
rtx last_float_move, last_float = 0, *last_float_addr;
+ int may_need_align = 1;
if (num_mova && ! mova_p (mova))
{
@@ -3397,6 +3723,27 @@ machine_dependent_reorg (first)
if (last_float
&& reg_set_between_p (r0_rtx, last_float_move, scan))
last_float = 0;
+ if (TARGET_SHCOMPACT)
+ {
+ /* The first SFmode constant after a DFmode
+ constant may be pulled before a sequence
+ of DFmode constants, so the second SFmode
+ needs a label, just in case. */
+ if (GET_MODE_SIZE (mode) == 4)
+ {
+ if (last_float && may_need_align)
+ last_float = 0;
+ may_need_align = 0;
+ }
+ if (last_float
+ && (GET_MODE_SIZE (GET_MODE (last_float))
+ != GET_MODE_SIZE (mode)))
+ {
+ last_float = 0;
+ if (GET_MODE_SIZE (mode) == 4)
+ may_need_align = 1;
+ }
+ }
lab = add_constant (src, mode, last_float);
if (lab)
emit_insn_before (gen_mova (lab), scan);
@@ -3842,16 +4189,21 @@ output_stack_adjust (size, reg, temp)
{
if (size)
{
- if (CONST_OK_FOR_I (size))
- emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
+ HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+ if (size % align)
+ abort ();
+
+ if (CONST_OK_FOR_ADD (size))
+ emit_insn (GEN_ADD3 (reg, reg, GEN_INT (size)));
/* Try to do it with two partial adjustments; however, we must make
sure that the stack is properly aligned at all times, in case
an interrupt occurs between the two partial adjustments. */
- else if (CONST_OK_FOR_I (size / 2 & -4)
- && CONST_OK_FOR_I (size - (size / 2 & -4)))
+ else if (CONST_OK_FOR_ADD (size / 2 & -align)
+ && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
{
- emit_insn (gen_addsi3 (reg, reg, GEN_INT (size / 2 & -4)));
- emit_insn (gen_addsi3 (reg, reg, GEN_INT (size - (size / 2 & -4))));
+ emit_insn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
+ emit_insn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
}
else
{
@@ -3862,20 +4214,20 @@ output_stack_adjust (size, reg, temp)
to handle this case, so just abort when we see it. */
if (temp < 0)
abort ();
- const_reg = gen_rtx_REG (SImode, temp);
+ const_reg = gen_rtx_REG (GET_MODE (reg), temp);
/* If SIZE is negative, subtract the positive value.
This sometimes allows a constant pool entry to be shared
between prologue and epilogue code. */
if (size < 0)
{
- emit_insn (gen_movsi (const_reg, GEN_INT (-size)));
- emit_insn (gen_subsi3 (reg, reg, const_reg));
+ emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
+ emit_insn (GEN_SUB3 (reg, reg, const_reg));
}
else
{
- emit_insn (gen_movsi (const_reg, GEN_INT (size)));
- emit_insn (gen_addsi3 (reg, reg, const_reg));
+ emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
+ emit_insn (GEN_ADD3 (reg, reg, const_reg));
}
}
}
@@ -3938,21 +4290,18 @@ pop (rn)
/* Generate code to push the regs specified in the mask. */
static void
-push_regs (mask, mask2)
- int mask, mask2;
+push_regs (mask)
+ HOST_WIDE_INT *mask;
{
int i;
/* Push PR last; this gives better latencies after the prologue, and
candidates for the return delay slot when there are no general
registers pushed. */
- for (i = 0; i < 32; i++)
- if (mask & (1 << i) && i != PR_REG)
- push (i);
- for (i = 32; i < FIRST_PSEUDO_REGISTER; i++)
- if (mask2 & (1 << (i - 32)))
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (i != PR_REG && mask[i / 32] & (1 << (i % 32)))
push (i);
- if (mask & (1 << PR_REG))
+ if (mask[PR_REG / 32] & (1 << (PR_REG % 32)))
push (PR_REG);
}
@@ -3963,13 +4312,12 @@ push_regs (mask, mask2)
function, and if we call another function (we can tell by looking at PR),
make sure that all the regs it clobbers are safe too. */
-static int
-calc_live_regs (count_ptr, live_regs_mask2)
+static void
+calc_live_regs (count_ptr, live_regs_mask)
int *count_ptr;
- int *live_regs_mask2;
+ HOST_WIDE_INT *live_regs_mask;
{
int reg;
- int live_regs_mask = 0;
int count;
int interrupt_handler;
rtx pr_initial;
@@ -3983,7 +4331,8 @@ calc_live_regs (count_ptr, live_regs_mask2)
else
interrupt_handler = 0;
- *live_regs_mask2 = 0;
+ for (count = 0; 32 * count < FIRST_PSEUDO_REGISTER; count++)
+ live_regs_mask[count] = 0;
/* If we can save a lot of saves by switching to double mode, do that. */
if (TARGET_SH4 && TARGET_FMOVD && TARGET_FPU_SINGLE)
for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
@@ -3998,6 +4347,13 @@ calc_live_regs (count_ptr, live_regs_mask2)
pr_live = (pr_initial
? REGNO (pr_initial) != PR_REG
: regs_ever_live[PR_REG]);
+ /* Force PR to be live if the prologue has to call the SHmedia
+ argument decoder or register saver. */
+ if (TARGET_SHCOMPACT
+ && ((current_function_args_info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || current_function_has_nonlocal_label))
+ pr_live = 1;
for (count = 0, reg = FIRST_PSEUDO_REGISTER - 1; reg >= 0; reg--)
{
if (reg == PR_REG
@@ -4014,36 +4370,30 @@ calc_live_regs (count_ptr, live_regs_mask2)
: (/* Only push those regs which are used and need to be saved. */
regs_ever_live[reg] && ! call_used_regs[reg]))
{
- if (reg >= 32)
- *live_regs_mask2 |= 1 << (reg - 32);
- else
- live_regs_mask |= 1 << reg;
- count++;
- if (TARGET_SH4 && TARGET_FMOVD && FP_OR_XD_REGISTER_P (reg))
+ live_regs_mask[reg / 32] |= 1 << (reg % 32);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+
+ if ((TARGET_SH4 || TARGET_SH5) && TARGET_FMOVD
+ && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
{
if (FP_REGISTER_P (reg))
{
if (! TARGET_FPU_SINGLE && ! regs_ever_live[reg ^ 1])
{
- if (reg >= 32)
- *live_regs_mask2 |= 1 << ((reg ^ 1) - 32);
- else
- live_regs_mask |= 1 << (reg ^ 1);
- count++;
+ live_regs_mask[(reg ^ 1) / 32] |= 1 << ((reg ^ 1) % 32);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
}
}
- else /* if (XD_REGISTER_P (reg)) */
+ else if (XD_REGISTER_P (reg))
{
/* Must switch to double mode to access these registers. */
target_flags &= ~FPU_SINGLE_BIT;
- count++;
}
}
}
}
- *count_ptr = count * UNITS_PER_WORD;
- return live_regs_mask;
+ *count_ptr = count;
}
/* Code to generate prologue and epilogue sequences */
@@ -4061,12 +4411,33 @@ rounded_frame_size (pushed)
return ((size + pushed + align - 1) & -align) - pushed;
}
+/* Choose a call-clobbered target-branch register that remains
+ unchanged along the whole function. We set it up as the return
+ value in the prologue. */
+int
+sh_media_register_for_return ()
+{
+ int regno;
+ int tr0_used;
+
+ if (! current_function_is_leaf)
+ return -1;
+
+ tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+
+ for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
+ if (call_used_regs[regno] && ! regs_ever_live[regno])
+ return regno;
+
+ return -1;
+}
+
void
sh_expand_prologue ()
{
- int live_regs_mask;
+ HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
int d, i;
- int live_regs_mask2;
+ int d_rounding = 0;
int save_flags = target_flags;
current_function_interrupt
@@ -4076,11 +4447,55 @@ sh_expand_prologue ()
/* We have pretend args if we had an object sent partially in registers
and partially on the stack, e.g. a large structure. */
- output_stack_adjust (-current_function_pretend_args_size,
- stack_pointer_rtx, 1);
+ output_stack_adjust (-current_function_pretend_args_size
+ - current_function_args_info.stack_regs * 8,
+ stack_pointer_rtx, TARGET_SH5 ? 0 : 1);
extra_push = 0;
+ if (TARGET_SHCOMPACT
+ && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
+ {
+ int reg;
+
+ /* First, make all registers with incoming arguments that will
+ be pushed onto the stack live, so that register renaming
+ doesn't overwrite them. */
+ for (reg = 0; reg < NPARM_REGS (SImode); reg++)
+ if (CALL_COOKIE_STACKSEQ_GET (current_function_args_info.call_cookie)
+ >= NPARM_REGS (SImode) - reg)
+ for (; reg < NPARM_REGS (SImode); reg++)
+ emit_insn (gen_shcompact_preserve_incoming_args
+ (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+ else if (CALL_COOKIE_INT_REG_GET
+ (current_function_args_info.call_cookie, reg) == 1)
+ emit_insn (gen_shcompact_preserve_incoming_args
+ (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+
+ emit_move_insn (gen_rtx_REG (Pmode, MACL_REG),
+ stack_pointer_rtx);
+ emit_move_insn (gen_rtx_REG (SImode, R0_REG),
+ GEN_INT (current_function_args_info.call_cookie));
+ emit_move_insn (gen_rtx_REG (SImode, MACH_REG),
+ gen_rtx_REG (SImode, R0_REG));
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ int tr = sh_media_register_for_return ();
+
+ if (tr >= 0)
+ {
+ rtx insn = emit_move_insn (gen_rtx_REG (DImode, tr),
+ gen_rtx_REG (DImode, PR_MEDIA_REG));
+
+ /* If this function only exits with sibcalls, this copy
+ will be flagged as dead. */
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+ const0_rtx,
+ REG_NOTES (insn));
+ }
+ }
+
/* This is set by SETUP_VARARGS to indicate that this is a varargs
routine. Clear it here so that the next function isn't affected. */
if (current_function_anonymous_args)
@@ -4088,7 +4503,7 @@ sh_expand_prologue ()
current_function_anonymous_args = 0;
/* This is not used by the SH3E calling convention */
- if (! TARGET_SH3E && ! TARGET_HITACHI)
+ if (TARGET_SH1 && ! TARGET_SH3E && ! TARGET_HITACHI)
{
/* Push arg regs as if they'd been provided by caller in stack. */
for (i = 0; i < NPARM_REGS(SImode); i++)
@@ -4108,13 +4523,164 @@ sh_expand_prologue ()
if (sp_switch)
emit_insn (gen_sp_switch_1 ());
- live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
+ calc_live_regs (&d, live_regs_mask);
/* ??? Maybe we could save some switching if we can move a mode switch
that already happens to be at the function start into the prologue. */
if (target_flags != save_flags)
emit_insn (gen_toggle_sz ());
- push_regs (live_regs_mask, live_regs_mask2);
+ if (TARGET_SH5)
+ {
+ int i;
+ int offset;
+ int align;
+ rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+ int offset_in_r0 = -1;
+ int sp_in_r0 = 0;
+
+ if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
+ d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+ offset = d + d_rounding;
+ output_stack_adjust (-offset, stack_pointer_rtx, 1);
+
+ /* We loop twice: first, we save 8-byte aligned registers in the
+ higher addresses, that are known to be aligned. Then, we
+ proceed to saving 32-bit registers that don't need 8-byte
+ alignment. */
+ for (align = 1; align >= 0; align--)
+ for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if (live_regs_mask[i/32] & (1 << (i % 32)))
+ {
+ enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+ int reg = i;
+ rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
+
+ if (mode == SFmode && (i % 2) == 1
+ && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+ && (live_regs_mask[(i ^ 1) / 32] & (1 << ((i ^ 1) % 32))))
+ {
+ mode = DFmode;
+ i--;
+ reg--;
+ }
+
+ /* If we're doing the aligned pass and this is not aligned,
+ or we're doing the unaligned pass and this is aligned,
+ skip it. */
+ if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+ == 0) != align)
+ continue;
+
+ offset -= GET_MODE_SIZE (mode);
+
+ reg_rtx = gen_rtx_REG (mode, reg);
+
+ mem_rtx = gen_rtx_MEM (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+
+ GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec);
+
+ mem_rtx = NULL_RTX;
+
+ try_pre_dec:
+ do
+ if (HAVE_PRE_DECREMENT
+ && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
+ || mem_rtx == NULL_RTX
+ || i == PR_REG || SPECIAL_REGISTER_P (i)))
+ {
+ pre_dec = gen_rtx_MEM (mode,
+ gen_rtx_PRE_DEC (Pmode, r0));
+
+ GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0),
+ pre_dec_ok);
+
+ pre_dec = NULL_RTX;
+
+ break;
+
+ pre_dec_ok:
+ mem_rtx = NULL_RTX;
+ offset += GET_MODE_SIZE (mode);
+ }
+ while (0);
+
+ if (mem_rtx != NULL_RTX)
+ goto addr_ok;
+
+ if (offset_in_r0 == -1)
+ {
+ emit_move_insn (r0, GEN_INT (offset));
+ offset_in_r0 = offset;
+ }
+ else if (offset != offset_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0,
+ GEN_INT (offset - offset_in_r0)));
+ offset_in_r0 += offset - offset_in_r0;
+ }
+
+ if (pre_dec != NULL_RTX)
+ {
+ if (! sp_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0, stack_pointer_rtx));
+ sp_in_r0 = 1;
+ }
+
+ offset -= GET_MODE_SIZE (mode);
+ offset_in_r0 -= GET_MODE_SIZE (mode);
+
+ mem_rtx = pre_dec;
+ }
+ else if (sp_in_r0)
+ mem_rtx = gen_rtx_MEM (mode, r0);
+ else
+ mem_rtx = gen_rtx_MEM (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ r0));
+
+ /* We must not use an r0-based address for target-branch
+ registers or for special registers without pre-dec
+ memory addresses, since we store their values in r0
+ first. */
+ if (TARGET_REGISTER_P (i)
+ || ((i == PR_REG || SPECIAL_REGISTER_P (i))
+ && mem_rtx != pre_dec))
+ abort ();
+
+ addr_ok:
+ if (TARGET_REGISTER_P (i)
+ || ((i == PR_REG || SPECIAL_REGISTER_P (i))
+ && mem_rtx != pre_dec))
+ {
+ rtx r0mode = gen_rtx_REG (GET_MODE (reg_rtx), R0_REG);
+
+ emit_move_insn (r0mode, reg_rtx);
+
+ offset_in_r0 = -1;
+ sp_in_r0 = 0;
+
+ reg_rtx = r0mode;
+ }
+
+ emit_move_insn (mem_rtx, reg_rtx);
+ }
+
+ if (offset != d_rounding)
+ abort ();
+ }
+ else
+ push_regs (live_regs_mask);
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
{
@@ -4135,6 +4701,19 @@ sh_expand_prologue ()
while (insn != last);
}
+ if (SHMEDIA_REGS_STACK_ADJUST ())
+ {
+ emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
+ gen_rtx_SYMBOL_REF (Pmode,
+ TARGET_FPU_ANY
+ ? "__GCC_push_shmedia_regs"
+ : "__GCC_push_shmedia_regs_nofpu"));
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ emit_insn (gen_shmedia_save_restore_regs_compact
+ (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
+ }
+
if (target_flags != save_flags)
{
rtx insn = emit_insn (gen_toggle_sz ());
@@ -4149,26 +4728,48 @@ sh_expand_prologue ()
target_flags = save_flags;
- output_stack_adjust (-rounded_frame_size (d),
- stack_pointer_rtx, 1);
+ output_stack_adjust (-rounded_frame_size (d) + d_rounding,
+ stack_pointer_rtx, TARGET_SH5 ? 0 : 1);
if (frame_pointer_needed)
- emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+ emit_insn (GEN_MOV (frame_pointer_rtx, stack_pointer_rtx));
+
+ if (TARGET_SHCOMPACT && flag_pic && current_function_args_info.call_cookie)
+ /* We're going to use the PIC register to load the address of the
+ incoming-argument decoder and/or of the return trampoline from
+ the GOT, so make sure the PIC register is preserved and
+ initialized. */
+ regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+
+ if (TARGET_SHCOMPACT
+ && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
+ {
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
+ gen_rtx_SYMBOL_REF (Pmode,
+ "__GCC_shcompact_incoming_args"));
+ emit_insn (gen_shcompact_incoming_args ());
+ }
}
void
sh_expand_epilogue ()
{
- int live_regs_mask;
+ HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
int d, i;
+ int d_rounding = 0;
- int live_regs_mask2;
int save_flags = target_flags;
int frame_size;
- live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
+ calc_live_regs (&d, live_regs_mask);
- frame_size = rounded_frame_size (d);
+ if (TARGET_SH5 && d % (STACK_BOUNDARY / BITS_PER_UNIT))
+ d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+ frame_size = rounded_frame_size (d) - d_rounding;
if (frame_pointer_needed)
{
@@ -4179,7 +4780,7 @@ sh_expand_epilogue ()
occur after the SP adjustment and clobber data in the local
frame. */
emit_insn (gen_blockage ());
- emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
+ emit_insn (GEN_MOV (stack_pointer_rtx, frame_pointer_rtx));
}
else if (frame_size)
{
@@ -4191,25 +4792,179 @@ sh_expand_epilogue ()
output_stack_adjust (frame_size, stack_pointer_rtx, 7);
}
+ if (SHMEDIA_REGS_STACK_ADJUST ())
+ {
+ emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
+ gen_rtx_SYMBOL_REF (Pmode,
+ TARGET_FPU_ANY
+ ? "__GCC_pop_shmedia_regs"
+ : "__GCC_pop_shmedia_regs_nofpu"));
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ emit_insn (gen_shmedia_save_restore_regs_compact
+ (GEN_INT (SHMEDIA_REGS_STACK_ADJUST ())));
+ }
+
/* Pop all the registers. */
if (target_flags != save_flags)
emit_insn (gen_toggle_sz ());
- if (live_regs_mask & (1 << PR_REG))
+ if (TARGET_SH5)
+ {
+ int offset = d_rounding;
+ int offset_in_r0 = -1;
+ int sp_in_r0 = 0;
+ int align;
+ rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+
+ /* We loop twice: first, we save 8-byte aligned registers in the
+ higher addresses, that are known to be aligned. Then, we
+ proceed to saving 32-bit registers that don't need 8-byte
+ alignment. */
+ for (align = 0; align <= 1; align++)
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (live_regs_mask[i/32] & (1 << (i % 32)))
+ {
+ enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+ int reg = i;
+ rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
+
+ if (mode == SFmode && (i % 2) == 0
+ && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+ && (live_regs_mask[(i ^ 1) / 32] & (1 << ((i ^ 1) % 32))))
+ {
+ mode = DFmode;
+ i++;
+ }
+
+ /* If we're doing the aligned pass and this is not aligned,
+ or we're doing the unaligned pass and this is aligned,
+ skip it. */
+ if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+ == 0) != align)
+ continue;
+
+ reg_rtx = gen_rtx_REG (mode, reg);
+
+ mem_rtx = gen_rtx_MEM (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+
+ GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc);
+
+ mem_rtx = NULL_RTX;
+
+ try_post_inc:
+ do
+ if (HAVE_POST_INCREMENT
+ && (offset == offset_in_r0
+ || (offset + GET_MODE_SIZE (mode) != d + d_rounding
+ && mem_rtx == NULL_RTX)
+ || i == PR_REG || SPECIAL_REGISTER_P (i)))
+ {
+ post_inc = gen_rtx_MEM (mode,
+ gen_rtx_POST_INC (Pmode, r0));
+
+ GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0),
+ post_inc_ok);
+
+ post_inc = NULL_RTX;
+
+ break;
+
+ post_inc_ok:
+ mem_rtx = NULL_RTX;
+ }
+ while (0);
+
+ if (mem_rtx != NULL_RTX)
+ goto addr_ok;
+
+ if (offset_in_r0 == -1)
+ {
+ emit_move_insn (r0, GEN_INT (offset));
+ offset_in_r0 = offset;
+ }
+ else if (offset != offset_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0,
+ GEN_INT (offset - offset_in_r0)));
+ offset_in_r0 += offset - offset_in_r0;
+ }
+
+ if (post_inc != NULL_RTX)
+ {
+ if (! sp_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0, stack_pointer_rtx));
+ sp_in_r0 = 1;
+ }
+
+ mem_rtx = post_inc;
+
+ offset_in_r0 += GET_MODE_SIZE (mode);
+ }
+ else if (sp_in_r0)
+ mem_rtx = gen_rtx_MEM (mode, r0);
+ else
+ mem_rtx = gen_rtx_MEM (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ r0));
+
+ if ((i == PR_REG || SPECIAL_REGISTER_P (i))
+ && mem_rtx != post_inc)
+ abort ();
+
+ addr_ok:
+ if ((i == PR_REG || SPECIAL_REGISTER_P (i))
+ && mem_rtx != post_inc)
+ {
+ insn = emit_move_insn (r0, mem_rtx);
+ mem_rtx = r0;
+ }
+ else if (TARGET_REGISTER_P (i))
+ {
+ rtx r1 = gen_rtx_REG (mode, R1_REG);
+
+ insn = emit_move_insn (r1, mem_rtx);
+ mem_rtx = r1;
+ }
+
+ insn = emit_move_insn (reg_rtx, mem_rtx);
+
+ offset += GET_MODE_SIZE (mode);
+ }
+
+ if (offset != d + d_rounding)
+ abort ();
+
+ goto finish;
+ }
+ else
+ d = 0;
+ if (live_regs_mask[PR_REG / 32] & (1 << (PR_REG % 32)))
pop (PR_REG);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
int j = (FIRST_PSEUDO_REGISTER - 1) - i;
- if (j < 32 && (live_regs_mask & (1 << j)) && j != PR_REG)
- pop (j);
- else if (j >= 32 && (live_regs_mask2 & (1 << (j - 32))))
+
+ if (j != PR_REG && live_regs_mask[j / 32] & (1 << (j % 32)))
pop (j);
}
+ finish:
if (target_flags != save_flags)
emit_insn (gen_toggle_sz ());
target_flags = save_flags;
- output_stack_adjust (extra_push + current_function_pretend_args_size,
+ output_stack_adjust (extra_push + current_function_pretend_args_size
+ + d + d_rounding
+ + current_function_args_info.stack_regs * 8,
stack_pointer_rtx, 7);
/* Switch back to the normal stack if necessary. */
@@ -4217,7 +4972,10 @@ sh_expand_epilogue ()
emit_insn (gen_sp_switch_2 ());
/* Tell flow the insn that pops PR isn't dead. */
- if (live_regs_mask & (1 << PR_REG))
+ /* PR_REG will never be live in SHmedia mode, and we don't need to
+ USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
+ by the return pattern. */
+ if (live_regs_mask[PR_REG / 32] & (1 << (PR_REG % 32)))
emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, PR_REG)));
}
@@ -4268,12 +5026,57 @@ sh_builtin_saveregs ()
int bufsize, regno;
HOST_WIDE_INT alias_set;
+ if (TARGET_SH5)
+ {
+ if (n_intregs)
+ {
+ int pushregs = n_intregs;
+
+ while (pushregs < NPARM_REGS (SImode) - 1
+ && (CALL_COOKIE_INT_REG_GET
+ (current_function_args_info.call_cookie,
+ NPARM_REGS (SImode) - pushregs)
+ == 1))
+ {
+ current_function_args_info.call_cookie
+ &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
+ - pushregs, 1);
+ pushregs++;
+ }
+
+ if (pushregs == NPARM_REGS (SImode))
+ current_function_args_info.call_cookie
+ |= (CALL_COOKIE_INT_REG (0, 1)
+ | CALL_COOKIE_STACKSEQ (pushregs - 1));
+ else
+ current_function_args_info.call_cookie
+ |= CALL_COOKIE_STACKSEQ (pushregs);
+
+ current_function_pretend_args_size += 8 * n_intregs;
+ }
+ if (TARGET_SHCOMPACT)
+ return const0_rtx;
+ }
+
+ if (! TARGET_SH3E && ! TARGET_SH4 && ! TARGET_SH5)
+ {
+ error ("__builtin_saveregs not supported by this subtarget");
+ return const0_rtx;
+ }
+
+ if (TARGET_SHMEDIA)
+ n_floatregs = 0;
+
/* Allocate block of memory for the regs. */
/* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
Or can assign_stack_local accept a 0 SIZE argument? */
bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
- regbuf = assign_stack_local (BLKmode, bufsize, 0);
+ if (TARGET_SHMEDIA)
+ regbuf = gen_rtx_MEM (BLKmode,
+ gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
+ else
+ regbuf = assign_stack_local (BLKmode, bufsize, 0);
alias_set = get_varargs_alias_set ();
set_mem_alias_set (regbuf, alias_set);
@@ -4286,6 +5089,10 @@ sh_builtin_saveregs ()
n_floatregs * UNITS_PER_WORD),
n_intregs, n_intregs * UNITS_PER_WORD);
+ if (TARGET_SHMEDIA)
+ /* Return the address of the regbuf. */
+ return XEXP (regbuf, 0);
+
/* Save float args.
This is optimized to only save the regs that are necessary. Explicitly
named args need not be saved.
@@ -4345,7 +5152,7 @@ sh_build_va_list ()
tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
tree record;
- if ((! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
+ if (TARGET_SH5 || (! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
return ptr_type_node;
record = make_node (RECORD_TYPE);
@@ -4393,6 +5200,20 @@ sh_va_start (stdarg_p, valist, nextarg)
tree t, u;
int nfp, nint;
+ if (TARGET_SH5)
+ {
+ expand_builtin_saveregs ();
+ /* When the varargs dummy argument is ``passed'' on a register,
+ we don't want std_expand_builtin_va_start() to apply any
+ correction for it, so set stdarg_p so as to pretend there's
+ no such dummy argument. */
+ if (current_function_args_info.arg_count[(int) SH_ARG_INT]
+ < NPARM_REGS (SImode))
+ stdarg_p = 1;
+ std_expand_builtin_va_start (stdarg_p, valist, nextarg);
+ return;
+ }
+
if ((! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
{
std_expand_builtin_va_start (stdarg_p, valist, nextarg);
@@ -4471,7 +5292,7 @@ sh_va_arg (valist, type)
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
pptr_type_node = build_pointer_type (ptr_type_node);
- if ((TARGET_SH3E || TARGET_SH4) && ! TARGET_HITACHI)
+ if (! TARGET_SH5 && (TARGET_SH3E || TARGET_SH4) && ! TARGET_HITACHI)
{
tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
@@ -4596,22 +5417,32 @@ initial_elimination_offset (from, to)
int to;
{
int regs_saved;
+ int regs_saved_rounding = 0;
int total_saved_regs_space;
int total_auto_space;
int save_flags = target_flags;
+ int copy_flags;
+
+ HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
+ calc_live_regs (&regs_saved, live_regs_mask);
+ regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
+ if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
+ regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
- int live_regs_mask, live_regs_mask2;
- live_regs_mask = calc_live_regs (&regs_saved, &live_regs_mask2);
- total_auto_space = rounded_frame_size (regs_saved);
+ total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
+ copy_flags = target_flags;
target_flags = save_flags;
- total_saved_regs_space = regs_saved;
+ total_saved_regs_space = regs_saved + regs_saved_rounding;
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
- return total_saved_regs_space + total_auto_space;
+ return total_saved_regs_space + total_auto_space
+ + current_function_args_info.byref_regs * 8;
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
- return total_saved_regs_space + total_auto_space;
+ return total_saved_regs_space + total_auto_space
+ + current_function_args_info.byref_regs * 8;
/* Initial gap between fp and sp is 0. */
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
@@ -4619,6 +5450,58 @@ initial_elimination_offset (from, to)
if (from == RETURN_ADDRESS_POINTER_REGNUM
&& (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM))
+ if (TARGET_SH5)
+ {
+ int i, n = total_saved_regs_space;
+ int align;
+ int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
+
+ n += total_auto_space;
+
+ /* If it wasn't saved, there's not much we can do. */
+ if ((live_regs_mask[pr_reg / 32] & (1 << (pr_reg % 32))) == 0)
+ return n;
+
+ target_flags = copy_flags;
+
+ /* We loop twice: first, check 8-byte aligned registers,
+ that are stored in the higher addresses, that are known
+ to be aligned. Then, check 32-bit registers that don't
+ need 8-byte alignment. */
+ for (align = 1; align >= 0; align--)
+ for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if (live_regs_mask[i/32] & (1 << (i % 32)))
+ {
+ enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+
+ if (mode == SFmode && (i % 2) == 1
+ && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+ && (live_regs_mask[(i ^ 1) / 32]
+ & (1 << ((i ^ 1) % 32))))
+ {
+ mode = DFmode;
+ i--;
+ }
+
+ /* If we're doing the aligned pass and this is not aligned,
+ or we're doing the unaligned pass and this is aligned,
+ skip it. */
+ if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+ == 0) != align)
+ continue;
+
+ n -= GET_MODE_SIZE (mode);
+
+ if (i == pr_reg)
+ {
+ target_flags = save_flags;
+ return n;
+ }
+ }
+
+ abort ();
+ }
+ else
return total_auto_space;
abort ();
@@ -4858,6 +5741,20 @@ general_movdst_operand (op, mode)
return general_operand (op, mode);
}
+/* Accept a register, but not a subreg of any kind. This allows us to
+ avoid pathological cases in reload wrt data movement common in
+ int->fp conversion. */
+
+int
+reg_no_subreg_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == SUBREG)
+ return 0;
+ return register_operand (op, mode);
+}
+
/* Returns 1 if OP is a normal arithmetic register. */
int
@@ -4877,6 +5774,7 @@ arith_reg_operand (op, mode)
return 1;
return (regno != T_REG && regno != PR_REG
+ && ! TARGET_REGISTER_P (regno)
&& (regno != FPUL_REG || TARGET_SH4)
&& regno != MACH_REG && regno != MACL_REG);
}
@@ -4915,7 +5813,20 @@ arith_operand (op, mode)
if (arith_reg_operand (op, mode))
return 1;
- if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op)))
+ if (TARGET_SHMEDIA)
+ {
+ /* FIXME: We should be checking whether the CONST_INT fits in a
+ CONST_OK_FOR_J here, but this causes reload_cse to crash when
+ attempting to transform a sequence of two 64-bit sets of the
+ same register from literal constants into a set and an add,
+ when the difference is too wide for an add. */
+ if (GET_CODE (op) == CONST_INT
+ || EXTRA_CONSTRAINT_S (op))
+ return 1;
+ else
+ return 0;
+ }
+ else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op)))
return 1;
return 0;
@@ -4937,6 +5848,18 @@ arith_reg_or_0_operand (op, mode)
return 0;
}
+/* Return 1 if OP is a valid source operand for an SHmedia operation
+ that takes either a register or a 6-bit immediate. */
+
+int
+shmedia_6bit_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (arith_reg_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_O (INTVAL (op))));
+}
+
/* Returns 1 if OP is a valid source operand for a logical operation. */
int
@@ -4947,7 +5870,14 @@ logical_operand (op, mode)
if (arith_reg_operand (op, mode))
return 1;
- if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op)))
+ if (TARGET_SHMEDIA)
+ {
+ if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_P (INTVAL (op)))
+ return 1;
+ else
+ return 0;
+ }
+ else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op)))
return 1;
return 0;
@@ -5019,6 +5949,9 @@ fpul_operand (op, mode)
rtx op;
enum machine_mode mode;
{
+ if (TARGET_SHMEDIA)
+ return fp_arith_reg_operand (op, mode);
+
return (GET_CODE (op) == REG
&& (REGNO (op) == FPUL_REG || REGNO (op) >= FIRST_PSEUDO_REGISTER)
&& GET_MODE (op) == mode);
@@ -5087,6 +6020,50 @@ binary_float_operator (op, mode)
}
return 0;
}
+
+/* Accept pseudos and branch target registers. */
+int
+target_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != DImode
+ || GET_MODE (op) != DImode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = XEXP (op, 0);
+
+ if (GET_CODE (op) != REG)
+ return 0;
+
+ /* We must protect ourselves from matching pseudos that are virtual
+ register, because they will eventually be replaced with hardware
+ registers that aren't branch-target registers. */
+ if (REGNO (op) > LAST_VIRTUAL_REGISTER
+ || TARGET_REGISTER_P (REGNO (op)))
+ return 1;
+
+ return 0;
+}
+
+/* Same as target_reg_operand, except that label_refs and symbol_refs
+ are accepted before reload. */
+int
+target_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != DImode)
+ return 0;
+
+ if ((GET_MODE (op) == DImode || GET_MODE (op) == VOIDmode)
+ && EXTRA_CONSTRAINT_T (op))
+ return ! reload_completed;
+
+ return target_reg_operand (op, mode);
+}
+
/* Return the destination address of a branch. */
@@ -5480,13 +6457,20 @@ nonpic_symbol_mentioned_p (x)
register const char *fmt;
register int i;
- if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
+ || GET_CODE (x) == PC)
return 1;
+ /* We don't want to look into the possible MEM location of a
+ CONST_DOUBLE, since we're not going to use it, in general. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return 0;
+
if (GET_CODE (x) == UNSPEC
&& (XINT (x, 1) == UNSPEC_PIC
|| XINT (x, 1) == UNSPEC_GOT
|| XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_GOTPLT
|| XINT (x, 1) == UNSPEC_PLT))
return 0;
@@ -5718,3 +6702,18 @@ sh_pr_n_sets ()
{
return REG_N_SETS (PR_REG);
}
+
+/* SHmedia requires registers for branches, so we can't generate new
+ branches past reload. */
+static bool
+sh_cannot_modify_jumps_p ()
+{
+ return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
+}
+
+static bool
+sh_ms_bitfield_layout_p (record_type)
+ tree record_type ATTRIBUTE_UNUSED;
+{
+ return TARGET_SH5;
+}