| /* Definitions of target machine for GNU compiler for Renesas / SuperH SH. |
| Copyright (C) 1993-2020 Free Software Foundation, Inc. |
| Contributed by Steve Chamberlain (sac@cygnus.com). |
| Improved by Jim Wilson (wilson@cygnus.com). |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef GCC_SH_PROTOS_H |
| #define GCC_SH_PROTOS_H |
| |
| enum sh_function_kind { |
| /* A function with normal C ABI */ |
| FUNCTION_ORDINARY, |
| /* A special function that guarantees that some otherwise call-clobbered |
| registers are not clobbered. These can't go through the SH5 resolver, |
| because it only saves argument passing registers. */ |
| SFUNC_GOT, |
| /* A special function that should be linked statically. These are typically |
| smaller or not much larger than a PLT entry. |
| Some also have a non-standard ABI which precludes dynamic linking. */ |
| SFUNC_STATIC |
| }; |
| |
| #ifdef RTX_CODE |
| extern rtx sh_fsca_sf2int (void); |
| extern rtx sh_fsca_int2sf (void); |
| |
| /* Declare functions defined in sh.c and used in templates. */ |
| extern bool sh_lra_p (void); |
| |
| extern const char *output_branch (int, rtx_insn *, rtx *); |
| extern const char *output_ieee_ccmpeq (rtx_insn *, rtx *); |
| extern const char *output_branchy_insn (enum rtx_code, const char *, |
| rtx_insn *, rtx *); |
| extern const char *output_movedouble (rtx, rtx[], machine_mode); |
| extern const char *output_movepcrel (rtx, rtx[], machine_mode); |
| extern const char *output_far_jump (rtx_insn *, rtx); |
| |
| extern rtx sfunc_uses_reg (rtx_insn *); |
| extern int barrier_align (rtx_insn *); |
| extern int sh_loop_align (rtx_insn *); |
| extern bool fp_zero_operand (rtx); |
| extern bool fp_one_operand (rtx); |
| extern bool sh_legitimate_index_p (machine_mode, rtx, bool, bool); |
| extern bool sh_legitimize_reload_address (rtx *, machine_mode, int, int); |
| extern rtx legitimize_pic_address (rtx, machine_mode, rtx); |
| extern bool nonpic_symbol_mentioned_p (rtx); |
| extern void output_pic_addr_const (FILE *, rtx); |
| extern bool expand_block_move (rtx *); |
| extern void prepare_move_operands (rtx[], machine_mode mode); |
| extern bool sh_expand_cmpstr (rtx *); |
| extern bool sh_expand_cmpnstr (rtx *); |
| extern bool sh_expand_strlen (rtx *); |
| extern void sh_expand_setmem (rtx *); |
| extern enum rtx_code prepare_cbranch_operands (rtx *, machine_mode mode, |
| enum rtx_code comparison); |
| extern void expand_cbranchsi4 (rtx *operands, enum rtx_code comparison); |
| extern bool expand_cbranchdi4 (rtx *operands, enum rtx_code comparison); |
| extern void sh_emit_scc_to_t (enum rtx_code, rtx, rtx); |
| extern void sh_emit_compare_and_branch (rtx *, machine_mode); |
| extern void sh_emit_compare_and_set (rtx *, machine_mode); |
| extern bool sh_ashlsi_clobbers_t_reg_p (rtx); |
| extern bool sh_lshrsi_clobbers_t_reg_p (rtx); |
| extern void gen_shifty_op (int, rtx *); |
| extern void gen_shifty_hi_op (int, rtx *); |
| extern bool expand_ashiftrt (rtx *); |
| extern bool sh_dynamicalize_shift_p (rtx); |
| extern int shl_and_kind (rtx, rtx, int *); |
| extern int shl_and_length (rtx); |
| extern int shl_and_scr_length (rtx); |
| extern bool gen_shl_and (rtx, rtx, rtx, rtx); |
| extern int shl_sext_kind (rtx, rtx, int *); |
| extern int shl_sext_length (rtx); |
| extern bool gen_shl_sext (rtx, rtx, rtx, rtx); |
| extern int regs_used (rtx, int); |
| extern void fixup_addr_diff_vecs (rtx_insn *); |
| extern int get_dest_uid (rtx_insn *, int); |
| extern void final_prescan_insn (rtx_insn *, rtx *, int); |
| extern enum tls_model tls_symbolic_operand (rtx, machine_mode); |
| extern bool system_reg_operand (rtx, machine_mode); |
| extern bool reg_unused_after (rtx, rtx_insn *); |
| extern int sh_insn_length_adjustment (rtx_insn *); |
| extern bool sh_expand_t_scc (rtx *); |
| extern rtx sh_gen_truncate (machine_mode, rtx, int); |
| extern bool sh_vector_mode_supported_p (machine_mode); |
| extern bool sh_cfun_trap_exit_p (void); |
| extern rtx sh_find_equiv_gbr_addr (rtx_insn* cur_insn, rtx mem); |
| extern int sh_eval_treg_value (rtx op); |
| extern HOST_WIDE_INT sh_disp_addr_displacement (rtx mem_op); |
| extern int sh_max_mov_insn_displacement (machine_mode mode, bool consider_sh2a); |
| extern bool sh_movsf_ie_ra_split_p (rtx, rtx, rtx); |
| extern void sh_expand_sym_label2reg (rtx, rtx, rtx, bool); |
| |
| /* Result value of sh_find_set_of_reg. */ |
| struct set_of_reg |
| { |
| /* The insn where sh_find_set_of_reg stopped looking. |
| Can be NULL_RTX if the end of the insn list was reached. */ |
| rtx_insn* insn; |
| |
| /* The set rtx of the specified reg if found, NULL_RTX otherwise. */ |
| const_rtx set_rtx; |
| |
| /* The set source rtx of the specified reg if found, NULL_RTX otherwise. |
| Usually, this is the most interesting return value. */ |
| rtx set_src; |
| }; |
| |
| /* Given a reg rtx and a start insn, try to find the insn that sets |
| the specified reg by using the specified insn stepping function, |
| such as 'prev_nonnote_nondebug_insn_bb'. When the insn is found, |
| try to extract the rtx of the reg set. */ |
| template <typename F> inline set_of_reg |
| sh_find_set_of_reg (rtx reg, rtx_insn* insn, F stepfunc, |
| bool ignore_reg_reg_copies = false) |
| { |
| set_of_reg result; |
| result.insn = insn; |
| result.set_rtx = NULL_RTX; |
| result.set_src = NULL_RTX; |
| |
| if (!REG_P (reg) || insn == NULL_RTX) |
| return result; |
| |
| for (rtx_insn* i = stepfunc (insn); i != NULL_RTX; i = stepfunc (i)) |
| { |
| if (BARRIER_P (i)) |
| break; |
| if (!INSN_P (i) || DEBUG_INSN_P (i)) |
| continue; |
| if (reg_set_p (reg, i)) |
| { |
| if (CALL_P (i)) |
| break; |
| |
| result.insn = i; |
| result.set_rtx = set_of (reg, i); |
| |
| if (result.set_rtx == NULL_RTX || GET_CODE (result.set_rtx) != SET) |
| break; |
| |
| result.set_src = XEXP (result.set_rtx, 1); |
| |
| if (ignore_reg_reg_copies && REG_P (result.set_src)) |
| { |
| reg = result.set_src; |
| continue; |
| } |
| if (ignore_reg_reg_copies && SUBREG_P (result.set_src) |
| && REG_P (SUBREG_REG (result.set_src))) |
| { |
| reg = SUBREG_REG (result.set_src); |
| continue; |
| } |
| |
| break; |
| } |
| } |
| |
| /* If the searched reg is found inside a (mem (post_inc:SI (reg))), set_of |
| will return NULL and set_rtx will be NULL. |
| In this case report a 'not found'. result.insn will always be non-null |
| at this point, so no need to check it. */ |
| if (result.set_src != NULL && result.set_rtx == NULL) |
| result.set_src = NULL; |
| |
| return result; |
| } |
| |
| /* Result value of sh_find_extending_set_of_reg. */ |
| struct sh_extending_set_of_reg : public set_of_reg |
| { |
| /* The mode the set is extending from (QImode or HImode), or VOIDmode if |
| this is not a zero/sign extending set. */ |
| machine_mode from_mode; |
| |
| /* ZERO_EXTEND, SIGN_EXTEND or UNKNOWN. */ |
| rtx_code ext_code; |
| |
| sh_extending_set_of_reg (rtx_insn* i) |
| { |
| insn = i; |
| set_rtx = NULL; |
| set_src = NULL; |
| from_mode = VOIDmode; |
| ext_code = UNKNOWN; |
| } |
| |
| sh_extending_set_of_reg (const set_of_reg& rhs) |
| { |
| *((set_of_reg*)this) = rhs; |
| from_mode = VOIDmode; |
| ext_code = UNKNOWN; |
| } |
| |
| /* Returns true if it's possible to use the source reg of the sign |
| or zero extending set directly, bypassing the extension. */ |
| bool can_use_as_unextended_reg (void) const; |
| |
| /* Returns the reg rtx of the sign or zero extending set source, that can |
| be safely used at the specified insn in SImode. */ |
| rtx use_as_unextended_reg (rtx_insn* use_at_insn) const; |
| |
| /* Returns the reg rtx of the sign or zero extending result, that can be |
| safely used at the specified insn in SImode. If the set source is an |
| implicitly sign extending mem load, the mem load is converted into an |
| explicitly sign extending mem load. */ |
| rtx use_as_extended_reg (rtx_insn* use_at_insn) const; |
| }; |
| |
| extern sh_extending_set_of_reg sh_find_extending_set_of_reg (rtx reg, |
| rtx_insn* insn); |
| |
| extern bool sh_is_logical_t_store_expr (rtx op, rtx_insn* insn); |
| extern rtx sh_try_omit_signzero_extend (rtx extended_op, rtx_insn* insn); |
| extern bool sh_split_movrt_negc_to_movt_xor (rtx_insn* curr_insn, |
| rtx operands[]); |
| extern void sh_split_tst_subregs (rtx_insn* curr_insn, |
| machine_mode subreg_mode, int subreg_offset, |
| rtx operands[]); |
| |
| extern bool sh_is_nott_insn (const rtx_insn* i); |
| extern rtx sh_movt_set_dest (const rtx_insn* i); |
| extern rtx sh_movt_set_dest (const_rtx i); |
| extern rtx sh_movrt_set_dest (const rtx_insn* i); |
| extern rtx sh_movrt_set_dest (const_rtx i); |
| |
| inline bool sh_is_movt_insn (const rtx_insn* i) |
| { |
| return sh_movt_set_dest (i) != NULL; |
| } |
| |
| inline bool sh_is_movrt_insn (const rtx_insn* i) |
| { |
| return sh_movrt_set_dest (i) != NULL; |
| } |
| |
| extern bool sh_insn_operands_modified_between_p (rtx_insn* operands_insn, |
| const rtx_insn* from, |
| const rtx_insn* to); |
| |
| extern bool sh_reg_dead_or_unused_after_insn (const rtx_insn* i, int regno); |
| extern void sh_remove_reg_dead_or_unused_notes (rtx_insn* i, int regno); |
| extern rtx_insn* sh_check_add_incdec_notes (rtx_insn* i); |
| extern rtx sh_remove_overlapping_post_inc (rtx dst, rtx src); |
| extern rtx_insn* sh_peephole_emit_move_insn (rtx dst, rtx src); |
| |
| extern bool sh_in_recog_treg_set_expr (void); |
| extern bool sh_recog_treg_set_expr (rtx op, machine_mode mode); |
| |
| /* Result value of sh_split_treg_set_expr. Contains the first insn emitted |
| and the optional trailing nott insn. */ |
| class sh_treg_insns |
| { |
| public: |
| sh_treg_insns (void) : m_first_insn (NULL), m_trailing_nott_insn (NULL) { } |
| sh_treg_insns (rtx_insn* first_insn, rtx_insn* nott_insn) |
| : m_first_insn (first_insn), |
| m_trailing_nott_insn (nott_insn) |
| { } |
| |
| bool was_treg_operand (void) const { return m_first_insn == NULL; } |
| bool has_trailing_nott (void) const { return m_trailing_nott_insn != NULL; } |
| rtx_insn* trailing_nott (void) const { return m_trailing_nott_insn; } |
| rtx_insn* first_insn (void) const { return m_first_insn; } |
| |
| /* If there is a trailing nott, remove it from the emitted insns and |
| return true. Return false otherwise. */ |
| bool |
| remove_trailing_nott (void) |
| { |
| if (!has_trailing_nott ()) |
| return false; |
| |
| remove_insn (trailing_nott ()); |
| return true; |
| } |
| |
| private: |
| rtx_insn* m_first_insn; |
| rtx_insn* m_trailing_nott_insn; |
| }; |
| |
| extern sh_treg_insns sh_split_treg_set_expr (rtx x, rtx_insn* curr_insn); |
| |
| enum |
| { |
| /* An effective conditional branch distance of zero bytes is impossible. |
| Hence we can use it to designate an unknown value. */ |
| unknown_cbranch_distance = 0u, |
| infinite_cbranch_distance = ~0u |
| }; |
| |
| unsigned int |
| sh_cbranch_distance (rtx_insn* cbranch_insn, |
| unsigned int max_dist = infinite_cbranch_distance); |
| |
| #endif /* RTX_CODE */ |
| |
| extern void sh_cpu_cpp_builtins (cpp_reader* pfile); |
| |
| extern const char *output_jump_label_table (void); |
| extern rtx get_t_reg_rtx (void); |
| extern void sh_expand_prologue (void); |
| extern void sh_expand_epilogue (bool); |
| extern void sh_set_return_address (rtx, rtx); |
| extern int initial_elimination_offset (int, int); |
| extern bool sh_hard_regno_rename_ok (unsigned int, unsigned int); |
| extern bool sh_cfun_interrupt_handler_p (void); |
| extern bool sh_cfun_resbank_handler_p (void); |
| extern bool sh_attr_renesas_p (const_tree); |
| extern bool sh_cfun_attr_renesas_p (void); |
| extern bool sh_small_register_classes_for_mode_p (machine_mode); |
| extern void sh_mark_label (rtx, int); |
| extern bool check_use_sfunc_addr (rtx_insn *, rtx); |
| |
| #ifdef HARD_CONST |
| extern void fpscr_set_from_mem (int, HARD_REG_SET); |
| #endif |
| |
| extern void sh_pr_interrupt (struct cpp_reader *); |
| extern void sh_pr_trapa (struct cpp_reader *); |
| extern void sh_pr_nosave_low_regs (struct cpp_reader *); |
| |
| struct function_symbol_result |
| { |
| function_symbol_result (void) : sym (NULL), lab (NULL) { } |
| function_symbol_result (rtx s, rtx l) : sym (s), lab (l) { } |
| |
| rtx sym; |
| rtx lab; |
| }; |
| |
| extern function_symbol_result function_symbol (rtx, const char *, |
| sh_function_kind); |
| extern rtx sh_get_fdpic_reg_initial_val (void); |
| extern rtx sh_get_pr_initial_val (void); |
| |
| extern void sh_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, |
| signed int, machine_mode); |
| extern rtx sh_dwarf_register_span (rtx); |
| |
| extern bool sh_contains_memref_p (rtx); |
| extern bool sh_loads_bankedreg_p (rtx); |
| extern int sh2a_get_function_vector_number (rtx); |
| extern bool sh2a_is_function_vector_call (rtx); |
| extern void sh_fix_range (const char *); |
| extern machine_mode sh_hard_regno_caller_save_mode (unsigned int, unsigned int, |
| machine_mode); |
| extern bool sh_can_use_simple_return_p (void); |
| extern rtx sh_load_function_descriptor (rtx); |
| #endif /* ! GCC_SH_PROTOS_H */ |