From ab96de7ea659d9cdb6eb218739c8d24d4856f789 Mon Sep 17 00:00:00 2001 From: Adrian Straetling Date: Mon, 9 May 2005 18:09:52 +0000 Subject: [PATCH] s390.c: (s390_match_ccmode_set... 2005-05-09 Adrian Straetling * config/s390/s390.c: (s390_match_ccmode_set, s390_branch_condition_mnemonic, s390_short_displacement, s390_decompose_address, get_thread_pointer, legitimize_tls_address, print_shift_count_operand, get_some_local_dynamic_name, get_some_local_dynamic_name_1, reg_used_in_mem_p, addr_generation_dependency_p, s390_split_branches, annotate_constant_pool_refs, find_constant_pool_ref, replace_constant_pool_ref, find_ltrel_base, replace_ltrel_base, s390_optimize_prologue, find_unused_clobbered_reg, s390_frame_area, s390_register_info, s390_frame_info, s390_init_frame_layout, s390_update_frame_layout, save_fpr, restore_fpr, save_gprs, restore_gprs, s390_function_arg_size, s390_function_arg_float, s390_init_machine_status, s390_assemble_integer, s390_handle_option, s390_encode_section_info, s390_cannot_force_const_mem, s390_delegitimize_address, s390_return_in_memory, s390_init_builtins, s390_expand_builtin, s390_output_mi_thunk, s390_safe_attr_type, s390_adjust_priority, s390_issue_rate, s390_first_cycle_multipass_dfa_lookahead, s390_cannot_copy_insn_p, s390_rtx_costs, s390_address_cost, s390_reorg, s390_valid_pointer_mode, s390_build_builtin_va_list, s390_gimplify_va_arg, s390_function_ok_for_sibcall, s390_call_saved_register_used, s390_pass_by_reference, s390_fixed_condition_code_regs, s390_cc_modes_compatible s390_mainpool_start,, s390_mainpool_finish, s390_mainpool_cancel, s390_chunkify_start, s390_chunkify_finish, s390_chunkify_cancel, s390_start_pool, s390_end_pool, s390_add_pool_insn, s390_find_pool, s390_add_constant, s390_find_constant, s390_add_execute, s390_find_execute, s390_execute_label, s390_execute_target, s390_dump_pool, s390_dump_execute, s390_alloc_pool, s390_free_pool): Remove prototypes. Move target macro definitions to the end of the file. Move several functions within the file to assure correct compiling. From-SVN: r99462 --- gcc/ChangeLog | 34 + gcc/config/s390/s390.c | 3562 +++++++++++++++++++++++------------------------- 2 files changed, 1774 insertions(+), 1822 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 49abfdd..5ed9ea9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,37 @@ +2005-05-09 Adrian Straetling + + * config/s390/s390.c: (s390_match_ccmode_set, + s390_branch_condition_mnemonic, s390_short_displacement, + s390_decompose_address, get_thread_pointer, legitimize_tls_address, + print_shift_count_operand, get_some_local_dynamic_name, + get_some_local_dynamic_name_1, reg_used_in_mem_p, + addr_generation_dependency_p, s390_split_branches, + annotate_constant_pool_refs, find_constant_pool_ref, + replace_constant_pool_ref, find_ltrel_base, replace_ltrel_base, + s390_optimize_prologue, find_unused_clobbered_reg, s390_frame_area, + s390_register_info, s390_frame_info, s390_init_frame_layout, + s390_update_frame_layout, save_fpr, restore_fpr, save_gprs, + restore_gprs, s390_function_arg_size, s390_function_arg_float, + s390_init_machine_status, s390_assemble_integer, s390_handle_option, + s390_encode_section_info, s390_cannot_force_const_mem, + s390_delegitimize_address, s390_return_in_memory, s390_init_builtins, + s390_expand_builtin, s390_output_mi_thunk, s390_safe_attr_type, + s390_adjust_priority, s390_issue_rate, + s390_first_cycle_multipass_dfa_lookahead, s390_cannot_copy_insn_p, + s390_rtx_costs, s390_address_cost, s390_reorg, s390_valid_pointer_mode, + s390_build_builtin_va_list, s390_gimplify_va_arg, + s390_function_ok_for_sibcall, s390_call_saved_register_used, + s390_pass_by_reference, s390_fixed_condition_code_regs, + s390_cc_modes_compatible s390_mainpool_start,, s390_mainpool_finish, + s390_mainpool_cancel, s390_chunkify_start, s390_chunkify_finish, + s390_chunkify_cancel, s390_start_pool, s390_end_pool, + s390_add_pool_insn, s390_find_pool, s390_add_constant, + s390_find_constant, s390_add_execute, s390_find_execute, + s390_execute_label, s390_execute_target, s390_dump_pool, + s390_dump_execute, s390_alloc_pool, s390_free_pool): Remove prototypes. + Move target macro definitions to the end of the file. Move several + functions within the file to assure correct compiling. + 2005-05-09 Steven Bosscher * calls.c (purge_reg_equiv_notes): Remove, fold into... diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 69adbfe..35cd86d 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -53,37 +53,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "tree-gimple.h" -static bool s390_assemble_integer (rtx, unsigned int, int); -static bool s390_handle_option (size_t, const char *, int); -static void s390_encode_section_info (tree, rtx, int); -static bool s390_cannot_force_const_mem (rtx); -static rtx s390_delegitimize_address (rtx); -static bool s390_return_in_memory (tree, tree); -static void s390_init_builtins (void); -static rtx s390_expand_builtin (tree, rtx, rtx, enum machine_mode, int); -static void s390_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, - HOST_WIDE_INT, tree); -static enum attr_type s390_safe_attr_type (rtx); - -static int s390_adjust_priority (rtx, int); -static int s390_issue_rate (void); -static int s390_first_cycle_multipass_dfa_lookahead (void); -static bool s390_cannot_copy_insn_p (rtx); -static bool s390_rtx_costs (rtx, int, int, int *); -static int s390_address_cost (rtx); -static void s390_reorg (void); -static bool s390_valid_pointer_mode (enum machine_mode); -static tree s390_build_builtin_va_list (void); -static tree s390_gimplify_va_arg (tree, tree, tree *, tree *); -static bool s390_function_ok_for_sibcall (tree, tree); -static bool s390_call_saved_register_used (tree); -static bool s390_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode, - tree, bool); -static bool s390_fixed_condition_code_regs (unsigned int *, unsigned int *); -static enum machine_mode s390_cc_modes_compatible (enum machine_mode, - enum machine_mode); - - /* Define the specific costs for a given cpu. */ struct processor_costs @@ -185,93 +154,6 @@ struct processor_costs z990_cost = }; -#undef TARGET_ASM_ALIGNED_HI_OP -#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" -#undef TARGET_ASM_ALIGNED_DI_OP -#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" -#undef TARGET_ASM_INTEGER -#define TARGET_ASM_INTEGER s390_assemble_integer - -#undef TARGET_ASM_OPEN_PAREN -#define TARGET_ASM_OPEN_PAREN "" - -#undef TARGET_ASM_CLOSE_PAREN -#define TARGET_ASM_CLOSE_PAREN "" - -#undef TARGET_DEFAULT_TARGET_FLAGS -#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD) -#undef TARGET_HANDLE_OPTION -#define TARGET_HANDLE_OPTION s390_handle_option - -#undef TARGET_ENCODE_SECTION_INFO -#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info - -#ifdef HAVE_AS_TLS -#undef TARGET_HAVE_TLS -#define TARGET_HAVE_TLS true -#endif -#undef TARGET_CANNOT_FORCE_CONST_MEM -#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem - -#undef TARGET_DELEGITIMIZE_ADDRESS -#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address - -#undef TARGET_RETURN_IN_MEMORY -#define TARGET_RETURN_IN_MEMORY s390_return_in_memory - -#undef TARGET_INIT_BUILTINS -#define TARGET_INIT_BUILTINS s390_init_builtins -#undef TARGET_EXPAND_BUILTIN -#define TARGET_EXPAND_BUILTIN s390_expand_builtin - -#undef TARGET_ASM_OUTPUT_MI_THUNK -#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk -#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK -#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true - -#undef TARGET_SCHED_ADJUST_PRIORITY -#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority -#undef TARGET_SCHED_ISSUE_RATE -#define TARGET_SCHED_ISSUE_RATE s390_issue_rate -#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD -#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead - -#undef TARGET_CANNOT_COPY_INSN_P -#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p -#undef TARGET_RTX_COSTS -#define TARGET_RTX_COSTS s390_rtx_costs -#undef TARGET_ADDRESS_COST -#define TARGET_ADDRESS_COST s390_address_cost - -#undef TARGET_MACHINE_DEPENDENT_REORG -#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg - -#undef TARGET_VALID_POINTER_MODE -#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode - -#undef TARGET_BUILD_BUILTIN_VA_LIST -#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list -#undef TARGET_GIMPLIFY_VA_ARG_EXPR -#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg - -#undef TARGET_PROMOTE_FUNCTION_ARGS -#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true -#undef TARGET_PROMOTE_FUNCTION_RETURN -#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true -#undef TARGET_PASS_BY_REFERENCE -#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference - -#undef TARGET_FUNCTION_OK_FOR_SIBCALL -#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall - -#undef TARGET_FIXED_CONDITION_CODE_REGS -#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs - -#undef TARGET_CC_MODES_COMPATIBLE -#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible - -struct gcc_target targetm = TARGET_INITIALIZER; - extern int reload_completed; /* The alias set for prologue/epilogue register save/restore. */ @@ -373,53 +255,6 @@ struct machine_function GTY(()) #define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \ (1 << (BITNUM)))) -static int s390_match_ccmode_set (rtx, enum machine_mode); -static const char *s390_branch_condition_mnemonic (rtx, int); -static int s390_short_displacement (rtx); -static int s390_decompose_address (rtx, struct s390_address *); -static rtx get_thread_pointer (void); -static rtx legitimize_tls_address (rtx, rtx); -static void print_shift_count_operand (FILE *, rtx); -static const char *get_some_local_dynamic_name (void); -static int get_some_local_dynamic_name_1 (rtx *, void *); -static int reg_used_in_mem_p (int, rtx); -static int addr_generation_dependency_p (rtx, rtx); -static int s390_split_branches (void); -static void annotate_constant_pool_refs (rtx *x); -static void find_constant_pool_ref (rtx, rtx *); -static void replace_constant_pool_ref (rtx *, rtx, rtx); -static rtx find_ltrel_base (rtx); -static void replace_ltrel_base (rtx *); -static void s390_optimize_prologue (void); -static int find_unused_clobbered_reg (void); -static void s390_frame_area (int *, int *); -static void s390_register_info (int []); -static void s390_frame_info (void); -static void s390_init_frame_layout (void); -static void s390_update_frame_layout (void); -static rtx save_fpr (rtx, int, int); -static rtx restore_fpr (rtx, int, int); -static rtx save_gprs (rtx, int, int, int); -static rtx restore_gprs (rtx, int, int, int); -static int s390_function_arg_size (enum machine_mode, tree); -static bool s390_function_arg_float (enum machine_mode, tree); -static struct machine_function * s390_init_machine_status (void); - -/* Return true if CODE is a valid address without index. */ - -bool -s390_legitimate_address_without_index_p (rtx op) -{ - struct s390_address addr; - - if (!s390_decompose_address (XEXP (op, 0), &addr)) - return false; - if (addr.indx) - return false; - - return true; -} - /* Return true if SET either doesn't set the CC register, or else the source and destination have matching CC modes and that CC mode is at least as constrained as REQ_MODE. */ @@ -1222,6 +1057,14 @@ s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop) } +/* How to allocate a 'struct machine_function'. */ + +static struct machine_function * +s390_init_machine_status (void) +{ + return ggc_alloc_cleared (sizeof (struct machine_function)); +} + /* Change optimizations to be performed, depending on the optimization level. @@ -1426,217 +1269,504 @@ s390_short_displacement (rtx disp) return 0; } -/* Return true if OP is a valid operand for a C constraint. */ +/* Decompose a RTL expression ADDR for a memory address into + its components, returned in OUT. -int -s390_extra_constraint_str (rtx op, int c, const char * str) -{ - struct s390_address addr; + Returns 0 if ADDR is not a valid memory address, nonzero + otherwise. If OUT is NULL, don't return the components, + but check for validity only. - if (c != str[0]) - abort (); + Note: Only addresses in canonical form are recognized. + LEGITIMIZE_ADDRESS should convert non-canonical forms to the + canonical form so that they will be recognized. */ - /* Check for offsettable variants of memory constraints. */ - if (c == 'A') - { - /* Only accept non-volatile MEMs. */ - if (!MEM_P (op) || MEM_VOLATILE_P (op)) - return 0; +static int +s390_decompose_address (register rtx addr, struct s390_address *out) +{ + HOST_WIDE_INT offset = 0; + rtx base = NULL_RTX; + rtx indx = NULL_RTX; + rtx disp = NULL_RTX; + rtx orig_disp; + int pointer = FALSE; + int base_ptr = FALSE; + int indx_ptr = FALSE; - if ((reload_completed || reload_in_progress) - ? !offsettable_memref_p (op) - : !offsettable_nonstrict_memref_p (op)) - return 0; + /* Decompose address into base + index + displacement. */ - c = str[1]; - } + if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC) + base = addr; - /* Check for non-literal-pool variants of memory constraints. */ - else if (c == 'B') + else if (GET_CODE (addr) == PLUS) { - if (GET_CODE (op) != MEM) - return 0; - if (!s390_decompose_address (XEXP (op, 0), &addr)) - return 0; - if (addr.base && REG_P (addr.base) && REGNO (addr.base) == BASE_REGNUM) - return 0; - if (addr.indx && REG_P (addr.indx) && REGNO (addr.indx) == BASE_REGNUM) - return 0; + rtx op0 = XEXP (addr, 0); + rtx op1 = XEXP (addr, 1); + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); - c = str[1]; - } + if (code0 == REG || code0 == UNSPEC) + { + if (code1 == REG || code1 == UNSPEC) + { + indx = op0; /* index + base */ + base = op1; + } - switch (c) - { - case 'Q': - if (GET_CODE (op) != MEM) - return 0; - if (!s390_decompose_address (XEXP (op, 0), &addr)) - return 0; - if (addr.indx) - return 0; + else + { + base = op0; /* base + displacement */ + disp = op1; + } + } - if (TARGET_LONG_DISPLACEMENT) + else if (code0 == PLUS) { - if (!s390_short_displacement (addr.disp)) - return 0; + indx = XEXP (op0, 0); /* index + base + disp */ + base = XEXP (op0, 1); + disp = op1; } - break; - - case 'R': - if (GET_CODE (op) != MEM) - return 0; - if (TARGET_LONG_DISPLACEMENT) + else { - if (!s390_decompose_address (XEXP (op, 0), &addr)) - return 0; - if (!s390_short_displacement (addr.disp)) - return 0; + return FALSE; } - break; - - case 'S': - if (!TARGET_LONG_DISPLACEMENT) - return 0; - if (GET_CODE (op) != MEM) - return 0; - if (!s390_decompose_address (XEXP (op, 0), &addr)) - return 0; - if (addr.indx) - return 0; - if (s390_short_displacement (addr.disp)) - return 0; - break; + } - case 'T': - if (!TARGET_LONG_DISPLACEMENT) - return 0; - if (GET_CODE (op) != MEM) - return 0; - /* Any invalid address here will be fixed up by reload, - so accept it for the most generic constraint. */ - if (s390_decompose_address (XEXP (op, 0), &addr) - && s390_short_displacement (addr.disp)) - return 0; - break; + else + disp = addr; /* displacement */ - case 'U': - if (TARGET_LONG_DISPLACEMENT) + /* Extract integer part of displacement. */ + orig_disp = disp; + if (disp) + { + if (GET_CODE (disp) == CONST_INT) { - if (!s390_decompose_address (op, &addr)) - return 0; - if (!s390_short_displacement (addr.disp)) - return 0; + offset = INTVAL (disp); + disp = NULL_RTX; } - break; - - case 'W': - if (!TARGET_LONG_DISPLACEMENT) - return 0; - /* Any invalid address here will be fixed up by reload, - so accept it for the most generic constraint. */ - if (s390_decompose_address (op, &addr) - && s390_short_displacement (addr.disp)) - return 0; - break; + else if (GET_CODE (disp) == CONST + && GET_CODE (XEXP (disp, 0)) == PLUS + && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT) + { + offset = INTVAL (XEXP (XEXP (disp, 0), 1)); + disp = XEXP (XEXP (disp, 0), 0); + } + } - case 'Y': - return shift_count_operand (op, VOIDmode); + /* Strip off CONST here to avoid special case tests later. */ + if (disp && GET_CODE (disp) == CONST) + disp = XEXP (disp, 0); - default: - return 0; + /* We can convert literal pool addresses to + displacements by basing them off the base register. */ + if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp)) + { + /* Either base or index must be free to hold the base register. */ + if (!base) + base = gen_rtx_REG (Pmode, BASE_REGNUM); + else if (!indx) + indx = gen_rtx_REG (Pmode, BASE_REGNUM); + else + return FALSE; + + /* Mark up the displacement. */ + disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), + UNSPEC_LTREL_OFFSET); } - return 1; -} + /* Validate base register. */ + if (base) + { + if (GET_CODE (base) == UNSPEC) + switch (XINT (base, 1)) + { + case UNSPEC_LTREF: + if (!disp) + disp = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, XVECEXP (base, 0, 0)), + UNSPEC_LTREL_OFFSET); + else + return FALSE; -/* Return true if VALUE matches the constraint STR. */ + base = gen_rtx_REG (Pmode, BASE_REGNUM); + break; -int -s390_const_ok_for_constraint_p (HOST_WIDE_INT value, - int c, - const char * str) -{ - enum machine_mode mode, part_mode; - int def; - int part, part_goal; + case UNSPEC_LTREL_BASE: + base = gen_rtx_REG (Pmode, BASE_REGNUM); + break; - if (c != str[0]) - abort (); + default: + return FALSE; + } - switch (str[0]) + if (GET_CODE (base) != REG || GET_MODE (base) != Pmode) + return FALSE; + + if (REGNO (base) == BASE_REGNUM + || REGNO (base) == STACK_POINTER_REGNUM + || REGNO (base) == FRAME_POINTER_REGNUM + || ((reload_completed || reload_in_progress) + && frame_pointer_needed + && REGNO (base) == HARD_FRAME_POINTER_REGNUM) + || REGNO (base) == ARG_POINTER_REGNUM + || (flag_pic + && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) + pointer = base_ptr = TRUE; + } + + /* Validate index register. */ + if (indx) { - case 'I': - return (unsigned int)value < 256; + if (GET_CODE (indx) == UNSPEC) + switch (XINT (indx, 1)) + { + case UNSPEC_LTREF: + if (!disp) + disp = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, XVECEXP (indx, 0, 0)), + UNSPEC_LTREL_OFFSET); + else + return FALSE; - case 'J': - return (unsigned int)value < 4096; + indx = gen_rtx_REG (Pmode, BASE_REGNUM); + break; - case 'K': - return value >= -32768 && value < 32768; + case UNSPEC_LTREL_BASE: + indx = gen_rtx_REG (Pmode, BASE_REGNUM); + break; - case 'L': - return (TARGET_LONG_DISPLACEMENT ? - (value >= -524288 && value <= 524287) - : (value >= 0 && value <= 4095)); - case 'M': - return value == 2147483647; + default: + return FALSE; + } - case 'N': - if (str[1] == 'x') - part_goal = -1; - else - part_goal = str[1] - '0'; + if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode) + return FALSE; - switch (str[2]) - { - case 'H': part_mode = HImode; break; - case 'Q': part_mode = QImode; break; - default: return 0; - } + if (REGNO (indx) == BASE_REGNUM + || REGNO (indx) == STACK_POINTER_REGNUM + || REGNO (indx) == FRAME_POINTER_REGNUM + || ((reload_completed || reload_in_progress) + && frame_pointer_needed + && REGNO (indx) == HARD_FRAME_POINTER_REGNUM) + || REGNO (indx) == ARG_POINTER_REGNUM + || (flag_pic + && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) + pointer = indx_ptr = TRUE; + } - switch (str[3]) - { - case 'H': mode = HImode; break; - case 'S': mode = SImode; break; - case 'D': mode = DImode; break; - default: return 0; - } + /* Prefer to use pointer as base, not index. */ + if (base && indx && !base_ptr + && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx)))) + { + rtx tmp = base; + base = indx; + indx = tmp; + } - switch (str[4]) - { - case '0': def = 0; break; - case 'F': def = -1; break; - default: return 0; - } + /* Validate displacement. */ + if (!disp) + { + /* If the argument pointer or the return address pointer are involved, + the displacement will change later anyway as the virtual registers get + eliminated. This could make a valid displacement invalid, but it is + more likely to make an invalid displacement valid, because we sometimes + access the register save area via negative offsets to one of those + registers. + Thus we don't check the displacement for validity here. If after + elimination the displacement turns out to be invalid after all, + this is fixed up by reload in any case. */ + if (base != arg_pointer_rtx + && indx != arg_pointer_rtx + && base != return_address_pointer_rtx + && indx != return_address_pointer_rtx) + if (!DISP_IN_RANGE (offset)) + return FALSE; + } + else + { + /* All the special cases are pointers. */ + pointer = TRUE; - if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode)) - return 0; + /* In the small-PIC case, the linker converts @GOT + and @GOTNTPOFF offsets to possible displacements. */ + if (GET_CODE (disp) == UNSPEC + && (XINT (disp, 1) == UNSPEC_GOT + || XINT (disp, 1) == UNSPEC_GOTNTPOFF) + && offset == 0 + && flag_pic == 1) + { + ; + } - part = s390_single_part (GEN_INT (value), mode, part_mode, def); - if (part < 0) - return 0; - if (part_goal != -1 && part_goal != part) - return 0; + /* Accept chunkified literal pool symbol references. */ + else if (GET_CODE (disp) == MINUS + && GET_CODE (XEXP (disp, 0)) == LABEL_REF + && GET_CODE (XEXP (disp, 1)) == LABEL_REF) + { + ; + } - break; + /* Accept literal pool references. */ + else if (GET_CODE (disp) == UNSPEC + && XINT (disp, 1) == UNSPEC_LTREL_OFFSET) + { + orig_disp = gen_rtx_CONST (Pmode, disp); + if (offset) + { + /* If we have an offset, make sure it does not + exceed the size of the constant pool entry. */ + rtx sym = XVECEXP (disp, 0, 0); + if (offset >= GET_MODE_SIZE (get_pool_mode (sym))) + return FALSE; - default: - return 0; + orig_disp = plus_constant (orig_disp, offset); + } + } + + else + return FALSE; } - return 1; + if (!base && !indx) + pointer = TRUE; + + if (out) + { + out->base = base; + out->indx = indx; + out->disp = orig_disp; + out->pointer = pointer; + } + + return TRUE; } -/* Compute a (partial) cost for rtx X. Return true if the complete - cost has been computed, and false if subexpressions should be - scanned. In either case, *TOTAL contains the cost result. - CODE contains GET_CODE (x), OUTER_CODE contains the code - of the superexpression of x. */ +/* Return true if CODE is a valid address without index. */ -static bool -s390_rtx_costs (rtx x, int code, int outer_code, int *total) +bool +s390_legitimate_address_without_index_p (rtx op) +{ + struct s390_address addr; + + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return false; + if (addr.indx) + return false; + + return true; +} + +/* Return true if OP is a valid operand for a C constraint. */ + +int +s390_extra_constraint_str (rtx op, int c, const char * str) +{ + struct s390_address addr; + + if (c != str[0]) + abort (); + + /* Check for offsettable variants of memory constraints. */ + if (c == 'A') + { + /* Only accept non-volatile MEMs. */ + if (!MEM_P (op) || MEM_VOLATILE_P (op)) + return 0; + + if ((reload_completed || reload_in_progress) + ? !offsettable_memref_p (op) + : !offsettable_nonstrict_memref_p (op)) + return 0; + + c = str[1]; + } + + /* Check for non-literal-pool variants of memory constraints. */ + else if (c == 'B') + { + if (GET_CODE (op) != MEM) + return 0; + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return 0; + if (addr.base && REG_P (addr.base) && REGNO (addr.base) == BASE_REGNUM) + return 0; + if (addr.indx && REG_P (addr.indx) && REGNO (addr.indx) == BASE_REGNUM) + return 0; + + c = str[1]; + } + + switch (c) + { + case 'Q': + if (GET_CODE (op) != MEM) + return 0; + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return 0; + if (addr.indx) + return 0; + + if (TARGET_LONG_DISPLACEMENT) + { + if (!s390_short_displacement (addr.disp)) + return 0; + } + break; + + case 'R': + if (GET_CODE (op) != MEM) + return 0; + + if (TARGET_LONG_DISPLACEMENT) + { + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return 0; + if (!s390_short_displacement (addr.disp)) + return 0; + } + break; + + case 'S': + if (!TARGET_LONG_DISPLACEMENT) + return 0; + if (GET_CODE (op) != MEM) + return 0; + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return 0; + if (addr.indx) + return 0; + if (s390_short_displacement (addr.disp)) + return 0; + break; + + case 'T': + if (!TARGET_LONG_DISPLACEMENT) + return 0; + if (GET_CODE (op) != MEM) + return 0; + /* Any invalid address here will be fixed up by reload, + so accept it for the most generic constraint. */ + if (s390_decompose_address (XEXP (op, 0), &addr) + && s390_short_displacement (addr.disp)) + return 0; + break; + + case 'U': + if (TARGET_LONG_DISPLACEMENT) + { + if (!s390_decompose_address (op, &addr)) + return 0; + if (!s390_short_displacement (addr.disp)) + return 0; + } + break; + + case 'W': + if (!TARGET_LONG_DISPLACEMENT) + return 0; + /* Any invalid address here will be fixed up by reload, + so accept it for the most generic constraint. */ + if (s390_decompose_address (op, &addr) + && s390_short_displacement (addr.disp)) + return 0; + break; + + case 'Y': + return shift_count_operand (op, VOIDmode); + + default: + return 0; + } + + return 1; +} + +/* Return true if VALUE matches the constraint STR. */ + +int +s390_const_ok_for_constraint_p (HOST_WIDE_INT value, + int c, + const char * str) +{ + enum machine_mode mode, part_mode; + int def; + int part, part_goal; + + if (c != str[0]) + abort (); + + switch (str[0]) + { + case 'I': + return (unsigned int)value < 256; + + case 'J': + return (unsigned int)value < 4096; + + case 'K': + return value >= -32768 && value < 32768; + + case 'L': + return (TARGET_LONG_DISPLACEMENT ? + (value >= -524288 && value <= 524287) + : (value >= 0 && value <= 4095)); + case 'M': + return value == 2147483647; + + case 'N': + if (str[1] == 'x') + part_goal = -1; + else + part_goal = str[1] - '0'; + + switch (str[2]) + { + case 'H': part_mode = HImode; break; + case 'Q': part_mode = QImode; break; + default: return 0; + } + + switch (str[3]) + { + case 'H': mode = HImode; break; + case 'S': mode = SImode; break; + case 'D': mode = DImode; break; + default: return 0; + } + + switch (str[4]) + { + case '0': def = 0; break; + case 'F': def = -1; break; + default: return 0; + } + + if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode)) + return 0; + + part = s390_single_part (GEN_INT (value), mode, part_mode, def); + if (part < 0) + return 0; + if (part_goal != -1 && part_goal != part) + return 0; + + break; + + default: + return 0; + } + + return 1; +} + +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. + CODE contains GET_CODE (x), OUTER_CODE contains the code + of the superexpression of x. */ + +static bool +s390_rtx_costs (rtx x, int code, int outer_code, int *total) { switch (code) { @@ -2213,288 +2343,16 @@ s390_expand_plus_operand (register rtx target, register rtx src, } -/* Decompose a RTL expression ADDR for a memory address into - its components, returned in OUT. - - Returns 0 if ADDR is not a valid memory address, nonzero - otherwise. If OUT is NULL, don't return the components, - but check for validity only. - - Note: Only addresses in canonical form are recognized. - LEGITIMIZE_ADDRESS should convert non-canonical forms to the - canonical form so that they will be recognized. */ +/* Return nonzero if ADDR is a valid memory address. + STRICT specifies whether strict register checking applies. */ -static int -s390_decompose_address (register rtx addr, struct s390_address *out) +int +legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, + register rtx addr, int strict) { - HOST_WIDE_INT offset = 0; - rtx base = NULL_RTX; - rtx indx = NULL_RTX; - rtx disp = NULL_RTX; - rtx orig_disp; - int pointer = FALSE; - int base_ptr = FALSE; - int indx_ptr = FALSE; - - /* Decompose address into base + index + displacement. */ - - if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC) - base = addr; - - else if (GET_CODE (addr) == PLUS) - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - enum rtx_code code0 = GET_CODE (op0); - enum rtx_code code1 = GET_CODE (op1); - - if (code0 == REG || code0 == UNSPEC) - { - if (code1 == REG || code1 == UNSPEC) - { - indx = op0; /* index + base */ - base = op1; - } - - else - { - base = op0; /* base + displacement */ - disp = op1; - } - } - - else if (code0 == PLUS) - { - indx = XEXP (op0, 0); /* index + base + disp */ - base = XEXP (op0, 1); - disp = op1; - } - - else - { - return FALSE; - } - } - - else - disp = addr; /* displacement */ - - /* Extract integer part of displacement. */ - orig_disp = disp; - if (disp) - { - if (GET_CODE (disp) == CONST_INT) - { - offset = INTVAL (disp); - disp = NULL_RTX; - } - else if (GET_CODE (disp) == CONST - && GET_CODE (XEXP (disp, 0)) == PLUS - && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT) - { - offset = INTVAL (XEXP (XEXP (disp, 0), 1)); - disp = XEXP (XEXP (disp, 0), 0); - } - } - - /* Strip off CONST here to avoid special case tests later. */ - if (disp && GET_CODE (disp) == CONST) - disp = XEXP (disp, 0); - - /* We can convert literal pool addresses to - displacements by basing them off the base register. */ - if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp)) - { - /* Either base or index must be free to hold the base register. */ - if (!base) - base = gen_rtx_REG (Pmode, BASE_REGNUM); - else if (!indx) - indx = gen_rtx_REG (Pmode, BASE_REGNUM); - else - return FALSE; - - /* Mark up the displacement. */ - disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), - UNSPEC_LTREL_OFFSET); - } - - /* Validate base register. */ - if (base) - { - if (GET_CODE (base) == UNSPEC) - switch (XINT (base, 1)) - { - case UNSPEC_LTREF: - if (!disp) - disp = gen_rtx_UNSPEC (Pmode, - gen_rtvec (1, XVECEXP (base, 0, 0)), - UNSPEC_LTREL_OFFSET); - else - return FALSE; - - base = gen_rtx_REG (Pmode, BASE_REGNUM); - break; - - case UNSPEC_LTREL_BASE: - base = gen_rtx_REG (Pmode, BASE_REGNUM); - break; - - default: - return FALSE; - } - - if (GET_CODE (base) != REG || GET_MODE (base) != Pmode) - return FALSE; - - if (REGNO (base) == BASE_REGNUM - || REGNO (base) == STACK_POINTER_REGNUM - || REGNO (base) == FRAME_POINTER_REGNUM - || ((reload_completed || reload_in_progress) - && frame_pointer_needed - && REGNO (base) == HARD_FRAME_POINTER_REGNUM) - || REGNO (base) == ARG_POINTER_REGNUM - || (flag_pic - && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) - pointer = base_ptr = TRUE; - } - - /* Validate index register. */ - if (indx) - { - if (GET_CODE (indx) == UNSPEC) - switch (XINT (indx, 1)) - { - case UNSPEC_LTREF: - if (!disp) - disp = gen_rtx_UNSPEC (Pmode, - gen_rtvec (1, XVECEXP (indx, 0, 0)), - UNSPEC_LTREL_OFFSET); - else - return FALSE; - - indx = gen_rtx_REG (Pmode, BASE_REGNUM); - break; - - case UNSPEC_LTREL_BASE: - indx = gen_rtx_REG (Pmode, BASE_REGNUM); - break; - - default: - return FALSE; - } - - if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode) - return FALSE; - - if (REGNO (indx) == BASE_REGNUM - || REGNO (indx) == STACK_POINTER_REGNUM - || REGNO (indx) == FRAME_POINTER_REGNUM - || ((reload_completed || reload_in_progress) - && frame_pointer_needed - && REGNO (indx) == HARD_FRAME_POINTER_REGNUM) - || REGNO (indx) == ARG_POINTER_REGNUM - || (flag_pic - && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) - pointer = indx_ptr = TRUE; - } - - /* Prefer to use pointer as base, not index. */ - if (base && indx && !base_ptr - && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx)))) - { - rtx tmp = base; - base = indx; - indx = tmp; - } - - /* Validate displacement. */ - if (!disp) - { - /* If the argument pointer or the return address pointer are involved, - the displacement will change later anyway as the virtual registers get - eliminated. This could make a valid displacement invalid, but it is - more likely to make an invalid displacement valid, because we sometimes - access the register save area via negative offsets to one of those - registers. - Thus we don't check the displacement for validity here. If after - elimination the displacement turns out to be invalid after all, - this is fixed up by reload in any case. */ - if (base != arg_pointer_rtx - && indx != arg_pointer_rtx - && base != return_address_pointer_rtx - && indx != return_address_pointer_rtx) - if (!DISP_IN_RANGE (offset)) - return FALSE; - } - else - { - /* All the special cases are pointers. */ - pointer = TRUE; - - /* In the small-PIC case, the linker converts @GOT - and @GOTNTPOFF offsets to possible displacements. */ - if (GET_CODE (disp) == UNSPEC - && (XINT (disp, 1) == UNSPEC_GOT - || XINT (disp, 1) == UNSPEC_GOTNTPOFF) - && offset == 0 - && flag_pic == 1) - { - ; - } - - /* Accept chunkified literal pool symbol references. */ - else if (GET_CODE (disp) == MINUS - && GET_CODE (XEXP (disp, 0)) == LABEL_REF - && GET_CODE (XEXP (disp, 1)) == LABEL_REF) - { - ; - } - - /* Accept literal pool references. */ - else if (GET_CODE (disp) == UNSPEC - && XINT (disp, 1) == UNSPEC_LTREL_OFFSET) - { - orig_disp = gen_rtx_CONST (Pmode, disp); - if (offset) - { - /* If we have an offset, make sure it does not - exceed the size of the constant pool entry. */ - rtx sym = XVECEXP (disp, 0, 0); - if (offset >= GET_MODE_SIZE (get_pool_mode (sym))) - return FALSE; - - orig_disp = plus_constant (orig_disp, offset); - } - } - - else - return FALSE; - } - - if (!base && !indx) - pointer = TRUE; - - if (out) - { - out->base = base; - out->indx = indx; - out->disp = orig_disp; - out->pointer = pointer; - } - - return TRUE; -} - -/* Return nonzero if ADDR is a valid memory address. - STRICT specifies whether strict register checking applies. */ - -int -legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, - register rtx addr, int strict) -{ - struct s390_address ad; - if (!s390_decompose_address (addr, &ad)) - return FALSE; + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return FALSE; if (strict) { @@ -3763,24 +3621,7 @@ print_shift_count_operand (FILE *file, rtx op) fprintf (file, "(%s)", reg_names[REGNO (op)]); } -/* Locate some local-dynamic symbol still in use by this function - so that we can print its name in local-dynamic base patterns. */ - -static const char * -get_some_local_dynamic_name (void) -{ - rtx insn; - - if (cfun->machine->some_ld_name) - return cfun->machine->some_ld_name; - - for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) - if (INSN_P (insn) - && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) - return cfun->machine->some_ld_name; - - abort (); -} +/* See 'get_some_local_dynamic_name'. */ static int get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) @@ -3803,6 +3644,25 @@ get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) return 0; } +/* Locate some local-dynamic symbol still in use by this function + so that we can print its name in local-dynamic base patterns. */ + +static const char * +get_some_local_dynamic_name (void) +{ + rtx insn; + + if (cfun->machine->some_ld_name) + return cfun->machine->some_ld_name; + + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) + return cfun->machine->some_ld_name; + + abort (); +} + /* Output machine-dependent UNSPECs occurring in address constant X in assembler syntax to stdio stream FILE. Returns true if the constant X could be recognized, false otherwise. */ @@ -4242,91 +4102,6 @@ s390_first_cycle_multipass_dfa_lookahead (void) } -/* Split all branches that exceed the maximum distance. - Returns true if this created a new literal pool entry. */ - -static int -s390_split_branches (void) -{ - rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); - int new_literal = 0; - rtx insn, pat, tmp, target; - rtx *label; - - /* We need correct insn addresses. */ - - shorten_branches (get_insns ()); - - /* Find all branches that exceed 64KB, and split them. */ - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) != JUMP_INSN) - continue; - - pat = PATTERN (insn); - if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) - pat = XVECEXP (pat, 0, 0); - if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx) - continue; - - if (GET_CODE (SET_SRC (pat)) == LABEL_REF) - { - label = &SET_SRC (pat); - } - else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) - { - if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - label = &XEXP (SET_SRC (pat), 1); - else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) - label = &XEXP (SET_SRC (pat), 2); - else - continue; - } - else - continue; - - if (get_attr_length (insn) <= 4) - continue; - - /* We are going to use the return register as scratch register, - make sure it will be saved/restored by the prologue/epilogue. */ - cfun_frame_layout.save_return_addr_p = 1; - - if (!flag_pic) - { - new_literal = 1; - tmp = force_const_mem (Pmode, *label); - tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); - INSN_ADDRESSES_NEW (tmp, -1); - annotate_constant_pool_refs (&PATTERN (tmp)); - - target = temp_reg; - } - else - { - new_literal = 1; - target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label), - UNSPEC_LTREL_OFFSET); - target = gen_rtx_CONST (Pmode, target); - target = force_const_mem (Pmode, target); - tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); - INSN_ADDRESSES_NEW (tmp, -1); - annotate_constant_pool_refs (&PATTERN (tmp)); - - target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0), - cfun->machine->base_reg), - UNSPEC_LTREL_BASE); - target = gen_rtx_PLUS (Pmode, temp_reg, target); - } - - if (!validate_change (insn, label, target, 0)) - abort (); - } - - return new_literal; -} - /* Annotate every literal pool reference in X by an UNSPEC_LTREF expression. Fix up MEMs as required. */ @@ -4431,6 +4206,91 @@ annotate_constant_pool_refs (rtx *x) } } +/* Split all branches that exceed the maximum distance. + Returns true if this created a new literal pool entry. */ + +static int +s390_split_branches (void) +{ + rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + int new_literal = 0; + rtx insn, pat, tmp, target; + rtx *label; + + /* We need correct insn addresses. */ + + shorten_branches (get_insns ()); + + /* Find all branches that exceed 64KB, and split them. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) != JUMP_INSN) + continue; + + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx) + continue; + + if (GET_CODE (SET_SRC (pat)) == LABEL_REF) + { + label = &SET_SRC (pat); + } + else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) + { + if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) + label = &XEXP (SET_SRC (pat), 1); + else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) + label = &XEXP (SET_SRC (pat), 2); + else + continue; + } + else + continue; + + if (get_attr_length (insn) <= 4) + continue; + + /* We are going to use the return register as scratch register, + make sure it will be saved/restored by the prologue/epilogue. */ + cfun_frame_layout.save_return_addr_p = 1; + + if (!flag_pic) + { + new_literal = 1; + tmp = force_const_mem (Pmode, *label); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); + INSN_ADDRESSES_NEW (tmp, -1); + annotate_constant_pool_refs (&PATTERN (tmp)); + + target = temp_reg; + } + else + { + new_literal = 1; + target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label), + UNSPEC_LTREL_OFFSET); + target = gen_rtx_CONST (Pmode, target); + target = force_const_mem (Pmode, target); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); + INSN_ADDRESSES_NEW (tmp, -1); + annotate_constant_pool_refs (&PATTERN (tmp)); + + target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0), + cfun->machine->base_reg), + UNSPEC_LTREL_BASE); + target = gen_rtx_PLUS (Pmode, temp_reg, target); + } + + if (!validate_change (insn, label, target, 0)) + abort (); + } + + return new_literal; +} + /* Find an annotated literal pool symbol referenced in RTX X, and store it at REF. Will abort if X contains references to @@ -4635,28 +4495,28 @@ struct constant_pool int size; }; -static struct constant_pool * s390_mainpool_start (void); -static void s390_mainpool_finish (struct constant_pool *); -static void s390_mainpool_cancel (struct constant_pool *); - -static struct constant_pool * s390_chunkify_start (void); -static void s390_chunkify_finish (struct constant_pool *); -static void s390_chunkify_cancel (struct constant_pool *); - -static struct constant_pool *s390_start_pool (struct constant_pool **, rtx); -static void s390_end_pool (struct constant_pool *, rtx); -static void s390_add_pool_insn (struct constant_pool *, rtx); -static struct constant_pool *s390_find_pool (struct constant_pool *, rtx); -static void s390_add_constant (struct constant_pool *, rtx, enum machine_mode); -static rtx s390_find_constant (struct constant_pool *, rtx, enum machine_mode); -static void s390_add_execute (struct constant_pool *, rtx); -static rtx s390_find_execute (struct constant_pool *, rtx); -static rtx s390_execute_label (rtx); -static rtx s390_execute_target (rtx); -static void s390_dump_pool (struct constant_pool *, bool); -static void s390_dump_execute (struct constant_pool *); -static struct constant_pool *s390_alloc_pool (void); -static void s390_free_pool (struct constant_pool *); +/* Allocate new constant_pool structure. */ + +static struct constant_pool * +s390_alloc_pool (void) +{ + struct constant_pool *pool; + int i; + + pool = (struct constant_pool *) xmalloc (sizeof *pool); + pool->next = NULL; + for (i = 0; i < NR_C_MODES; i++) + pool->constants[i] = NULL; + + pool->execute = NULL; + pool->label = gen_label_rtx (); + pool->first_insn = NULL_RTX; + pool->pool_insn = NULL_RTX; + pool->insns = BITMAP_ALLOC (NULL); + pool->size = 0; + + return pool; +} /* Create new constant pool covering instructions starting at INSN and chain it to the end of POOL_LIST. */ @@ -4773,6 +4633,21 @@ s390_find_constant (struct constant_pool *pool, rtx val, return offset; } +/* Check whether INSN is an execute. Return the label_ref to its + execute target template if so, NULL_RTX otherwise. */ + +static rtx +s390_execute_label (rtx insn) +{ + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC + && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) + return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); + + return NULL_RTX; +} + /* Add execute target for INSN to the constant pool POOL. */ static void @@ -4821,28 +4696,13 @@ s390_find_execute (struct constant_pool *pool, rtx insn) return offset; } -/* Check whether INSN is an execute. Return the label_ref to its - execute target template if so, NULL_RTX otherwise. */ +/* For an execute INSN, extract the execute target template. */ static rtx -s390_execute_label (rtx insn) +s390_execute_target (rtx insn) { - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC - && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) - return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); - - return NULL_RTX; -} - -/* For an execute INSN, extract the execute target template. */ - -static rtx -s390_execute_target (rtx insn) -{ - rtx pattern = PATTERN (insn); - gcc_assert (s390_execute_label (insn)); + rtx pattern = PATTERN (insn); + gcc_assert (s390_execute_label (insn)); if (XVECLEN (pattern, 0) == 2) { @@ -4862,6 +4722,28 @@ s390_execute_target (rtx insn) return pattern; } +/* Dump out the out-of-pool execute template insns in POOL + at the end of the instruction stream. */ + +static void +s390_dump_execute (struct constant_pool *pool) +{ + struct constant *c; + rtx insn; + + for (c = pool->execute; c; c = c->next) + { + if (s390_execute_label (c->value) == const0_rtx) + continue; + + insn = emit_label (c->label); + INSN_ADDRESSES_NEW (insn, -1); + + insn = emit_insn (s390_execute_target (c->value)); + INSN_ADDRESSES_NEW (insn, -1); + } +} + /* Indicate that INSN cannot be duplicated. This is the case for execute insns that carry a unique label. */ @@ -4964,51 +4846,6 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label) s390_dump_execute (pool); } -/* Dump out the out-of-pool execute template insns in POOL - at the end of the instruction stream. */ - -static void -s390_dump_execute (struct constant_pool *pool) -{ - struct constant *c; - rtx insn; - - for (c = pool->execute; c; c = c->next) - { - if (s390_execute_label (c->value) == const0_rtx) - continue; - - insn = emit_label (c->label); - INSN_ADDRESSES_NEW (insn, -1); - - insn = emit_insn (s390_execute_target (c->value)); - INSN_ADDRESSES_NEW (insn, -1); - } -} - -/* Allocate new constant_pool structure. */ - -static struct constant_pool * -s390_alloc_pool (void) -{ - struct constant_pool *pool; - int i; - - pool = (struct constant_pool *) xmalloc (sizeof *pool); - pool->next = NULL; - for (i = 0; i < NR_C_MODES; i++) - pool->constants[i] = NULL; - - pool->execute = NULL; - pool->label = gen_label_rtx (); - pool->first_insn = NULL_RTX; - pool->pool_insn = NULL_RTX; - pool->insns = BITMAP_ALLOC (NULL); - pool->size = 0; - - return pool; -} - /* Free all memory used by POOL. */ static void @@ -5639,347 +5476,88 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align) } -/* Rework the prologue/epilogue to avoid saving/restoring - registers unnecessarily. */ +/* Return an RTL expression representing the value of the return address + for the frame COUNT steps up from the current frame. FRAME is the + frame pointer of that frame. */ -static void -s390_optimize_prologue (void) +rtx +s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) { - rtx insn, new_insn, next_insn; - - /* Do a final recompute of the frame-related data. */ - - s390_update_frame_layout (); + int offset; + rtx addr; - /* If all special registers are in fact used, there's nothing we - can do, so no point in walking the insn list. */ + /* Without backchain, we fail for all but the current frame. */ - if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM - && cfun_frame_layout.last_save_gpr >= BASE_REGNUM - && (TARGET_CPU_ZARCH - || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM - && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM))) - return; + if (!TARGET_BACKCHAIN && count > 0) + return NULL_RTX; - /* Search for prologue/epilogue insns and replace them. */ + /* For the current frame, we need to make sure the initial + value of RETURN_REGNUM is actually saved. */ - for (insn = get_insns (); insn; insn = next_insn) + if (count == 0) { - int first, last, off; - rtx set, base, offset; - - next_insn = NEXT_INSN (insn); + cfun_frame_layout.save_return_addr_p = true; + return gen_rtx_MEM (Pmode, return_address_pointer_rtx); + } - if (GET_CODE (insn) != INSN) - continue; + if (TARGET_PACKED_STACK) + offset = -2 * UNITS_PER_WORD; + else + offset = RETURN_REGNUM * UNITS_PER_WORD; - if (GET_CODE (PATTERN (insn)) == PARALLEL - && store_multiple_operation (PATTERN (insn), VOIDmode)) - { - set = XVECEXP (PATTERN (insn), 0, 0); - first = REGNO (SET_SRC (set)); - last = first + XVECLEN (PATTERN (insn), 0) - 1; - offset = const0_rtx; - base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); - off = INTVAL (offset); + addr = plus_constant (frame, offset); + addr = memory_address (Pmode, addr); + return gen_rtx_MEM (Pmode, addr); +} - if (GET_CODE (base) != REG || off < 0) - continue; - if (REGNO (base) != STACK_POINTER_REGNUM - && REGNO (base) != HARD_FRAME_POINTER_REGNUM) - continue; - if (first > BASE_REGNUM || last < BASE_REGNUM) - continue; +/* Return an RTL expression representing the back chain stored in + the current stack frame. */ - if (cfun_frame_layout.first_save_gpr != -1) - { - new_insn = save_gprs (base, - off + (cfun_frame_layout.first_save_gpr - - first) * UNITS_PER_WORD, - cfun_frame_layout.first_save_gpr, - cfun_frame_layout.last_save_gpr); - new_insn = emit_insn_before (new_insn, insn); - INSN_ADDRESSES_NEW (new_insn, -1); - } +rtx +s390_back_chain_rtx (void) +{ + rtx chain; - remove_insn (insn); - continue; - } + gcc_assert (TARGET_BACKCHAIN); - if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_SRC (PATTERN (insn))) == REG - && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM - || (!TARGET_CPU_ZARCH - && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM)) - && GET_CODE (SET_DEST (PATTERN (insn))) == MEM) - { - set = PATTERN (insn); - first = REGNO (SET_SRC (set)); - offset = const0_rtx; - base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); - off = INTVAL (offset); + if (TARGET_PACKED_STACK) + chain = plus_constant (stack_pointer_rtx, + STACK_POINTER_OFFSET - UNITS_PER_WORD); + else + chain = stack_pointer_rtx; - if (GET_CODE (base) != REG || off < 0) - continue; - if (REGNO (base) != STACK_POINTER_REGNUM - && REGNO (base) != HARD_FRAME_POINTER_REGNUM) - continue; - if (cfun_frame_layout.first_save_gpr != -1) - { - new_insn = save_gprs (base, - off + (cfun_frame_layout.first_save_gpr - - first) * UNITS_PER_WORD, - cfun_frame_layout.first_save_gpr, - cfun_frame_layout.last_save_gpr); - new_insn = emit_insn_before (new_insn, insn); - INSN_ADDRESSES_NEW (new_insn, -1); - } + chain = gen_rtx_MEM (Pmode, chain); + return chain; +} - remove_insn (insn); - continue; - } +/* Find first call clobbered register unused in a function. + This could be used as base register in a leaf function + or for holding the return address before epilogue. */ - if (GET_CODE (PATTERN (insn)) == PARALLEL - && load_multiple_operation (PATTERN (insn), VOIDmode)) - { - set = XVECEXP (PATTERN (insn), 0, 0); - first = REGNO (SET_DEST (set)); - last = first + XVECLEN (PATTERN (insn), 0) - 1; - offset = const0_rtx; - base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); - off = INTVAL (offset); +static int +find_unused_clobbered_reg (void) +{ + int i; + for (i = 0; i < 6; i++) + if (!regs_ever_live[i]) + return i; + return 0; +} - if (GET_CODE (base) != REG || off < 0) - continue; - if (REGNO (base) != STACK_POINTER_REGNUM - && REGNO (base) != HARD_FRAME_POINTER_REGNUM) - continue; - if (first > BASE_REGNUM || last < BASE_REGNUM) - continue; +/* Determine the frame area which actually has to be accessed + in the function epilogue. The values are stored at the + given pointers AREA_BOTTOM (address of the lowest used stack + address) and AREA_TOP (address of the first item which does + not belong to the stack frame). */ - if (cfun_frame_layout.first_restore_gpr != -1) - { - new_insn = restore_gprs (base, - off + (cfun_frame_layout.first_restore_gpr - - first) * UNITS_PER_WORD, - cfun_frame_layout.first_restore_gpr, - cfun_frame_layout.last_restore_gpr); - new_insn = emit_insn_before (new_insn, insn); - INSN_ADDRESSES_NEW (new_insn, -1); - } +static void +s390_frame_area (int *area_bottom, int *area_top) +{ + int b, t; + int i; - remove_insn (insn); - continue; - } - - if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_DEST (PATTERN (insn))) == REG - && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM - || (!TARGET_CPU_ZARCH - && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM)) - && GET_CODE (SET_SRC (PATTERN (insn))) == MEM) - { - set = PATTERN (insn); - first = REGNO (SET_DEST (set)); - offset = const0_rtx; - base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); - off = INTVAL (offset); - - if (GET_CODE (base) != REG || off < 0) - continue; - if (REGNO (base) != STACK_POINTER_REGNUM - && REGNO (base) != HARD_FRAME_POINTER_REGNUM) - continue; - if (cfun_frame_layout.first_restore_gpr != -1) - { - new_insn = restore_gprs (base, - off + (cfun_frame_layout.first_restore_gpr - - first) * UNITS_PER_WORD, - cfun_frame_layout.first_restore_gpr, - cfun_frame_layout.last_restore_gpr); - new_insn = emit_insn_before (new_insn, insn); - INSN_ADDRESSES_NEW (new_insn, -1); - } - - remove_insn (insn); - continue; - } - } -} - -/* Perform machine-dependent processing. */ - -static void -s390_reorg (void) -{ - bool pool_overflow = false; - - /* Make sure all splits have been performed; splits after - machine_dependent_reorg might confuse insn length counts. */ - split_all_insns_noflow (); - - - /* Install the main literal pool and the associated base - register load insns. - - In addition, there are two problematic situations we need - to correct: - - - the literal pool might be > 4096 bytes in size, so that - some of its elements cannot be directly accessed - - - a branch target might be > 64K away from the branch, so that - it is not possible to use a PC-relative instruction. - - To fix those, we split the single literal pool into multiple - pool chunks, reloading the pool base register at various - points throughout the function to ensure it always points to - the pool chunk the following code expects, and / or replace - PC-relative branches by absolute branches. - - However, the two problems are interdependent: splitting the - literal pool can move a branch further away from its target, - causing the 64K limit to overflow, and on the other hand, - replacing a PC-relative branch by an absolute branch means - we need to put the branch target address into the literal - pool, possibly causing it to overflow. - - So, we loop trying to fix up both problems until we manage - to satisfy both conditions at the same time. Note that the - loop is guaranteed to terminate as every pass of the loop - strictly decreases the total number of PC-relative branches - in the function. (This is not completely true as there - might be branch-over-pool insns introduced by chunkify_start. - Those never need to be split however.) */ - - for (;;) - { - struct constant_pool *pool = NULL; - - /* Collect the literal pool. */ - if (!pool_overflow) - { - pool = s390_mainpool_start (); - if (!pool) - pool_overflow = true; - } - - /* If literal pool overflowed, start to chunkify it. */ - if (pool_overflow) - pool = s390_chunkify_start (); - - /* Split out-of-range branches. If this has created new - literal pool entries, cancel current chunk list and - recompute it. zSeries machines have large branch - instructions, so we never need to split a branch. */ - if (!TARGET_CPU_ZARCH && s390_split_branches ()) - { - if (pool_overflow) - s390_chunkify_cancel (pool); - else - s390_mainpool_cancel (pool); - - continue; - } - - /* If we made it up to here, both conditions are satisfied. - Finish up literal pool related changes. */ - if (pool_overflow) - s390_chunkify_finish (pool); - else - s390_mainpool_finish (pool); - - /* We're done splitting branches. */ - cfun->machine->split_branches_pending_p = false; - break; - } - - s390_optimize_prologue (); -} - - -/* Return an RTL expression representing the value of the return address - for the frame COUNT steps up from the current frame. FRAME is the - frame pointer of that frame. */ - -rtx -s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) -{ - int offset; - rtx addr; - - /* Without backchain, we fail for all but the current frame. */ - - if (!TARGET_BACKCHAIN && count > 0) - return NULL_RTX; - - /* For the current frame, we need to make sure the initial - value of RETURN_REGNUM is actually saved. */ - - if (count == 0) - { - cfun_frame_layout.save_return_addr_p = true; - return gen_rtx_MEM (Pmode, return_address_pointer_rtx); - } - - if (TARGET_PACKED_STACK) - offset = -2 * UNITS_PER_WORD; - else - offset = RETURN_REGNUM * UNITS_PER_WORD; - - addr = plus_constant (frame, offset); - addr = memory_address (Pmode, addr); - return gen_rtx_MEM (Pmode, addr); -} - -/* Return an RTL expression representing the back chain stored in - the current stack frame. */ - -rtx -s390_back_chain_rtx (void) -{ - rtx chain; - - gcc_assert (TARGET_BACKCHAIN); - - if (TARGET_PACKED_STACK) - chain = plus_constant (stack_pointer_rtx, - STACK_POINTER_OFFSET - UNITS_PER_WORD); - else - chain = stack_pointer_rtx; - - chain = gen_rtx_MEM (Pmode, chain); - return chain; -} - -/* Find first call clobbered register unused in a function. - This could be used as base register in a leaf function - or for holding the return address before epilogue. */ - -static int -find_unused_clobbered_reg (void) -{ - int i; - for (i = 0; i < 6; i++) - if (!regs_ever_live[i]) - return i; - return 0; -} - -/* Determine the frame area which actually has to be accessed - in the function epilogue. The values are stored at the - given pointers AREA_BOTTOM (address of the lowest used stack - address) and AREA_TOP (address of the first item which does - not belong to the stack frame). */ - -static void -s390_frame_area (int *area_bottom, int *area_top) -{ - int b, t; - int i; - - b = INT_MAX; - t = INT_MIN; + b = INT_MAX; + t = INT_MIN; if (cfun_frame_layout.first_restore_gpr != -1) { @@ -7372,7 +6950,7 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) ret = **args.overflow_arg_area++; } */ -tree +static tree s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p ATTRIBUTE_UNUSED) { @@ -7491,854 +7069,1194 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, /* ... Otherwise out of the overflow area. */ - t = ovf; - if (size < UNITS_PER_WORD) - t = build2 (PLUS_EXPR, ptr_type_node, t, - fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size))); + t = ovf; + if (size < UNITS_PER_WORD) + t = build2 (PLUS_EXPR, ptr_type_node, t, + fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size))); + + gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); + + u = build2 (MODIFY_EXPR, void_type_node, addr, t); + gimplify_and_add (u, pre_p); + + t = build2 (PLUS_EXPR, ptr_type_node, t, + fold_convert (ptr_type_node, size_int (size))); + t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t); + gimplify_and_add (t, pre_p); + + t = build1 (LABEL_EXPR, void_type_node, lab_over); + append_to_statement_list (t, pre_p); + + + /* Increment register save count. */ + + u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, + fold_convert (TREE_TYPE (reg), size_int (n_reg))); + gimplify_and_add (u, pre_p); + + if (indirect_p) + { + t = build_pointer_type (build_pointer_type (type)); + addr = fold_convert (t, addr); + addr = build_va_arg_indirect_ref (addr); + } + else + { + t = build_pointer_type (type); + addr = fold_convert (t, addr); + } + + return build_va_arg_indirect_ref (addr); +} + + +/* Builtins. */ + +enum s390_builtin +{ + S390_BUILTIN_THREAD_POINTER, + S390_BUILTIN_SET_THREAD_POINTER, + + S390_BUILTIN_max +}; + +static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = { + CODE_FOR_get_tp_64, + CODE_FOR_set_tp_64 +}; + +static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = { + CODE_FOR_get_tp_31, + CODE_FOR_set_tp_31 +}; + +static void +s390_init_builtins (void) +{ + tree ftype; + + ftype = build_function_type (ptr_type_node, void_list_node); + lang_hooks.builtin_function ("__builtin_thread_pointer", ftype, + S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype, + S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +static rtx +s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ +#define MAX_ARGS 2 + + unsigned int const *code_for_builtin = + TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31; + + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + tree arglist = TREE_OPERAND (exp, 1); + enum insn_code icode; + rtx op[MAX_ARGS], pat; + int arity; + bool nonvoid; + + if (fcode >= S390_BUILTIN_max) + internal_error ("bad builtin fcode"); + icode = code_for_builtin[fcode]; + if (icode == 0) + internal_error ("bad builtin fcode"); + + nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; + + for (arglist = TREE_OPERAND (exp, 1), arity = 0; + arglist; + arglist = TREE_CHAIN (arglist), arity++) + { + const struct insn_operand_data *insn_op; + + tree arg = TREE_VALUE (arglist); + if (arg == error_mark_node) + return NULL_RTX; + if (arity > MAX_ARGS) + return NULL_RTX; + + insn_op = &insn_data[icode].operand[arity + nonvoid]; + + op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); + + if (!(*insn_op->predicate) (op[arity], insn_op->mode)) + op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); + } + + if (nonvoid) + { + enum machine_mode tmode = insn_data[icode].operand[0].mode; + if (!target + || GET_MODE (target) != tmode + || !(*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + } + + switch (arity) + { + case 0: + pat = GEN_FCN (icode) (target); + break; + case 1: + if (nonvoid) + pat = GEN_FCN (icode) (target, op[0]); + else + pat = GEN_FCN (icode) (op[0]); + break; + case 2: + pat = GEN_FCN (icode) (target, op[0], op[1]); + break; + default: + abort (); + } + if (!pat) + return NULL_RTX; + emit_insn (pat); + + if (nonvoid) + return target; + else + return const0_rtx; +} + + +/* Output assembly code for the trampoline template to + stdio stream FILE. + + On S/390, we use gpr 1 internally in the trampoline code; + gpr 0 is used to hold the static chain. */ + +void +s390_trampoline_template (FILE *file) +{ + rtx op[2]; + op[0] = gen_rtx_REG (Pmode, 0); + op[1] = gen_rtx_REG (Pmode, 1); + + if (TARGET_64BIT) + { + output_asm_insn ("basr\t%1,0", op); + output_asm_insn ("lmg\t%0,%1,14(%1)", op); + output_asm_insn ("br\t%1", op); + ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10)); + } + else + { + output_asm_insn ("basr\t%1,0", op); + output_asm_insn ("lm\t%0,%1,6(%1)", op); + output_asm_insn ("br\t%1", op); + ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8)); + } +} + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. */ + +void +s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt) +{ + emit_move_insn (gen_rtx_MEM (Pmode, + memory_address (Pmode, + plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt); + emit_move_insn (gen_rtx_MEM (Pmode, + memory_address (Pmode, + plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr); +} + +/* Return rtx for 64-bit constant formed from the 32-bit subwords + LOW and HIGH, independent of the host word size. */ + +rtx +s390_gen_rtx_const_DI (int high, int low) +{ +#if HOST_BITS_PER_WIDE_INT >= 64 + HOST_WIDE_INT val; + val = (HOST_WIDE_INT)high; + val <<= 32; + val |= (HOST_WIDE_INT)low; + + return GEN_INT (val); +#else +#if HOST_BITS_PER_WIDE_INT >= 32 + return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode); +#else + abort (); +#endif +#endif +} + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +void +s390_function_profiler (FILE *file, int labelno) +{ + rtx op[7]; + + char label[128]; + ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); + + fprintf (file, "# function profiler \n"); + + op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM); + op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD)); + + op[2] = gen_rtx_REG (Pmode, 1); + op[3] = gen_rtx_SYMBOL_REF (Pmode, label); + SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL; + + op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount"); + if (flag_pic) + { + op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT); + op[4] = gen_rtx_CONST (Pmode, op[4]); + } + + if (TARGET_64BIT) + { + output_asm_insn ("stg\t%0,%1", op); + output_asm_insn ("larl\t%2,%3", op); + output_asm_insn ("brasl\t%0,%4", op); + output_asm_insn ("lg\t%0,%1", op); + } + else if (!flag_pic) + { + op[6] = gen_label_rtx (); + + output_asm_insn ("st\t%0,%1", op); + output_asm_insn ("bras\t%2,%l6", op); + output_asm_insn (".long\t%4", op); + output_asm_insn (".long\t%3", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn ("l\t%0,0(%2)", op); + output_asm_insn ("l\t%2,4(%2)", op); + output_asm_insn ("basr\t%0,%0", op); + output_asm_insn ("l\t%0,%1", op); + } + else + { + op[5] = gen_label_rtx (); + op[6] = gen_label_rtx (); + + output_asm_insn ("st\t%0,%1", op); + output_asm_insn ("bras\t%2,%l6", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5])); + output_asm_insn (".long\t%4-%l5", op); + output_asm_insn (".long\t%3-%l5", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn ("lr\t%0,%2", op); + output_asm_insn ("a\t%0,0(%2)", op); + output_asm_insn ("a\t%2,4(%2)", op); + output_asm_insn ("basr\t%0,%0", op); + output_asm_insn ("l\t%0,%1", op); + } +} + +/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF + into its SYMBOL_REF_FLAGS. */ + +static void +s390_encode_section_info (tree decl, rtx rtl, int first) +{ + default_encode_section_info (decl, rtl, first); + + /* If a variable has a forced alignment to < 2 bytes, mark it with + SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand. */ + if (TREE_CODE (decl) == VAR_DECL + && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16) + SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1; +} + +/* Output thunk to FILE that implements a C++ virtual function call (with + multiple inheritance) to FUNCTION. The thunk adjusts the this pointer + by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment + stored at VCALL_OFFSET in the vtable whose address is located at offset 0 + relative to the resulting this pointer. */ + +static void +s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) +{ + rtx op[10]; + int nonlocal = 0; + + /* Operand 0 is the target function. */ + op[0] = XEXP (DECL_RTL (function), 0); + if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0])) + { + nonlocal = 1; + op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), + TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT); + op[0] = gen_rtx_CONST (Pmode, op[0]); + } + + /* Operand 1 is the 'this' pointer. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + op[1] = gen_rtx_REG (Pmode, 3); + else + op[1] = gen_rtx_REG (Pmode, 2); + + /* Operand 2 is the delta. */ + op[2] = GEN_INT (delta); + + /* Operand 3 is the vcall_offset. */ + op[3] = GEN_INT (vcall_offset); + + /* Operand 4 is the temporary register. */ + op[4] = gen_rtx_REG (Pmode, 1); + + /* Operands 5 to 8 can be used as labels. */ + op[5] = NULL_RTX; + op[6] = NULL_RTX; + op[7] = NULL_RTX; + op[8] = NULL_RTX; + + /* Operand 9 can be used for temporary register. */ + op[9] = NULL_RTX; + + /* Generate code. */ + if (TARGET_64BIT) + { + /* Setup literal pool pointer if required. */ + if ((!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) + || (!DISP_IN_RANGE (vcall_offset) + && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))) + { + op[5] = gen_label_rtx (); + output_asm_insn ("larl\t%4,%5", op); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J")) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (DISP_IN_RANGE (delta)) + output_asm_insn ("lay\t%1,%2(%1)", op); + else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) + output_asm_insn ("aghi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("agf\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (DISP_IN_RANGE (vcall_offset)) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")) + { + output_asm_insn ("lghi\t%4,%3", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("llgf\t%4,%7-%5(%4)", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + } + + /* Jump to target. */ + output_asm_insn ("jg\t%0", op); + + /* Output literal pool if required. */ + if (op[5]) + { + output_asm_insn (".align\t4", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } + if (op[6]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } + } + else + { + /* Setup base pointer if required. */ + if (!vcall_offset + || (!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) + || (!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))) + { + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J")) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (DISP_IN_RANGE (delta)) + output_asm_insn ("lay\t%1,%2(%1)", op); + else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) + output_asm_insn ("ahi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("a\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'J', "J")) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("a\t%1,%3(%4)", op); + } + else if (DISP_IN_RANGE (vcall_offset)) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("ay\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")) + { + output_asm_insn ("lhi\t%4,%3", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("l\t%4,%7-%5(%4)", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } - gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); + /* We had to clobber the base pointer register. + Re-setup the base pointer (with a different base). */ + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } - u = build2 (MODIFY_EXPR, void_type_node, addr, t); - gimplify_and_add (u, pre_p); + /* Jump to target. */ + op[8] = gen_label_rtx (); - t = build2 (PLUS_EXPR, ptr_type_node, t, - fold_convert (ptr_type_node, size_int (size))); - t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t); - gimplify_and_add (t, pre_p); + if (!flag_pic) + output_asm_insn ("l\t%4,%8-%5(%4)", op); + else if (!nonlocal) + output_asm_insn ("a\t%4,%8-%5(%4)", op); + /* We cannot call through .plt, since .plt requires %r12 loaded. */ + else if (flag_pic == 1) + { + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("l\t%4,%0(%4)", op); + } + else if (flag_pic == 2) + { + op[9] = gen_rtx_REG (Pmode, 0); + output_asm_insn ("l\t%9,%8-4-%5(%4)", op); + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("ar\t%4,%9", op); + output_asm_insn ("l\t%4,0(%4)", op); + } - t = build1 (LABEL_EXPR, void_type_node, lab_over); - append_to_statement_list (t, pre_p); + output_asm_insn ("br\t%4", op); + /* Output literal pool. */ + output_asm_insn (".align\t4", op); - /* Increment register save count. */ + if (nonlocal && flag_pic == 2) + output_asm_insn (".long\t%0", op); + if (nonlocal) + { + op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL; + } - u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, - fold_convert (TREE_TYPE (reg), size_int (n_reg))); - gimplify_and_add (u, pre_p); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8])); + if (!flag_pic) + output_asm_insn (".long\t%0", op); + else + output_asm_insn (".long\t%0-%5", op); - if (indirect_p) - { - t = build_pointer_type (build_pointer_type (type)); - addr = fold_convert (t, addr); - addr = build_va_arg_indirect_ref (addr); - } - else - { - t = build_pointer_type (type); - addr = fold_convert (t, addr); + if (op[6]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } } - - return build_va_arg_indirect_ref (addr); } +static bool +s390_valid_pointer_mode (enum machine_mode mode) +{ + return (mode == SImode || (TARGET_64BIT && mode == DImode)); +} -/* Builtins. */ +/* Checks whether the given ARGUMENT_LIST would use a caller + saved register. This is used to decide whether sibling call + optimization could be performed on the respective function + call. */ -enum s390_builtin +static bool +s390_call_saved_register_used (tree argument_list) { - S390_BUILTIN_THREAD_POINTER, - S390_BUILTIN_SET_THREAD_POINTER, + CUMULATIVE_ARGS cum; + tree parameter; + enum machine_mode mode; + tree type; + rtx parm_rtx; + int reg; - S390_BUILTIN_max -}; + INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0); -static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = { - CODE_FOR_get_tp_64, - CODE_FOR_set_tp_64 -}; + while (argument_list) + { + parameter = TREE_VALUE (argument_list); + argument_list = TREE_CHAIN (argument_list); -static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = { - CODE_FOR_get_tp_31, - CODE_FOR_set_tp_31 -}; + if (!parameter) + abort(); -static void -s390_init_builtins (void) -{ - tree ftype; + /* For an undeclared variable passed as parameter we will get + an ERROR_MARK node here. */ + if (TREE_CODE (parameter) == ERROR_MARK) + return true; - ftype = build_function_type (ptr_type_node, void_list_node); - lang_hooks.builtin_function ("__builtin_thread_pointer", ftype, - S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD, - NULL, NULL_TREE); + if (! (type = TREE_TYPE (parameter))) + abort(); - ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); - lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype, - S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, - NULL, NULL_TREE); -} + if (! (mode = TYPE_MODE (TREE_TYPE (parameter)))) + abort(); -/* Expand an expression EXP that calls a built-in function, - with result going to TARGET if that's convenient - (and in mode MODE if that's convenient). - SUBTARGET may be used as the target for computing one of EXP's operands. - IGNORE is nonzero if the value is to be ignored. */ + if (pass_by_reference (&cum, mode, type, true)) + { + mode = Pmode; + type = build_pointer_type (type); + } -static rtx -s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, - enum machine_mode mode ATTRIBUTE_UNUSED, - int ignore ATTRIBUTE_UNUSED) -{ -#define MAX_ARGS 2 + parm_rtx = s390_function_arg (&cum, mode, type, 0); - unsigned int const *code_for_builtin = - TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31; + s390_function_arg_advance (&cum, mode, type, 0); - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - unsigned int fcode = DECL_FUNCTION_CODE (fndecl); - tree arglist = TREE_OPERAND (exp, 1); - enum insn_code icode; - rtx op[MAX_ARGS], pat; - int arity; - bool nonvoid; + if (parm_rtx && REG_P (parm_rtx)) + { + for (reg = 0; + reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx)); + reg++) + if (! call_used_regs[reg + REGNO (parm_rtx)]) + return true; + } + } + return false; +} - if (fcode >= S390_BUILTIN_max) - internal_error ("bad builtin fcode"); - icode = code_for_builtin[fcode]; - if (icode == 0) - internal_error ("bad builtin fcode"); +/* Return true if the given call expression can be + turned into a sibling call. + DECL holds the declaration of the function to be called whereas + EXP is the call expression itself. */ - nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; +static bool +s390_function_ok_for_sibcall (tree decl, tree exp) +{ + /* The TPF epilogue uses register 1. */ + if (TARGET_TPF_PROFILING) + return false; - for (arglist = TREE_OPERAND (exp, 1), arity = 0; - arglist; - arglist = TREE_CHAIN (arglist), arity++) - { - const struct insn_operand_data *insn_op; + /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved) + which would have to be restored before the sibcall. */ + if (!TARGET_64BIT && flag_pic && decl && TREE_PUBLIC (decl)) + return false; - tree arg = TREE_VALUE (arglist); - if (arg == error_mark_node) - return NULL_RTX; - if (arity > MAX_ARGS) - return NULL_RTX; + /* Register 6 on s390 is available as an argument register but unfortunately + "caller saved". This makes functions needing this register for arguments + not suitable for sibcalls. */ + if (TREE_OPERAND (exp, 1) + && s390_call_saved_register_used (TREE_OPERAND (exp, 1))) + return false; - insn_op = &insn_data[icode].operand[arity + nonvoid]; + return true; +} - op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); +/* Return the fixed registers used for condition codes. */ - if (!(*insn_op->predicate) (op[arity], insn_op->mode)) - op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); - } +static bool +s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = CC_REGNUM; + *p2 = INVALID_REGNUM; + + return true; +} - if (nonvoid) - { - enum machine_mode tmode = insn_data[icode].operand[0].mode; - if (!target - || GET_MODE (target) != tmode - || !(*insn_data[icode].operand[0].predicate) (target, tmode)) - target = gen_reg_rtx (tmode); - } +/* If two condition code modes are compatible, return a condition code + mode which is compatible with both. Otherwise, return + VOIDmode. */ - switch (arity) +static enum machine_mode +s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) +{ + if (m1 == m2) + return m1; + + switch (m1) { - case 0: - pat = GEN_FCN (icode) (target); - break; - case 1: - if (nonvoid) - pat = GEN_FCN (icode) (target, op[0]); - else - pat = GEN_FCN (icode) (op[0]); - break; - case 2: - pat = GEN_FCN (icode) (target, op[0], op[1]); - break; + case CCZmode: + if (m2 == CCUmode || m2 == CCTmode + || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode) + return m2; + return VOIDmode; + + case CCSmode: + case CCUmode: + case CCTmode: + case CCSRmode: + case CCURmode: + if (m2 == CCZmode) + return m1; + + return VOIDmode; + default: - abort (); + return VOIDmode; } - if (!pat) - return NULL_RTX; - emit_insn (pat); - - if (nonvoid) - return target; - else - return const0_rtx; + return VOIDmode; } +/* This function is used by the call expanders of the machine description. + It emits the call insn itself together with the necessary operations + to adjust the target address and returns the emitted insn. + ADDR_LOCATION is the target address rtx + TLS_CALL the location of the thread-local symbol + RESULT_REG the register where the result of the call should be stored + RETADDR_REG the register where the return address should be stored + If this parameter is NULL_RTX the call is considered + to be a sibling call. */ -/* Output assembly code for the trampoline template to - stdio stream FILE. - - On S/390, we use gpr 1 internally in the trampoline code; - gpr 0 is used to hold the static chain. */ - -void -s390_trampoline_template (FILE *file) +rtx +s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, + rtx retaddr_reg) { - rtx op[2]; - op[0] = gen_rtx_REG (Pmode, 0); - op[1] = gen_rtx_REG (Pmode, 1); + bool plt_call = false; + rtx insn; + rtx call; + rtx clobber; + rtvec vec; - if (TARGET_64BIT) + /* Direct function calls need special treatment. */ + if (GET_CODE (addr_location) == SYMBOL_REF) { - output_asm_insn ("basr\t%1,0", op); - output_asm_insn ("lmg\t%0,%1,14(%1)", op); - output_asm_insn ("br\t%1", op); - ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10)); + /* When calling a global routine in PIC mode, we must + replace the symbol itself with the PLT stub. */ + if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location)) + { + addr_location = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, addr_location), + UNSPEC_PLT); + addr_location = gen_rtx_CONST (Pmode, addr_location); + plt_call = true; + } + + /* Unless we can use the bras(l) insn, force the + routine address into a register. */ + if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH) + { + if (flag_pic) + addr_location = legitimize_pic_address (addr_location, 0); + else + addr_location = force_reg (Pmode, addr_location); + } } - else + + /* If it is already an indirect call or the code above moved the + SYMBOL_REF to somewhere else make sure the address can be found in + register 1. */ + if (retaddr_reg == NULL_RTX + && GET_CODE (addr_location) != SYMBOL_REF + && !plt_call) { - output_asm_insn ("basr\t%1,0", op); - output_asm_insn ("lm\t%0,%1,6(%1)", op); - output_asm_insn ("br\t%1", op); - ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8)); + emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location); + addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM); } -} -/* Emit RTL insns to initialize the variable parts of a trampoline. - FNADDR is an RTX for the address of the function's pure code. - CXT is an RTX for the static chain value for the function. */ - -void -s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt) -{ - emit_move_insn (gen_rtx_MEM (Pmode, - memory_address (Pmode, - plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt); - emit_move_insn (gen_rtx_MEM (Pmode, - memory_address (Pmode, - plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr); -} + addr_location = gen_rtx_MEM (QImode, addr_location); + call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); -/* Return rtx for 64-bit constant formed from the 32-bit subwords - LOW and HIGH, independent of the host word size. */ + if (result_reg != NULL_RTX) + call = gen_rtx_SET (VOIDmode, result_reg, call); -rtx -s390_gen_rtx_const_DI (int high, int low) -{ -#if HOST_BITS_PER_WIDE_INT >= 64 - HOST_WIDE_INT val; - val = (HOST_WIDE_INT)high; - val <<= 32; - val |= (HOST_WIDE_INT)low; + if (retaddr_reg != NULL_RTX) + { + clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); - return GEN_INT (val); -#else -#if HOST_BITS_PER_WIDE_INT >= 32 - return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode); -#else - abort (); -#endif -#endif -} + if (tls_call != NULL_RTX) + vec = gen_rtvec (3, call, clobber, + gen_rtx_USE (VOIDmode, tls_call)); + else + vec = gen_rtvec (2, call, clobber); -/* Output assembler code to FILE to increment profiler label # LABELNO - for profiling a function entry. */ + call = gen_rtx_PARALLEL (VOIDmode, vec); + } -void -s390_function_profiler (FILE *file, int labelno) -{ - rtx op[7]; + insn = emit_call_insn (call); - char label[128]; - ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); + /* 31-bit PLT stubs and tls calls use the GOT register implicitly. */ + if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX) + { + /* s390_function_ok_for_sibcall should + have denied sibcalls in this case. */ + if (retaddr_reg == NULL_RTX) + abort (); - fprintf (file, "# function profiler \n"); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); + } + return insn; +} - op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM); - op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); - op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD)); +/* Implement CONDITIONAL_REGISTER_USAGE. */ - op[2] = gen_rtx_REG (Pmode, 1); - op[3] = gen_rtx_SYMBOL_REF (Pmode, label); - SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL; +void +s390_conditional_register_usage (void) +{ + int i; - op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount"); if (flag_pic) { - op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT); - op[4] = gen_rtx_CONST (Pmode, op[4]); + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; } - - if (TARGET_64BIT) + if (TARGET_CPU_ZARCH) { - output_asm_insn ("stg\t%0,%1", op); - output_asm_insn ("larl\t%2,%3", op); - output_asm_insn ("brasl\t%0,%4", op); - output_asm_insn ("lg\t%0,%1", op); + fixed_regs[RETURN_REGNUM] = 0; + call_used_regs[RETURN_REGNUM] = 0; } - else if (!flag_pic) + if (TARGET_64BIT) { - op[6] = gen_label_rtx (); - - output_asm_insn ("st\t%0,%1", op); - output_asm_insn ("bras\t%2,%l6", op); - output_asm_insn (".long\t%4", op); - output_asm_insn (".long\t%3", op); - targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); - output_asm_insn ("l\t%0,0(%2)", op); - output_asm_insn ("l\t%2,4(%2)", op); - output_asm_insn ("basr\t%0,%0", op); - output_asm_insn ("l\t%0,%1", op); + for (i = 24; i < 32; i++) + call_used_regs[i] = call_really_used_regs[i] = 0; } else { - op[5] = gen_label_rtx (); - op[6] = gen_label_rtx (); + for (i = 18; i < 20; i++) + call_used_regs[i] = call_really_used_regs[i] = 0; + } - output_asm_insn ("st\t%0,%1", op); - output_asm_insn ("bras\t%2,%l6", op); - targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5])); - output_asm_insn (".long\t%4-%l5", op); - output_asm_insn (".long\t%3-%l5", op); - targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); - output_asm_insn ("lr\t%0,%2", op); - output_asm_insn ("a\t%0,0(%2)", op); - output_asm_insn ("a\t%2,4(%2)", op); - output_asm_insn ("basr\t%0,%0", op); - output_asm_insn ("l\t%0,%1", op); + if (TARGET_SOFT_FLOAT) + { + for (i = 16; i < 32; i++) + call_used_regs[i] = fixed_regs[i] = 1; } } -/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF - into its SYMBOL_REF_FLAGS. */ +/* Corresponding function to eh_return expander. */ -static void -s390_encode_section_info (tree decl, rtx rtl, int first) +static GTY(()) rtx s390_tpf_eh_return_symbol; +void +s390_emit_tpf_eh_return (rtx target) { - default_encode_section_info (decl, rtl, first); + rtx insn, reg; - /* If a variable has a forced alignment to < 2 bytes, mark it with - SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand. */ - if (TREE_CODE (decl) == VAR_DECL - && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16) - SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1; + if (!s390_tpf_eh_return_symbol) + s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return"); + + reg = gen_rtx_REG (Pmode, 2); + + emit_move_insn (reg, target); + insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg, + gen_rtx_REG (Pmode, RETURN_REGNUM)); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg); + + emit_move_insn (EH_RETURN_HANDLER_RTX, reg); } -/* Output thunk to FILE that implements a C++ virtual function call (with - multiple inheritance) to FUNCTION. The thunk adjusts the this pointer - by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment - stored at VCALL_OFFSET in the vtable whose address is located at offset 0 - relative to the resulting this pointer. */ +/* Rework the prologue/epilogue to avoid saving/restoring + registers unnecessarily. */ static void -s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, - HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, - tree function) +s390_optimize_prologue (void) { - rtx op[10]; - int nonlocal = 0; + rtx insn, new_insn, next_insn; - /* Operand 0 is the target function. */ - op[0] = XEXP (DECL_RTL (function), 0); - if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0])) - { - nonlocal = 1; - op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), - TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT); - op[0] = gen_rtx_CONST (Pmode, op[0]); - } + /* Do a final recompute of the frame-related data. */ - /* Operand 1 is the 'this' pointer. */ - if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) - op[1] = gen_rtx_REG (Pmode, 3); - else - op[1] = gen_rtx_REG (Pmode, 2); + s390_update_frame_layout (); - /* Operand 2 is the delta. */ - op[2] = GEN_INT (delta); + /* If all special registers are in fact used, there's nothing we + can do, so no point in walking the insn list. */ - /* Operand 3 is the vcall_offset. */ - op[3] = GEN_INT (vcall_offset); + if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM + && cfun_frame_layout.last_save_gpr >= BASE_REGNUM + && (TARGET_CPU_ZARCH + || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM + && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM))) + return; - /* Operand 4 is the temporary register. */ - op[4] = gen_rtx_REG (Pmode, 1); + /* Search for prologue/epilogue insns and replace them. */ - /* Operands 5 to 8 can be used as labels. */ - op[5] = NULL_RTX; - op[6] = NULL_RTX; - op[7] = NULL_RTX; - op[8] = NULL_RTX; + for (insn = get_insns (); insn; insn = next_insn) + { + int first, last, off; + rtx set, base, offset; - /* Operand 9 can be used for temporary register. */ - op[9] = NULL_RTX; + next_insn = NEXT_INSN (insn); - /* Generate code. */ - if (TARGET_64BIT) - { - /* Setup literal pool pointer if required. */ - if ((!DISP_IN_RANGE (delta) - && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) - || (!DISP_IN_RANGE (vcall_offset) - && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))) - { - op[5] = gen_label_rtx (); - output_asm_insn ("larl\t%4,%5", op); - } + if (GET_CODE (insn) != INSN) + continue; - /* Add DELTA to this pointer. */ - if (delta) + if (GET_CODE (PATTERN (insn)) == PARALLEL + && store_multiple_operation (PATTERN (insn), VOIDmode)) { - if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J")) - output_asm_insn ("la\t%1,%2(%1)", op); - else if (DISP_IN_RANGE (delta)) - output_asm_insn ("lay\t%1,%2(%1)", op); - else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) - output_asm_insn ("aghi\t%1,%2", op); - else - { - op[6] = gen_label_rtx (); - output_asm_insn ("agf\t%1,%6-%5(%4)", op); - } - } + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_SRC (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + off = INTVAL (offset); - /* Perform vcall adjustment. */ - if (vcall_offset) - { - if (DISP_IN_RANGE (vcall_offset)) - { - output_asm_insn ("lg\t%4,0(%1)", op); - output_asm_insn ("ag\t%1,%3(%4)", op); - } - else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")) - { - output_asm_insn ("lghi\t%4,%3", op); - output_asm_insn ("ag\t%4,0(%1)", op); - output_asm_insn ("ag\t%1,0(%4)", op); - } - else + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (first > BASE_REGNUM || last < BASE_REGNUM) + continue; + + if (cfun_frame_layout.first_save_gpr != -1) { - op[7] = gen_label_rtx (); - output_asm_insn ("llgf\t%4,%7-%5(%4)", op); - output_asm_insn ("ag\t%4,0(%1)", op); - output_asm_insn ("ag\t%1,0(%4)", op); + new_insn = save_gprs (base, + off + (cfun_frame_layout.first_save_gpr + - first) * UNITS_PER_WORD, + cfun_frame_layout.first_save_gpr, + cfun_frame_layout.last_save_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); } - } - - /* Jump to target. */ - output_asm_insn ("jg\t%0", op); - /* Output literal pool if required. */ - if (op[5]) - { - output_asm_insn (".align\t4", op); - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[5])); - } - if (op[6]) - { - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[6])); - output_asm_insn (".long\t%2", op); - } - if (op[7]) - { - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[7])); - output_asm_insn (".long\t%3", op); - } - } - else - { - /* Setup base pointer if required. */ - if (!vcall_offset - || (!DISP_IN_RANGE (delta) - && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) - || (!DISP_IN_RANGE (delta) - && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))) - { - op[5] = gen_label_rtx (); - output_asm_insn ("basr\t%4,0", op); - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[5])); + remove_insn (insn); + continue; } - /* Add DELTA to this pointer. */ - if (delta) + if (GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == REG + && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM + || (!TARGET_CPU_ZARCH + && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM)) + && GET_CODE (SET_DEST (PATTERN (insn))) == MEM) { - if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J")) - output_asm_insn ("la\t%1,%2(%1)", op); - else if (DISP_IN_RANGE (delta)) - output_asm_insn ("lay\t%1,%2(%1)", op); - else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K")) - output_asm_insn ("ahi\t%1,%2", op); - else - { - op[6] = gen_label_rtx (); - output_asm_insn ("a\t%1,%6-%5(%4)", op); - } - } + set = PATTERN (insn); + first = REGNO (SET_SRC (set)); + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + off = INTVAL (offset); - /* Perform vcall adjustment. */ - if (vcall_offset) - { - if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'J', "J")) - { - output_asm_insn ("lg\t%4,0(%1)", op); - output_asm_insn ("a\t%1,%3(%4)", op); - } - else if (DISP_IN_RANGE (vcall_offset)) - { - output_asm_insn ("lg\t%4,0(%1)", op); - output_asm_insn ("ay\t%1,%3(%4)", op); - } - else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")) - { - output_asm_insn ("lhi\t%4,%3", op); - output_asm_insn ("a\t%4,0(%1)", op); - output_asm_insn ("a\t%1,0(%4)", op); - } - else + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (cfun_frame_layout.first_save_gpr != -1) { - op[7] = gen_label_rtx (); - output_asm_insn ("l\t%4,%7-%5(%4)", op); - output_asm_insn ("a\t%4,0(%1)", op); - output_asm_insn ("a\t%1,0(%4)", op); + new_insn = save_gprs (base, + off + (cfun_frame_layout.first_save_gpr + - first) * UNITS_PER_WORD, + cfun_frame_layout.first_save_gpr, + cfun_frame_layout.last_save_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); } - /* We had to clobber the base pointer register. - Re-setup the base pointer (with a different base). */ - op[5] = gen_label_rtx (); - output_asm_insn ("basr\t%4,0", op); - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[5])); + remove_insn (insn); + continue; } - /* Jump to target. */ - op[8] = gen_label_rtx (); - - if (!flag_pic) - output_asm_insn ("l\t%4,%8-%5(%4)", op); - else if (!nonlocal) - output_asm_insn ("a\t%4,%8-%5(%4)", op); - /* We cannot call through .plt, since .plt requires %r12 loaded. */ - else if (flag_pic == 1) - { - output_asm_insn ("a\t%4,%8-%5(%4)", op); - output_asm_insn ("l\t%4,%0(%4)", op); - } - else if (flag_pic == 2) + if (GET_CODE (PATTERN (insn)) == PARALLEL + && load_multiple_operation (PATTERN (insn), VOIDmode)) { - op[9] = gen_rtx_REG (Pmode, 0); - output_asm_insn ("l\t%9,%8-4-%5(%4)", op); - output_asm_insn ("a\t%4,%8-%5(%4)", op); - output_asm_insn ("ar\t%4,%9", op); - output_asm_insn ("l\t%4,0(%4)", op); - } + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_DEST (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); + off = INTVAL (offset); - output_asm_insn ("br\t%4", op); + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (first > BASE_REGNUM || last < BASE_REGNUM) + continue; - /* Output literal pool. */ - output_asm_insn (".align\t4", op); + if (cfun_frame_layout.first_restore_gpr != -1) + { + new_insn = restore_gprs (base, + off + (cfun_frame_layout.first_restore_gpr + - first) * UNITS_PER_WORD, + cfun_frame_layout.first_restore_gpr, + cfun_frame_layout.last_restore_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } - if (nonlocal && flag_pic == 2) - output_asm_insn (".long\t%0", op); - if (nonlocal) - { - op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); - SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL; + remove_insn (insn); + continue; } - targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8])); - if (!flag_pic) - output_asm_insn (".long\t%0", op); - else - output_asm_insn (".long\t%0-%5", op); - - if (op[6]) - { - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[6])); - output_asm_insn (".long\t%2", op); - } - if (op[7]) + if (GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_DEST (PATTERN (insn))) == REG + && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM + || (!TARGET_CPU_ZARCH + && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM)) + && GET_CODE (SET_SRC (PATTERN (insn))) == MEM) { - targetm.asm_out.internal_label (file, "L", - CODE_LABEL_NUMBER (op[7])); - output_asm_insn (".long\t%3", op); - } - } -} - -bool -s390_valid_pointer_mode (enum machine_mode mode) -{ - return (mode == SImode || (TARGET_64BIT && mode == DImode)); -} + set = PATTERN (insn); + first = REGNO (SET_DEST (set)); + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); + off = INTVAL (offset); -/* How to allocate a 'struct machine_function'. */ + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (cfun_frame_layout.first_restore_gpr != -1) + { + new_insn = restore_gprs (base, + off + (cfun_frame_layout.first_restore_gpr + - first) * UNITS_PER_WORD, + cfun_frame_layout.first_restore_gpr, + cfun_frame_layout.last_restore_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } -static struct machine_function * -s390_init_machine_status (void) -{ - return ggc_alloc_cleared (sizeof (struct machine_function)); + remove_insn (insn); + continue; + } + } } -/* Checks whether the given ARGUMENT_LIST would use a caller - saved register. This is used to decide whether sibling call - optimization could be performed on the respective function - call. */ +/* Perform machine-dependent processing. */ -static bool -s390_call_saved_register_used (tree argument_list) +static void +s390_reorg (void) { - CUMULATIVE_ARGS cum; - tree parameter; - enum machine_mode mode; - tree type; - rtx parm_rtx; - int reg; - - INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0); - - while (argument_list) - { - parameter = TREE_VALUE (argument_list); - argument_list = TREE_CHAIN (argument_list); - - if (!parameter) - abort(); - - /* For an undeclared variable passed as parameter we will get - an ERROR_MARK node here. */ - if (TREE_CODE (parameter) == ERROR_MARK) - return true; - - if (! (type = TREE_TYPE (parameter))) - abort(); - - if (! (mode = TYPE_MODE (TREE_TYPE (parameter)))) - abort(); - - if (pass_by_reference (&cum, mode, type, true)) - { - mode = Pmode; - type = build_pointer_type (type); - } - - parm_rtx = s390_function_arg (&cum, mode, type, 0); + bool pool_overflow = false; - s390_function_arg_advance (&cum, mode, type, 0); + /* Make sure all splits have been performed; splits after + machine_dependent_reorg might confuse insn length counts. */ + split_all_insns_noflow (); - if (parm_rtx && REG_P (parm_rtx)) - { - for (reg = 0; - reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx)); - reg++) - if (! call_used_regs[reg + REGNO (parm_rtx)]) - return true; - } - } - return false; -} -/* Return true if the given call expression can be - turned into a sibling call. - DECL holds the declaration of the function to be called whereas - EXP is the call expression itself. */ + /* Install the main literal pool and the associated base + register load insns. -static bool -s390_function_ok_for_sibcall (tree decl, tree exp) -{ - /* The TPF epilogue uses register 1. */ - if (TARGET_TPF_PROFILING) - return false; + In addition, there are two problematic situations we need + to correct: - /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved) - which would have to be restored before the sibcall. */ - if (!TARGET_64BIT && flag_pic && decl && TREE_PUBLIC (decl)) - return false; + - the literal pool might be > 4096 bytes in size, so that + some of its elements cannot be directly accessed - /* Register 6 on s390 is available as an argument register but unfortunately - "caller saved". This makes functions needing this register for arguments - not suitable for sibcalls. */ - if (TREE_OPERAND (exp, 1) - && s390_call_saved_register_used (TREE_OPERAND (exp, 1))) - return false; + - a branch target might be > 64K away from the branch, so that + it is not possible to use a PC-relative instruction. - return true; -} + To fix those, we split the single literal pool into multiple + pool chunks, reloading the pool base register at various + points throughout the function to ensure it always points to + the pool chunk the following code expects, and / or replace + PC-relative branches by absolute branches. -/* Return the fixed registers used for condition codes. */ + However, the two problems are interdependent: splitting the + literal pool can move a branch further away from its target, + causing the 64K limit to overflow, and on the other hand, + replacing a PC-relative branch by an absolute branch means + we need to put the branch target address into the literal + pool, possibly causing it to overflow. -static bool -s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) -{ - *p1 = CC_REGNUM; - *p2 = INVALID_REGNUM; - - return true; -} + So, we loop trying to fix up both problems until we manage + to satisfy both conditions at the same time. Note that the + loop is guaranteed to terminate as every pass of the loop + strictly decreases the total number of PC-relative branches + in the function. (This is not completely true as there + might be branch-over-pool insns introduced by chunkify_start. + Those never need to be split however.) */ -/* If two condition code modes are compatible, return a condition code - mode which is compatible with both. Otherwise, return - VOIDmode. */ + for (;;) + { + struct constant_pool *pool = NULL; -static enum machine_mode -s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) -{ - if (m1 == m2) - return m1; + /* Collect the literal pool. */ + if (!pool_overflow) + { + pool = s390_mainpool_start (); + if (!pool) + pool_overflow = true; + } - switch (m1) - { - case CCZmode: - if (m2 == CCUmode || m2 == CCTmode - || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode) - return m2; - return VOIDmode; + /* If literal pool overflowed, start to chunkify it. */ + if (pool_overflow) + pool = s390_chunkify_start (); - case CCSmode: - case CCUmode: - case CCTmode: - case CCSRmode: - case CCURmode: - if (m2 == CCZmode) - return m1; - - return VOIDmode; + /* Split out-of-range branches. If this has created new + literal pool entries, cancel current chunk list and + recompute it. zSeries machines have large branch + instructions, so we never need to split a branch. */ + if (!TARGET_CPU_ZARCH && s390_split_branches ()) + { + if (pool_overflow) + s390_chunkify_cancel (pool); + else + s390_mainpool_cancel (pool); - default: - return VOIDmode; + continue; + } + + /* If we made it up to here, both conditions are satisfied. + Finish up literal pool related changes. */ + if (pool_overflow) + s390_chunkify_finish (pool); + else + s390_mainpool_finish (pool); + + /* We're done splitting branches. */ + cfun->machine->split_branches_pending_p = false; + break; } - return VOIDmode; -} -/* This function is used by the call expanders of the machine description. - It emits the call insn itself together with the necessary operations - to adjust the target address and returns the emitted insn. - ADDR_LOCATION is the target address rtx - TLS_CALL the location of the thread-local symbol - RESULT_REG the register where the result of the call should be stored - RETADDR_REG the register where the return address should be stored - If this parameter is NULL_RTX the call is considered - to be a sibling call. */ + s390_optimize_prologue (); +} -rtx -s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, - rtx retaddr_reg) -{ - bool plt_call = false; - rtx insn; - rtx call; - rtx clobber; - rtvec vec; - /* Direct function calls need special treatment. */ - if (GET_CODE (addr_location) == SYMBOL_REF) - { - /* When calling a global routine in PIC mode, we must - replace the symbol itself with the PLT stub. */ - if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location)) - { - addr_location = gen_rtx_UNSPEC (Pmode, - gen_rtvec (1, addr_location), - UNSPEC_PLT); - addr_location = gen_rtx_CONST (Pmode, addr_location); - plt_call = true; - } +/* Initialize GCC target structure. */ - /* Unless we can use the bras(l) insn, force the - routine address into a register. */ - if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH) - { - if (flag_pic) - addr_location = legitimize_pic_address (addr_location, 0); - else - addr_location = force_reg (Pmode, addr_location); - } - } +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" +#undef TARGET_ASM_ALIGNED_DI_OP +#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER s390_assemble_integer - /* If it is already an indirect call or the code above moved the - SYMBOL_REF to somewhere else make sure the address can be found in - register 1. */ - if (retaddr_reg == NULL_RTX - && GET_CODE (addr_location) != SYMBOL_REF - && !plt_call) - { - emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location); - addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM); - } +#undef TARGET_ASM_OPEN_PAREN +#define TARGET_ASM_OPEN_PAREN "" - addr_location = gen_rtx_MEM (QImode, addr_location); - call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); +#undef TARGET_ASM_CLOSE_PAREN +#define TARGET_ASM_CLOSE_PAREN "" - if (result_reg != NULL_RTX) - call = gen_rtx_SET (VOIDmode, result_reg, call); +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD) +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION s390_handle_option - if (retaddr_reg != NULL_RTX) - { - clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info - if (tls_call != NULL_RTX) - vec = gen_rtvec (3, call, clobber, - gen_rtx_USE (VOIDmode, tls_call)); - else - vec = gen_rtvec (2, call, clobber); +#ifdef HAVE_AS_TLS +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS true +#endif +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem - call = gen_rtx_PARALLEL (VOIDmode, vec); - } +#undef TARGET_DELEGITIMIZE_ADDRESS +#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address - insn = emit_call_insn (call); +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY s390_return_in_memory - /* 31-bit PLT stubs and tls calls use the GOT register implicitly. */ - if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX) - { - /* s390_function_ok_for_sibcall should - have denied sibcalls in this case. */ - if (retaddr_reg == NULL_RTX) - abort (); +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS s390_init_builtins +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN s390_expand_builtin - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); - } - return insn; -} +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true -/* Implement CONDITIONAL_REGISTER_USAGE. */ +#undef TARGET_SCHED_ADJUST_PRIORITY +#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE s390_issue_rate +#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD +#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead -void -s390_conditional_register_usage (void) -{ - int i; +#undef TARGET_CANNOT_COPY_INSN_P +#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS s390_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST s390_address_cost - if (flag_pic) - { - fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; - call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; - } - if (TARGET_CPU_ZARCH) - { - fixed_regs[RETURN_REGNUM] = 0; - call_used_regs[RETURN_REGNUM] = 0; - } - if (TARGET_64BIT) - { - for (i = 24; i < 32; i++) - call_used_regs[i] = call_really_used_regs[i] = 0; - } - else - { - for (i = 18; i < 20; i++) - call_used_regs[i] = call_really_used_regs[i] = 0; - } +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg - if (TARGET_SOFT_FLOAT) - { - for (i = 16; i < 32; i++) - call_used_regs[i] = fixed_regs[i] = 1; - } -} +#undef TARGET_VALID_POINTER_MODE +#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode -/* Corresponding function to eh_return expander. */ +#undef TARGET_BUILD_BUILTIN_VA_LIST +#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list +#undef TARGET_GIMPLIFY_VA_ARG_EXPR +#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg -static GTY(()) rtx s390_tpf_eh_return_symbol; -void -s390_emit_tpf_eh_return (rtx target) -{ - rtx insn, reg; +#undef TARGET_PROMOTE_FUNCTION_ARGS +#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true +#undef TARGET_PROMOTE_FUNCTION_RETURN +#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference - if (!s390_tpf_eh_return_symbol) - s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return"); +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall - reg = gen_rtx_REG (Pmode, 2); +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs - emit_move_insn (reg, target); - insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg, - gen_rtx_REG (Pmode, RETURN_REGNUM)); - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg); +#undef TARGET_CC_MODES_COMPATIBLE +#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible - emit_move_insn (EH_RETURN_HANDLER_RTX, reg); -} +struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-s390.h" -- 2.7.4