| /* Subroutines used for code generation on IBM S/390 and zSeries |
| Copyright (C) 1999-2015 Free Software Foundation, Inc. |
| Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
| Ulrich Weigand (uweigand@de.ibm.com) and |
| Andreas Krebbel (Andreas.Krebbel@de.ibm.com). |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "fold-const.h" |
| #include "print-tree.h" |
| #include "stringpool.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "calls.h" |
| #include "tm_p.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "insn-config.h" |
| #include "conditions.h" |
| #include "output.h" |
| #include "insn-attr.h" |
| #include "flags.h" |
| #include "except.h" |
| #include "function.h" |
| #include "recog.h" |
| #include "hashtab.h" |
| #include "statistics.h" |
| #include "real.h" |
| #include "fixed-value.h" |
| #include "expmed.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "emit-rtl.h" |
| #include "stmt.h" |
| #include "expr.h" |
| #include "reload.h" |
| #include "diagnostic-core.h" |
| #include "predict.h" |
| #include "dominance.h" |
| #include "cfg.h" |
| #include "cfgrtl.h" |
| #include "cfganal.h" |
| #include "lcm.h" |
| #include "cfgbuild.h" |
| #include "cfgcleanup.h" |
| #include "basic-block.h" |
| #include "ggc.h" |
| #include "target.h" |
| #include "target-def.h" |
| #include "debug.h" |
| #include "langhooks.h" |
| #include "insn-codes.h" |
| #include "optabs.h" |
| #include "hash-table.h" |
| #include "tree-ssa-alias.h" |
| #include "internal-fn.h" |
| #include "gimple-fold.h" |
| #include "tree-eh.h" |
| #include "gimple-expr.h" |
| #include "is-a.h" |
| #include "gimple.h" |
| #include "gimplify.h" |
| #include "df.h" |
| #include "params.h" |
| #include "cfgloop.h" |
| #include "opts.h" |
| #include "tree-pass.h" |
| #include "context.h" |
| #include "builtins.h" |
| #include "rtl-iter.h" |
| #include "intl.h" |
| #include "plugin-api.h" |
| #include "ipa-ref.h" |
| #include "cgraph.h" |
| #include "tm-constrs.h" |
| |
| /* Define the specific costs for a given cpu. */ |
| |
| struct processor_costs |
| { |
| /* multiplication */ |
| const int m; /* cost of an M instruction. */ |
| const int mghi; /* cost of an MGHI instruction. */ |
| const int mh; /* cost of an MH instruction. */ |
| const int mhi; /* cost of an MHI instruction. */ |
| const int ml; /* cost of an ML instruction. */ |
| const int mr; /* cost of an MR instruction. */ |
| const int ms; /* cost of an MS instruction. */ |
| const int msg; /* cost of an MSG instruction. */ |
| const int msgf; /* cost of an MSGF instruction. */ |
| const int msgfr; /* cost of an MSGFR instruction. */ |
| 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 debr; |
| const int dlgr; |
| const int dlr; |
| const int dr; |
| const int dsgfr; |
| const int dsgr; |
| }; |
| |
| const struct processor_costs *s390_cost; |
| |
| static const |
| struct processor_costs z900_cost = |
| { |
| COSTS_N_INSNS (5), /* M */ |
| COSTS_N_INSNS (10), /* MGHI */ |
| COSTS_N_INSNS (5), /* MH */ |
| COSTS_N_INSNS (4), /* MHI */ |
| COSTS_N_INSNS (5), /* ML */ |
| COSTS_N_INSNS (5), /* MR */ |
| COSTS_N_INSNS (4), /* MS */ |
| COSTS_N_INSNS (15), /* MSG */ |
| COSTS_N_INSNS (7), /* MSGF */ |
| COSTS_N_INSNS (7), /* MSGFR */ |
| 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 (27), /* DEBR */ |
| COSTS_N_INSNS (220), /* DLGR */ |
| COSTS_N_INSNS (34), /* DLR */ |
| COSTS_N_INSNS (34), /* DR */ |
| COSTS_N_INSNS (32), /* DSGFR */ |
| COSTS_N_INSNS (32), /* DSGR */ |
| }; |
| |
| static const |
| struct processor_costs z990_cost = |
| { |
| COSTS_N_INSNS (4), /* M */ |
| COSTS_N_INSNS (2), /* MGHI */ |
| COSTS_N_INSNS (2), /* MH */ |
| COSTS_N_INSNS (2), /* MHI */ |
| COSTS_N_INSNS (4), /* ML */ |
| COSTS_N_INSNS (4), /* MR */ |
| COSTS_N_INSNS (5), /* MS */ |
| COSTS_N_INSNS (6), /* MSG */ |
| COSTS_N_INSNS (4), /* MSGF */ |
| COSTS_N_INSNS (4), /* MSGFR */ |
| 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 (26), /* DEBR */ |
| COSTS_N_INSNS (176), /* DLGR */ |
| COSTS_N_INSNS (31), /* DLR */ |
| COSTS_N_INSNS (31), /* DR */ |
| COSTS_N_INSNS (31), /* DSGFR */ |
| COSTS_N_INSNS (31), /* DSGR */ |
| }; |
| |
| static const |
| struct processor_costs z9_109_cost = |
| { |
| COSTS_N_INSNS (4), /* M */ |
| COSTS_N_INSNS (2), /* MGHI */ |
| COSTS_N_INSNS (2), /* MH */ |
| COSTS_N_INSNS (2), /* MHI */ |
| COSTS_N_INSNS (4), /* ML */ |
| COSTS_N_INSNS (4), /* MR */ |
| COSTS_N_INSNS (5), /* MS */ |
| COSTS_N_INSNS (6), /* MSG */ |
| COSTS_N_INSNS (4), /* MSGF */ |
| COSTS_N_INSNS (4), /* MSGFR */ |
| 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 (26), /* DEBR */ |
| COSTS_N_INSNS (30), /* DLGR */ |
| COSTS_N_INSNS (23), /* DLR */ |
| COSTS_N_INSNS (23), /* DR */ |
| COSTS_N_INSNS (24), /* DSGFR */ |
| COSTS_N_INSNS (24), /* DSGR */ |
| }; |
| |
| static const |
| struct processor_costs z10_cost = |
| { |
| COSTS_N_INSNS (10), /* M */ |
| COSTS_N_INSNS (10), /* MGHI */ |
| COSTS_N_INSNS (10), /* MH */ |
| COSTS_N_INSNS (10), /* MHI */ |
| COSTS_N_INSNS (10), /* ML */ |
| COSTS_N_INSNS (10), /* MR */ |
| COSTS_N_INSNS (10), /* MS */ |
| COSTS_N_INSNS (10), /* MSG */ |
| COSTS_N_INSNS (10), /* MSGF */ |
| COSTS_N_INSNS (10), /* MSGFR */ |
| COSTS_N_INSNS (10), /* MSGR */ |
| COSTS_N_INSNS (10), /* MSR */ |
| COSTS_N_INSNS (1) , /* multiplication in DFmode */ |
| COSTS_N_INSNS (50), /* MXBR */ |
| COSTS_N_INSNS (120), /* SQXBR */ |
| COSTS_N_INSNS (52), /* SQDBR */ |
| COSTS_N_INSNS (38), /* SQEBR */ |
| COSTS_N_INSNS (1), /* MADBR */ |
| COSTS_N_INSNS (1), /* MAEBR */ |
| COSTS_N_INSNS (111), /* DXBR */ |
| COSTS_N_INSNS (39), /* DDBR */ |
| COSTS_N_INSNS (32), /* DEBR */ |
| COSTS_N_INSNS (160), /* DLGR */ |
| COSTS_N_INSNS (71), /* DLR */ |
| COSTS_N_INSNS (71), /* DR */ |
| COSTS_N_INSNS (71), /* DSGFR */ |
| COSTS_N_INSNS (71), /* DSGR */ |
| }; |
| |
| static const |
| struct processor_costs z196_cost = |
| { |
| COSTS_N_INSNS (7), /* M */ |
| COSTS_N_INSNS (5), /* MGHI */ |
| COSTS_N_INSNS (5), /* MH */ |
| COSTS_N_INSNS (5), /* MHI */ |
| COSTS_N_INSNS (7), /* ML */ |
| COSTS_N_INSNS (7), /* MR */ |
| COSTS_N_INSNS (6), /* MS */ |
| COSTS_N_INSNS (8), /* MSG */ |
| COSTS_N_INSNS (6), /* MSGF */ |
| COSTS_N_INSNS (6), /* MSGFR */ |
| COSTS_N_INSNS (8), /* MSGR */ |
| COSTS_N_INSNS (6), /* MSR */ |
| COSTS_N_INSNS (1) , /* multiplication in DFmode */ |
| COSTS_N_INSNS (40), /* MXBR B+40 */ |
| COSTS_N_INSNS (100), /* SQXBR B+100 */ |
| COSTS_N_INSNS (42), /* SQDBR B+42 */ |
| COSTS_N_INSNS (28), /* SQEBR B+28 */ |
| COSTS_N_INSNS (1), /* MADBR B */ |
| COSTS_N_INSNS (1), /* MAEBR B */ |
| COSTS_N_INSNS (101), /* DXBR B+101 */ |
| COSTS_N_INSNS (29), /* DDBR */ |
| COSTS_N_INSNS (22), /* DEBR */ |
| COSTS_N_INSNS (160), /* DLGR cracked */ |
| COSTS_N_INSNS (160), /* DLR cracked */ |
| COSTS_N_INSNS (160), /* DR expanded */ |
| COSTS_N_INSNS (160), /* DSGFR cracked */ |
| COSTS_N_INSNS (160), /* DSGR cracked */ |
| }; |
| |
| static const |
| struct processor_costs zEC12_cost = |
| { |
| COSTS_N_INSNS (7), /* M */ |
| COSTS_N_INSNS (5), /* MGHI */ |
| COSTS_N_INSNS (5), /* MH */ |
| COSTS_N_INSNS (5), /* MHI */ |
| COSTS_N_INSNS (7), /* ML */ |
| COSTS_N_INSNS (7), /* MR */ |
| COSTS_N_INSNS (6), /* MS */ |
| COSTS_N_INSNS (8), /* MSG */ |
| COSTS_N_INSNS (6), /* MSGF */ |
| COSTS_N_INSNS (6), /* MSGFR */ |
| COSTS_N_INSNS (8), /* MSGR */ |
| COSTS_N_INSNS (6), /* MSR */ |
| COSTS_N_INSNS (1) , /* multiplication in DFmode */ |
| COSTS_N_INSNS (40), /* MXBR B+40 */ |
| COSTS_N_INSNS (100), /* SQXBR B+100 */ |
| COSTS_N_INSNS (42), /* SQDBR B+42 */ |
| COSTS_N_INSNS (28), /* SQEBR B+28 */ |
| COSTS_N_INSNS (1), /* MADBR B */ |
| COSTS_N_INSNS (1), /* MAEBR B */ |
| COSTS_N_INSNS (131), /* DXBR B+131 */ |
| COSTS_N_INSNS (29), /* DDBR */ |
| COSTS_N_INSNS (22), /* DEBR */ |
| COSTS_N_INSNS (160), /* DLGR cracked */ |
| COSTS_N_INSNS (160), /* DLR cracked */ |
| COSTS_N_INSNS (160), /* DR expanded */ |
| COSTS_N_INSNS (160), /* DSGFR cracked */ |
| COSTS_N_INSNS (160), /* DSGR cracked */ |
| }; |
| |
| extern int reload_completed; |
| |
| /* Kept up to date using the SCHED_VARIABLE_ISSUE hook. */ |
| static rtx_insn *last_scheduled_insn; |
| #define MAX_SCHED_UNITS 3 |
| static int last_scheduled_unit_distance[MAX_SCHED_UNITS]; |
| |
| /* The maximum score added for an instruction whose unit hasn't been |
| in use for MAX_SCHED_MIX_DISTANCE steps. Increase this value to |
| give instruction mix scheduling more priority over instruction |
| grouping. */ |
| #define MAX_SCHED_MIX_SCORE 8 |
| |
| /* The maximum distance up to which individual scores will be |
| calculated. Everything beyond this gives MAX_SCHED_MIX_SCORE. |
| Increase this with the OOO windows size of the machine. */ |
| #define MAX_SCHED_MIX_DISTANCE 100 |
| |
| /* Structure used to hold the components of a S/390 memory |
| address. A legitimate address on S/390 is of the general |
| form |
| base + index + displacement |
| where any of the components is optional. |
| |
| base and index are registers of the class ADDR_REGS, |
| displacement is an unsigned 12-bit immediate constant. */ |
| |
| struct s390_address |
| { |
| rtx base; |
| rtx indx; |
| rtx disp; |
| bool pointer; |
| bool literal_pool; |
| }; |
| |
| /* The following structure is embedded in the machine |
| specific part of struct function. */ |
| |
| struct GTY (()) s390_frame_layout |
| { |
| /* Offset within stack frame. */ |
| HOST_WIDE_INT gprs_offset; |
| HOST_WIDE_INT f0_offset; |
| 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; |
| |
| /* Location (FP register number) where GPRs (r0-r15) should |
| be saved to. |
| 0 - does not need to be saved at all |
| -1 - stack slot */ |
| #define SAVE_SLOT_NONE 0 |
| #define SAVE_SLOT_STACK -1 |
| signed char gpr_save_slots[16]; |
| |
| /* Number of first and last gpr to be saved, restored. */ |
| int first_save_gpr; |
| int first_restore_gpr; |
| int last_save_gpr; |
| int last_restore_gpr; |
| |
| /* Bits standing for floating point registers. Set, if the |
| respective register has to be saved. Starting with reg 16 (f0) |
| at the rightmost bit. |
| Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| fpr 15 13 11 9 14 12 10 8 7 5 3 1 6 4 2 0 |
| reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */ |
| unsigned int fpr_bitmap; |
| |
| /* Number of floating point registers f8-f15 which must be saved. */ |
| int high_fprs; |
| |
| /* Set if return address needs to be saved. |
| This flag is set by s390_return_addr_rtx if it could not use |
| the initial value of r14 and therefore depends on r14 saved |
| to the stack. */ |
| bool save_return_addr_p; |
| |
| /* Size of stack frame. */ |
| HOST_WIDE_INT frame_size; |
| }; |
| |
| /* Define the structure for the machine field in struct function. */ |
| |
| struct GTY(()) machine_function |
| { |
| struct s390_frame_layout frame_layout; |
| |
| /* Literal pool base register. */ |
| rtx base_reg; |
| |
| /* True if we may need to perform branch splitting. */ |
| bool split_branches_pending_p; |
| |
| bool has_landing_pad_p; |
| |
| /* True if the current function may contain a tbegin clobbering |
| FPRs. */ |
| bool tbegin_p; |
| }; |
| |
| /* Few accessor macros for struct cfun->machine->s390_frame_layout. */ |
| |
| #define cfun_frame_layout (cfun->machine->frame_layout) |
| #define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs) |
| #define cfun_save_arg_fprs_p (!!(TARGET_64BIT \ |
| ? cfun_frame_layout.fpr_bitmap & 0x0f \ |
| : cfun_frame_layout.fpr_bitmap & 0x03)) |
| #define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \ |
| cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_LONG) |
| #define cfun_set_fpr_save(REGNO) (cfun->machine->frame_layout.fpr_bitmap |= \ |
| (1 << (REGNO - FPR0_REGNUM))) |
| #define cfun_fpr_save_p(REGNO) (!!(cfun->machine->frame_layout.fpr_bitmap & \ |
| (1 << (REGNO - FPR0_REGNUM)))) |
| #define cfun_gpr_save_slot(REGNO) \ |
| cfun->machine->frame_layout.gpr_save_slots[REGNO] |
| |
| /* Number of GPRs and FPRs used for argument passing. */ |
| #define GP_ARG_NUM_REG 5 |
| #define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2) |
| #define VEC_ARG_NUM_REG 8 |
| |
| /* A couple of shortcuts. */ |
| #define CONST_OK_FOR_J(x) \ |
| CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J") |
| #define CONST_OK_FOR_K(x) \ |
| CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K") |
| #define CONST_OK_FOR_Os(x) \ |
| CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os") |
| #define CONST_OK_FOR_Op(x) \ |
| CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op") |
| #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)) |
| |
| /* That's the read ahead of the dynamic branch prediction unit in |
| bytes on a z10 (or higher) CPU. */ |
| #define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048) |
| |
| |
| /* Indicate which ABI has been used for passing vector args. |
| 0 - no vector type arguments have been passed where the ABI is relevant |
| 1 - the old ABI has been used |
| 2 - a vector type argument has been passed either in a vector register |
| or on the stack by value */ |
| static int s390_vector_abi = 0; |
| |
| /* Set the vector ABI marker if TYPE is subject to the vector ABI |
| switch. The vector ABI affects only vector data types. There are |
| two aspects of the vector ABI relevant here: |
| |
| 1. vectors >= 16 bytes have an alignment of 8 bytes with the new |
| ABI and natural alignment with the old. |
| |
| 2. vector <= 16 bytes are passed in VRs or by value on the stack |
| with the new ABI but by reference on the stack with the old. |
| |
| If ARG_P is true TYPE is used for a function argument or return |
| value. The ABI marker then is set for all vector data types. If |
| ARG_P is false only type 1 vectors are being checked. */ |
| |
| static void |
| s390_check_type_for_vector_abi (const_tree type, bool arg_p, bool in_struct_p) |
| { |
| static hash_set<const_tree> visited_types_hash; |
| |
| if (s390_vector_abi) |
| return; |
| |
| if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK) |
| return; |
| |
| if (visited_types_hash.contains (type)) |
| return; |
| |
| visited_types_hash.add (type); |
| |
| if (VECTOR_TYPE_P (type)) |
| { |
| int type_size = int_size_in_bytes (type); |
| |
| /* Outside arguments only the alignment is changing and this |
| only happens for vector types >= 16 bytes. */ |
| if (!arg_p && type_size < 16) |
| return; |
| |
| /* In arguments vector types > 16 are passed as before (GCC |
| never enforced the bigger alignment for arguments which was |
| required by the old vector ABI). However, it might still be |
| ABI relevant due to the changed alignment if it is a struct |
| member. */ |
| if (arg_p && type_size > 16 && !in_struct_p) |
| return; |
| |
| s390_vector_abi = TARGET_VX_ABI ? 2 : 1; |
| } |
| else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) |
| { |
| /* ARRAY_TYPE: Since with neither of the ABIs we have more than |
| natural alignment there will never be ABI dependent padding |
| in an array type. That's why we do not set in_struct_p to |
| true here. */ |
| s390_check_type_for_vector_abi (TREE_TYPE (type), arg_p, in_struct_p); |
| } |
| else if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) |
| { |
| tree arg_chain; |
| |
| /* Check the return type. */ |
| s390_check_type_for_vector_abi (TREE_TYPE (type), true, false); |
| |
| for (arg_chain = TYPE_ARG_TYPES (type); |
| arg_chain; |
| arg_chain = TREE_CHAIN (arg_chain)) |
| s390_check_type_for_vector_abi (TREE_VALUE (arg_chain), true, false); |
| } |
| else if (RECORD_OR_UNION_TYPE_P (type)) |
| { |
| tree field; |
| |
| for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) |
| { |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| |
| s390_check_type_for_vector_abi (TREE_TYPE (field), arg_p, true); |
| } |
| } |
| } |
| |
| |
| /* System z builtins. */ |
| |
| #include "s390-builtins.h" |
| |
| const unsigned int bflags_builtin[S390_BUILTIN_MAX + 1] = |
| { |
| #undef B_DEF |
| #undef OB_DEF |
| #undef OB_DEF_VAR |
| #define B_DEF(NAME, PATTERN, ATTRS, BFLAGS, ...) BFLAGS, |
| #define OB_DEF(...) |
| #define OB_DEF_VAR(...) |
| #include "s390-builtins.def" |
| 0 |
| }; |
| |
| const unsigned int opflags_builtin[S390_BUILTIN_MAX + 1] = |
| { |
| #undef B_DEF |
| #undef OB_DEF |
| #undef OB_DEF_VAR |
| #define B_DEF(NAME, PATTERN, ATTRS, BFLAGS, OPFLAGS, ...) OPFLAGS, |
| #define OB_DEF(...) |
| #define OB_DEF_VAR(...) |
| #include "s390-builtins.def" |
| 0 |
| }; |
| |
| const unsigned int bflags_overloaded_builtin[S390_OVERLOADED_BUILTIN_MAX + 1] = |
| { |
| #undef B_DEF |
| #undef OB_DEF |
| #undef OB_DEF_VAR |
| #define B_DEF(...) |
| #define OB_DEF(NAME, FIRST_VAR_NAME, LAST_VAR_NAME, BFLAGS, ...) BFLAGS, |
| #define OB_DEF_VAR(...) |
| #include "s390-builtins.def" |
| 0 |
| }; |
| |
| const unsigned int |
| opflags_overloaded_builtin_var[S390_OVERLOADED_BUILTIN_VAR_MAX + 1] = |
| { |
| #undef B_DEF |
| #undef OB_DEF |
| #undef OB_DEF_VAR |
| #define B_DEF(...) |
| #define OB_DEF(...) |
| #define OB_DEF_VAR(NAME, PATTERN, FLAGS, FNTYPE) FLAGS, |
| #include "s390-builtins.def" |
| 0 |
| }; |
| |
| tree s390_builtin_types[BT_MAX]; |
| tree s390_builtin_fn_types[BT_FN_MAX]; |
| tree s390_builtin_decls[S390_BUILTIN_MAX + |
| S390_OVERLOADED_BUILTIN_MAX + |
| S390_OVERLOADED_BUILTIN_VAR_MAX]; |
| |
| static enum insn_code const code_for_builtin[S390_BUILTIN_MAX + 1] = { |
| #undef B_DEF |
| #undef OB_DEF |
| #undef OB_DEF_VAR |
| #define B_DEF(NAME, PATTERN, ...) CODE_FOR_##PATTERN, |
| #define OB_DEF(...) |
| #define OB_DEF_VAR(...) |
| |
| #include "s390-builtins.def" |
| CODE_FOR_nothing |
| }; |
| |
| static void |
| s390_init_builtins (void) |
| { |
| /* These definitions are being used in s390-builtins.def. */ |
| tree returns_twice_attr = tree_cons (get_identifier ("returns_twice"), |
| NULL, NULL); |
| tree noreturn_attr = tree_cons (get_identifier ("noreturn"), NULL, NULL); |
| tree c_uint64_type_node; |
| unsigned int bflags_mask = (BFLAGS_MASK_INIT); |
| |
| bflags_mask |= (TARGET_VX) ? B_VX : 0; |
| bflags_mask |= (TARGET_HTM) ? B_HTM : 0; |
| |
| /* The uint64_type_node from tree.c is not compatible to the C99 |
| uint64_t data type. What we want is c_uint64_type_node from |
| c-common.c. But since backend code is not supposed to interface |
| with the frontend we recreate it here. */ |
| if (TARGET_64BIT) |
| c_uint64_type_node = long_unsigned_type_node; |
| else |
| c_uint64_type_node = long_long_unsigned_type_node; |
| |
| #undef DEF_TYPE |
| #define DEF_TYPE(INDEX, BFLAGS, NODE, CONST_P) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_types[INDEX] = (!CONST_P) ? \ |
| (NODE) : build_type_variant ((NODE), 1, 0); |
| |
| #undef DEF_POINTER_TYPE |
| #define DEF_POINTER_TYPE(INDEX, BFLAGS, INDEX_BASE) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_types[INDEX] = \ |
| build_pointer_type (s390_builtin_types[INDEX_BASE]); |
| |
| #undef DEF_DISTINCT_TYPE |
| #define DEF_DISTINCT_TYPE(INDEX, BFLAGS, INDEX_BASE) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_types[INDEX] = \ |
| build_distinct_type_copy (s390_builtin_types[INDEX_BASE]); |
| |
| #undef DEF_VECTOR_TYPE |
| #define DEF_VECTOR_TYPE(INDEX, BFLAGS, INDEX_BASE, ELEMENTS) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_types[INDEX] = \ |
| build_vector_type (s390_builtin_types[INDEX_BASE], ELEMENTS); |
| |
| #undef DEF_OPAQUE_VECTOR_TYPE |
| #define DEF_OPAQUE_VECTOR_TYPE(INDEX, BFLAGS, INDEX_BASE, ELEMENTS) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_types[INDEX] = \ |
| build_opaque_vector_type (s390_builtin_types[INDEX_BASE], ELEMENTS); |
| |
| #undef DEF_FN_TYPE |
| #define DEF_FN_TYPE(INDEX, BFLAGS, args...) \ |
| if ((BFLAGS) == 0 || ((BFLAGS) & bflags_mask)) \ |
| s390_builtin_fn_types[INDEX] = \ |
| build_function_type_list (args, NULL_TREE); |
| #undef DEF_OV_TYPE |
| #define DEF_OV_TYPE(...) |
| #include "s390-builtin-types.def" |
| |
| #undef B_DEF |
| #define B_DEF(NAME, PATTERN, ATTRS, BFLAGS, OPFLAGS, FNTYPE) \ |
| if (((BFLAGS) & ~bflags_mask) == 0) \ |
| s390_builtin_decls[S390_BUILTIN_##NAME] = \ |
| add_builtin_function ("__builtin_" #NAME, \ |
| s390_builtin_fn_types[FNTYPE], \ |
| S390_BUILTIN_##NAME, \ |
| BUILT_IN_MD, \ |
| NULL, \ |
| ATTRS); |
| #undef OB_DEF |
| #define OB_DEF(NAME, FIRST_VAR_NAME, LAST_VAR_NAME, BFLAGS, FNTYPE) \ |
| if (((BFLAGS) & ~bflags_mask) == 0) \ |
| s390_builtin_decls[S390_OVERLOADED_BUILTIN_##NAME + S390_BUILTIN_MAX] = \ |
| add_builtin_function ("__builtin_" #NAME, \ |
| s390_builtin_fn_types[FNTYPE], \ |
| S390_OVERLOADED_BUILTIN_##NAME + S390_BUILTIN_MAX, \ |
| BUILT_IN_MD, \ |
| NULL, \ |
| 0); |
| #undef OB_DEF_VAR |
| #define OB_DEF_VAR(...) |
| #include "s390-builtins.def" |
| |
| } |
| |
| /* Return true if ARG is appropriate as argument number ARGNUM of |
| builtin DECL. The operand flags from s390-builtins.def have to |
| passed as OP_FLAGS. */ |
| bool |
| s390_const_operand_ok (tree arg, int argnum, int op_flags, tree decl) |
| { |
| if (O_UIMM_P (op_flags)) |
| { |
| int bitwidths[] = { 1, 2, 3, 4, 5, 8, 12, 16, 32 }; |
| int bitwidth = bitwidths[op_flags - O_U1]; |
| |
| if (!tree_fits_uhwi_p (arg) |
| || tree_to_uhwi (arg) > ((unsigned HOST_WIDE_INT)1 << bitwidth) - 1) |
| { |
| error("constant argument %d for builtin %qF is out of range (0.." |
| HOST_WIDE_INT_PRINT_UNSIGNED ")", |
| argnum, decl, |
| ((unsigned HOST_WIDE_INT)1 << bitwidth) - 1); |
| return false; |
| } |
| } |
| |
| if (O_SIMM_P (op_flags)) |
| { |
| int bitwidths[] = { 2, 3, 4, 5, 8, 12, 16, 32 }; |
| int bitwidth = bitwidths[op_flags - O_S2]; |
| |
| if (!tree_fits_shwi_p (arg) |
| || tree_to_shwi (arg) < -((HOST_WIDE_INT)1 << (bitwidth - 1)) |
| || tree_to_shwi (arg) > (((HOST_WIDE_INT)1 << (bitwidth - 1)) - 1)) |
| { |
| error("constant argument %d for builtin %qF is out of range (" |
| HOST_WIDE_INT_PRINT_DEC ".." |
| HOST_WIDE_INT_PRINT_DEC ")", |
| argnum, decl, |
| -((HOST_WIDE_INT)1 << (bitwidth - 1)), |
| ((HOST_WIDE_INT)1 << (bitwidth - 1)) - 1); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* Expand an expression EXP that calls a built-in function, |
| with result going to TARGET if that's convenient |
| (and in mode MODE if that's convenient). |
| SUBTARGET may be used as the target for computing one of EXP's operands. |
| IGNORE is nonzero if the value is to be ignored. */ |
| |
| static rtx |
| s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, |
| machine_mode mode ATTRIBUTE_UNUSED, |
| int ignore ATTRIBUTE_UNUSED) |
| { |
| #define MAX_ARGS 6 |
| |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| enum insn_code icode; |
| rtx op[MAX_ARGS], pat; |
| int arity; |
| bool nonvoid; |
| tree arg; |
| call_expr_arg_iterator iter; |
| unsigned int all_op_flags = opflags_for_builtin (fcode); |
| machine_mode last_vec_mode = VOIDmode; |
| |
| if (TARGET_DEBUG_ARG) |
| { |
| fprintf (stderr, |
| "s390_expand_builtin, code = %4d, %s\n", |
| (int)fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl))); |
| } |
| |
| if (fcode >= S390_OVERLOADED_BUILTIN_VAR_OFFSET |
| && fcode < S390_ALL_BUILTIN_MAX) |
| { |
| gcc_unreachable (); |
| } |
| else if (fcode < S390_OVERLOADED_BUILTIN_OFFSET) |
| { |
| icode = code_for_builtin[fcode]; |
| /* Set a flag in the machine specific cfun part in order to support |
| saving/restoring of FPRs. */ |
| if (fcode == S390_BUILTIN_tbegin || fcode == S390_BUILTIN_tbegin_retry) |
| cfun->machine->tbegin_p = true; |
| } |
| else if (fcode < S390_OVERLOADED_BUILTIN_VAR_OFFSET) |
| { |
| error ("Unresolved overloaded builtin"); |
| return const0_rtx; |
| } |
| else |
| internal_error ("bad builtin fcode"); |
| |
| if (icode == 0) |
| internal_error ("bad builtin icode"); |
| |
| nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; |
| |
| if (nonvoid) |
| { |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| if (!target |
| || GET_MODE (target) != tmode |
| || !(*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| /* There are builtins (e.g. vec_promote) with no vector |
| arguments but an element selector. So we have to also look |
| at the vector return type when emitting the modulo |
| operation. */ |
| if (VECTOR_MODE_P (insn_data[icode].operand[0].mode)) |
| last_vec_mode = insn_data[icode].operand[0].mode; |
| } |
| |
| arity = 0; |
| FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) |
| { |
| const struct insn_operand_data *insn_op; |
| unsigned int op_flags = all_op_flags & ((1 << O_SHIFT) - 1); |
| |
| all_op_flags = all_op_flags >> O_SHIFT; |
| |
| if (arg == error_mark_node) |
| return NULL_RTX; |
| if (arity >= MAX_ARGS) |
| return NULL_RTX; |
| |
| if (O_IMM_P (op_flags) |
| && TREE_CODE (arg) != INTEGER_CST) |
| { |
| error ("constant value required for builtin %qF argument %d", |
| fndecl, arity + 1); |
| return const0_rtx; |
| } |
| |
| if (!s390_const_operand_ok (arg, arity + 1, op_flags, fndecl)) |
| return const0_rtx; |
| |
| insn_op = &insn_data[icode].operand[arity + nonvoid]; |
| op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, EXPAND_NORMAL); |
| |
| /* expand_expr truncates constants to the target mode only if it |
| is "convenient". However, our checks below rely on this |
| being done. */ |
| if (CONST_INT_P (op[arity]) |
| && SCALAR_INT_MODE_P (insn_op->mode) |
| && GET_MODE (op[arity]) != insn_op->mode) |
| op[arity] = GEN_INT (trunc_int_for_mode (INTVAL (op[arity]), |
| insn_op->mode)); |
| |
| /* Wrap the expanded RTX for pointer types into a MEM expr with |
| the proper mode. This allows us to use e.g. (match_operand |
| "memory_operand"..) in the insn patterns instead of (mem |
| (match_operand "address_operand)). This is helpful for |
| patterns not just accepting MEMs. */ |
| if (POINTER_TYPE_P (TREE_TYPE (arg)) |
| && insn_op->predicate != address_operand) |
| op[arity] = gen_rtx_MEM (insn_op->mode, op[arity]); |
| |
| /* Expand the module operation required on element selectors. */ |
| if (op_flags == O_ELEM) |
| { |
| gcc_assert (last_vec_mode != VOIDmode); |
| op[arity] = simplify_expand_binop (SImode, code_to_optab (AND), |
| op[arity], |
| GEN_INT (GET_MODE_NUNITS (last_vec_mode) - 1), |
| NULL_RTX, 1, OPTAB_DIRECT); |
| } |
| |
| /* Record the vector mode used for an element selector. This assumes: |
| 1. There is no builtin with two different vector modes and an element selector |
| 2. The element selector comes after the vector type it is referring to. |
| This currently the true for all the builtins but FIXME we |
| should better check for that. */ |
| if (VECTOR_MODE_P (insn_op->mode)) |
| last_vec_mode = insn_op->mode; |
| |
| if (insn_op->predicate (op[arity], insn_op->mode)) |
| { |
| arity++; |
| continue; |
| } |
| |
| if (MEM_P (op[arity]) |
| && insn_op->predicate == memory_operand |
| && (GET_MODE (XEXP (op[arity], 0)) == Pmode |
| || GET_MODE (XEXP (op[arity], 0)) == VOIDmode)) |
| { |
| op[arity] = replace_equiv_address (op[arity], |
| copy_to_mode_reg (Pmode, |
| XEXP (op[arity], 0))); |
| } |
| else if (GET_MODE (op[arity]) == insn_op->mode |
| || GET_MODE (op[arity]) == VOIDmode |
| || (insn_op->predicate == address_operand |
| && GET_MODE (op[arity]) == Pmode)) |
| { |
| /* An address_operand usually has VOIDmode in the expander |
| so we cannot use this. */ |
| machine_mode target_mode = |
| (insn_op->predicate == address_operand |
| ? Pmode : insn_op->mode); |
| op[arity] = copy_to_mode_reg (target_mode, op[arity]); |
| } |
| |
| if (!insn_op->predicate (op[arity], insn_op->mode)) |
| { |
| error ("Invalid argument %d for builtin %qF", arity + 1, fndecl); |
| return const0_rtx; |
| } |
| arity++; |
| } |
| |
| if (last_vec_mode != VOIDmode && !TARGET_VX) |
| { |
| error ("Vector type builtin %qF is not supported without -mvx " |
| "(default with -march=z13).", |
| fndecl); |
| return const0_rtx; |
| } |
| |
| switch (arity) |
| { |
| case 0: |
| pat = GEN_FCN (icode) (target); |
| break; |
| case 1: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0]); |
| else |
| pat = GEN_FCN (icode) (op[0]); |
| break; |
| case 2: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0], op[1]); |
| else |
| pat = GEN_FCN (icode) (op[0], op[1]); |
| break; |
| case 3: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0], op[1], op[2]); |
| else |
| pat = GEN_FCN (icode) (op[0], op[1], op[2]); |
| break; |
| case 4: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]); |
| else |
| pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]); |
| break; |
| case 5: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3], op[4]); |
| else |
| pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3], op[4]); |
| break; |
| case 6: |
| if (nonvoid) |
| pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3], op[4], op[5]); |
| else |
| pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3], op[4], op[5]); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (!pat) |
| return NULL_RTX; |
| emit_insn (pat); |
| |
| if (nonvoid) |
| return target; |
| else |
| return const0_rtx; |
| } |
| |
| |
| static const int s390_hotpatch_hw_max = 1000000; |
| static int s390_hotpatch_hw_before_label = 0; |
| static int s390_hotpatch_hw_after_label = 0; |
| |
| /* Check whether the hotpatch attribute is applied to a function and, if it has |
| an argument, the argument is valid. */ |
| |
| static tree |
| s390_handle_hotpatch_attribute (tree *node, tree name, tree args, |
| int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) |
| { |
| tree expr; |
| tree expr2; |
| int err; |
| |
| if (TREE_CODE (*node) != FUNCTION_DECL) |
| { |
| warning (OPT_Wattributes, "%qE attribute only applies to functions", |
| name); |
| *no_add_attrs = true; |
| } |
| if (args != NULL && TREE_CHAIN (args) != NULL) |
| { |
| expr = TREE_VALUE (args); |
| expr2 = TREE_VALUE (TREE_CHAIN (args)); |
| } |
| if (args == NULL || TREE_CHAIN (args) == NULL) |
| err = 1; |
| else if (TREE_CODE (expr) != INTEGER_CST |
| || !INTEGRAL_TYPE_P (TREE_TYPE (expr)) |
| || wi::gtu_p (expr, s390_hotpatch_hw_max)) |
| err = 1; |
| else if (TREE_CODE (expr2) != INTEGER_CST |
| || !INTEGRAL_TYPE_P (TREE_TYPE (expr2)) |
| || wi::gtu_p (expr2, s390_hotpatch_hw_max)) |
| err = 1; |
| else |
| err = 0; |
| if (err) |
| { |
| error ("requested %qE attribute is not a comma separated pair of" |
| " non-negative integer constants or too large (max. %d)", name, |
| s390_hotpatch_hw_max); |
| *no_add_attrs = true; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Expand the s390_vector_bool type attribute. */ |
| |
| static tree |
| s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED, |
| tree args ATTRIBUTE_UNUSED, |
| int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) |
| { |
| tree type = *node, result = NULL_TREE; |
| machine_mode mode; |
| |
| while (POINTER_TYPE_P (type) |
| || TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE |
| || TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| |
| mode = TYPE_MODE (type); |
| switch (mode) |
| { |
| case DImode: case V2DImode: result = s390_builtin_types[BT_BV2DI]; break; |
| case SImode: case V4SImode: result = s390_builtin_types[BT_BV4SI]; break; |
| case HImode: case V8HImode: result = s390_builtin_types[BT_BV8HI]; break; |
| case QImode: case V16QImode: result = s390_builtin_types[BT_BV16QI]; |
| default: break; |
| } |
| |
| *no_add_attrs = true; /* No need to hang on to the attribute. */ |
| |
| if (result) |
| *node = lang_hooks.types.reconstruct_complex_type (*node, result); |
| |
| return NULL_TREE; |
| } |
| |
| static const struct attribute_spec s390_attribute_table[] = { |
| { "hotpatch", 2, 2, true, false, false, s390_handle_hotpatch_attribute, false }, |
| { "s390_vector_bool", 0, 0, false, true, false, s390_handle_vectorbool_attribute, true }, |
| /* End element. */ |
| { NULL, 0, 0, false, false, false, NULL, false } |
| }; |
| |
| /* Return the alignment for LABEL. We default to the -falign-labels |
| value except for the literal pool base label. */ |
| int |
| s390_label_align (rtx label) |
| { |
| rtx_insn *prev_insn = prev_active_insn (label); |
| rtx set, src; |
| |
| if (prev_insn == NULL_RTX) |
| goto old; |
| |
| set = single_set (prev_insn); |
| |
| if (set == NULL_RTX) |
| goto old; |
| |
| src = SET_SRC (set); |
| |
| /* Don't align literal pool base labels. */ |
| if (GET_CODE (src) == UNSPEC |
| && XINT (src, 1) == UNSPEC_MAIN_BASE) |
| return 0; |
| |
| old: |
| return align_labels_log; |
| } |
| |
| static machine_mode |
| s390_libgcc_cmp_return_mode (void) |
| { |
| return TARGET_64BIT ? DImode : SImode; |
| } |
| |
| static machine_mode |
| s390_libgcc_shift_count_mode (void) |
| { |
| return TARGET_64BIT ? DImode : SImode; |
| } |
| |
| static machine_mode |
| s390_unwind_word_mode (void) |
| { |
| return TARGET_64BIT ? DImode : SImode; |
| } |
| |
| /* Return true if the back end supports mode MODE. */ |
| static bool |
| s390_scalar_mode_supported_p (machine_mode mode) |
| { |
| /* In contrast to the default implementation reject TImode constants on 31bit |
| TARGET_ZARCH for ABI compliance. */ |
| if (!TARGET_64BIT && TARGET_ZARCH && mode == TImode) |
| return false; |
| |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| return default_decimal_float_supported_p (); |
| |
| return default_scalar_mode_supported_p (mode); |
| } |
| |
| /* Return true if the back end supports vector mode MODE. */ |
| static bool |
| s390_vector_mode_supported_p (machine_mode mode) |
| { |
| machine_mode inner; |
| |
| if (!VECTOR_MODE_P (mode) |
| || !TARGET_VX |
| || GET_MODE_SIZE (mode) > 16) |
| return false; |
| |
| inner = GET_MODE_INNER (mode); |
| |
| switch (inner) |
| { |
| case QImode: |
| case HImode: |
| case SImode: |
| case DImode: |
| case TImode: |
| case SFmode: |
| case DFmode: |
| case TFmode: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /* Set the has_landing_pad_p flag in struct machine_function to VALUE. */ |
| |
| void |
| s390_set_has_landing_pad_p (bool value) |
| { |
| cfun->machine->has_landing_pad_p = value; |
| } |
| |
| /* If two condition code modes are compatible, return a condition code |
| mode which is compatible with both. Otherwise, return |
| VOIDmode. */ |
| |
| static machine_mode |
| s390_cc_modes_compatible (machine_mode m1, machine_mode m2) |
| { |
| if (m1 == m2) |
| return m1; |
| |
| switch (m1) |
| { |
| case CCZmode: |
| if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode |
| || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode) |
| return m2; |
| return VOIDmode; |
| |
| case CCSmode: |
| case CCUmode: |
| case CCTmode: |
| case CCSRmode: |
| case CCURmode: |
| case CCZ1mode: |
| if (m2 == CCZmode) |
| return m1; |
| |
| return VOIDmode; |
| |
| default: |
| return VOIDmode; |
| } |
| return VOIDmode; |
| } |
| |
| /* Return true if SET either doesn't set the CC register, or else |
| the source and destination have matching CC modes and that |
| CC mode is at least as constrained as REQ_MODE. */ |
| |
| static bool |
| s390_match_ccmode_set (rtx set, machine_mode req_mode) |
| { |
| machine_mode set_mode; |
| |
| gcc_assert (GET_CODE (set) == SET); |
| |
| /* These modes are supposed to be used only in CC consumer |
| patterns. */ |
| gcc_assert (req_mode != CCVIALLmode && req_mode != CCVIANYmode |
| && req_mode != CCVFALLmode && req_mode != CCVFANYmode); |
| |
| if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set)))) |
| return 1; |
| |
| set_mode = GET_MODE (SET_DEST (set)); |
| switch (set_mode) |
| { |
| case CCSmode: |
| case CCSRmode: |
| case CCUmode: |
| case CCURmode: |
| case CCLmode: |
| case CCL1mode: |
| case CCL2mode: |
| case CCL3mode: |
| case CCT1mode: |
| case CCT2mode: |
| case CCT3mode: |
| case CCVEQmode: |
| case CCVIHmode: |
| case CCVIHUmode: |
| case CCVFHmode: |
| case CCVFHEmode: |
| if (req_mode != set_mode) |
| return 0; |
| break; |
| |
| case CCZmode: |
| if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode |
| && req_mode != CCSRmode && req_mode != CCURmode) |
| return 0; |
| break; |
| |
| case CCAPmode: |
| case CCANmode: |
| if (req_mode != CCAmode) |
| return 0; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return (GET_MODE (SET_SRC (set)) == set_mode); |
| } |
| |
| /* Return true if every SET in INSN that sets the CC register |
| has source and destination with matching CC modes and that |
| CC mode is at least as constrained as REQ_MODE. |
| If REQ_MODE is VOIDmode, always return false. */ |
| |
| bool |
| s390_match_ccmode (rtx_insn *insn, machine_mode req_mode) |
| { |
| int i; |
| |
| /* s390_tm_ccmode returns VOIDmode to indicate failure. */ |
| if (req_mode == VOIDmode) |
| return false; |
| |
| if (GET_CODE (PATTERN (insn)) == SET) |
| return s390_match_ccmode_set (PATTERN (insn), req_mode); |
| |
| if (GET_CODE (PATTERN (insn)) == PARALLEL) |
| for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) |
| { |
| rtx set = XVECEXP (PATTERN (insn), 0, i); |
| if (GET_CODE (set) == SET) |
| if (!s390_match_ccmode_set (set, req_mode)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* If a test-under-mask instruction can be used to implement |
| (compare (and ... OP1) OP2), return the CC mode required |
| to do that. Otherwise, return VOIDmode. |
| MIXED is true if the instruction can distinguish between |
| CC1 and CC2 for mixed selected bits (TMxx), it is false |
| if the instruction cannot (TM). */ |
| |
| machine_mode |
| s390_tm_ccmode (rtx op1, rtx op2, bool mixed) |
| { |
| int bit0, bit1; |
| |
| /* ??? Fixme: should work on CONST_DOUBLE as well. */ |
| if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT) |
| return VOIDmode; |
| |
| /* Selected bits all zero: CC0. |
| e.g.: int a; if ((a & (16 + 128)) == 0) */ |
| if (INTVAL (op2) == 0) |
| return CCTmode; |
| |
| /* Selected bits all one: CC3. |
| e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */ |
| if (INTVAL (op2) == INTVAL (op1)) |
| return CCT3mode; |
| |
| /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.: |
| int a; |
| if ((a & (16 + 128)) == 16) -> CCT1 |
| if ((a & (16 + 128)) == 128) -> CCT2 */ |
| if (mixed) |
| { |
| bit1 = exact_log2 (INTVAL (op2)); |
| bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2)); |
| if (bit0 != -1 && bit1 != -1) |
| return bit0 > bit1 ? CCT1mode : CCT2mode; |
| } |
| |
| return VOIDmode; |
| } |
| |
| /* Given a comparison code OP (EQ, NE, etc.) and the operands |
| OP0 and OP1 of a COMPARE, return the mode to be used for the |
| comparison. */ |
| |
| machine_mode |
| s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1) |
| { |
| if (TARGET_VX |
| && register_operand (op0, DFmode) |
| && register_operand (op1, DFmode)) |
| { |
| /* LT, LE, UNGT, UNGE require swapping OP0 and OP1. Either |
| s390_emit_compare or s390_canonicalize_comparison will take |
| care of it. */ |
| switch (code) |
| { |
| case EQ: |
| case NE: |
| return CCVEQmode; |
| case GT: |
| case UNLE: |
| return CCVFHmode; |
| case GE: |
| case UNLT: |
| return CCVFHEmode; |
| default: |
| ; |
| } |
| } |
| |
| switch (code) |
| { |
| case EQ: |
| case NE: |
| if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS) |
| && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) |
| return CCAPmode; |
| if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT |
| && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1)))) |
| return CCAPmode; |
| if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS |
| || GET_CODE (op1) == NEG) |
| && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) |
| return CCLmode; |
| |
| if (GET_CODE (op0) == AND) |
| { |
| /* Check whether we can potentially do it via TM. */ |
| machine_mode ccmode; |
| ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1); |
| if (ccmode != VOIDmode) |
| { |
| /* Relax CCTmode to CCZmode to allow fall-back to AND |
| if that turns out to be beneficial. */ |
| return ccmode == CCTmode ? CCZmode : ccmode; |
| } |
| } |
| |
| if (register_operand (op0, HImode) |
| && GET_CODE (op1) == CONST_INT |
| && (INTVAL (op1) == -1 || INTVAL (op1) == 65535)) |
| return CCT3mode; |
| if (register_operand (op0, QImode) |
| && GET_CODE (op1) == CONST_INT |
| && (INTVAL (op1) == -1 || INTVAL (op1) == 255)) |
| return CCT3mode; |
| |
| return CCZmode; |
| |
| case LE: |
| case LT: |
| case GE: |
| case GT: |
| /* The only overflow condition of NEG and ABS happens when |
| -INT_MAX is used as parameter, which stays negative. So |
| we have an overflow from a positive value to a negative. |
| Using CCAP mode the resulting cc can be used for comparisons. */ |
| if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS) |
| && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) |
| return CCAPmode; |
| |
| /* If constants are involved in an add instruction it is possible to use |
| the resulting cc for comparisons with zero. Knowing the sign of the |
| constant the overflow behavior gets predictable. e.g.: |
| int a, b; if ((b = a + c) > 0) |
| with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP */ |
| if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT |
| && (CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))) |
| || (CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'O', "Os") |
| /* Avoid INT32_MIN on 32 bit. */ |
| && (!TARGET_ZARCH || INTVAL (XEXP (op0, 1)) != -0x7fffffff - 1)))) |
| { |
| if (INTVAL (XEXP((op0), 1)) < 0) |
| return CCANmode; |
| else |
| return CCAPmode; |
| } |
| /* Fall through. */ |
| case UNORDERED: |
| case ORDERED: |
| case UNEQ: |
| case UNLE: |
| case UNLT: |
| case UNGE: |
| case UNGT: |
| case LTGT: |
| if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) |
| && GET_CODE (op1) != CONST_INT) |
| return CCSRmode; |
| return CCSmode; |
| |
| case LTU: |
| case GEU: |
| if (GET_CODE (op0) == PLUS |
| && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) |
| return CCL1mode; |
| |
| if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) |
| && GET_CODE (op1) != CONST_INT) |
| return CCURmode; |
| return CCUmode; |
| |
| case LEU: |
| case GTU: |
| if (GET_CODE (op0) == MINUS |
| && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) |
| return CCL2mode; |
| |
| if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) |
| && GET_CODE (op1) != CONST_INT) |
| return CCURmode; |
| return CCUmode; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Replace the comparison OP0 CODE OP1 by a semantically equivalent one |
| that we can implement more efficiently. */ |
| |
| static void |
| s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1, |
| bool op0_preserve_value) |
| { |
| if (op0_preserve_value) |
| return; |
| |
| /* Convert ZERO_EXTRACT back to AND to enable TM patterns. */ |
| if ((*code == EQ || *code == NE) |
| && *op1 == const0_rtx |
| && GET_CODE (*op0) == ZERO_EXTRACT |
| && GET_CODE (XEXP (*op0, 1)) == CONST_INT |
| && GET_CODE (XEXP (*op0, 2)) == CONST_INT |
| && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0)))) |
| { |
| rtx inner = XEXP (*op0, 0); |
| HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner)); |
| HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1)); |
| HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2)); |
| |
| if (len > 0 && len < modesize |
| && pos >= 0 && pos + len <= modesize |
| && modesize <= HOST_BITS_PER_WIDE_INT) |
| { |
| unsigned HOST_WIDE_INT block; |
| block = ((unsigned HOST_WIDE_INT) 1 << len) - 1; |
| block <<= modesize - pos - len; |
| |
| *op0 = gen_rtx_AND (GET_MODE (inner), inner, |
| gen_int_mode (block, GET_MODE (inner))); |
| } |
| } |
| |
| /* Narrow AND of memory against immediate to enable TM. */ |
| if ((*code == EQ || *code == NE) |
| && *op1 == const0_rtx |
| && GET_CODE (*op0) == AND |
| && GET_CODE (XEXP (*op0, 1)) == CONST_INT |
| && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0)))) |
| { |
| rtx inner = XEXP (*op0, 0); |
| rtx mask = XEXP (*op0, 1); |
| |
| /* Ignore paradoxical SUBREGs if all extra bits are masked out. */ |
| if (GET_CODE (inner) == SUBREG |
| && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner))) |
| && (GET_MODE_SIZE (GET_MODE (inner)) |
| >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner)))) |
| && ((INTVAL (mask) |
| & GET_MODE_MASK (GET_MODE (inner)) |
| & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner)))) |
| == 0)) |
| inner = SUBREG_REG (inner); |
| |
| /* Do not change volatile MEMs. */ |
| if (MEM_P (inner) && !MEM_VOLATILE_P (inner)) |
| { |
| int part = s390_single_part (XEXP (*op0, 1), |
| GET_MODE (inner), QImode, 0); |
| if (part >= 0) |
| { |
| mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode); |
| inner = adjust_address_nv (inner, QImode, part); |
| *op0 = gen_rtx_AND (QImode, inner, mask); |
| } |
| } |
| } |
| |
| /* Narrow comparisons against 0xffff to HImode if possible. */ |
| if ((*code == EQ || *code == NE) |
| && GET_CODE (*op1) == CONST_INT |
| && INTVAL (*op1) == 0xffff |
| && SCALAR_INT_MODE_P (GET_MODE (*op0)) |
| && (nonzero_bits (*op0, GET_MODE (*op0)) |
| & ~(unsigned HOST_WIDE_INT) 0xffff) == 0) |
| { |
| *op0 = gen_lowpart (HImode, *op0); |
| *op1 = constm1_rtx; |
| } |
| |
| /* Remove redundant UNSPEC_STRCMPCC_TO_INT conversions if possible. */ |
| if (GET_CODE (*op0) == UNSPEC |
| && XINT (*op0, 1) == UNSPEC_STRCMPCC_TO_INT |
| && XVECLEN (*op0, 0) == 1 |
| && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode |
| && 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; |
| case LT: new_code = GTU; break; |
| case GT: new_code = LTU; break; |
| case LE: new_code = GEU; break; |
| case GE: new_code = LEU; break; |
| default: break; |
| } |
| |
| if (new_code != UNKNOWN) |
| { |
| *op0 = XVECEXP (*op0, 0, 0); |
| *code = new_code; |
| } |
| } |
| |
| /* Remove redundant UNSPEC_CC_TO_INT conversions if possible. */ |
| if (GET_CODE (*op0) == UNSPEC |
| && XINT (*op0, 1) == UNSPEC_CC_TO_INT |
| && XVECLEN (*op0, 0) == 1 |
| && GET_CODE (XVECEXP (*op0, 0, 0)) == REG |
| && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM |
| && CONST_INT_P (*op1)) |
| { |
| enum rtx_code new_code = UNKNOWN; |
| switch (GET_MODE (XVECEXP (*op0, 0, 0))) |
| { |
| case CCZmode: |
| case CCRAWmode: |
| switch (*code) |
| { |
| case EQ: new_code = EQ; break; |
| case NE: new_code = NE; break; |
| default: break; |
| } |
| break; |
| default: break; |
| } |
| |
| if (new_code != UNKNOWN) |
| { |
| /* For CCRAWmode put the required cc mask into the second |
| operand. */ |
| if (GET_MODE (XVECEXP (*op0, 0, 0)) == CCRAWmode |
| && INTVAL (*op1) >= 0 && INTVAL (*op1) <= 3) |
| *op1 = gen_rtx_CONST_INT (VOIDmode, 1 << (3 - INTVAL (*op1))); |
| *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) |
| && GET_MODE (*op0) == SImode |
| && GET_MODE (XEXP (*op0, 0)) == CCZ1mode |
| && REG_P (XEXP (*op0, 0)) |
| && XEXP (*op0, 1) == const0_rtx |
| && *op1 == const0_rtx) |
| { |
| if ((*code == EQ && GET_CODE (*op0) == NE) |
| || (*code == NE && GET_CODE (*op0) == EQ)) |
| *code = EQ; |
| else |
| *code = NE; |
| *op0 = XEXP (*op0, 0); |
| } |
| |
| /* Prefer register over memory as first operand. */ |
| if (MEM_P (*op0) && REG_P (*op1)) |
| { |
| rtx tem = *op0; *op0 = *op1; *op1 = tem; |
| *code = (int)swap_condition ((enum rtx_code)*code); |
| } |
| |
| /* Using the scalar variants of vector instructions for 64 bit FP |
| comparisons might require swapping the operands. */ |
| if (TARGET_VX |
| && register_operand (*op0, DFmode) |
| && register_operand (*op1, DFmode) |
| && (*code == LT || *code == LE || *code == UNGT || *code == UNGE)) |
| { |
| rtx tmp; |
| |
| switch (*code) |
| { |
| case LT: *code = GT; break; |
| case LE: *code = GE; break; |
| case UNGT: *code = UNLE; break; |
| case UNGE: *code = UNLT; break; |
| default: ; |
| } |
| tmp = *op0; *op0 = *op1; *op1 = tmp; |
| } |
| } |
| |
| /* Helper function for s390_emit_compare. If possible emit a 64 bit |
| FP compare using the single element variant of vector instructions. |
| Replace CODE with the comparison code to be used in the CC reg |
| compare and return the condition code register RTX in CC. */ |
| |
| static bool |
| s390_expand_vec_compare_scalar (enum rtx_code *code, rtx cmp1, rtx cmp2, |
| rtx *cc) |
| { |
| machine_mode cmp_mode; |
| bool swap_p = false; |
| |
| switch (*code) |
| { |
| case EQ: cmp_mode = CCVEQmode; break; |
| case NE: cmp_mode = CCVEQmode; break; |
| case GT: cmp_mode = CCVFHmode; break; |
| case GE: cmp_mode = CCVFHEmode; break; |
| case UNLE: cmp_mode = CCVFHmode; break; |
| case UNLT: cmp_mode = CCVFHEmode; break; |
| case LT: cmp_mode = CCVFHmode; *code = GT; swap_p = true; break; |
| case LE: cmp_mode = CCVFHEmode; *code = GE; swap_p = true; break; |
| case UNGE: cmp_mode = CCVFHmode; *code = UNLE; swap_p = true; break; |
| case UNGT: cmp_mode = CCVFHEmode; *code = UNLT; swap_p = true; break; |
| default: return false; |
| } |
| |
| if (swap_p) |
| { |
| rtx tmp = cmp2; |
| cmp2 = cmp1; |
| cmp1 = tmp; |
| } |
| |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (2, |
| gen_rtx_SET (VOIDmode, |
| gen_rtx_REG (cmp_mode, CC_REGNUM), |
| gen_rtx_COMPARE (cmp_mode, cmp1, |
| cmp2)), |
| gen_rtx_CLOBBER (VOIDmode, |
| gen_rtx_SCRATCH (V2DImode))))); |
| |
| /* This is the cc reg how it will be used in the cc mode consumer. |
| It either needs to be CCVFALL or CCVFANY. However, CC1 will |
| never be set by the scalar variants. So it actually doesn't |
| matter which one we choose here. */ |
| *cc = gen_rtx_REG (CCVFALLmode, CC_REGNUM); |
| return true; |
| } |
| |
| |
| /* Emit a compare instruction suitable to implement the comparison |
| OP0 CODE OP1. Return the correct condition RTL to be placed in |
| the IF_THEN_ELSE of the conditional branch testing the result. */ |
| |
| rtx |
| s390_emit_compare (enum rtx_code code, rtx op0, rtx op1) |
| { |
| machine_mode mode = s390_select_ccmode (code, op0, op1); |
| rtx cc; |
| |
| if (TARGET_VX |
| && register_operand (op0, DFmode) |
| && register_operand (op1, DFmode) |
| && s390_expand_vec_compare_scalar (&code, op0, op1, &cc)) |
| { |
| /* Work has been done by s390_expand_vec_compare_scalar already. */ |
| } |
| else if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) |
| { |
| /* Do not output a redundant compare instruction if a |
| compare_and_swap pattern already computed the result and the |
| machine modes are compatible. */ |
| gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), mode) |
| == GET_MODE (op0)); |
| cc = op0; |
| } |
| else |
| { |
| cc = gen_rtx_REG (mode, CC_REGNUM); |
| emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1))); |
| } |
| |
| return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); |
| } |
| |
| /* Emit a SImode compare and swap instruction setting MEM to NEW_RTX 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) |
| { |
| emit_insn (gen_atomic_compare_and_swapsi_internal (old, mem, cmp, new_rtx)); |
| return s390_emit_compare (code, gen_rtx_REG (CCZ1mode, CC_REGNUM), |
| const0_rtx); |
| } |
| |
| /* Emit a jump instruction to TARGET and return it. If COND is |
| NULL_RTX, emit an unconditional jump, else a conditional jump under |
| condition COND. */ |
| |
| rtx_insn * |
| s390_emit_jump (rtx target, rtx cond) |
| { |
| rtx insn; |
| |
| target = gen_rtx_LABEL_REF (VOIDmode, target); |
| if (cond) |
| target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx); |
| |
| insn = gen_rtx_SET (VOIDmode, pc_rtx, target); |
| return emit_jump_insn (insn); |
| } |
| |
| /* Return branch condition mask to implement a branch |
| specified by CODE. Return -1 for invalid comparisons. */ |
| |
| int |
| s390_branch_condition_mask (rtx code) |
| { |
| const int CC0 = 1 << 3; |
| const int CC1 = 1 << 2; |
| const int CC2 = 1 << 1; |
| const int CC3 = 1 << 0; |
| |
| gcc_assert (GET_CODE (XEXP (code, 0)) == REG); |
| gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM); |
| gcc_assert (XEXP (code, 1) == const0_rtx |
| || (GET_MODE (XEXP (code, 0)) == CCRAWmode |
| && CONST_INT_P (XEXP (code, 1)))); |
| |
| |
| switch (GET_MODE (XEXP (code, 0))) |
| { |
| case CCZmode: |
| case CCZ1mode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0; |
| case NE: return CC1 | CC2 | CC3; |
| default: return -1; |
| } |
| break; |
| |
| case CCT1mode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC1; |
| case NE: return CC0 | CC2 | CC3; |
| default: return -1; |
| } |
| break; |
| |
| case CCT2mode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC2; |
| case NE: return CC0 | CC1 | CC3; |
| default: return -1; |
| } |
| break; |
| |
| case CCT3mode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC3; |
| case NE: return CC0 | CC1 | CC2; |
| default: return -1; |
| } |
| break; |
| |
| case CCLmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0 | CC2; |
| case NE: return CC1 | CC3; |
| default: return -1; |
| } |
| break; |
| |
| case CCL1mode: |
| switch (GET_CODE (code)) |
| { |
| case LTU: return CC2 | CC3; /* carry */ |
| case GEU: return CC0 | CC1; /* no carry */ |
| default: return -1; |
| } |
| break; |
| |
| case CCL2mode: |
| switch (GET_CODE (code)) |
| { |
| case GTU: return CC0 | CC1; /* borrow */ |
| case LEU: return CC2 | CC3; /* no borrow */ |
| default: return -1; |
| } |
| break; |
| |
| case CCL3mode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0 | CC2; |
| case NE: return CC1 | CC3; |
| case LTU: return CC1; |
| case GTU: return CC3; |
| case LEU: return CC1 | CC2; |
| case GEU: return CC2 | CC3; |
| default: return -1; |
| } |
| |
| case CCUmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0; |
| case NE: return CC1 | CC2 | CC3; |
| case LTU: return CC1; |
| case GTU: return CC2; |
| case LEU: return CC0 | CC1; |
| case GEU: return CC0 | CC2; |
| default: return -1; |
| } |
| break; |
| |
| case CCURmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0; |
| case NE: return CC2 | CC1 | CC3; |
| case LTU: return CC2; |
| case GTU: return CC1; |
| case LEU: return CC0 | CC2; |
| case GEU: return CC0 | CC1; |
| default: return -1; |
| } |
| 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: return -1; |
| } |
| 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: return -1; |
| } |
| break; |
| |
| case CCSmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0; |
| case NE: return CC1 | CC2 | CC3; |
| case LT: return CC1; |
| case GT: return CC2; |
| case LE: return CC0 | CC1; |
| case GE: return CC0 | CC2; |
| case UNORDERED: return CC3; |
| case ORDERED: return CC0 | CC1 | CC2; |
| case UNEQ: return CC0 | CC3; |
| case UNLT: return CC1 | CC3; |
| case UNGT: return CC2 | CC3; |
| case UNLE: return CC0 | CC1 | CC3; |
| case UNGE: return CC0 | CC2 | CC3; |
| case LTGT: return CC1 | CC2; |
| default: return -1; |
| } |
| break; |
| |
| case CCSRmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: return CC0; |
| case NE: return CC2 | CC1 | CC3; |
| case LT: return CC2; |
| case GT: return CC1; |
| case LE: return CC0 | CC2; |
| case GE: return CC0 | CC1; |
| case UNORDERED: return CC3; |
| case ORDERED: return CC0 | CC2 | CC1; |
| case UNEQ: return CC0 | CC3; |
| case UNLT: return CC2 | CC3; |
| case UNGT: return CC1 | CC3; |
| case UNLE: return CC0 | CC2 | CC3; |
| case UNGE: return CC0 | CC1 | CC3; |
| case LTGT: return CC2 | CC1; |
| default: return -1; |
| } |
| break; |
| |
| /* Vector comparison modes. */ |
| /* CC2 will never be set. It however is part of the negated |
| masks. */ |
| case CCVIALLmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| case GTU: |
| case GT: |
| case GE: return CC0; |
| /* The inverted modes are in fact *any* modes. */ |
| case NE: |
| case LEU: |
| case LE: |
| case LT: return CC3 | CC1 | CC2; |
| default: return -1; |
| } |
| |
| case CCVIANYmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| case GTU: |
| case GT: |
| case GE: return CC0 | CC1; |
| /* The inverted modes are in fact *all* modes. */ |
| case NE: |
| case LEU: |
| case LE: |
| case LT: return CC3 | CC2; |
| default: return -1; |
| } |
| case CCVFALLmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| case GT: |
| case GE: return CC0; |
| /* The inverted modes are in fact *any* modes. */ |
| case NE: |
| case UNLE: |
| case UNLT: return CC3 | CC1 | CC2; |
| default: return -1; |
| } |
| |
| case CCVFANYmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| case GT: |
| case GE: return CC0 | CC1; |
| /* The inverted modes are in fact *all* modes. */ |
| case NE: |
| case UNLE: |
| case UNLT: return CC3 | CC2; |
| default: return -1; |
| } |
| |
| case CCRAWmode: |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| return INTVAL (XEXP (code, 1)); |
| case NE: |
| return (INTVAL (XEXP (code, 1))) ^ 0xf; |
| default: |
| gcc_unreachable (); |
| } |
| |
| default: |
| return -1; |
| } |
| } |
| |
| |
| /* Return branch condition mask to implement a compare and branch |
| specified by CODE. Return -1 for invalid comparisons. */ |
| |
| int |
| s390_compare_and_branch_condition_mask (rtx code) |
| { |
| const int CC0 = 1 << 3; |
| const int CC1 = 1 << 2; |
| const int CC2 = 1 << 1; |
| |
| switch (GET_CODE (code)) |
| { |
| case EQ: |
| return CC0; |
| case NE: |
| return CC1 | CC2; |
| case LT: |
| case LTU: |
| return CC1; |
| case GT: |
| case GTU: |
| return CC2; |
| case LE: |
| case LEU: |
| return CC0 | CC1; |
| case GE: |
| case GEU: |
| return CC0 | CC2; |
| default: |
| gcc_unreachable (); |
| } |
| return -1; |
| } |
| |
| /* If INV is false, return assembler mnemonic string to implement |
| a branch specified by CODE. If INV is true, return mnemonic |
| for the corresponding inverted branch. */ |
| |
| static const char * |
| s390_branch_condition_mnemonic (rtx code, int inv) |
| { |
| int mask; |
| |
| static const char *const mnemonic[16] = |
| { |
| NULL, "o", "h", "nle", |
| "l", "nhe", "lh", "ne", |
| "e", "nlh", "he", "nl", |
| "le", "nh", "no", NULL |
| }; |
| |
| if (GET_CODE (XEXP (code, 0)) == REG |
| && REGNO (XEXP (code, 0)) == CC_REGNUM |
| && (XEXP (code, 1) == const0_rtx |
| || (GET_MODE (XEXP (code, 0)) == CCRAWmode |
| && CONST_INT_P (XEXP (code, 1))))) |
| mask = s390_branch_condition_mask (code); |
| else |
| mask = s390_compare_and_branch_condition_mask (code); |
| |
| gcc_assert (mask >= 0); |
| |
| if (inv) |
| mask ^= 15; |
| |
| gcc_assert (mask >= 1 && mask <= 14); |
| |
| return mnemonic[mask]; |
| } |
| |
| /* Return the part of op which has a value different from def. |
| The size of the part is determined by mode. |
| Use this function only if you already know that op really |
| contains such a part. */ |
| |
| unsigned HOST_WIDE_INT |
| s390_extract_part (rtx op, machine_mode mode, int def) |
| { |
| unsigned HOST_WIDE_INT value = 0; |
| int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode); |
| int part_bits = GET_MODE_BITSIZE (mode); |
| unsigned HOST_WIDE_INT part_mask |
| = ((unsigned HOST_WIDE_INT)1 << part_bits) - 1; |
| int i; |
| |
| for (i = 0; i < max_parts; i++) |
| { |
| if (i == 0) |
| value = (unsigned HOST_WIDE_INT) INTVAL (op); |
| else |
| value >>= part_bits; |
| |
| if ((value & part_mask) != (def & part_mask)) |
| return value & part_mask; |
| } |
| |
| gcc_unreachable (); |
| } |
| |
| /* If OP is an integer constant of mode MODE with exactly one |
| part of mode PART_MODE unequal to DEF, return the number of that |
| part. Otherwise, return -1. */ |
| |
| int |
| s390_single_part (rtx op, |
| machine_mode mode, |
| machine_mode part_mode, |
| int def) |
| { |
| unsigned HOST_WIDE_INT value = 0; |
| int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode); |
| unsigned HOST_WIDE_INT part_mask |
| = ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1; |
| int i, part = -1; |
| |
| if (GET_CODE (op) != CONST_INT) |
| return -1; |
| |
| for (i = 0; i < n_parts; i++) |
| { |
| if (i == 0) |
| value = (unsigned HOST_WIDE_INT) INTVAL (op); |
| else |
| value >>= GET_MODE_BITSIZE (part_mode); |
| |
| if ((value & part_mask) != (def & part_mask)) |
| { |
| if (part != -1) |
| return -1; |
| else |
| part = i; |
| } |
| } |
| return part == -1 ? -1 : n_parts - 1 - part; |
| } |
| |
| /* Return true if IN contains a contiguous bitfield in the lower SIZE |
| bits and no other bits are set in IN. POS and LENGTH can be used |
| to obtain the start position and the length of the bitfield. |
| |
| POS gives the position of the first bit of the bitfield counting |
| from the lowest order bit starting with zero. In order to use this |
| value for S/390 instructions this has to be converted to "bits big |
| endian" style. */ |
| |
| bool |
| s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size, |
| int *pos, int *length) |
| { |
| int tmp_pos = 0; |
| int tmp_length = 0; |
| int i; |
| unsigned HOST_WIDE_INT mask = 1ULL; |
| bool contiguous = false; |
| |
| for (i = 0; i < size; mask <<= 1, i++) |
| { |
| if (contiguous) |
| { |
| if (mask & in) |
| tmp_length++; |
| else |
| break; |
| } |
| else |
| { |
| if (mask & in) |
| { |
| contiguous = true; |
| tmp_length++; |
| } |
| else |
| tmp_pos++; |
| } |
| } |
| |
| if (!tmp_length) |
| return false; |
| |
| /* Calculate a mask for all bits beyond the contiguous bits. */ |
| mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1)); |
| |
| if ((unsigned)size < sizeof (HOST_WIDE_INT) * BITS_PER_UNIT) |
| mask &= (HOST_WIDE_INT_1U << size) - 1; |
| |
| if (mask & in) |
| return false; |
| |
| if (tmp_length + tmp_pos - 1 > size) |
| return false; |
| |
| if (length) |
| *length = tmp_length; |
| |
| if (pos) |
| *pos = tmp_pos; |
| |
| return true; |
| } |
| |
| bool |
| s390_const_vec_duplicate_p (rtx op) |
| { |
| if (!VECTOR_MODE_P (GET_MODE (op)) |
| || GET_CODE (op) != CONST_VECTOR |
| || !CONST_INT_P (XVECEXP (op, 0, 0))) |
| return false; |
| |
| if (GET_MODE_NUNITS (GET_MODE (op)) > 1) |
| { |
| int i; |
| |
| for (i = 1; i < GET_MODE_NUNITS (GET_MODE (op)); ++i) |
| if (!rtx_equal_p (XVECEXP (op, 0, i), XVECEXP (op, 0, 0))) |
| return false; |
| } |
| return true; |
| } |
| /* Return true if OP contains the same contiguous bitfield in *all* |
| its elements. START and END can be used to obtain the start and |
| end position of the bitfield. |
| |
| START/STOP give the position of the first/last bit of the bitfield |
| counting from the lowest order bit starting with zero. In order to |
| use these values for S/390 instructions this has to be converted to |
| "bits big endian" style. */ |
| |
| bool |
| s390_contiguous_bitmask_vector_p (rtx op, int *start, int *end) |
| { |
| unsigned HOST_WIDE_INT mask; |
| int length, size; |
| |
| if (!s390_const_vec_duplicate_p (op)) |
| return false; |
| |
| size = GET_MODE_UNIT_BITSIZE (GET_MODE (op)); |
| mask = UINTVAL (XVECEXP (op, 0, 0)); |
| if (s390_contiguous_bitmask_p (mask, size, start, |
| end != NULL ? &length : NULL)) |
| { |
| if (end != NULL) |
| *end = *start + length - 1; |
| return true; |
| } |
| /* 0xff00000f style immediates can be covered by swapping start and |
| end indices in vgm. */ |
| if (s390_contiguous_bitmask_p (~mask, size, start, |
| end != NULL ? &length : NULL)) |
| { |
| if (end != NULL) |
| *end = *start - 1; |
| if (start != NULL) |
| *start = *start + length; |
| return true; |
| } |
| return false; |
| } |
| |
| /* Return true if C consists only of byte chunks being either 0 or |
| 0xff. If MASK is !=NULL a byte mask is generated which is |
| appropriate for the vector generate byte mask instruction. */ |
| |
| bool |
| s390_bytemask_vector_p (rtx op, unsigned *mask) |
| { |
| int i; |
| unsigned tmp_mask = 0; |
| int nunit, unit_size; |
| |
| if (!VECTOR_MODE_P (GET_MODE (op)) |
| || GET_CODE (op) != CONST_VECTOR |
| || !CONST_INT_P (XVECEXP (op, 0, 0))) |
| return false; |
| |
| nunit = GET_MODE_NUNITS (GET_MODE (op)); |
| unit_size = GET_MODE_UNIT_SIZE (GET_MODE (op)); |
| |
| for (i = 0; i < nunit; i++) |
| { |
| unsigned HOST_WIDE_INT c; |
| int j; |
| |
| if (!CONST_INT_P (XVECEXP (op, 0, i))) |
| return false; |
| |
| c = UINTVAL (XVECEXP (op, 0, i)); |
| for (j = 0; j < unit_size; j++) |
| { |
| if ((c & 0xff) != 0 && (c & 0xff) != 0xff) |
| return false; |
| tmp_mask |= (c & 1) << ((nunit - 1 - i) * unit_size + j); |
| c = c >> BITS_PER_UNIT; |
| } |
| } |
| |
| if (mask != NULL) |
| *mask = tmp_mask; |
| |
| return true; |
| } |
| |
| /* Check whether a rotate of ROTL followed by an AND of CONTIG is |
| equivalent to a shift followed by the AND. In particular, CONTIG |
| should not overlap the (rotated) bit 0/bit 63 gap. Negative values |
| for ROTL indicate a rotate to the right. */ |
| |
| bool |
| s390_extzv_shift_ok (int bitsize, int rotl, unsigned HOST_WIDE_INT contig) |
| { |
| int pos, len; |
| bool ok; |
| |
| ok = s390_contiguous_bitmask_p (contig, bitsize, &pos, &len); |
| gcc_assert (ok); |
| |
| return ((rotl >= 0 && rotl <= pos) |
| || (rotl < 0 && -rotl <= bitsize - len - pos)); |
| } |
| |
| /* Check whether we can (and want to) split a double-word |
| move in mode MODE from SRC to DST into two single-word |
| moves, moving the subword FIRST_SUBWORD first. */ |
| |
| bool |
| s390_split_ok_p (rtx dst, rtx src, machine_mode mode, int first_subword) |
| { |
| /* Floating point and vector registers cannot be split. */ |
| if (FP_REG_P (src) || FP_REG_P (dst) || VECTOR_REG_P (src) || VECTOR_REG_P (dst)) |
| return false; |
| |
| /* We don't need to split if operands are directly accessible. */ |
| if (s_operand (src, mode) || s_operand (dst, mode)) |
| return false; |
| |
| /* Non-offsettable memory references cannot be split. */ |
| if ((GET_CODE (src) == MEM && !offsettable_memref_p (src)) |
| || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst))) |
| return false; |
| |
| /* Moving the first subword must not clobber a register |
| needed to move the second subword. */ |
| if (register_operand (dst, mode)) |
| { |
| rtx subreg = operand_subword (dst, first_subword, 0, mode); |
| if (reg_overlap_mentioned_p (subreg, src)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Return true if it can be proven that [MEM1, MEM1 + SIZE] |
| and [MEM2, MEM2 + SIZE] do overlap and false |
| otherwise. */ |
| |
| bool |
| s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size) |
| { |
| rtx addr1, addr2, addr_delta; |
| HOST_WIDE_INT delta; |
| |
| if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM) |
| return true; |
| |
| if (size == 0) |
| return false; |
| |
| addr1 = XEXP (mem1, 0); |
| addr2 = XEXP (mem2, 0); |
| |
| addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1); |
| |
| /* This overlapping check is used by peepholes merging memory block operations. |
| Overlapping operations would otherwise be recognized by the S/390 hardware |
| and would fall back to a slower implementation. Allowing overlapping |
| operations would lead to slow code but not to wrong code. Therefore we are |
| somewhat optimistic if we cannot prove that the memory blocks are |
| overlapping. |
| That's why we return false here although this may accept operations on |
| overlapping memory areas. */ |
| if (!addr_delta || GET_CODE (addr_delta) != CONST_INT) |
| return false; |
| |
| delta = INTVAL (addr_delta); |
| |
| if (delta == 0 |
| || (delta > 0 && delta < size) |
| || (delta < 0 && -delta < size)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Check whether the address of memory reference MEM2 equals exactly |
| the address of memory reference MEM1 plus DELTA. Return true if |
| we can prove this to be the case, false otherwise. */ |
| |
| bool |
| s390_offset_p (rtx mem1, rtx mem2, rtx delta) |
| { |
| rtx addr1, addr2, addr_delta; |
| |
| if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM) |
| return false; |
| |
| addr1 = XEXP (mem1, 0); |
| addr2 = XEXP (mem2, 0); |
| |
| addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1); |
| if (!addr_delta || !rtx_equal_p (addr_delta, delta)) |
| return false; |
| |
| return true; |
| } |
| |
| /* Expand logical operator CODE in mode MODE with operands OPERANDS. */ |
| |
| void |
| s390_expand_logical_operator (enum rtx_code code, machine_mode mode, |
| rtx *operands) |
| { |
| machine_mode wmode = mode; |
| rtx dst = operands[0]; |
| rtx src1 = operands[1]; |
| rtx src2 = operands[2]; |
| rtx op, clob, tem; |
| |
| /* If we cannot handle the operation directly, use a temp register. */ |
| if (!s390_logical_operator_ok_p (operands)) |
| dst = gen_reg_rtx (mode); |
| |
| /* QImode and HImode patterns make sense only if we have a destination |
| in memory. Otherwise perform the operation in SImode. */ |
| if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM) |
| wmode = SImode; |
| |
| /* Widen operands if required. */ |
| if (mode != wmode) |
| { |
| if (GET_CODE (dst) == SUBREG |
| && (tem = simplify_subreg (wmode, dst, mode, 0)) != 0) |
| dst = tem; |
| else if (REG_P (dst)) |
| dst = gen_rtx_SUBREG (wmode, dst, 0); |
| else |
| dst = gen_reg_rtx (wmode); |
| |
| if (GET_CODE (src1) == SUBREG |
| && (tem = simplify_subreg (wmode, src1, mode, 0)) != 0) |
| src1 = tem; |
| else if (GET_MODE (src1) != VOIDmode) |
| src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0); |
| |
| if (GET_CODE (src2) == SUBREG |
| && (tem = simplify_subreg (wmode, src2, mode, 0)) != 0) |
| src2 = tem; |
| else if (GET_MODE (src2) != VOIDmode) |
| src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0); |
| } |
| |
| /* Emit the instruction. */ |
| op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2)); |
| clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob))); |
| |
| /* Fix up the destination if needed. */ |
| if (dst != operands[0]) |
| emit_move_insn (operands[0], gen_lowpart (mode, dst)); |
| } |
| |
| /* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR). */ |
| |
| bool |
| s390_logical_operator_ok_p (rtx *operands) |
| { |
| /* If the destination operand is in memory, it needs to coincide |
| with one of the source operands. After reload, it has to be |
| the first source operand. */ |
| if (GET_CODE (operands[0]) == MEM) |
| return rtx_equal_p (operands[0], operands[1]) |
| || (!reload_completed && rtx_equal_p (operands[0], operands[2])); |
| |
| return true; |
| } |
| |
| /* Narrow logical operation CODE of memory operand MEMOP with immediate |
| operand IMMOP to switch from SS to SI type instructions. */ |
| |
| void |
| s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop) |
| { |
| int def = code == AND ? -1 : 0; |
| HOST_WIDE_INT mask; |
| int part; |
| |
| gcc_assert (GET_CODE (*memop) == MEM); |
| gcc_assert (!MEM_VOLATILE_P (*memop)); |
| |
| mask = s390_extract_part (*immop, QImode, def); |
| part = s390_single_part (*immop, GET_MODE (*memop), QImode, def); |
| gcc_assert (part >= 0); |
| |
| *memop = adjust_address (*memop, QImode, part); |
| *immop = gen_int_mode (mask, QImode); |
| } |
| |
| |
| /* How to allocate a 'struct machine_function'. */ |
| |
| static struct machine_function * |
| s390_init_machine_status (void) |
| { |
| return ggc_cleared_alloc<machine_function> (); |
| } |
| |
| /* Map for smallest class containing reg regno. */ |
| |
| const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = |
| { GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, /* 0 */ |
| ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, /* 4 */ |
| ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, /* 8 */ |
| ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, /* 12 */ |
| FP_REGS, FP_REGS, FP_REGS, FP_REGS, /* 16 */ |
| FP_REGS, FP_REGS, FP_REGS, FP_REGS, /* 20 */ |
| FP_REGS, FP_REGS, FP_REGS, FP_REGS, /* 24 */ |
| FP_REGS, FP_REGS, FP_REGS, FP_REGS, /* 28 */ |
| ADDR_REGS, CC_REGS, ADDR_REGS, ADDR_REGS, /* 32 */ |
| ACCESS_REGS, ACCESS_REGS, VEC_REGS, VEC_REGS, /* 36 */ |
| VEC_REGS, VEC_REGS, VEC_REGS, VEC_REGS, /* 40 */ |
| VEC_REGS, VEC_REGS, VEC_REGS, VEC_REGS, /* 44 */ |
| VEC_REGS, VEC_REGS, VEC_REGS, VEC_REGS, /* 48 */ |
| VEC_REGS, VEC_REGS /* 52 */ |
| }; |
| |
| /* Return attribute type of insn. */ |
| |
| static enum attr_type |
| s390_safe_attr_type (rtx_insn *insn) |
| { |
| if (recog_memoized (insn) >= 0) |
| return get_attr_type (insn); |
| else |
| return TYPE_NONE; |
| } |
| |
| /* Return true if DISP is a valid short displacement. */ |
| |
| static bool |
| s390_short_displacement (rtx disp) |
| { |
| /* No displacement is OK. */ |
| if (!disp) |
| return true; |
| |
| /* Without the long displacement facility we don't need to |
| distingiush between long and short displacement. */ |
| if (!TARGET_LONG_DISPLACEMENT) |
| return true; |
| |
| /* Integer displacement in range. */ |
| if (GET_CODE (disp) == CONST_INT) |
| return INTVAL (disp) >= 0 && INTVAL (disp) < 4096; |
| |
| /* GOT offset is not OK, the GOT can be large. */ |
| if (GET_CODE (disp) == CONST |
| && GET_CODE (XEXP (disp, 0)) == UNSPEC |
| && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT |
| || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF)) |
| return false; |
| |
| /* All other symbolic constants are literal pool references, |
| which are OK as the literal pool must be small. */ |
| if (GET_CODE (disp) == CONST) |
| return true; |
| |
| return false; |
| } |
| |
| /* Decompose a RTL expression ADDR for a memory address into |
| its components, returned in OUT. |
| |
| Returns false if ADDR is not a valid memory address, true |
| otherwise. If OUT is NULL, don't return the components, |
| but check for validity only. |
| |
| Note: Only addresses in canonical form are recognized. |
| LEGITIMIZE_ADDRESS should convert non-canonical forms to the |
| canonical form so that they will be recognized. */ |
| |
| static int |
| s390_decompose_address (rtx addr, struct s390_address *out) |
| { |
| HOST_WIDE_INT offset = 0; |
| rtx base = NULL_RTX; |
| rtx indx = NULL_RTX; |
| rtx disp = NULL_RTX; |
| rtx orig_disp; |
| 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. */ |
| |
| if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC) |
| base = addr; |
| |
| else if (GET_CODE (addr) == PLUS) |
| { |
| rtx op0 = XEXP (addr, 0); |
| rtx op1 = XEXP (addr, 1); |
| enum rtx_code code0 = GET_CODE (op0); |
| enum rtx_code code1 = GET_CODE (op1); |
| |
| if (code0 == REG || code0 == UNSPEC) |
| { |
| if (code1 == REG || code1 == UNSPEC) |
| { |
| indx = op0; /* index + base */ |
| base = op1; |
| } |
| |
| else |
| { |
| base = op0; /* base + displacement */ |
| disp = op1; |
| } |
| } |
| |
| else if (code0 == PLUS) |
| { |
| indx = XEXP (op0, 0); /* index + base + disp */ |
| base = XEXP (op0, 1); |
| disp = op1; |
| } |
| |
| else |
| { |
| return false; |
| } |
| } |
| |
| else |
| disp = addr; /* displacement */ |
| |
| /* Extract integer part of displacement. */ |
| orig_disp = disp; |
| if (disp) |
| { |
| if (GET_CODE (disp) == CONST_INT) |
| { |
| offset = INTVAL (disp); |
| disp = NULL_RTX; |
| } |
| else if (GET_CODE (disp) == CONST |
| && GET_CODE (XEXP (disp, 0)) == PLUS |
| && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT) |
| { |
| offset = INTVAL (XEXP (XEXP (disp, 0), 1)); |
| disp = XEXP (XEXP (disp, 0), 0); |
| } |
| } |
| |
| /* Strip off CONST here to avoid special case tests later. */ |
| if (disp && GET_CODE (disp) == CONST) |
| disp = XEXP (disp, 0); |
| |
| /* We can convert literal pool addresses to |
| displacements by basing them off the base register. */ |
| if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp)) |
| { |
| /* Either base or index must be free to hold the base register. */ |
| if (!base) |
| base = fake_pool_base, literal_pool = true; |
| else if (!indx) |
| indx = fake_pool_base, literal_pool = true; |
| else |
| return false; |
| |
| /* Mark up the displacement. */ |
| disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), |
| UNSPEC_LTREL_OFFSET); |
| } |
| |
| /* Validate base register. */ |
| if (base) |
| { |
| if (GET_CODE (base) == UNSPEC) |
| switch (XINT (base, 1)) |
| { |
| case UNSPEC_LTREF: |
| if (!disp) |
| disp = gen_rtx_UNSPEC (Pmode, |
| gen_rtvec (1, XVECEXP (base, 0, 0)), |
| UNSPEC_LTREL_OFFSET); |
| else |
| return false; |
| |
| base = XVECEXP (base, 0, 1); |
| break; |
| |
| case UNSPEC_LTREL_BASE: |
| if (XVECLEN (base, 0) == 1) |
| base = fake_pool_base, literal_pool = true; |
| else |
| base = XVECEXP (base, 0, 1); |
| break; |
| |
| default: |
| return false; |
| } |
| |
| if (!REG_P (base) |
| || (GET_MODE (base) != SImode |
| && GET_MODE (base) != Pmode)) |
| return false; |
| |
| if (REGNO (base) == STACK_POINTER_REGNUM |
| || REGNO (base) == FRAME_POINTER_REGNUM |
| || ((reload_completed || reload_in_progress) |
| && frame_pointer_needed |
| && REGNO (base) == HARD_FRAME_POINTER_REGNUM) |
| || REGNO (base) == ARG_POINTER_REGNUM |
| || (flag_pic |
| && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) |
| pointer = base_ptr = true; |
| |
| if ((reload_completed || reload_in_progress) |
| && base == cfun->machine->base_reg) |
| pointer = base_ptr = literal_pool = true; |
| } |
| |
| /* Validate index register. */ |
| if (indx) |
| { |
| if (GET_CODE (indx) == UNSPEC) |
| switch (XINT (indx, 1)) |
| { |
| case UNSPEC_LTREF: |
| if (!disp) |
| disp = gen_rtx_UNSPEC (Pmode, |
| gen_rtvec (1, XVECEXP (indx, 0, 0)), |
| UNSPEC_LTREL_OFFSET); |
| else |
| return false; |
| |
| indx = XVECEXP (indx, 0, 1); |
| break; |
| |
| case UNSPEC_LTREL_BASE: |
| if (XVECLEN (indx, 0) == 1) |
| indx = fake_pool_base, literal_pool = true; |
| else |
| indx = XVECEXP (indx, 0, 1); |
| break; |
| |
| default: |
| return false; |
| } |
| |
| if (!REG_P (indx) |
| || (GET_MODE (indx) != SImode |
| && GET_MODE (indx) != Pmode)) |
| return false; |
| |
| if (REGNO (indx) == STACK_POINTER_REGNUM |
| || REGNO (indx) == FRAME_POINTER_REGNUM |
| || ((reload_completed || reload_in_progress) |
| && frame_pointer_needed |
| && REGNO (indx) == HARD_FRAME_POINTER_REGNUM) |
| || REGNO (indx) == ARG_POINTER_REGNUM |
| || (flag_pic |
| && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) |
| pointer = indx_ptr = true; |
| |
| 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. */ |
| if (base && indx && !base_ptr |
| && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx)))) |
| { |
| rtx tmp = base; |
| base = indx; |
| indx = tmp; |
| } |
| |
| /* Validate displacement. */ |
| if (!disp) |
| { |
| /* If virtual registers are involved, the displacement will change later |
| anyway as the virtual registers get eliminated. This could make a |
| valid displacement invalid, but it is more likely to make an invalid |
| displacement valid, because we sometimes access the register save area |
| via negative offsets to one of those registers. |
| Thus we don't check the displacement for validity here. If after |
| elimination the displacement turns out to be invalid after all, |
| this is fixed up by reload in any case. */ |
| /* LRA maintains always displacements up to date and we need to |
| know the displacement is right during all LRA not only at the |
| final elimination. */ |
| if (lra_in_progress |
| || (base != arg_pointer_rtx |
| && indx != arg_pointer_rtx |
| && base != return_address_pointer_rtx |
| && indx != return_address_pointer_rtx |
| && base != frame_pointer_rtx |
| && indx != frame_pointer_rtx |
| && base != virtual_stack_vars_rtx |
| && indx != virtual_stack_vars_rtx)) |
| if (!DISP_IN_RANGE (offset)) |
| return false; |
| } |
| else |
| { |
| /* All the special cases are pointers. */ |
| pointer = true; |
| |
| /* In the small-PIC case, the linker converts @GOT |
| and @GOTNTPOFF offsets to possible displacements. */ |
| if (GET_CODE (disp) == UNSPEC |
| && (XINT (disp, 1) == UNSPEC_GOT |
| || XINT (disp, 1) == UNSPEC_GOTNTPOFF) |
| && flag_pic == 1) |
| { |
| ; |
| } |
| |
| /* Accept pool label offsets. */ |
| else if (GET_CODE (disp) == UNSPEC |
| && XINT (disp, 1) == UNSPEC_POOL_OFFSET) |
| ; |
| |
| /* Accept literal pool references. */ |
| else if (GET_CODE (disp) == UNSPEC |
| && XINT (disp, 1) == UNSPEC_LTREL_OFFSET) |
| { |
| /* In case CSE pulled a non literal pool reference out of |
| the pool we have to reject the address. This is |
| especially important when loading the GOT pointer on non |
| zarch CPUs. In this case the literal pool contains an lt |
| relative offset to the _GLOBAL_OFFSET_TABLE_ label which |
| will most likely exceed the displacement. */ |
| if (GET_CODE (XVECEXP (disp, 0, 0)) != SYMBOL_REF |
| || !CONSTANT_POOL_ADDRESS_P (XVECEXP (disp, 0, 0))) |
| return false; |
| |
| orig_disp = gen_rtx_CONST (Pmode, disp); |
| if (offset) |
| { |
| /* If we have an offset, make sure it does not |
| exceed the size of the constant pool entry. */ |
| rtx sym = XVECEXP (disp, 0, 0); |
| if (offset >= GET_MODE_SIZE (get_pool_mode (sym))) |
| return false; |
| |
| orig_disp = plus_constant (Pmode, orig_disp, offset); |
| } |
| } |
| |
| else |
| return false; |
| } |
| |
| if (!base && !indx) |
| pointer = true; |
| |
| if (out) |
| { |
| out->base = base; |
| out->indx = indx; |
| out->disp = orig_disp; |
| out->pointer = pointer; |
| 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 |
| s390_legitimate_address_without_index_p (rtx op) |
| { |
| struct s390_address addr; |
| |
| if (!s390_decompose_address (XEXP (op, 0), &addr)) |
| return false; |
| if (addr.indx) |
| return false; |
| |
| return true; |
| } |
| |
| |
| /* Return TRUE if ADDR is an operand valid for a load/store relative |
| instruction. Be aware that the alignment of the operand needs to |
| be checked separately. |
| Valid addresses are single references or a sum of a reference and a |
| constant integer. Return these parts in SYMREF and ADDEND. You can |
| pass NULL in REF and/or ADDEND if you are not interested in these |
| values. Literal pool references are *not* considered symbol |
| references. */ |
| |
| static bool |
| s390_loadrelative_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend) |
| { |
| HOST_WIDE_INT tmpaddend = 0; |
| |
| if (GET_CODE (addr) == CONST) |
| addr |