X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fconfig%2Fs390%2Fs390.c;h=0b6d63bf01944a8a9232a9ada3e0f22126e77ade;hb=29847ec4952258319a397b4a567dc31f942aac5c;hp=009c17828d89d71bcd0ecefc680392380028163d;hpb=0349cc73f60b33693513d5ce793d699a6ed22045;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 009c17828d8..0b6d63bf019 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -1,6 +1,6 @@ /* Subroutines used for code generation on IBM S/390 and zSeries - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007 Free Software Foundation, Inc. Contributed by Hartmut Penner (hpenner@de.ibm.com) and Ulrich Weigand (uweigand@de.ibm.com). @@ -8,7 +8,7 @@ This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -17,9 +17,8 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -51,6 +50,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "langhooks.h" #include "optabs.h" #include "tree-gimple.h" +#include "df.h" /* Define the specific costs for a given cpu. */ @@ -71,17 +71,18 @@ struct processor_costs const int msgr; /* cost of an MSGR instruction. */ const int msr; /* cost of an MSR instruction. */ const int mult_df; /* cost of multiplication in DFmode. */ + const int mxbr; /* square root */ + const int sqxbr; /* cost of square root in TFmode. */ const int sqdbr; /* cost of square root in DFmode. */ const int sqebr; /* cost of square root in SFmode. */ /* multiply and add */ const int madbr; /* cost of multiply and add in DFmode. */ const int maebr; /* cost of multiply and add in SFmode. */ /* division */ + const int dxbr; const int ddbr; - const int ddr; const int debr; - const int der; const int dlgr; const int dlr; const int dr; @@ -107,14 +108,15 @@ struct processor_costs z900_cost = COSTS_N_INSNS (10), /* MSGR */ COSTS_N_INSNS (4), /* MSR */ COSTS_N_INSNS (7), /* multiplication in DFmode */ + COSTS_N_INSNS (13), /* MXBR */ + COSTS_N_INSNS (136), /* SQXBR */ COSTS_N_INSNS (44), /* SQDBR */ COSTS_N_INSNS (35), /* SQEBR */ COSTS_N_INSNS (18), /* MADBR */ COSTS_N_INSNS (13), /* MAEBR */ + COSTS_N_INSNS (134), /* DXBR */ COSTS_N_INSNS (30), /* DDBR */ - COSTS_N_INSNS (30), /* DDR */ COSTS_N_INSNS (27), /* DEBR */ - COSTS_N_INSNS (26), /* DER */ COSTS_N_INSNS (220), /* DLGR */ COSTS_N_INSNS (34), /* DLR */ COSTS_N_INSNS (34), /* DR */ @@ -138,14 +140,15 @@ struct processor_costs z990_cost = COSTS_N_INSNS (4), /* MSGR */ COSTS_N_INSNS (4), /* MSR */ COSTS_N_INSNS (1), /* multiplication in DFmode */ + COSTS_N_INSNS (28), /* MXBR */ + COSTS_N_INSNS (130), /* SQXBR */ COSTS_N_INSNS (66), /* SQDBR */ COSTS_N_INSNS (38), /* SQEBR */ COSTS_N_INSNS (1), /* MADBR */ COSTS_N_INSNS (1), /* MAEBR */ + COSTS_N_INSNS (60), /* DXBR */ COSTS_N_INSNS (40), /* DDBR */ - COSTS_N_INSNS (44), /* DDR */ - COSTS_N_INSNS (26), /* DDBR */ - COSTS_N_INSNS (28), /* DER */ + COSTS_N_INSNS (26), /* DEBR */ COSTS_N_INSNS (176), /* DLGR */ COSTS_N_INSNS (31), /* DLR */ COSTS_N_INSNS (31), /* DR */ @@ -169,14 +172,15 @@ struct processor_costs z9_109_cost = COSTS_N_INSNS (4), /* MSGR */ COSTS_N_INSNS (4), /* MSR */ COSTS_N_INSNS (1), /* multiplication in DFmode */ + COSTS_N_INSNS (28), /* MXBR */ + COSTS_N_INSNS (130), /* SQXBR */ COSTS_N_INSNS (66), /* SQDBR */ COSTS_N_INSNS (38), /* SQEBR */ COSTS_N_INSNS (1), /* MADBR */ COSTS_N_INSNS (1), /* MAEBR */ + COSTS_N_INSNS (60), /* DXBR */ COSTS_N_INSNS (40), /* DDBR */ - COSTS_N_INSNS (37), /* DDR */ - COSTS_N_INSNS (26), /* DDBR */ - COSTS_N_INSNS (28), /* DER */ + COSTS_N_INSNS (26), /* DEBR */ COSTS_N_INSNS (30), /* DLGR */ COSTS_N_INSNS (23), /* DLR */ COSTS_N_INSNS (23), /* DR */ @@ -209,6 +213,7 @@ struct s390_address rtx indx; rtx disp; bool pointer; + bool literal_pool; }; /* Which cpu are we tuning for. */ @@ -233,7 +238,12 @@ struct s390_frame_layout GTY (()) HOST_WIDE_INT f4_offset; HOST_WIDE_INT f8_offset; HOST_WIDE_INT backchain_offset; - + + /* Number of first and last gpr where slots in the register + save area are reserved for. */ + int first_save_gpr_slot; + int last_save_gpr_slot; + /* Number of first and last gpr to be saved, restored. */ int first_save_gpr; int first_restore_gpr; @@ -273,6 +283,9 @@ struct machine_function GTY(()) /* True if we may need to perform branch splitting. */ bool split_branches_pending_p; + /* True during final stage of literal pool processing. */ + bool decomposed_literal_pool_addresses_ok_p; + /* Some local-dynamic TLS symbol name. */ const char *some_ld_name; @@ -283,8 +296,8 @@ struct machine_function GTY(()) #define cfun_frame_layout (cfun->machine->frame_layout) #define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs) -#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr - \ - cfun_frame_layout.first_save_gpr + 1) * UNITS_PER_WORD) +#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \ + cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD) #define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |= \ (1 << (BITNUM))) #define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \ @@ -306,6 +319,31 @@ struct machine_function GTY(()) #define CONST_OK_FOR_On(x) \ CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On") +#define REGNO_PAIR_OK(REGNO, MODE) \ + (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1)) + +static enum machine_mode +s390_libgcc_cmp_return_mode (void) +{ + return TARGET_64BIT ? DImode : SImode; +} + +static enum machine_mode +s390_libgcc_shift_count_mode (void) +{ + return TARGET_64BIT ? DImode : SImode; +} + +/* Return true if the back end supports mode MODE. */ +static bool +s390_scalar_mode_supported_p (enum machine_mode mode) +{ + if (DECIMAL_FLOAT_MODE_P (mode)) + return true; + else + return default_scalar_mode_supported_p (mode); +} + /* Set the has_landing_pad_p flag in struct machine_function to VALUE. */ void @@ -661,10 +699,9 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1) *op1 = constm1_rtx; } - - /* Remove redundant UNSPEC_CMPINT conversions if possible. */ + /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible. */ if (GET_CODE (*op0) == UNSPEC - && XINT (*op0, 1) == UNSPEC_CMPINT + && XINT (*op0, 1) == UNSPEC_CCU_TO_INT && XVECLEN (*op0, 0) == 1 && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode && GET_CODE (XVECEXP (*op0, 0, 0)) == REG @@ -690,6 +727,30 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1) } } + /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible. */ + if (GET_CODE (*op0) == UNSPEC + && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT + && XVECLEN (*op0, 0) == 1 + && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode + && GET_CODE (XVECEXP (*op0, 0, 0)) == REG + && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM + && *op1 == const0_rtx) + { + enum rtx_code new_code = UNKNOWN; + switch (*code) + { + case EQ: new_code = EQ; break; + case NE: new_code = NE; break; + default: break; + } + + if (new_code != UNKNOWN) + { + *op0 = XVECEXP (*op0, 0, 0); + *code = new_code; + } + } + /* Simplify cascaded EQ, NE with const0_rtx. */ if ((*code == NE || *code == EQ) && (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE) @@ -742,6 +803,24 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1) return ret; } +/* Emit a SImode compare and swap instruction setting MEM to NEW if OLD + matches CMP. + Return the correct condition RTL to be placed in the IF_THEN_ELSE of the + conditional branch testing the result. */ + +static rtx +s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new) +{ + rtx ret; + + emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new)); + ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx); + + s390_compare_emitted = NULL_RTX; + + return ret; +} + /* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an unconditional jump, else a conditional jump under condition COND. */ @@ -1284,6 +1363,8 @@ s390_handle_arch_option (const char *arg, | PF_LONG_DISPLACEMENT}, {"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH | PF_LONG_DISPLACEMENT | PF_EXTIMM}, + {"z9-ec", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP }, }; size_t i; @@ -1362,11 +1443,34 @@ override_options (void) } /* Sanity checks. */ - if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH)) + if (TARGET_ZARCH && !TARGET_CPU_ZARCH) error ("z/Architecture mode not supported on %s", s390_arch_string); if (TARGET_64BIT && !TARGET_ZARCH) error ("64-bit ABI not supported in ESA/390 mode"); + if (TARGET_HARD_DFP && !TARGET_DFP) + { + if (target_flags_explicit & MASK_HARD_DFP) + { + if (!TARGET_CPU_DFP) + error ("Hardware decimal floating point instructions" + " not available on %s", s390_arch_string); + if (!TARGET_ZARCH) + error ("Hardware decimal floating point instructions" + " not available in ESA/390 mode"); + } + else + target_flags &= ~MASK_HARD_DFP; + } + + if ((target_flags_explicit & MASK_SOFT_FLOAT) && TARGET_SOFT_FLOAT) + { + if ((target_flags_explicit & MASK_HARD_DFP) && TARGET_HARD_DFP) + error ("-mhard-dfp can't be used in conjunction with -msoft-float"); + + target_flags &= ~MASK_HARD_DFP; + } + /* Set processor cost function. */ if (s390_tune == PROCESSOR_2094_Z9_109) s390_cost = &z9_109_cost; @@ -1381,15 +1485,18 @@ override_options (void) if (s390_stack_size) { - if (!s390_stack_guard) - error ("-mstack-size implies use of -mstack-guard"); - else if (s390_stack_guard >= s390_stack_size) + if (s390_stack_guard >= s390_stack_size) error ("stack size must be greater than the stack guard value"); else if (s390_stack_size > 1 << 16) error ("stack size must not be greater than 64k"); } else if (s390_stack_guard) error ("-mstack-guard implies use of -mstack-size"); + +#ifdef TARGET_DEFAULT_LONG_DOUBLE_128 + if (!(target_flags_explicit & MASK_LONG_DOUBLE_128)) + target_flags |= MASK_LONG_DOUBLE_128; +#endif } /* Map for smallest class containing reg regno. */ @@ -1468,6 +1575,14 @@ s390_decompose_address (rtx addr, struct s390_address *out) bool pointer = false; bool base_ptr = false; bool indx_ptr = false; + bool literal_pool = false; + + /* We may need to substitute the literal pool base register into the address + below. However, at this point we do not know which register is going to + be used as base, so we substitute the arg pointer register. This is going + to be treated as holding a pointer below -- it shouldn't be used for any + other purpose. */ + rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM); /* Decompose address into base + index + displacement. */ @@ -1540,9 +1655,9 @@ s390_decompose_address (rtx addr, struct s390_address *out) { /* Either base or index must be free to hold the base register. */ if (!base) - base = gen_rtx_REG (Pmode, BASE_REGNUM); + base = fake_pool_base, literal_pool = true; else if (!indx) - indx = gen_rtx_REG (Pmode, BASE_REGNUM); + indx = fake_pool_base, literal_pool = true; else return false; @@ -1565,22 +1680,26 @@ s390_decompose_address (rtx addr, struct s390_address *out) else return false; - base = gen_rtx_REG (Pmode, BASE_REGNUM); + base = XVECEXP (base, 0, 1); break; case UNSPEC_LTREL_BASE: - base = gen_rtx_REG (Pmode, BASE_REGNUM); + if (XVECLEN (base, 0) == 1) + base = fake_pool_base, literal_pool = true; + else + base = XVECEXP (base, 0, 1); break; default: return false; } - if (GET_CODE (base) != REG || GET_MODE (base) != Pmode) + if (!REG_P (base) + || (GET_MODE (base) != SImode + && GET_MODE (base) != Pmode)) return false; - if (REGNO (base) == BASE_REGNUM - || REGNO (base) == STACK_POINTER_REGNUM + if (REGNO (base) == STACK_POINTER_REGNUM || REGNO (base) == FRAME_POINTER_REGNUM || ((reload_completed || reload_in_progress) && frame_pointer_needed @@ -1589,6 +1708,10 @@ s390_decompose_address (rtx addr, struct s390_address *out) || (flag_pic && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) pointer = base_ptr = true; + + if ((reload_completed || reload_in_progress) + && base == cfun->machine->base_reg) + pointer = base_ptr = literal_pool = true; } /* Validate index register. */ @@ -1605,22 +1728,26 @@ s390_decompose_address (rtx addr, struct s390_address *out) else return false; - indx = gen_rtx_REG (Pmode, BASE_REGNUM); + indx = XVECEXP (indx, 0, 1); break; case UNSPEC_LTREL_BASE: - indx = gen_rtx_REG (Pmode, BASE_REGNUM); + if (XVECLEN (indx, 0) == 1) + indx = fake_pool_base, literal_pool = true; + else + indx = XVECEXP (indx, 0, 1); break; default: return false; } - if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode) + if (!REG_P (indx) + || (GET_MODE (indx) != SImode + && GET_MODE (indx) != Pmode)) return false; - if (REGNO (indx) == BASE_REGNUM - || REGNO (indx) == STACK_POINTER_REGNUM + if (REGNO (indx) == STACK_POINTER_REGNUM || REGNO (indx) == FRAME_POINTER_REGNUM || ((reload_completed || reload_in_progress) && frame_pointer_needed @@ -1629,6 +1756,10 @@ s390_decompose_address (rtx addr, struct s390_address *out) || (flag_pic && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) pointer = indx_ptr = true; + + if ((reload_completed || reload_in_progress) + && indx == cfun->machine->base_reg) + pointer = indx_ptr = literal_pool = true; } /* Prefer to use pointer as base, not index. */ @@ -1672,14 +1803,15 @@ s390_decompose_address (rtx addr, struct s390_address *out) 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 + else if (cfun && cfun->machine + && cfun->machine->decomposed_literal_pool_addresses_ok_p + && GET_CODE (disp) == MINUS && GET_CODE (XEXP (disp, 0)) == LABEL_REF && GET_CODE (XEXP (disp, 1)) == LABEL_REF) { @@ -1716,11 +1848,49 @@ s390_decompose_address (rtx addr, struct s390_address *out) out->indx = indx; out->disp = orig_disp; out->pointer = pointer; + out->literal_pool = literal_pool; } return true; } +/* Decompose a RTL expression OP for a shift count into its components, + and return the base register in BASE and the offset in OFFSET. + + Return true if OP is a valid shift count, false if not. */ + +bool +s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset) +{ + HOST_WIDE_INT off = 0; + + /* We can have an integer constant, an address register, + or a sum of the two. */ + if (GET_CODE (op) == CONST_INT) + { + off = INTVAL (op); + op = NULL_RTX; + } + if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT) + { + off = INTVAL (XEXP (op, 1)); + op = XEXP (op, 0); + } + while (op && GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (op && GET_CODE (op) != REG) + return false; + + if (offset) + *offset = off; + if (base) + *base = op; + + return true; +} + + /* Return true if CODE is a valid address without index. */ bool @@ -1736,14 +1906,16 @@ s390_legitimate_address_without_index_p (rtx op) return true; } -/* Return 1 if OP is a valid operand for a C constraint, 0 else. */ + +/* Evaluates constraint strings described by the regular expression + ([A|B](Q|R|S|T))|U|W and returns 1 if OP is a valid operand for the + constraint given in STR, or 0 else. */ int -s390_extra_constraint_str (rtx op, int c, const char * str) +s390_mem_constraint (const char *str, rtx op) { struct s390_address addr; - - gcc_assert (c == str[0]); + char c = str[0]; /* Check for offsettable variants of memory constraints. */ if (c == 'A') @@ -1753,8 +1925,7 @@ s390_extra_constraint_str (rtx op, int c, const char * str) return 0; if ((reload_completed || reload_in_progress) - ? !offsettable_memref_p (op) - : !offsettable_nonstrict_memref_p (op)) + ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op)) return 0; c = str[1]; @@ -1767,9 +1938,7 @@ s390_extra_constraint_str (rtx op, int c, const char * str) 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) + if (addr.literal_pool) return 0; c = str[1]; @@ -1851,7 +2020,11 @@ s390_extra_constraint_str (rtx op, int c, const char * str) break; case 'Y': - return shift_count_operand (op, VOIDmode); + /* Simply check for the basic form of a shift count. Reload will + take care of making sure we have a proper base register. */ + if (!s390_decompose_shift_count (op, NULL, NULL)) + return 0; + break; default: return 0; @@ -1860,130 +2033,119 @@ s390_extra_constraint_str (rtx op, int c, const char * str) return 1; } -/* Return true if VALUE matches the constraint STR. */ + + +/* Evaluates constraint strings starting with letter O. Input + parameter C is the second letter following the "O" in the constraint + string. Returns 1 if VALUE meets the respective constraint and 0 + otherwise. */ int -s390_const_double_ok_for_constraint_p (rtx value, - int c, - const char * str) +s390_O_constraint_str (const char c, HOST_WIDE_INT value) { - gcc_assert (c == str[0]); + if (!TARGET_EXTIMM) + return 0; - switch (str[0]) + switch (c) { - case 'G': - /* The floating point zero constant. */ - return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT - && value == CONST0_RTX (GET_MODE (value))); - + case 's': + return trunc_int_for_mode (value, SImode) == value; + + case 'p': + return value == 0 + || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1; + + case 'n': + return s390_single_part (GEN_INT (value - 1), DImode, SImode, -1) == 1; + default: - return 0; + gcc_unreachable (); } } -/* Return true if VALUE matches the constraint STR. */ + +/* Evaluates constraint strings starting with letter N. Parameter STR + contains the letters following letter "N" in the constraint string. + Returns true if VALUE matches the constraint. */ int -s390_const_ok_for_constraint_p (HOST_WIDE_INT value, - int c, - const char * str) +s390_N_constraint_str (const char *str, HOST_WIDE_INT value) { enum machine_mode mode, part_mode; int def; int part, part_goal; - gcc_assert (c == str[0]); - - 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 'Q': part_mode = QImode; break; - case 'H': part_mode = HImode; break; - case 'S': part_mode = SImode; 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; + if (str[0] == 'x') + part_goal = -1; + else + part_goal = str[0] - '0'; + switch (str[1]) + { + case 'Q': + part_mode = QImode; break; - - case 'O': - if (!TARGET_EXTIMM) - return 0; - - switch (str[1]) - { - case 's': - return trunc_int_for_mode (value, SImode) == value; - - case 'p': - return value == 0 - || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1; - - case 'n': - return value == -1 - || s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1; - - default: - gcc_unreachable (); - } + case 'H': + part_mode = HImode; break; + case 'S': + part_mode = SImode; + break; + default: + return 0; + } - case 'P': - return legitimate_reload_constant_p (GEN_INT (value)); + switch (str[2]) + { + case 'H': + mode = HImode; + break; + case 'S': + mode = SImode; + break; + case 'D': + mode = DImode; + break; + default: + return 0; + } + switch (str[3]) + { + 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; + return 1; } + +/* Returns true if the input parameter VALUE is a float zero. */ + +int +s390_float_const_zero_p (rtx value) +{ + return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT + && value == CONST0_RTX (GET_MODE (value))); +} + + /* 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. @@ -2022,7 +2184,7 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total) /* Check for multiply and add. */ if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode) && GET_CODE (XEXP (x, 0)) == MULT - && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD) + && TARGET_HARD_FLOAT && TARGET_FUSED_MADD) { /* This is the multiply and add case. */ if (GET_MODE (x) == DFmode) @@ -2088,6 +2250,9 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total) case DFmode: *total = s390_cost->mult_df; break; + case TFmode: + *total = s390_cost->mxbr; + break; default: return false; } @@ -2126,25 +2291,25 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total) *total = s390_cost->dlr; else if (GET_MODE (x) == SFmode) { - if (TARGET_IEEE_FLOAT) - *total = s390_cost->debr; - else /* TARGET_IBM_FLOAT */ - *total = s390_cost->der; + *total = s390_cost->debr; } else if (GET_MODE (x) == DFmode) { - if (TARGET_IEEE_FLOAT) - *total = s390_cost->ddbr; - else /* TARGET_IBM_FLOAT */ - *total = s390_cost->ddr; + *total = s390_cost->ddbr; + } + else if (GET_MODE (x) == TFmode) + { + *total = s390_cost->dxbr; } return false; case SQRT: if (GET_MODE (x) == SFmode) *total = s390_cost->sqebr; - else /* DFmode */ + else if (GET_MODE (x) == DFmode) *total = s390_cost->sqdbr; + else /* TFmode */ + *total = s390_cost->sqxbr; return false; case SIGN_EXTEND: @@ -2486,49 +2651,57 @@ s390_preferred_reload_class (rtx op, enum reg_class class) return class; } -/* Return the register class of a scratch register needed to - load IN into a register of class CLASS in MODE. - - We need a temporary when loading a PLUS expression which - is not a legitimate operand of the LOAD ADDRESS instruction. */ +/* Inform reload about cases where moving X with a mode MODE to a register in + CLASS requires an extra scratch or immediate register. Return the class + needed for the immediate register. */ -enum reg_class -s390_secondary_input_reload_class (enum reg_class class, - enum machine_mode mode, rtx in) +static enum reg_class +s390_secondary_reload (bool in_p, rtx x, enum reg_class class, + enum machine_mode mode, secondary_reload_info *sri) { - if (s390_plus_operand (in, mode)) - return ADDR_REGS; - + /* Intermediate register needed. */ if (reg_classes_intersect_p (CC_REGS, class)) return GENERAL_REGS; - return NO_REGS; -} - -/* Return the register class of a scratch register needed to - store a register of class CLASS in MODE into OUT: - - We need a temporary when storing a double-word to a - non-offsettable memory address. */ - -enum reg_class -s390_secondary_output_reload_class (enum reg_class class, - enum machine_mode mode, rtx out) -{ - if ((TARGET_64BIT ? mode == TImode - : (mode == DImode || mode == DFmode)) - && reg_classes_intersect_p (GENERAL_REGS, class) - && GET_CODE (out) == MEM - && GET_CODE (XEXP (out, 0)) == PLUS - && GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS - && GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT - && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1)) + /* We need a scratch register when loading a PLUS expression which + is not a legitimate operand of the LOAD ADDRESS instruction. */ + if (in_p && s390_plus_operand (x, mode)) + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_plus : CODE_FOR_reloadsi_plus); + + /* Performing a multiword move from or to memory we have to make sure the + second chunk in memory is addressable without causing a displacement + overflow. If that would be the case we calculate the address in + a scratch register. */ + if (MEM_P (x) + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (x, 0), 1)) + GET_MODE_SIZE (mode) - 1)) - return ADDR_REGS; - - if (reg_classes_intersect_p (CC_REGS, class)) - return GENERAL_REGS; + { + /* For GENERAL_REGS a displacement overflow is no problem if occurring + in a s_operand address since we may fallback to lm/stm. So we only + have to care about overflows in the b+i+d case. */ + if ((reg_classes_intersect_p (GENERAL_REGS, class) + && s390_class_max_nregs (GENERAL_REGS, mode) > 1 + && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS) + /* For FP_REGS no lm/stm is available so this check is triggered + for displacement overflows in b+i+d and b+d like addresses. */ + || (reg_classes_intersect_p (FP_REGS, class) + && s390_class_max_nregs (FP_REGS, mode) > 1)) + { + if (in_p) + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_nonoffmem_in : + CODE_FOR_reloadsi_nonoffmem_in); + else + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_nonoffmem_out : + CODE_FOR_reloadsi_nonoffmem_out); + } + } + /* Either scratch or no register needed. */ return NO_REGS; } @@ -2556,8 +2729,8 @@ s390_expand_plus_operand (rtx target, rtx src, /* 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))) + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) { /* Otherwise, one of the operands cannot be an address register; we reload its value into the scratch register. */ @@ -2605,19 +2778,24 @@ legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, if (strict) { - if (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) return false; - if (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)) + + if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (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)) + if (ad.base + && !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS)) return false; + + if (ad.indx + && !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS)) + return false; } - return true; } @@ -2648,9 +2826,9 @@ preferred_la_operand_p (rtx op1, rtx op2) if (!s390_decompose_address (op1, &addr)) return false; - if (addr.base && !REG_OK_FOR_BASE_STRICT_P (addr.base)) + if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base))) return false; - if (addr.indx && !REG_OK_FOR_INDEX_STRICT_P (addr.indx)) + if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx))) return false; if (!TARGET_64BIT && !addr.pointer) @@ -2704,6 +2882,8 @@ legitimize_pic_address (rtx orig, rtx reg) rtx new = orig; rtx base; + gcc_assert (!TLS_SYMBOLIC_CONST (addr)); + if (GET_CODE (addr) == LABEL_REF || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr))) { @@ -2721,7 +2901,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF); addr = gen_rtx_CONST (Pmode, addr); @@ -2747,7 +2927,7 @@ legitimize_pic_address (rtx orig, rtx reg) in both 31- and 64-bit code (@GOT). */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); new = gen_rtx_CONST (Pmode, new); @@ -2779,7 +2959,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); addr = gen_rtx_CONST (Pmode, addr); @@ -2827,7 +3007,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = XVECEXP (addr, 0, 0); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), @@ -2856,13 +3036,20 @@ legitimize_pic_address (rtx orig, rtx reg) if (GET_CODE (addr) == PLUS) { rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1); + + gcc_assert (!TLS_SYMBOLIC_CONST (op0)); + gcc_assert (!TLS_SYMBOLIC_CONST (op1)); + /* Check first to see if this is a constant offset from a local symbol reference. */ if ((GET_CODE (op0) == LABEL_REF || (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0))) && GET_CODE (op1) == CONST_INT) { - if (TARGET_CPU_ZARCH && larl_operand (op0, VOIDmode)) + if (TARGET_CPU_ZARCH + && larl_operand (op0, VOIDmode) + && INTVAL (op1) < (HOST_WIDE_INT)1 << 31 + && INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31)) { if (INTVAL (op1) & 1) { @@ -2872,7 +3059,7 @@ legitimize_pic_address (rtx orig, rtx reg) if (!DISP_IN_RANGE (INTVAL (op1))) { - int even = INTVAL (op1) - 1; + HOST_WIDE_INT even = INTVAL (op1) - 1; op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even)); op0 = gen_rtx_CONST (Pmode, op0); op1 = const1_rtx; @@ -2900,7 +3087,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF); @@ -3068,7 +3255,7 @@ legitimize_tls_address (rtx addr, rtx reg) in both 31- and 64-bit code. */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); new = gen_rtx_CONST (Pmode, new); @@ -3097,7 +3284,7 @@ legitimize_tls_address (rtx addr, rtx reg) from the literal pool. */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); new = gen_rtx_CONST (Pmode, new); @@ -3194,7 +3381,7 @@ legitimize_tls_address (rtx addr, rtx reg) void emit_symbolic_move (rtx *operands) { - rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); + rtx temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode); if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (Pmode, operands[1]); @@ -3228,6 +3415,12 @@ legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, if (legitimate_address_p (mode, x, FALSE)) return x; } + else if (GET_CODE (x) == PLUS + && (TLS_SYMBOLIC_CONST (XEXP (x, 0)) + || TLS_SYMBOLIC_CONST (XEXP (x, 1)))) + { + return x; + } else if (flag_pic) { if (SYMBOLIC_CONST (x) @@ -3388,7 +3581,7 @@ s390_expand_movmem (rtx dst, rtx src, rtx len) if (temp != count) emit_move_insn (count, temp); - temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); if (temp != blocks) emit_move_insn (blocks, temp); @@ -3425,10 +3618,12 @@ s390_expand_movmem (rtx dst, rtx src, rtx len) void s390_expand_setmem (rtx dst, rtx len, rtx val) { - gcc_assert (GET_CODE (len) != CONST_INT || INTVAL (len) > 0); + if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0) + return; + gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode); - if (GET_CODE (len) == CONST_INT && INTVAL (len) <= 257) + if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257) { if (val == const0_rtx && INTVAL (len) <= 256) emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1))); @@ -3502,7 +3697,7 @@ s390_expand_setmem (rtx dst, rtx len, rtx val) if (temp != count) emit_move_insn (count, temp); - temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); if (temp != blocks) emit_move_insn (blocks, temp); @@ -3594,7 +3789,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len) if (temp != count) emit_move_insn (count, temp); - temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); if (temp != blocks) emit_move_insn (blocks, temp); @@ -3731,8 +3926,8 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1, if (!register_operand (src, GET_MODE (dst))) src = force_reg (GET_MODE (dst), src); - src = gen_rtx_PLUS (GET_MODE (dst), src, const0_rtx); - op_res = gen_rtx_PLUS (GET_MODE (dst), src, op_res); + op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, src); + op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, const0_rtx); } p = rtvec_alloc (2); @@ -3823,7 +4018,7 @@ s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src) int bitsize = INTVAL (op1); int bitpos = INTVAL (op2); - /* We need byte alignement. */ + /* We need byte alignment. */ if (bitsize % BITS_PER_UNIT) return false; @@ -3911,6 +4106,242 @@ s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src) return false; } +/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a + register that holds VAL of mode MODE shifted by COUNT bits. */ + +static inline rtx +s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count) +{ + val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)), + NULL_RTX, 1, OPTAB_DIRECT); + return expand_simple_binop (SImode, ASHIFT, val, count, + NULL_RTX, 1, OPTAB_DIRECT); +} + +/* Structure to hold the initial parameters for a compare_and_swap operation + in HImode and QImode. */ + +struct alignment_context +{ + rtx memsi; /* SI aligned memory location. */ + rtx shift; /* Bit offset with regard to lsb. */ + rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */ + rtx modemaski; /* ~modemask */ + bool aligned; /* True if memory is aligned, false else. */ +}; + +/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize + structure AC for transparent simplifying, if the memory alignment is known + to be at least 32bit. MEM is the memory location for the actual operation + and MODE its mode. */ + +static void +init_alignment_context (struct alignment_context *ac, rtx mem, + enum machine_mode mode) +{ + ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode)); + ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode)); + + if (ac->aligned) + ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */ + else + { + /* Alignment is unknown. */ + rtx byteoffset, addr, align; + + /* Force the address into a register. */ + addr = force_reg (Pmode, XEXP (mem, 0)); + + /* Align it to SImode. */ + align = expand_simple_binop (Pmode, AND, addr, + GEN_INT (-GET_MODE_SIZE (SImode)), + NULL_RTX, 1, OPTAB_DIRECT); + /* Generate MEM. */ + ac->memsi = gen_rtx_MEM (SImode, align); + MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem); + set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER); + set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode)); + + /* Calculate shiftcount. */ + byteoffset = expand_simple_binop (Pmode, AND, addr, + GEN_INT (GET_MODE_SIZE (SImode) - 1), + NULL_RTX, 1, OPTAB_DIRECT); + /* As we already have some offset, evaluate the remaining distance. */ + ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset, + NULL_RTX, 1, OPTAB_DIRECT); + + } + /* Shift is the byte count, but we need the bitcount. */ + ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT), + NULL_RTX, 1, OPTAB_DIRECT); + /* Calculate masks. */ + ac->modemask = expand_simple_binop (SImode, ASHIFT, + GEN_INT (GET_MODE_MASK (mode)), ac->shift, + NULL_RTX, 1, OPTAB_DIRECT); + ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1); +} + +/* Expand an atomic compare and swap operation for HImode and QImode. MEM is + the memory location, CMP the old value to compare MEM with and NEW the value + to set if CMP == MEM. + CMP is never in memory for compare_and_swap_cc because + expand_bool_compare_and_swap puts it into a register for later compare. */ + +void +s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new) +{ + struct alignment_context ac; + rtx cmpv, newv, val, resv, cc; + rtx res = gen_reg_rtx (SImode); + rtx csloop = gen_label_rtx (); + rtx csend = gen_label_rtx (); + + gcc_assert (register_operand (target, VOIDmode)); + gcc_assert (MEM_P (mem)); + + init_alignment_context (&ac, mem, mode); + + /* Shift the values to the correct bit positions. */ + if (!(ac.aligned && MEM_P (cmp))) + cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift); + if (!(ac.aligned && MEM_P (new))) + new = s390_expand_mask_and_shift (new, mode, ac.shift); + + /* Load full word. Subsequent loads are performed by CS. */ + val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + + /* Start CS loop. */ + emit_label (csloop); + /* val = "00..0" + * cmp = "00..000..0" + * new = "00..000..0" + */ + + /* Patch cmp and new with val at correct position. */ + if (ac.aligned && MEM_P (cmp)) + { + cmpv = force_reg (SImode, val); + store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp); + } + else + cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val, + NULL_RTX, 1, OPTAB_DIRECT)); + if (ac.aligned && MEM_P (new)) + { + newv = force_reg (SImode, val); + store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new); + } + else + newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val, + NULL_RTX, 1, OPTAB_DIRECT)); + + /* Jump to end if we're done (likely?). */ + s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi, + cmpv, newv)); + + /* Check for changes outside mode. */ + resv = expand_simple_binop (SImode, AND, res, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + cc = s390_emit_compare (NE, resv, val); + emit_move_insn (val, resv); + /* Loop internal if so. */ + s390_emit_jump (csloop, cc); + + emit_label (csend); + + /* Return the correct part of the bitfield. */ + convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift, + NULL_RTX, 1, OPTAB_DIRECT), 1); +} + +/* Expand an atomic operation CODE of mode MODE. MEM is the memory location + and VAL the value to play with. If AFTER is true then store the value + MEM holds after the operation, if AFTER is false then store the value MEM + holds before the operation. If TARGET is zero then discard that value, else + store it to TARGET. */ + +void +s390_expand_atomic (enum machine_mode mode, enum rtx_code code, + rtx target, rtx mem, rtx val, bool after) +{ + struct alignment_context ac; + rtx cmp; + rtx new = gen_reg_rtx (SImode); + rtx orig = gen_reg_rtx (SImode); + rtx csloop = gen_label_rtx (); + + gcc_assert (!target || register_operand (target, VOIDmode)); + gcc_assert (MEM_P (mem)); + + init_alignment_context (&ac, mem, mode); + + /* Shift val to the correct bit positions. + Preserve "icm", but prevent "ex icm". */ + if (!(ac.aligned && code == SET && MEM_P (val))) + val = s390_expand_mask_and_shift (val, mode, ac.shift); + + /* Further preparation insns. */ + if (code == PLUS || code == MINUS) + emit_move_insn (orig, val); + else if (code == MULT || code == AND) /* val = "11..111..1" */ + val = expand_simple_binop (SImode, XOR, val, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + + /* Load full word. Subsequent loads are performed by CS. */ + cmp = force_reg (SImode, ac.memsi); + + /* Start CS loop. */ + emit_label (csloop); + emit_move_insn (new, cmp); + + /* Patch new with val at correct position. */ + switch (code) + { + case PLUS: + case MINUS: + val = expand_simple_binop (SImode, code, new, orig, + NULL_RTX, 1, OPTAB_DIRECT); + val = expand_simple_binop (SImode, AND, val, ac.modemask, + NULL_RTX, 1, OPTAB_DIRECT); + /* FALLTHRU */ + case SET: + if (ac.aligned && MEM_P (val)) + store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val); + else + { + new = expand_simple_binop (SImode, AND, new, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + new = expand_simple_binop (SImode, IOR, new, val, + NULL_RTX, 1, OPTAB_DIRECT); + } + break; + case AND: + case IOR: + case XOR: + new = expand_simple_binop (SImode, code, new, val, + NULL_RTX, 1, OPTAB_DIRECT); + break; + case MULT: /* NAND */ + new = expand_simple_binop (SImode, XOR, new, ac.modemask, + NULL_RTX, 1, OPTAB_DIRECT); + new = expand_simple_binop (SImode, AND, new, val, + NULL_RTX, 1, OPTAB_DIRECT); + break; + default: + gcc_unreachable (); + } + + s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp, + ac.memsi, cmp, new)); + + /* Return the correct part of the bitfield. */ + if (target) + convert_move (target, expand_simple_binop (SImode, LSHIFTRT, + after ? new : cmp, ac.shift, + NULL_RTX, 1, OPTAB_DIRECT), 1); +} + /* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL. We need to emit DTP-relative relocations. */ @@ -3934,6 +4365,21 @@ s390_output_dwarf_dtprel (FILE *file, int size, rtx x) fputs ("@DTPOFF", file); } +#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING +/* Implement TARGET_MANGLE_TYPE. */ + +static const char * +s390_mangle_type (const_tree type) +{ + if (TYPE_MAIN_VARIANT (type) == long_double_type_node + && TARGET_LONG_DOUBLE_128) + return "g"; + + /* For all other types, use normal C++ mangling. */ + return NULL; +} +#endif + /* In the name of slightly smaller debug output, and to cater to general assembler lossage, recognize various UNSPEC sequences and turn them back into a direct symbol reference. */ @@ -3978,46 +4424,25 @@ s390_delegitimize_address (rtx orig_x) static void print_shift_count_operand (FILE *file, rtx op) { - HOST_WIDE_INT offset = 0; - - /* Shift count operands are always truncated to the 6 least significant bits and - the setmem padding byte to the least 8 significant bits. Hence we can drop - pointless ANDs. */ - if (GET_CODE (op) == AND && GET_CODE (XEXP (op, 1)) == CONST_INT) - { - if ((INTVAL (XEXP (op, 1)) & 63) != 63) - gcc_unreachable (); - - op = XEXP (op, 0); - } + HOST_WIDE_INT offset; + rtx base; - /* We can have an integer constant, an address register, - or a sum of the two. */ - if (GET_CODE (op) == CONST_INT) - { - offset = INTVAL (op); - op = NULL_RTX; - } - if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT) - { - offset = INTVAL (XEXP (op, 1)); - op = XEXP (op, 0); - } - while (op && GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); + /* Extract base register and offset. */ + if (!s390_decompose_shift_count (op, &base, &offset)) + gcc_unreachable (); /* Sanity check. */ - if (op) + if (base) { - gcc_assert (GET_CODE (op) == REG); - gcc_assert (REGNO (op) < FIRST_PSEUDO_REGISTER); - gcc_assert (REGNO_REG_CLASS (REGNO (op)) == ADDR_REGS); + gcc_assert (GET_CODE (base) == REG); + gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER); + gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS); } /* Offsets are constricted to twelve bits. */ fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1)); - if (op) - fprintf (file, "(%s)", reg_names[REGNO (op)]); + if (base) + fprintf (file, "(%s)", reg_names[REGNO (base)]); } /* See 'get_some_local_dynamic_name'. */ @@ -4130,8 +4555,8 @@ print_operand_address (FILE *file, rtx addr) struct s390_address ad; 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))) + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) output_operand_lossage ("cannot decompose address"); if (ad.disp) @@ -4215,7 +4640,7 @@ print_operand (FILE *file, rtx x, int code) gcc_assert (GET_CODE (x) == MEM); ret = s390_decompose_address (XEXP (x, 0), &ad); gcc_assert (ret); - gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base)); + gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); gcc_assert (!ad.indx); if (ad.disp) @@ -4233,7 +4658,7 @@ print_operand (FILE *file, rtx x, int code) gcc_assert (GET_CODE (x) == MEM); ret = s390_decompose_address (XEXP (x, 0), &ad); gcc_assert (ret); - gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base)); + gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); gcc_assert (!ad.indx); if (ad.base) @@ -4251,7 +4676,7 @@ print_operand (FILE *file, rtx x, int code) gcc_assert (GET_CODE (x) == MEM); ret = s390_decompose_address (XEXP (x, 0), &ad); gcc_assert (ret); - gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base)); + gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); gcc_assert (!ad.indx); if (ad.disp) @@ -4877,12 +5302,12 @@ replace_ltrel_base (rtx *x) /* We keep a list of constants which we have to add to internal constant tables in the middle of large functions. */ -#define NR_C_MODES 7 +#define NR_C_MODES 11 enum machine_mode constant_modes[NR_C_MODES] = { - TImode, - DFmode, DImode, - SFmode, SImode, + TFmode, TImode, TDmode, + DFmode, DImode, DDmode, + SFmode, SImode, SDmode, HImode, QImode }; @@ -5174,7 +5599,7 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label) for (c = pool->constants[i]; c; c = c->next) { /* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references. */ - rtx value = c->value; + rtx value = copy_rtx (c->value); if (GET_CODE (value) == CONST && GET_CODE (XEXP (value, 0)) == UNSPEC && XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET @@ -5825,6 +6250,7 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align) switch (GET_MODE_CLASS (mode)) { case MODE_FLOAT: + case MODE_DECIMAL_FLOAT: gcc_assert (GET_CODE (exp) == CONST_DOUBLE); REAL_VALUE_FROM_CONST_DOUBLE (r, exp); @@ -5910,7 +6336,7 @@ find_unused_clobbered_reg (void) { int i; for (i = 0; i < 6; i++) - if (!regs_ever_live[i]) + if (!df_regs_ever_live_p (i)) return i; return 0; } @@ -5920,7 +6346,7 @@ find_unused_clobbered_reg (void) clobbered hard regs in SETREG. */ static void -s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data) +s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *data) { int *regs_ever_clobbered = (int *)data; unsigned int i, regno; @@ -5974,7 +6400,10 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered) deal with this automatically. */ if (current_function_calls_eh_return || cfun->machine->has_landing_pad_p) for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++) - regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1; + if (current_function_calls_eh_return + || (cfun->machine->has_landing_pad_p + && df_regs_ever_live_p (EH_RETURN_DATA_REGNO (i)))) + regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1; /* For nonlocal gotos all call-saved registers have to be saved. This flag is also set for the unwinding code in libgcc. @@ -6052,7 +6481,7 @@ s390_register_info (int clobbered_regs[]) cfun_frame_layout.high_fprs = 0; if (TARGET_64BIT) for (i = 24; i < 32; i++) - if (regs_ever_live[i] && !global_regs[i]) + if (df_regs_ever_live_p (i) && !global_regs[i]) { cfun_set_fpr_bit (i - 16); cfun_frame_layout.high_fprs++; @@ -6074,7 +6503,7 @@ s390_register_info (int clobbered_regs[]) if (flag_pic) clobbered_regs[PIC_OFFSET_TABLE_REGNUM] - |= regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; + |= df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM); clobbered_regs[BASE_REGNUM] |= (cfun->machine->base_reg @@ -6097,15 +6526,17 @@ s390_register_info (int clobbered_regs[]) || current_function_stdarg); for (i = 6; i < 16; i++) - if (clobbered_regs[i]) + if (df_regs_ever_live_p (i) || clobbered_regs[i]) break; for (j = 15; j > i; j--) - if (clobbered_regs[j]) + if (df_regs_ever_live_p (j) || clobbered_regs[j]) break; if (i == 16) { /* Nothing to save/restore. */ + cfun_frame_layout.first_save_gpr_slot = -1; + cfun_frame_layout.last_save_gpr_slot = -1; cfun_frame_layout.first_save_gpr = -1; cfun_frame_layout.first_restore_gpr = -1; cfun_frame_layout.last_save_gpr = -1; @@ -6113,11 +6544,36 @@ s390_register_info (int clobbered_regs[]) } else { - /* Save / Restore from gpr i to j. */ - cfun_frame_layout.first_save_gpr = i; - cfun_frame_layout.first_restore_gpr = i; - cfun_frame_layout.last_save_gpr = j; - cfun_frame_layout.last_restore_gpr = j; + /* Save slots for gprs from i to j. */ + cfun_frame_layout.first_save_gpr_slot = i; + cfun_frame_layout.last_save_gpr_slot = j; + + for (i = cfun_frame_layout.first_save_gpr_slot; + i < cfun_frame_layout.last_save_gpr_slot + 1; + i++) + if (clobbered_regs[i]) + break; + + for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--) + if (clobbered_regs[j]) + break; + + if (i == cfun_frame_layout.last_save_gpr_slot + 1) + { + /* Nothing to save/restore. */ + cfun_frame_layout.first_save_gpr = -1; + cfun_frame_layout.first_restore_gpr = -1; + cfun_frame_layout.last_save_gpr = -1; + cfun_frame_layout.last_restore_gpr = -1; + } + else + { + /* Save / Restore from gpr i to j. */ + cfun_frame_layout.first_save_gpr = i; + cfun_frame_layout.first_restore_gpr = i; + cfun_frame_layout.last_save_gpr = j; + cfun_frame_layout.last_restore_gpr = j; + } } if (current_function_stdarg) @@ -6133,11 +6589,17 @@ s390_register_info (int clobbered_regs[]) if (cfun_frame_layout.first_save_gpr == -1 || cfun_frame_layout.first_save_gpr > 2 + min_gpr) - cfun_frame_layout.first_save_gpr = 2 + min_gpr; + { + cfun_frame_layout.first_save_gpr = 2 + min_gpr; + cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr; + } if (cfun_frame_layout.last_save_gpr == -1 || cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1) - cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1; + { + cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1; + cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1; + } } /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved. */ @@ -6161,7 +6623,7 @@ s390_register_info (int clobbered_regs[]) if (!TARGET_64BIT) for (i = 2; i < 4; i++) - if (regs_ever_live[i + 16] && !global_regs[i + 16]) + if (df_regs_ever_live_p (i + 16) && !global_regs[i + 16]) cfun_set_fpr_bit (i); } @@ -6182,7 +6644,7 @@ s390_frame_info (void) cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD; cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8; cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8; - cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr + cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot * UNITS_PER_WORD); } else if (TARGET_BACKCHAIN) /* kernel stack layout */ @@ -6191,7 +6653,7 @@ s390_frame_info (void) - UNITS_PER_WORD); cfun_frame_layout.gprs_offset = (cfun_frame_layout.backchain_offset - - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr + 1) + - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD); if (TARGET_64BIT) @@ -6299,15 +6761,15 @@ s390_init_frame_layout (void) /* Try to predict whether we'll need the base register. */ base_used = cfun->machine->split_branches_pending_p || current_function_uses_const_pool - || (!DISP_IN_RANGE (-frame_size) - && !CONST_OK_FOR_K (-frame_size)); + || (!DISP_IN_RANGE (frame_size) + && !CONST_OK_FOR_K (frame_size)); /* Decide which register to use as literal pool base. In small leaf functions, try to use an unused call-clobbered register as base register to avoid save/restore overhead. */ if (!base_used) cfun->machine->base_reg = NULL_RTX; - else if (current_function_is_leaf && !regs_ever_live[5]) + else if (current_function_is_leaf && !df_regs_ever_live_p (5)) cfun->machine->base_reg = gen_rtx_REG (Pmode, 5); else cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM); @@ -6330,12 +6792,63 @@ s390_update_frame_layout (void) s390_register_info (clobbered_regs); - regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM]; - regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM]; - regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM]; + df_set_regs_ever_live (BASE_REGNUM, + clobbered_regs[BASE_REGNUM] ? true : false); + df_set_regs_ever_live (RETURN_REGNUM, + clobbered_regs[RETURN_REGNUM] ? true : false); + df_set_regs_ever_live (STACK_POINTER_REGNUM, + clobbered_regs[STACK_POINTER_REGNUM] ? true : false); if (cfun->machine->base_reg) - regs_ever_live[REGNO (cfun->machine->base_reg)] = 1; + df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true); +} + +/* Return true if it is legal to put a value with MODE into REGNO. */ + +bool +s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) +{ + switch (REGNO_REG_CLASS (regno)) + { + case FP_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (mode == SImode || mode == DImode) + return true; + + if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT) + return true; + } + break; + case ADDR_REGS: + if (FRAME_REGNO_P (regno) && mode == Pmode) + return true; + + /* fallthrough */ + case GENERAL_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (TARGET_64BIT + || (mode != TFmode && mode != TCmode && mode != TDmode)) + return true; + } + break; + case CC_REGS: + if (GET_MODE_CLASS (mode) == MODE_CC) + return true; + break; + case ACCESS_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (mode == SImode || mode == Pmode) + return true; + } + break; + default: + return false; + } + + return false; } /* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ @@ -6353,6 +6866,27 @@ s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg) return true; } +/* Maximum number of registers to represent a value of mode MODE + in a register of class CLASS. */ + +bool +s390_class_max_nregs (enum reg_class class, enum machine_mode mode) +{ + switch (class) + { + case FP_REGS: + if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8); + else + return (GET_MODE_SIZE (mode) + 8 - 1) / 8; + case ACCESS_REGS: + return (GET_MODE_SIZE (mode) + 4 - 1) / 4; + default: + break; + } + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + /* Return true if register FROM can be eliminated via register TO. */ bool @@ -6422,7 +6956,7 @@ s390_initial_elimination_offset (int from, int to) case RETURN_ADDRESS_POINTER_REGNUM: s390_init_frame_layout (); - index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr; + index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot; gcc_assert (index >= 0); offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset; offset += index * UNITS_PER_WORD; @@ -6627,6 +7161,18 @@ s390_load_got (void) return insns; } +/* This ties together stack memory (MEM with an alias set of frame_alias_set) + and the change to the stack pointer. */ + +static void +s390_emit_stack_tie (void) +{ + rtx mem = gen_frame_mem (BLKmode, + gen_rtx_REG (Pmode, STACK_POINTER_REGNUM)); + + emit_insn (gen_stack_tie (mem)); +} + /* Expand the prologue into a bunch of separate insns. */ void @@ -6649,7 +7195,10 @@ s390_emit_prologue (void) for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) - annotate_constant_pool_refs (&PATTERN (insn)); + { + annotate_constant_pool_refs (&PATTERN (insn)); + df_insn_rescan (insn); + } pop_topmost_sequence (); @@ -6667,7 +7216,9 @@ s390_emit_prologue (void) if (cfun_frame_layout.first_save_gpr != -1) { insn = save_gprs (stack_pointer_rtx, - cfun_frame_layout.gprs_offset, + cfun_frame_layout.gprs_offset + + UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr + - cfun_frame_layout.first_save_gpr_slot), cfun_frame_layout.first_save_gpr, cfun_frame_layout.last_save_gpr); emit_insn (insn); @@ -6740,21 +7291,47 @@ s390_emit_prologue (void) if (s390_stack_size) { - HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1) - & ~(s390_stack_guard - 1)); - rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx, - GEN_INT (stack_check_mask)); + HOST_WIDE_INT stack_guard; + + if (s390_stack_guard) + stack_guard = s390_stack_guard; + else + { + /* If no value for stack guard is provided the smallest power of 2 + larger than the current frame size is chosen. */ + stack_guard = 1; + while (stack_guard < cfun_frame_layout.frame_size) + stack_guard <<= 1; + } - if (TARGET_64BIT) - gen_cmpdi (t, const0_rtx); + if (cfun_frame_layout.frame_size >= s390_stack_size) + { + warning (0, "frame size of function %qs is " + HOST_WIDE_INT_PRINT_DEC + " bytes exceeding user provided stack limit of " + HOST_WIDE_INT_PRINT_DEC " bytes. " + "An unconditional trap is added.", + current_function_name(), cfun_frame_layout.frame_size, + s390_stack_size); + emit_insn (gen_trap ()); + } else - gen_cmpsi (t, const0_rtx); + { + HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1) + & ~(stack_guard - 1)); + rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx, + GEN_INT (stack_check_mask)); + if (TARGET_64BIT) + gen_cmpdi (t, const0_rtx); + else + gen_cmpsi (t, const0_rtx); - emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, - gen_rtx_REG (CCmode, - CC_REGNUM), - const0_rtx), - const0_rtx)); + emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, + gen_rtx_REG (CCmode, + CC_REGNUM), + const0_rtx), + const0_rtx)); + } } if (s390_warn_framesize > 0 @@ -6824,6 +7401,11 @@ s390_emit_prologue (void) if (cfun_save_high_fprs_p && next_fpr) { + /* If the stack might be accessed through a different register + we have to make sure that the stack pointer decrement is not + moved below the use of the stack slots. */ + s390_emit_stack_tie (); + insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT (cfun_frame_layout.f8_offset))); @@ -6859,17 +7441,12 @@ s390_emit_prologue (void) /* Set up got pointer, if needed. */ - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) { rtx insns = s390_load_got (); for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - annotate_constant_pool_refs (&PATTERN (insn)); - - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, - REG_NOTES (insn)); - } + annotate_constant_pool_refs (&PATTERN (insn)); emit_insn (insns); } @@ -7021,7 +7598,7 @@ s390_emit_epilogue (bool sibcall) { addr = plus_constant (frame_pointer, offset + cfun_frame_layout.gprs_offset - + (i - cfun_frame_layout.first_save_gpr) + + (i - cfun_frame_layout.first_save_gpr_slot) * UNITS_PER_WORD); addr = gen_rtx_MEM (Pmode, addr); set_mem_alias_set (addr, get_frame_alias_set ()); @@ -7046,7 +7623,7 @@ s390_emit_epilogue (bool sibcall) addr = plus_constant (frame_pointer, offset + cfun_frame_layout.gprs_offset + (RETURN_REGNUM - - cfun_frame_layout.first_save_gpr) + - cfun_frame_layout.first_save_gpr_slot) * UNITS_PER_WORD); addr = gen_rtx_MEM (Pmode, addr); set_mem_alias_set (addr, get_frame_alias_set ()); @@ -7057,7 +7634,7 @@ s390_emit_epilogue (bool sibcall) insn = restore_gprs (frame_pointer, offset + cfun_frame_layout.gprs_offset + (cfun_frame_layout.first_restore_gpr - - cfun_frame_layout.first_save_gpr) + - cfun_frame_layout.first_save_gpr_slot) * UNITS_PER_WORD, cfun_frame_layout.first_restore_gpr, cfun_frame_layout.last_restore_gpr); @@ -7083,7 +7660,7 @@ s390_emit_epilogue (bool sibcall) MODE must be specified. */ static int -s390_function_arg_size (enum machine_mode mode, tree type) +s390_function_arg_size (enum machine_mode mode, const_tree type) { if (type) return int_size_in_bytes (type); @@ -7112,7 +7689,7 @@ s390_function_arg_float (enum machine_mode mode, tree type) /* No type info available for some library calls ... */ if (!type) - return mode == SFmode || mode == DFmode; + return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode; /* The ABI says that record types with a single member are treated just like that member would be. */ @@ -7154,7 +7731,7 @@ s390_function_arg_integer (enum machine_mode mode, tree type) /* No type info available for some library calls ... */ if (!type) return GET_MODE_CLASS (mode) == MODE_INT - || (TARGET_SOFT_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT); + || (TARGET_SOFT_FLOAT && SCALAR_FLOAT_MODE_P (mode)); /* We accept small integral (and similar) types. */ if (INTEGRAL_TYPE_P (type) @@ -7181,7 +7758,7 @@ s390_function_arg_integer (enum machine_mode mode, tree type) static bool s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, - enum machine_mode mode, tree type, + enum machine_mode mode, const_tree type, bool named ATTRIBUTE_UNUSED) { int size = s390_function_arg_size (mode, type); @@ -7281,7 +7858,7 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, hidden first argument. */ static bool -s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED) +s390_return_in_memory (const_tree type, const_tree fundecl ATTRIBUTE_UNUSED) { /* We accept small integral (and similar) types. */ if (INTEGRAL_TYPE_P (type) @@ -7308,7 +7885,7 @@ s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED) value of mode MODE from a libcall. */ rtx -s390_function_value (tree type, enum machine_mode mode) +s390_function_value (const_tree type, enum machine_mode mode) { if (type) { @@ -7316,11 +7893,10 @@ s390_function_value (tree type, enum machine_mode mode) mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1); } - gcc_assert (GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_FLOAT); + gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode)); gcc_assert (GET_MODE_SIZE (mode) <= 8); - if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT) + if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode)) return gen_rtx_REG (mode, 16); else return gen_rtx_REG (mode, 2); @@ -7401,7 +7977,7 @@ s390_build_builtin_va_list (void) holds the offset of the first anonymous stack argument (relative to the virtual arg pointer). */ -void +static void s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) { HOST_WIDE_INT n_gpr, n_fpr; @@ -7415,10 +7991,10 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) f_sav = TREE_CHAIN (f_ovf); valist = build_va_arg_indirect_ref (valist); - gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); - fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); - ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); - sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); + gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); + fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); + ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); + sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); /* Count number of gp and fp argument registers used. */ @@ -7427,16 +8003,16 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) if (cfun->va_list_gpr_size) { - t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, - build_int_cst (NULL_TREE, n_gpr)); + t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (gpr), gpr, + build_int_cst (NULL_TREE, n_gpr)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } if (cfun->va_list_fpr_size) { - t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, - build_int_cst (NULL_TREE, n_fpr)); + t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (fpr), fpr, + build_int_cst (NULL_TREE, n_fpr)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } @@ -7453,9 +8029,9 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n", (int)n_gpr, (int)n_fpr, off); - t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off)); + t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t, size_int (off)); - t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); + t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } @@ -7465,10 +8041,10 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG)) { t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx); - t = build (PLUS_EXPR, TREE_TYPE (sav), t, - build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD)); + t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t, + size_int (-RETURN_REGNUM * UNITS_PER_WORD)); - t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t); + t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (sav), sav, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } @@ -7513,10 +8089,10 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, f_sav = TREE_CHAIN (f_ovf); valist = build_va_arg_indirect_ref (valist); - gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); - fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); - ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); - sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); + gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); + fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); + ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); + sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); size = int_size_in_bytes (type); @@ -7595,13 +8171,13 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE); gimplify_and_add (t, pre_p); - t = build2 (PLUS_EXPR, ptr_type_node, sav, - fold_convert (ptr_type_node, size_int (sav_ofs))); + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, + size_int (sav_ofs)); u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, fold_convert (TREE_TYPE (reg), size_int (sav_scale))); - t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u)); + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, fold_convert (sizetype, u)); - t = build2 (MODIFY_EXPR, void_type_node, addr, t); + t = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t); gimplify_and_add (t, pre_p); t = build1 (GOTO_EXPR, void_type_node, lab_over); @@ -7615,17 +8191,17 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, 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 = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, + 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); + u = build2 (GIMPLE_MODIFY_STMT, 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); + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, + size_int (size)); + t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, ovf, t); gimplify_and_add (t, pre_p); t = build1 (LABEL_EXPR, void_type_node, lab_over); @@ -7680,14 +8256,14 @@ 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); + add_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); + add_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, @@ -7706,13 +8282,14 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, 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); + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 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; + tree arg; + call_expr_arg_iterator iter; if (fcode >= S390_BUILTIN_max) internal_error ("bad builtin fcode"); @@ -7722,13 +8299,11 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; - for (arglist = TREE_OPERAND (exp, 1), arity = 0; - arglist; - arglist = TREE_CHAIN (arglist), arity++) + arity = 0; + FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) { const struct insn_operand_data *insn_op; - tree arg = TREE_VALUE (arglist); if (arg == error_mark_node) return NULL_RTX; if (arity > MAX_ARGS) @@ -7740,6 +8315,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, if (!(*insn_op->predicate) (op[arity], insn_op->mode)) op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); + arity++; } if (nonvoid) @@ -7823,28 +8399,6 @@ s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt) 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 - gcc_unreachable (); -#endif -#endif -} - /* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. */ @@ -8199,28 +8753,26 @@ s390_valid_pointer_mode (enum machine_mode mode) return (mode == SImode || (TARGET_64BIT && mode == DImode)); } -/* Checks whether the given ARGUMENT_LIST would use a caller +/* Checks whether the given CALL_EXPR would use a caller saved register. This is used to decide whether sibling call optimization could be performed on the respective function call. */ static bool -s390_call_saved_register_used (tree argument_list) +s390_call_saved_register_used (tree call_expr) { CUMULATIVE_ARGS cum; tree parameter; enum machine_mode mode; tree type; rtx parm_rtx; - int reg; + int reg, i; INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0); - while (argument_list) + for (i = 0; i < call_expr_nargs (call_expr); i++) { - parameter = TREE_VALUE (argument_list); - argument_list = TREE_CHAIN (argument_list); - + parameter = CALL_EXPR_ARG (call_expr, i); gcc_assert (parameter); /* For an undeclared variable passed as parameter we will get @@ -8270,17 +8822,13 @@ s390_function_ok_for_sibcall (tree decl, tree exp) /* 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)) + if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl)) return false; /* 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; - - return true; + return !s390_call_saved_register_used (exp); } /* Return the fixed registers used for condition codes. */ @@ -8610,6 +9158,8 @@ s390_reorg (void) machine_dependent_reorg might confuse insn length counts. */ split_all_insns_noflow (); + /* From here on decomposed literal pool addresses must be accepted. */ + cfun->machine->decomposed_literal_pool_addresses_ok_p = true; /* Install the main literal pool and the associated base register load insns. @@ -8756,7 +9306,7 @@ s390_reorg (void) #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 +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true #undef TARGET_SCHED_ADJUST_PRIORITY #define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority @@ -8780,13 +9330,15 @@ s390_reorg (void) #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list +#undef TARGET_EXPAND_BUILTIN_VA_START +#define TARGET_EXPAND_BUILTIN_VA_START s390_va_start #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 +#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true #undef TARGET_PROMOTE_FUNCTION_RETURN -#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true +#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true #undef TARGET_PASS_BY_REFERENCE #define TARGET_PASS_BY_REFERENCE s390_pass_by_reference @@ -8800,13 +9352,30 @@ s390_reorg (void) #define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible #undef TARGET_INVALID_WITHIN_DOLOOP -#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null +#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_null #ifdef HAVE_AS_TLS #undef TARGET_ASM_OUTPUT_DWARF_DTPREL #define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel #endif +#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING +#undef TARGET_MANGLE_TYPE +#define TARGET_MANGLE_TYPE s390_mangle_type +#endif + +#undef TARGET_SCALAR_MODE_SUPPORTED_P +#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD s390_secondary_reload + +#undef TARGET_LIBGCC_CMP_RETURN_MODE +#define TARGET_LIBGCC_CMP_RETURN_MODE s390_libgcc_cmp_return_mode + +#undef TARGET_LIBGCC_SHIFT_COUNT_MODE +#define TARGET_LIBGCC_SHIFT_COUNT_MODE s390_libgcc_shift_count_mode + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-s390.h"