blob: 9629525e5c41fcf27f392949c6690b3e01f6cab7 [file] [log] [blame]
/* 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