diff options
author | aoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-02-09 03:08:08 +0000 |
---|---|---|
committer | aoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-02-09 03:08:08 +0000 |
commit | 87e19636a559963421ed4b38ab15416642cd741a (patch) | |
tree | d0a27ffe9a822025fe0fa1929bfd170fabd4723e /gcc/config/sh/sh.c | |
parent | e0c8d7fd2d498dd4d3a2d09b6f97248d794d3c9d (diff) | |
download | gcc-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.c | 1191 |
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 (®s_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 (®s_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; +} |