diff options
Diffstat (limited to 'gcc/config/s390/s390.c')
-rw-r--r-- | gcc/config/s390/s390.c | 1978 |
1 files changed, 1513 insertions, 465 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 85952c5bacb..b0e63d26ed4 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -46,6 +46,7 @@ Boston, MA 02111-1307, USA. */ #include "target-def.h" #include "debug.h" #include "langhooks.h" +#include "optabs.h" static bool s390_assemble_integer PARAMS ((rtx, unsigned int, int)); static int s390_adjust_cost PARAMS ((rtx, rtx, rtx, int)); @@ -53,6 +54,7 @@ static int s390_adjust_priority PARAMS ((rtx, int)); static void s390_select_rtx_section PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT)); static void s390_encode_section_info PARAMS ((tree, int)); +static void s390_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, tree)); #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" @@ -61,12 +63,6 @@ static void s390_encode_section_info PARAMS ((tree, int)); #undef TARGET_ASM_INTEGER #define TARGET_ASM_INTEGER s390_assemble_integer -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE s390_function_prologue - -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE s390_function_epilogue - #undef TARGET_ASM_OPEN_PAREN #define TARGET_ASM_OPEN_PAREN "" @@ -85,6 +81,9 @@ static void s390_encode_section_info PARAMS ((tree, int)); #undef TARGET_ENCODE_SECTION_INFO #define TARGET_ENCODE_SECTION_INFO s390_encode_section_info +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk + struct gcc_target targetm = TARGET_INITIALIZER; extern int reload_completed; @@ -92,9 +91,6 @@ extern int reload_completed; /* The alias set for prologue/epilogue register save/restore. */ static int s390_sr_alias_set = 0; -/* Function count for creating unique internal labels in a compile unit. */ -int s390_function_count = 0; - /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ rtx s390_compare_op0, s390_compare_op1; @@ -121,7 +117,6 @@ struct s390_address struct s390_frame { int frame_pointer_p; - int return_reg_saved_p; int save_fprs_p; int first_save_gpr; int first_restore_gpr; @@ -136,18 +131,23 @@ static int s390_branch_condition_mask PARAMS ((rtx)); static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int)); static int check_mode PARAMS ((rtx, enum machine_mode *)); static int general_s_operand PARAMS ((rtx, enum machine_mode, int)); -static int s390_decompose_address PARAMS ((rtx, struct s390_address *, int)); +static int s390_decompose_address PARAMS ((rtx, struct s390_address *)); static int reg_used_in_mem_p PARAMS ((int, rtx)); static int addr_generation_dependency_p PARAMS ((rtx, rtx)); -static void s390_split_branches PARAMS ((void)); +static int s390_split_branches PARAMS ((rtx, bool *)); static void find_constant_pool_ref PARAMS ((rtx, rtx *)); static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx)); -static void s390_chunkify_pool PARAMS ((void)); -static int save_fprs_p PARAMS ((void)); +static int find_base_register_in_addr PARAMS ((struct s390_address *)); +static bool find_base_register_ref PARAMS ((rtx)); +static void replace_base_register_ref PARAMS ((rtx *, rtx)); +static void s390_optimize_prolog PARAMS ((int)); +static bool s390_fixup_clobbered_return_reg PARAMS ((rtx)); static int find_unused_clobbered_reg PARAMS ((void)); static void s390_frame_info PARAMS ((struct s390_frame *)); static rtx save_fpr PARAMS ((rtx, int, int)); static rtx restore_fpr PARAMS ((rtx, int, int)); +static rtx save_gprs PARAMS ((rtx, int, int, int)); +static rtx restore_gprs PARAMS ((rtx, int, int, int)); static int s390_function_arg_size PARAMS ((enum machine_mode, tree)); @@ -190,6 +190,12 @@ s390_match_ccmode_set (set, req_mode) && req_mode != CCSRmode && req_mode != CCURmode) return 0; break; + + case CCAPmode: + case CCANmode: + if (req_mode != CCAmode) + return 0; + break; default: abort (); @@ -282,6 +288,9 @@ s390_select_ccmode (code, op0, op1) { case EQ: case NE: + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) + return CCAPmode; if (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS || GET_CODE (op1) == NEG) return CCLmode; @@ -314,6 +323,14 @@ s390_select_ccmode (code, op0, op1) case LT: case GE: case GT: + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) + { + if (INTVAL (XEXP((op0), 1)) < 0) + return CCANmode; + else + return CCAPmode; + } case UNORDERED: case ORDERED: case UNEQ: @@ -469,6 +486,34 @@ s390_branch_condition_mask (code) } break; + case CCAPmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1 | CC3; + case GT: return CC2; + case LE: return CC0 | CC1 | CC3; + case GE: return CC0 | CC2; + default: + abort (); + } + break; + + case CCANmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1; + case GT: return CC2 | CC3; + case LE: return CC0 | CC1; + case GE: return CC0 | CC2 | CC3; + default: + abort (); + } + break; + case CCSmode: switch (GET_CODE (code)) { @@ -759,18 +804,13 @@ s390_extract_qi (op, mode, part) LEVEL is the optimization level specified; 2 if `-O2' is specified, 1 if `-O' is specified, and 0 if neither is specified. - SIZE is non-zero if `-Os' is specified and zero otherwise. */ + SIZE is nonzero if `-Os' is specified and zero otherwise. */ void optimization_options (level, size) int level ATTRIBUTE_UNUSED; int size ATTRIBUTE_UNUSED; { -#ifdef HAVE_decrement_and_branch_on_count - /* When optimizing, enable use of BRCT instruction. */ - if (level >= 1) - flag_branch_on_count_reg = 1; -#endif } void @@ -957,7 +997,7 @@ general_s_operand (op, mode, allow_immediate) case MEM: if (GET_CODE (XEXP (op, 0)) == ADDRESSOF) return 1; - if (s390_decompose_address (XEXP (op, 0), &addr, FALSE) + if (s390_decompose_address (XEXP (op, 0), &addr) && !addr.indx) return 1; break; @@ -1008,7 +1048,7 @@ q_constraint (op) if (GET_CODE (op) != MEM) return 0; - if (!s390_decompose_address (XEXP (op, 0), &addr, FALSE)) + if (!s390_decompose_address (XEXP (op, 0), &addr)) return 0; if (addr.indx) @@ -1017,6 +1057,19 @@ q_constraint (op) return 1; } +/* Return the cost of an address rtx ADDR. */ + +int +s390_address_cost (addr) + rtx addr; +{ + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return 1000; + + return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1); +} + /* Return true if OP is a valid operand for the BRAS instruction. OP is the current operation. MODE is the current operation mode. */ @@ -1269,15 +1322,6 @@ legitimate_reload_constant_p (op) && larl_operand (op, VOIDmode)) return 1; - /* If reload is completed, and we do not already have a - literal pool, and OP must be forced to the literal - pool, then something must have gone wrong earlier. - We *cannot* force the constant any more, because the - prolog generation already decided we don't need to - set up the base register. */ - if (reload_completed && !regs_ever_live[BASE_REGISTER]) - abort (); - /* Everything else cannot be handled without reload. */ return 0; } @@ -1382,21 +1426,13 @@ s390_plus_operand (op, mode) SCRATCH may be used as scratch register. */ void -s390_expand_plus_operand (target, src, scratch_in) +s390_expand_plus_operand (target, src, scratch) register rtx target; register rtx src; - register rtx scratch_in; + register rtx scratch; { - rtx sum1, sum2, scratch; - - /* ??? reload apparently does not ensure that the scratch register - and the target do not overlap. We absolutely require this to be - the case, however. Therefore the reload_in[sd]i patterns ask for - a double-sized scratch register, and if one part happens to be - equal to the target, we use the other one. */ - scratch = gen_rtx_REG (Pmode, REGNO (scratch_in)); - if (rtx_equal_p (scratch, target)) - scratch = gen_rtx_REG (Pmode, REGNO (scratch_in) + 1); + rtx sum1, sum2; + struct s390_address ad; /* src must be a PLUS; get its two operands. */ if (GET_CODE (src) != PLUS || GET_MODE (src) != Pmode) @@ -1407,67 +1443,49 @@ s390_expand_plus_operand (target, src, scratch_in) float registers occur in an address. */ sum1 = find_replacement (&XEXP (src, 0)); sum2 = find_replacement (&XEXP (src, 1)); - - /* Accept already valid addresses. */ src = gen_rtx_PLUS (Pmode, sum1, sum2); - if (s390_decompose_address (src, NULL, 1)) - { - src = legitimize_la_operand (src); - emit_insn (gen_rtx_SET (VOIDmode, target, src)); - return; - } - /* If one of the two operands is equal to the target, - make it the first one. If one is a constant, make - it the second one. */ - if (rtx_equal_p (target, sum2) - || GET_CODE (sum1) == CONST_INT) + /* If the address is already strictly valid, there's nothing to do. */ + if (!s390_decompose_address (src, &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))) { - rtx tem = sum2; - sum2 = sum1; - sum1 = tem; - } + /* Otherwise, one of the operands cannot be an address register; + we reload its value into the scratch register. */ + if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) + { + emit_move_insn (scratch, sum1); + sum1 = scratch; + } + if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15) + { + emit_move_insn (scratch, sum2); + sum2 = scratch; + } - /* If the first operand is not an address register, - we reload it into the target. */ - if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) - { - emit_move_insn (target, sum1); - sum1 = target; - } + /* According to the way these invalid addresses are generated + in reload.c, it should never happen (at least on s390) that + *neither* of the PLUS components, after find_replacements + was applied, is an address register. */ + if (sum1 == scratch && sum2 == scratch) + { + debug_rtx (src); + abort (); + } - /* Likewise for the second operand. However, take - care not to clobber the target if we already used - it for the first operand. Use the scratch instead. - Also, allow an immediate offset if it is in range. */ - if ((true_regnum (sum2) < 1 || true_regnum (sum2) > 15) - && !(GET_CODE (sum2) == CONST_INT - && INTVAL (sum2) >= 0 && INTVAL (sum2) < 4096)) - { - if (!rtx_equal_p (target, sum1)) - { - emit_move_insn (target, sum2); - sum2 = target; - } - else - { - emit_move_insn (scratch, sum2); - sum2 = scratch; - } + src = gen_rtx_PLUS (Pmode, sum1, sum2); } /* Emit the LOAD ADDRESS pattern. Note that reload of PLUS is only ever performed on addresses, so we can mark the sum as legitimate for LA in any case. */ - src = gen_rtx_PLUS (Pmode, sum1, sum2); - src = legitimize_la_operand (src); - emit_insn (gen_rtx_SET (VOIDmode, target, src)); + s390_load_address (target, src); } /* Decompose a RTL expression ADDR for a memory address into - its components, returned in OUT. The boolean STRICT - specifies whether strict register checking applies. + 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. @@ -1477,10 +1495,9 @@ s390_expand_plus_operand (target, src, scratch_in) canonical form so that they will be recognized. */ static int -s390_decompose_address (addr, out, strict) +s390_decompose_address (addr, out) register rtx addr; struct s390_address *out; - int strict; { rtx base = NULL_RTX; rtx indx = NULL_RTX; @@ -1545,16 +1562,15 @@ s390_decompose_address (addr, out, strict) if (GET_CODE (base) != REG || GET_MODE (base) != Pmode) return FALSE; - if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base)) - || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base))) - return FALSE; - if (REGNO (base) == BASE_REGISTER || 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 + || (REGNO (base) >= FIRST_VIRTUAL_REGISTER + && REGNO (base) <= LAST_VIRTUAL_REGISTER) || (flag_pic && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) pointer = TRUE; @@ -1574,16 +1590,15 @@ s390_decompose_address (addr, out, strict) if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode) return FALSE; - if ((strict && ! REG_OK_FOR_BASE_STRICT_P (indx)) - || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (indx))) - return FALSE; - if (REGNO (indx) == BASE_REGISTER || 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 + || (REGNO (indx) >= FIRST_VIRTUAL_REGISTER + && REGNO (indx) <= LAST_VIRTUAL_REGISTER) || (flag_pic && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) pointer = TRUE; @@ -1708,7 +1723,26 @@ legitimate_address_p (mode, addr, strict) register rtx addr; int strict; { - return s390_decompose_address (addr, NULL, strict); + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return FALSE; + + if (strict) + { + if (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + return FALSE; + if (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)) + return FALSE; + } + else + { + if (ad.base && !REG_OK_FOR_BASE_NONSTRICT_P (ad.base)) + return FALSE; + if (ad.indx && !REG_OK_FOR_INDEX_NONSTRICT_P (ad.indx)) + return FALSE; + } + + return TRUE; } /* Return 1 if OP is a valid operand for the LA instruction. @@ -1720,7 +1754,7 @@ legitimate_la_operand_p (op) register rtx op; { struct s390_address addr; - if (!s390_decompose_address (op, &addr, FALSE)) + if (!s390_decompose_address (op, &addr)) return FALSE; if (TARGET_64BIT || addr.pointer) @@ -1729,30 +1763,47 @@ legitimate_la_operand_p (op) return FALSE; } -/* Return a modified variant of OP that is guaranteed to - be accepted by legitimate_la_operand_p. */ - -rtx -legitimize_la_operand (op) +/* Return 1 if OP is a valid operand for the LA instruction, + and we prefer to use LA over addition to compute it. + If STRICT is true, only accept operands that will never + change to something we cannot recognize as preferred. */ + +int +preferred_la_operand_p (op, strict) register rtx op; + int strict; { struct s390_address addr; - if (!s390_decompose_address (op, &addr, FALSE)) - abort (); + if (!s390_decompose_address (op, &addr)) + return FALSE; - if (TARGET_64BIT || addr.pointer) - return op; + if (!TARGET_64BIT && !addr.pointer) + return FALSE; - if (!addr.base) - abort (); + if (addr.pointer) + return TRUE; - op = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr.base), 101); - if (addr.indx) - op = gen_rtx_PLUS (Pmode, op, addr.indx); - if (addr.disp) - op = gen_rtx_PLUS (Pmode, op, addr.disp); + if (!strict) + if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) + || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) + return TRUE; + + return FALSE; +} + +/* Emit a forced load-address operation to load SRC into DST. + This will use the LOAD ADDRESS instruction even in situations + where legitimate_la_operand_p (SRC) returns false. */ - return op; +void +s390_load_address (dst, src) + rtx dst; + rtx src; +{ + if (TARGET_64BIT) + emit_move_insn (dst, src); + else + emit_insn (gen_force_la_31 (dst, src)); } /* Return a legitimate reference for ORIG (an address) using the @@ -1826,7 +1877,8 @@ legitimize_pic_address (orig, reg) /* Assume GOT offset < 4k. This is handled the same way in both 31- and 64-bit code (@GOT12). */ - current_function_uses_pic_offset_table = 1; + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 110); new = gen_rtx_CONST (Pmode, new); @@ -1859,7 +1911,8 @@ legitimize_pic_address (orig, reg) rtx temp = gen_reg_rtx (Pmode); - current_function_uses_pic_offset_table = 1; + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 112); addr = gen_rtx_CONST (SImode, addr); @@ -2138,6 +2191,312 @@ legitimize_address (x, oldx, mode) return x; } +/* Emit code to move LEN bytes from DST to SRC. */ + +void +s390_expand_movstr (dst, src, len) + rtx dst; + rtx src; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_movstr_short_64 : gen_movstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = + TARGET_64BIT ? gen_movstr_long_64 : gen_movstr_long_31; + + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + emit_insn ((*gen_short) (dst, src, GEN_INT (INTVAL (len) - 1))); + } + + else if (TARGET_MVCLE) + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (dst, 0), NULL_RTX)); + emit_move_insn (gen_highpart (single_mode, reg1), + force_operand (XEXP (src, 0), NULL_RTX)); + + convert_move (gen_lowpart (single_mode, reg0), len, 1); + convert_move (gen_lowpart (single_mode, reg1), len, 1); + + emit_insn ((*gen_long) (reg0, reg1, reg0, reg1)); + } + + else + { + rtx dst_addr, src_addr, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + dst_addr = gen_reg_rtx (Pmode); + src_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + src = change_address (src, VOIDmode, src_addr); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (dst, src, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + s390_load_address (src_addr, + gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (dst, src, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to clear LEN bytes at DST. */ + +void +s390_expand_clrstr (dst, len) + rtx dst; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx)) = + TARGET_64BIT ? gen_clrstr_short_64 : gen_clrstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_clrstr_long_64 : gen_clrstr_long_31; + + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + emit_insn ((*gen_short) (dst, GEN_INT (INTVAL (len) - 1))); + } + + else if (TARGET_MVCLE) + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (dst, 0), NULL_RTX)); + convert_move (gen_lowpart (single_mode, reg0), len, 1); + + emit_move_insn (gen_highpart (single_mode, reg1), const0_rtx); + emit_move_insn (gen_lowpart (single_mode, reg1), const0_rtx); + + emit_insn ((*gen_long) (reg0, reg1, reg0)); + } + + else + { + rtx dst_addr, src_addr, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + dst_addr = gen_reg_rtx (Pmode); + src_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (dst, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (dst, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to compare LEN bytes at OP0 with those at OP1, + and return the result in TARGET. */ + +void +s390_expand_cmpstr (target, op0, op1, len) + rtx target; + rtx op0; + rtx op1; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_cmpstr_short_64 : gen_cmpstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = + TARGET_64BIT ? gen_cmpstr_long_64 : gen_cmpstr_long_31; + rtx (*gen_result) PARAMS ((rtx)) = + GET_MODE (target) == DImode ? gen_cmpint_di : gen_cmpint_si; + + op0 = protect_from_queue (op0, 0); + op1 = protect_from_queue (op1, 0); + len = protect_from_queue (len, 0); + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + { + emit_insn ((*gen_short) (op0, op1, GEN_INT (INTVAL (len) - 1))); + emit_insn ((*gen_result) (target)); + } + else + emit_move_insn (target, const0_rtx); + } + + else if (TARGET_MVCLE) + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (op0, 0), NULL_RTX)); + emit_move_insn (gen_highpart (single_mode, reg1), + force_operand (XEXP (op1, 0), NULL_RTX)); + + convert_move (gen_lowpart (single_mode, reg0), len, 1); + convert_move (gen_lowpart (single_mode, reg1), len, 1); + + emit_insn ((*gen_long) (reg0, reg1, reg0, reg1)); + emit_insn ((*gen_result) (target)); + } + + else + { + rtx addr0, addr1, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + addr0 = gen_reg_rtx (Pmode); + addr1 = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX)); + emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); + op0 = change_address (op0, VOIDmode, addr0); + op1 = change_address (op1, VOIDmode, addr1); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (op0, op1, GEN_INT (255))); + temp = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCSmode, 33), const0_rtx); + temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp, + gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx); + temp = gen_rtx_SET (VOIDmode, pc_rtx, temp); + emit_jump_insn (temp); + + s390_load_address (addr0, + gen_rtx_PLUS (Pmode, addr0, GEN_INT (256))); + s390_load_address (addr1, + gen_rtx_PLUS (Pmode, addr1, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (op0, op1, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + + emit_insn ((*gen_result) (target)); + } +} + /* In the name of slightly smaller debug output, and to cater to general assembler losage, recognize various UNSPEC sequences and turn them back into a direct symbol reference. */ @@ -2217,8 +2576,13 @@ s390_output_symbolic_const (file, x) switch (XINT (x, 1)) { case 100: + case 104: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "-.LT%d", current_function_funcdef_no); + break; + case 105: + fprintf (file, ".LT%d-", current_function_funcdef_no); s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); - fprintf (file, "-.LT%X", s390_function_count); break; case 110: s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); @@ -2238,7 +2602,7 @@ s390_output_symbolic_const (file, x) break; case 114: s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); - fprintf (file, "@PLT-.LT%X", s390_function_count); + fprintf (file, "@PLT-.LT%d", current_function_funcdef_no); break; default: output_operand_lossage ("invalid UNSPEC as operand (2)"); @@ -2262,7 +2626,9 @@ print_operand_address (file, addr) { struct s390_address ad; - if (!s390_decompose_address (addr, &ad, TRUE)) + if (!s390_decompose_address (addr, &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))) output_operand_lossage ("Cannot decompose address."); if (ad.disp) @@ -2313,7 +2679,8 @@ print_operand (file, x, code) struct s390_address ad; if (GET_CODE (x) != MEM - || !s390_decompose_address (XEXP (x, 0), &ad, TRUE) + || !s390_decompose_address (XEXP (x, 0), &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) || ad.indx) abort (); @@ -2329,7 +2696,8 @@ print_operand (file, x, code) struct s390_address ad; if (GET_CODE (x) != MEM - || !s390_decompose_address (XEXP (x, 0), &ad, TRUE) + || !s390_decompose_address (XEXP (x, 0), &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) || ad.indx) abort (); @@ -2484,7 +2852,11 @@ addr_generation_dependency_p (dep_rtx, insn) if (GET_CODE (dep_rtx) == SET) { target = SET_DEST (dep_rtx); - + if (GET_CODE (target) == STRICT_LOW_PART) + target = XEXP (target, 0); + while (GET_CODE (target) == SUBREG) + target = SUBREG_REG (target); + if (GET_CODE (target) == REG) { int regno = REGNO (target); @@ -2625,18 +2997,25 @@ s390_adjust_priority (insn, priority) } -/* Split all branches that exceed the maximum distance. */ +/* Split all branches that exceed the maximum distance. + Returns true if this created a new literal pool entry. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. If this is + done, TEMP_USED is set to true. */ -static void -s390_split_branches () +static int +s390_split_branches (temp_reg, temp_used) + rtx temp_reg; + bool *temp_used; { - rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); - rtx insn, pat, label, target, jump, tmp; + int new_literal = 0; + rtx insn, pat, tmp, target; + rtx *label; - /* In 64-bit mode we can jump +- 4GB. */ + /* We need correct insn addresses. */ - if (TARGET_64BIT) - return; + shorten_branches (get_insns ()); /* Find all branches that exceed 64KB, and split them. */ @@ -2646,61 +3025,66 @@ s390_split_branches () continue; pat = PATTERN (insn); - if (GET_CODE (pat) != SET) + 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); + 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); + label = &XEXP (SET_SRC (pat), 1); else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) - label = XEXP (SET_SRC (pat), 2); + label = &XEXP (SET_SRC (pat), 2); else continue; } else continue; - if (get_attr_length (insn) == 4) + if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4)) continue; - if (flag_pic) + *temp_used = 1; + + if (TARGET_64BIT) { - target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, label), 100); - target = gen_rtx_CONST (SImode, target); - target = force_const_mem (SImode, target); - jump = gen_rtx_REG (Pmode, BASE_REGISTER); - jump = gen_rtx_PLUS (Pmode, jump, temp_reg); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, *label), insn); + INSN_ADDRESSES_NEW (tmp, -1); + + target = temp_reg; } - else + else if (!flag_pic) { - target = force_const_mem (Pmode, label); - jump = temp_reg; - } + 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); - if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) + target = temp_reg; + } + else { - if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0), - jump, pc_rtx); - else - jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0), - pc_rtx, jump); + new_literal = 1; + tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 104); + tmp = gen_rtx_CONST (SImode, tmp); + tmp = force_const_mem (SImode, tmp); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); + INSN_ADDRESSES_NEW (tmp, -1); + + target = gen_rtx_REG (Pmode, BASE_REGISTER); + target = gen_rtx_PLUS (Pmode, target, temp_reg); } - tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); - INSN_ADDRESSES_NEW (tmp, -1); - - tmp = emit_jump_insn_before (gen_rtx_SET (VOIDmode, pc_rtx, jump), insn); - INSN_ADDRESSES_NEW (tmp, -1); - - remove_insn (insn); - insn = tmp; + if (!validate_change (insn, label, target, 0)) + abort (); } + + return new_literal; } @@ -2818,6 +3202,153 @@ replace_constant_pool_ref (x, ref, addr) } } +/* Check whether ADDR is an address that uses the base register, + without actually constituting a literal pool access. (This happens + in 31-bit PIC mode, where the base register is used as anchor for + relative addressing of local symbols.) + + Returns 1 if the base register occupies the base slot, + returns 2 if the base register occupies the index slot, + returns 0 if the address is not of this form. */ + +static int +find_base_register_in_addr (addr) + struct s390_address *addr; +{ + /* If DISP is complex, we might have a literal pool reference. */ + if (addr->disp && GET_CODE (addr->disp) != CONST_INT) + return 0; + + if (addr->base && REG_P (addr->base) && REGNO (addr->base) == BASE_REGISTER) + return 1; + + if (addr->indx && REG_P (addr->indx) && REGNO (addr->indx) == BASE_REGISTER) + return 2; + + return 0; +} + +/* Return true if X contains an address that uses the base register, + without actually constituting a literal pool access. */ + +static bool +find_base_register_ref (x) + rtx x; +{ + bool retv = FALSE; + struct s390_address addr; + int i, j; + const char *fmt; + + /* Addresses can only occur inside a MEM ... */ + if (GET_CODE (x) == MEM) + { + if (s390_decompose_address (XEXP (x, 0), &addr) + && find_base_register_in_addr (&addr)) + return TRUE; + } + + /* ... or a load-address type pattern. */ + if (GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG) + { + if (s390_decompose_address (SET_SRC (x), &addr) + && find_base_register_in_addr (&addr)) + return TRUE; + } + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + retv |= find_base_register_ref (XEXP (x, i)); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (x, i); j++) + retv |= find_base_register_ref (XVECEXP (x, i, j)); + } + } + + return retv; +} + +/* If X contains an address that uses the base register, + without actually constituting a literal pool access, + replace the base register with REPL in all such cases. + + Handles both MEMs and load address patterns. */ + +static void +replace_base_register_ref (x, repl) + rtx *x; + rtx repl; +{ + struct s390_address addr; + rtx new_addr; + int i, j, pos; + const char *fmt; + + /* Addresses can only occur inside a MEM ... */ + if (GET_CODE (*x) == MEM) + { + if (s390_decompose_address (XEXP (*x, 0), &addr) + && (pos = find_base_register_in_addr (&addr))) + { + if (pos == 1) + addr.base = repl; + else + addr.indx = repl; + + new_addr = addr.base; + if (addr.indx) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx); + if (addr.disp) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp); + + *x = replace_equiv_address (*x, new_addr); + return; + } + } + + /* ... or a load-address type pattern. */ + if (GET_CODE (*x) == SET && GET_CODE (SET_DEST (*x)) == REG) + { + if (s390_decompose_address (SET_SRC (*x), &addr) + && (pos = find_base_register_in_addr (&addr))) + { + if (pos == 1) + addr.base = repl; + else + addr.indx = repl; + + new_addr = addr.base; + if (addr.indx) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx); + if (addr.disp) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp); + + SET_SRC (*x) = new_addr; + return; + } + } + + fmt = GET_RTX_FORMAT (GET_CODE (*x)); + for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + replace_base_register_ref (&XEXP (*x, i), repl); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (*x, i); j++) + replace_base_register_ref (&XVECEXP (*x, i, j), repl); + } + } +} + + /* We keep a list of constants we which we have to add to internal constant tables in the middle of large functions. */ @@ -2849,17 +3380,26 @@ struct constant_pool { struct constant_pool *next; rtx first_insn; - rtx last_insn; + rtx pool_insn; + bitmap insns; struct constant *constants[NR_C_MODES]; rtx label; int size; + bool anchor; }; +static struct constant_pool * s390_chunkify_start PARAMS ((rtx, bool *)); +static void s390_chunkify_finish PARAMS ((struct constant_pool *, rtx)); +static void s390_chunkify_cancel PARAMS ((struct constant_pool *)); + static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx)); static void s390_end_pool PARAMS ((struct constant_pool *, rtx)); +static void s390_add_pool_insn PARAMS ((struct constant_pool *, rtx)); static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx)); -static rtx s390_add_pool PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static void s390_add_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static rtx s390_find_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static void s390_add_anchor PARAMS ((struct constant_pool *)); static rtx s390_dump_pool PARAMS ((struct constant_pool *)); static void s390_free_pool PARAMS ((struct constant_pool *)); @@ -2881,9 +3421,11 @@ s390_start_pool (pool_list, insn) pool->label = gen_label_rtx (); pool->first_insn = insn; - pool->last_insn = NULL_RTX; + pool->pool_insn = NULL_RTX; + pool->insns = BITMAP_XMALLOC (); pool->size = 0; - + pool->anchor = FALSE; + for (prev = pool_list; *prev; prev = &(*prev)->next) ; *prev = pool; @@ -2891,14 +3433,31 @@ s390_start_pool (pool_list, insn) return pool; } -/* End range of instructions covered by POOL at INSN. */ +/* End range of instructions covered by POOL at INSN and emit + placeholder insn representing the pool. */ static void s390_end_pool (pool, insn) struct constant_pool *pool; rtx insn; { - pool->last_insn = insn; + rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */); + + if (!insn) + insn = get_last_insn (); + + pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); +} + +/* Add INSN to the list of insns covered by POOL. */ + +static void +s390_add_pool_insn (pool, insn) + struct constant_pool *pool; + rtx insn; +{ + bitmap_set_bit (pool->insns, INSN_UID (insn)); } /* Return pool out of POOL_LIST that covers INSN. */ @@ -2908,33 +3467,24 @@ s390_find_pool (pool_list, insn) struct constant_pool *pool_list; rtx insn; { - int addr = INSN_ADDRESSES (INSN_UID (insn)); struct constant_pool *pool; - if (addr == -1) - return NULL; - for (pool = pool_list; pool; pool = pool->next) - if (INSN_ADDRESSES (INSN_UID (pool->first_insn)) <= addr - && (pool->last_insn == NULL_RTX - || INSN_ADDRESSES (INSN_UID (pool->last_insn)) > addr)) + if (bitmap_bit_p (pool->insns, INSN_UID (insn))) break; return pool; } -/* Add constant VAL of mode MODE to the constant pool POOL. - Return an RTX describing the distance from the start of - the pool to the location of the new constant. */ +/* Add constant VAL of mode MODE to the constant pool POOL. */ -static rtx -s390_add_pool (pool, val, mode) +static void +s390_add_constant (pool, val, mode) struct constant_pool *pool; rtx val; enum machine_mode mode; { struct constant *c; - rtx offset; int i; for (i = 0; i < NR_C_MODES; i++) @@ -2956,13 +3506,54 @@ s390_add_pool (pool, val, mode) pool->constants[i] = c; pool->size += GET_MODE_SIZE (mode); } +} - offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), - gen_rtx_LABEL_REF (Pmode, pool->label)); +/* Find constant VAL of mode MODE in the constant pool POOL. + Return an RTX describing the distance from the start of + the pool to the location of the new constant. */ + +static rtx +s390_find_constant (pool, val, mode) + struct constant_pool *pool; + rtx val; + enum machine_mode mode; +{ + struct constant *c; + rtx offset; + int i; + + for (i = 0; i < NR_C_MODES; i++) + if (constant_modes[i] == mode) + break; + if (i == NR_C_MODES) + abort (); + + for (c = pool->constants[i]; c != NULL; c = c->next) + if (rtx_equal_p (val, c->value)) + break; + + if (c == NULL) + abort (); + + offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), + gen_rtx_LABEL_REF (Pmode, pool->label)); offset = gen_rtx_CONST (Pmode, offset); return offset; } +/* Set 'anchor' flag in POOL. */ + +static void +s390_add_anchor (pool) + struct constant_pool *pool; +{ + if (!pool->anchor) + { + pool->anchor = TRUE; + pool->size += 4; + } +} + /* Dump out the constants in POOL. */ static rtx @@ -2973,31 +3564,47 @@ s390_dump_pool (pool) rtx insn; int i; - /* Select location to put literal pool. */ - if (TARGET_64BIT) - insn = get_last_insn (); - else - insn = pool->last_insn? pool->last_insn : get_last_insn (); - /* Pool start insn switches to proper section and guarantees necessary alignment. */ if (TARGET_64BIT) - insn = emit_insn_after (gen_pool_start_64 (), insn); + insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn); else - insn = emit_insn_after (gen_pool_start_31 (), insn); + insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn); INSN_ADDRESSES_NEW (insn, -1); insn = emit_label_after (pool->label, insn); INSN_ADDRESSES_NEW (insn, -1); + /* Emit anchor if we need one. */ + if (pool->anchor) + { + rtx anchor = gen_rtx_LABEL_REF (VOIDmode, pool->label); + anchor = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, anchor), 105); + anchor = gen_rtx_CONST (VOIDmode, anchor); + insn = emit_insn_after (gen_consttable_si (anchor), insn); + INSN_ADDRESSES_NEW (insn, -1); + } + /* Dump constants in descending alignment requirement order, ensuring proper alignment for every constant. */ for (i = 0; i < NR_C_MODES; i++) for (c = pool->constants[i]; c; c = c->next) { + /* Convert 104 unspecs to pool-relative references. */ + rtx value = c->value; + if (GET_CODE (value) == CONST + && GET_CODE (XEXP (value, 0)) == UNSPEC + && XINT (XEXP (value, 0), 1) == 104 + && XVECLEN (XEXP (value, 0), 0) == 1) + { + value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0), + gen_rtx_LABEL_REF (VOIDmode, pool->label)); + value = gen_rtx_CONST (VOIDmode, value); + } + insn = emit_label_after (c->label, insn); INSN_ADDRESSES_NEW (insn, -1); - insn = emit_insn_after (gen_consttable[i] (c->value), insn); + insn = emit_insn_after (gen_consttable[i] (value), insn); INSN_ADDRESSES_NEW (insn, -1); } @@ -3012,6 +3619,9 @@ s390_dump_pool (pool) insn = emit_barrier_after (insn); INSN_ADDRESSES_NEW (insn, -1); + /* Remove placeholder insn. */ + remove_insn (pool->pool_insn); + return insn; } @@ -3034,57 +3644,86 @@ s390_free_pool (pool) } } + BITMAP_XFREE (pool->insns); free (pool); } -/* Used in s390.md for branch length calculation. */ -int s390_pool_overflow = 0; -/* Chunkify the literal pool if required. */ +/* Chunkify the literal pool if required. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. If this is + done, TEMP_USED is set to true. */ #define S390_POOL_CHUNK_MIN 0xc00 #define S390_POOL_CHUNK_MAX 0xe00 -static void -s390_chunkify_pool () +static struct constant_pool * +s390_chunkify_start (temp_reg, temp_used) + rtx temp_reg; + bool *temp_used; { - rtx base_reg = gen_rtx_REG (Pmode, - TARGET_64BIT? BASE_REGISTER : RETURN_REGNUM); + rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER); struct constant_pool *curr_pool = NULL, *pool_list = NULL; int extra_size = 0; bitmap far_labels; rtx insn; + rtx (*gen_reload_base) PARAMS ((rtx, rtx)) = + TARGET_64BIT? gen_reload_base_64 : gen_reload_base_31; + + /* Do we need to chunkify the literal pool? */ if (get_pool_size () < S390_POOL_CHUNK_MAX) - return; + return NULL; + + /* We need correct insn addresses. */ + + shorten_branches (get_insns ()); /* Scan all insns and move literals to pool chunks. - Replace all occurrances of literal pool references - by explicit references to pool chunk entries. */ + Also, emit anchor reload insns before every insn that uses + the literal pool base register as anchor pointer. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { - if (GET_CODE (insn) == INSN) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) { - rtx addr, pool_ref = NULL_RTX; + rtx pool_ref = NULL_RTX; find_constant_pool_ref (PATTERN (insn), &pool_ref); if (pool_ref) { if (!curr_pool) curr_pool = s390_start_pool (&pool_list, insn); - addr = s390_add_pool (curr_pool, get_pool_constant (pool_ref), - get_pool_mode (pool_ref)); + s390_add_constant (curr_pool, get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + s390_add_pool_insn (curr_pool, insn); + } + + else if (!TARGET_64BIT && flag_pic + && find_base_register_ref (PATTERN (insn))) + { + rtx new = gen_reload_anchor (temp_reg, base_reg); + new = emit_insn_before (new, insn); + INSN_ADDRESSES_NEW (new, INSN_ADDRESSES (INSN_UID (insn))); + extra_size += 8; + *temp_used = 1; + + if (!curr_pool) + curr_pool = s390_start_pool (&pool_list, new); - addr = gen_rtx_PLUS (Pmode, base_reg, addr); - replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); - INSN_CODE (insn) = -1; + s390_add_anchor (curr_pool); + s390_add_pool_insn (curr_pool, insn); } } + if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL) + if (curr_pool) + s390_add_pool_insn (curr_pool, insn); + if (!curr_pool || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn) || INSN_ADDRESSES (INSN_UID (insn)) == -1) @@ -3095,7 +3734,7 @@ s390_chunkify_pool () if (curr_pool->size < S390_POOL_CHUNK_MAX) continue; - s390_end_pool (curr_pool, insn); + s390_end_pool (curr_pool, NULL_RTX); curr_pool = NULL; } else @@ -3108,11 +3747,8 @@ s390_chunkify_pool () Those will have an effect on code size, which we need to consider here. This calculation makes rather pessimistic worst-case assumptions. */ - if (GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == JUMP_INSN) + if (GET_CODE (insn) == CODE_LABEL) extra_size += 6; - else if (GET_CODE (insn) == CALL_INSN) - extra_size += 4; if (chunk_size < S390_POOL_CHUNK_MIN && curr_pool->size < S390_POOL_CHUNK_MIN) @@ -3128,12 +3764,22 @@ s390_chunkify_pool () /* ... so if we don't find one in time, create one. */ else if ((chunk_size > S390_POOL_CHUNK_MAX - || curr_pool->size > S390_POOL_CHUNK_MAX) - && (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)) + || curr_pool->size > S390_POOL_CHUNK_MAX)) { - int addr = INSN_ADDRESSES (INSN_UID (insn)); rtx label, jump, barrier; + /* We can insert the barrier only after a 'real' insn. */ + if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + continue; + if (get_attr_length (insn) == 0) + continue; + + /* Don't separate insns created by s390_split_branches. */ + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && rtx_equal_p (SET_DEST (PATTERN (insn)), temp_reg)) + continue; + label = gen_label_rtx (); jump = emit_jump_insn_after (gen_jump (label), insn); barrier = emit_barrier_after (jump); @@ -3141,8 +3787,8 @@ s390_chunkify_pool () JUMP_LABEL (jump) = label; LABEL_NUSES (label) = 1; - INSN_ADDRESSES_NEW (jump, addr+1); - INSN_ADDRESSES_NEW (barrier, addr+1); + INSN_ADDRESSES_NEW (jump, -1); + INSN_ADDRESSES_NEW (barrier, -1); INSN_ADDRESSES_NEW (insn, -1); s390_end_pool (curr_pool, barrier); @@ -3152,10 +3798,8 @@ s390_chunkify_pool () } } - /* Dump out all literal pools. */ - - for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) - s390_dump_pool (curr_pool); + if (curr_pool) + s390_end_pool (curr_pool, NULL_RTX); /* Find all labels that are branched into @@ -3189,22 +3833,12 @@ s390_chunkify_pool () else if (GET_CODE (insn) == JUMP_INSN) { rtx pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + if (GET_CODE (pat) == SET) { - rtx label = 0; - - if (GET_CODE (SET_SRC (pat)) == LABEL_REF) - { - label = XEXP (SET_SRC (pat), 0); - } - else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) - { - if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - label = XEXP (XEXP (SET_SRC (pat), 1), 0); - else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) - label = XEXP (XEXP (SET_SRC (pat), 2), 0); - } - + rtx label = JUMP_LABEL (insn); if (label) { if (s390_find_pool (pool_list, label) @@ -3245,19 +3879,11 @@ s390_chunkify_pool () /* Insert base register reload insns before every pool. */ for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) - if (TARGET_64BIT) - { - rtx pool_ref = gen_rtx_LABEL_REF (Pmode, curr_pool->label); - rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref); - rtx insn = curr_pool->first_insn; - INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); - } - else - { - rtx new_insn = gen_reload_base (base_reg, curr_pool->label); - rtx insn = curr_pool->first_insn; - INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); - } + { + rtx new_insn = gen_reload_base (base_reg, curr_pool->label); + rtx insn = curr_pool->first_insn; + INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); + } /* Insert base register reload insns at every far label. */ @@ -3268,60 +3894,137 @@ s390_chunkify_pool () struct constant_pool *pool = s390_find_pool (pool_list, insn); if (pool) { - if (TARGET_64BIT) - { - rtx pool_ref = gen_rtx_LABEL_REF (Pmode, pool->label); - rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } - else - { - rtx new_insn = gen_reload_base (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } + rtx new_insn = gen_reload_base (base_reg, pool->label); + INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); } } - /* Insert base register reload insns after every call if necessary. */ - - if (REGNO (base_reg) == RETURN_REGNUM) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - struct constant_pool *pool = s390_find_pool (pool_list, insn); - if (pool) - { - rtx new_insn = gen_reload_base2 (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } - } + + BITMAP_XFREE (far_labels); /* Recompute insn addresses. */ - s390_pool_overflow = 1; init_insn_lengths (); shorten_branches (get_insns ()); - s390_pool_overflow = 0; - /* Insert base register reload insns after far branches. */ + return pool_list; +} - if (!TARGET_64BIT) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == SET - && get_attr_length (insn) >= 12) - { - struct constant_pool *pool = s390_find_pool (pool_list, insn); - if (pool) +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + After we have decided to use this list, finish implementing + all changes to the current function as required. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. */ + +static void +s390_chunkify_finish (pool_list, temp_reg) + struct constant_pool *pool_list; + rtx temp_reg; +{ + rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER); + struct constant_pool *curr_pool = NULL; + rtx insn; + + + /* Replace all literal pool references. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + curr_pool = s390_find_pool (pool_list, insn); + if (!curr_pool) + continue; + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx addr, pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + addr = s390_find_constant (curr_pool, get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + addr = gen_rtx_PLUS (Pmode, base_reg, addr); + replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); + INSN_CODE (insn) = -1; + } + + else if (!TARGET_64BIT && flag_pic + && find_base_register_ref (PATTERN (insn))) { - rtx new_insn = gen_reload_base (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); + replace_base_register_ref (&PATTERN (insn), temp_reg); } + } + } + + /* Dump out all literal pools. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + s390_dump_pool (curr_pool); + + /* Free pool list. */ + + while (pool_list) + { + struct constant_pool *next = pool_list->next; + s390_free_pool (pool_list); + pool_list = next; + } +} + +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + We have decided we cannot use this list, so revert all changes + to the current function that were done by s390_chunkify_start. */ + +static void +s390_chunkify_cancel (pool_list) + struct constant_pool *pool_list; +{ + struct constant_pool *curr_pool = NULL; + rtx insn; + + /* Remove all pool placeholder insns. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + { + /* Did we insert an extra barrier? Remove it. */ + rtx barrier = PREV_INSN (curr_pool->pool_insn); + rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX; + rtx label = NEXT_INSN (curr_pool->pool_insn); + + if (jump && GET_CODE (jump) == JUMP_INSN + && barrier && GET_CODE (barrier) == BARRIER + && label && GET_CODE (label) == CODE_LABEL + && GET_CODE (PATTERN (jump)) == SET + && SET_DEST (PATTERN (jump)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF + && XEXP (SET_SRC (PATTERN (jump)), 0) == label) + { + remove_insn (jump); + remove_insn (barrier); + remove_insn (label); } + remove_insn (curr_pool->pool_insn); + } - /* Free all memory. */ + /* Remove all base/anchor register reload insns. */ + + for (insn = get_insns (); insn; ) + { + rtx next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC + && (XINT (SET_SRC (PATTERN (insn)), 1) == 210 + || XINT (SET_SRC (PATTERN (insn)), 1) == 211)) + remove_insn (insn); + + insn = next_insn; + } + + /* Free pool list. */ while (pool_list) { @@ -3329,8 +4032,6 @@ s390_chunkify_pool () s390_free_pool (pool_list); pool_list = next; } - - BITMAP_XFREE (far_labels); } @@ -3352,17 +4053,17 @@ s390_output_constant_pool (file) { if (TARGET_64BIT) { - fprintf (file, "\tlarl\t%s,.LT%X\n", reg_names[BASE_REGISTER], - s390_function_count); + fprintf (file, "\tlarl\t%s,.LT%d\n", reg_names[BASE_REGISTER], + current_function_funcdef_no); readonly_data_section (); ASM_OUTPUT_ALIGN (file, 3); } else { - fprintf (file, "\tbras\t%s,.LTN%X\n", reg_names[BASE_REGISTER], - s390_function_count); + fprintf (file, "\tbras\t%s,.LTN%d\n", reg_names[BASE_REGISTER], + current_function_funcdef_no); } - fprintf (file, ".LT%X:\n", s390_function_count); + fprintf (file, ".LT%d:\n", current_function_funcdef_no); s390_pool_count = 0; output_constant_pool (current_function_name, current_function_decl); @@ -3371,27 +4072,303 @@ s390_output_constant_pool (file) if (TARGET_64BIT) function_section (current_function_decl); else - fprintf (file, ".LTN%X:\n", s390_function_count); + fprintf (file, ".LTN%d:\n", current_function_funcdef_no); } + + /* If no pool required, at least output the anchor label. */ + else if (!TARGET_64BIT && flag_pic) + fprintf (file, ".LT%d:\n", current_function_funcdef_no); } -/* Return true if floating point registers need to be saved. */ +/* Rework the prolog/epilog to avoid saving/restoring + registers unnecessarily. If TEMP_REGNO is nonnegative, + it specifies the number of a caller-saved register used + as temporary scratch register by code emitted during + machine dependent reorg. */ -static int -save_fprs_p () +static void +s390_optimize_prolog (temp_regno) + int temp_regno; { - int i; - if (!TARGET_64BIT) - return 0; - for (i=24; i<=31; i++) + int save_first, save_last, restore_first, restore_last; + int i, j; + rtx insn, new_insn, next_insn; + + struct s390_frame frame; + s390_frame_info (&frame); + + /* Recompute regs_ever_live data for special registers. */ + regs_ever_live[BASE_REGISTER] = 0; + regs_ever_live[RETURN_REGNUM] = 0; + regs_ever_live[STACK_POINTER_REGNUM] = frame.frame_size > 0; + + /* If there is (possibly) any pool entry, we need to + load the base register. + ??? FIXME: this should be more precise. */ + if (get_pool_size ()) + regs_ever_live[BASE_REGISTER] = 1; + + /* In non-leaf functions, the prolog/epilog code relies + on RETURN_REGNUM being saved in any case. */ + if (!current_function_is_leaf) + regs_ever_live[RETURN_REGNUM] = 1; + + /* We need to save/restore the temporary register. */ + if (temp_regno >= 0) + regs_ever_live[temp_regno] = 1; + + + /* Find first and last gpr to be saved. */ + + for (i = 6; i < 16; i++) + if (regs_ever_live[i]) + break; + + for (j = 15; j > i; j--) + if (regs_ever_live[j]) + break; + + if (i == 16) { - if (regs_ever_live[i] == 1) - return 1; + /* Nothing to save/restore. */ + save_first = restore_first = -1; + save_last = restore_last = -1; + } + else + { + /* Save/restore from i to j. */ + save_first = restore_first = i; + save_last = restore_last = j; + } + + /* Varargs functions need to save gprs 2 to 6. */ + if (current_function_stdarg) + { + save_first = 2; + if (save_last < 6) + save_last = 6; + } + + + /* If all special registers are in fact used, there's nothing we + can do, so no point in walking the insn list. */ + if (i <= BASE_REGISTER && j >= BASE_REGISTER + && i <= RETURN_REGNUM && j >= RETURN_REGNUM) + return; + + + /* Search for prolog/epilog insns and replace them. */ + + for (insn = get_insns (); insn; insn = next_insn) + { + int first, last, off; + rtx set, base, offset; + + next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) != INSN) + continue; + if (GET_CODE (PATTERN (insn)) != PARALLEL) + continue; + + if (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) - first * UNITS_PER_WORD; + + if (GET_CODE (base) != REG || off < 0) + continue; + if (first > BASE_REGISTER && first > RETURN_REGNUM) + continue; + if (last < BASE_REGISTER && last < RETURN_REGNUM) + continue; + + if (save_first != -1) + { + new_insn = save_gprs (base, off, save_first, save_last); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); + } + + if (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) - first * UNITS_PER_WORD; + + if (GET_CODE (base) != REG || off < 0) + continue; + if (first > BASE_REGISTER && first > RETURN_REGNUM) + continue; + if (last < BASE_REGISTER && last < RETURN_REGNUM) + continue; + + if (restore_first != -1) + { + new_insn = restore_gprs (base, off, restore_first, restore_last); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); + } } - return 0; } +/* Check whether any insn in the function makes use of the original + value of RETURN_REG (e.g. for __builtin_return_address). + If so, insert an insn reloading that value. + + Return true if any such insn was found. */ + +static bool +s390_fixup_clobbered_return_reg (return_reg) + rtx return_reg; +{ + bool replacement_done = 0; + rtx insn; + + struct s390_frame frame; + s390_frame_info (&frame); + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + rtx reg, off, new_insn; + + if (GET_CODE (insn) != INSN) + continue; + if (!reg_referenced_p (return_reg, PATTERN (insn))) + continue; + if (GET_CODE (PATTERN (insn)) == PARALLEL + && store_multiple_operation (PATTERN (insn), VOIDmode)) + continue; + + if (frame.frame_pointer_p) + reg = hard_frame_pointer_rtx; + else + reg = stack_pointer_rtx; + + off = GEN_INT (frame.frame_size + REGNO (return_reg) * UNITS_PER_WORD); + if (INTVAL (off) >= 4096) + { + off = force_const_mem (Pmode, off); + new_insn = gen_rtx_SET (Pmode, return_reg, off); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + off = return_reg; + } + + new_insn = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, reg, off)); + new_insn = gen_rtx_SET (Pmode, return_reg, new_insn); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + + replacement_done = 1; + } + + return replacement_done; +} + +/* Perform machine-dependent processing. */ + +void +s390_machine_dependent_reorg (first) + rtx first ATTRIBUTE_UNUSED; +{ + bool fixed_up_clobbered_return_reg = 0; + rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + bool temp_used = 0; + + /* Make sure all splits have been performed; splits after + machine_dependent_reorg might confuse insn length counts. */ + split_all_insns_noflow (); + + + /* 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_list; + + /* Try to chunkify the literal pool. */ + pool_list = s390_chunkify_start (temp_reg, &temp_used); + + /* Split out-of-range branches. If this has created new + literal pool entries, cancel current chunk list and + recompute it. */ + if (s390_split_branches (temp_reg, &temp_used)) + { + if (pool_list) + s390_chunkify_cancel (pool_list); + + continue; + } + + /* Check whether we have clobbered a use of the return + register (e.g. for __builtin_return_address). If so, + add insns reloading the register where necessary. */ + if (temp_used && !fixed_up_clobbered_return_reg + && s390_fixup_clobbered_return_reg (temp_reg)) + { + fixed_up_clobbered_return_reg = 1; + + /* The fixup insns might have caused a jump to overflow. */ + if (pool_list) + s390_chunkify_cancel (pool_list); + + continue; + } + + /* If we made it up to here, both conditions are satisfied. + Finish up pool chunkification if required. */ + if (pool_list) + s390_chunkify_finish (pool_list, temp_reg); + + break; + } + + s390_optimize_prolog (temp_used? RETURN_REGNUM : -1); +} + + /* Find first call clobbered register unsused in a function. This could be used as base register in a leaf function or for holding the return address before epilogue. */ @@ -3412,6 +4389,7 @@ static void s390_frame_info (frame) struct s390_frame *frame; { + char gprs_ever_live[16]; int i, j; HOST_WIDE_INT fsize = get_frame_size (); @@ -3419,7 +4397,14 @@ s390_frame_info (frame) fatal_error ("Total size of local variables exceeds architecture limit."); /* fprs 8 - 15 are caller saved for 64 Bit ABI. */ - frame->save_fprs_p = save_fprs_p (); + frame->save_fprs_p = 0; + if (TARGET_64BIT) + for (i = 24; i < 32; i++) + if (regs_ever_live[i]) + { + frame->save_fprs_p = 1; + break; + } frame->frame_size = fsize + frame->save_fprs_p * 64; @@ -3431,66 +4416,41 @@ s390_frame_info (frame) || current_function_stdarg) frame->frame_size += STARTING_FRAME_OFFSET; - /* If we need to allocate a frame, the stack pointer is changed. */ - - if (frame->frame_size > 0) - regs_ever_live[STACK_POINTER_REGNUM] = 1; - - /* If the literal pool might overflow, the return register might - be used as temp literal pointer. */ - - if (!TARGET_64BIT && get_pool_size () >= S390_POOL_CHUNK_MAX / 2) - regs_ever_live[RETURN_REGNUM] = 1; - - /* If there is (possibly) any pool entry, we need to - load base register. */ - - if (get_pool_size () - || !CONST_OK_FOR_LETTER_P (frame->frame_size, 'K') - || (!TARGET_64BIT && current_function_uses_pic_offset_table)) - regs_ever_live[BASE_REGISTER] = 1; - - /* If we need the GOT pointer, remember to save/restore it. */ - - if (current_function_uses_pic_offset_table) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; - /* Frame pointer needed. */ frame->frame_pointer_p = frame_pointer_needed; - /* Find first and last gpr to be saved. */ + /* Find first and last gpr to be saved. Note that at this point, + we assume the return register and the base register always + need to be saved. This is done because the usage of these + register might change even after the prolog was emitted. + If it turns out later that we really don't need them, the + prolog/epilog code is modified again. */ + + for (i = 0; i < 16; i++) + gprs_ever_live[i] = regs_ever_live[i]; + + gprs_ever_live[BASE_REGISTER] = 1; + gprs_ever_live[RETURN_REGNUM] = 1; + gprs_ever_live[STACK_POINTER_REGNUM] = frame->frame_size > 0; for (i = 6; i < 16; i++) - if (regs_ever_live[i]) + if (gprs_ever_live[i]) break; for (j = 15; j > i; j--) - if (regs_ever_live[j]) + if (gprs_ever_live[j]) break; - - if (i == 16) - { - /* Nothing to save / restore. */ - frame->first_save_gpr = -1; - frame->first_restore_gpr = -1; - frame->last_save_gpr = -1; - frame->return_reg_saved_p = 0; - } - else - { - /* Save / Restore from gpr i to j. */ - frame->first_save_gpr = i; - frame->first_restore_gpr = i; - frame->last_save_gpr = j; - frame->return_reg_saved_p = (j >= RETURN_REGNUM && i <= RETURN_REGNUM); - } + + /* Save / Restore from gpr i to j. */ + frame->first_save_gpr = i; + frame->first_restore_gpr = i; + frame->last_save_gpr = j; + + /* Varargs functions need to save gprs 2 to 6. */ if (current_function_stdarg) - { - /* Varargs function need to save from gpr 2 to gpr 15. */ - frame->first_save_gpr = 2; - } + frame->first_save_gpr = 2; } /* Return offset between argument pointer and frame pointer @@ -3540,30 +4500,118 @@ restore_fpr (base, offset, regnum) return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr); } -/* Output the function prologue assembly code to the - stdio stream FILE. The local frame size is passed - in LSIZE. */ +/* Generate insn to save registers FIRST to LAST into + the register save area located at offset OFFSET + relative to register BASE. */ -void -s390_function_prologue (file, lsize) - FILE *file ATTRIBUTE_UNUSED; - HOST_WIDE_INT lsize ATTRIBUTE_UNUSED; +static rtx +save_gprs (base, offset, first, last) + rtx base; + int offset; + int first; + int last; { - s390_chunkify_pool (); - s390_split_branches (); + rtx addr, insn, note; + int i; + + addr = plus_constant (base, offset + first * UNITS_PER_WORD); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, s390_sr_alias_set); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (addr, gen_rtx_REG (Pmode, first)); + else + insn = gen_movsi (addr, gen_rtx_REG (Pmode, first)); + + RTX_FRAME_RELATED_P (insn) = 1; + return insn; + } + + + insn = gen_store_multiple (addr, + gen_rtx_REG (Pmode, first), + GEN_INT (last - first + 1)); + + + /* We need to set the FRAME_RELATED flag on all SETs + inside the store-multiple pattern. + + However, we must not emit DWARF records for registers 2..5 + if they are stored for use by variable arguments ... + + ??? Unfortunately, it is not enough to simply not the the + FRAME_RELATED flags for those SETs, because the first SET + of the PARALLEL is always treated as if it had the flag + set, even if it does not. Therefore we emit a new pattern + without those registers as REG_FRAME_RELATED_EXPR note. */ + + if (first >= 6) + { + rtx pat = PATTERN (insn); + + for (i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) == SET) + RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (last >= 6) + { + addr = plus_constant (base, offset + 6 * UNITS_PER_WORD); + note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), + gen_rtx_REG (Pmode, 6), + GEN_INT (last - 6 + 1)); + note = PATTERN (note); + + REG_NOTES (insn) = + gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + note, REG_NOTES (insn)); + + for (i = 0; i < XVECLEN (note, 0); i++) + if (GET_CODE (XVECEXP (note, 0, i)) == SET) + RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + + return insn; } -/* Output the function epilogue assembly code to the - stdio stream FILE. The local frame size is passed - in LSIZE. */ +/* Generate insn to restore registers FIRST to LAST from + the register save area located at offset OFFSET + relative to register BASE. */ -void -s390_function_epilogue (file, lsize) - FILE *file ATTRIBUTE_UNUSED; - HOST_WIDE_INT lsize ATTRIBUTE_UNUSED; +static rtx +restore_gprs (base, offset, first, last) + rtx base; + int offset; + int first; + int last; { - current_function_uses_pic_offset_table = 0; - s390_function_count++; + rtx addr, insn; + + addr = plus_constant (base, offset + first * UNITS_PER_WORD); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, s390_sr_alias_set); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (gen_rtx_REG (Pmode, first), addr); + else + insn = gen_movsi (gen_rtx_REG (Pmode, first), addr); + + return insn; + } + + insn = gen_load_multiple (gen_rtx_REG (Pmode, first), + addr, + GEN_INT (last - first + 1)); + return insn; } /* Expand the prologue into a bunch of separate insns. */ @@ -3582,7 +4630,7 @@ s390_emit_prologue () /* Choose best register to use for temp use within prologue. */ - if (frame.return_reg_saved_p + if (!current_function_is_leaf && !has_hard_reg_initial_val (Pmode, RETURN_REGNUM) && get_pool_size () < S390_POOL_CHUNK_MAX / 2) temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); @@ -3591,69 +4639,9 @@ s390_emit_prologue () /* Save call saved gprs. */ - if (frame.first_save_gpr != -1) - { - addr = plus_constant (stack_pointer_rtx, - frame.first_save_gpr * UNITS_PER_WORD); - addr = gen_rtx_MEM (Pmode, addr); - set_mem_alias_set (addr, s390_sr_alias_set); - - if (frame.first_save_gpr != frame.last_save_gpr ) - { - insn = emit_insn (gen_store_multiple (addr, - gen_rtx_REG (Pmode, frame.first_save_gpr), - GEN_INT (frame.last_save_gpr - - frame.first_save_gpr + 1))); - - /* We need to set the FRAME_RELATED flag on all SETs - inside the store-multiple pattern. - - However, we must not emit DWARF records for registers 2..5 - if they are stored for use by variable arguments ... - - ??? Unfortunately, it is not enough to simply not the the - FRAME_RELATED flags for those SETs, because the first SET - of the PARALLEL is always treated as if it had the flag - set, even if it does not. Therefore we emit a new pattern - without those registers as REG_FRAME_RELATED_EXPR note. */ - - if (frame.first_save_gpr >= 6) - { - rtx pat = PATTERN (insn); - - for (i = 0; i < XVECLEN (pat, 0); i++) - if (GET_CODE (XVECEXP (pat, 0, i)) == SET) - RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; - - RTX_FRAME_RELATED_P (insn) = 1; - } - else if (frame.last_save_gpr >= 6) - { - rtx note, naddr; - naddr = plus_constant (stack_pointer_rtx, 6 * UNITS_PER_WORD); - note = gen_store_multiple (gen_rtx_MEM (Pmode, naddr), - gen_rtx_REG (Pmode, 6), - GEN_INT (frame.last_save_gpr - 6 + 1)); - note = PATTERN (note); - - REG_NOTES (insn) = - gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, - note, REG_NOTES (insn)); - - for (i = 0; i < XVECLEN (note, 0); i++) - if (GET_CODE (XVECEXP (note, 0, i)) == SET) - RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; - - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - insn = emit_move_insn (addr, - gen_rtx_REG (Pmode, frame.first_save_gpr)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } + insn = save_gprs (stack_pointer_rtx, 0, + frame.first_save_gpr, frame.last_save_gpr); + emit_insn (insn); /* Dump constant pool and set constant pool register (13). */ @@ -3765,7 +4753,7 @@ s390_emit_prologue () /* Set up got pointer, if needed. */ - if (current_function_uses_pic_offset_table) + if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) { rtx got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); SYMBOL_REF_FLAG (got_symbol) = 1; @@ -3912,7 +4900,7 @@ s390_emit_epilogue () if (frame.first_restore_gpr != -1) { - rtx addr; + rtx insn, addr; int i; /* Check for global register and save them @@ -3943,8 +4931,7 @@ s390_emit_epilogue () /* Fetch return address from stack before load multiple, this will do good for scheduling. */ - if (frame.last_save_gpr >= RETURN_REGNUM - && frame.first_restore_gpr < RETURN_REGNUM) + if (!current_function_is_leaf) { int return_regnum = find_unused_clobbered_reg(); if (!return_regnum) @@ -3964,23 +4951,9 @@ s390_emit_epilogue () emit_insn (gen_blockage()); - addr = plus_constant (frame_pointer, - offset + frame.first_restore_gpr * UNITS_PER_WORD); - addr = gen_rtx_MEM (Pmode, addr); - set_mem_alias_set (addr, s390_sr_alias_set); - - if (frame.first_restore_gpr != frame.last_save_gpr) - { - emit_insn (gen_load_multiple ( - gen_rtx_REG (Pmode, frame.first_restore_gpr), - addr, - GEN_INT (frame.last_save_gpr - frame.first_restore_gpr + 1))); - } - else - { - emit_move_insn (gen_rtx_REG (Pmode, frame.first_restore_gpr), - addr); - } + insn = restore_gprs (frame_pointer, offset, + frame.first_restore_gpr, frame.last_save_gpr); + emit_insn (insn); } /* Return to caller. */ @@ -4518,7 +5491,7 @@ s390_function_profiler (file, labelno) rtx op[7]; char label[128]; - sprintf (label, "%sP%d", LPREFIX, labelno); + ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); fprintf (file, "# function profiler \n"); @@ -4552,7 +5525,7 @@ s390_function_profiler (file, labelno) output_asm_insn ("bras\t%2,%l6", op); output_asm_insn (".long\t%4", op); output_asm_insn (".long\t%3", op); - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6])); + (*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); @@ -4565,10 +5538,10 @@ s390_function_profiler (file, labelno) output_asm_insn ("st\t%0,%1", op); output_asm_insn ("bras\t%2,%l6", op); - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5])); + (*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); - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6])); + (*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); @@ -4613,3 +5586,78 @@ s390_encode_section_info (decl, first) } } } + +static void +s390_output_mi_thunk (file, thunk, delta, function) + FILE *file; + tree thunk ATTRIBUTE_UNUSED; + HOST_WIDE_INT delta; + tree function; +{ + if (TARGET_64BIT) + { + if (flag_pic) + { + fprintf (file, "\tlarl 1,0f\n"); + fprintf (file, "\tagf %d,0(1)\n", + aggregate_value_p (TREE_TYPE + (TREE_TYPE (function))) ? 3 :2 ); + fprintf (file, "\tlarl 1,"); + assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); + fprintf (file, "@GOTENT\n"); + fprintf (file, "\tlg 1,0(1)\n"); + fprintf (file, "\tbr 1\n"); + fprintf (file, "0:\t.long "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (delta)); + fprintf (file, "\n"); + } + else + { + fprintf (file, "\tlarl 1,0f\n"); + fprintf (file, "\tagf %d,0(1)\n", + aggregate_value_p (TREE_TYPE + (TREE_TYPE (function))) ? 3 :2 ); + fprintf (file, "\tjg "); + assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); + fprintf (file, "\n"); + fprintf (file, "0:\t.long "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (delta)); + fprintf (file, "\n"); + } + } + else + { + if (flag_pic) + { + fprintf (file, "\tbras 1,0f\n"); + fprintf (file, "\t.long _GLOBAL_OFFSET_TABLE_-.\n"); + fprintf (file, "\t.long "); + assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); + fprintf (file, "@GOT\n"); + fprintf (file, "\t.long "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (delta)); + fprintf (file, "\n"); + fprintf (file, "0:\tal %d,8(1)\n", + aggregate_value_p (TREE_TYPE + (TREE_TYPE (function))) ? 3 : 2 ); + fprintf (file, "\tl 0,4(1)\n"); + fprintf (file, "\tal 1,0(1)\n"); + fprintf (file, "\talr 1,0\n"); + fprintf (file, "\tl 1,0(1)\n"); + fprintf (file, "\tbr 1\n"); + } else { + fprintf (file, "\tbras 1,0f\n"); + fprintf (file, "\t.long "); + assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); + fprintf (file, "-.\n"); + fprintf (file, "\t.long "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (delta)); + fprintf (file, "\n"); + fprintf (file, "0:\tal %d,4(1)\n", + aggregate_value_p (TREE_TYPE + (TREE_TYPE (function))) ? 3 : 2 ); + fprintf (file, "\tal 1,0(1)\n"); + fprintf (file, "\tbr 1\n"); + } + } +} |