URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/s390
- from Rev 154 to Rev 816
- ↔ Reverse comparison
Rev 154 → Rev 816
/tpf.h
0,0 → 1,129
/* Definitions for target OS TPF for GNU compiler, for IBM S/390 hardware |
Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
Contributed by P.J. Darcy (darcypj@us.ibm.com), |
Hartmut Penner (hpenner@de.ibm.com), and |
Ulrich Weigand (uweigand@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/>. */ |
|
#ifndef _TPF_H |
#define _TPF_H |
|
/* TPF wants the following macros defined/undefined as follows. */ |
#undef TARGET_TPF |
#define TARGET_TPF 1 |
#undef ASM_APP_ON |
#define ASM_APP_ON "#APP\n" |
#undef ASM_APP_OFF |
#define ASM_APP_OFF "#NO_APP\n" |
#define NO_IMPLICIT_EXTERN_C |
#define TARGET_POSIX_IO |
|
#undef SIZE_TYPE |
#define SIZE_TYPE ("long unsigned int") |
#undef PTRDIFF_TYPE |
#define PTRDIFF_TYPE ("long int") |
#undef WCHAR_TYPE |
#define WCHAR_TYPE "int" |
#undef WCHAR_TYPE_SIZE |
#define WCHAR_TYPE_SIZE 32 |
|
|
/* Basic record keeping for the TPF OS name. */ |
#undef TARGET_VERSION |
#define TARGET_VERSION fprintf (stderr, " (TPF: zSeries)"); |
|
/* TPF OS specific stack-pointer offset. */ |
#undef STACK_POINTER_OFFSET |
#define STACK_POINTER_OFFSET 448 |
|
/* When building for TPF, set a generic default target that is 64 bits. Also |
enable TPF profiling support and the standard backchain by default. */ |
#undef TARGET_DEFAULT |
#define TARGET_DEFAULT (MASK_TPF_PROFILING | MASK_64BIT | MASK_ZARCH \ |
| MASK_HARD_FLOAT | MASK_BACKCHAIN) |
|
/* Exception handling. */ |
|
/* Select a format to encode pointers in exception handling data. */ |
#undef ASM_PREFERRED_EH_DATA_FORMAT |
#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) DW_EH_PE_absptr |
|
/* TPF OS specific compiler settings. */ |
#undef TARGET_OS_CPP_BUILTINS |
#define TARGET_OS_CPP_BUILTINS() \ |
do \ |
{ \ |
builtin_define_std ("tpf"); \ |
builtin_assert ("system=tpf"); \ |
builtin_define ("__ELF__"); \ |
} \ |
while (0) |
|
|
#define EXTRA_SPECS \ |
{ "entry_spec", ENTRY_SPEC } |
|
/* Make TPF specific spec file settings here. */ |
|
#undef STARTFILE_SPEC |
#define STARTFILE_SPEC \ |
"%{mmain:crt0%O%s} crtbeginS%O%s crt3%O%s" |
|
#undef ENDFILE_SPEC |
#define ENDFILE_SPEC "crtendS%O%s" |
|
#undef CC1_SPEC |
#define CC1_SPEC "%{!fverbose-asm: -fverbose-asm}" |
|
/* The GNU C++ standard library requires that these macros be defined. */ |
#undef CPLUSPLUS_CPP_SPEC |
#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" |
|
#undef ASM_SPEC |
#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*} \ |
-alshd=%b.lst" |
|
/* It would be nice to get the system linker script define the ones that it |
needed. */ |
#undef LIB_SPEC |
#define LIB_SPEC "-lCTIS -lCISO -lCLBM -lCTAL -lCFVS -lCTBX -lCTXO \ |
-lCJ00 -lCTDF -lCOMX -lCOMS -lCTHD -lCTAD -lTPFSTUB" |
|
#undef TARGET_C99_FUNCTIONS |
#define TARGET_C99_FUNCTIONS 1 |
|
#define ENTRY_SPEC "%{mmain:-entry=_start} \ |
%{!mmain:-entry=0}" |
|
/* All linking is done shared on TPF-OS. */ |
/* FIXME: When binutils patch for new emulation is committed |
then change emulation to elf64_s390_tpf. */ |
#undef LINK_SPEC |
#define LINK_SPEC \ |
"-m elf64_s390 \ |
%{static:%estatic is not supported on TPF-OS} \ |
%{shared: -shared} \ |
%{!shared:-shared} \ |
%(entry_spec)" |
|
#define MD_UNWIND_SUPPORT "config/s390/tpf-unwind.h" |
|
/* IBM copies these libraries over with these names. */ |
#define MATH_LIBRARY "-lCLBM" |
#define LIBSTDCXX "-lCPP1" |
#endif /* ! _TPF_H */ |
/s390.c
0,0 → 1,9327
/* Subroutines used for code generation on IBM S/390 and zSeries |
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 |
Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@de.ibm.com). |
|
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 "tree.h" |
#include "tm_p.h" |
#include "regs.h" |
#include "hard-reg-set.h" |
#include "real.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 "expr.h" |
#include "reload.h" |
#include "toplev.h" |
#include "basic-block.h" |
#include "integrate.h" |
#include "ggc.h" |
#include "target.h" |
#include "target-def.h" |
#include "debug.h" |
#include "langhooks.h" |
#include "optabs.h" |
#include "tree-gimple.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 dxr; |
const int ddbr; |
const int ddr; |
const int debr; |
const int der; |
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 (135), /* DXR */ |
COSTS_N_INSNS (30), /* DDBR */ |
COSTS_N_INSNS (30), /* DDR */ |
COSTS_N_INSNS (27), /* DEBR */ |
COSTS_N_INSNS (26), /* DER */ |
COSTS_N_INSNS (220), /* DLGR */ |
COSTS_N_INSNS (34), /* DLR */ |
COSTS_N_INSNS (34), /* DR */ |
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 (72), /* DXR */ |
COSTS_N_INSNS (40), /* DDBR */ |
COSTS_N_INSNS (44), /* DDR */ |
COSTS_N_INSNS (26), /* DDBR */ |
COSTS_N_INSNS (28), /* DER */ |
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 (72), /* DXR */ |
COSTS_N_INSNS (40), /* DDBR */ |
COSTS_N_INSNS (37), /* DDR */ |
COSTS_N_INSNS (26), /* DDBR */ |
COSTS_N_INSNS (28), /* DER */ |
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 */ |
}; |
|
extern int reload_completed; |
|
/* Save information from a "cmpxx" operation until the branch or scc is |
emitted. */ |
rtx s390_compare_op0, s390_compare_op1; |
|
/* Save the result of a compare_and_swap until the branch or scc is |
emitted. */ |
rtx s390_compare_emitted = NULL_RTX; |
|
/* 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; |
}; |
|
/* Which cpu are we tuning for. */ |
enum processor_type s390_tune = PROCESSOR_max; |
enum processor_flags s390_tune_flags; |
/* Which instruction set architecture to use. */ |
enum processor_type s390_arch; |
enum processor_flags s390_arch_flags; |
|
HOST_WIDE_INT s390_warn_framesize = 0; |
HOST_WIDE_INT s390_stack_size = 0; |
HOST_WIDE_INT s390_stack_guard = 0; |
|
/* The following structure is embedded in the machine |
specific part of struct function. */ |
|
struct s390_frame_layout GTY (()) |
{ |
/* 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; |
|
/* 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 - 8 7 6 5 4 3 2 1 0 |
fpr 15 - 8 7 5 3 1 6 4 2 0 |
reg 31 - 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 machine_function GTY(()) |
{ |
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; |
|
/* True during final stage of literal pool processing. */ |
bool decomposed_literal_pool_addresses_ok_p; |
|
/* Some local-dynamic TLS symbol name. */ |
const char *some_ld_name; |
|
bool has_landing_pad_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_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \ |
cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD) |
#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |= \ |
(1 << (BITNUM))) |
#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \ |
(1 << (BITNUM)))) |
|
/* Number of GPRs and FPRs used for argument passing. */ |
#define GP_ARG_NUM_REG 5 |
#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2) |
|
/* 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)) |
|
/* Return true if the back end supports mode MODE. */ |
static bool |
s390_scalar_mode_supported_p (enum machine_mode mode) |
{ |
if (DECIMAL_FLOAT_MODE_P (mode)) |
return true; |
else |
return default_scalar_mode_supported_p (mode); |
} |
|
/* Set the has_landing_pad_p flag in struct machine_function to VALUE. */ |
|
void |
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 enum machine_mode |
s390_cc_modes_compatible (enum machine_mode m1, enum 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, enum machine_mode req_mode) |
{ |
enum machine_mode set_mode; |
|
gcc_assert (GET_CODE (set) == SET); |
|
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: |
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, enum 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). */ |
|
enum 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. */ |
|
enum machine_mode |
s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1) |
{ |
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. */ |
enum 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)))) |
{ |
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. */ |
|
void |
s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1) |
{ |
/* 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_CMPINT conversions if possible. */ |
if (GET_CODE (*op0) == UNSPEC |
&& XINT (*op0, 1) == UNSPEC_CMPINT |
&& 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; |
} |
} |
|
/* 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 = swap_condition (*code); |
} |
} |
|
/* 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) |
{ |
enum machine_mode mode = s390_select_ccmode (code, op0, op1); |
rtx ret = NULL_RTX; |
|
/* Do not output a redundant compare instruction if a compare_and_swap |
pattern already computed the result and the machine modes are compatible. */ |
if (s390_compare_emitted |
&& (s390_cc_modes_compatible (GET_MODE (s390_compare_emitted), mode) |
== GET_MODE (s390_compare_emitted))) |
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx); |
else |
{ |
rtx cc = gen_rtx_REG (mode, CC_REGNUM); |
|
emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1))); |
ret = gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); |
} |
s390_compare_emitted = NULL_RTX; |
return ret; |
} |
|
/* Emit a SImode compare and swap instruction setting MEM to NEW if OLD |
matches CMP. |
Return the correct condition RTL to be placed in the IF_THEN_ELSE of the |
conditional branch testing the result. */ |
|
static rtx |
s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new) |
{ |
rtx ret; |
|
emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new)); |
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx); |
|
s390_compare_emitted = NULL_RTX; |
|
return ret; |
} |
|
/* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an |
unconditional jump, else a conditional jump under condition COND. */ |
|
void |
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); |
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); |
|
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; |
|
default: |
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) |
{ |
static const char *const mnemonic[16] = |
{ |
NULL, "o", "h", "nle", |
"l", "nhe", "lh", "ne", |
"e", "nlh", "he", "nl", |
"le", "nh", "no", NULL |
}; |
|
int mask = s390_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, enum 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, |
enum machine_mode mode, |
enum 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; |
} |
|
/* 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, enum machine_mode mode, int first_subword) |
{ |
/* Floating point registers cannot be split. */ |
if (FP_REG_P (src) || FP_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, enum machine_mode mode, |
rtx *operands) |
{ |
enum 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_alloc_cleared (sizeof (struct machine_function)); |
} |
|
/* Change optimizations to be performed, depending on the |
optimization level. |
|
LEVEL is the optimization level specified; 2 if `-O2' is |
specified, 1 if `-O' is specified, and 0 if neither is specified. |
|
SIZE is nonzero if `-Os' is specified and zero otherwise. */ |
|
void |
optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED) |
{ |
/* ??? There are apparently still problems with -fcaller-saves. */ |
flag_caller_saves = 0; |
|
/* By default, always emit DWARF-2 unwind info. This allows debugging |
without maintaining a stack frame back-chain. */ |
flag_asynchronous_unwind_tables = 1; |
|
/* Use MVCLE instructions to decrease code size if requested. */ |
if (size != 0) |
target_flags |= MASK_MVCLE; |
} |
|
/* Return true if ARG is the name of a processor. Set *TYPE and *FLAGS |
to the associated processor_type and processor_flags if so. */ |
|
static bool |
s390_handle_arch_option (const char *arg, |
enum processor_type *type, |
enum processor_flags *flags) |
{ |
static struct pta |
{ |
const char *const name; /* processor name or nickname. */ |
const enum processor_type processor; |
const enum processor_flags flags; |
} |
const processor_alias_table[] = |
{ |
{"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT}, |
{"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT}, |
{"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH}, |
{"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH |
| PF_LONG_DISPLACEMENT}, |
{"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH |
| PF_LONG_DISPLACEMENT | PF_EXTIMM}, |
}; |
size_t i; |
|
for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++) |
if (strcmp (arg, processor_alias_table[i].name) == 0) |
{ |
*type = processor_alias_table[i].processor; |
*flags = processor_alias_table[i].flags; |
return true; |
} |
return false; |
} |
|
/* Implement TARGET_HANDLE_OPTION. */ |
|
static bool |
s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) |
{ |
switch (code) |
{ |
case OPT_march_: |
return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags); |
|
case OPT_mstack_guard_: |
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1) |
return false; |
if (exact_log2 (s390_stack_guard) == -1) |
error ("stack guard value must be an exact power of 2"); |
return true; |
|
case OPT_mstack_size_: |
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1) |
return false; |
if (exact_log2 (s390_stack_size) == -1) |
error ("stack size must be an exact power of 2"); |
return true; |
|
case OPT_mtune_: |
return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags); |
|
case OPT_mwarn_framesize_: |
return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1; |
|
default: |
return true; |
} |
} |
|
void |
override_options (void) |
{ |
/* Set up function hooks. */ |
init_machine_status = s390_init_machine_status; |
|
/* Architecture mode defaults according to ABI. */ |
if (!(target_flags_explicit & MASK_ZARCH)) |
{ |
if (TARGET_64BIT) |
target_flags |= MASK_ZARCH; |
else |
target_flags &= ~MASK_ZARCH; |
} |
|
/* Determine processor architectural level. */ |
if (!s390_arch_string) |
{ |
s390_arch_string = TARGET_ZARCH? "z900" : "g5"; |
s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags); |
} |
|
/* Determine processor to tune for. */ |
if (s390_tune == PROCESSOR_max) |
{ |
s390_tune = s390_arch; |
s390_tune_flags = s390_arch_flags; |
} |
|
/* Sanity checks. */ |
if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH)) |
error ("z/Architecture mode not supported on %s", s390_arch_string); |
if (TARGET_64BIT && !TARGET_ZARCH) |
error ("64-bit ABI not supported in ESA/390 mode"); |
|
/* Set processor cost function. */ |
if (s390_tune == PROCESSOR_2094_Z9_109) |
s390_cost = &z9_109_cost; |
else if (s390_tune == PROCESSOR_2084_Z990) |
s390_cost = &z990_cost; |
else |
s390_cost = &z900_cost; |
|
if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT) |
error ("-mbackchain -mpacked-stack -mhard-float are not supported " |
"in combination"); |
|
if (s390_stack_size) |
{ |
if (!s390_stack_guard) |
error ("-mstack-size implies use of -mstack-guard"); |
else if (s390_stack_guard >= s390_stack_size) |
error ("stack size must be greater than the stack guard value"); |
else if (s390_stack_size > 1 << 16) |
error ("stack size must not be greater than 64k"); |
} |
else if (s390_stack_guard) |
error ("-mstack-guard implies use of -mstack-size"); |
|
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128 |
if (!(target_flags_explicit & MASK_LONG_DOUBLE_128)) |
target_flags |= MASK_LONG_DOUBLE_128; |
#endif |
} |
|
/* Map for smallest class containing reg regno. */ |
|
const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = |
{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, |
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, |
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, |
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, |
FP_REGS, FP_REGS, FP_REGS, FP_REGS, |
FP_REGS, FP_REGS, FP_REGS, FP_REGS, |
FP_REGS, FP_REGS, FP_REGS, FP_REGS, |
FP_REGS, FP_REGS, FP_REGS, FP_REGS, |
ADDR_REGS, CC_REGS, ADDR_REGS, ADDR_REGS, |
ACCESS_REGS, ACCESS_REGS |
}; |
|
/* Return attribute type of insn. */ |
|
static enum attr_type |
s390_safe_attr_type (rtx 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; |
|
/* 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. */ |
if (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 chunkified literal pool symbol references. */ |
else if (cfun && cfun->machine |
&& cfun->machine->decomposed_literal_pool_addresses_ok_p |
&& GET_CODE (disp) == MINUS |
&& GET_CODE (XEXP (disp, 0)) == LABEL_REF |
&& GET_CODE (XEXP (disp, 1)) == LABEL_REF) |
{ |
; |
} |
|
/* Accept literal pool references. */ |
else if (GET_CODE (disp) == UNSPEC |
&& XINT (disp, 1) == UNSPEC_LTREL_OFFSET) |
{ |
orig_disp = gen_rtx_CONST (Pmode, disp); |
if (offset) |
{ |
/* If we have an offset, make sure it does not |
exceed the size of the constant pool entry. */ |
rtx sym = XVECEXP (disp, 0, 0); |
if (offset >= GET_MODE_SIZE (get_pool_mode (sym))) |
return false; |
|
orig_disp = plus_constant (orig_disp, offset); |
} |
} |
|
else |
return false; |
} |
|
if (!base && !indx) |
pointer = true; |
|
if (out) |
{ |
out->base = base; |
out->indx = indx; |
out->disp = orig_disp; |
out->pointer = pointer; |
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; |
} |
|
|
/* Evaluates constraint strings described by the regular expression |
([A|B](Q|R|S|T))|U|W and returns 1 if OP is a valid operand for the |
constraint given in STR, or 0 else. */ |
|
int |
s390_mem_constraint (const char *str, rtx op) |
{ |
struct s390_address addr; |
char c = str[0]; |
|
/* Check for offsettable variants of memory constraints. */ |
if (c == 'A') |
{ |
/* Only accept non-volatile MEMs. */ |
if (!MEM_P (op) || MEM_VOLATILE_P (op)) |
return 0; |
|
if ((reload_completed || reload_in_progress) |
? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op)) |
return 0; |
|
c = str[1]; |
} |
|
/* Check for non-literal-pool variants of memory constraints. */ |
else if (c == 'B') |
{ |
if (GET_CODE (op) != MEM) |
return 0; |
if (!s390_decompose_address (XEXP (op, 0), &addr)) |
return 0; |
if (addr.literal_pool) |
return 0; |
|
c = str[1]; |
} |
|
switch (c) |
{ |
case 'Q': |
if (GET_CODE (op) != MEM) |
return 0; |
if (!s390_decompose_address (XEXP (op, 0), &addr)) |
return 0; |
if (addr.indx) |
return 0; |
|
if (TARGET_LONG_DISPLACEMENT) |
{ |
if (!s390_short_displacement (addr.disp)) |
return 0; |
} |
break; |
|
case 'R': |
if (GET_CODE (op) != MEM) |
return 0; |
|
if (TARGET_LONG_DISPLACEMENT) |
{ |
if (!s390_decompose_address (XEXP (op, 0), &addr)) |
return 0; |
if (!s390_short_displacement (addr.disp)) |
return 0; |
} |
break; |
|
case 'S': |
if (!TARGET_LONG_DISPLACEMENT) |
return 0; |
if (GET_CODE (op) != MEM) |
return 0; |
if (!s390_decompose_address (XEXP (op, 0), &addr)) |
return 0; |
if (addr.indx) |
return 0; |
if (s390_short_displacement (addr.disp)) |
return 0; |
break; |
|
case 'T': |
if (!TARGET_LONG_DISPLACEMENT) |
return 0; |
if (GET_CODE (op) != MEM) |
return 0; |
/* Any invalid address here will be fixed up by reload, |
so accept it for the most generic constraint. */ |
if (s390_decompose_address (XEXP (op, 0), &addr) |
&& s390_short_displacement (addr.disp)) |
return 0; |
break; |
|
case 'U': |
if (TARGET_LONG_DISPLACEMENT) |
{ |
if (!s390_decompose_address (op, &addr)) |
return 0; |
if (!s390_short_displacement (addr.disp)) |
return 0; |
} |
break; |
|
case 'W': |
if (!TARGET_LONG_DISPLACEMENT) |
return 0; |
/* Any invalid address here will be fixed up by reload, |
so accept it for the most generic constraint. */ |
if (s390_decompose_address (op, &addr) |
&& s390_short_displacement (addr.disp)) |
return 0; |
break; |
|
case 'Y': |
/* Simply check for the basic form of a shift count. Reload will |
take care of making sure we have a proper base register. */ |
if (!s390_decompose_shift_count (op, NULL, NULL)) |
return 0; |
break; |
|
default: |
return 0; |
} |
|
return 1; |
} |
|
|
|
/* Evaluates constraint strings starting with letter O. Input |
parameter C is the second letter following the "O" in the constraint |
string. Returns 1 if VALUE meets the respective constraint and 0 |
otherwise. */ |
|
int |
s390_O_constraint_str (const char c, HOST_WIDE_INT value) |
{ |
if (!TARGET_EXTIMM) |
return 0; |
|
switch (c) |
{ |
case 's': |
return trunc_int_for_mode (value, SImode) == value; |
|
case 'p': |
return value == 0 |
|| s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1; |
|
case 'n': |
return value == -1 |
|| s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1; |
|
default: |
gcc_unreachable (); |
} |
} |
|
|
/* Evaluates constraint strings starting with letter N. Parameter STR |
contains the letters following letter "N" in the constraint string. |
Returns true if VALUE matches the constraint. */ |
|
int |
s390_N_constraint_str (const char *str, HOST_WIDE_INT value) |
{ |
enum machine_mode mode, part_mode; |
int def; |
int part, part_goal; |
|
|
if (str[0] == 'x') |
part_goal = -1; |
else |
part_goal = str[0] - '0'; |
|
switch (str[1]) |
{ |
case 'Q': |
part_mode = QImode; |
break; |
case 'H': |
part_mode = HImode; |
break; |
case 'S': |
part_mode = SImode; |
break; |
default: |
return 0; |
} |
|
switch (str[2]) |
{ |
case 'H': |
mode = HImode; |
break; |
case 'S': |
mode = SImode; |
break; |
case 'D': |
mode = DImode; |
break; |
default: |
return 0; |
} |
|
switch (str[3]) |
{ |
case '0': |
def = 0; |
break; |
case 'F': |
def = -1; |
break; |
default: |
return 0; |
} |
|
if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode)) |
return 0; |
|
part = s390_single_part (GEN_INT (value), mode, part_mode, def); |
if (part < 0) |
return 0; |
if (part_goal != -1 && part_goal != part) |
return 0; |
|
return 1; |
} |
|
|
/* Returns true if the input parameter VALUE is a float zero. */ |
|
int |
s390_float_const_zero_p (rtx value) |
{ |
return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT |
&& value == CONST0_RTX (GET_MODE (value))); |
} |
|
|
/* Compute a (partial) cost for rtx X. Return true if the complete |
cost has been computed, and false if subexpressions should be |
scanned. In either case, *TOTAL contains the cost result. |
CODE contains GET_CODE (x), OUTER_CODE contains the code |
of the superexpression of x. */ |
|
static bool |
s390_rtx_costs (rtx x, int code, int outer_code, int *total) |
{ |
switch (code) |
{ |
case CONST: |
case CONST_INT: |
case LABEL_REF: |
case SYMBOL_REF: |
case CONST_DOUBLE: |
case MEM: |
*total = 0; |
return true; |
|
case ASHIFT: |
case ASHIFTRT: |
case LSHIFTRT: |
case ROTATE: |
case ROTATERT: |
case AND: |
case IOR: |
case XOR: |
case NEG: |
case NOT: |
*total = COSTS_N_INSNS (1); |
return false; |
|
case PLUS: |
case MINUS: |
/* Check for multiply and add. */ |
if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode) |
&& GET_CODE (XEXP (x, 0)) == MULT |
&& TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD) |
{ |
/* This is the multiply and add case. */ |
if (GET_MODE (x) == DFmode) |
*total = s390_cost->madbr; |
else |
*total = s390_cost->maebr; |
*total += rtx_cost (XEXP (XEXP (x, 0), 0), MULT) |
+ rtx_cost (XEXP (XEXP (x, 0), 1), MULT) |
+ rtx_cost (XEXP (x, 1), code); |
return true; /* Do not do an additional recursive descent. */ |
} |
*total = COSTS_N_INSNS (1); |
return false; |
|
case MULT: |
switch (GET_MODE (x)) |
{ |
case SImode: |
{ |
rtx left = XEXP (x, 0); |
rtx right = XEXP (x, 1); |
if (GET_CODE (right) == CONST_INT |
&& CONST_OK_FOR_K (INTVAL (right))) |
*total = s390_cost->mhi; |
else if (GET_CODE (left) == SIGN_EXTEND) |
*total = s390_cost->mh; |
else |
*total = s390_cost->ms; /* msr, ms, msy */ |
break; |
} |
case DImode: |
{ |
rtx left = XEXP (x, 0); |
rtx right = XEXP (x, 1); |
if (TARGET_64BIT) |
{ |
if (GET_CODE (right) == CONST_INT |
&& CONST_OK_FOR_K (INTVAL (right))) |
*total = s390_cost->mghi; |
else if (GET_CODE (left) == SIGN_EXTEND) |
*total = s390_cost->msgf; |
else |
*total = s390_cost->msg; /* msgr, msg */ |
} |
else /* TARGET_31BIT */ |
{ |
if (GET_CODE (left) == SIGN_EXTEND |
&& GET_CODE (right) == SIGN_EXTEND) |
/* mulsidi case: mr, m */ |
*total = s390_cost->m; |
else if (GET_CODE (left) == ZERO_EXTEND |
&& GET_CODE (right) == ZERO_EXTEND |
&& TARGET_CPU_ZARCH) |
/* umulsidi case: ml, mlr */ |
*total = s390_cost->ml; |
else |
/* Complex calculation is required. */ |
*total = COSTS_N_INSNS (40); |
} |
break; |
} |
case SFmode: |
case DFmode: |
*total = s390_cost->mult_df; |
break; |
case TFmode: |
*total = s390_cost->mxbr; |
break; |
default: |
return false; |
} |
return false; |
|
case UDIV: |
case UMOD: |
if (GET_MODE (x) == TImode) /* 128 bit division */ |
*total = s390_cost->dlgr; |
else if (GET_MODE (x) == DImode) |
{ |
rtx right = XEXP (x, 1); |
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */ |
*total = s390_cost->dlr; |
else /* 64 by 64 bit division */ |
*total = s390_cost->dlgr; |
} |
else if (GET_MODE (x) == SImode) /* 32 bit division */ |
*total = s390_cost->dlr; |
return false; |
|
case DIV: |
case MOD: |
if (GET_MODE (x) == DImode) |
{ |
rtx right = XEXP (x, 1); |
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */ |
if (TARGET_64BIT) |
*total = s390_cost->dsgfr; |
else |
*total = s390_cost->dr; |
else /* 64 by 64 bit division */ |
*total = s390_cost->dsgr; |
} |
else if (GET_MODE (x) == SImode) /* 32 bit division */ |
*total = s390_cost->dlr; |
else if (GET_MODE (x) == SFmode) |
{ |
if (TARGET_IEEE_FLOAT) |
*total = s390_cost->debr; |
else /* TARGET_IBM_FLOAT */ |
*total = s390_cost->der; |
} |
else if (GET_MODE (x) == DFmode) |
{ |
if (TARGET_IEEE_FLOAT) |
*total = s390_cost->ddbr; |
else /* TARGET_IBM_FLOAT */ |
*total = s390_cost->ddr; |
} |
else if (GET_MODE (x) == TFmode) |
{ |
if (TARGET_IEEE_FLOAT) |
*total = s390_cost->dxbr; |
else /* TARGET_IBM_FLOAT */ |
*total = s390_cost->dxr; |
} |
return false; |
|
case SQRT: |
if (GET_MODE (x) == SFmode) |
*total = s390_cost->sqebr; |
else if (GET_MODE (x) == DFmode) |
*total = s390_cost->sqdbr; |
else /* TFmode */ |
*total = s390_cost->sqxbr; |
return false; |
|
case SIGN_EXTEND: |
case ZERO_EXTEND: |
if (outer_code == MULT || outer_code == DIV || outer_code == MOD |
|| outer_code == PLUS || outer_code == MINUS |
|| outer_code == COMPARE) |
*total = 0; |
return false; |
|
case COMPARE: |
*total = COSTS_N_INSNS (1); |
if (GET_CODE (XEXP (x, 0)) == AND |
&& GET_CODE (XEXP (x, 1)) == CONST_INT |
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) |
{ |
rtx op0 = XEXP (XEXP (x, 0), 0); |
rtx op1 = XEXP (XEXP (x, 0), 1); |
rtx op2 = XEXP (x, 1); |
|
if (memory_operand (op0, GET_MODE (op0)) |
&& s390_tm_ccmode (op1, op2, 0) != VOIDmode) |
return true; |
if (register_operand (op0, GET_MODE (op0)) |
&& s390_tm_ccmode (op1, op2, 1) != VOIDmode) |
return true; |
} |
return false; |
|
default: |
return false; |
} |
} |
|
/* Return the cost of an address rtx ADDR. */ |
|
static int |
s390_address_cost (rtx addr) |
{ |
struct s390_address ad; |
if (!s390_decompose_address (addr, &ad)) |
return 1000; |
|
return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1); |
} |
|
/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode, |
otherwise return 0. */ |
|
int |
tls_symbolic_operand (rtx op) |
{ |
if (GET_CODE (op) != SYMBOL_REF) |
return 0; |
return SYMBOL_REF_TLS_MODEL (op); |
} |
|
/* Split DImode access register reference REG (on 64-bit) into its constituent |
low and high parts, and store them into LO and HI. Note that gen_lowpart/ |
gen_highpart cannot be used as they assume all registers are word-sized, |
while our access registers have only half that size. */ |
|
void |
s390_split_access_reg (rtx reg, rtx *lo, rtx *hi) |
{ |
gcc_assert (TARGET_64BIT); |
gcc_assert (ACCESS_REG_P (reg)); |
gcc_assert (GET_MODE (reg) == DImode); |
gcc_assert (!(REGNO (reg) & 1)); |
|
*lo = gen_rtx_REG (SImode, REGNO (reg) + 1); |
*hi = gen_rtx_REG (SImode, REGNO (reg)); |
} |
|
/* Return true if OP contains a symbol reference */ |
|
bool |
symbolic_reference_mentioned_p (rtx op) |
{ |
const char *fmt; |
int i; |
|
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) |
return 1; |
|
fmt = GET_RTX_FORMAT (GET_CODE (op)); |
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'E') |
{ |
int j; |
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--) |
if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) |
return 1; |
} |
|
else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) |
return 1; |
} |
|
return 0; |
} |
|
/* Return true if OP contains a reference to a thread-local symbol. */ |
|
bool |
tls_symbolic_reference_mentioned_p (rtx op) |
{ |
const char *fmt; |
int i; |
|
if (GET_CODE (op) == SYMBOL_REF) |
return tls_symbolic_operand (op); |
|
fmt = GET_RTX_FORMAT (GET_CODE (op)); |
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'E') |
{ |
int j; |
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--) |
if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j))) |
return true; |
} |
|
else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i))) |
return true; |
} |
|
return false; |
} |
|
|
/* Return true if OP is a legitimate general operand when |
generating PIC code. It is given that flag_pic is on |
and that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */ |
|
int |
legitimate_pic_operand_p (rtx op) |
{ |
/* Accept all non-symbolic constants. */ |
if (!SYMBOLIC_CONST (op)) |
return 1; |
|
/* Reject everything else; must be handled |
via emit_symbolic_move. */ |
return 0; |
} |
|
/* Returns true if the constant value OP is a legitimate general operand. |
It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */ |
|
int |
legitimate_constant_p (rtx op) |
{ |
/* Accept all non-symbolic constants. */ |
if (!SYMBOLIC_CONST (op)) |
return 1; |
|
/* Accept immediate LARL operands. */ |
if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode)) |
return 1; |
|
/* Thread-local symbols are never legal constants. This is |
so that emit_call knows that computing such addresses |
might require a function call. */ |
if (TLS_SYMBOLIC_CONST (op)) |
return 0; |
|
/* In the PIC case, symbolic constants must *not* be |
forced into the literal pool. We accept them here, |
so that they will be handled by emit_symbolic_move. */ |
if (flag_pic) |
return 1; |
|
/* All remaining non-PIC symbolic constants are |
forced into the literal pool. */ |
return 0; |
} |
|
/* Determine if it's legal to put X into the constant pool. This |
is not possible if X contains the address of a symbol that is |
not constant (TLS) or not known at final link time (PIC). */ |
|
static bool |
s390_cannot_force_const_mem (rtx x) |
{ |
switch (GET_CODE (x)) |
{ |
case CONST_INT: |
case CONST_DOUBLE: |
/* Accept all non-symbolic constants. */ |
return false; |
|
case LABEL_REF: |
/* Labels are OK iff we are non-PIC. */ |
return flag_pic != 0; |
|
case SYMBOL_REF: |
/* 'Naked' TLS symbol references are never OK, |
non-TLS symbols are OK iff we are non-PIC. */ |
if (tls_symbolic_operand (x)) |
return true; |
else |
return flag_pic != 0; |
|
case CONST: |
return s390_cannot_force_const_mem (XEXP (x, 0)); |
case PLUS: |
case MINUS: |
return s390_cannot_force_const_mem (XEXP (x, 0)) |
|| s390_cannot_force_const_mem (XEXP (x, 1)); |
|
case UNSPEC: |
switch (XINT (x, 1)) |
{ |
/* Only lt-relative or GOT-relative UNSPECs are OK. */ |
case UNSPEC_LTREL_OFFSET: |
case UNSPEC_GOT: |
case UNSPEC_GOTOFF: |
case UNSPEC_PLTOFF: |
case UNSPEC_TLSGD: |
case UNSPEC_TLSLDM: |
case UNSPEC_NTPOFF: |
case UNSPEC_DTPOFF: |
case UNSPEC_GOTNTPOFF: |
case UNSPEC_INDNTPOFF: |
return false; |
|
/* If the literal pool shares the code section, be put |
execute template placeholders into the pool as well. */ |
case UNSPEC_INSN: |
return TARGET_CPU_ZARCH; |
|
default: |
return true; |
} |
break; |
|
default: |
gcc_unreachable (); |
} |
} |
|
/* Returns true if the constant value OP is a legitimate general |
operand during and after reload. The difference to |
legitimate_constant_p is that this function will not accept |
a constant that would need to be forced to the literal pool |
before it can be used as operand. */ |
|
bool |
legitimate_reload_constant_p (rtx op) |
{ |
/* Accept la(y) operands. */ |
if (GET_CODE (op) == CONST_INT |
&& DISP_IN_RANGE (INTVAL (op))) |
return true; |
|
/* Accept l(g)hi/l(g)fi operands. */ |
if (GET_CODE (op) == CONST_INT |
&& (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op)))) |
return true; |
|
/* Accept lliXX operands. */ |
if (TARGET_ZARCH |
&& GET_CODE (op) == CONST_INT |
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op) |
&& s390_single_part (op, word_mode, HImode, 0) >= 0) |
return true; |
|
if (TARGET_EXTIMM |
&& GET_CODE (op) == CONST_INT |
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op) |
&& s390_single_part (op, word_mode, SImode, 0) >= 0) |
return true; |
|
/* Accept larl operands. */ |
if (TARGET_CPU_ZARCH |
&& larl_operand (op, VOIDmode)) |
return true; |
|
/* Accept lzXX operands. */ |
if (GET_CODE (op) == CONST_DOUBLE |
&& CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G")) |
return true; |
|
/* Accept double-word operands that can be split. */ |
if (GET_CODE (op) == CONST_INT |
&& trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op)) |
{ |
enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode; |
rtx hi = operand_subword (op, 0, 0, dword_mode); |
rtx lo = operand_subword (op, 1, 0, dword_mode); |
return legitimate_reload_constant_p (hi) |
&& legitimate_reload_constant_p (lo); |
} |
|
/* Everything else cannot be handled without reload. */ |
return false; |
} |
|
/* Given an rtx OP being reloaded into a reg required to be in class CLASS, |
return the class of reg to actually use. */ |
|
enum reg_class |
s390_preferred_reload_class (rtx op, enum reg_class class) |
{ |
switch (GET_CODE (op)) |
{ |
/* Constants we cannot reload must be forced into the |
literal pool. */ |
|
case CONST_DOUBLE: |
case CONST_INT: |
if (legitimate_reload_constant_p (op)) |
return class; |
else |
return NO_REGS; |
|
/* If a symbolic constant or a PLUS is reloaded, |
it is most likely being used as an address, so |
prefer ADDR_REGS. If 'class' is not a superset |
of ADDR_REGS, e.g. FP_REGS, reject this reload. */ |
case PLUS: |
case LABEL_REF: |
case SYMBOL_REF: |
case CONST: |
if (reg_class_subset_p (ADDR_REGS, class)) |
return ADDR_REGS; |
else |
return NO_REGS; |
|
default: |
break; |
} |
|
return class; |
} |
|
/* Return the register class of a scratch register needed to |
load IN into a register of class CLASS in MODE. |
|
We need a temporary when loading a PLUS expression which |
is not a legitimate operand of the LOAD ADDRESS instruction. */ |
|
enum reg_class |
s390_secondary_input_reload_class (enum reg_class class, |
enum machine_mode mode, rtx in) |
{ |
if (s390_plus_operand (in, mode)) |
return ADDR_REGS; |
|
if (reg_classes_intersect_p (FP_REGS, class) |
&& mode == TFmode |
&& GET_CODE (in) == MEM |
&& GET_CODE (XEXP (in, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (in, 0), 1)) == CONST_INT |
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (in, 0), 1)) |
+ GET_MODE_SIZE (mode) - 1)) |
return ADDR_REGS; |
|
if (reg_classes_intersect_p (CC_REGS, class)) |
return GENERAL_REGS; |
|
return NO_REGS; |
} |
|
/* Return the register class of a scratch register needed to |
store a register of class CLASS in MODE into OUT: |
|
We need a temporary when storing a double-word to a |
non-offsettable memory address. */ |
|
enum reg_class |
s390_secondary_output_reload_class (enum reg_class class, |
enum machine_mode mode, rtx out) |
{ |
if ((TARGET_64BIT ? (mode == TImode || mode == TFmode) |
: (mode == DImode || mode == DFmode)) |
&& reg_classes_intersect_p (GENERAL_REGS, class) |
&& GET_CODE (out) == MEM |
&& GET_CODE (XEXP (out, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT |
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1)) |
+ GET_MODE_SIZE (mode) - 1)) |
return ADDR_REGS; |
|
if (reg_classes_intersect_p (FP_REGS, class) |
&& mode == TFmode |
&& GET_CODE (out) == MEM |
&& GET_CODE (XEXP (out, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT |
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1)) |
+ GET_MODE_SIZE (mode) - 1)) |
return ADDR_REGS; |
|
if (reg_classes_intersect_p (CC_REGS, class)) |
return GENERAL_REGS; |
|
return NO_REGS; |
} |
|
/* Generate code to load SRC, which is PLUS that is not a |
legitimate operand for the LA instruction, into TARGET. |
SCRATCH may be used as scratch register. */ |
|
void |
s390_expand_plus_operand (rtx target, rtx src, |
rtx scratch) |
{ |
rtx sum1, sum2; |
struct s390_address ad; |
|
/* src must be a PLUS; get its two operands. */ |
gcc_assert (GET_CODE (src) == PLUS); |
gcc_assert (GET_MODE (src) == Pmode); |
|
/* Check if any of the two operands is already scheduled |
for replacement by reload. This can happen e.g. when |
float registers occur in an address. */ |
sum1 = find_replacement (&XEXP (src, 0)); |
sum2 = find_replacement (&XEXP (src, 1)); |
src = gen_rtx_PLUS (Pmode, sum1, sum2); |
|
/* If the address is already strictly valid, there's nothing to do. */ |
if (!s390_decompose_address (src, &ad) |
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) |
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) |
{ |
/* Otherwise, one of the operands cannot be an address register; |
we reload its value into the scratch register. */ |
if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) |
{ |
emit_move_insn (scratch, sum1); |
sum1 = scratch; |
} |
if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15) |
{ |
emit_move_insn (scratch, sum2); |
sum2 = scratch; |
} |
|
/* According to the way these invalid addresses are generated |
in reload.c, it should never happen (at least on s390) that |
*neither* of the PLUS components, after find_replacements |
was applied, is an address register. */ |
if (sum1 == scratch && sum2 == scratch) |
{ |
debug_rtx (src); |
gcc_unreachable (); |
} |
|
src = gen_rtx_PLUS (Pmode, sum1, sum2); |
} |
|
/* Emit the LOAD ADDRESS pattern. Note that reload of PLUS |
is only ever performed on addresses, so we can mark the |
sum as legitimate for LA in any case. */ |
s390_load_address (target, src); |
} |
|
|
/* Return true if ADDR is a valid memory address. |
STRICT specifies whether strict register checking applies. */ |
|
bool |
legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, |
rtx addr, int strict) |
{ |
struct s390_address ad; |
if (!s390_decompose_address (addr, &ad)) |
return false; |
|
if (strict) |
{ |
if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) |
return false; |
|
if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))) |
return false; |
} |
else |
{ |
if (ad.base |
&& !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER |
|| REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS)) |
return false; |
|
if (ad.indx |
&& !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER |
|| REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS)) |
return false; |
} |
return true; |
} |
|
/* Return true if OP is a valid operand for the LA instruction. |
In 31-bit, we need to prove that the result is used as an |
address, as LA performs only a 31-bit addition. */ |
|
bool |
legitimate_la_operand_p (rtx op) |
{ |
struct s390_address addr; |
if (!s390_decompose_address (op, &addr)) |
return false; |
|
return (TARGET_64BIT || addr.pointer); |
} |
|
/* Return true if it is valid *and* preferable to use LA to |
compute the sum of OP1 and OP2. */ |
|
bool |
preferred_la_operand_p (rtx op1, rtx op2) |
{ |
struct s390_address addr; |
|
if (op2 != const0_rtx) |
op1 = gen_rtx_PLUS (Pmode, op1, op2); |
|
if (!s390_decompose_address (op1, &addr)) |
return false; |
if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base))) |
return false; |
if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx))) |
return false; |
|
if (!TARGET_64BIT && !addr.pointer) |
return false; |
|
if (addr.pointer) |
return true; |
|
if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) |
|| (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) |
return true; |
|
return false; |
} |
|
/* Emit a forced load-address operation to load SRC into DST. |
This will use the LOAD ADDRESS instruction even in situations |
where legitimate_la_operand_p (SRC) returns false. */ |
|
void |
s390_load_address (rtx dst, rtx src) |
{ |
if (TARGET_64BIT) |
emit_move_insn (dst, src); |
else |
emit_insn (gen_force_la_31 (dst, src)); |
} |
|
/* Return a legitimate reference for ORIG (an address) using the |
register REG. If REG is 0, a new pseudo is generated. |
|
There are two types of references that must be handled: |
|
1. Global data references must load the address from the GOT, via |
the PIC reg. An insn is emitted to do this load, and the reg is |
returned. |
|
2. Static data references, constant pool addresses, and code labels |
compute the address as an offset from the GOT, whose base is in |
the PIC reg. Static data objects have SYMBOL_FLAG_LOCAL set to |
differentiate them from global data objects. The returned |
address is the PIC reg + an unspec constant. |
|
GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC |
reg also appears in the address. */ |
|
rtx |
legitimize_pic_address (rtx orig, rtx reg) |
{ |
rtx addr = orig; |
rtx new = orig; |
rtx base; |
|
gcc_assert (!TLS_SYMBOLIC_CONST (addr)); |
|
if (GET_CODE (addr) == LABEL_REF |
|| (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr))) |
{ |
/* This is a local symbol. */ |
if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode)) |
{ |
/* Access local symbols PC-relative via LARL. |
This is the same as in the non-PIC case, so it is |
handled automatically ... */ |
} |
else |
{ |
/* Access local symbols relative to the GOT. */ |
|
rtx temp = reg? reg : gen_reg_rtx (Pmode); |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF); |
addr = gen_rtx_CONST (Pmode, addr); |
addr = force_const_mem (Pmode, addr); |
emit_move_insn (temp, addr); |
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
} |
} |
else if (GET_CODE (addr) == SYMBOL_REF) |
{ |
if (reg == 0) |
reg = gen_reg_rtx (Pmode); |
|
if (flag_pic == 1) |
{ |
/* Assume GOT offset < 4k. This is handled the same way |
in both 31- and 64-bit code (@GOT). */ |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); |
new = gen_rtx_CONST (Pmode, new); |
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); |
new = gen_const_mem (Pmode, new); |
emit_move_insn (reg, new); |
new = reg; |
} |
else if (TARGET_CPU_ZARCH) |
{ |
/* If the GOT offset might be >= 4k, we determine the position |
of the GOT entry via a PC-relative LARL (@GOTENT). */ |
|
rtx temp = gen_reg_rtx (Pmode); |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT); |
new = gen_rtx_CONST (Pmode, new); |
emit_move_insn (temp, new); |
|
new = gen_const_mem (Pmode, temp); |
emit_move_insn (reg, new); |
new = reg; |
} |
else |
{ |
/* If the GOT offset might be >= 4k, we have to load it |
from the literal pool (@GOT). */ |
|
rtx temp = gen_reg_rtx (Pmode); |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); |
addr = gen_rtx_CONST (Pmode, addr); |
addr = force_const_mem (Pmode, addr); |
emit_move_insn (temp, addr); |
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); |
new = gen_const_mem (Pmode, new); |
emit_move_insn (reg, new); |
new = reg; |
} |
} |
else |
{ |
if (GET_CODE (addr) == CONST) |
{ |
addr = XEXP (addr, 0); |
if (GET_CODE (addr) == UNSPEC) |
{ |
gcc_assert (XVECLEN (addr, 0) == 1); |
switch (XINT (addr, 1)) |
{ |
/* If someone moved a GOT-relative UNSPEC |
out of the literal pool, force them back in. */ |
case UNSPEC_GOTOFF: |
case UNSPEC_PLTOFF: |
new = force_const_mem (Pmode, orig); |
break; |
|
/* @GOT is OK as is if small. */ |
case UNSPEC_GOT: |
if (flag_pic == 2) |
new = force_const_mem (Pmode, orig); |
break; |
|
/* @GOTENT is OK as is. */ |
case UNSPEC_GOTENT: |
break; |
|
/* @PLT is OK as is on 64-bit, must be converted to |
GOT-relative @PLTOFF on 31-bit. */ |
case UNSPEC_PLT: |
if (!TARGET_CPU_ZARCH) |
{ |
rtx temp = reg? reg : gen_reg_rtx (Pmode); |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
addr = XVECEXP (addr, 0, 0); |
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), |
UNSPEC_PLTOFF); |
addr = gen_rtx_CONST (Pmode, addr); |
addr = force_const_mem (Pmode, addr); |
emit_move_insn (temp, addr); |
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
} |
break; |
|
/* Everything else cannot happen. */ |
default: |
gcc_unreachable (); |
} |
} |
else |
gcc_assert (GET_CODE (addr) == PLUS); |
} |
if (GET_CODE (addr) == PLUS) |
{ |
rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1); |
|
gcc_assert (!TLS_SYMBOLIC_CONST (op0)); |
gcc_assert (!TLS_SYMBOLIC_CONST (op1)); |
|
/* Check first to see if this is a constant offset |
from a local symbol reference. */ |
if ((GET_CODE (op0) == LABEL_REF |
|| (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0))) |
&& GET_CODE (op1) == CONST_INT) |
{ |
if (TARGET_CPU_ZARCH |
&& larl_operand (op0, VOIDmode) |
&& INTVAL (op1) < (HOST_WIDE_INT)1 << 31 |
&& INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31)) |
{ |
if (INTVAL (op1) & 1) |
{ |
/* LARL can't handle odd offsets, so emit a |
pair of LARL and LA. */ |
rtx temp = reg? reg : gen_reg_rtx (Pmode); |
|
if (!DISP_IN_RANGE (INTVAL (op1))) |
{ |
HOST_WIDE_INT even = INTVAL (op1) - 1; |
op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even)); |
op0 = gen_rtx_CONST (Pmode, op0); |
op1 = const1_rtx; |
} |
|
emit_move_insn (temp, op0); |
new = gen_rtx_PLUS (Pmode, temp, op1); |
|
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
} |
else |
{ |
/* If the offset is even, we can just use LARL. |
This will happen automatically. */ |
} |
} |
else |
{ |
/* Access local symbols relative to the GOT. */ |
|
rtx temp = reg? reg : gen_reg_rtx (Pmode); |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), |
UNSPEC_GOTOFF); |
addr = gen_rtx_PLUS (Pmode, addr, op1); |
addr = gen_rtx_CONST (Pmode, addr); |
addr = force_const_mem (Pmode, addr); |
emit_move_insn (temp, addr); |
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
} |
} |
|
/* Now, check whether it is a GOT relative symbol plus offset |
that was pulled out of the literal pool. Force it back in. */ |
|
else if (GET_CODE (op0) == UNSPEC |
&& GET_CODE (op1) == CONST_INT |
&& XINT (op0, 1) == UNSPEC_GOTOFF) |
{ |
gcc_assert (XVECLEN (op0, 0) == 1); |
|
new = force_const_mem (Pmode, orig); |
} |
|
/* Otherwise, compute the sum. */ |
else |
{ |
base = legitimize_pic_address (XEXP (addr, 0), reg); |
new = legitimize_pic_address (XEXP (addr, 1), |
base == reg ? NULL_RTX : reg); |
if (GET_CODE (new) == CONST_INT) |
new = plus_constant (base, INTVAL (new)); |
else |
{ |
if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1))) |
{ |
base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0)); |
new = XEXP (new, 1); |
} |
new = gen_rtx_PLUS (Pmode, base, new); |
} |
|
if (GET_CODE (new) == CONST) |
new = XEXP (new, 0); |
new = force_operand (new, 0); |
} |
} |
} |
return new; |
} |
|
/* Load the thread pointer into a register. */ |
|
rtx |
s390_get_thread_pointer (void) |
{ |
rtx tp = gen_reg_rtx (Pmode); |
|
emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM)); |
mark_reg_pointer (tp, BITS_PER_WORD); |
|
return tp; |
} |
|
/* Emit a tls call insn. The call target is the SYMBOL_REF stored |
in s390_tls_symbol which always refers to __tls_get_offset. |
The returned offset is written to RESULT_REG and an USE rtx is |
generated for TLS_CALL. */ |
|
static GTY(()) rtx s390_tls_symbol; |
|
static void |
s390_emit_tls_call_insn (rtx result_reg, rtx tls_call) |
{ |
rtx insn; |
|
gcc_assert (flag_pic); |
|
if (!s390_tls_symbol) |
s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset"); |
|
insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg, |
gen_rtx_REG (Pmode, RETURN_REGNUM)); |
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg); |
CONST_OR_PURE_CALL_P (insn) = 1; |
} |
|
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute |
this (thread-local) address. REG may be used as temporary. */ |
|
static rtx |
legitimize_tls_address (rtx addr, rtx reg) |
{ |
rtx new, tls_call, temp, base, r2, insn; |
|
if (GET_CODE (addr) == SYMBOL_REF) |
switch (tls_symbolic_operand (addr)) |
{ |
case TLS_MODEL_GLOBAL_DYNAMIC: |
start_sequence (); |
r2 = gen_rtx_REG (Pmode, 2); |
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD); |
new = gen_rtx_CONST (Pmode, tls_call); |
new = force_const_mem (Pmode, new); |
emit_move_insn (r2, new); |
s390_emit_tls_call_insn (r2, tls_call); |
insn = get_insns (); |
end_sequence (); |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); |
temp = gen_reg_rtx (Pmode); |
emit_libcall_block (insn, temp, r2, new); |
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
break; |
|
case TLS_MODEL_LOCAL_DYNAMIC: |
start_sequence (); |
r2 = gen_rtx_REG (Pmode, 2); |
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM); |
new = gen_rtx_CONST (Pmode, tls_call); |
new = force_const_mem (Pmode, new); |
emit_move_insn (r2, new); |
s390_emit_tls_call_insn (r2, tls_call); |
insn = get_insns (); |
end_sequence (); |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF); |
temp = gen_reg_rtx (Pmode); |
emit_libcall_block (insn, temp, r2, new); |
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); |
base = gen_reg_rtx (Pmode); |
s390_load_address (base, new); |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
new = force_const_mem (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
|
new = gen_rtx_PLUS (Pmode, base, temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
break; |
|
case TLS_MODEL_INITIAL_EXEC: |
if (flag_pic == 1) |
{ |
/* Assume GOT offset < 4k. This is handled the same way |
in both 31- and 64-bit code. */ |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); |
new = gen_const_mem (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
} |
else if (TARGET_CPU_ZARCH) |
{ |
/* If the GOT offset might be >= 4k, we determine the position |
of the GOT entry via a PC-relative LARL. */ |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
|
new = gen_const_mem (Pmode, temp); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
} |
else if (flag_pic) |
{ |
/* If the GOT offset might be >= 4k, we have to load it |
from the literal pool. */ |
|
if (reload_in_progress || reload_completed) |
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
new = force_const_mem (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); |
new = gen_const_mem (Pmode, new); |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD); |
temp = gen_reg_rtx (Pmode); |
emit_insn (gen_rtx_SET (Pmode, temp, new)); |
} |
else |
{ |
/* In position-dependent code, load the absolute address of |
the GOT entry from the literal pool. */ |
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
new = force_const_mem (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
|
new = temp; |
new = gen_const_mem (Pmode, new); |
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD); |
temp = gen_reg_rtx (Pmode); |
emit_insn (gen_rtx_SET (Pmode, temp, new)); |
} |
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
break; |
|
case TLS_MODEL_LOCAL_EXEC: |
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); |
new = gen_rtx_CONST (Pmode, new); |
new = force_const_mem (Pmode, new); |
temp = gen_reg_rtx (Pmode); |
emit_move_insn (temp, new); |
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); |
if (reg != 0) |
{ |
s390_load_address (reg, new); |
new = reg; |
} |
break; |
|
default: |
gcc_unreachable (); |
} |
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC) |
{ |
switch (XINT (XEXP (addr, 0), 1)) |
{ |
case UNSPEC_INDNTPOFF: |
gcc_assert (TARGET_CPU_ZARCH); |
new = addr; |
break; |
|
default: |
gcc_unreachable (); |
} |
} |
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) |
{ |
new = XEXP (XEXP (addr, 0), 0); |
if (GET_CODE (new) != SYMBOL_REF) |
new = gen_rtx_CONST (Pmode, new); |
|
new = legitimize_tls_address (new, reg); |
new = plus_constant (new, INTVAL (XEXP (XEXP (addr, 0), 1))); |
new = force_operand (new, 0); |
} |
|
else |
gcc_unreachable (); /* for now ... */ |
|
return new; |
} |
|
/* Emit insns to move operands[1] into operands[0]. */ |
|
void |
emit_symbolic_move (rtx *operands) |
{ |
rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); |
|
if (GET_CODE (operands[0]) == MEM) |
operands[1] = force_reg (Pmode, operands[1]); |
else if (TLS_SYMBOLIC_CONST (operands[1])) |
operands[1] = legitimize_tls_address (operands[1], temp); |
else if (flag_pic) |
operands[1] = legitimize_pic_address (operands[1], temp); |
} |
|
/* Try machine-dependent ways of modifying an illegitimate address X |
to be legitimate. If we find one, return the new, valid address. |
|
OLDX is the address as it was before break_out_memory_refs was called. |
In some cases it is useful to look at this to decide what needs to be done. |
|
MODE is the mode of the operand pointed to by X. |
|
When -fpic is used, special handling is needed for symbolic references. |
See comments by legitimize_pic_address for details. */ |
|
rtx |
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, |
enum machine_mode mode ATTRIBUTE_UNUSED) |
{ |
rtx constant_term = const0_rtx; |
|
if (TLS_SYMBOLIC_CONST (x)) |
{ |
x = legitimize_tls_address (x, 0); |
|
if (legitimate_address_p (mode, x, FALSE)) |
return x; |
} |
else if (GET_CODE (x) == PLUS |
&& (TLS_SYMBOLIC_CONST (XEXP (x, 0)) |
|| TLS_SYMBOLIC_CONST (XEXP (x, 1)))) |
{ |
return x; |
} |
else if (flag_pic) |
{ |
if (SYMBOLIC_CONST (x) |
|| (GET_CODE (x) == PLUS |
&& (SYMBOLIC_CONST (XEXP (x, 0)) |
|| SYMBOLIC_CONST (XEXP (x, 1))))) |
x = legitimize_pic_address (x, 0); |
|
if (legitimate_address_p (mode, x, FALSE)) |
return x; |
} |
|
x = eliminate_constant_term (x, &constant_term); |
|
/* Optimize loading of large displacements by splitting them |
into the multiple of 4K and the rest; this allows the |
former to be CSE'd if possible. |
|
Don't do this if the displacement is added to a register |
pointing into the stack frame, as the offsets will |
change later anyway. */ |
|
if (GET_CODE (constant_term) == CONST_INT |
&& !TARGET_LONG_DISPLACEMENT |
&& !DISP_IN_RANGE (INTVAL (constant_term)) |
&& !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x)))) |
{ |
HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff; |
HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower; |
|
rtx temp = gen_reg_rtx (Pmode); |
rtx val = force_operand (GEN_INT (upper), temp); |
if (val != temp) |
emit_move_insn (temp, val); |
|
x = gen_rtx_PLUS (Pmode, x, temp); |
constant_term = GEN_INT (lower); |
} |
|
if (GET_CODE (x) == PLUS) |
{ |
if (GET_CODE (XEXP (x, 0)) == REG) |
{ |
rtx temp = gen_reg_rtx (Pmode); |
rtx val = force_operand (XEXP (x, 1), temp); |
if (val != temp) |
emit_move_insn (temp, val); |
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp); |
} |
|
else if (GET_CODE (XEXP (x, 1)) == REG) |
{ |
rtx temp = gen_reg_rtx (Pmode); |
rtx val = force_operand (XEXP (x, 0), temp); |
if (val != temp) |
emit_move_insn (temp, val); |
|
x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1)); |
} |
} |
|
if (constant_term != const0_rtx) |
x = gen_rtx_PLUS (Pmode, x, constant_term); |
|
return x; |
} |
|
/* Try a machine-dependent way of reloading an illegitimate address AD |
operand. If we find one, push the reload and and return the new address. |
|
MODE is the mode of the enclosing MEM. OPNUM is the operand number |
and TYPE is the reload type of the current reload. */ |
|
rtx |
legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED, |
int opnum, int type) |
{ |
if (!optimize || TARGET_LONG_DISPLACEMENT) |
return NULL_RTX; |
|
if (GET_CODE (ad) == PLUS) |
{ |
rtx tem = simplify_binary_operation (PLUS, Pmode, |
XEXP (ad, 0), XEXP (ad, 1)); |
if (tem) |
ad = tem; |
} |
|
if (GET_CODE (ad) == PLUS |
&& GET_CODE (XEXP (ad, 0)) == REG |
&& GET_CODE (XEXP (ad, 1)) == CONST_INT |
&& !DISP_IN_RANGE (INTVAL (XEXP (ad, 1)))) |
{ |
HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff; |
HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower; |
rtx cst, tem, new; |
|
cst = GEN_INT (upper); |
if (!legitimate_reload_constant_p (cst)) |
cst = force_const_mem (Pmode, cst); |
|
tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst); |
new = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower)); |
|
push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0, |
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
opnum, (enum reload_type) type); |
return new; |
} |
|
return NULL_RTX; |
} |
|
/* Emit code to move LEN bytes from DST to SRC. */ |
|
void |
s390_expand_movmem (rtx dst, rtx src, rtx len) |
{ |
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) |
{ |
if (INTVAL (len) > 0) |
emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1))); |
} |
|
else if (TARGET_MVCLE) |
{ |
emit_insn (gen_movmem_long (dst, src, convert_to_mode (Pmode, len, 1))); |
} |
|
else |
{ |
rtx dst_addr, src_addr, count, blocks, temp; |
rtx loop_start_label = gen_label_rtx (); |
rtx loop_end_label = gen_label_rtx (); |
rtx end_label = gen_label_rtx (); |
enum machine_mode mode; |
|
mode = GET_MODE (len); |
if (mode == VOIDmode) |
mode = Pmode; |
|
dst_addr = gen_reg_rtx (Pmode); |
src_addr = gen_reg_rtx (Pmode); |
count = gen_reg_rtx (mode); |
blocks = gen_reg_rtx (mode); |
|
convert_move (count, len, 1); |
emit_cmp_and_jump_insns (count, const0_rtx, |
EQ, NULL_RTX, mode, 1, end_label); |
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); |
emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX)); |
dst = change_address (dst, VOIDmode, dst_addr); |
src = change_address (src, VOIDmode, src_addr); |
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); |
if (temp != count) |
emit_move_insn (count, temp); |
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_label (loop_start_label); |
|
emit_insn (gen_movmem_short (dst, src, GEN_INT (255))); |
s390_load_address (dst_addr, |
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); |
s390_load_address (src_addr, |
gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256))); |
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_jump (loop_start_label); |
emit_label (loop_end_label); |
|
emit_insn (gen_movmem_short (dst, src, |
convert_to_mode (Pmode, count, 1))); |
emit_label (end_label); |
} |
} |
|
/* Emit code to set LEN bytes at DST to VAL. |
Make use of clrmem if VAL is zero. */ |
|
void |
s390_expand_setmem (rtx dst, rtx len, rtx val) |
{ |
if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0) |
return; |
|
gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode); |
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257) |
{ |
if (val == const0_rtx && INTVAL (len) <= 256) |
emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1))); |
else |
{ |
/* Initialize memory by storing the first byte. */ |
emit_move_insn (adjust_address (dst, QImode, 0), val); |
|
if (INTVAL (len) > 1) |
{ |
/* Initiate 1 byte overlap move. |
The first byte of DST is propagated through DSTP1. |
Prepare a movmem for: DST+1 = DST (length = LEN - 1). |
DST is set to size 1 so the rest of the memory location |
does not count as source operand. */ |
rtx dstp1 = adjust_address (dst, VOIDmode, 1); |
set_mem_size (dst, const1_rtx); |
|
emit_insn (gen_movmem_short (dstp1, dst, |
GEN_INT (INTVAL (len) - 2))); |
} |
} |
} |
|
else if (TARGET_MVCLE) |
{ |
val = force_not_mem (convert_modes (Pmode, QImode, val, 1)); |
emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val)); |
} |
|
else |
{ |
rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX; |
rtx loop_start_label = gen_label_rtx (); |
rtx loop_end_label = gen_label_rtx (); |
rtx end_label = gen_label_rtx (); |
enum machine_mode mode; |
|
mode = GET_MODE (len); |
if (mode == VOIDmode) |
mode = Pmode; |
|
dst_addr = gen_reg_rtx (Pmode); |
src_addr = gen_reg_rtx (Pmode); |
count = gen_reg_rtx (mode); |
blocks = gen_reg_rtx (mode); |
|
convert_move (count, len, 1); |
emit_cmp_and_jump_insns (count, const0_rtx, |
EQ, NULL_RTX, mode, 1, end_label); |
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); |
dst = change_address (dst, VOIDmode, dst_addr); |
|
if (val == const0_rtx) |
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); |
else |
{ |
dstp1 = adjust_address (dst, VOIDmode, 1); |
set_mem_size (dst, const1_rtx); |
|
/* Initialize memory by storing the first byte. */ |
emit_move_insn (adjust_address (dst, QImode, 0), val); |
|
/* If count is 1 we are done. */ |
emit_cmp_and_jump_insns (count, const1_rtx, |
EQ, NULL_RTX, mode, 1, end_label); |
|
temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, 0); |
} |
if (temp != count) |
emit_move_insn (count, temp); |
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_label (loop_start_label); |
|
if (val == const0_rtx) |
emit_insn (gen_clrmem_short (dst, GEN_INT (255))); |
else |
emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255))); |
s390_load_address (dst_addr, |
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); |
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_jump (loop_start_label); |
emit_label (loop_end_label); |
|
if (val == const0_rtx) |
emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1))); |
else |
emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1))); |
emit_label (end_label); |
} |
} |
|
/* Emit code to compare LEN bytes at OP0 with those at OP1, |
and return the result in TARGET. */ |
|
void |
s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len) |
{ |
rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM); |
rtx tmp; |
|
/* As the result of CMPINT is inverted compared to what we need, |
we have to swap the operands. */ |
tmp = op0; op0 = op1; op1 = tmp; |
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) |
{ |
if (INTVAL (len) > 0) |
{ |
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1))); |
emit_insn (gen_cmpint (target, ccreg)); |
} |
else |
emit_move_insn (target, const0_rtx); |
} |
else if (TARGET_MVCLE) |
{ |
emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1))); |
emit_insn (gen_cmpint (target, ccreg)); |
} |
else |
{ |
rtx addr0, addr1, count, blocks, temp; |
rtx loop_start_label = gen_label_rtx (); |
rtx loop_end_label = gen_label_rtx (); |
rtx end_label = gen_label_rtx (); |
enum machine_mode mode; |
|
mode = GET_MODE (len); |
if (mode == VOIDmode) |
mode = Pmode; |
|
addr0 = gen_reg_rtx (Pmode); |
addr1 = gen_reg_rtx (Pmode); |
count = gen_reg_rtx (mode); |
blocks = gen_reg_rtx (mode); |
|
convert_move (count, len, 1); |
emit_cmp_and_jump_insns (count, const0_rtx, |
EQ, NULL_RTX, mode, 1, end_label); |
|
emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX)); |
emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); |
op0 = change_address (op0, VOIDmode, addr0); |
op1 = change_address (op1, VOIDmode, addr1); |
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); |
if (temp != count) |
emit_move_insn (count, temp); |
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_label (loop_start_label); |
|
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255))); |
temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx); |
temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp, |
gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx); |
temp = gen_rtx_SET (VOIDmode, pc_rtx, temp); |
emit_jump_insn (temp); |
|
s390_load_address (addr0, |
gen_rtx_PLUS (Pmode, addr0, GEN_INT (256))); |
s390_load_address (addr1, |
gen_rtx_PLUS (Pmode, addr1, GEN_INT (256))); |
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); |
if (temp != blocks) |
emit_move_insn (blocks, temp); |
|
emit_cmp_and_jump_insns (blocks, const0_rtx, |
EQ, NULL_RTX, mode, 1, loop_end_label); |
|
emit_jump (loop_start_label); |
emit_label (loop_end_label); |
|
emit_insn (gen_cmpmem_short (op0, op1, |
convert_to_mode (Pmode, count, 1))); |
emit_label (end_label); |
|
emit_insn (gen_cmpint (target, ccreg)); |
} |
} |
|
|
/* Expand conditional increment or decrement using alc/slb instructions. |
Should generate code setting DST to either SRC or SRC + INCREMENT, |
depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1. |
Returns true if successful, false otherwise. |
|
That makes it possible to implement some if-constructs without jumps e.g.: |
(borrow = CC0 | CC1 and carry = CC2 | CC3) |
unsigned int a, b, c; |
if (a < b) c++; -> CCU b > a -> CC2; c += carry; |
if (a < b) c--; -> CCL3 a - b -> borrow; c -= borrow; |
if (a <= b) c++; -> CCL3 b - a -> borrow; c += carry; |
if (a <= b) c--; -> CCU a <= b -> borrow; c -= borrow; |
|
Checks for EQ and NE with a nonzero value need an additional xor e.g.: |
if (a == b) c++; -> CCL3 a ^= b; 0 - a -> borrow; c += carry; |
if (a == b) c--; -> CCU a ^= b; a <= 0 -> CC0 | CC1; c -= borrow; |
if (a != b) c++; -> CCU a ^= b; a > 0 -> CC2; c += carry; |
if (a != b) c--; -> CCL3 a ^= b; 0 - a -> borrow; c -= borrow; */ |
|
bool |
s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1, |
rtx dst, rtx src, rtx increment) |
{ |
enum machine_mode cmp_mode; |
enum machine_mode cc_mode; |
rtx op_res; |
rtx insn; |
rtvec p; |
int ret; |
|
if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode) |
&& (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode)) |
cmp_mode = SImode; |
else if ((GET_MODE (cmp_op0) == DImode || GET_MODE (cmp_op0) == VOIDmode) |
&& (GET_MODE (cmp_op1) == DImode || GET_MODE (cmp_op1) == VOIDmode)) |
cmp_mode = DImode; |
else |
return false; |
|
/* Try ADD LOGICAL WITH CARRY. */ |
if (increment == const1_rtx) |
{ |
/* Determine CC mode to use. */ |
if (cmp_code == EQ || cmp_code == NE) |
{ |
if (cmp_op1 != const0_rtx) |
{ |
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1, |
NULL_RTX, 0, OPTAB_WIDEN); |
cmp_op1 = const0_rtx; |
} |
|
cmp_code = cmp_code == EQ ? LEU : GTU; |
} |
|
if (cmp_code == LTU || cmp_code == LEU) |
{ |
rtx tem = cmp_op0; |
cmp_op0 = cmp_op1; |
cmp_op1 = tem; |
cmp_code = swap_condition (cmp_code); |
} |
|
switch (cmp_code) |
{ |
case GTU: |
cc_mode = CCUmode; |
break; |
|
case GEU: |
cc_mode = CCL3mode; |
break; |
|
default: |
return false; |
} |
|
/* Emit comparison instruction pattern. */ |
if (!register_operand (cmp_op0, cmp_mode)) |
cmp_op0 = force_reg (cmp_mode, cmp_op0); |
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM), |
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1)); |
/* We use insn_invalid_p here to add clobbers if required. */ |
ret = insn_invalid_p (emit_insn (insn)); |
gcc_assert (!ret); |
|
/* Emit ALC instruction pattern. */ |
op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), |
gen_rtx_REG (cc_mode, CC_REGNUM), |
const0_rtx); |
|
if (src != const0_rtx) |
{ |
if (!register_operand (src, GET_MODE (dst))) |
src = force_reg (GET_MODE (dst), src); |
|
src = gen_rtx_PLUS (GET_MODE (dst), src, const0_rtx); |
op_res = gen_rtx_PLUS (GET_MODE (dst), src, op_res); |
} |
|
p = rtvec_alloc (2); |
RTVEC_ELT (p, 0) = |
gen_rtx_SET (VOIDmode, dst, op_res); |
RTVEC_ELT (p, 1) = |
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); |
emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
|
return true; |
} |
|
/* Try SUBTRACT LOGICAL WITH BORROW. */ |
if (increment == constm1_rtx) |
{ |
/* Determine CC mode to use. */ |
if (cmp_code == EQ || cmp_code == NE) |
{ |
if (cmp_op1 != const0_rtx) |
{ |
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1, |
NULL_RTX, 0, OPTAB_WIDEN); |
cmp_op1 = const0_rtx; |
} |
|
cmp_code = cmp_code == EQ ? LEU : GTU; |
} |
|
if (cmp_code == GTU || cmp_code == GEU) |
{ |
rtx tem = cmp_op0; |
cmp_op0 = cmp_op1; |
cmp_op1 = tem; |
cmp_code = swap_condition (cmp_code); |
} |
|
switch (cmp_code) |
{ |
case LEU: |
cc_mode = CCUmode; |
break; |
|
case LTU: |
cc_mode = CCL3mode; |
break; |
|
default: |
return false; |
} |
|
/* Emit comparison instruction pattern. */ |
if (!register_operand (cmp_op0, cmp_mode)) |
cmp_op0 = force_reg (cmp_mode, cmp_op0); |
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM), |
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1)); |
/* We use insn_invalid_p here to add clobbers if required. */ |
ret = insn_invalid_p (emit_insn (insn)); |
gcc_assert (!ret); |
|
/* Emit SLB instruction pattern. */ |
if (!register_operand (src, GET_MODE (dst))) |
src = force_reg (GET_MODE (dst), src); |
|
op_res = gen_rtx_MINUS (GET_MODE (dst), |
gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx), |
gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), |
gen_rtx_REG (cc_mode, CC_REGNUM), |
const0_rtx)); |
p = rtvec_alloc (2); |
RTVEC_ELT (p, 0) = |
gen_rtx_SET (VOIDmode, dst, op_res); |
RTVEC_ELT (p, 1) = |
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); |
emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
|
return true; |
} |
|
return false; |
} |
|
/* Expand code for the insv template. Return true if successful, false else. */ |
|
bool |
s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src) |
{ |
int bitsize = INTVAL (op1); |
int bitpos = INTVAL (op2); |
|
/* We need byte alignment. */ |
if (bitsize % BITS_PER_UNIT) |
return false; |
|
if (bitpos == 0 |
&& memory_operand (dest, VOIDmode) |
&& (register_operand (src, word_mode) |
|| const_int_operand (src, VOIDmode))) |
{ |
/* Emit standard pattern if possible. */ |
enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT); |
if (GET_MODE_BITSIZE (mode) == bitsize) |
emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src)); |
|
/* (set (ze (mem)) (const_int)). */ |
else if (const_int_operand (src, VOIDmode)) |
{ |
int size = bitsize / BITS_PER_UNIT; |
rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode, |
GET_MODE_SIZE (word_mode) - size); |
|
dest = adjust_address (dest, BLKmode, 0); |
set_mem_size (dest, GEN_INT (size)); |
s390_expand_movmem (dest, src_mem, GEN_INT (size)); |
} |
|
/* (set (ze (mem)) (reg)). */ |
else if (register_operand (src, word_mode)) |
{ |
if (bitsize <= GET_MODE_BITSIZE (SImode)) |
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1, |
const0_rtx), src); |
else |
{ |
/* Emit st,stcmh sequence. */ |
int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode); |
int size = stcmh_width / BITS_PER_UNIT; |
|
emit_move_insn (adjust_address (dest, SImode, size), |
gen_lowpart (SImode, src)); |
set_mem_size (dest, GEN_INT (size)); |
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT |
(stcmh_width), const0_rtx), |
gen_rtx_LSHIFTRT (word_mode, src, GEN_INT |
(GET_MODE_BITSIZE (SImode)))); |
} |
} |
else |
return false; |
|
return true; |
} |
|
/* (set (ze (reg)) (const_int)). */ |
if (TARGET_ZARCH |
&& register_operand (dest, word_mode) |
&& (bitpos % 16) == 0 |
&& (bitsize % 16) == 0 |
&& const_int_operand (src, VOIDmode)) |
{ |
HOST_WIDE_INT val = INTVAL (src); |
int regpos = bitpos + bitsize; |
|
while (regpos > bitpos) |
{ |
enum machine_mode putmode; |
int putsize; |
|
if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32)) |
putmode = SImode; |
else |
putmode = HImode; |
|
putsize = GET_MODE_BITSIZE (putmode); |
regpos -= putsize; |
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, |
GEN_INT (putsize), |
GEN_INT (regpos)), |
gen_int_mode (val, putmode)); |
val >>= putsize; |
} |
gcc_assert (regpos == bitpos); |
return true; |
} |
|
return false; |
} |
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a |
register that holds VAL of mode MODE shifted by COUNT bits. */ |
|
static inline rtx |
s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count) |
{ |
val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)), |
NULL_RTX, 1, OPTAB_DIRECT); |
return expand_simple_binop (SImode, ASHIFT, val, count, |
NULL_RTX, 1, OPTAB_DIRECT); |
} |
|
/* Structure to hold the initial parameters for a compare_and_swap operation |
in HImode and QImode. */ |
|
struct alignment_context |
{ |
rtx memsi; /* SI aligned memory location. */ |
rtx shift; /* Bit offset with regard to lsb. */ |
rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */ |
rtx modemaski; /* ~modemask */ |
bool aligned; /* True if memory is aligned, false else. */ |
}; |
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize |
structure AC for transparent simplifying, if the memory alignment is known |
to be at least 32bit. MEM is the memory location for the actual operation |
and MODE its mode. */ |
|
static void |
init_alignment_context (struct alignment_context *ac, rtx mem, |
enum machine_mode mode) |
{ |
ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode)); |
ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode)); |
|
if (ac->aligned) |
ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */ |
else |
{ |
/* Alignment is unknown. */ |
rtx byteoffset, addr, align; |
|
/* Force the address into a register. */ |
addr = force_reg (Pmode, XEXP (mem, 0)); |
|
/* Align it to SImode. */ |
align = expand_simple_binop (Pmode, AND, addr, |
GEN_INT (-GET_MODE_SIZE (SImode)), |
NULL_RTX, 1, OPTAB_DIRECT); |
/* Generate MEM. */ |
ac->memsi = gen_rtx_MEM (SImode, align); |
MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem); |
set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER); |
set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode)); |
|
/* Calculate shiftcount. */ |
byteoffset = expand_simple_binop (Pmode, AND, addr, |
GEN_INT (GET_MODE_SIZE (SImode) - 1), |
NULL_RTX, 1, OPTAB_DIRECT); |
/* As we already have some offset, evaluate the remaining distance. */ |
ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset, |
NULL_RTX, 1, OPTAB_DIRECT); |
|
} |
/* Shift is the byte count, but we need the bitcount. */ |
ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT), |
NULL_RTX, 1, OPTAB_DIRECT); |
/* Calculate masks. */ |
ac->modemask = expand_simple_binop (SImode, ASHIFT, |
GEN_INT (GET_MODE_MASK (mode)), ac->shift, |
NULL_RTX, 1, OPTAB_DIRECT); |
ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1); |
} |
|
/* Expand an atomic compare and swap operation for HImode and QImode. MEM is |
the memory location, CMP the old value to compare MEM with and NEW the value |
to set if CMP == MEM. |
CMP is never in memory for compare_and_swap_cc because |
expand_bool_compare_and_swap puts it into a register for later compare. */ |
|
void |
s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new) |
{ |
struct alignment_context ac; |
rtx cmpv, newv, val, resv, cc; |
rtx res = gen_reg_rtx (SImode); |
rtx csloop = gen_label_rtx (); |
rtx csend = gen_label_rtx (); |
|
gcc_assert (register_operand (target, VOIDmode)); |
gcc_assert (MEM_P (mem)); |
|
init_alignment_context (&ac, mem, mode); |
|
/* Shift the values to the correct bit positions. */ |
if (!(ac.aligned && MEM_P (cmp))) |
cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift); |
if (!(ac.aligned && MEM_P (new))) |
new = s390_expand_mask_and_shift (new, mode, ac.shift); |
|
/* Load full word. Subsequent loads are performed by CS. */ |
val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski, |
NULL_RTX, 1, OPTAB_DIRECT); |
|
/* Start CS loop. */ |
emit_label (csloop); |
/* val = "<mem>00..0<mem>" |
* cmp = "00..0<cmp>00..0" |
* new = "00..0<new>00..0" |
*/ |
|
/* Patch cmp and new with val at correct position. */ |
if (ac.aligned && MEM_P (cmp)) |
{ |
cmpv = force_reg (SImode, val); |
store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp); |
} |
else |
cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val, |
NULL_RTX, 1, OPTAB_DIRECT)); |
if (ac.aligned && MEM_P (new)) |
{ |
newv = force_reg (SImode, val); |
store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new); |
} |
else |
newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val, |
NULL_RTX, 1, OPTAB_DIRECT)); |
|
/* Jump to end if we're done (likely?). */ |
s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi, |
cmpv, newv)); |
|
/* Check for changes outside mode. */ |
resv = expand_simple_binop (SImode, AND, res, ac.modemaski, |
NULL_RTX, 1, OPTAB_DIRECT); |
cc = s390_emit_compare (NE, resv, val); |
emit_move_insn (val, resv); |
/* Loop internal if so. */ |
s390_emit_jump (csloop, cc); |
|
emit_label (csend); |
|
/* Return the correct part of the bitfield. */ |
convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift, |
NULL_RTX, 1, OPTAB_DIRECT), 1); |
} |
|
/* Expand an atomic operation CODE of mode MODE. MEM is the memory location |
and VAL the value to play with. If AFTER is true then store the the value |
MEM holds after the operation, if AFTER is false then store the value MEM |
holds before the operation. If TARGET is zero then discard that value, else |
store it to TARGET. */ |
|
void |
s390_expand_atomic (enum machine_mode mode, enum rtx_code code, |
rtx target, rtx mem, rtx val, bool after) |
{ |
struct alignment_context ac; |
rtx cmp; |
rtx new = gen_reg_rtx (SImode); |
rtx orig = gen_reg_rtx (SImode); |
rtx csloop = gen_label_rtx (); |
|
gcc_assert (!target || register_operand (target, VOIDmode)); |
gcc_assert (MEM_P (mem)); |
|
init_alignment_context (&ac, mem, mode); |
|
/* Shift val to the correct bit positions. |
Preserve "icm", but prevent "ex icm". */ |
if (!(ac.aligned && code == SET && MEM_P (val))) |
val = s390_expand_mask_and_shift (val, mode, ac.shift); |
|
/* Further preparation insns. */ |
if (code == PLUS || code == MINUS) |
emit_move_insn (orig, val); |
else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */ |
val = expand_simple_binop (SImode, XOR, val, ac.modemaski, |
NULL_RTX, 1, OPTAB_DIRECT); |
|
/* Load full word. Subsequent loads are performed by CS. */ |
cmp = force_reg (SImode, ac.memsi); |
|
/* Start CS loop. */ |
emit_label (csloop); |
emit_move_insn (new, cmp); |
|
/* Patch new with val at correct position. */ |
switch (code) |
{ |
case PLUS: |
case MINUS: |
val = expand_simple_binop (SImode, code, new, orig, |
NULL_RTX, 1, OPTAB_DIRECT); |
val = expand_simple_binop (SImode, AND, val, ac.modemask, |
NULL_RTX, 1, OPTAB_DIRECT); |
/* FALLTHRU */ |
case SET: |
if (ac.aligned && MEM_P (val)) |
store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val); |
else |
{ |
new = expand_simple_binop (SImode, AND, new, ac.modemaski, |
NULL_RTX, 1, OPTAB_DIRECT); |
new = expand_simple_binop (SImode, IOR, new, val, |
NULL_RTX, 1, OPTAB_DIRECT); |
} |
break; |
case AND: |
case IOR: |
case XOR: |
new = expand_simple_binop (SImode, code, new, val, |
NULL_RTX, 1, OPTAB_DIRECT); |
break; |
case MULT: /* NAND */ |
new = expand_simple_binop (SImode, XOR, new, ac.modemask, |
NULL_RTX, 1, OPTAB_DIRECT); |
new = expand_simple_binop (SImode, AND, new, val, |
NULL_RTX, 1, OPTAB_DIRECT); |
break; |
default: |
gcc_unreachable (); |
} |
|
s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp, |
ac.memsi, cmp, new)); |
|
/* Return the correct part of the bitfield. */ |
if (target) |
convert_move (target, expand_simple_binop (SImode, LSHIFTRT, |
after ? new : cmp, ac.shift, |
NULL_RTX, 1, OPTAB_DIRECT), 1); |
} |
|
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL. |
We need to emit DTP-relative relocations. */ |
|
static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED; |
|
static void |
s390_output_dwarf_dtprel (FILE *file, int size, rtx x) |
{ |
switch (size) |
{ |
case 4: |
fputs ("\t.long\t", file); |
break; |
case 8: |
fputs ("\t.quad\t", file); |
break; |
default: |
gcc_unreachable (); |
} |
output_addr_const (file, x); |
fputs ("@DTPOFF", file); |
} |
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING |
/* Implement TARGET_MANGLE_FUNDAMENTAL_TYPE. */ |
|
static const char * |
s390_mangle_fundamental_type (tree type) |
{ |
if (TYPE_MAIN_VARIANT (type) == long_double_type_node |
&& TARGET_LONG_DOUBLE_128) |
return "g"; |
|
/* For all other types, use normal C++ mangling. */ |
return NULL; |
} |
#endif |
|
/* In the name of slightly smaller debug output, and to cater to |
general assembler lossage, recognize various UNSPEC sequences |
and turn them back into a direct symbol reference. */ |
|
static rtx |
s390_delegitimize_address (rtx orig_x) |
{ |
rtx x = orig_x, y; |
|
if (GET_CODE (x) != MEM) |
return orig_x; |
|
x = XEXP (x, 0); |
if (GET_CODE (x) == PLUS |
&& GET_CODE (XEXP (x, 1)) == CONST |
&& GET_CODE (XEXP (x, 0)) == REG |
&& REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM) |
{ |
y = XEXP (XEXP (x, 1), 0); |
if (GET_CODE (y) == UNSPEC |
&& XINT (y, 1) == UNSPEC_GOT) |
return XVECEXP (y, 0, 0); |
return orig_x; |
} |
|
if (GET_CODE (x) == CONST) |
{ |
y = XEXP (x, 0); |
if (GET_CODE (y) == UNSPEC |
&& XINT (y, 1) == UNSPEC_GOTENT) |
return XVECEXP (y, 0, 0); |
return orig_x; |
} |
|
return orig_x; |
} |
|
/* Output operand OP to stdio stream FILE. |
OP is an address (register + offset) which is not used to address data; |
instead the rightmost bits are interpreted as the value. */ |
|
static void |
print_shift_count_operand (FILE *file, rtx op) |
{ |
HOST_WIDE_INT offset; |
rtx base; |
|
/* Extract base register and offset. */ |
if (!s390_decompose_shift_count (op, &base, &offset)) |
gcc_unreachable (); |
|
/* Sanity check. */ |
if (base) |
{ |
gcc_assert (GET_CODE (base) == REG); |
gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER); |
gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS); |
} |
|
/* Offsets are constricted to twelve bits. */ |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1)); |
if (base) |
fprintf (file, "(%s)", reg_names[REGNO (base)]); |
} |
|
/* See 'get_some_local_dynamic_name'. */ |
|
static int |
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) |
{ |
rtx x = *px; |
|
if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) |
{ |
x = get_pool_constant (x); |
return for_each_rtx (&x, get_some_local_dynamic_name_1, 0); |
} |
|
if (GET_CODE (x) == SYMBOL_REF |
&& tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC) |
{ |
cfun->machine->some_ld_name = XSTR (x, 0); |
return 1; |
} |
|
return 0; |
} |
|
/* Locate some local-dynamic symbol still in use by this function |
so that we can print its name in local-dynamic base patterns. */ |
|
static const char * |
get_some_local_dynamic_name (void) |
{ |
rtx insn; |
|
if (cfun->machine->some_ld_name) |
return cfun->machine->some_ld_name; |
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) |
if (INSN_P (insn) |
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) |
return cfun->machine->some_ld_name; |
|
gcc_unreachable (); |
} |
|
/* Output machine-dependent UNSPECs occurring in address constant X |
in assembler syntax to stdio stream FILE. Returns true if the |
constant X could be recognized, false otherwise. */ |
|
bool |
s390_output_addr_const_extra (FILE *file, rtx x) |
{ |
if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1) |
switch (XINT (x, 1)) |
{ |
case UNSPEC_GOTENT: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@GOTENT"); |
return true; |
case UNSPEC_GOT: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@GOT"); |
return true; |
case UNSPEC_GOTOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@GOTOFF"); |
return true; |
case UNSPEC_PLT: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@PLT"); |
return true; |
case UNSPEC_PLTOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@PLTOFF"); |
return true; |
case UNSPEC_TLSGD: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@TLSGD"); |
return true; |
case UNSPEC_TLSLDM: |
assemble_name (file, get_some_local_dynamic_name ()); |
fprintf (file, "@TLSLDM"); |
return true; |
case UNSPEC_DTPOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@DTPOFF"); |
return true; |
case UNSPEC_NTPOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@NTPOFF"); |
return true; |
case UNSPEC_GOTNTPOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@GOTNTPOFF"); |
return true; |
case UNSPEC_INDNTPOFF: |
output_addr_const (file, XVECEXP (x, 0, 0)); |
fprintf (file, "@INDNTPOFF"); |
return true; |
} |
|
return false; |
} |
|
/* Output address operand ADDR in assembler syntax to |
stdio stream FILE. */ |
|
void |
print_operand_address (FILE *file, rtx addr) |
{ |
struct s390_address ad; |
|
if (!s390_decompose_address (addr, &ad) |
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) |
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) |
output_operand_lossage ("cannot decompose address"); |
|
if (ad.disp) |
output_addr_const (file, ad.disp); |
else |
fprintf (file, "0"); |
|
if (ad.base && ad.indx) |
fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)], |
reg_names[REGNO (ad.base)]); |
else if (ad.base) |
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]); |
} |
|
/* Output operand X in assembler syntax to stdio stream FILE. |
CODE specified the format flag. The following format flags |
are recognized: |
|
'C': print opcode suffix for branch condition. |
'D': print opcode suffix for inverse branch condition. |
'J': print tls_load/tls_gdcall/tls_ldcall suffix |
'G': print the size of the operand in bytes. |
'O': print only the displacement of a memory reference. |
'R': print only the base register of a memory reference. |
'S': print S-type memory reference (base+displacement). |
'N': print the second word of a DImode operand. |
'M': print the second word of a TImode operand. |
'Y': print shift count operand. |
|
'b': print integer X as if it's an unsigned byte. |
'x': print integer X as if it's an unsigned halfword. |
'h': print integer X as if it's a signed halfword. |
'i': print the first nonzero HImode part of X. |
'j': print the first HImode part unequal to -1 of X. |
'k': print the first nonzero SImode part of X. |
'm': print the first SImode part unequal to -1 of X. |
'o': print integer X as if it's an unsigned 32bit word. */ |
|
void |
print_operand (FILE *file, rtx x, int code) |
{ |
switch (code) |
{ |
case 'C': |
fprintf (file, s390_branch_condition_mnemonic (x, FALSE)); |
return; |
|
case 'D': |
fprintf (file, s390_branch_condition_mnemonic (x, TRUE)); |
return; |
|
case 'J': |
if (GET_CODE (x) == SYMBOL_REF) |
{ |
fprintf (file, "%s", ":tls_load:"); |
output_addr_const (file, x); |
} |
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD) |
{ |
fprintf (file, "%s", ":tls_gdcall:"); |
output_addr_const (file, XVECEXP (x, 0, 0)); |
} |
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM) |
{ |
fprintf (file, "%s", ":tls_ldcall:"); |
assemble_name (file, get_some_local_dynamic_name ()); |
} |
else |
gcc_unreachable (); |
return; |
|
case 'G': |
fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x))); |
return; |
|
case 'O': |
{ |
struct s390_address ad; |
int ret; |
|
gcc_assert (GET_CODE (x) == MEM); |
ret = s390_decompose_address (XEXP (x, 0), &ad); |
gcc_assert (ret); |
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); |
gcc_assert (!ad.indx); |
|
if (ad.disp) |
output_addr_const (file, ad.disp); |
else |
fprintf (file, "0"); |
} |
return; |
|
case 'R': |
{ |
struct s390_address ad; |
int ret; |
|
gcc_assert (GET_CODE (x) == MEM); |
ret = s390_decompose_address (XEXP (x, 0), &ad); |
gcc_assert (ret); |
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); |
gcc_assert (!ad.indx); |
|
if (ad.base) |
fprintf (file, "%s", reg_names[REGNO (ad.base)]); |
else |
fprintf (file, "0"); |
} |
return; |
|
case 'S': |
{ |
struct s390_address ad; |
int ret; |
|
gcc_assert (GET_CODE (x) == MEM); |
ret = s390_decompose_address (XEXP (x, 0), &ad); |
gcc_assert (ret); |
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base))); |
gcc_assert (!ad.indx); |
|
if (ad.disp) |
output_addr_const (file, ad.disp); |
else |
fprintf (file, "0"); |
|
if (ad.base) |
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]); |
} |
return; |
|
case 'N': |
if (GET_CODE (x) == REG) |
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1); |
else if (GET_CODE (x) == MEM) |
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4)); |
else |
gcc_unreachable (); |
break; |
|
case 'M': |
if (GET_CODE (x) == REG) |
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1); |
else if (GET_CODE (x) == MEM) |
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8)); |
else |
gcc_unreachable (); |
break; |
|
case 'Y': |
print_shift_count_operand (file, x); |
return; |
} |
|
switch (GET_CODE (x)) |
{ |
case REG: |
fprintf (file, "%s", reg_names[REGNO (x)]); |
break; |
|
case MEM: |
output_address (XEXP (x, 0)); |
break; |
|
case CONST: |
case CODE_LABEL: |
case LABEL_REF: |
case SYMBOL_REF: |
output_addr_const (file, x); |
break; |
|
case CONST_INT: |
if (code == 'b') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff); |
else if (code == 'x') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff); |
else if (code == 'h') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000); |
else if (code == 'i') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, |
s390_extract_part (x, HImode, 0)); |
else if (code == 'j') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, |
s390_extract_part (x, HImode, -1)); |
else if (code == 'k') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, |
s390_extract_part (x, SImode, 0)); |
else if (code == 'm') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, |
s390_extract_part (x, SImode, -1)); |
else if (code == 'o') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff); |
else |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); |
break; |
|
case CONST_DOUBLE: |
gcc_assert (GET_MODE (x) == VOIDmode); |
if (code == 'b') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff); |
else if (code == 'x') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff); |
else if (code == 'h') |
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000); |
else |
gcc_unreachable (); |
break; |
|
default: |
fatal_insn ("UNKNOWN in print_operand !?", x); |
break; |
} |
} |
|
/* Target hook for assembling integer objects. We need to define it |
here to work a round a bug in some versions of GAS, which couldn't |
handle values smaller than INT_MIN when printed in decimal. */ |
|
static bool |
s390_assemble_integer (rtx x, unsigned int size, int aligned_p) |
{ |
if (size == 8 && aligned_p |
&& GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN) |
{ |
fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n", |
INTVAL (x)); |
return true; |
} |
return default_assemble_integer (x, size, aligned_p); |
} |
|
/* Returns true if register REGNO is used for forming |
a memory address in expression X. */ |
|
static bool |
reg_used_in_mem_p (int regno, rtx x) |
{ |
enum rtx_code code = GET_CODE (x); |
int i, j; |
const char *fmt; |
|
if (code == MEM) |
{ |
if (refers_to_regno_p (regno, regno+1, |
XEXP (x, 0), 0)) |
return true; |
} |
else if (code == SET |
&& GET_CODE (SET_DEST (x)) == PC) |
{ |
if (refers_to_regno_p (regno, regno+1, |
SET_SRC (x), 0)) |
return true; |
} |
|
fmt = GET_RTX_FORMAT (code); |
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e' |
&& reg_used_in_mem_p (regno, XEXP (x, i))) |
return true; |
|
else if (fmt[i] == 'E') |
for (j = 0; j < XVECLEN (x, i); j++) |
if (reg_used_in_mem_p (regno, XVECEXP (x, i, j))) |
return true; |
} |
return false; |
} |
|
/* Returns true if expression DEP_RTX sets an address register |
used by instruction INSN to address memory. */ |
|
static bool |
addr_generation_dependency_p (rtx dep_rtx, rtx insn) |
{ |
rtx target, pat; |
|
if (GET_CODE (dep_rtx) == INSN) |
dep_rtx = PATTERN (dep_rtx); |
|
if (GET_CODE (dep_rtx) == SET) |
{ |
target = SET_DEST (dep_rtx); |
if (GET_CODE (target) == STRICT_LOW_PART) |
target = XEXP (target, 0); |
while (GET_CODE (target) == SUBREG) |
target = SUBREG_REG (target); |
|
if (GET_CODE (target) == REG) |
{ |
int regno = REGNO (target); |
|
if (s390_safe_attr_type (insn) == TYPE_LA) |
{ |
pat = PATTERN (insn); |
if (GET_CODE (pat) == PARALLEL) |
{ |
gcc_assert (XVECLEN (pat, 0) == 2); |
pat = XVECEXP (pat, 0, 0); |
} |
gcc_assert (GET_CODE (pat) == SET); |
return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0); |
} |
else if (get_attr_atype (insn) == ATYPE_AGEN) |
return reg_used_in_mem_p (regno, PATTERN (insn)); |
} |
} |
return false; |
} |
|
/* Return 1, if dep_insn sets register used in insn in the agen unit. */ |
|
int |
s390_agen_dep_p (rtx dep_insn, rtx insn) |
{ |
rtx dep_rtx = PATTERN (dep_insn); |
int i; |
|
if (GET_CODE (dep_rtx) == SET |
&& addr_generation_dependency_p (dep_rtx, insn)) |
return 1; |
else if (GET_CODE (dep_rtx) == PARALLEL) |
{ |
for (i = 0; i < XVECLEN (dep_rtx, 0); i++) |
{ |
if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn)) |
return 1; |
} |
} |
return 0; |
} |
|
/* A C statement (sans semicolon) to update the integer scheduling priority |
INSN_PRIORITY (INSN). Increase the priority to execute the INSN earlier, |
reduce the priority to execute INSN later. Do not define this macro if |
you do not need to adjust the scheduling priorities of insns. |
|
A STD instruction should be scheduled earlier, |
in order to use the bypass. */ |
|
static int |
s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority) |
{ |
if (! INSN_P (insn)) |
return priority; |
|
if (s390_tune != PROCESSOR_2084_Z990 |
&& s390_tune != PROCESSOR_2094_Z9_109) |
return priority; |
|
switch (s390_safe_attr_type (insn)) |
{ |
case TYPE_FSTOREDF: |
case TYPE_FSTORESF: |
priority = priority << 3; |
break; |
case TYPE_STORE: |
case TYPE_STM: |
priority = priority << 1; |
break; |
default: |
break; |
} |
return priority; |
} |
|
/* The number of instructions that can be issued per cycle. */ |
|
static int |
s390_issue_rate (void) |
{ |
if (s390_tune == PROCESSOR_2084_Z990 |
|| s390_tune == PROCESSOR_2094_Z9_109) |
return 3; |
return 1; |
} |
|
static int |
s390_first_cycle_multipass_dfa_lookahead (void) |
{ |
return 4; |
} |
|
|
/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression. |
Fix up MEMs as required. */ |
|
static void |
annotate_constant_pool_refs (rtx *x) |
{ |
int i, j; |
const char *fmt; |
|
gcc_assert (GET_CODE (*x) != SYMBOL_REF |
|| !CONSTANT_POOL_ADDRESS_P (*x)); |
|
/* Literal pool references can only occur inside a MEM ... */ |
if (GET_CODE (*x) == MEM) |
{ |
rtx memref = XEXP (*x, 0); |
|
if (GET_CODE (memref) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (memref)) |
{ |
rtx base = cfun->machine->base_reg; |
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base), |
UNSPEC_LTREF); |
|
*x = replace_equiv_address (*x, addr); |
return; |
} |
|
if (GET_CODE (memref) == CONST |
&& GET_CODE (XEXP (memref, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT |
&& GET_CODE (XEXP (XEXP (memref, 0), 0)) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (memref, 0), 0))) |
{ |
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1)); |
rtx sym = XEXP (XEXP (memref, 0), 0); |
rtx base = cfun->machine->base_reg; |
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base), |
UNSPEC_LTREF); |
|
*x = replace_equiv_address (*x, plus_constant (addr, off)); |
return; |
} |
} |
|
/* ... or a load-address type pattern. */ |
if (GET_CODE (*x) == SET) |
{ |
rtx addrref = SET_SRC (*x); |
|
if (GET_CODE (addrref) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (addrref)) |
{ |
rtx base = cfun->machine->base_reg; |
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addrref, base), |
UNSPEC_LTREF); |
|
SET_SRC (*x) = addr; |
return; |
} |
|
if (GET_CODE (addrref) == CONST |
&& GET_CODE (XEXP (addrref, 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT |
&& GET_CODE (XEXP (XEXP (addrref, 0), 0)) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (addrref, 0), 0))) |
{ |
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1)); |
rtx sym = XEXP (XEXP (addrref, 0), 0); |
rtx base = cfun->machine->base_reg; |
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base), |
UNSPEC_LTREF); |
|
SET_SRC (*x) = plus_constant (addr, off); |
return; |
} |
} |
|
/* Annotate LTREL_BASE as well. */ |
if (GET_CODE (*x) == UNSPEC |
&& XINT (*x, 1) == UNSPEC_LTREL_BASE) |
{ |
rtx base = cfun->machine->base_reg; |
*x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XVECEXP (*x, 0, 0), base), |
UNSPEC_LTREL_BASE); |
return; |
} |
|
fmt = GET_RTX_FORMAT (GET_CODE (*x)); |
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e') |
{ |
annotate_constant_pool_refs (&XEXP (*x, i)); |
} |
else if (fmt[i] == 'E') |
{ |
for (j = 0; j < XVECLEN (*x, i); j++) |
annotate_constant_pool_refs (&XVECEXP (*x, i, j)); |
} |
} |
} |
|
/* Split all branches that exceed the maximum distance. |
Returns true if this created a new literal pool entry. */ |
|
static int |
s390_split_branches (void) |
{ |
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); |
int new_literal = 0, ret; |
rtx insn, pat, tmp, target; |
rtx *label; |
|
/* We need correct insn addresses. */ |
|
shorten_branches (get_insns ()); |
|
/* Find all branches that exceed 64KB, and split them. */ |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
if (GET_CODE (insn) != JUMP_INSN) |
continue; |
|
pat = PATTERN (insn); |
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) |
pat = XVECEXP (pat, 0, 0); |
if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx) |
continue; |
|
if (GET_CODE (SET_SRC (pat)) == LABEL_REF) |
{ |
label = &SET_SRC (pat); |
} |
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) |
{ |
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) |
label = &XEXP (SET_SRC (pat), 1); |
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) |
label = &XEXP (SET_SRC (pat), 2); |
else |
continue; |
} |
else |
continue; |
|
if (get_attr_length (insn) <= 4) |
continue; |
|
/* We are going to use the return register as scratch register, |
make sure it will be saved/restored by the prologue/epilogue. */ |
cfun_frame_layout.save_return_addr_p = 1; |
|
if (!flag_pic) |
{ |
new_literal = 1; |
tmp = force_const_mem (Pmode, *label); |
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); |
INSN_ADDRESSES_NEW (tmp, -1); |
annotate_constant_pool_refs (&PATTERN (tmp)); |
|
target = temp_reg; |
} |
else |
{ |
new_literal = 1; |
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label), |
UNSPEC_LTREL_OFFSET); |
target = gen_rtx_CONST (Pmode, target); |
target = force_const_mem (Pmode, target); |
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); |
INSN_ADDRESSES_NEW (tmp, -1); |
annotate_constant_pool_refs (&PATTERN (tmp)); |
|
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0), |
cfun->machine->base_reg), |
UNSPEC_LTREL_BASE); |
target = gen_rtx_PLUS (Pmode, temp_reg, target); |
} |
|
ret = validate_change (insn, label, target, 0); |
gcc_assert (ret); |
} |
|
return new_literal; |
} |
|
|
/* Find an annotated literal pool symbol referenced in RTX X, |
and store it at REF. Will abort if X contains references to |
more than one such pool symbol; multiple references to the same |
symbol are allowed, however. |
|
The rtx pointed to by REF must be initialized to NULL_RTX |
by the caller before calling this routine. */ |
|
static void |
find_constant_pool_ref (rtx x, rtx *ref) |
{ |
int i, j; |
const char *fmt; |
|
/* Ignore LTREL_BASE references. */ |
if (GET_CODE (x) == UNSPEC |
&& XINT (x, 1) == UNSPEC_LTREL_BASE) |
return; |
/* Likewise POOL_ENTRY insns. */ |
if (GET_CODE (x) == UNSPEC_VOLATILE |
&& XINT (x, 1) == UNSPECV_POOL_ENTRY) |
return; |
|
gcc_assert (GET_CODE (x) != SYMBOL_REF |
|| !CONSTANT_POOL_ADDRESS_P (x)); |
|
if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF) |
{ |
rtx sym = XVECEXP (x, 0, 0); |
gcc_assert (GET_CODE (sym) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (sym)); |
|
if (*ref == NULL_RTX) |
*ref = sym; |
else |
gcc_assert (*ref == sym); |
|
return; |
} |
|
fmt = GET_RTX_FORMAT (GET_CODE (x)); |
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e') |
{ |
find_constant_pool_ref (XEXP (x, i), ref); |
} |
else if (fmt[i] == 'E') |
{ |
for (j = 0; j < XVECLEN (x, i); j++) |
find_constant_pool_ref (XVECEXP (x, i, j), ref); |
} |
} |
} |
|
/* Replace every reference to the annotated literal pool |
symbol REF in X by its base plus OFFSET. */ |
|
static void |
replace_constant_pool_ref (rtx *x, rtx ref, rtx offset) |
{ |
int i, j; |
const char *fmt; |
|
gcc_assert (*x != ref); |
|
if (GET_CODE (*x) == UNSPEC |
&& XINT (*x, 1) == UNSPEC_LTREF |
&& XVECEXP (*x, 0, 0) == ref) |
{ |
*x = gen_rtx_PLUS (Pmode, XVECEXP (*x, 0, 1), offset); |
return; |
} |
|
if (GET_CODE (*x) == PLUS |
&& GET_CODE (XEXP (*x, 1)) == CONST_INT |
&& GET_CODE (XEXP (*x, 0)) == UNSPEC |
&& XINT (XEXP (*x, 0), 1) == UNSPEC_LTREF |
&& XVECEXP (XEXP (*x, 0), 0, 0) == ref) |
{ |
rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset); |
*x = plus_constant (addr, INTVAL (XEXP (*x, 1))); |
return; |
} |
|
fmt = GET_RTX_FORMAT (GET_CODE (*x)); |
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e') |
{ |
replace_constant_pool_ref (&XEXP (*x, i), ref, offset); |
} |
else if (fmt[i] == 'E') |
{ |
for (j = 0; j < XVECLEN (*x, i); j++) |
replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, offset); |
} |
} |
} |
|
/* Check whether X contains an UNSPEC_LTREL_BASE. |
Return its constant pool symbol if found, NULL_RTX otherwise. */ |
|
static rtx |
find_ltrel_base (rtx x) |
{ |
int i, j; |
const char *fmt; |
|
if (GET_CODE (x) == UNSPEC |
&& XINT (x, 1) == UNSPEC_LTREL_BASE) |
return XVECEXP (x, 0, 0); |
|
fmt = GET_RTX_FORMAT (GET_CODE (x)); |
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e') |
{ |
rtx fnd = find_ltrel_base (XEXP (x, i)); |
if (fnd) |
return fnd; |
} |
else if (fmt[i] == 'E') |
{ |
for (j = 0; j < XVECLEN (x, i); j++) |
{ |
rtx fnd = find_ltrel_base (XVECEXP (x, i, j)); |
if (fnd) |
return fnd; |
} |
} |
} |
|
return NULL_RTX; |
} |
|
/* Replace any occurrence of UNSPEC_LTREL_BASE in X with its base. */ |
|
static void |
replace_ltrel_base (rtx *x) |
{ |
int i, j; |
const char *fmt; |
|
if (GET_CODE (*x) == UNSPEC |
&& XINT (*x, 1) == UNSPEC_LTREL_BASE) |
{ |
*x = XVECEXP (*x, 0, 1); |
return; |
} |
|
fmt = GET_RTX_FORMAT (GET_CODE (*x)); |
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) |
{ |
if (fmt[i] == 'e') |
{ |
replace_ltrel_base (&XEXP (*x, i)); |
} |
else if (fmt[i] == 'E') |
{ |
for (j = 0; j < XVECLEN (*x, i); j++) |
replace_ltrel_base (&XVECEXP (*x, i, j)); |
} |
} |
} |
|
|
/* We keep a list of constants which we have to add to internal |
constant tables in the middle of large functions. */ |
|
#define NR_C_MODES 11 |
enum machine_mode constant_modes[NR_C_MODES] = |
{ |
TFmode, TImode, TDmode, |
DFmode, DImode, DDmode, |
SFmode, SImode, SDmode, |
HImode, |
QImode |
}; |
|
struct constant |
{ |
struct constant *next; |
rtx value; |
rtx label; |
}; |
|
struct constant_pool |
{ |
struct constant_pool *next; |
rtx first_insn; |
rtx pool_insn; |
bitmap insns; |
|
struct constant *constants[NR_C_MODES]; |
struct constant *execute; |
rtx label; |
int size; |
}; |
|
/* Allocate new constant_pool structure. */ |
|
static struct constant_pool * |
s390_alloc_pool (void) |
{ |
struct constant_pool *pool; |
int i; |
|
pool = (struct constant_pool *) xmalloc (sizeof *pool); |
pool->next = NULL; |
for (i = 0; i < NR_C_MODES; i++) |
pool->constants[i] = NULL; |
|
pool->execute = NULL; |
pool->label = gen_label_rtx (); |
pool->first_insn = NULL_RTX; |
pool->pool_insn = NULL_RTX; |
pool->insns = BITMAP_ALLOC (NULL); |
pool->size = 0; |
|
return pool; |
} |
|
/* Create new constant pool covering instructions starting at INSN |
and chain it to the end of POOL_LIST. */ |
|
static struct constant_pool * |
s390_start_pool (struct constant_pool **pool_list, rtx insn) |
{ |
struct constant_pool *pool, **prev; |
|
pool = s390_alloc_pool (); |
pool->first_insn = insn; |
|
for (prev = pool_list; *prev; prev = &(*prev)->next) |
; |
*prev = pool; |
|
return pool; |
} |
|
/* End range of instructions covered by POOL at INSN and emit |
placeholder insn representing the pool. */ |
|
static void |
s390_end_pool (struct constant_pool *pool, rtx insn) |
{ |
rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */); |
|
if (!insn) |
insn = get_last_insn (); |
|
pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn); |
INSN_ADDRESSES_NEW (pool->pool_insn, -1); |
} |
|
/* Add INSN to the list of insns covered by POOL. */ |
|
static void |
s390_add_pool_insn (struct constant_pool *pool, rtx insn) |
{ |
bitmap_set_bit (pool->insns, INSN_UID (insn)); |
} |
|
/* Return pool out of POOL_LIST that covers INSN. */ |
|
static struct constant_pool * |
s390_find_pool (struct constant_pool *pool_list, rtx insn) |
{ |
struct constant_pool *pool; |
|
for (pool = pool_list; pool; pool = pool->next) |
if (bitmap_bit_p (pool->insns, INSN_UID (insn))) |
break; |
|
return pool; |
} |
|
/* Add constant VAL of mode MODE to the constant pool POOL. */ |
|
static void |
s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode) |
{ |
struct constant *c; |
int i; |
|
for (i = 0; i < NR_C_MODES; i++) |
if (constant_modes[i] == mode) |
break; |
gcc_assert (i != NR_C_MODES); |
|
for (c = pool->constants[i]; c != NULL; c = c->next) |
if (rtx_equal_p (val, c->value)) |
break; |
|
if (c == NULL) |
{ |
c = (struct constant *) xmalloc (sizeof *c); |
c->value = val; |
c->label = gen_label_rtx (); |
c->next = pool->constants[i]; |
pool->constants[i] = c; |
pool->size += GET_MODE_SIZE (mode); |
} |
} |
|
/* Find constant VAL of mode MODE in the constant pool POOL. |
Return an RTX describing the distance from the start of |
the pool to the location of the new constant. */ |
|
static rtx |
s390_find_constant (struct constant_pool *pool, rtx val, |
enum machine_mode mode) |
{ |
struct constant *c; |
rtx offset; |
int i; |
|
for (i = 0; i < NR_C_MODES; i++) |
if (constant_modes[i] == mode) |
break; |
gcc_assert (i != NR_C_MODES); |
|
for (c = pool->constants[i]; c != NULL; c = c->next) |
if (rtx_equal_p (val, c->value)) |
break; |
|
gcc_assert (c); |
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), |
gen_rtx_LABEL_REF (Pmode, pool->label)); |
offset = gen_rtx_CONST (Pmode, offset); |
return offset; |
} |
|
/* Check whether INSN is an execute. Return the label_ref to its |
execute target template if so, NULL_RTX otherwise. */ |
|
static rtx |
s390_execute_label (rtx insn) |
{ |
if (GET_CODE (insn) == INSN |
&& GET_CODE (PATTERN (insn)) == PARALLEL |
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC |
&& XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) |
return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); |
|
return NULL_RTX; |
} |
|
/* Add execute target for INSN to the constant pool POOL. */ |
|
static void |
s390_add_execute (struct constant_pool *pool, rtx insn) |
{ |
struct constant *c; |
|
for (c = pool->execute; c != NULL; c = c->next) |
if (INSN_UID (insn) == INSN_UID (c->value)) |
break; |
|
if (c == NULL) |
{ |
c = (struct constant *) xmalloc (sizeof *c); |
c->value = insn; |
c->label = gen_label_rtx (); |
c->next = pool->execute; |
pool->execute = c; |
pool->size += 6; |
} |
} |
|
/* Find execute target for INSN in the constant pool POOL. |
Return an RTX describing the distance from the start of |
the pool to the location of the execute target. */ |
|
static rtx |
s390_find_execute (struct constant_pool *pool, rtx insn) |
{ |
struct constant *c; |
rtx offset; |
|
for (c = pool->execute; c != NULL; c = c->next) |
if (INSN_UID (insn) == INSN_UID (c->value)) |
break; |
|
gcc_assert (c); |
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), |
gen_rtx_LABEL_REF (Pmode, pool->label)); |
offset = gen_rtx_CONST (Pmode, offset); |
return offset; |
} |
|
/* For an execute INSN, extract the execute target template. */ |
|
static rtx |
s390_execute_target (rtx insn) |
{ |
rtx pattern = PATTERN (insn); |
gcc_assert (s390_execute_label (insn)); |
|
if (XVECLEN (pattern, 0) == 2) |
{ |
pattern = copy_rtx (XVECEXP (pattern, 0, 1)); |
} |
else |
{ |
rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1); |
int i; |
|
for (i = 0; i < XVECLEN (pattern, 0) - 1; i++) |
RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1)); |
|
pattern = gen_rtx_PARALLEL (VOIDmode, vec); |
} |
|
return pattern; |
} |
|
/* Indicate that INSN cannot be duplicated. This is the case for |
execute insns that carry a unique label. */ |
|
static bool |
s390_cannot_copy_insn_p (rtx insn) |
{ |
rtx label = s390_execute_label (insn); |
return label && label != const0_rtx; |
} |
|
/* Dump out the constants in POOL. If REMOTE_LABEL is true, |
do not emit the pool base label. */ |
|
static void |
s390_dump_pool (struct constant_pool *pool, bool remote_label) |
{ |
struct constant *c; |
rtx insn = pool->pool_insn; |
int i; |
|
/* Switch to rodata section. */ |
if (TARGET_CPU_ZARCH) |
{ |
insn = emit_insn_after (gen_pool_section_start (), insn); |
INSN_ADDRESSES_NEW (insn, -1); |
} |
|
/* Ensure minimum pool alignment. */ |
if (TARGET_CPU_ZARCH) |
insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn); |
else |
insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
/* Emit pool base label. */ |
if (!remote_label) |
{ |
insn = emit_label_after (pool->label, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
} |
|
/* Dump constants in descending alignment requirement order, |
ensuring proper alignment for every constant. */ |
for (i = 0; i < NR_C_MODES; i++) |
for (c = pool->constants[i]; c; c = c->next) |
{ |
/* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references. */ |
rtx value = c->value; |
if (GET_CODE (value) == CONST |
&& GET_CODE (XEXP (value, 0)) == UNSPEC |
&& XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET |
&& XVECLEN (XEXP (value, 0), 0) == 1) |
{ |
value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0), |
gen_rtx_LABEL_REF (VOIDmode, pool->label)); |
value = gen_rtx_CONST (VOIDmode, value); |
} |
|
insn = emit_label_after (c->label, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i], |
gen_rtvec (1, value), |
UNSPECV_POOL_ENTRY); |
insn = emit_insn_after (value, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
} |
|
/* Ensure minimum alignment for instructions. */ |
insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
/* Output in-pool execute template insns. */ |
for (c = pool->execute; c; c = c->next) |
{ |
insn = emit_label_after (c->label, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
insn = emit_insn_after (s390_execute_target (c->value), insn); |
INSN_ADDRESSES_NEW (insn, -1); |
} |
|
/* Switch back to previous section. */ |
if (TARGET_CPU_ZARCH) |
{ |
insn = emit_insn_after (gen_pool_section_end (), insn); |
INSN_ADDRESSES_NEW (insn, -1); |
} |
|
insn = emit_barrier_after (insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
/* Remove placeholder insn. */ |
remove_insn (pool->pool_insn); |
} |
|
/* Free all memory used by POOL. */ |
|
static void |
s390_free_pool (struct constant_pool *pool) |
{ |
struct constant *c, *next; |
int i; |
|
for (i = 0; i < NR_C_MODES; i++) |
for (c = pool->constants[i]; c; c = next) |
{ |
next = c->next; |
free (c); |
} |
|
for (c = pool->execute; c; c = next) |
{ |
next = c->next; |
free (c); |
} |
|
BITMAP_FREE (pool->insns); |
free (pool); |
} |
|
|
/* Collect main literal pool. Return NULL on overflow. */ |
|
static struct constant_pool * |
s390_mainpool_start (void) |
{ |
struct constant_pool *pool; |
rtx insn; |
|
pool = s390_alloc_pool (); |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
if (GET_CODE (insn) == INSN |
&& GET_CODE (PATTERN (insn)) == SET |
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE |
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL) |
{ |
gcc_assert (!pool->pool_insn); |
pool->pool_insn = insn; |
} |
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn)) |
{ |
s390_add_execute (pool, insn); |
} |
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) |
{ |
rtx pool_ref = NULL_RTX; |
find_constant_pool_ref (PATTERN (insn), &pool_ref); |
if (pool_ref) |
{ |
rtx constant = get_pool_constant (pool_ref); |
enum machine_mode mode = get_pool_mode (pool_ref); |
s390_add_constant (pool, constant, mode); |
} |
} |
} |
|
gcc_assert (pool->pool_insn || pool->size == 0); |
|
if (pool->size >= 4096) |
{ |
/* We're going to chunkify the pool, so remove the main |
pool placeholder insn. */ |
remove_insn (pool->pool_insn); |
|
s390_free_pool (pool); |
pool = NULL; |
} |
|
return pool; |
} |
|
/* POOL holds the main literal pool as collected by s390_mainpool_start. |
Modify the current function to output the pool constants as well as |
the pool register setup instruction. */ |
|
static void |
s390_mainpool_finish (struct constant_pool *pool) |
{ |
rtx base_reg = cfun->machine->base_reg; |
rtx insn; |
|
/* If the pool is empty, we're done. */ |
if (pool->size == 0) |
{ |
/* We don't actually need a base register after all. */ |
cfun->machine->base_reg = NULL_RTX; |
|
if (pool->pool_insn) |
remove_insn (pool->pool_insn); |
s390_free_pool (pool); |
return; |
} |
|
/* We need correct insn addresses. */ |
shorten_branches (get_insns ()); |
|
/* On zSeries, we use a LARL to load the pool register. The pool is |
located in the .rodata section, so we emit it after the function. */ |
if (TARGET_CPU_ZARCH) |
{ |
insn = gen_main_base_64 (base_reg, pool->label); |
insn = emit_insn_after (insn, pool->pool_insn); |
INSN_ADDRESSES_NEW (insn, -1); |
remove_insn (pool->pool_insn); |
|
insn = get_last_insn (); |
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); |
INSN_ADDRESSES_NEW (pool->pool_insn, -1); |
|
s390_dump_pool (pool, 0); |
} |
|
/* On S/390, if the total size of the function's code plus literal pool |
does not exceed 4096 bytes, we use BASR to set up a function base |
pointer, and emit the literal pool at the end of the function. */ |
else if (INSN_ADDRESSES (INSN_UID (get_last_insn ())) |
+ pool->size + 8 /* alignment slop */ < 4096) |
{ |
insn = gen_main_base_31_small (base_reg, pool->label); |
insn = emit_insn_after (insn, pool->pool_insn); |
INSN_ADDRESSES_NEW (insn, -1); |
remove_insn (pool->pool_insn); |
|
insn = emit_label_after (pool->label, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
insn = get_last_insn (); |
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); |
INSN_ADDRESSES_NEW (pool->pool_insn, -1); |
|
s390_dump_pool (pool, 1); |
} |
|
/* Otherwise, we emit an inline literal pool and use BASR to branch |
over it, setting up the pool register at the same time. */ |
else |
{ |
rtx pool_end = gen_label_rtx (); |
|
insn = gen_main_base_31_large (base_reg, pool->label, pool_end); |
insn = emit_insn_after (insn, pool->pool_insn); |
INSN_ADDRESSES_NEW (insn, -1); |
remove_insn (pool->pool_insn); |
|
insn = emit_label_after (pool->label, insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); |
INSN_ADDRESSES_NEW (pool->pool_insn, -1); |
|
insn = emit_label_after (pool_end, pool->pool_insn); |
INSN_ADDRESSES_NEW (insn, -1); |
|
s390_dump_pool (pool, 1); |
} |
|
|
/* Replace all literal pool references. */ |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
if (INSN_P (insn)) |
replace_ltrel_base (&PATTERN (insn)); |
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) |
{ |
rtx addr, pool_ref = NULL_RTX; |
find_constant_pool_ref (PATTERN (insn), &pool_ref); |
if (pool_ref) |
{ |
if (s390_execute_label (insn)) |
addr = s390_find_execute (pool, insn); |
else |
addr = s390_find_constant (pool, get_pool_constant (pool_ref), |
get_pool_mode (pool_ref)); |
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); |
INSN_CODE (insn) = -1; |
} |
} |
} |
|
|
/* Free the pool. */ |
s390_free_pool (pool); |
} |
|
/* POOL holds the main literal pool as collected by s390_mainpool_start. |
We have decided we cannot use this pool, so revert all changes |
to the current function that were done by s390_mainpool_start. */ |
static void |
s390_mainpool_cancel (struct constant_pool *pool) |
{ |
/* We didn't actually change the instruction stream, so simply |
free the pool memory. */ |
s390_free_pool (pool); |
} |
|
|
/* Chunkify the literal pool. */ |
|
#define S390_POOL_CHUNK_MIN 0xc00 |
#define S390_POOL_CHUNK_MAX 0xe00 |
|
static struct constant_pool * |
s390_chunkify_start (void) |
{ |
struct constant_pool *curr_pool = NULL, *pool_list = NULL; |
int extra_size = 0; |
bitmap far_labels; |
rtx pending_ltrel = NULL_RTX; |
rtx insn; |
|
rtx (*gen_reload_base) (rtx, rtx) = |
TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31; |
|
|
/* We need correct insn addresses. */ |
|
shorten_branches (get_insns ()); |
|
/* Scan all insns and move literals to pool chunks. */ |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
/* Check for pending LTREL_BASE. */ |
if (INSN_P (insn)) |
{ |
rtx ltrel_base = find_ltrel_base (PATTERN (insn)); |
if (ltrel_base) |
{ |
gcc_assert (ltrel_base == pending_ltrel); |
pending_ltrel = NULL_RTX; |
} |
} |
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn)) |
{ |
if (!curr_pool) |
curr_pool = s390_start_pool (&pool_list, insn); |
|
s390_add_execute (curr_pool, insn); |
s390_add_pool_insn (curr_pool, insn); |
} |
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) |
{ |
rtx pool_ref = NULL_RTX; |
find_constant_pool_ref (PATTERN (insn), &pool_ref); |
if (pool_ref) |
{ |
rtx constant = get_pool_constant (pool_ref); |
enum machine_mode mode = get_pool_mode (pool_ref); |
|
if (!curr_pool) |
curr_pool = s390_start_pool (&pool_list, insn); |
|
s390_add_constant (curr_pool, constant, mode); |
s390_add_pool_insn (curr_pool, insn); |
|
/* Don't split the pool chunk between a LTREL_OFFSET load |
and the corresponding LTREL_BASE. */ |
if (GET_CODE (constant) == CONST |
&& GET_CODE (XEXP (constant, 0)) == UNSPEC |
&& XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET) |
{ |
gcc_assert (!pending_ltrel); |
pending_ltrel = pool_ref; |
} |
} |
} |
|
if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL) |
{ |
if (curr_pool) |
s390_add_pool_insn (curr_pool, insn); |
/* An LTREL_BASE must follow within the same basic block. */ |
gcc_assert (!pending_ltrel); |
} |
|
if (!curr_pool |
|| INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn) |
|| INSN_ADDRESSES (INSN_UID (insn)) == -1) |
continue; |
|
if (TARGET_CPU_ZARCH) |
{ |
if (curr_pool->size < S390_POOL_CHUNK_MAX) |
continue; |
|
s390_end_pool (curr_pool, NULL_RTX); |
curr_pool = NULL; |
} |
else |
{ |
int chunk_size = INSN_ADDRESSES (INSN_UID (insn)) |
- INSN_ADDRESSES (INSN_UID (curr_pool->first_insn)) |
+ extra_size; |
|
/* We will later have to insert base register reload insns. |
Those will have an effect on code size, which we need to |
consider here. This calculation makes rather pessimistic |
worst-case assumptions. */ |
if (GET_CODE (insn) == CODE_LABEL) |
extra_size += 6; |
|
if (chunk_size < S390_POOL_CHUNK_MIN |
&& curr_pool->size < S390_POOL_CHUNK_MIN) |
continue; |
|
/* Pool chunks can only be inserted after BARRIERs ... */ |
if (GET_CODE (insn) == BARRIER) |
{ |
s390_end_pool (curr_pool, insn); |
curr_pool = NULL; |
extra_size = 0; |
} |
|
/* ... so if we don't find one in time, create one. */ |
else if ((chunk_size > S390_POOL_CHUNK_MAX |
|| curr_pool->size > S390_POOL_CHUNK_MAX)) |
{ |
rtx label, jump, barrier; |
|
/* We can insert the barrier only after a 'real' insn. */ |
if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) |
continue; |
if (get_attr_length (insn) == 0) |
continue; |
|
/* Don't separate LTREL_BASE from the corresponding |
LTREL_OFFSET load. */ |
if (pending_ltrel) |
continue; |
|
label = gen_label_rtx (); |
jump = emit_jump_insn_after (gen_jump (label), insn); |
barrier = emit_barrier_after (jump); |
insn = emit_label_after (label, barrier); |
JUMP_LABEL (jump) = label; |
LABEL_NUSES (label) = 1; |
|
INSN_ADDRESSES_NEW (jump, -1); |
INSN_ADDRESSES_NEW (barrier, -1); |
INSN_ADDRESSES_NEW (insn, -1); |
|
s390_end_pool (curr_pool, barrier); |
curr_pool = NULL; |
extra_size = 0; |
} |
} |
} |
|
if (curr_pool) |
s390_end_pool (curr_pool, NULL_RTX); |
gcc_assert (!pending_ltrel); |
|
/* Find all labels that are branched into |
from an insn belonging to a different chunk. */ |
|
far_labels = BITMAP_ALLOC (NULL); |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
/* Labels marked with LABEL_PRESERVE_P can be target |
of non-local jumps, so we have to mark them. |
The same holds for named labels. |
|
Don't do that, however, if it is the label before |
a jump table. */ |
|
if (GET_CODE (insn) == CODE_LABEL |
&& (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn))) |
{ |
rtx vec_insn = next_real_insn (insn); |
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? |
PATTERN (vec_insn) : NULL_RTX; |
if (!vec_pat |
|| !(GET_CODE (vec_pat) == ADDR_VEC |
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC)) |
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn)); |
} |
|
/* If we have a direct jump (conditional or unconditional) |
or a casesi jump, check all potential targets. */ |
else if (GET_CODE (insn) == JUMP_INSN) |
{ |
rtx pat = PATTERN (insn); |
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) |
pat = XVECEXP (pat, 0, 0); |
|
if (GET_CODE (pat) == SET) |
{ |
rtx label = JUMP_LABEL (insn); |
if (label) |
{ |
if (s390_find_pool (pool_list, label) |
!= s390_find_pool (pool_list, insn)) |
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label)); |
} |
} |
else if (GET_CODE (pat) == PARALLEL |
&& XVECLEN (pat, 0) == 2 |
&& GET_CODE (XVECEXP (pat, 0, 0)) == SET |
&& GET_CODE (XVECEXP (pat, 0, 1)) == USE |
&& GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF) |
{ |
/* Find the jump table used by this casesi jump. */ |
rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0); |
rtx vec_insn = next_real_insn (vec_label); |
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? |
PATTERN (vec_insn) : NULL_RTX; |
if (vec_pat |
&& (GET_CODE (vec_pat) == ADDR_VEC |
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC)) |
{ |
int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC; |
|
for (i = 0; i < XVECLEN (vec_pat, diff_p); i++) |
{ |
rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0); |
|
if (s390_find_pool (pool_list, label) |
!= s390_find_pool (pool_list, insn)) |
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label)); |
} |
} |
} |
} |
} |
|
/* Insert base register reload insns before every pool. */ |
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) |
{ |
rtx new_insn = gen_reload_base (cfun->machine->base_reg, |
curr_pool->label); |
rtx insn = curr_pool->first_insn; |
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); |
} |
|
/* Insert base register reload insns at every far label. */ |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
if (GET_CODE (insn) == CODE_LABEL |
&& bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn))) |
{ |
struct constant_pool *pool = s390_find_pool (pool_list, insn); |
if (pool) |
{ |
rtx new_insn = gen_reload_base (cfun->machine->base_reg, |
pool->label); |
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); |
} |
} |
|
|
BITMAP_FREE (far_labels); |
|
|
/* Recompute insn addresses. */ |
|
init_insn_lengths (); |
shorten_branches (get_insns ()); |
|
return pool_list; |
} |
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. |
After we have decided to use this list, finish implementing |
all changes to the current function as required. */ |
|
static void |
s390_chunkify_finish (struct constant_pool *pool_list) |
{ |
struct constant_pool *curr_pool = NULL; |
rtx insn; |
|
|
/* Replace all literal pool references. */ |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
if (INSN_P (insn)) |
replace_ltrel_base (&PATTERN (insn)); |
|
curr_pool = s390_find_pool (pool_list, insn); |
if (!curr_pool) |
continue; |
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) |
{ |
rtx addr, pool_ref = NULL_RTX; |
find_constant_pool_ref (PATTERN (insn), &pool_ref); |
if (pool_ref) |
{ |
if (s390_execute_label (insn)) |
addr = s390_find_execute (curr_pool, insn); |
else |
addr = s390_find_constant (curr_pool, |
get_pool_constant (pool_ref), |
get_pool_mode (pool_ref)); |
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); |
INSN_CODE (insn) = -1; |
} |
} |
} |
|
/* Dump out all literal pools. */ |
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) |
s390_dump_pool (curr_pool, 0); |
|
/* Free pool list. */ |
|
while (pool_list) |
{ |
struct constant_pool *next = pool_list->next; |
s390_free_pool (pool_list); |
pool_list = next; |
} |
} |
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. |
We have decided we cannot use this list, so revert all changes |
to the current function that were done by s390_chunkify_start. */ |
|
static void |
s390_chunkify_cancel (struct constant_pool *pool_list) |
{ |
struct constant_pool *curr_pool = NULL; |
rtx insn; |
|
/* Remove all pool placeholder insns. */ |
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) |
{ |
/* Did we insert an extra barrier? Remove it. */ |
rtx barrier = PREV_INSN (curr_pool->pool_insn); |
rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX; |
rtx label = NEXT_INSN (curr_pool->pool_insn); |
|
if (jump && GET_CODE (jump) == JUMP_INSN |
&& barrier && GET_CODE (barrier) == BARRIER |
&& label && GET_CODE (label) == CODE_LABEL |
&& GET_CODE (PATTERN (jump)) == SET |
&& SET_DEST (PATTERN (jump)) == pc_rtx |
&& GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF |
&& XEXP (SET_SRC (PATTERN (jump)), 0) == label) |
{ |
remove_insn (jump); |
remove_insn (barrier); |
remove_insn (label); |
} |
|
remove_insn (curr_pool->pool_insn); |
} |
|
/* Remove all base register reload insns. */ |
|
for (insn = get_insns (); insn; ) |
{ |
rtx next_insn = NEXT_INSN (insn); |
|
if (GET_CODE (insn) == INSN |
&& GET_CODE (PATTERN (insn)) == SET |
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC |
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE) |
remove_insn (insn); |
|
insn = next_insn; |
} |
|
/* Free pool list. */ |
|
while (pool_list) |
{ |
struct constant_pool *next = pool_list->next; |
s390_free_pool (pool_list); |
pool_list = next; |
} |
} |
|
|
/* Output the constant pool entry EXP in mode MODE with alignment ALIGN. */ |
|
void |
s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align) |
{ |
REAL_VALUE_TYPE r; |
|
switch (GET_MODE_CLASS (mode)) |
{ |
case MODE_FLOAT: |
case MODE_DECIMAL_FLOAT: |
gcc_assert (GET_CODE (exp) == CONST_DOUBLE); |
|
REAL_VALUE_FROM_CONST_DOUBLE (r, exp); |
assemble_real (r, mode, align); |
break; |
|
case MODE_INT: |
assemble_integer (exp, GET_MODE_SIZE (mode), align, 1); |
break; |
|
default: |
gcc_unreachable (); |
} |
} |
|
|
/* Return an RTL expression representing the value of the return address |
for the frame COUNT steps up from the current frame. FRAME is the |
frame pointer of that frame. */ |
|
rtx |
s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) |
{ |
int offset; |
rtx addr; |
|
/* Without backchain, we fail for all but the current frame. */ |
|
if (!TARGET_BACKCHAIN && count > 0) |
return NULL_RTX; |
|
/* For the current frame, we need to make sure the initial |
value of RETURN_REGNUM is actually saved. */ |
|
if (count == 0) |
{ |
/* On non-z architectures branch splitting could overwrite r14. */ |
if (TARGET_CPU_ZARCH) |
return get_hard_reg_initial_val (Pmode, RETURN_REGNUM); |
else |
{ |
cfun_frame_layout.save_return_addr_p = true; |
return gen_rtx_MEM (Pmode, return_address_pointer_rtx); |
} |
} |
|
if (TARGET_PACKED_STACK) |
offset = -2 * UNITS_PER_WORD; |
else |
offset = RETURN_REGNUM * UNITS_PER_WORD; |
|
addr = plus_constant (frame, offset); |
addr = memory_address (Pmode, addr); |
return gen_rtx_MEM (Pmode, addr); |
} |
|
/* Return an RTL expression representing the back chain stored in |
the current stack frame. */ |
|
rtx |
s390_back_chain_rtx (void) |
{ |
rtx chain; |
|
gcc_assert (TARGET_BACKCHAIN); |
|
if (TARGET_PACKED_STACK) |
chain = plus_constant (stack_pointer_rtx, |
STACK_POINTER_OFFSET - UNITS_PER_WORD); |
else |
chain = stack_pointer_rtx; |
|
chain = gen_rtx_MEM (Pmode, chain); |
return chain; |
} |
|
/* Find first call clobbered register unused in a function. |
This could be used as base register in a leaf function |
or for holding the return address before epilogue. */ |
|
static int |
find_unused_clobbered_reg (void) |
{ |
int i; |
for (i = 0; i < 6; i++) |
if (!regs_ever_live[i]) |
return i; |
return 0; |
} |
|
|
/* Helper function for s390_regs_ever_clobbered. Sets the fields in DATA for all |
clobbered hard regs in SETREG. */ |
|
static void |
s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data) |
{ |
int *regs_ever_clobbered = (int *)data; |
unsigned int i, regno; |
enum machine_mode mode = GET_MODE (setreg); |
|
if (GET_CODE (setreg) == SUBREG) |
{ |
rtx inner = SUBREG_REG (setreg); |
if (!GENERAL_REG_P (inner)) |
return; |
regno = subreg_regno (setreg); |
} |
else if (GENERAL_REG_P (setreg)) |
regno = REGNO (setreg); |
else |
return; |
|
for (i = regno; |
i < regno + HARD_REGNO_NREGS (regno, mode); |
i++) |
regs_ever_clobbered[i] = 1; |
} |
|
/* Walks through all basic blocks of the current function looking |
for clobbered hard regs using s390_reg_clobbered_rtx. The fields |
of the passed integer array REGS_EVER_CLOBBERED are set to one for |
each of those regs. */ |
|
static void |
s390_regs_ever_clobbered (int *regs_ever_clobbered) |
{ |
basic_block cur_bb; |
rtx cur_insn; |
unsigned int i; |
|
memset (regs_ever_clobbered, 0, 16 * sizeof (int)); |
|
/* For non-leaf functions we have to consider all call clobbered regs to be |
clobbered. */ |
if (!current_function_is_leaf) |
{ |
for (i = 0; i < 16; i++) |
regs_ever_clobbered[i] = call_really_used_regs[i]; |
} |
|
/* Make the "magic" eh_return registers live if necessary. For regs_ever_live |
this work is done by liveness analysis (mark_regs_live_at_end). |
Special care is needed for functions containing landing pads. Landing pads |
may use the eh registers, but the code which sets these registers is not |
contained in that function. Hence s390_regs_ever_clobbered is not able to |
deal with this automatically. */ |
if (current_function_calls_eh_return || cfun->machine->has_landing_pad_p) |
for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++) |
if (current_function_calls_eh_return |
|| (cfun->machine->has_landing_pad_p |
&& regs_ever_live [EH_RETURN_DATA_REGNO (i)])) |
regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1; |
|
/* For nonlocal gotos all call-saved registers have to be saved. |
This flag is also set for the unwinding code in libgcc. |
See expand_builtin_unwind_init. For regs_ever_live this is done by |
reload. */ |
if (current_function_has_nonlocal_label) |
for (i = 0; i < 16; i++) |
if (!call_really_used_regs[i]) |
regs_ever_clobbered[i] = 1; |
|
FOR_EACH_BB (cur_bb) |
{ |
FOR_BB_INSNS (cur_bb, cur_insn) |
{ |
if (INSN_P (cur_insn)) |
note_stores (PATTERN (cur_insn), |
s390_reg_clobbered_rtx, |
regs_ever_clobbered); |
} |
} |
} |
|
/* Determine the frame area which actually has to be accessed |
in the function epilogue. The values are stored at the |
given pointers AREA_BOTTOM (address of the lowest used stack |
address) and AREA_TOP (address of the first item which does |
not belong to the stack frame). */ |
|
static void |
s390_frame_area (int *area_bottom, int *area_top) |
{ |
int b, t; |
int i; |
|
b = INT_MAX; |
t = INT_MIN; |
|
if (cfun_frame_layout.first_restore_gpr != -1) |
{ |
b = (cfun_frame_layout.gprs_offset |
+ cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD); |
t = b + (cfun_frame_layout.last_restore_gpr |
- cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD; |
} |
|
if (TARGET_64BIT && cfun_save_high_fprs_p) |
{ |
b = MIN (b, cfun_frame_layout.f8_offset); |
t = MAX (t, (cfun_frame_layout.f8_offset |
+ cfun_frame_layout.high_fprs * 8)); |
} |
|
if (!TARGET_64BIT) |
for (i = 2; i < 4; i++) |
if (cfun_fpr_bit_p (i)) |
{ |
b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8); |
t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8); |
} |
|
*area_bottom = b; |
*area_top = t; |
} |
|
/* Fill cfun->machine with info about register usage of current function. |
Return in CLOBBERED_REGS which GPRs are currently considered set. */ |
|
static void |
s390_register_info (int clobbered_regs[]) |
{ |
int i, j; |
|
/* fprs 8 - 15 are call saved for 64 Bit ABI. */ |
cfun_frame_layout.fpr_bitmap = 0; |
cfun_frame_layout.high_fprs = 0; |
if (TARGET_64BIT) |
for (i = 24; i < 32; i++) |
if (regs_ever_live[i] && !global_regs[i]) |
{ |
cfun_set_fpr_bit (i - 16); |
cfun_frame_layout.high_fprs++; |
} |
|
/* Find first and last gpr to be saved. We trust regs_ever_live |
data, except that we don't save and restore global registers. |
|
Also, all registers with special meaning to the compiler need |
to be handled extra. */ |
|
s390_regs_ever_clobbered (clobbered_regs); |
|
for (i = 0; i < 16; i++) |
clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i]; |
|
if (frame_pointer_needed) |
clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1; |
|
if (flag_pic) |
clobbered_regs[PIC_OFFSET_TABLE_REGNUM] |
|= regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; |
|
clobbered_regs[BASE_REGNUM] |
|= (cfun->machine->base_reg |
&& REGNO (cfun->machine->base_reg) == BASE_REGNUM); |
|
clobbered_regs[RETURN_REGNUM] |
|= (!current_function_is_leaf |
|| TARGET_TPF_PROFILING |
|| cfun->machine->split_branches_pending_p |
|| cfun_frame_layout.save_return_addr_p |
|| current_function_calls_eh_return |
|| current_function_stdarg); |
|
clobbered_regs[STACK_POINTER_REGNUM] |
|= (!current_function_is_leaf |
|| TARGET_TPF_PROFILING |
|| cfun_save_high_fprs_p |
|| get_frame_size () > 0 |
|| current_function_calls_alloca |
|| current_function_stdarg); |
|
for (i = 6; i < 16; i++) |
if (regs_ever_live[i] || clobbered_regs[i]) |
break; |
for (j = 15; j > i; j--) |
if (regs_ever_live[j] || clobbered_regs[j]) |
break; |
|
if (i == 16) |
{ |
/* Nothing to save/restore. */ |
cfun_frame_layout.first_save_gpr_slot = -1; |
cfun_frame_layout.last_save_gpr_slot = -1; |
cfun_frame_layout.first_save_gpr = -1; |
cfun_frame_layout.first_restore_gpr = -1; |
cfun_frame_layout.last_save_gpr = -1; |
cfun_frame_layout.last_restore_gpr = -1; |
} |
else |
{ |
/* Save slots for gprs from i to j. */ |
cfun_frame_layout.first_save_gpr_slot = i; |
cfun_frame_layout.last_save_gpr_slot = j; |
|
for (i = cfun_frame_layout.first_save_gpr_slot; |
i < cfun_frame_layout.last_save_gpr_slot + 1; |
i++) |
if (clobbered_regs[i]) |
break; |
|
for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--) |
if (clobbered_regs[j]) |
break; |
|
if (i == cfun_frame_layout.last_save_gpr_slot + 1) |
{ |
/* Nothing to save/restore. */ |
cfun_frame_layout.first_save_gpr = -1; |
cfun_frame_layout.first_restore_gpr = -1; |
cfun_frame_layout.last_save_gpr = -1; |
cfun_frame_layout.last_restore_gpr = -1; |
} |
else |
{ |
/* Save / Restore from gpr i to j. */ |
cfun_frame_layout.first_save_gpr = i; |
cfun_frame_layout.first_restore_gpr = i; |
cfun_frame_layout.last_save_gpr = j; |
cfun_frame_layout.last_restore_gpr = j; |
} |
} |
|
if (current_function_stdarg) |
{ |
/* Varargs functions need to save gprs 2 to 6. */ |
if (cfun->va_list_gpr_size |
&& current_function_args_info.gprs < GP_ARG_NUM_REG) |
{ |
int min_gpr = current_function_args_info.gprs; |
int max_gpr = min_gpr + cfun->va_list_gpr_size; |
if (max_gpr > GP_ARG_NUM_REG) |
max_gpr = GP_ARG_NUM_REG; |
|
if (cfun_frame_layout.first_save_gpr == -1 |
|| cfun_frame_layout.first_save_gpr > 2 + min_gpr) |
{ |
cfun_frame_layout.first_save_gpr = 2 + min_gpr; |
cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr; |
} |
|
if (cfun_frame_layout.last_save_gpr == -1 |
|| cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1) |
{ |
cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1; |
cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1; |
} |
} |
|
/* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved. */ |
if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size |
&& current_function_args_info.fprs < FP_ARG_NUM_REG) |
{ |
int min_fpr = current_function_args_info.fprs; |
int max_fpr = min_fpr + cfun->va_list_fpr_size; |
if (max_fpr > FP_ARG_NUM_REG) |
max_fpr = FP_ARG_NUM_REG; |
|
/* ??? This is currently required to ensure proper location |
of the fpr save slots within the va_list save area. */ |
if (TARGET_PACKED_STACK) |
min_fpr = 0; |
|
for (i = min_fpr; i < max_fpr; i++) |
cfun_set_fpr_bit (i); |
} |
} |
|
if (!TARGET_64BIT) |
for (i = 2; i < 4; i++) |
if (regs_ever_live[i + 16] && !global_regs[i + 16]) |
cfun_set_fpr_bit (i); |
} |
|
/* Fill cfun->machine with info about frame of current function. */ |
|
static void |
s390_frame_info (void) |
{ |
int i; |
|
cfun_frame_layout.frame_size = get_frame_size (); |
if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000) |
fatal_error ("total size of local variables exceeds architecture limit"); |
|
if (!TARGET_PACKED_STACK) |
{ |
cfun_frame_layout.backchain_offset = 0; |
cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD; |
cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8; |
cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8; |
cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot |
* UNITS_PER_WORD); |
} |
else if (TARGET_BACKCHAIN) /* kernel stack layout */ |
{ |
cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET |
- UNITS_PER_WORD); |
cfun_frame_layout.gprs_offset |
= (cfun_frame_layout.backchain_offset |
- (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1) |
* UNITS_PER_WORD); |
|
if (TARGET_64BIT) |
{ |
cfun_frame_layout.f4_offset |
= (cfun_frame_layout.gprs_offset |
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); |
|
cfun_frame_layout.f0_offset |
= (cfun_frame_layout.f4_offset |
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); |
} |
else |
{ |
/* On 31 bit we have to care about alignment of the |
floating point regs to provide fastest access. */ |
cfun_frame_layout.f0_offset |
= ((cfun_frame_layout.gprs_offset |
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1)) |
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); |
|
cfun_frame_layout.f4_offset |
= (cfun_frame_layout.f0_offset |
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); |
} |
} |
else /* no backchain */ |
{ |
cfun_frame_layout.f4_offset |
= (STACK_POINTER_OFFSET |
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); |
|
cfun_frame_layout.f0_offset |
= (cfun_frame_layout.f4_offset |
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); |
|
cfun_frame_layout.gprs_offset |
= cfun_frame_layout.f0_offset - cfun_gprs_save_area_size; |
} |
|
if (current_function_is_leaf |
&& !TARGET_TPF_PROFILING |
&& cfun_frame_layout.frame_size == 0 |
&& !cfun_save_high_fprs_p |
&& !current_function_calls_alloca |
&& !current_function_stdarg) |
return; |
|
if (!TARGET_PACKED_STACK) |
cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET |
+ current_function_outgoing_args_size |
+ cfun_frame_layout.high_fprs * 8); |
else |
{ |
if (TARGET_BACKCHAIN) |
cfun_frame_layout.frame_size += UNITS_PER_WORD; |
|
/* No alignment trouble here because f8-f15 are only saved under |
64 bit. */ |
cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset, |
cfun_frame_layout.f4_offset), |
cfun_frame_layout.gprs_offset) |
- cfun_frame_layout.high_fprs * 8); |
|
cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8; |
|
for (i = 0; i < 8; i++) |
if (cfun_fpr_bit_p (i)) |
cfun_frame_layout.frame_size += 8; |
|
cfun_frame_layout.frame_size += cfun_gprs_save_area_size; |
|
/* If under 31 bit an odd number of gprs has to be saved we have to adjust |
the frame size to sustain 8 byte alignment of stack frames. */ |
cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size + |
STACK_BOUNDARY / BITS_PER_UNIT - 1) |
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1)); |
|
cfun_frame_layout.frame_size += current_function_outgoing_args_size; |
} |
} |
|
/* Generate frame layout. Fills in register and frame data for the current |
function in cfun->machine. This routine can be called multiple times; |
it will re-do the complete frame layout every time. */ |
|
static void |
s390_init_frame_layout (void) |
{ |
HOST_WIDE_INT frame_size; |
int base_used; |
int clobbered_regs[16]; |
|
/* On S/390 machines, we may need to perform branch splitting, which |
will require both base and return address register. We have no |
choice but to assume we're going to need them until right at the |
end of the machine dependent reorg phase. */ |
if (!TARGET_CPU_ZARCH) |
cfun->machine->split_branches_pending_p = true; |
|
do |
{ |
frame_size = cfun_frame_layout.frame_size; |
|
/* Try to predict whether we'll need the base register. */ |
base_used = cfun->machine->split_branches_pending_p |
|| current_function_uses_const_pool |
|| (!DISP_IN_RANGE (frame_size) |
&& !CONST_OK_FOR_K (frame_size)); |
|
/* Decide which register to use as literal pool base. In small |
leaf functions, try to use an unused call-clobbered register |
as base register to avoid save/restore overhead. */ |
if (!base_used) |
cfun->machine->base_reg = NULL_RTX; |
else if (current_function_is_leaf && !regs_ever_live[5]) |
cfun->machine->base_reg = gen_rtx_REG (Pmode, 5); |
else |
cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM); |
|
s390_register_info (clobbered_regs); |
s390_frame_info (); |
} |
while (frame_size != cfun_frame_layout.frame_size); |
} |
|
/* Update frame layout. Recompute actual register save data based on |
current info and update regs_ever_live for the special registers. |
May be called multiple times, but may never cause *more* registers |
to be saved than s390_init_frame_layout allocated room for. */ |
|
static void |
s390_update_frame_layout (void) |
{ |
int clobbered_regs[16]; |
|
s390_register_info (clobbered_regs); |
|
regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM]; |
regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM]; |
regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM]; |
|
if (cfun->machine->base_reg) |
regs_ever_live[REGNO (cfun->machine->base_reg)] = 1; |
} |
|
/* Return true if it is legal to put a value with MODE into REGNO. */ |
|
bool |
s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) |
{ |
switch (REGNO_REG_CLASS (regno)) |
{ |
case FP_REGS: |
if (REGNO_PAIR_OK (regno, mode)) |
{ |
if (mode == SImode || mode == DImode) |
return true; |
|
if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT) |
return true; |
} |
break; |
case ADDR_REGS: |
if (FRAME_REGNO_P (regno) && mode == Pmode) |
return true; |
|
/* fallthrough */ |
case GENERAL_REGS: |
if (REGNO_PAIR_OK (regno, mode)) |
{ |
if (TARGET_64BIT |
|| (mode != TFmode && mode != TCmode && mode != TDmode)) |
return true; |
} |
break; |
case CC_REGS: |
if (GET_MODE_CLASS (mode) == MODE_CC) |
return true; |
break; |
case ACCESS_REGS: |
if (REGNO_PAIR_OK (regno, mode)) |
{ |
if (mode == SImode || mode == Pmode) |
return true; |
} |
break; |
default: |
return false; |
} |
|
return false; |
} |
|
/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ |
|
bool |
s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg) |
{ |
/* Once we've decided upon a register to use as base register, it must |
no longer be used for any other purpose. */ |
if (cfun->machine->base_reg) |
if (REGNO (cfun->machine->base_reg) == old_reg |
|| REGNO (cfun->machine->base_reg) == new_reg) |
return false; |
|
return true; |
} |
|
/* Maximum number of registers to represent a value of mode MODE |
in a register of class CLASS. */ |
|
bool |
s390_class_max_nregs (enum reg_class class, enum machine_mode mode) |
{ |
switch (class) |
{ |
case FP_REGS: |
if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) |
return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8); |
else |
return (GET_MODE_SIZE (mode) + 8 - 1) / 8; |
case ACCESS_REGS: |
return (GET_MODE_SIZE (mode) + 4 - 1) / 4; |
default: |
break; |
} |
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; |
} |
|
/* Return true if register FROM can be eliminated via register TO. */ |
|
bool |
s390_can_eliminate (int from, int to) |
{ |
/* On zSeries machines, we have not marked the base register as fixed. |
Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM. |
If a function requires the base register, we say here that this |
elimination cannot be performed. This will cause reload to free |
up the base register (as if it were fixed). On the other hand, |
if the current function does *not* require the base register, we |
say here the elimination succeeds, which in turn allows reload |
to allocate the base register for any other purpose. */ |
if (from == BASE_REGNUM && to == BASE_REGNUM) |
{ |
if (TARGET_CPU_ZARCH) |
{ |
s390_init_frame_layout (); |
return cfun->machine->base_reg == NULL_RTX; |
} |
|
return false; |
} |
|
/* Everything else must point into the stack frame. */ |
gcc_assert (to == STACK_POINTER_REGNUM |
|| to == HARD_FRAME_POINTER_REGNUM); |
|
gcc_assert (from == FRAME_POINTER_REGNUM |
|| from == ARG_POINTER_REGNUM |
|| from == RETURN_ADDRESS_POINTER_REGNUM); |
|
/* Make sure we actually saved the return address. */ |
if (from == RETURN_ADDRESS_POINTER_REGNUM) |
if (!current_function_calls_eh_return |
&& !current_function_stdarg |
&& !cfun_frame_layout.save_return_addr_p) |
return false; |
|
return true; |
} |
|
/* Return offset between register FROM and TO initially after prolog. */ |
|
HOST_WIDE_INT |
s390_initial_elimination_offset (int from, int to) |
{ |
HOST_WIDE_INT offset; |
int index; |
|
/* ??? Why are we called for non-eliminable pairs? */ |
if (!s390_can_eliminate (from, to)) |
return 0; |
|
switch (from) |
{ |
case FRAME_POINTER_REGNUM: |
offset = (get_frame_size() |
+ STACK_POINTER_OFFSET |
+ current_function_outgoing_args_size); |
break; |
|
case ARG_POINTER_REGNUM: |
s390_init_frame_layout (); |
offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET; |
break; |
|
case RETURN_ADDRESS_POINTER_REGNUM: |
s390_init_frame_layout (); |
index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot; |
gcc_assert (index >= 0); |
offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset; |
offset += index * UNITS_PER_WORD; |
break; |
|
case BASE_REGNUM: |
offset = 0; |
break; |
|
default: |
gcc_unreachable (); |
} |
|
return offset; |
} |
|
/* Emit insn to save fpr REGNUM at offset OFFSET relative |
to register BASE. Return generated insn. */ |
|
static rtx |
save_fpr (rtx base, int offset, int regnum) |
{ |
rtx addr; |
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset)); |
|
if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG)) |
set_mem_alias_set (addr, get_varargs_alias_set ()); |
else |
set_mem_alias_set (addr, get_frame_alias_set ()); |
|
return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum)); |
} |
|
/* Emit insn to restore fpr REGNUM from offset OFFSET relative |
to register BASE. Return generated insn. */ |
|
static rtx |
restore_fpr (rtx base, int offset, int regnum) |
{ |
rtx addr; |
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset)); |
set_mem_alias_set (addr, get_frame_alias_set ()); |
|
return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr); |
} |
|
/* Generate insn to save registers FIRST to LAST into |
the register save area located at offset OFFSET |
relative to register BASE. */ |
|
static rtx |
save_gprs (rtx base, int offset, int first, int last) |
{ |
rtx addr, insn, note; |
int i; |
|
addr = plus_constant (base, offset); |
addr = gen_rtx_MEM (Pmode, addr); |
|
set_mem_alias_set (addr, get_frame_alias_set ()); |
|
/* Special-case single register. */ |
if (first == last) |
{ |
if (TARGET_64BIT) |
insn = gen_movdi (addr, gen_rtx_REG (Pmode, first)); |
else |
insn = gen_movsi (addr, gen_rtx_REG (Pmode, first)); |
|
RTX_FRAME_RELATED_P (insn) = 1; |
return insn; |
} |
|
|
insn = gen_store_multiple (addr, |
gen_rtx_REG (Pmode, first), |
GEN_INT (last - first + 1)); |
|
if (first <= 6 && current_function_stdarg) |
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) |
{ |
rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0); |
|
if (first + i <= 6) |
set_mem_alias_set (mem, get_varargs_alias_set ()); |
} |
|
/* We need to set the FRAME_RELATED flag on all SETs |
inside the store-multiple pattern. |
|
However, we must not emit DWARF records for registers 2..5 |
if they are stored for use by variable arguments ... |
|
??? Unfortunately, it is not enough to simply not the |
FRAME_RELATED flags for those SETs, because the first SET |
of the PARALLEL is always treated as if it had the flag |
set, even if it does not. Therefore we emit a new pattern |
without those registers as REG_FRAME_RELATED_EXPR note. */ |
|
if (first >= 6) |
{ |
rtx pat = PATTERN (insn); |
|
for (i = 0; i < XVECLEN (pat, 0); i++) |
if (GET_CODE (XVECEXP (pat, 0, i)) == SET) |
RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; |
|
RTX_FRAME_RELATED_P (insn) = 1; |
} |
else if (last >= 6) |
{ |
addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD); |
note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), |
gen_rtx_REG (Pmode, 6), |
GEN_INT (last - 6 + 1)); |
note = PATTERN (note); |
|
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
note, REG_NOTES (insn)); |
|
for (i = 0; i < XVECLEN (note, 0); i++) |
if (GET_CODE (XVECEXP (note, 0, i)) == SET) |
RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; |
|
RTX_FRAME_RELATED_P (insn) = 1; |
} |
|
return insn; |
} |
|
/* Generate insn to restore registers FIRST to LAST from |
the register save area located at offset OFFSET |
relative to register BASE. */ |
|
static rtx |
restore_gprs (rtx base, int offset, int first, int last) |
{ |
rtx addr, insn; |
|
addr = plus_constant (base, offset); |
addr = gen_rtx_MEM (Pmode, addr); |
set_mem_alias_set (addr, get_frame_alias_set ()); |
|
/* Special-case single register. */ |
if (first == last) |
{ |
if (TARGET_64BIT) |
insn = gen_movdi (gen_rtx_REG (Pmode, first), addr); |
else |
insn = gen_movsi (gen_rtx_REG (Pmode, first), addr); |
|
return insn; |
} |
|
insn = gen_load_multiple (gen_rtx_REG (Pmode, first), |
addr, |
GEN_INT (last - first + 1)); |
return insn; |
} |
|
/* Return insn sequence to load the GOT register. */ |
|
static GTY(()) rtx got_symbol; |
rtx |
s390_load_got (void) |
{ |
rtx insns; |
|
if (!got_symbol) |
{ |
got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); |
SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL; |
} |
|
start_sequence (); |
|
if (TARGET_CPU_ZARCH) |
{ |
emit_move_insn (pic_offset_table_rtx, got_symbol); |
} |
else |
{ |
rtx offset; |
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol), |
UNSPEC_LTREL_OFFSET); |
offset = gen_rtx_CONST (Pmode, offset); |
offset = force_const_mem (Pmode, offset); |
|
emit_move_insn (pic_offset_table_rtx, offset); |
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)), |
UNSPEC_LTREL_BASE); |
offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset); |
|
emit_move_insn (pic_offset_table_rtx, offset); |
} |
|
insns = get_insns (); |
end_sequence (); |
return insns; |
} |
|
/* Expand the prologue into a bunch of separate insns. */ |
|
void |
s390_emit_prologue (void) |
{ |
rtx insn, addr; |
rtx temp_reg; |
int i; |
int offset; |
int next_fpr = 0; |
|
/* Complete frame layout. */ |
|
s390_update_frame_layout (); |
|
/* Annotate all constant pool references to let the scheduler know |
they implicitly use the base register. */ |
|
push_topmost_sequence (); |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
if (INSN_P (insn)) |
annotate_constant_pool_refs (&PATTERN (insn)); |
|
pop_topmost_sequence (); |
|
/* Choose best register to use for temp use within prologue. |
See below for why TPF must use the register 1. */ |
|
if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) |
&& !current_function_is_leaf |
&& !TARGET_TPF_PROFILING) |
temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); |
else |
temp_reg = gen_rtx_REG (Pmode, 1); |
|
/* Save call saved gprs. */ |
if (cfun_frame_layout.first_save_gpr != -1) |
{ |
insn = save_gprs (stack_pointer_rtx, |
cfun_frame_layout.gprs_offset + |
UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr |
- cfun_frame_layout.first_save_gpr_slot), |
cfun_frame_layout.first_save_gpr, |
cfun_frame_layout.last_save_gpr); |
emit_insn (insn); |
} |
|
/* Dummy insn to mark literal pool slot. */ |
|
if (cfun->machine->base_reg) |
emit_insn (gen_main_pool (cfun->machine->base_reg)); |
|
offset = cfun_frame_layout.f0_offset; |
|
/* Save f0 and f2. */ |
for (i = 0; i < 2; i++) |
{ |
if (cfun_fpr_bit_p (i)) |
{ |
save_fpr (stack_pointer_rtx, offset, i + 16); |
offset += 8; |
} |
else if (!TARGET_PACKED_STACK) |
offset += 8; |
} |
|
/* Save f4 and f6. */ |
offset = cfun_frame_layout.f4_offset; |
for (i = 2; i < 4; i++) |
{ |
if (cfun_fpr_bit_p (i)) |
{ |
insn = save_fpr (stack_pointer_rtx, offset, i + 16); |
offset += 8; |
|
/* If f4 and f6 are call clobbered they are saved due to stdargs and |
therefore are not frame related. */ |
if (!call_really_used_regs[i + 16]) |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
else if (!TARGET_PACKED_STACK) |
offset += 8; |
} |
|
if (TARGET_PACKED_STACK |
&& cfun_save_high_fprs_p |
&& cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0) |
{ |
offset = (cfun_frame_layout.f8_offset |
+ (cfun_frame_layout.high_fprs - 1) * 8); |
|
for (i = 15; i > 7 && offset >= 0; i--) |
if (cfun_fpr_bit_p (i)) |
{ |
insn = save_fpr (stack_pointer_rtx, offset, i + 16); |
|
RTX_FRAME_RELATED_P (insn) = 1; |
offset -= 8; |
} |
if (offset >= cfun_frame_layout.f8_offset) |
next_fpr = i + 16; |
} |
|
if (!TARGET_PACKED_STACK) |
next_fpr = cfun_save_high_fprs_p ? 31 : 0; |
|
/* Decrement stack pointer. */ |
|
if (cfun_frame_layout.frame_size > 0) |
{ |
rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size); |
|
if (s390_stack_size) |
{ |
HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1) |
& ~(s390_stack_guard - 1)); |
rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx, |
GEN_INT (stack_check_mask)); |
|
if (TARGET_64BIT) |
gen_cmpdi (t, const0_rtx); |
else |
gen_cmpsi (t, const0_rtx); |
|
emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, |
gen_rtx_REG (CCmode, |
CC_REGNUM), |
const0_rtx), |
const0_rtx)); |
} |
|
if (s390_warn_framesize > 0 |
&& cfun_frame_layout.frame_size >= s390_warn_framesize) |
warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes", |
current_function_name (), cfun_frame_layout.frame_size); |
|
if (s390_warn_dynamicstack_p && cfun->calls_alloca) |
warning (0, "%qs uses dynamic stack allocation", current_function_name ()); |
|
/* Save incoming stack pointer into temp reg. */ |
if (TARGET_BACKCHAIN || next_fpr) |
insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx)); |
|
/* Subtract frame size from stack pointer. */ |
|
if (DISP_IN_RANGE (INTVAL (frame_off))) |
{ |
insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx, |
gen_rtx_PLUS (Pmode, stack_pointer_rtx, |
frame_off)); |
insn = emit_insn (insn); |
} |
else |
{ |
if (!CONST_OK_FOR_K (INTVAL (frame_off))) |
frame_off = force_const_mem (Pmode, frame_off); |
|
insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off)); |
annotate_constant_pool_refs (&PATTERN (insn)); |
} |
|
RTX_FRAME_RELATED_P (insn) = 1; |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
gen_rtx_SET (VOIDmode, stack_pointer_rtx, |
gen_rtx_PLUS (Pmode, stack_pointer_rtx, |
GEN_INT (-cfun_frame_layout.frame_size))), |
REG_NOTES (insn)); |
|
/* Set backchain. */ |
|
if (TARGET_BACKCHAIN) |
{ |
if (cfun_frame_layout.backchain_offset) |
addr = gen_rtx_MEM (Pmode, |
plus_constant (stack_pointer_rtx, |
cfun_frame_layout.backchain_offset)); |
else |
addr = gen_rtx_MEM (Pmode, stack_pointer_rtx); |
set_mem_alias_set (addr, get_frame_alias_set ()); |
insn = emit_insn (gen_move_insn (addr, temp_reg)); |
} |
|
/* If we support asynchronous exceptions (e.g. for Java), |
we need to make sure the backchain pointer is set up |
before any possibly trapping memory access. */ |
|
if (TARGET_BACKCHAIN && flag_non_call_exceptions) |
{ |
addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); |
emit_insn (gen_rtx_CLOBBER (VOIDmode, addr)); |
} |
} |
|
/* Save fprs 8 - 15 (64 bit ABI). */ |
|
if (cfun_save_high_fprs_p && next_fpr) |
{ |
insn = emit_insn (gen_add2_insn (temp_reg, |
GEN_INT (cfun_frame_layout.f8_offset))); |
|
offset = 0; |
|
for (i = 24; i <= next_fpr; i++) |
if (cfun_fpr_bit_p (i - 16)) |
{ |
rtx addr = plus_constant (stack_pointer_rtx, |
cfun_frame_layout.frame_size |
+ cfun_frame_layout.f8_offset |
+ offset); |
|
insn = save_fpr (temp_reg, offset, i); |
offset += 8; |
RTX_FRAME_RELATED_P (insn) = 1; |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
gen_rtx_SET (VOIDmode, |
gen_rtx_MEM (DFmode, addr), |
gen_rtx_REG (DFmode, i)), |
REG_NOTES (insn)); |
} |
} |
|
/* Set frame pointer, if needed. */ |
|
if (frame_pointer_needed) |
{ |
insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); |
RTX_FRAME_RELATED_P (insn) = 1; |
} |
|
/* Set up got pointer, if needed. */ |
|
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) |
{ |
rtx insns = s390_load_got (); |
|
for (insn = insns; insn; insn = NEXT_INSN (insn)) |
{ |
annotate_constant_pool_refs (&PATTERN (insn)); |
|
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, |
REG_NOTES (insn)); |
} |
|
emit_insn (insns); |
} |
|
if (TARGET_TPF_PROFILING) |
{ |
/* Generate a BAS instruction to serve as a function |
entry intercept to facilitate the use of tracing |
algorithms located at the branch target. */ |
emit_insn (gen_prologue_tpf ()); |
|
/* Emit a blockage here so that all code |
lies between the profiling mechanisms. */ |
emit_insn (gen_blockage ()); |
} |
} |
|
/* Expand the epilogue into a bunch of separate insns. */ |
|
void |
s390_emit_epilogue (bool sibcall) |
{ |
rtx frame_pointer, return_reg; |
int area_bottom, area_top, offset = 0; |
int next_offset; |
rtvec p; |
int i; |
|
if (TARGET_TPF_PROFILING) |
{ |
|
/* Generate a BAS instruction to serve as a function |
entry intercept to facilitate the use of tracing |
algorithms located at the branch target. */ |
|
/* Emit a blockage here so that all code |
lies between the profiling mechanisms. */ |
emit_insn (gen_blockage ()); |
|
emit_insn (gen_epilogue_tpf ()); |
} |
|
/* Check whether to use frame or stack pointer for restore. */ |
|
frame_pointer = (frame_pointer_needed |
? hard_frame_pointer_rtx : stack_pointer_rtx); |
|
s390_frame_area (&area_bottom, &area_top); |
|
/* Check whether we can access the register save area. |
If not, increment the frame pointer as required. */ |
|
if (area_top <= area_bottom) |
{ |
/* Nothing to restore. */ |
} |
else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom) |
&& DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1)) |
{ |
/* Area is in range. */ |
offset = cfun_frame_layout.frame_size; |
} |
else |
{ |
rtx insn, frame_off; |
|
offset = area_bottom < 0 ? -area_bottom : 0; |
frame_off = GEN_INT (cfun_frame_layout.frame_size - offset); |
|
if (DISP_IN_RANGE (INTVAL (frame_off))) |
{ |
insn = gen_rtx_SET (VOIDmode, frame_pointer, |
gen_rtx_PLUS (Pmode, frame_pointer, frame_off)); |
insn = emit_insn (insn); |
} |
else |
{ |
if (!CONST_OK_FOR_K (INTVAL (frame_off))) |
frame_off = force_const_mem (Pmode, frame_off); |
|
insn = emit_insn (gen_add2_insn (frame_pointer, frame_off)); |
annotate_constant_pool_refs (&PATTERN (insn)); |
} |
} |
|
/* Restore call saved fprs. */ |
|
if (TARGET_64BIT) |
{ |
if (cfun_save_high_fprs_p) |
{ |
next_offset = cfun_frame_layout.f8_offset; |
for (i = 24; i < 32; i++) |
{ |
if (cfun_fpr_bit_p (i - 16)) |
{ |
restore_fpr (frame_pointer, |
offset + next_offset, i); |
next_offset += 8; |
} |
} |
} |
|
} |
else |
{ |
next_offset = cfun_frame_layout.f4_offset; |
for (i = 18; i < 20; i++) |
{ |
if (cfun_fpr_bit_p (i - 16)) |
{ |
restore_fpr (frame_pointer, |
offset + next_offset, i); |
next_offset += 8; |
} |
else if (!TARGET_PACKED_STACK) |
next_offset += 8; |
} |
|
} |
|
/* Return register. */ |
|
return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); |
|
/* Restore call saved gprs. */ |
|
if (cfun_frame_layout.first_restore_gpr != -1) |
{ |
rtx insn, addr; |
int i; |
|
/* Check for global register and save them |
to stack location from where they get restored. */ |
|
for (i = cfun_frame_layout.first_restore_gpr; |
i <= cfun_frame_layout.last_restore_gpr; |
i++) |
{ |
/* These registers are special and need to be |
restored in any case. */ |
if (i == STACK_POINTER_REGNUM |
|| i == RETURN_REGNUM |
|| i == BASE_REGNUM |
|| (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM)) |
continue; |
|
if (global_regs[i]) |
{ |
addr = plus_constant (frame_pointer, |
offset + cfun_frame_layout.gprs_offset |
+ (i - cfun_frame_layout.first_save_gpr_slot) |
* UNITS_PER_WORD); |
addr = gen_rtx_MEM (Pmode, addr); |
set_mem_alias_set (addr, get_frame_alias_set ()); |
emit_move_insn (addr, gen_rtx_REG (Pmode, i)); |
} |
} |
|
if (! sibcall) |
{ |
/* Fetch return address from stack before load multiple, |
this will do good for scheduling. */ |
|
if (cfun_frame_layout.save_return_addr_p |
|| (cfun_frame_layout.first_restore_gpr < BASE_REGNUM |
&& cfun_frame_layout.last_restore_gpr > RETURN_REGNUM)) |
{ |
int return_regnum = find_unused_clobbered_reg(); |
if (!return_regnum) |
return_regnum = 4; |
return_reg = gen_rtx_REG (Pmode, return_regnum); |
|
addr = plus_constant (frame_pointer, |
offset + cfun_frame_layout.gprs_offset |
+ (RETURN_REGNUM |
- cfun_frame_layout.first_save_gpr_slot) |
* UNITS_PER_WORD); |
addr = gen_rtx_MEM (Pmode, addr); |
set_mem_alias_set (addr, get_frame_alias_set ()); |
emit_move_insn (return_reg, addr); |
} |
} |
|
insn = restore_gprs (frame_pointer, |
offset + cfun_frame_layout.gprs_offset |
+ (cfun_frame_layout.first_restore_gpr |
- cfun_frame_layout.first_save_gpr_slot) |
* UNITS_PER_WORD, |
cfun_frame_layout.first_restore_gpr, |
cfun_frame_layout.last_restore_gpr); |
emit_insn (insn); |
} |
|
if (! sibcall) |
{ |
|
/* Return to caller. */ |
|
p = rtvec_alloc (2); |
|
RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode); |
RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg); |
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
} |
} |
|
|
/* Return the size in bytes of a function argument of |
type TYPE and/or mode MODE. At least one of TYPE or |
MODE must be specified. */ |
|
static int |
s390_function_arg_size (enum machine_mode mode, tree type) |
{ |
if (type) |
return int_size_in_bytes (type); |
|
/* No type info available for some library calls ... */ |
if (mode != BLKmode) |
return GET_MODE_SIZE (mode); |
|
/* If we have neither type nor mode, abort */ |
gcc_unreachable (); |
} |
|
/* Return true if a function argument of type TYPE and mode MODE |
is to be passed in a floating-point register, if available. */ |
|
static bool |
s390_function_arg_float (enum machine_mode mode, tree type) |
{ |
int size = s390_function_arg_size (mode, type); |
if (size > 8) |
return false; |
|
/* Soft-float changes the ABI: no floating-point registers are used. */ |
if (TARGET_SOFT_FLOAT) |
return false; |
|
/* No type info available for some library calls ... */ |
if (!type) |
return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode; |
|
/* The ABI says that record types with a single member are treated |
just like that member would be. */ |
while (TREE_CODE (type) == RECORD_TYPE) |
{ |
tree field, single = NULL_TREE; |
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) |
{ |
if (TREE_CODE (field) != FIELD_DECL) |
continue; |
|
if (single == NULL_TREE) |
single = TREE_TYPE (field); |
else |
return false; |
} |
|
if (single == NULL_TREE) |
return false; |
else |
type = single; |
} |
|
return TREE_CODE (type) == REAL_TYPE; |
} |
|
/* Return true if a function argument of type TYPE and mode MODE |
is to be passed in an integer register, or a pair of integer |
registers, if available. */ |
|
static bool |
s390_function_arg_integer (enum machine_mode mode, tree type) |
{ |
int size = s390_function_arg_size (mode, type); |
if (size > 8) |
return false; |
|
/* No type info available for some library calls ... */ |
if (!type) |
return GET_MODE_CLASS (mode) == MODE_INT |
|| (TARGET_SOFT_FLOAT && SCALAR_FLOAT_MODE_P (mode)); |
|
/* We accept small integral (and similar) types. */ |
if (INTEGRAL_TYPE_P (type) |
|| POINTER_TYPE_P (type) |
|| TREE_CODE (type) == OFFSET_TYPE |
|| (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE)) |
return true; |
|
/* We also accept structs of size 1, 2, 4, 8 that are not |
passed in floating-point registers. */ |
if (AGGREGATE_TYPE_P (type) |
&& exact_log2 (size) >= 0 |
&& !s390_function_arg_float (mode, type)) |
return true; |
|
return false; |
} |
|
/* Return 1 if a function argument of type TYPE and mode MODE |
is to be passed by reference. The ABI specifies that only |
structures of size 1, 2, 4, or 8 bytes are passed by value, |
all other structures (and complex numbers) are passed by |
reference. */ |
|
static bool |
s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, |
enum machine_mode mode, tree type, |
bool named ATTRIBUTE_UNUSED) |
{ |
int size = s390_function_arg_size (mode, type); |
if (size > 8) |
return true; |
|
if (type) |
{ |
if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0) |
return 1; |
|
if (TREE_CODE (type) == COMPLEX_TYPE |
|| TREE_CODE (type) == VECTOR_TYPE) |
return 1; |
} |
|
return 0; |
} |
|
/* Update the data in CUM to advance over an argument of mode MODE and |
data type TYPE. (TYPE is null for libcalls where that information |
may not be available.). The boolean NAMED specifies whether the |
argument is a named argument (as opposed to an unnamed argument |
matching an ellipsis). */ |
|
void |
s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
tree type, int named ATTRIBUTE_UNUSED) |
{ |
if (s390_function_arg_float (mode, type)) |
{ |
cum->fprs += 1; |
} |
else if (s390_function_arg_integer (mode, type)) |
{ |
int size = s390_function_arg_size (mode, type); |
cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD); |
} |
else |
gcc_unreachable (); |
} |
|
/* Define where to put the arguments to a function. |
Value is zero to push the argument on the stack, |
or a hard register in which to store the argument. |
|
MODE is the argument's machine mode. |
TYPE is the data type of the argument (as a tree). |
This is null for libcalls where that information may |
not be available. |
CUM is a variable of type CUMULATIVE_ARGS which gives info about |
the preceding args and about the function being called. |
NAMED is nonzero if this argument is a named parameter |
(otherwise it is an extra parameter matching an ellipsis). |
|
On S/390, we use general purpose registers 2 through 6 to |
pass integer, pointer, and certain structure arguments, and |
floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit) |
to pass floating point arguments. All remaining arguments |
are pushed to the stack. */ |
|
rtx |
s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, |
int named ATTRIBUTE_UNUSED) |
{ |
if (s390_function_arg_float (mode, type)) |
{ |
if (cum->fprs + 1 > FP_ARG_NUM_REG) |
return 0; |
else |
return gen_rtx_REG (mode, cum->fprs + 16); |
} |
else if (s390_function_arg_integer (mode, type)) |
{ |
int size = s390_function_arg_size (mode, type); |
int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD; |
|
if (cum->gprs + n_gprs > GP_ARG_NUM_REG) |
return 0; |
else |
return gen_rtx_REG (mode, cum->gprs + 2); |
} |
|
/* After the real arguments, expand_call calls us once again |
with a void_type_node type. Whatever we return here is |
passed as operand 2 to the call expanders. |
|
We don't need this feature ... */ |
else if (type == void_type_node) |
return const0_rtx; |
|
gcc_unreachable (); |
} |
|
/* Return true if return values of type TYPE should be returned |
in a memory buffer whose address is passed by the caller as |
hidden first argument. */ |
|
static bool |
s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED) |
{ |
/* We accept small integral (and similar) types. */ |
if (INTEGRAL_TYPE_P (type) |
|| POINTER_TYPE_P (type) |
|| TREE_CODE (type) == OFFSET_TYPE |
|| TREE_CODE (type) == REAL_TYPE) |
return int_size_in_bytes (type) > 8; |
|
/* Aggregates and similar constructs are always returned |
in memory. */ |
if (AGGREGATE_TYPE_P (type) |
|| TREE_CODE (type) == COMPLEX_TYPE |
|| TREE_CODE (type) == VECTOR_TYPE) |
return true; |
|
/* ??? We get called on all sorts of random stuff from |
aggregate_value_p. We can't abort, but it's not clear |
what's safe to return. Pretend it's a struct I guess. */ |
return true; |
} |
|
/* Define where to return a (scalar) value of type TYPE. |
If TYPE is null, define where to return a (scalar) |
value of mode MODE from a libcall. */ |
|
rtx |
s390_function_value (tree type, enum machine_mode mode) |
{ |
if (type) |
{ |
int unsignedp = TYPE_UNSIGNED (type); |
mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1); |
} |
|
gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode)); |
gcc_assert (GET_MODE_SIZE (mode) <= 8); |
|
if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode)) |
return gen_rtx_REG (mode, 16); |
else |
return gen_rtx_REG (mode, 2); |
} |
|
|
/* Create and return the va_list datatype. |
|
On S/390, va_list is an array type equivalent to |
|
typedef struct __va_list_tag |
{ |
long __gpr; |
long __fpr; |
void *__overflow_arg_area; |
void *__reg_save_area; |
} va_list[1]; |
|
where __gpr and __fpr hold the number of general purpose |
or floating point arguments used up to now, respectively, |
__overflow_arg_area points to the stack location of the |
next argument passed on the stack, and __reg_save_area |
always points to the start of the register area in the |
call frame of the current function. The function prologue |
saves all registers used for argument passing into this |
area if the function uses variable arguments. */ |
|
static tree |
s390_build_builtin_va_list (void) |
{ |
tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl; |
|
record = lang_hooks.types.make_type (RECORD_TYPE); |
|
type_decl = |
build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); |
|
f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"), |
long_integer_type_node); |
f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"), |
long_integer_type_node); |
f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"), |
ptr_type_node); |
f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"), |
ptr_type_node); |
|
va_list_gpr_counter_field = f_gpr; |
va_list_fpr_counter_field = f_fpr; |
|
DECL_FIELD_CONTEXT (f_gpr) = record; |
DECL_FIELD_CONTEXT (f_fpr) = record; |
DECL_FIELD_CONTEXT (f_ovf) = record; |
DECL_FIELD_CONTEXT (f_sav) = record; |
|
TREE_CHAIN (record) = type_decl; |
TYPE_NAME (record) = type_decl; |
TYPE_FIELDS (record) = f_gpr; |
TREE_CHAIN (f_gpr) = f_fpr; |
TREE_CHAIN (f_fpr) = f_ovf; |
TREE_CHAIN (f_ovf) = f_sav; |
|
layout_type (record); |
|
/* The correct type is an array type of one element. */ |
return build_array_type (record, build_index_type (size_zero_node)); |
} |
|
/* Implement va_start by filling the va_list structure VALIST. |
STDARG_P is always true, and ignored. |
NEXTARG points to the first anonymous stack argument. |
|
The following global variables are used to initialize |
the va_list structure: |
|
current_function_args_info: |
holds number of gprs and fprs used for named arguments. |
current_function_arg_offset_rtx: |
holds the offset of the first anonymous stack argument |
(relative to the virtual arg pointer). */ |
|
void |
s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) |
{ |
HOST_WIDE_INT n_gpr, n_fpr; |
int off; |
tree f_gpr, f_fpr, f_ovf, f_sav; |
tree gpr, fpr, ovf, sav, t; |
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); |
f_fpr = TREE_CHAIN (f_gpr); |
f_ovf = TREE_CHAIN (f_fpr); |
f_sav = TREE_CHAIN (f_ovf); |
|
valist = build_va_arg_indirect_ref (valist); |
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); |
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); |
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); |
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); |
|
/* Count number of gp and fp argument registers used. */ |
|
n_gpr = current_function_args_info.gprs; |
n_fpr = current_function_args_info.fprs; |
|
if (cfun->va_list_gpr_size) |
{ |
t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr, |
build_int_cst (NULL_TREE, n_gpr)); |
TREE_SIDE_EFFECTS (t) = 1; |
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
} |
|
if (cfun->va_list_fpr_size) |
{ |
t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr, |
build_int_cst (NULL_TREE, n_fpr)); |
TREE_SIDE_EFFECTS (t) = 1; |
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
} |
|
/* Find the overflow area. */ |
if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG |
|| n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG) |
{ |
t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); |
|
off = INTVAL (current_function_arg_offset_rtx); |
off = off < 0 ? 0 : off; |
if (TARGET_DEBUG_ARG) |
fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n", |
(int)n_gpr, (int)n_fpr, off); |
|
t = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off)); |
|
t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); |
TREE_SIDE_EFFECTS (t) = 1; |
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
} |
|
/* Find the register save area. */ |
if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG) |
|| (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG)) |
{ |
t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx); |
t = build2 (PLUS_EXPR, TREE_TYPE (sav), t, |
build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD)); |
|
t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t); |
TREE_SIDE_EFFECTS (t) = 1; |
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
} |
} |
|
/* Implement va_arg by updating the va_list structure |
VALIST as required to retrieve an argument of type |
TYPE, and returning that argument. |
|
Generates code equivalent to: |
|
if (integral value) { |
if (size <= 4 && args.gpr < 5 || |
size > 4 && args.gpr < 4 ) |
ret = args.reg_save_area[args.gpr+8] |
else |
ret = *args.overflow_arg_area++; |
} else if (float value) { |
if (args.fgpr < 2) |
ret = args.reg_save_area[args.fpr+64] |
else |
ret = *args.overflow_arg_area++; |
} else if (aggregate value) { |
if (args.gpr < 5) |
ret = *args.reg_save_area[args.gpr] |
else |
ret = **args.overflow_arg_area++; |
} */ |
|
static tree |
s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, |
tree *post_p ATTRIBUTE_UNUSED) |
{ |
tree f_gpr, f_fpr, f_ovf, f_sav; |
tree gpr, fpr, ovf, sav, reg, t, u; |
int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg; |
tree lab_false, lab_over, addr; |
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); |
f_fpr = TREE_CHAIN (f_gpr); |
f_ovf = TREE_CHAIN (f_fpr); |
f_sav = TREE_CHAIN (f_ovf); |
|
valist = build_va_arg_indirect_ref (valist); |
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); |
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); |
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); |
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); |
|
size = int_size_in_bytes (type); |
|
if (pass_by_reference (NULL, TYPE_MODE (type), type, false)) |
{ |
if (TARGET_DEBUG_ARG) |
{ |
fprintf (stderr, "va_arg: aggregate type"); |
debug_tree (type); |
} |
|
/* Aggregates are passed by reference. */ |
indirect_p = 1; |
reg = gpr; |
n_reg = 1; |
|
/* kernel stack layout on 31 bit: It is assumed here that no padding |
will be added by s390_frame_info because for va_args always an even |
number of gprs has to be saved r15-r2 = 14 regs. */ |
sav_ofs = 2 * UNITS_PER_WORD; |
sav_scale = UNITS_PER_WORD; |
size = UNITS_PER_WORD; |
max_reg = GP_ARG_NUM_REG - n_reg; |
} |
else if (s390_function_arg_float (TYPE_MODE (type), type)) |
{ |
if (TARGET_DEBUG_ARG) |
{ |
fprintf (stderr, "va_arg: float type"); |
debug_tree (type); |
} |
|
/* FP args go in FP registers, if present. */ |
indirect_p = 0; |
reg = fpr; |
n_reg = 1; |
sav_ofs = 16 * UNITS_PER_WORD; |
sav_scale = 8; |
max_reg = FP_ARG_NUM_REG - n_reg; |
} |
else |
{ |
if (TARGET_DEBUG_ARG) |
{ |
fprintf (stderr, "va_arg: other type"); |
debug_tree (type); |
} |
|
/* Otherwise into GP registers. */ |
indirect_p = 0; |
reg = gpr; |
n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; |
|
/* kernel stack layout on 31 bit: It is assumed here that no padding |
will be added by s390_frame_info because for va_args always an even |
number of gprs has to be saved r15-r2 = 14 regs. */ |
sav_ofs = 2 * UNITS_PER_WORD; |
|
if (size < UNITS_PER_WORD) |
sav_ofs += UNITS_PER_WORD - size; |
|
sav_scale = UNITS_PER_WORD; |
max_reg = GP_ARG_NUM_REG - n_reg; |
} |
|
/* Pull the value out of the saved registers ... */ |
|
lab_false = create_artificial_label (); |
lab_over = create_artificial_label (); |
addr = create_tmp_var (ptr_type_node, "addr"); |
DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set (); |
|
t = fold_convert (TREE_TYPE (reg), size_int (max_reg)); |
t = build2 (GT_EXPR, boolean_type_node, reg, t); |
u = build1 (GOTO_EXPR, void_type_node, lab_false); |
t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE); |
gimplify_and_add (t, pre_p); |
|
t = build2 (PLUS_EXPR, ptr_type_node, sav, |
fold_convert (ptr_type_node, size_int (sav_ofs))); |
u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, |
fold_convert (TREE_TYPE (reg), size_int (sav_scale))); |
t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u)); |
|
t = build2 (MODIFY_EXPR, void_type_node, addr, t); |
gimplify_and_add (t, pre_p); |
|
t = build1 (GOTO_EXPR, void_type_node, lab_over); |
gimplify_and_add (t, pre_p); |
|
t = build1 (LABEL_EXPR, void_type_node, lab_false); |
append_to_statement_list (t, pre_p); |
|
|
/* ... Otherwise out of the overflow area. */ |
|
t = ovf; |
if (size < UNITS_PER_WORD) |
t = build2 (PLUS_EXPR, ptr_type_node, t, |
fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size))); |
|
gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); |
|
u = build2 (MODIFY_EXPR, void_type_node, addr, t); |
gimplify_and_add (u, pre_p); |
|
t = build2 (PLUS_EXPR, ptr_type_node, t, |
fold_convert (ptr_type_node, size_int (size))); |
t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t); |
gimplify_and_add (t, pre_p); |
|
t = build1 (LABEL_EXPR, void_type_node, lab_over); |
append_to_statement_list (t, pre_p); |
|
|
/* Increment register save count. */ |
|
u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, |
fold_convert (TREE_TYPE (reg), size_int (n_reg))); |
gimplify_and_add (u, pre_p); |
|
if (indirect_p) |
{ |
t = build_pointer_type (build_pointer_type (type)); |
addr = fold_convert (t, addr); |
addr = build_va_arg_indirect_ref (addr); |
} |
else |
{ |
t = build_pointer_type (type); |
addr = fold_convert (t, addr); |
} |
|
return build_va_arg_indirect_ref (addr); |
} |
|
|
/* Builtins. */ |
|
enum s390_builtin |
{ |
S390_BUILTIN_THREAD_POINTER, |
S390_BUILTIN_SET_THREAD_POINTER, |
|
S390_BUILTIN_max |
}; |
|
static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = { |
CODE_FOR_get_tp_64, |
CODE_FOR_set_tp_64 |
}; |
|
static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = { |
CODE_FOR_get_tp_31, |
CODE_FOR_set_tp_31 |
}; |
|
static void |
s390_init_builtins (void) |
{ |
tree ftype; |
|
ftype = build_function_type (ptr_type_node, void_list_node); |
lang_hooks.builtin_function ("__builtin_thread_pointer", ftype, |
S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD, |
NULL, NULL_TREE); |
|
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); |
lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype, |
S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, |
NULL, NULL_TREE); |
} |
|
/* Expand an expression EXP that calls a built-in function, |
with result going to TARGET if that's convenient |
(and in mode MODE if that's convenient). |
SUBTARGET may be used as the target for computing one of EXP's operands. |
IGNORE is nonzero if the value is to be ignored. */ |
|
static rtx |
s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, |
enum machine_mode mode ATTRIBUTE_UNUSED, |
int ignore ATTRIBUTE_UNUSED) |
{ |
#define MAX_ARGS 2 |
|
unsigned int const *code_for_builtin = |
TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31; |
|
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
tree arglist = TREE_OPERAND (exp, 1); |
enum insn_code icode; |
rtx op[MAX_ARGS], pat; |
int arity; |
bool nonvoid; |
|
if (fcode >= S390_BUILTIN_max) |
internal_error ("bad builtin fcode"); |
icode = code_for_builtin[fcode]; |
if (icode == 0) |
internal_error ("bad builtin fcode"); |
|
nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; |
|
for (arglist = TREE_OPERAND (exp, 1), arity = 0; |
arglist; |
arglist = TREE_CHAIN (arglist), arity++) |
{ |
const struct insn_operand_data *insn_op; |
|
tree arg = TREE_VALUE (arglist); |
if (arg == error_mark_node) |
return NULL_RTX; |
if (arity > MAX_ARGS) |
return NULL_RTX; |
|
insn_op = &insn_data[icode].operand[arity + nonvoid]; |
|
op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); |
|
if (!(*insn_op->predicate) (op[arity], insn_op->mode)) |
op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); |
} |
|
if (nonvoid) |
{ |
enum machine_mode tmode = insn_data[icode].operand[0].mode; |
if (!target |
|| GET_MODE (target) != tmode |
|| !(*insn_data[icode].operand[0].predicate) (target, tmode)) |
target = gen_reg_rtx (tmode); |
} |
|
switch (arity) |
{ |
case 0: |
pat = GEN_FCN (icode) (target); |
break; |
case 1: |
if (nonvoid) |
pat = GEN_FCN (icode) (target, op[0]); |
else |
pat = GEN_FCN (icode) (op[0]); |
break; |
case 2: |
pat = GEN_FCN (icode) (target, op[0], op[1]); |
break; |
default: |
gcc_unreachable (); |
} |
if (!pat) |
return NULL_RTX; |
emit_insn (pat); |
|
if (nonvoid) |
return target; |
else |
return const0_rtx; |
} |
|
|
/* Output assembly code for the trampoline template to |
stdio stream FILE. |
|
On S/390, we use gpr 1 internally in the trampoline code; |
gpr 0 is used to hold the static chain. */ |
|
void |
s390_trampoline_template (FILE *file) |
{ |
rtx op[2]; |
op[0] = gen_rtx_REG (Pmode, 0); |
op[1] = gen_rtx_REG (Pmode, 1); |
|
if (TARGET_64BIT) |
{ |
output_asm_insn ("basr\t%1,0", op); |
output_asm_insn ("lmg\t%0,%1,14(%1)", op); |
output_asm_insn ("br\t%1", op); |
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10)); |
} |
else |
{ |
output_asm_insn ("basr\t%1,0", op); |
output_asm_insn ("lm\t%0,%1,6(%1)", op); |
output_asm_insn ("br\t%1", op); |
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8)); |
} |
} |
|
/* Emit RTL insns to initialize the variable parts of a trampoline. |
FNADDR is an RTX for the address of the function's pure code. |
CXT is an RTX for the static chain value for the function. */ |
|
void |
s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt) |
{ |
emit_move_insn (gen_rtx_MEM (Pmode, |
memory_address (Pmode, |
plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt); |
emit_move_insn (gen_rtx_MEM (Pmode, |
memory_address (Pmode, |
plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr); |
} |
|
/* Return rtx for 64-bit constant formed from the 32-bit subwords |
LOW and HIGH, independent of the host word size. */ |
|
rtx |
s390_gen_rtx_const_DI (int high, int low) |
{ |
#if HOST_BITS_PER_WIDE_INT >= 64 |
HOST_WIDE_INT val; |
val = (HOST_WIDE_INT)high; |
val <<= 32; |
val |= (HOST_WIDE_INT)low; |
|
return GEN_INT (val); |
#else |
#if HOST_BITS_PER_WIDE_INT >= 32 |
return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode); |
#else |
gcc_unreachable (); |
#endif |
#endif |
} |
|
/* Output assembler code to FILE to increment profiler label # LABELNO |
for profiling a function entry. */ |
|
void |
s390_function_profiler (FILE *file, int labelno) |
{ |
rtx op[7]; |
|
char label[128]; |
ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); |
|
fprintf (file, "# function profiler \n"); |
|
op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM); |
op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); |
op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD)); |
|
op[2] = gen_rtx_REG (Pmode, 1); |
op[3] = gen_rtx_SYMBOL_REF (Pmode, label); |
SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL; |
|
op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount"); |
if (flag_pic) |
{ |
op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT); |
op[4] = gen_rtx_CONST (Pmode, op[4]); |
} |
|
if (TARGET_64BIT) |
{ |
output_asm_insn ("stg\t%0,%1", op); |
output_asm_insn ("larl\t%2,%3", op); |
output_asm_insn ("brasl\t%0,%4", op); |
output_asm_insn ("lg\t%0,%1", op); |
} |
else if (!flag_pic) |
{ |
op[6] = gen_label_rtx (); |
|
output_asm_insn ("st\t%0,%1", op); |
output_asm_insn ("bras\t%2,%l6", op); |
output_asm_insn (".long\t%4", op); |
output_asm_insn (".long\t%3", op); |
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); |
output_asm_insn ("l\t%0,0(%2)", op); |
output_asm_insn ("l\t%2,4(%2)", op); |
output_asm_insn ("basr\t%0,%0", op); |
output_asm_insn ("l\t%0,%1", op); |
} |
else |
{ |
op[5] = gen_label_rtx (); |
op[6] = gen_label_rtx (); |
|
output_asm_insn ("st\t%0,%1", op); |
output_asm_insn ("bras\t%2,%l6", op); |
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5])); |
output_asm_insn (".long\t%4-%l5", op); |
output_asm_insn (".long\t%3-%l5", op); |
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); |
output_asm_insn ("lr\t%0,%2", op); |
output_asm_insn ("a\t%0,0(%2)", op); |
output_asm_insn ("a\t%2,4(%2)", op); |
output_asm_insn ("basr\t%0,%0", op); |
output_asm_insn ("l\t%0,%1", op); |
} |
} |
|
/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF |
into its SYMBOL_REF_FLAGS. */ |
|
static void |
s390_encode_section_info (tree decl, rtx rtl, int first) |
{ |
default_encode_section_info (decl, rtl, first); |
|
/* If a variable has a forced alignment to < 2 bytes, mark it with |
SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand. */ |
if (TREE_CODE (decl) == VAR_DECL |
&& DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16) |
SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1; |
} |
|
/* Output thunk to FILE that implements a C++ virtual function call (with |
multiple inheritance) to FUNCTION. The thunk adjusts the this pointer |
by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment |
stored at VCALL_OFFSET in the vtable whose address is located at offset 0 |
relative to the resulting this pointer. */ |
|
static void |
s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, |
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, |
tree function) |
{ |
rtx op[10]; |
int nonlocal = 0; |
|
/* Operand 0 is the target function. */ |
op[0] = XEXP (DECL_RTL (function), 0); |
if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0])) |
{ |
nonlocal = 1; |
op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), |
TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT); |
op[0] = gen_rtx_CONST (Pmode, op[0]); |
} |
|
/* Operand 1 is the 'this' pointer. */ |
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) |
op[1] = gen_rtx_REG (Pmode, 3); |
else |
op[1] = gen_rtx_REG (Pmode, 2); |
|
/* Operand 2 is the delta. */ |
op[2] = GEN_INT (delta); |
|
/* Operand 3 is the vcall_offset. */ |
op[3] = GEN_INT (vcall_offset); |
|
/* Operand 4 is the temporary register. */ |
op[4] = gen_rtx_REG (Pmode, 1); |
|
/* Operands 5 to 8 can be used as labels. */ |
op[5] = NULL_RTX; |
op[6] = NULL_RTX; |
op[7] = NULL_RTX; |
op[8] = NULL_RTX; |
|
/* Operand 9 can be used for temporary register. */ |
op[9] = NULL_RTX; |
|
/* Generate code. */ |
if (TARGET_64BIT) |
{ |
/* Setup literal pool pointer if required. */ |
if ((!DISP_IN_RANGE (delta) |
&& !CONST_OK_FOR_K (delta) |
&& !CONST_OK_FOR_Os (delta)) |
|| (!DISP_IN_RANGE (vcall_offset) |
&& !CONST_OK_FOR_K (vcall_offset) |
&& !CONST_OK_FOR_Os (vcall_offset))) |
{ |
op[5] = gen_label_rtx (); |
output_asm_insn ("larl\t%4,%5", op); |
} |
|
/* Add DELTA to this pointer. */ |
if (delta) |
{ |
if (CONST_OK_FOR_J (delta)) |
output_asm_insn ("la\t%1,%2(%1)", op); |
else if (DISP_IN_RANGE (delta)) |
output_asm_insn ("lay\t%1,%2(%1)", op); |
else if (CONST_OK_FOR_K (delta)) |
output_asm_insn ("aghi\t%1,%2", op); |
else if (CONST_OK_FOR_Os (delta)) |
output_asm_insn ("agfi\t%1,%2", op); |
else |
{ |
op[6] = gen_label_rtx (); |
output_asm_insn ("agf\t%1,%6-%5(%4)", op); |
} |
} |
|
/* Perform vcall adjustment. */ |
if (vcall_offset) |
{ |
if (DISP_IN_RANGE (vcall_offset)) |
{ |
output_asm_insn ("lg\t%4,0(%1)", op); |
output_asm_insn ("ag\t%1,%3(%4)", op); |
} |
else if (CONST_OK_FOR_K (vcall_offset)) |
{ |
output_asm_insn ("lghi\t%4,%3", op); |
output_asm_insn ("ag\t%4,0(%1)", op); |
output_asm_insn ("ag\t%1,0(%4)", op); |
} |
else if (CONST_OK_FOR_Os (vcall_offset)) |
{ |
output_asm_insn ("lgfi\t%4,%3", op); |
output_asm_insn ("ag\t%4,0(%1)", op); |
output_asm_insn ("ag\t%1,0(%4)", op); |
} |
else |
{ |
op[7] = gen_label_rtx (); |
output_asm_insn ("llgf\t%4,%7-%5(%4)", op); |
output_asm_insn ("ag\t%4,0(%1)", op); |
output_asm_insn ("ag\t%1,0(%4)", op); |
} |
} |
|
/* Jump to target. */ |
output_asm_insn ("jg\t%0", op); |
|
/* Output literal pool if required. */ |
if (op[5]) |
{ |
output_asm_insn (".align\t4", op); |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[5])); |
} |
if (op[6]) |
{ |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[6])); |
output_asm_insn (".long\t%2", op); |
} |
if (op[7]) |
{ |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[7])); |
output_asm_insn (".long\t%3", op); |
} |
} |
else |
{ |
/* Setup base pointer if required. */ |
if (!vcall_offset |
|| (!DISP_IN_RANGE (delta) |
&& !CONST_OK_FOR_K (delta) |
&& !CONST_OK_FOR_Os (delta)) |
|| (!DISP_IN_RANGE (delta) |
&& !CONST_OK_FOR_K (vcall_offset) |
&& !CONST_OK_FOR_Os (vcall_offset))) |
{ |
op[5] = gen_label_rtx (); |
output_asm_insn ("basr\t%4,0", op); |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[5])); |
} |
|
/* Add DELTA to this pointer. */ |
if (delta) |
{ |
if (CONST_OK_FOR_J (delta)) |
output_asm_insn ("la\t%1,%2(%1)", op); |
else if (DISP_IN_RANGE (delta)) |
output_asm_insn ("lay\t%1,%2(%1)", op); |
else if (CONST_OK_FOR_K (delta)) |
output_asm_insn ("ahi\t%1,%2", op); |
else if (CONST_OK_FOR_Os (delta)) |
output_asm_insn ("afi\t%1,%2", op); |
else |
{ |
op[6] = gen_label_rtx (); |
output_asm_insn ("a\t%1,%6-%5(%4)", op); |
} |
} |
|
/* Perform vcall adjustment. */ |
if (vcall_offset) |
{ |
if (CONST_OK_FOR_J (vcall_offset)) |
{ |
output_asm_insn ("l\t%4,0(%1)", op); |
output_asm_insn ("a\t%1,%3(%4)", op); |
} |
else if (DISP_IN_RANGE (vcall_offset)) |
{ |
output_asm_insn ("l\t%4,0(%1)", op); |
output_asm_insn ("ay\t%1,%3(%4)", op); |
} |
else if (CONST_OK_FOR_K (vcall_offset)) |
{ |
output_asm_insn ("lhi\t%4,%3", op); |
output_asm_insn ("a\t%4,0(%1)", op); |
output_asm_insn ("a\t%1,0(%4)", op); |
} |
else if (CONST_OK_FOR_Os (vcall_offset)) |
{ |
output_asm_insn ("iilf\t%4,%3", op); |
output_asm_insn ("a\t%4,0(%1)", op); |
output_asm_insn ("a\t%1,0(%4)", op); |
} |
else |
{ |
op[7] = gen_label_rtx (); |
output_asm_insn ("l\t%4,%7-%5(%4)", op); |
output_asm_insn ("a\t%4,0(%1)", op); |
output_asm_insn ("a\t%1,0(%4)", op); |
} |
|
/* We had to clobber the base pointer register. |
Re-setup the base pointer (with a different base). */ |
op[5] = gen_label_rtx (); |
output_asm_insn ("basr\t%4,0", op); |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[5])); |
} |
|
/* Jump to target. */ |
op[8] = gen_label_rtx (); |
|
if (!flag_pic) |
output_asm_insn ("l\t%4,%8-%5(%4)", op); |
else if (!nonlocal) |
output_asm_insn ("a\t%4,%8-%5(%4)", op); |
/* We cannot call through .plt, since .plt requires %r12 loaded. */ |
else if (flag_pic == 1) |
{ |
output_asm_insn ("a\t%4,%8-%5(%4)", op); |
output_asm_insn ("l\t%4,%0(%4)", op); |
} |
else if (flag_pic == 2) |
{ |
op[9] = gen_rtx_REG (Pmode, 0); |
output_asm_insn ("l\t%9,%8-4-%5(%4)", op); |
output_asm_insn ("a\t%4,%8-%5(%4)", op); |
output_asm_insn ("ar\t%4,%9", op); |
output_asm_insn ("l\t%4,0(%4)", op); |
} |
|
output_asm_insn ("br\t%4", op); |
|
/* Output literal pool. */ |
output_asm_insn (".align\t4", op); |
|
if (nonlocal && flag_pic == 2) |
output_asm_insn (".long\t%0", op); |
if (nonlocal) |
{ |
op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); |
SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL; |
} |
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8])); |
if (!flag_pic) |
output_asm_insn (".long\t%0", op); |
else |
output_asm_insn (".long\t%0-%5", op); |
|
if (op[6]) |
{ |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[6])); |
output_asm_insn (".long\t%2", op); |
} |
if (op[7]) |
{ |
targetm.asm_out.internal_label (file, "L", |
CODE_LABEL_NUMBER (op[7])); |
output_asm_insn (".long\t%3", op); |
} |
} |
} |
|
static bool |
s390_valid_pointer_mode (enum machine_mode mode) |
{ |
return (mode == SImode || (TARGET_64BIT && mode == DImode)); |
} |
|
/* Checks whether the given ARGUMENT_LIST would use a caller |
saved register. This is used to decide whether sibling call |
optimization could be performed on the respective function |
call. */ |
|
static bool |
s390_call_saved_register_used (tree argument_list) |
{ |
CUMULATIVE_ARGS cum; |
tree parameter; |
enum machine_mode mode; |
tree type; |
rtx parm_rtx; |
int reg; |
|
INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0); |
|
while (argument_list) |
{ |
parameter = TREE_VALUE (argument_list); |
argument_list = TREE_CHAIN (argument_list); |
|
gcc_assert (parameter); |
|
/* For an undeclared variable passed as parameter we will get |
an ERROR_MARK node here. */ |
if (TREE_CODE (parameter) == ERROR_MARK) |
return true; |
|
type = TREE_TYPE (parameter); |
gcc_assert (type); |
|
mode = TYPE_MODE (type); |
gcc_assert (mode); |
|
if (pass_by_reference (&cum, mode, type, true)) |
{ |
mode = Pmode; |
type = build_pointer_type (type); |
} |
|
parm_rtx = s390_function_arg (&cum, mode, type, 0); |
|
s390_function_arg_advance (&cum, mode, type, 0); |
|
if (parm_rtx && REG_P (parm_rtx)) |
{ |
for (reg = 0; |
reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx)); |
reg++) |
if (! call_used_regs[reg + REGNO (parm_rtx)]) |
return true; |
} |
} |
return false; |
} |
|
/* Return true if the given call expression can be |
turned into a sibling call. |
DECL holds the declaration of the function to be called whereas |
EXP is the call expression itself. */ |
|
static bool |
s390_function_ok_for_sibcall (tree decl, tree exp) |
{ |
/* The TPF epilogue uses register 1. */ |
if (TARGET_TPF_PROFILING) |
return false; |
|
/* The 31 bit PLT code uses register 12 (GOT pointer - caller saved) |
which would have to be restored before the sibcall. */ |
if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl)) |
return false; |
|
/* Register 6 on s390 is available as an argument register but unfortunately |
"caller saved". This makes functions needing this register for arguments |
not suitable for sibcalls. */ |
if (TREE_OPERAND (exp, 1) |
&& s390_call_saved_register_used (TREE_OPERAND (exp, 1))) |
return false; |
|
return true; |
} |
|
/* Return the fixed registers used for condition codes. */ |
|
static bool |
s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) |
{ |
*p1 = CC_REGNUM; |
*p2 = INVALID_REGNUM; |
|
return true; |
} |
|
/* This function is used by the call expanders of the machine description. |
It emits the call insn itself together with the necessary operations |
to adjust the target address and returns the emitted insn. |
ADDR_LOCATION is the target address rtx |
TLS_CALL the location of the thread-local symbol |
RESULT_REG the register where the result of the call should be stored |
RETADDR_REG the register where the return address should be stored |
If this parameter is NULL_RTX the call is considered |
to be a sibling call. */ |
|
rtx |
s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, |
rtx retaddr_reg) |
{ |
bool plt_call = false; |
rtx insn; |
rtx call; |
rtx clobber; |
rtvec vec; |
|
/* Direct function calls need special treatment. */ |
if (GET_CODE (addr_location) == SYMBOL_REF) |
{ |
/* When calling a global routine in PIC mode, we must |
replace the symbol itself with the PLT stub. */ |
if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location)) |
{ |
addr_location = gen_rtx_UNSPEC (Pmode, |
gen_rtvec (1, addr_location), |
UNSPEC_PLT); |
addr_location = gen_rtx_CONST (Pmode, addr_location); |
plt_call = true; |
} |
|
/* Unless we can use the bras(l) insn, force the |
routine address into a register. */ |
if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH) |
{ |
if (flag_pic) |
addr_location = legitimize_pic_address (addr_location, 0); |
else |
addr_location = force_reg (Pmode, addr_location); |
} |
} |
|
/* If it is already an indirect call or the code above moved the |
SYMBOL_REF to somewhere else make sure the address can be found in |
register 1. */ |
if (retaddr_reg == NULL_RTX |
&& GET_CODE (addr_location) != SYMBOL_REF |
&& !plt_call) |
{ |
emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location); |
addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM); |
} |
|
addr_location = gen_rtx_MEM (QImode, addr_location); |
call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); |
|
if (result_reg != NULL_RTX) |
call = gen_rtx_SET (VOIDmode, result_reg, call); |
|
if (retaddr_reg != NULL_RTX) |
{ |
clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); |
|
if (tls_call != NULL_RTX) |
vec = gen_rtvec (3, call, clobber, |
gen_rtx_USE (VOIDmode, tls_call)); |
else |
vec = gen_rtvec (2, call, clobber); |
|
call = gen_rtx_PARALLEL (VOIDmode, vec); |
} |
|
insn = emit_call_insn (call); |
|
/* 31-bit PLT stubs and tls calls use the GOT register implicitly. */ |
if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX) |
{ |
/* s390_function_ok_for_sibcall should |
have denied sibcalls in this case. */ |
gcc_assert (retaddr_reg != NULL_RTX); |
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); |
} |
return insn; |
} |
|
/* Implement CONDITIONAL_REGISTER_USAGE. */ |
|
void |
s390_conditional_register_usage (void) |
{ |
int i; |
|
if (flag_pic) |
{ |
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; |
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; |
} |
if (TARGET_CPU_ZARCH) |
{ |
fixed_regs[BASE_REGNUM] = 0; |
call_used_regs[BASE_REGNUM] = 0; |
fixed_regs[RETURN_REGNUM] = 0; |
call_used_regs[RETURN_REGNUM] = 0; |
} |
if (TARGET_64BIT) |
{ |
for (i = 24; i < 32; i++) |
call_used_regs[i] = call_really_used_regs[i] = 0; |
} |
else |
{ |
for (i = 18; i < 20; i++) |
call_used_regs[i] = call_really_used_regs[i] = 0; |
} |
|
if (TARGET_SOFT_FLOAT) |
{ |
for (i = 16; i < 32; i++) |
call_used_regs[i] = fixed_regs[i] = 1; |
} |
} |
|
/* Corresponding function to eh_return expander. */ |
|
static GTY(()) rtx s390_tpf_eh_return_symbol; |
void |
s390_emit_tpf_eh_return (rtx target) |
{ |
rtx insn, reg; |
|
if (!s390_tpf_eh_return_symbol) |
s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return"); |
|
reg = gen_rtx_REG (Pmode, 2); |
|
emit_move_insn (reg, target); |
insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg, |
gen_rtx_REG (Pmode, RETURN_REGNUM)); |
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg); |
|
emit_move_insn (EH_RETURN_HANDLER_RTX, reg); |
} |
|
/* Rework the prologue/epilogue to avoid saving/restoring |
registers unnecessarily. */ |
|
static void |
s390_optimize_prologue (void) |
{ |
rtx insn, new_insn, next_insn; |
|
/* Do a final recompute of the frame-related data. */ |
|
s390_update_frame_layout (); |
|
/* If all special registers are in fact used, there's nothing we |
can do, so no point in walking the insn list. */ |
|
if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM |
&& cfun_frame_layout.last_save_gpr >= BASE_REGNUM |
&& (TARGET_CPU_ZARCH |
|| (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM |
&& cfun_frame_layout.last_save_gpr >= RETURN_REGNUM))) |
return; |
|
/* Search for prologue/epilogue insns and replace them. */ |
|
for (insn = get_insns (); insn; insn = next_insn) |
{ |
int first, last, off; |
rtx set, base, offset; |
|
next_insn = NEXT_INSN (insn); |
|
if (GET_CODE (insn) != INSN) |
continue; |
|
if (GET_CODE (PATTERN (insn)) == PARALLEL |
&& store_multiple_operation (PATTERN (insn), VOIDmode)) |
{ |
set = XVECEXP (PATTERN (insn), 0, 0); |
first = REGNO (SET_SRC (set)); |
last = first + XVECLEN (PATTERN (insn), 0) - 1; |
offset = const0_rtx; |
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); |
off = INTVAL (offset); |
|
if (GET_CODE (base) != REG || off < 0) |
continue; |
if (cfun_frame_layout.first_save_gpr != -1 |
&& (cfun_frame_layout.first_save_gpr < first |
|| cfun_frame_layout.last_save_gpr > last)) |
continue; |
if (REGNO (base) != STACK_POINTER_REGNUM |
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM) |
continue; |
if (first > BASE_REGNUM || last < BASE_REGNUM) |
continue; |
|
if (cfun_frame_layout.first_save_gpr != -1) |
{ |
new_insn = save_gprs (base, |
off + (cfun_frame_layout.first_save_gpr |
- first) * UNITS_PER_WORD, |
cfun_frame_layout.first_save_gpr, |
cfun_frame_layout.last_save_gpr); |
new_insn = emit_insn_before (new_insn, insn); |
INSN_ADDRESSES_NEW (new_insn, -1); |
} |
|
remove_insn (insn); |
continue; |
} |
|
if (cfun_frame_layout.first_save_gpr == -1 |
&& GET_CODE (PATTERN (insn)) == SET |
&& GET_CODE (SET_SRC (PATTERN (insn))) == REG |
&& (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM |
|| (!TARGET_CPU_ZARCH |
&& REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM)) |
&& GET_CODE (SET_DEST (PATTERN (insn))) == MEM) |
{ |
set = PATTERN (insn); |
first = REGNO (SET_SRC (set)); |
offset = const0_rtx; |
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); |
off = INTVAL (offset); |
|
if (GET_CODE (base) != REG || off < 0) |
continue; |
if (REGNO (base) != STACK_POINTER_REGNUM |
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM) |
continue; |
|
remove_insn (insn); |
continue; |
} |
|
if (GET_CODE (PATTERN (insn)) == PARALLEL |
&& load_multiple_operation (PATTERN (insn), VOIDmode)) |
{ |
set = XVECEXP (PATTERN (insn), 0, 0); |
first = REGNO (SET_DEST (set)); |
last = first + XVECLEN (PATTERN (insn), 0) - 1; |
offset = const0_rtx; |
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); |
off = INTVAL (offset); |
|
if (GET_CODE (base) != REG || off < 0) |
continue; |
if (cfun_frame_layout.first_restore_gpr != -1 |
&& (cfun_frame_layout.first_restore_gpr < first |
|| cfun_frame_layout.last_restore_gpr > last)) |
continue; |
if (REGNO (base) != STACK_POINTER_REGNUM |
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM) |
continue; |
if (first > BASE_REGNUM || last < BASE_REGNUM) |
continue; |
|
if (cfun_frame_layout.first_restore_gpr != -1) |
{ |
new_insn = restore_gprs (base, |
off + (cfun_frame_layout.first_restore_gpr |
- first) * UNITS_PER_WORD, |
cfun_frame_layout.first_restore_gpr, |
cfun_frame_layout.last_restore_gpr); |
new_insn = emit_insn_before (new_insn, insn); |
INSN_ADDRESSES_NEW (new_insn, -1); |
} |
|
remove_insn (insn); |
continue; |
} |
|
if (cfun_frame_layout.first_restore_gpr == -1 |
&& GET_CODE (PATTERN (insn)) == SET |
&& GET_CODE (SET_DEST (PATTERN (insn))) == REG |
&& (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM |
|| (!TARGET_CPU_ZARCH |
&& REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM)) |
&& GET_CODE (SET_SRC (PATTERN (insn))) == MEM) |
{ |
set = PATTERN (insn); |
first = REGNO (SET_DEST (set)); |
offset = const0_rtx; |
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); |
off = INTVAL (offset); |
|
if (GET_CODE (base) != REG || off < 0) |
continue; |
if (REGNO (base) != STACK_POINTER_REGNUM |
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM) |
continue; |
|
remove_insn (insn); |
continue; |
} |
} |
} |
|
/* Perform machine-dependent processing. */ |
|
static void |
s390_reorg (void) |
{ |
bool pool_overflow = false; |
|
/* Make sure all splits have been performed; splits after |
machine_dependent_reorg might confuse insn length counts. */ |
split_all_insns_noflow (); |
|
/* From here on decomposed literal pool addresses must be accepted. */ |
cfun->machine->decomposed_literal_pool_addresses_ok_p = true; |
|
/* Install the main literal pool and the associated base |
register load insns. |
|
In addition, there are two problematic situations we need |
to correct: |
|
- the literal pool might be > 4096 bytes in size, so that |
some of its elements cannot be directly accessed |
|
- a branch target might be > 64K away from the branch, so that |
it is not possible to use a PC-relative instruction. |
|
To fix those, we split the single literal pool into multiple |
pool chunks, reloading the pool base register at various |
points throughout the function to ensure it always points to |
the pool chunk the following code expects, and / or replace |
PC-relative branches by absolute branches. |
|
However, the two problems are interdependent: splitting the |
literal pool can move a branch further away from its target, |
causing the 64K limit to overflow, and on the other hand, |
replacing a PC-relative branch by an absolute branch means |
we need to put the branch target address into the literal |
pool, possibly causing it to overflow. |
|
So, we loop trying to fix up both problems until we manage |
to satisfy both conditions at the same time. Note that the |
loop is guaranteed to terminate as every pass of the loop |
strictly decreases the total number of PC-relative branches |
in the function. (This is not completely true as there |
might be branch-over-pool insns introduced by chunkify_start. |
Those never need to be split however.) */ |
|
for (;;) |
{ |
struct constant_pool *pool = NULL; |
|
/* Collect the literal pool. */ |
if (!pool_overflow) |
{ |
pool = s390_mainpool_start (); |
if (!pool) |
pool_overflow = true; |
} |
|
/* If literal pool overflowed, start to chunkify it. */ |
if (pool_overflow) |
pool = s390_chunkify_start (); |
|
/* Split out-of-range branches. If this has created new |
literal pool entries, cancel current chunk list and |
recompute it. zSeries machines have large branch |
instructions, so we never need to split a branch. */ |
if (!TARGET_CPU_ZARCH && s390_split_branches ()) |
{ |
if (pool_overflow) |
s390_chunkify_cancel (pool); |
else |
s390_mainpool_cancel (pool); |
|
continue; |
} |
|
/* If we made it up to here, both conditions are satisfied. |
Finish up literal pool related changes. */ |
if (pool_overflow) |
s390_chunkify_finish (pool); |
else |
s390_mainpool_finish (pool); |
|
/* We're done splitting branches. */ |
cfun->machine->split_branches_pending_p = false; |
break; |
} |
|
/* Generate out-of-pool execute target insns. */ |
if (TARGET_CPU_ZARCH) |
{ |
rtx insn, label, target; |
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
{ |
label = s390_execute_label (insn); |
if (!label) |
continue; |
|
gcc_assert (label != const0_rtx); |
|
target = emit_label (XEXP (label, 0)); |
INSN_ADDRESSES_NEW (target, -1); |
|
target = emit_insn (s390_execute_target (insn)); |
INSN_ADDRESSES_NEW (target, -1); |
} |
} |
|
/* Try to optimize prologue and epilogue further. */ |
s390_optimize_prologue (); |
} |
|
|
/* Initialize GCC target structure. */ |
|
#undef TARGET_ASM_ALIGNED_HI_OP |
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" |
#undef TARGET_ASM_ALIGNED_DI_OP |
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" |
#undef TARGET_ASM_INTEGER |
#define TARGET_ASM_INTEGER s390_assemble_integer |
|
#undef TARGET_ASM_OPEN_PAREN |
#define TARGET_ASM_OPEN_PAREN "" |
|
#undef TARGET_ASM_CLOSE_PAREN |
#define TARGET_ASM_CLOSE_PAREN "" |
|
#undef TARGET_DEFAULT_TARGET_FLAGS |
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD) |
#undef TARGET_HANDLE_OPTION |
#define TARGET_HANDLE_OPTION s390_handle_option |
|
#undef TARGET_ENCODE_SECTION_INFO |
#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info |
|
#ifdef HAVE_AS_TLS |
#undef TARGET_HAVE_TLS |
#define TARGET_HAVE_TLS true |
#endif |
#undef TARGET_CANNOT_FORCE_CONST_MEM |
#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem |
|
#undef TARGET_DELEGITIMIZE_ADDRESS |
#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address |
|
#undef TARGET_RETURN_IN_MEMORY |
#define TARGET_RETURN_IN_MEMORY s390_return_in_memory |
|
#undef TARGET_INIT_BUILTINS |
#define TARGET_INIT_BUILTINS s390_init_builtins |
#undef TARGET_EXPAND_BUILTIN |
#define TARGET_EXPAND_BUILTIN s390_expand_builtin |
|
#undef TARGET_ASM_OUTPUT_MI_THUNK |
#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk |
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK |
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true |
|
#undef TARGET_SCHED_ADJUST_PRIORITY |
#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority |
#undef TARGET_SCHED_ISSUE_RATE |
#define TARGET_SCHED_ISSUE_RATE s390_issue_rate |
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD |
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead |
|
#undef TARGET_CANNOT_COPY_INSN_P |
#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p |
#undef TARGET_RTX_COSTS |
#define TARGET_RTX_COSTS s390_rtx_costs |
#undef TARGET_ADDRESS_COST |
#define TARGET_ADDRESS_COST s390_address_cost |
|
#undef TARGET_MACHINE_DEPENDENT_REORG |
#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg |
|
#undef TARGET_VALID_POINTER_MODE |
#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode |
|
#undef TARGET_BUILD_BUILTIN_VA_LIST |
#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list |
#undef TARGET_GIMPLIFY_VA_ARG_EXPR |
#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg |
|
#undef TARGET_PROMOTE_FUNCTION_ARGS |
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true |
#undef TARGET_PROMOTE_FUNCTION_RETURN |
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true |
#undef TARGET_PASS_BY_REFERENCE |
#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference |
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL |
#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall |
|
#undef TARGET_FIXED_CONDITION_CODE_REGS |
#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs |
|
#undef TARGET_CC_MODES_COMPATIBLE |
#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible |
|
#undef TARGET_INVALID_WITHIN_DOLOOP |
#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null |
|
#ifdef HAVE_AS_TLS |
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL |
#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel |
#endif |
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING |
#undef TARGET_MANGLE_FUNDAMENTAL_TYPE |
#define TARGET_MANGLE_FUNDAMENTAL_TYPE s390_mangle_fundamental_type |
#endif |
|
#undef TARGET_SCALAR_MODE_SUPPORTED_P |
#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p |
|
struct gcc_target targetm = TARGET_INITIALIZER; |
|
#include "gt-s390.h" |
/tpf-unwind.h
0,0 → 1,257
/* DWARF2 EH unwinding support for TPF OS. |
Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
Contributed by P.J. Darcy (darcypj@us.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 2, or (at your option) any later |
version. |
|
In addition to the permissions in the GNU General Public License, the |
Free Software Foundation gives you unlimited permission to link the |
compiled version of this file into combinations with other programs, |
and to distribute those combinations without any restriction coming |
from the use of this file. (The General Public License restrictions |
do apply in other respects; for example, they cover modification of |
the file, and distribution when not linked into a combined |
executable.) |
|
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 COPYING. If not, write to the Free |
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA |
02110-1301, USA. */ |
|
#include <dlfcn.h> |
|
/* Function Name: __isPATrange |
Parameters passed into it: address to check |
Return Value: A 1 if address is in pat code "range", 0 if not |
Description: This function simply checks to see if the address |
passed to it is in the CP pat code range. */ |
|
#define MIN_PATRANGE 0x10000 |
#define MAX_PATRANGE 0x800000 |
|
static inline unsigned int |
__isPATrange (void *addr) |
{ |
if (addr > (void *)MIN_PATRANGE && addr < (void *)MAX_PATRANGE) |
return 1; |
else |
return 0; |
} |
|
/* TPF return address offset from start of stack frame. */ |
#define TPFRA_OFFSET 168 |
|
/* Exceptions macro defined for TPF so that functions without |
dwarf frame information can be used with exceptions. */ |
#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state |
|
static _Unwind_Reason_Code |
s390_fallback_frame_state (struct _Unwind_Context *context, |
_Unwind_FrameState *fs) |
{ |
unsigned long int regs; |
unsigned long int new_cfa; |
int i; |
|
regs = *((unsigned long int *) |
(((unsigned long int) context->cfa) - STACK_POINTER_OFFSET)); |
|
/* Are we going through special linkage code? */ |
if (__isPATrange (context->ra)) |
{ |
|
/* Our return register isn't zero for end of stack, so |
check backward stackpointer to see if it is zero. */ |
if (regs == NULL) |
return _URC_END_OF_STACK; |
|
/* No stack frame. */ |
fs->cfa_how = CFA_REG_OFFSET; |
fs->cfa_reg = 15; |
fs->cfa_offset = STACK_POINTER_OFFSET; |
|
/* All registers remain unchanged ... */ |
for (i = 0; i < 32; i++) |
{ |
fs->regs.reg[i].how = REG_SAVED_REG; |
fs->regs.reg[i].loc.reg = i; |
} |
|
/* ... except for %r14, which is stored at CFA-112 |
and used as return address. */ |
fs->regs.reg[14].how = REG_SAVED_OFFSET; |
fs->regs.reg[14].loc.offset = TPFRA_OFFSET - STACK_POINTER_OFFSET; |
fs->retaddr_column = 14; |
|
return _URC_NO_REASON; |
} |
|
regs = *((unsigned long int *) |
(((unsigned long int) context->cfa) - STACK_POINTER_OFFSET)); |
new_cfa = regs + STACK_POINTER_OFFSET; |
|
fs->cfa_how = CFA_REG_OFFSET; |
fs->cfa_reg = 15; |
fs->cfa_offset = new_cfa - |
(unsigned long int) context->cfa + STACK_POINTER_OFFSET; |
|
for (i = 0; i < 16; i++) |
{ |
fs->regs.reg[i].how = REG_SAVED_OFFSET; |
fs->regs.reg[i].loc.offset = regs + i*8 - new_cfa; |
} |
|
for (i = 0; i < 4; i++) |
{ |
fs->regs.reg[16 + i].how = REG_SAVED_OFFSET; |
fs->regs.reg[16 + i].loc.offset = regs + 16*8 + i*8 - new_cfa; |
} |
|
fs->retaddr_column = 14; |
|
return _URC_NO_REASON; |
} |
|
/* Function Name: __tpf_eh_return |
Parameters passed into it: Destination address to jump to. |
Return Value: Converted Destination address if a Pat Stub exists. |
Description: This function swaps the unwinding return address |
with the cp stub code. The original target return address is |
then stored into the tpf return address field. The cp stub |
code is searched for by climbing back up the stack and |
comparing the tpf stored return address object address to |
that of the targets object address. */ |
|
#define CURRENT_STACK_PTR() \ |
({ register unsigned long int *stack_ptr asm ("%r15"); stack_ptr; }) |
|
#define PREVIOUS_STACK_PTR() \ |
((unsigned long int *)(*(CURRENT_STACK_PTR()))) |
|
#define RA_OFFSET 112 |
#define R15_OFFSET 120 |
#define TPFAREA_OFFSET 160 |
#define TPFAREA_SIZE STACK_POINTER_OFFSET-TPFAREA_OFFSET |
#define INVALID_RETURN 0 |
|
void * __tpf_eh_return (void *target); |
|
void * |
__tpf_eh_return (void *target) |
{ |
Dl_info targetcodeInfo, currentcodeInfo; |
int retval; |
void *current, *stackptr, *destination_frame; |
unsigned long int shifter, is_a_stub; |
|
is_a_stub = 0; |
|
/* Get code info for target return's address. */ |
retval = dladdr (target, &targetcodeInfo); |
|
/* Ensure the code info is valid (for target). */ |
if (retval != INVALID_RETURN) |
{ |
|
/* Get the stack pointer of the stack frame to be modified by |
the exception unwinder. So that we can begin our climb |
there. */ |
stackptr = (void *) *((unsigned long int *) (*(PREVIOUS_STACK_PTR()))); |
|
/* Begin looping through stack frames. Stop if invalid |
code information is retrieved or if a match between the |
current stack frame iteration shared object's address |
matches that of the target, calculated above. */ |
do |
{ |
/* Get return address based on our stackptr iterator. */ |
current = (void *) *((unsigned long int *) |
(stackptr+RA_OFFSET)); |
|
/* Is it a Pat Stub? */ |
if (__isPATrange (current)) |
{ |
/* Yes it was, get real return address |
in TPF stack area. */ |
current = (void *) *((unsigned long int *) |
(stackptr+TPFRA_OFFSET)); |
is_a_stub = 1; |
} |
|
/* Get codeinfo on RA so that we can figure out |
the module address. */ |
retval = dladdr (current, ¤tcodeInfo); |
|
/* Check that codeinfo for current stack frame is valid. |
Then compare the module address of current stack frame |
to target stack frame to determine if we have the pat |
stub address we want. Also ensure we are dealing |
with a module crossing, stub return address. */ |
if (is_a_stub && retval != INVALID_RETURN |
&& targetcodeInfo.dli_fbase == currentcodeInfo.dli_fbase) |
{ |
/* Yes! They are in the same module. |
Force copy of TPF private stack area to |
destination stack frame TPF private area. */ |
destination_frame = (void *) *((unsigned long int *) |
(*PREVIOUS_STACK_PTR() + R15_OFFSET)); |
|
/* Copy TPF linkage area from current frame to |
destination frame. */ |
memcpy((void *) (destination_frame + TPFAREA_OFFSET), |
(void *) (stackptr + TPFAREA_OFFSET), TPFAREA_SIZE); |
|
/* Now overlay the |
real target address into the TPF stack area of |
the target frame we are jumping to. */ |
*((unsigned long int *) (destination_frame + |
TPFRA_OFFSET)) = (unsigned long int) target; |
|
/* Before returning the desired pat stub address to |
the exception handling unwinder so that it can |
actually do the "leap" shift out the low order |
bit designated to determine if we are in 64BIT mode. |
This is necessary for CTOA stubs. |
Otherwise we leap one byte past where we want to |
go to in the TPF pat stub linkage code. */ |
shifter = *((unsigned long int *) |
(stackptr + RA_OFFSET)); |
|
shifter &= ~1ul; |
|
/* Store Pat Stub Address in destination Stack Frame. */ |
*((unsigned long int *) (destination_frame + |
RA_OFFSET)) = shifter; |
|
/* Re-adjust pat stub address to go to correct place |
in linkage. */ |
shifter = shifter - 4; |
|
return (void *) shifter; |
} |
|
/* Desired module pat stub not found ... |
Bump stack frame iterator. */ |
stackptr = (void *) *(unsigned long int *) stackptr; |
|
is_a_stub = 0; |
|
} while (stackptr && retval != INVALID_RETURN |
&& targetcodeInfo.dli_fbase != currentcodeInfo.dli_fbase); |
} |
|
/* No pat stub found, could be a problem? Simply return unmodified |
target address. */ |
return target; |
} |
|
/predicates.md
0,0 → 1,378
;; Predicate definitions for S/390 and zSeries. |
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
;; Ulrich Weigand (uweigand@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/>. |
|
;; OP is the current operation. |
;; MODE is the current operation mode. |
|
;; operands -------------------------------------------------------------- |
|
;; Return true if OP a (const_int 0) operand. |
|
(define_predicate "const0_operand" |
(and (match_code "const_int, const_double") |
(match_test "op == CONST0_RTX (mode)"))) |
|
;; Return true if OP is constant. |
|
(define_special_predicate "consttable_operand" |
(and (match_code "symbol_ref, label_ref, const, const_int, const_double") |
(match_test "CONSTANT_P (op)"))) |
|
;; Return true if OP is a valid S-type operand. |
|
(define_predicate "s_operand" |
(and (match_code "subreg, mem") |
(match_operand 0 "general_operand")) |
{ |
/* Just like memory_operand, allow (subreg (mem ...)) |
after reload. */ |
if (reload_completed |
&& GET_CODE (op) == SUBREG |
&& GET_CODE (SUBREG_REG (op)) == MEM) |
op = SUBREG_REG (op); |
|
if (GET_CODE (op) != MEM) |
return false; |
if (!s390_legitimate_address_without_index_p (op)) |
return false; |
|
return true; |
}) |
|
;; Return true if OP is a valid operand for the BRAS instruction. |
;; Allow SYMBOL_REFs and @PLT stubs. |
|
(define_special_predicate "bras_sym_operand" |
(ior (and (match_code "symbol_ref") |
(match_test "!flag_pic || SYMBOL_REF_LOCAL_P (op)")) |
(and (match_code "const") |
(and (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC") |
(match_test "XINT (XEXP (op, 0), 1) == UNSPEC_PLT"))))) |
|
;; Return true if OP is a PLUS that is not a legitimate |
;; operand for the LA instruction. |
|
(define_predicate "s390_plus_operand" |
(and (match_code "plus") |
(and (match_test "mode == Pmode") |
(match_test "!legitimate_la_operand_p (op)")))) |
|
;; Return true if OP is a valid operand as shift count or setmem. |
|
(define_predicate "shift_count_or_setmem_operand" |
(match_code "reg, subreg, plus, const_int") |
{ |
HOST_WIDE_INT offset; |
rtx base; |
|
/* Extract base register and offset. */ |
if (!s390_decompose_shift_count (op, &base, &offset)) |
return false; |
|
/* Don't allow any non-base hard registers. Doing so without |
confusing reload and/or regrename would be tricky, and doesn't |
buy us much anyway. */ |
if (base && REGNO (base) < FIRST_PSEUDO_REGISTER && !ADDR_REG_P (base)) |
return false; |
|
/* Unfortunately we have to reject constants that are invalid |
for an address, or else reload will get confused. */ |
if (!DISP_IN_RANGE (offset)) |
return false; |
|
return true; |
}) |
|
;; Return true if OP a valid operand for the LARL instruction. |
|
(define_predicate "larl_operand" |
(match_code "label_ref, symbol_ref, const, const_int, const_double") |
{ |
/* Allow labels and local symbols. */ |
if (GET_CODE (op) == LABEL_REF) |
return true; |
if (GET_CODE (op) == SYMBOL_REF) |
return ((SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_ALIGN1) == 0 |
&& SYMBOL_REF_TLS_MODEL (op) == 0 |
&& (!flag_pic || SYMBOL_REF_LOCAL_P (op))); |
|
/* Everything else must have a CONST, so strip it. */ |
if (GET_CODE (op) != CONST) |
return false; |
op = XEXP (op, 0); |
|
/* Allow adding *even* in-range constants. */ |
if (GET_CODE (op) == PLUS) |
{ |
if (GET_CODE (XEXP (op, 1)) != CONST_INT |
|| (INTVAL (XEXP (op, 1)) & 1) != 0) |
return false; |
if (INTVAL (XEXP (op, 1)) >= (HOST_WIDE_INT)1 << 31 |
|| INTVAL (XEXP (op, 1)) < -((HOST_WIDE_INT)1 << 31)) |
return false; |
op = XEXP (op, 0); |
} |
|
/* Labels and local symbols allowed here as well. */ |
if (GET_CODE (op) == LABEL_REF) |
return true; |
if (GET_CODE (op) == SYMBOL_REF) |
return ((SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_ALIGN1) == 0 |
&& SYMBOL_REF_TLS_MODEL (op) == 0 |
&& (!flag_pic || SYMBOL_REF_LOCAL_P (op))); |
|
/* Now we must have a @GOTENT offset or @PLT stub |
or an @INDNTPOFF TLS offset. */ |
if (GET_CODE (op) == UNSPEC |
&& XINT (op, 1) == UNSPEC_GOTENT) |
return true; |
if (GET_CODE (op) == UNSPEC |
&& XINT (op, 1) == UNSPEC_PLT) |
return true; |
if (GET_CODE (op) == UNSPEC |
&& XINT (op, 1) == UNSPEC_INDNTPOFF) |
return true; |
|
return false; |
}) |
|
;; operators -------------------------------------------------------------- |
|
;; Return nonzero if OP is a valid comparison operator |
;; for a branch condition. |
|
(define_predicate "s390_comparison" |
(match_code "eq, ne, lt, gt, le, ge, ltu, gtu, leu, geu, |
uneq, unlt, ungt, unle, unge, ltgt, |
unordered, ordered") |
{ |
if (GET_CODE (XEXP (op, 0)) != REG |
|| REGNO (XEXP (op, 0)) != CC_REGNUM |
|| XEXP (op, 1) != const0_rtx) |
return false; |
|
return (s390_branch_condition_mask (op) >= 0); |
}) |
|
;; Return nonzero if OP is a valid comparison operator |
;; for an ALC condition. |
|
(define_predicate "s390_alc_comparison" |
(match_code "zero_extend, sign_extend, ltu, gtu, leu, geu") |
{ |
while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND) |
op = XEXP (op, 0); |
|
if (!COMPARISON_P (op)) |
return false; |
|
if (GET_CODE (XEXP (op, 0)) != REG |
|| REGNO (XEXP (op, 0)) != CC_REGNUM |
|| XEXP (op, 1) != const0_rtx) |
return false; |
|
switch (GET_MODE (XEXP (op, 0))) |
{ |
case CCL1mode: |
return GET_CODE (op) == LTU; |
|
case CCL2mode: |
return GET_CODE (op) == LEU; |
|
case CCL3mode: |
return GET_CODE (op) == GEU; |
|
case CCUmode: |
return GET_CODE (op) == GTU; |
|
case CCURmode: |
return GET_CODE (op) == LTU; |
|
case CCSmode: |
return GET_CODE (op) == UNGT; |
|
case CCSRmode: |
return GET_CODE (op) == UNLT; |
|
default: |
return false; |
} |
}) |
|
;; Return nonzero if OP is a valid comparison operator |
;; for an SLB condition. |
|
(define_predicate "s390_slb_comparison" |
(match_code "zero_extend, sign_extend, ltu, gtu, leu, geu") |
{ |
while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND) |
op = XEXP (op, 0); |
|
if (!COMPARISON_P (op)) |
return false; |
|
if (GET_CODE (XEXP (op, 0)) != REG |
|| REGNO (XEXP (op, 0)) != CC_REGNUM |
|| XEXP (op, 1) != const0_rtx) |
return false; |
|
switch (GET_MODE (XEXP (op, 0))) |
{ |
case CCL1mode: |
return GET_CODE (op) == GEU; |
|
case CCL2mode: |
return GET_CODE (op) == GTU; |
|
case CCL3mode: |
return GET_CODE (op) == LTU; |
|
case CCUmode: |
return GET_CODE (op) == LEU; |
|
case CCURmode: |
return GET_CODE (op) == GEU; |
|
case CCSmode: |
return GET_CODE (op) == LE; |
|
case CCSRmode: |
return GET_CODE (op) == GE; |
|
default: |
return false; |
} |
}) |
|
;; Return true if OP is a load multiple operation. It is known to be a |
;; PARALLEL and the first section will be tested. |
|
(define_special_predicate "load_multiple_operation" |
(match_code "parallel") |
{ |
enum machine_mode elt_mode; |
int count = XVECLEN (op, 0); |
unsigned int dest_regno; |
rtx src_addr; |
int i, off; |
|
/* Perform a quick check so we don't blow up below. */ |
if (count <= 1 |
|| GET_CODE (XVECEXP (op, 0, 0)) != SET |
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG |
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) |
return false; |
|
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); |
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); |
elt_mode = GET_MODE (SET_DEST (XVECEXP (op, 0, 0))); |
|
/* Check, is base, or base + displacement. */ |
|
if (GET_CODE (src_addr) == REG) |
off = 0; |
else if (GET_CODE (src_addr) == PLUS |
&& GET_CODE (XEXP (src_addr, 0)) == REG |
&& GET_CODE (XEXP (src_addr, 1)) == CONST_INT) |
{ |
off = INTVAL (XEXP (src_addr, 1)); |
src_addr = XEXP (src_addr, 0); |
} |
else |
return false; |
|
for (i = 1; i < count; i++) |
{ |
rtx elt = XVECEXP (op, 0, i); |
|
if (GET_CODE (elt) != SET |
|| GET_CODE (SET_DEST (elt)) != REG |
|| GET_MODE (SET_DEST (elt)) != elt_mode |
|| REGNO (SET_DEST (elt)) != dest_regno + i |
|| GET_CODE (SET_SRC (elt)) != MEM |
|| GET_MODE (SET_SRC (elt)) != elt_mode |
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS |
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) |
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT |
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) |
!= off + i * GET_MODE_SIZE (elt_mode)) |
return false; |
} |
|
return true; |
}) |
|
;; Return true if OP is a store multiple operation. It is known to be a |
;; PARALLEL and the first section will be tested. |
|
(define_special_predicate "store_multiple_operation" |
(match_code "parallel") |
{ |
enum machine_mode elt_mode; |
int count = XVECLEN (op, 0); |
unsigned int src_regno; |
rtx dest_addr; |
int i, off; |
|
/* Perform a quick check so we don't blow up below. */ |
if (count <= 1 |
|| GET_CODE (XVECEXP (op, 0, 0)) != SET |
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM |
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) |
return false; |
|
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); |
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); |
elt_mode = GET_MODE (SET_SRC (XVECEXP (op, 0, 0))); |
|
/* Check, is base, or base + displacement. */ |
|
if (GET_CODE (dest_addr) == REG) |
off = 0; |
else if (GET_CODE (dest_addr) == PLUS |
&& GET_CODE (XEXP (dest_addr, 0)) == REG |
&& GET_CODE (XEXP (dest_addr, 1)) == CONST_INT) |
{ |
off = INTVAL (XEXP (dest_addr, 1)); |
dest_addr = XEXP (dest_addr, 0); |
} |
else |
return false; |
|
for (i = 1; i < count; i++) |
{ |
rtx elt = XVECEXP (op, 0, i); |
|
if (GET_CODE (elt) != SET |
|| GET_CODE (SET_SRC (elt)) != REG |
|| GET_MODE (SET_SRC (elt)) != elt_mode |
|| REGNO (SET_SRC (elt)) != src_regno + i |
|| GET_CODE (SET_DEST (elt)) != MEM |
|| GET_MODE (SET_DEST (elt)) != elt_mode |
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS |
|| ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) |
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT |
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) |
!= off + i * GET_MODE_SIZE (elt_mode)) |
return false; |
} |
return true; |
}) |
/s390.h
0,0 → 1,936
/* Definitions of target machine for GNU compiler, for IBM S/390 |
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, |
2007 Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@de.ibm.com). |
|
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/>. */ |
|
#ifndef _S390_H |
#define _S390_H |
|
/* Override the __fixdfdi etc. routines when building libgcc2. |
??? This should be done in a cleaner way ... */ |
#if defined (IN_LIBGCC2) && !defined (__s390x__) |
#include <config/s390/fixdfdi.h> |
#endif |
|
/* Which processor to generate code or schedule for. The cpu attribute |
defines a list that mirrors this list, so changes to s390.md must be |
made at the same time. */ |
|
enum processor_type |
{ |
PROCESSOR_9672_G5, |
PROCESSOR_9672_G6, |
PROCESSOR_2064_Z900, |
PROCESSOR_2084_Z990, |
PROCESSOR_2094_Z9_109, |
PROCESSOR_max |
}; |
|
/* Optional architectural facilities supported by the processor. */ |
|
enum processor_flags |
{ |
PF_IEEE_FLOAT = 1, |
PF_ZARCH = 2, |
PF_LONG_DISPLACEMENT = 4, |
PF_EXTIMM = 8 |
}; |
|
extern enum processor_type s390_tune; |
extern enum processor_flags s390_tune_flags; |
|
extern enum processor_type s390_arch; |
extern enum processor_flags s390_arch_flags; |
|
#define TARGET_CPU_IEEE_FLOAT \ |
(s390_arch_flags & PF_IEEE_FLOAT) |
#define TARGET_CPU_ZARCH \ |
(s390_arch_flags & PF_ZARCH) |
#define TARGET_CPU_LONG_DISPLACEMENT \ |
(s390_arch_flags & PF_LONG_DISPLACEMENT) |
#define TARGET_CPU_EXTIMM \ |
(s390_arch_flags & PF_EXTIMM) |
|
#define TARGET_LONG_DISPLACEMENT \ |
(TARGET_ZARCH && TARGET_CPU_LONG_DISPLACEMENT) |
#define TARGET_EXTIMM \ |
(TARGET_ZARCH && TARGET_CPU_EXTIMM) |
|
/* Run-time target specification. */ |
|
/* Defaults for option flags defined only on some subtargets. */ |
#ifndef TARGET_TPF_PROFILING |
#define TARGET_TPF_PROFILING 0 |
#endif |
|
/* This will be overridden by OS headers. */ |
#define TARGET_TPF 0 |
|
/* Target CPU builtins. */ |
#define TARGET_CPU_CPP_BUILTINS() \ |
do \ |
{ \ |
builtin_assert ("cpu=s390"); \ |
builtin_assert ("machine=s390"); \ |
builtin_define ("__s390__"); \ |
if (TARGET_64BIT) \ |
builtin_define ("__s390x__"); \ |
if (TARGET_LONG_DOUBLE_128) \ |
builtin_define ("__LONG_DOUBLE_128__"); \ |
} \ |
while (0) |
|
/* ??? Once this actually works, it could be made a runtime option. */ |
#define TARGET_IBM_FLOAT 0 |
#define TARGET_IEEE_FLOAT 1 |
|
#ifdef DEFAULT_TARGET_64BIT |
#define TARGET_DEFAULT (MASK_64BIT | MASK_ZARCH | MASK_HARD_FLOAT) |
#else |
#define TARGET_DEFAULT MASK_HARD_FLOAT |
#endif |
|
/* Support for configure-time defaults. */ |
#define OPTION_DEFAULT_SPECS \ |
{ "mode", "%{!mesa:%{!mzarch:-m%(VALUE)}}" }, \ |
{ "arch", "%{!march=*:-march=%(VALUE)}" }, \ |
{ "tune", "%{!mtune=*:-mtune=%(VALUE)}" } |
|
/* Defaulting rules. */ |
#ifdef DEFAULT_TARGET_64BIT |
#define DRIVER_SELF_SPECS \ |
"%{!m31:%{!m64:-m64}}", \ |
"%{!mesa:%{!mzarch:%{m31:-mesa}%{m64:-mzarch}}}", \ |
"%{!march=*:%{mesa:-march=g5}%{mzarch:-march=z900}}" |
#else |
#define DRIVER_SELF_SPECS \ |
"%{!m31:%{!m64:-m31}}", \ |
"%{!mesa:%{!mzarch:%{m31:-mesa}%{m64:-mzarch}}}", \ |
"%{!march=*:%{mesa:-march=g5}%{mzarch:-march=z900}}" |
#endif |
|
/* Target version string. Overridden by the OS header. */ |
#ifdef DEFAULT_TARGET_64BIT |
#define TARGET_VERSION fprintf (stderr, " (zSeries)"); |
#else |
#define TARGET_VERSION fprintf (stderr, " (S/390)"); |
#endif |
|
/* Hooks to override options. */ |
#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) optimization_options(LEVEL, SIZE) |
#define OVERRIDE_OPTIONS override_options () |
|
/* Frame pointer is not used for debugging. */ |
#define CAN_DEBUG_WITHOUT_FP |
|
|
/* In libgcc2, determine target settings as compile-time constants. */ |
#ifdef IN_LIBGCC2 |
#undef TARGET_64BIT |
#ifdef __s390x__ |
#define TARGET_64BIT 1 |
#else |
#define TARGET_64BIT 0 |
#endif |
#endif |
|
|
/* Target machine storage layout. */ |
|
/* Everything is big-endian. */ |
#define BITS_BIG_ENDIAN 1 |
#define BYTES_BIG_ENDIAN 1 |
#define WORDS_BIG_ENDIAN 1 |
|
/* Width of a word, in units (bytes). */ |
#define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4) |
#ifndef IN_LIBGCC2 |
#define MIN_UNITS_PER_WORD 4 |
#endif |
#define MAX_BITS_PER_WORD 64 |
|
/* Function arguments and return values are promoted to word size. */ |
#define PROMOTE_FUNCTION_MODE(MODE, UNSIGNEDP, TYPE) \ |
if (INTEGRAL_MODE_P (MODE) && \ |
GET_MODE_SIZE (MODE) < UNITS_PER_WORD) { \ |
(MODE) = Pmode; \ |
} |
|
/* Allocation boundary (in *bits*) for storing arguments in argument list. */ |
#define PARM_BOUNDARY (TARGET_64BIT ? 64 : 32) |
|
/* Boundary (in *bits*) on which stack pointer should be aligned. */ |
#define STACK_BOUNDARY 64 |
|
/* Allocation boundary (in *bits*) for the code of a function. */ |
#define FUNCTION_BOUNDARY 32 |
|
/* There is no point aligning anything to a rounder boundary than this. */ |
#define BIGGEST_ALIGNMENT 64 |
|
/* Alignment of field after `int : 0' in a structure. */ |
#define EMPTY_FIELD_BOUNDARY 32 |
|
/* Alignment on even addresses for LARL instruction. */ |
#define CONSTANT_ALIGNMENT(EXP, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) |
#define DATA_ALIGNMENT(TYPE, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) |
|
/* Alignment is not required by the hardware. */ |
#define STRICT_ALIGNMENT 0 |
|
/* Mode of stack savearea. |
FUNCTION is VOIDmode because calling convention maintains SP. |
BLOCK needs Pmode for SP. |
NONLOCAL needs twice Pmode to maintain both backchain and SP. */ |
#define STACK_SAVEAREA_MODE(LEVEL) \ |
(LEVEL == SAVE_FUNCTION ? VOIDmode \ |
: LEVEL == SAVE_NONLOCAL ? (TARGET_64BIT ? OImode : TImode) : Pmode) |
|
/* Define target floating point format. */ |
#define TARGET_FLOAT_FORMAT \ |
(TARGET_IEEE_FLOAT? IEEE_FLOAT_FORMAT : IBM_FLOAT_FORMAT) |
|
|
/* Type layout. */ |
|
/* Sizes in bits of the source language data types. */ |
#define SHORT_TYPE_SIZE 16 |
#define INT_TYPE_SIZE 32 |
#define LONG_TYPE_SIZE (TARGET_64BIT ? 64 : 32) |
#define LONG_LONG_TYPE_SIZE 64 |
#define FLOAT_TYPE_SIZE 32 |
#define DOUBLE_TYPE_SIZE 64 |
#define LONG_DOUBLE_TYPE_SIZE (TARGET_LONG_DOUBLE_128 ? 128 : 64) |
|
/* Define this to set long double type size to use in libgcc2.c, which can |
not depend on target_flags. */ |
#ifdef __LONG_DOUBLE_128__ |
#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 128 |
#else |
#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64 |
#endif |
|
/* Work around target_flags dependency in ada/targtyps.c. */ |
#define WIDEST_HARDWARE_FP_SIZE 64 |
|
/* We use "unsigned char" as default. */ |
#define DEFAULT_SIGNED_CHAR 0 |
|
|
/* Register usage. */ |
|
/* We have 16 general purpose registers (registers 0-15), |
and 16 floating point registers (registers 16-31). |
(On non-IEEE machines, we have only 4 fp registers.) |
|
Amongst the general purpose registers, some are used |
for specific purposes: |
GPR 11: Hard frame pointer (if needed) |
GPR 12: Global offset table pointer (if needed) |
GPR 13: Literal pool base register |
GPR 14: Return address register |
GPR 15: Stack pointer |
|
Registers 32-35 are 'fake' hard registers that do not |
correspond to actual hardware: |
Reg 32: Argument pointer |
Reg 33: Condition code |
Reg 34: Frame pointer |
Reg 35: Return address pointer |
|
Registers 36 and 37 are mapped to access registers |
0 and 1, used to implement thread-local storage. */ |
|
#define FIRST_PSEUDO_REGISTER 38 |
|
/* Standard register usage. */ |
#define GENERAL_REGNO_P(N) ((int)(N) >= 0 && (N) < 16) |
#define ADDR_REGNO_P(N) ((N) >= 1 && (N) < 16) |
#define FP_REGNO_P(N) ((N) >= 16 && (N) < (TARGET_IEEE_FLOAT? 32 : 20)) |
#define CC_REGNO_P(N) ((N) == 33) |
#define FRAME_REGNO_P(N) ((N) == 32 || (N) == 34 || (N) == 35) |
#define ACCESS_REGNO_P(N) ((N) == 36 || (N) == 37) |
|
#define GENERAL_REG_P(X) (REG_P (X) && GENERAL_REGNO_P (REGNO (X))) |
#define ADDR_REG_P(X) (REG_P (X) && ADDR_REGNO_P (REGNO (X))) |
#define FP_REG_P(X) (REG_P (X) && FP_REGNO_P (REGNO (X))) |
#define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X))) |
#define FRAME_REG_P(X) (REG_P (X) && FRAME_REGNO_P (REGNO (X))) |
#define ACCESS_REG_P(X) (REG_P (X) && ACCESS_REGNO_P (REGNO (X))) |
|
/* Set up fixed registers and calling convention: |
|
GPRs 0-5 are always call-clobbered, |
GPRs 6-15 are always call-saved. |
GPR 12 is fixed if used as GOT pointer. |
GPR 13 is always fixed (as literal pool pointer). |
GPR 14 is always fixed on S/390 machines (as return address). |
GPR 15 is always fixed (as stack pointer). |
The 'fake' hard registers are call-clobbered and fixed. |
The access registers are call-saved and fixed. |
|
On 31-bit, FPRs 18-19 are call-clobbered; |
on 64-bit, FPRs 24-31 are call-clobbered. |
The remaining FPRs are call-saved. */ |
|
#define FIXED_REGISTERS \ |
{ 0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
0, 1, 1, 1, \ |
0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
1, 1, 1, 1, \ |
1, 1 } |
|
#define CALL_USED_REGISTERS \ |
{ 1, 1, 1, 1, \ |
1, 1, 0, 0, \ |
0, 0, 0, 0, \ |
0, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1 } |
|
#define CALL_REALLY_USED_REGISTERS \ |
{ 1, 1, 1, 1, \ |
1, 1, 0, 0, \ |
0, 0, 0, 0, \ |
0, 0, 0, 0, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
1, 1, 1, 1, \ |
0, 0 } |
|
#define CONDITIONAL_REGISTER_USAGE s390_conditional_register_usage () |
|
/* Preferred register allocation order. */ |
#define REG_ALLOC_ORDER \ |
{ 1, 2, 3, 4, 5, 0, 12, 11, 10, 9, 8, 7, 6, 14, 13, \ |
16, 17, 18, 19, 20, 21, 22, 23, \ |
24, 25, 26, 27, 28, 29, 30, 31, \ |
15, 32, 33, 34, 35, 36, 37 } |
|
|
/* Fitting values into registers. */ |
|
/* Integer modes <= word size fit into any GPR. |
Integer modes > word size fit into successive GPRs, starting with |
an even-numbered register. |
SImode and DImode fit into FPRs as well. |
|
Floating point modes <= word size fit into any FPR or GPR. |
Floating point modes > word size (i.e. DFmode on 32-bit) fit |
into any FPR, or an even-odd GPR pair. |
TFmode fits only into an even-odd FPR pair. |
|
Complex floating point modes fit either into two FPRs, or into |
successive GPRs (again starting with an even number). |
TCmode fits only into two successive even-odd FPR pairs. |
|
Condition code modes fit only into the CC register. */ |
|
/* Because all registers in a class have the same size HARD_REGNO_NREGS |
is equivalent to CLASS_MAX_NREGS. */ |
#define HARD_REGNO_NREGS(REGNO, MODE) \ |
s390_class_max_nregs (REGNO_REG_CLASS (REGNO), (MODE)) |
|
#define HARD_REGNO_MODE_OK(REGNO, MODE) \ |
s390_hard_regno_mode_ok ((REGNO), (MODE)) |
|
#define HARD_REGNO_RENAME_OK(FROM, TO) \ |
s390_hard_regno_rename_ok (FROM, TO) |
|
#define MODES_TIEABLE_P(MODE1, MODE2) \ |
(((MODE1) == SFmode || (MODE1) == DFmode) \ |
== ((MODE2) == SFmode || (MODE2) == DFmode)) |
|
/* Maximum number of registers to represent a value of mode MODE |
in a register of class CLASS. */ |
#define CLASS_MAX_NREGS(CLASS, MODE) \ |
s390_class_max_nregs ((CLASS), (MODE)) |
|
/* If a 4-byte value is loaded into a FPR, it is placed into the |
*upper* half of the register, not the lower. Therefore, we |
cannot use SUBREGs to switch between modes in FP registers. |
Likewise for access registers, since they have only half the |
word size on 64-bit. */ |
#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \ |
(GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO) \ |
? ((reg_classes_intersect_p (FP_REGS, CLASS) \ |
&& (GET_MODE_SIZE (FROM) < 8 || GET_MODE_SIZE (TO) < 8)) \ |
|| reg_classes_intersect_p (ACCESS_REGS, CLASS)) : 0) |
|
/* Register classes. */ |
|
/* We use the following register classes: |
GENERAL_REGS All general purpose registers |
ADDR_REGS All general purpose registers except %r0 |
(These registers can be used in address generation) |
FP_REGS All floating point registers |
CC_REGS The condition code register |
ACCESS_REGS The access registers |
|
GENERAL_FP_REGS Union of GENERAL_REGS and FP_REGS |
ADDR_FP_REGS Union of ADDR_REGS and FP_REGS |
GENERAL_CC_REGS Union of GENERAL_REGS and CC_REGS |
ADDR_CC_REGS Union of ADDR_REGS and CC_REGS |
|
NO_REGS No registers |
ALL_REGS All registers |
|
Note that the 'fake' frame pointer and argument pointer registers |
are included amongst the address registers here. */ |
|
enum reg_class |
{ |
NO_REGS, CC_REGS, ADDR_REGS, GENERAL_REGS, ACCESS_REGS, |
ADDR_CC_REGS, GENERAL_CC_REGS, |
FP_REGS, ADDR_FP_REGS, GENERAL_FP_REGS, |
ALL_REGS, LIM_REG_CLASSES |
}; |
#define N_REG_CLASSES (int) LIM_REG_CLASSES |
|
#define REG_CLASS_NAMES \ |
{ "NO_REGS", "CC_REGS", "ADDR_REGS", "GENERAL_REGS", "ACCESS_REGS", \ |
"ADDR_CC_REGS", "GENERAL_CC_REGS", \ |
"FP_REGS", "ADDR_FP_REGS", "GENERAL_FP_REGS", "ALL_REGS" } |
|
/* Class -> register mapping. */ |
#define REG_CLASS_CONTENTS \ |
{ \ |
{ 0x00000000, 0x00000000 }, /* NO_REGS */ \ |
{ 0x00000000, 0x00000002 }, /* CC_REGS */ \ |
{ 0x0000fffe, 0x0000000d }, /* ADDR_REGS */ \ |
{ 0x0000ffff, 0x0000000d }, /* GENERAL_REGS */ \ |
{ 0x00000000, 0x00000030 }, /* ACCESS_REGS */ \ |
{ 0x0000fffe, 0x0000000f }, /* ADDR_CC_REGS */ \ |
{ 0x0000ffff, 0x0000000f }, /* GENERAL_CC_REGS */ \ |
{ 0xffff0000, 0x00000000 }, /* FP_REGS */ \ |
{ 0xfffffffe, 0x0000000d }, /* ADDR_FP_REGS */ \ |
{ 0xffffffff, 0x0000000d }, /* GENERAL_FP_REGS */ \ |
{ 0xffffffff, 0x0000003f }, /* ALL_REGS */ \ |
} |
|
/* Register -> class mapping. */ |
extern const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER]; |
#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO]) |
|
/* ADDR_REGS can be used as base or index register. */ |
#define INDEX_REG_CLASS ADDR_REGS |
#define BASE_REG_CLASS ADDR_REGS |
|
/* Check whether REGNO is a hard register of the suitable class |
or a pseudo register currently allocated to one such. */ |
#define REGNO_OK_FOR_INDEX_P(REGNO) \ |
(((REGNO) < FIRST_PSEUDO_REGISTER \ |
&& REGNO_REG_CLASS ((REGNO)) == ADDR_REGS) \ |
|| ADDR_REGNO_P (reg_renumber[REGNO])) |
#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_INDEX_P (REGNO) |
|
|
/* Given an rtx X being reloaded into a reg required to be in class CLASS, |
return the class of reg to actually use. */ |
#define PREFERRED_RELOAD_CLASS(X, CLASS) \ |
s390_preferred_reload_class ((X), (CLASS)) |
|
/* We need a secondary reload when loading a PLUS which is |
not a valid operand for LOAD ADDRESS. */ |
#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, IN) \ |
s390_secondary_input_reload_class ((CLASS), (MODE), (IN)) |
|
/* We need a secondary reload when storing a double-word |
to a non-offsettable memory address. */ |
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, OUT) \ |
s390_secondary_output_reload_class ((CLASS), (MODE), (OUT)) |
|
/* We need secondary memory to move data between GPRs and FPRs. */ |
#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ |
((CLASS1) != (CLASS2) && ((CLASS1) == FP_REGS || (CLASS2) == FP_REGS)) |
|
/* Get_secondary_mem widens its argument to BITS_PER_WORD which loses on 64bit |
because the movsi and movsf patterns don't handle r/f moves. */ |
#define SECONDARY_MEMORY_NEEDED_MODE(MODE) \ |
(GET_MODE_BITSIZE (MODE) < 32 \ |
? mode_for_size (32, GET_MODE_CLASS (MODE), 0) \ |
: MODE) |
|
|
/* Stack layout and calling conventions. */ |
|
/* Our stack grows from higher to lower addresses. However, local variables |
are accessed by positive offsets, and function arguments are stored at |
increasing addresses. */ |
#define STACK_GROWS_DOWNWARD |
#define FRAME_GROWS_DOWNWARD 1 |
/* #undef ARGS_GROW_DOWNWARD */ |
|
/* The basic stack layout looks like this: the stack pointer points |
to the register save area for called functions. Above that area |
is the location to place outgoing arguments. Above those follow |
dynamic allocations (alloca), and finally the local variables. */ |
|
/* Offset from stack-pointer to first location of outgoing args. */ |
#define STACK_POINTER_OFFSET (TARGET_64BIT ? 160 : 96) |
|
/* Offset within stack frame to start allocating local variables at. */ |
#define STARTING_FRAME_OFFSET 0 |
|
/* Offset from the stack pointer register to an item dynamically |
allocated on the stack, e.g., by `alloca'. */ |
extern int current_function_outgoing_args_size; |
#define STACK_DYNAMIC_OFFSET(FUNDECL) \ |
(STACK_POINTER_OFFSET + current_function_outgoing_args_size) |
|
/* Offset of first parameter from the argument pointer register value. |
We have a fake argument pointer register that points directly to |
the argument area. */ |
#define FIRST_PARM_OFFSET(FNDECL) 0 |
|
/* Defining this macro makes __builtin_frame_address(0) and |
__builtin_return_address(0) work with -fomit-frame-pointer. */ |
#define INITIAL_FRAME_ADDRESS_RTX \ |
(TARGET_PACKED_STACK ? \ |
plus_constant (arg_pointer_rtx, -UNITS_PER_WORD) : \ |
plus_constant (arg_pointer_rtx, -STACK_POINTER_OFFSET)) |
|
/* The return address of the current frame is retrieved |
from the initial value of register RETURN_REGNUM. |
For frames farther back, we use the stack slot where |
the corresponding RETURN_REGNUM register was saved. */ |
#define DYNAMIC_CHAIN_ADDRESS(FRAME) \ |
(TARGET_PACKED_STACK ? \ |
plus_constant ((FRAME), STACK_POINTER_OFFSET - UNITS_PER_WORD) : (FRAME)) |
|
#define RETURN_ADDR_RTX(COUNT, FRAME) \ |
s390_return_addr_rtx ((COUNT), DYNAMIC_CHAIN_ADDRESS ((FRAME))) |
|
/* In 31-bit mode, we need to mask off the high bit of return addresses. */ |
#define MASK_RETURN_ADDR (TARGET_64BIT ? constm1_rtx : GEN_INT (0x7fffffff)) |
|
|
/* Exception handling. */ |
|
/* Describe calling conventions for DWARF-2 exception handling. */ |
#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, RETURN_REGNUM) |
#define INCOMING_FRAME_SP_OFFSET STACK_POINTER_OFFSET |
#define DWARF_FRAME_RETURN_COLUMN 14 |
|
/* Describe how we implement __builtin_eh_return. */ |
#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + 6 : INVALID_REGNUM) |
#define EH_RETURN_HANDLER_RTX gen_rtx_MEM (Pmode, return_address_pointer_rtx) |
|
/* Select a format to encode pointers in exception handling data. */ |
#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \ |
(flag_pic \ |
? ((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_pcrel | DW_EH_PE_sdata4 \ |
: DW_EH_PE_absptr) |
|
|
/* Frame registers. */ |
|
#define STACK_POINTER_REGNUM 15 |
#define FRAME_POINTER_REGNUM 34 |
#define HARD_FRAME_POINTER_REGNUM 11 |
#define ARG_POINTER_REGNUM 32 |
#define RETURN_ADDRESS_POINTER_REGNUM 35 |
|
/* The static chain must be call-clobbered, but not used for |
function argument passing. As register 1 is clobbered by |
the trampoline code, we only have one option. */ |
#define STATIC_CHAIN_REGNUM 0 |
|
/* Number of hardware registers that go into the DWARF-2 unwind info. |
To avoid ABI incompatibility, this number must not change even as |
'fake' hard registers are added or removed. */ |
#define DWARF_FRAME_REGISTERS 34 |
|
|
/* Frame pointer and argument pointer elimination. */ |
|
#define FRAME_POINTER_REQUIRED 0 |
|
#define ELIMINABLE_REGS \ |
{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ |
{ FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ |
{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ |
{ ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ |
{ RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ |
{ RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ |
{ BASE_REGNUM, BASE_REGNUM }} |
|
#define CAN_ELIMINATE(FROM, TO) \ |
s390_can_eliminate ((FROM), (TO)) |
|
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ |
(OFFSET) = s390_initial_elimination_offset ((FROM), (TO)) |
|
|
/* Stack arguments. */ |
|
/* We need current_function_outgoing_args to be valid. */ |
#define ACCUMULATE_OUTGOING_ARGS 1 |
|
/* Return doesn't modify the stack. */ |
#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, SIZE) 0 |
|
|
/* Register arguments. */ |
|
typedef struct s390_arg_structure |
{ |
int gprs; /* gpr so far */ |
int fprs; /* fpr so far */ |
} |
CUMULATIVE_ARGS; |
|
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, NN, N_NAMED_ARGS) \ |
((CUM).gprs=0, (CUM).fprs=0) |
|
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ |
s390_function_arg_advance (&CUM, MODE, TYPE, NAMED) |
|
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ |
s390_function_arg (&CUM, MODE, TYPE, NAMED) |
|
/* Arguments can be placed in general registers 2 to 6, |
or in floating point registers 0 and 2. */ |
#define FUNCTION_ARG_REGNO_P(N) (((N) >=2 && (N) <7) || \ |
(N) == 16 || (N) == 17) |
|
|
/* Scalar return values. */ |
|
#define FUNCTION_VALUE(VALTYPE, FUNC) \ |
s390_function_value ((VALTYPE), VOIDmode) |
|
#define LIBCALL_VALUE(MODE) \ |
s390_function_value (NULL, (MODE)) |
|
/* Only gpr 2 and fpr 0 are ever used as return registers. */ |
#define FUNCTION_VALUE_REGNO_P(N) ((N) == 2 || (N) == 16) |
|
|
/* Function entry and exit. */ |
|
/* When returning from a function, the stack pointer does not matter. */ |
#define EXIT_IGNORE_STACK 1 |
|
|
/* Profiling. */ |
|
#define FUNCTION_PROFILER(FILE, LABELNO) \ |
s390_function_profiler ((FILE), ((LABELNO))) |
|
#define PROFILE_BEFORE_PROLOGUE 1 |
|
|
/* Implementing the varargs macros. */ |
|
#define EXPAND_BUILTIN_VA_START(valist, nextarg) \ |
s390_va_start (valist, nextarg) |
|
/* Trampolines for nested functions. */ |
|
#define TRAMPOLINE_SIZE (TARGET_64BIT ? 32 : 16) |
|
#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, CXT) \ |
s390_initialize_trampoline ((ADDR), (FNADDR), (CXT)) |
|
#define TRAMPOLINE_TEMPLATE(FILE) \ |
s390_trampoline_template (FILE) |
|
|
/* Addressing modes, and classification of registers for them. */ |
|
/* Recognize any constant value that is a valid address. */ |
#define CONSTANT_ADDRESS_P(X) 0 |
|
/* Maximum number of registers that can appear in a valid memory address. */ |
#define MAX_REGS_PER_ADDRESS 2 |
|
/* S/390 has no mode dependent addresses. */ |
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) |
|
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a |
valid memory address for an instruction. |
The MODE argument is the machine mode for the MEM expression |
that wants to use this address. */ |
#ifdef REG_OK_STRICT |
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ |
{ \ |
if (legitimate_address_p (MODE, X, 1)) \ |
goto ADDR; \ |
} |
#else |
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ |
{ \ |
if (legitimate_address_p (MODE, X, 0)) \ |
goto ADDR; \ |
} |
#endif |
|
/* Try machine-dependent ways of modifying an illegitimate address |
to be legitimate. If we find one, return the new, valid address. |
This macro is used in only one place: `memory_address' in explow.c. */ |
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ |
{ \ |
(X) = legitimize_address (X, OLDX, MODE); \ |
if (memory_address_p (MODE, X)) \ |
goto WIN; \ |
} |
|
/* Try a machine-dependent way of reloading an illegitimate address |
operand. If we find one, push the reload and jump to WIN. This |
macro is used in only one place: `find_reloads_address' in reload.c. */ |
#define LEGITIMIZE_RELOAD_ADDRESS(AD, MODE, OPNUM, TYPE, IND, WIN) \ |
do { \ |
rtx new = legitimize_reload_address (AD, MODE, OPNUM, (int)(TYPE)); \ |
if (new) \ |
{ \ |
(AD) = new; \ |
goto WIN; \ |
} \ |
} while (0) |
|
/* Nonzero if the constant value X is a legitimate general operand. |
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ |
#define LEGITIMATE_CONSTANT_P(X) \ |
legitimate_constant_p (X) |
|
/* Helper macro for s390.c and s390.md to check for symbolic constants. */ |
#define SYMBOLIC_CONST(X) \ |
(GET_CODE (X) == SYMBOL_REF \ |
|| GET_CODE (X) == LABEL_REF \ |
|| (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X))) |
|
#define TLS_SYMBOLIC_CONST(X) \ |
((GET_CODE (X) == SYMBOL_REF && tls_symbolic_operand (X)) \ |
|| (GET_CODE (X) == CONST && tls_symbolic_reference_mentioned_p (X))) |
|
|
/* Condition codes. */ |
|
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, |
return the mode to be used for the comparison. */ |
#define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y)) |
|
/* Canonicalize a comparison from one we don't have to one we do have. */ |
#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ |
s390_canonicalize_comparison (&(CODE), &(OP0), &(OP1)) |
|
/* Define the information needed to generate branch and scc insns. This is |
stored from the compare operation. Note that we can't use "rtx" here |
since it hasn't been defined! */ |
extern struct rtx_def *s390_compare_op0, *s390_compare_op1, *s390_compare_emitted; |
|
|
/* Relative costs of operations. */ |
|
/* On s390, copy between fprs and gprs is expensive. */ |
#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \ |
(( ( reg_classes_intersect_p ((CLASS1), GENERAL_REGS) \ |
&& reg_classes_intersect_p ((CLASS2), FP_REGS)) \ |
|| ( reg_classes_intersect_p ((CLASS1), FP_REGS) \ |
&& reg_classes_intersect_p ((CLASS2), GENERAL_REGS))) ? 10 : 1) |
|
/* A C expression for the cost of moving data of mode M between a |
register and memory. A value of 2 is the default; this cost is |
relative to those in `REGISTER_MOVE_COST'. */ |
#define MEMORY_MOVE_COST(M, C, I) 1 |
|
/* A C expression for the cost of a branch instruction. A value of 1 |
is the default; other values are interpreted relative to that. */ |
#define BRANCH_COST 1 |
|
/* Nonzero if access to memory by bytes is slow and undesirable. */ |
#define SLOW_BYTE_ACCESS 1 |
|
/* An integer expression for the size in bits of the largest integer machine |
mode that should actually be used. We allow pairs of registers. */ |
#define MAX_FIXED_MODE_SIZE GET_MODE_BITSIZE (TARGET_64BIT ? TImode : DImode) |
|
/* The maximum number of bytes that a single instruction can move quickly |
between memory and registers or between two memory locations. */ |
#define MOVE_MAX (TARGET_64BIT ? 16 : 8) |
#define MOVE_MAX_PIECES (TARGET_64BIT ? 8 : 4) |
#define MAX_MOVE_MAX 16 |
|
/* Determine whether to use move_by_pieces or block move insn. */ |
#define MOVE_BY_PIECES_P(SIZE, ALIGN) \ |
( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ |
|| (TARGET_64BIT && (SIZE) == 8) ) |
|
/* Determine whether to use clear_by_pieces or block clear insn. */ |
#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \ |
( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ |
|| (TARGET_64BIT && (SIZE) == 8) ) |
|
/* This macro is used to determine whether store_by_pieces should be |
called to "memset" storage with byte values other than zero, or |
to "memcpy" storage when the source is a constant string. */ |
#define STORE_BY_PIECES_P(SIZE, ALIGN) MOVE_BY_PIECES_P (SIZE, ALIGN) |
|
/* Don't perform CSE on function addresses. */ |
#define NO_FUNCTION_CSE |
|
|
/* Sections. */ |
|
/* Output before read-only data. */ |
#define TEXT_SECTION_ASM_OP ".text" |
|
/* Output before writable (initialized) data. */ |
#define DATA_SECTION_ASM_OP ".data" |
|
/* Output before writable (uninitialized) data. */ |
#define BSS_SECTION_ASM_OP ".bss" |
|
/* S/390 constant pool breaks the devices in crtstuff.c to control section |
in where code resides. We have to write it as asm code. */ |
#ifndef __s390x__ |
#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ |
asm (SECTION_OP "\n\ |
bras\t%r2,1f\n\ |
0: .long\t" USER_LABEL_PREFIX #FUNC " - 0b\n\ |
1: l\t%r3,0(%r2)\n\ |
bas\t%r14,0(%r3,%r2)\n\ |
.previous"); |
#endif |
|
|
/* Position independent code. */ |
|
extern int flag_pic; |
|
#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 12 : INVALID_REGNUM) |
|
#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X) |
|
|
/* Assembler file format. */ |
|
/* Character to start a comment. */ |
#define ASM_COMMENT_START "#" |
|
/* Declare an uninitialized external linkage data object. */ |
#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ |
asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) |
|
/* Globalizing directive for a label. */ |
#define GLOBAL_ASM_OP ".globl " |
|
/* Advance the location counter to a multiple of 2**LOG bytes. */ |
#define ASM_OUTPUT_ALIGN(FILE, LOG) \ |
if ((LOG)) fprintf ((FILE), "\t.align\t%d\n", 1 << (LOG)) |
|
/* Advance the location counter by SIZE bytes. */ |
#define ASM_OUTPUT_SKIP(FILE, SIZE) \ |
fprintf ((FILE), "\t.set\t.,.+"HOST_WIDE_INT_PRINT_UNSIGNED"\n", (SIZE)) |
|
/* The LOCAL_LABEL_PREFIX variable is used by dbxelf.h. */ |
#define LOCAL_LABEL_PREFIX "." |
|
/* How to refer to registers in assembler output. This sequence is |
indexed by compiler's hard-register-number (see above). */ |
#define REGISTER_NAMES \ |
{ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \ |
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", \ |
"%f0", "%f2", "%f4", "%f6", "%f1", "%f3", "%f5", "%f7", \ |
"%f8", "%f10", "%f12", "%f14", "%f9", "%f11", "%f13", "%f15", \ |
"%ap", "%cc", "%fp", "%rp", "%a0", "%a1" \ |
} |
|
/* Print operand X (an rtx) in assembler syntax to file FILE. */ |
#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) |
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) |
|
/* Output machine-dependent UNSPECs in address constants. */ |
#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL) \ |
do { \ |
if (!s390_output_addr_const_extra (FILE, (X))) \ |
goto FAIL; \ |
} while (0); |
|
/* Output an element of a case-vector that is absolute. */ |
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ |
do { \ |
char buf[32]; \ |
fputs (integer_asm_op (UNITS_PER_WORD, TRUE), (FILE)); \ |
ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ |
assemble_name ((FILE), buf); \ |
fputc ('\n', (FILE)); \ |
} while (0) |
|
/* Output an element of a case-vector that is relative. */ |
#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ |
do { \ |
char buf[32]; \ |
fputs (integer_asm_op (UNITS_PER_WORD, TRUE), (FILE)); \ |
ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ |
assemble_name ((FILE), buf); \ |
fputc ('-', (FILE)); \ |
ASM_GENERATE_INTERNAL_LABEL (buf, "L", (REL)); \ |
assemble_name ((FILE), buf); \ |
fputc ('\n', (FILE)); \ |
} while (0) |
|
|
/* Miscellaneous parameters. */ |
|
/* Specify the machine mode that this machine uses for the index in the |
tablejump instruction. */ |
#define CASE_VECTOR_MODE (TARGET_64BIT ? DImode : SImode) |
|
/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits |
is done just by pretending it is already truncated. */ |
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 |
|
/* Specify the machine mode that pointers have. |
After generation of rtl, the compiler makes no further distinction |
between pointers and any other objects of this machine mode. */ |
#define Pmode ((enum machine_mode) (TARGET_64BIT ? DImode : SImode)) |
|
/* This is -1 for "pointer mode" extend. See ptr_extend in s390.md. */ |
#define POINTERS_EXTEND_UNSIGNED -1 |
|
/* A function address in a call instruction is a byte address (for |
indexing purposes) so give the MEM rtx a byte's mode. */ |
#define FUNCTION_MODE QImode |
|
/* Specify the value which is used when clz operand is zero. */ |
#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 64, 1) |
|
/* Machine-specific symbol_ref flags. */ |
#define SYMBOL_FLAG_ALIGN1 (SYMBOL_FLAG_MACH_DEP << 0) |
|
/* Check whether integer displacement is in range. */ |
#define DISP_IN_RANGE(d) \ |
(TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \ |
: ((d) >= 0 && (d) <= 4095)) |
|
#endif |
/linux.h
0,0 → 1,105
/* Definitions for Linux for S/390. |
Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 |
Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@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/>. */ |
|
#ifndef _LINUX_H |
#define _LINUX_H |
|
/* Target specific version string. */ |
|
#ifdef DEFAULT_TARGET_64BIT |
#undef TARGET_VERSION |
#define TARGET_VERSION fprintf (stderr, " (Linux for zSeries)"); |
#else |
#undef TARGET_VERSION |
#define TARGET_VERSION fprintf (stderr, " (Linux for S/390)"); |
#endif |
|
|
/* Target specific type definitions. */ |
|
/* ??? Do we really want long as size_t on 31-bit? */ |
#undef SIZE_TYPE |
#define SIZE_TYPE (TARGET_64BIT ? "long unsigned int" : "long unsigned int") |
#undef PTRDIFF_TYPE |
#define PTRDIFF_TYPE (TARGET_64BIT ? "long int" : "int") |
|
#undef WCHAR_TYPE |
#define WCHAR_TYPE "int" |
#undef WCHAR_TYPE_SIZE |
#define WCHAR_TYPE_SIZE 32 |
|
|
/* Target specific preprocessor settings. */ |
|
#define TARGET_OS_CPP_BUILTINS() \ |
do \ |
{ \ |
LINUX_TARGET_OS_CPP_BUILTINS(); \ |
} \ |
while (0) |
|
|
/* Target specific assembler settings. */ |
|
#undef ASM_SPEC |
#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*}" |
|
|
/* Target specific linker settings. */ |
|
#ifdef DEFAULT_TARGET_64BIT |
#define MULTILIB_DEFAULTS { "m64" } |
#else |
#define MULTILIB_DEFAULTS { "m31" } |
#endif |
|
#define GLIBC_DYNAMIC_LINKER32 "/lib/ld.so.1" |
#define GLIBC_DYNAMIC_LINKER64 "/lib/ld64.so.1" |
|
#undef LINK_SPEC |
#define LINK_SPEC \ |
"%{m31:-m elf_s390}%{m64:-m elf64_s390} \ |
%{shared:-shared} \ |
%{!shared: \ |
%{static:-static} \ |
%{!static: \ |
%{rdynamic:-export-dynamic} \ |
%{!dynamic-linker: \ |
%{m31:-dynamic-linker " LINUX_DYNAMIC_LINKER32 "} \ |
%{m64:-dynamic-linker " LINUX_DYNAMIC_LINKER64 "}}}}" |
|
#define CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}" |
|
#define TARGET_ASM_FILE_END file_end_indicate_exec_stack |
|
#define MD_UNWIND_SUPPORT "config/s390/linux-unwind.h" |
|
#ifdef TARGET_LIBC_PROVIDES_SSP |
/* s390 glibc provides __stack_chk_guard in 0x14(tp), |
s390x glibc provides it at 0x28(tp). */ |
#define TARGET_THREAD_SSP_OFFSET (TARGET_64BIT ? 0x28 : 0x14) |
#endif |
|
/* Define if long doubles should be mangled as 'g'. */ |
#define TARGET_ALTERNATE_LONG_DOUBLE_MANGLING |
|
#endif |
/linux-unwind.h
0,0 → 1,134
/* DWARF2 EH unwinding support for S/390 Linux. |
Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. |
|
This file is part of GCC. |
|
GCC is free software; you can redistribute it and/or modify it under |
the terms of the GNU General Public License as published by the Free |
Software Foundation; either version 2, or (at your option) any later |
version. |
|
In addition to the permissions in the GNU General Public License, the |
Free Software Foundation gives you unlimited permission to link the |
compiled version of this file with other programs, and to distribute |
those programs without any restriction coming from the use of this |
file. (The General Public License restrictions do apply in other |
respects; for example, they cover modification of the file, and |
distribution when not linked into another program.) |
|
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 COPYING. If not, write to the Free |
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA |
02110-1301, USA. */ |
|
/* Do code reading to identify a signal frame, and set the frame |
state data appropriately. See unwind-dw2.c for the structs. */ |
|
#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state |
|
static _Unwind_Reason_Code |
s390_fallback_frame_state (struct _Unwind_Context *context, |
_Unwind_FrameState *fs) |
{ |
unsigned char *pc = context->ra; |
long new_cfa; |
int i; |
|
typedef struct |
{ |
unsigned long psw_mask; |
unsigned long psw_addr; |
unsigned long gprs[16]; |
unsigned int acrs[16]; |
unsigned int fpc; |
unsigned int __pad; |
double fprs[16]; |
} __attribute__ ((__aligned__ (8))) sigregs_; |
|
sigregs_ *regs; |
int *signo; |
|
/* svc $__NR_sigreturn or svc $__NR_rt_sigreturn */ |
if (pc[0] != 0x0a || (pc[1] != 119 && pc[1] != 173)) |
return _URC_END_OF_STACK; |
|
/* Legacy frames: |
old signal mask (8 bytes) |
pointer to sigregs (8 bytes) - points always to next location |
sigregs |
retcode |
This frame layout was used on kernels < 2.6.9 for non-RT frames, |
and on kernels < 2.4.13 for RT frames as well. Note that we need |
to look at RA to detect this layout -- this means that if you use |
sa_restorer to install a different signal restorer on a legacy |
kernel, unwinding from signal frames will not work. */ |
if (context->ra == context->cfa + 16 + sizeof (sigregs_)) |
{ |
regs = (sigregs_ *)(context->cfa + 16); |
signo = NULL; |
} |
|
/* New-style RT frame: |
retcode + alignment (8 bytes) |
siginfo (128 bytes) |
ucontext (contains sigregs) */ |
else if (pc[1] == 173 /* __NR_rt_sigreturn */) |
{ |
struct ucontext_ |
{ |
unsigned long uc_flags; |
struct ucontext_ *uc_link; |
unsigned long uc_stack[3]; |
sigregs_ uc_mcontext; |
} *uc = context->cfa + 8 + 128; |
|
regs = &uc->uc_mcontext; |
signo = context->cfa + sizeof(long); |
} |
|
/* New-style non-RT frame: |
old signal mask (8 bytes) |
pointer to sigregs (followed by signal number) */ |
else |
{ |
regs = *(sigregs_ **)(context->cfa + 8); |
signo = (int *)(regs + 1); |
} |
|
new_cfa = regs->gprs[15] + 16*sizeof(long) + 32; |
fs->cfa_how = CFA_REG_OFFSET; |
fs->cfa_reg = 15; |
fs->cfa_offset = |
new_cfa - (long) context->cfa + 16*sizeof(long) + 32; |
|
for (i = 0; i < 16; i++) |
{ |
fs->regs.reg[i].how = REG_SAVED_OFFSET; |
fs->regs.reg[i].loc.offset = |
(long)®s->gprs[i] - new_cfa; |
} |
for (i = 0; i < 16; i++) |
{ |
fs->regs.reg[16+i].how = REG_SAVED_OFFSET; |
fs->regs.reg[16+i].loc.offset = |
(long)®s->fprs[i] - new_cfa; |
} |
|
/* Load return addr from PSW into dummy register 32. */ |
|
fs->regs.reg[32].how = REG_SAVED_OFFSET; |
fs->regs.reg[32].loc.offset = (long)®s->psw_addr - new_cfa; |
fs->retaddr_column = 32; |
/* SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr |
after the faulting instruction rather than before it. |
Don't set FS->signal_frame in that case. */ |
if (!signo || (*signo != 4 && *signo != 5 && *signo != 8)) |
fs->signal_frame = 1; |
|
return _URC_NO_REASON; |
} |
/tpf.md
0,0 → 1,33
;; S390 TPF-OS specific machine patterns |
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
;; |
;; 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/>. |
|
(define_insn "prologue_tpf" |
[(unspec_volatile [(const_int 0)] UNSPECV_TPF_PROLOGUE) |
(clobber (reg:DI 1))] |
"TARGET_TPF_PROFILING" |
"larl\t%%r1,.+14\;tm\t4065,255\;bnz\t4064" |
[(set_attr "length" "14")]) |
|
|
(define_insn "epilogue_tpf" |
[(unspec_volatile [(const_int 0)] UNSPECV_TPF_EPILOGUE) |
(clobber (reg:DI 1))] |
"TARGET_TPF_PROFILING" |
"larl\t%%r1,.+14\;tm\t4071,255\;bnz\t4070" |
[(set_attr "length" "14")]) |
/tpf.opt
0,0 → 1,27
; Options for the TPF-OS port of the compiler. |
|
; Copyright (C) 2005, 2007 Free Software Foundation, Inc. |
; |
; 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/>. |
|
mtpf-trace |
Target Report Mask(TPF_PROFILING) |
Enable TPF-OS tracing code |
|
mmain |
Target Report |
Specify main object for TPF-OS |
/t-tpf
0,0 → 1,9
# Compile crtbeginS.o and crtendS.o with pic. |
CRTSTUFF_T_CFLAGS_S = $(CRTSTUFF_T_CFLAGS) -fPIC |
# Compile libgcc2.a with pic. |
TARGET_LIBGCC2_CFLAGS = -fPIC |
|
# Use unwind-dw2-fde-glibc. |
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-glibc.c \ |
$(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c |
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h |
/2064.md
0,0 → 1,135
;; Scheduling description for z900 (cpu 2064). |
;; Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
;; Ulrich Weigand (uweigand@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/>. |
|
;; |
;; References: |
;; The microarchitecture of the IBM eServer z900 processor. |
;; E.M. Schwarz et al. |
;; IBM Journal of Research and Development Vol. 46 No 4/5, 2002. |
;; |
;; z900 (cpu 2064) pipeline |
;; |
;; dec |
;; --> | <--- |
;; LA bypass | agen | |
;; | | | |
;; --- c1 | Load bypass |
;; | | |
;; c2---- |
;; | |
;; e1 |
;; | |
;; wr |
|
;; This scheduler description is also used for the g5 and g6. |
|
(define_automaton "z_ipu") |
(define_cpu_unit "z_e1" "z_ipu") |
(define_cpu_unit "z_wr" "z_ipu") |
|
|
(define_insn_reservation "z_la" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "la")) |
"z_e1,z_wr") |
|
(define_insn_reservation "z_larl" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "larl")) |
"z_e1,z_wr") |
|
(define_insn_reservation "z_load" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "load")) |
"z_e1,z_wr") |
|
(define_insn_reservation "z_store" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "store")) |
"z_e1,z_wr") |
|
(define_insn_reservation "z_sem" 2 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "sem")) |
"z_e1*2,z_wr") |
|
(define_insn_reservation "z_call" 5 |
(and (eq_attr "cpu" "z900,g5,g6") |
(eq_attr "type" "jsr")) |
"z_e1*5,z_wr") |
|
(define_insn_reservation "z_mul" 5 |
(and (eq_attr "cpu" "g5,g6,z900") |
(eq_attr "type" "imulsi,imulhi")) |
"z_e1*5,z_wr") |
|
(define_insn_reservation "z_inf" 10 |
(and (eq_attr "cpu" "g5,g6,z900") |
(eq_attr "type" "idiv,imuldi")) |
"z_e1*10,z_wr") |
|
;; For everything else we check the atype flag. |
|
(define_insn_reservation "z_int" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(and (not (eq_attr "type" "la,larl,load,store,jsr")) |
(eq_attr "atype" "reg"))) |
"z_e1,z_wr") |
|
(define_insn_reservation "z_agen" 1 |
(and (eq_attr "cpu" "z900,g5,g6") |
(and (not (eq_attr "type" "la,larl,load,store,jsr")) |
(eq_attr "atype" "agen"))) |
"z_e1,z_wr") |
|
;; |
;; s390_agen_dep_p returns 1, if a register is set in the |
;; first insn and used in the dependent insn to form a address. |
;; |
|
;; |
;; If an instruction uses a register to address memory, it needs |
;; to be set 5 cycles in advance. |
;; |
|
(define_bypass 5 "z_int,z_agen" |
"z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") |
|
;; |
;; A load type instruction uses a bypass to feed the result back |
;; to the address generation pipeline stage. |
;; |
|
(define_bypass 3 "z_load" |
"z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") |
|
;; |
;; A load address type instruction uses a bypass to feed the |
;; result back to the address generation pipeline stage. |
;; |
|
(define_bypass 2 "z_larl,z_la" |
"z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") |
|
|
|
|
|
/2084.md
0,0 → 1,309
;; Scheduling description for z990 (cpu 2084). |
;; Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. |
;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
;; Ulrich Weigand (uweigand@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/>. |
|
(define_automaton "x_ipu") |
|
(define_cpu_unit "x_e1_r,x_e1_s,x_e1_t" "x_ipu") |
(define_cpu_unit "x_wr_r,x_wr_s,x_wr_t,x_wr_fp" "x_ipu") |
(define_cpu_unit "x_s1,x_s2,x_s3,x_s4" "x_ipu") |
(define_cpu_unit "x_t1,x_t2,x_t3,x_t4" "x_ipu") |
(define_cpu_unit "x_f1,x_f2,x_f3,x_f4,x_f5,x_f6" "x_ipu") |
(define_cpu_unit "x_store_tok" "x_ipu") |
(define_cpu_unit "x_ms,x_mt" "x_ipu") |
|
(define_reservation "x-e1-st" "(x_e1_s | x_e1_t)") |
|
(define_reservation "x-e1-np" "(x_e1_r + x_e1_s + x_e1_t)") |
|
(absence_set "x_e1_r" "x_e1_s,x_e1_t") |
(absence_set "x_e1_s" "x_e1_t") |
|
;; Try to avoid int <-> fp transitions. |
|
(define_reservation "x-x" "x_s1|x_t1,x_s2|x_t2,x_s3|x_t3,x_s4|x_t4") |
(define_reservation "x-f" "x_f1,x_f2,x_f3,x_f4,x_f5,x_f6") |
(define_reservation "x-wr-st" "((x_wr_s | x_wr_t),x-x)") |
(define_reservation "x-wr-np" "((x_wr_r + x_wr_s + x_wr_t),x-x)") |
(define_reservation "x-wr-fp" "x_wr_fp,x-f") |
(define_reservation "x-mem" "x_ms|x_mt") |
|
(absence_set "x_wr_fp" |
"x_s1,x_s2,x_s3,x_s4,x_t1,x_t2,x_t3,x_t4,x_wr_s,x_wr_t") |
|
(absence_set "x_e1_r,x_wr_r,x_wr_s,x_wr_t" |
"x_f1,x_f2,x_f3,x_f4,x_f5,x_f6,x_wr_fp") |
|
;; Don't have any load type insn in same group as store |
|
(absence_set "x_ms,x_mt" "x_store_tok") |
|
|
;; |
;; Simple insns |
;; |
|
(define_insn_reservation "x_int" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(and (eq_attr "type" "integer") |
(eq_attr "atype" "reg"))) |
"x-e1-st,x-wr-st") |
|
(define_insn_reservation "x_agen" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(and (eq_attr "type" "integer") |
(eq_attr "atype" "agen"))) |
"x-e1-st,x-wr-st") |
|
(define_insn_reservation "x_lr" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "lr")) |
"x-e1-st,x-wr-st") |
|
(define_insn_reservation "x_la" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "la")) |
"x-e1-st,x-wr-st") |
|
(define_insn_reservation "x_larl" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "larl")) |
"x-e1-st,x-wr-st") |
|
(define_insn_reservation "x_load" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "load")) |
"x-e1-st+x-mem,x-wr-st") |
|
(define_insn_reservation "x_store" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "store")) |
"x-e1-st+x_store_tok,x-wr-st") |
|
(define_insn_reservation "x_branch" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "branch")) |
"x_e1_r,x_wr_r") |
|
(define_insn_reservation "x_call" 5 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "jsr")) |
"x-e1-np*5,x-wr-np") |
|
(define_insn_reservation "x_mul_hi" 2 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "imulhi")) |
"x-e1-np*2,x-wr-np") |
|
(define_insn_reservation "x_mul_sidi" 4 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "imulsi,imuldi")) |
"x-e1-np*4,x-wr-np") |
|
(define_insn_reservation "x_div" 10 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "idiv")) |
"x-e1-np*10,x-wr-np") |
|
(define_insn_reservation "x_sem" 17 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "sem")) |
"x-e1-np+x-mem,x-e1-np*16,x-wr-st") |
|
;; |
;; Multicycle insns |
;; |
|
(define_insn_reservation "x_cs" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "cs")) |
"x-e1-np,x-wr-np") |
|
(define_insn_reservation "x_vs" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "vs")) |
"x-e1-np*10,x-wr-np") |
|
(define_insn_reservation "x_stm" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "stm")) |
"(x-e1-np+x_store_tok)*10,x-wr-np") |
|
(define_insn_reservation "x_lm" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "lm")) |
"x-e1-np*10,x-wr-np") |
|
(define_insn_reservation "x_other" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "other")) |
"x-e1-np,x-wr-np") |
|
;; |
;; Floating point insns |
;; |
|
(define_insn_reservation "x_fsimptf" 7 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fsimptf")) |
"x_e1_t*2,x-wr-fp") |
|
(define_insn_reservation "x_fsimpdf" 6 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fsimpdf,fmuldf")) |
"x_e1_t,x-wr-fp") |
|
(define_insn_reservation "x_fsimpsf" 6 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fsimpsf,fmulsf")) |
"x_e1_t,x-wr-fp") |
|
|
(define_insn_reservation "x_fmultf" 33 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fmultf")) |
"x_e1_t*27,x-wr-fp") |
|
|
(define_insn_reservation "x_fdivtf" 82 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fdivtf,fsqrttf")) |
"x_e1_t*76,x-wr-fp") |
|
(define_insn_reservation "x_fdivdf" 36 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fdivdf,fsqrtdf")) |
"x_e1_t*30,x-wr-fp") |
|
(define_insn_reservation "x_fdivsf" 36 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fdivsf,fsqrtsf")) |
"x_e1_t*30,x-wr-fp") |
|
|
(define_insn_reservation "x_floadtf" 6 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "floadtf")) |
"x_e1_t,x-wr-fp") |
|
(define_insn_reservation "x_floaddf" 6 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "floaddf")) |
"x_e1_t,x-wr-fp") |
|
(define_insn_reservation "x_floadsf" 6 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "floadsf")) |
"x_e1_t,x-wr-fp") |
|
|
(define_insn_reservation "x_fstoredf" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fstoredf")) |
"x_e1_t,x-wr-fp") |
|
(define_insn_reservation "x_fstoresf" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "fstoresf")) |
"x_e1_t,x-wr-fp") |
|
|
(define_insn_reservation "x_ftrunctf" 16 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "ftrunctf")) |
"x_e1_t*10,x-wr-fp") |
|
(define_insn_reservation "x_ftruncdf" 11 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "ftruncdf")) |
"x_e1_t*5,x-wr-fp") |
|
|
(define_insn_reservation "x_ftoi" 1 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "ftoi")) |
"x_e1_t*3,x-wr-fp") |
|
(define_insn_reservation "x_itof" 7 |
(and (eq_attr "cpu" "z990,z9_109") |
(eq_attr "type" "itof")) |
"x_e1_t*3,x-wr-fp") |
|
(define_bypass 1 "x_fsimpdf" "x_fstoredf") |
|
(define_bypass 1 "x_fsimpsf" "x_fstoresf") |
|
(define_bypass 1 "x_floaddf" "x_fsimpdf,x_fstoredf,x_floaddf") |
|
(define_bypass 1 "x_floadsf" "x_fsimpsf,x_fstoresf,x_floadsf") |
|
;; |
;; s390_agen_dep_p returns 1, if a register is set in the |
;; first insn and used in the dependent insn to form a address. |
;; |
|
;; |
;; If an instruction uses a register to address memory, it needs |
;; to be set 5 cycles in advance. |
;; |
|
(define_bypass 5 "x_int,x_agen,x_lr" |
"x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" |
"s390_agen_dep_p") |
|
(define_bypass 9 "x_int,x_agen,x_lr" |
"x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ |
x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" |
"s390_agen_dep_p") |
;; |
;; A load type instruction uses a bypass to feed the result back |
;; to the address generation pipeline stage. |
;; |
|
(define_bypass 4 "x_load" |
"x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" |
"s390_agen_dep_p") |
|
(define_bypass 5 "x_load" |
"x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ |
x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" |
"s390_agen_dep_p") |
|
;; |
;; A load address type instruction uses a bypass to feed the |
;; result back to the address generation pipeline stage. |
;; |
|
(define_bypass 3 "x_larl,x_la" |
"x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" |
"s390_agen_dep_p") |
|
(define_bypass 5 "x_larl, x_la" |
"x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ |
x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" |
"s390_agen_dep_p") |
|
;; |
;; Operand forwarding |
;; |
|
(define_bypass 0 "x_lr,x_la,x_load" "x_int,x_lr") |
|
|
/libgcc-glibc.ver
0,0 → 1,74
# In order to work around the very problems that force us to now generally |
# create a libgcc.so, glibc reexported a number of routines from libgcc.a. |
# By now choosing the same version tags for these specific routines, we |
# maintain enough binary compatibility to allow future versions of glibc |
# to defer implementation of these routines to libgcc.so via DT_AUXILIARY. |
|
# Note that we cannot use the default libgcc-glibc.ver file on s390x, |
# because GLIBC_2.0 does not exist on this architecture, as the first |
# ever glibc release on the platform was GLIBC_2.2. |
|
%ifndef __s390x__ |
%inherit GCC_3.0 GLIBC_2.0 |
GLIBC_2.0 { |
__divdi3 |
__moddi3 |
__udivdi3 |
__umoddi3 |
|
__register_frame |
__register_frame_table |
__deregister_frame |
__register_frame_info |
__deregister_frame_info |
__frame_state_for |
__register_frame_info_table |
} |
%endif |
|
%ifdef __s390x__ |
%inherit GCC_3.0 GLIBC_2.2 |
GLIBC_2.2 { |
__register_frame |
__register_frame_table |
__deregister_frame |
__register_frame_info |
__deregister_frame_info |
__frame_state_for |
__register_frame_info_table |
} |
%endif |
|
# With GCC 4.1.0 long double 128 bit support was introduced. The |
# following symbols coming from libgcc are enabled when -mlong-double-128 |
# is specified. These lines make the symbols to get a @@GCC_4.1.0 attached. |
|
%exclude { |
__divtc3 |
__multc3 |
__powitf2 |
__fixtfti |
__fixunstfti |
__floattitf |
|
__fixtfdi |
__fixunstfdi |
__floatditf |
} |
|
GCC_4.1.0 { |
__divtc3 |
__multc3 |
__powitf2 |
|
%ifdef __s390x__ |
__fixtfti |
__fixunstfti |
__floattitf |
|
%else |
__fixtfdi |
__fixunstfdi |
__floatditf |
%endif |
} |
/s390.md
0,0 → 1,7856
;;- Machine description for GNU compiler -- S/390 / zSeries version. |
;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 |
;; Free Software Foundation, Inc. |
;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
;; Ulrich Weigand (uweigand@de.ibm.com). |
|
;; 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/>. |
|
;; |
;; See constraints.md for a description of constraints specific to s390. |
;; |
|
;; Special formats used for outputting 390 instructions. |
;; |
;; %C: print opcode suffix for branch condition. |
;; %D: print opcode suffix for inverse branch condition. |
;; %J: print tls_load/tls_gdcall/tls_ldcall suffix |
;; %G: print the size of the operand in bytes. |
;; %O: print only the displacement of a memory reference. |
;; %R: print only the base register of a memory reference. |
;; %S: print S-type memory reference (base+displacement). |
;; %N: print the second word of a DImode operand. |
;; %M: print the second word of a TImode operand. |
;; %Y: print shift count operand. |
;; |
;; %b: print integer X as if it's an unsigned byte. |
;; %x: print integer X as if it's an unsigned halfword. |
;; %h: print integer X as if it's a signed halfword. |
;; %i: print the first nonzero HImode part of X. |
;; %j: print the first HImode part unequal to -1 of X. |
;; %k: print the first nonzero SImode part of X. |
;; %m: print the first SImode part unequal to -1 of X. |
;; %o: print integer X as if it's an unsigned 32bit word. |
;; |
;; We have a special constraint for pattern matching. |
;; |
;; s_operand -- Matches a valid S operand in a RS, SI or SS type instruction. |
;; |
|
;; |
;; UNSPEC usage |
;; |
|
(define_constants |
[; Miscellaneous |
(UNSPEC_ROUND 1) |
(UNSPEC_CMPINT 2) |
(UNSPEC_ICM 10) |
|
; GOT/PLT and lt-relative accesses |
(UNSPEC_LTREL_OFFSET 100) |
(UNSPEC_LTREL_BASE 101) |
(UNSPEC_GOTENT 110) |
(UNSPEC_GOT 111) |
(UNSPEC_GOTOFF 112) |
(UNSPEC_PLT 113) |
(UNSPEC_PLTOFF 114) |
|
; Literal pool |
(UNSPEC_RELOAD_BASE 210) |
(UNSPEC_MAIN_BASE 211) |
(UNSPEC_LTREF 212) |
(UNSPEC_INSN 213) |
(UNSPEC_EXECUTE 214) |
|
; TLS relocation specifiers |
(UNSPEC_TLSGD 500) |
(UNSPEC_TLSLDM 501) |
(UNSPEC_NTPOFF 502) |
(UNSPEC_DTPOFF 503) |
(UNSPEC_GOTNTPOFF 504) |
(UNSPEC_INDNTPOFF 505) |
|
; TLS support |
(UNSPEC_TLSLDM_NTPOFF 511) |
(UNSPEC_TLS_LOAD 512) |
|
; String Functions |
(UNSPEC_SRST 600) |
(UNSPEC_MVST 601) |
|
; Stack Smashing Protector |
(UNSPEC_SP_SET 700) |
(UNSPEC_SP_TEST 701) |
]) |
|
;; |
;; UNSPEC_VOLATILE usage |
;; |
|
(define_constants |
[; Blockage |
(UNSPECV_BLOCKAGE 0) |
|
; TPF Support |
(UNSPECV_TPF_PROLOGUE 20) |
(UNSPECV_TPF_EPILOGUE 21) |
|
; Literal pool |
(UNSPECV_POOL 200) |
(UNSPECV_POOL_SECTION 201) |
(UNSPECV_POOL_ALIGN 202) |
(UNSPECV_POOL_ENTRY 203) |
(UNSPECV_MAIN_POOL 300) |
|
; TLS support |
(UNSPECV_SET_TP 500) |
|
; Atomic Support |
(UNSPECV_MB 700) |
(UNSPECV_CAS 701) |
]) |
|
;; |
;; Registers |
;; |
|
(define_constants |
[ |
; Sibling call register. |
(SIBCALL_REGNUM 1) |
; Literal pool base register. |
(BASE_REGNUM 13) |
; Return address register. |
(RETURN_REGNUM 14) |
; Condition code register. |
(CC_REGNUM 33) |
; Thread local storage pointer register. |
(TP_REGNUM 36) |
]) |
|
|
;; Instruction operand type as used in the Principles of Operation. |
;; Used to determine defaults for length and other attribute values. |
|
(define_attr "op_type" |
"NN,E,RR,RRE,RX,RS,RSI,RI,SI,S,SS,SSE,RXE,RSE,RIL,RIE,RXY,RSY,SIY" |
(const_string "NN")) |
|
;; Instruction type attribute used for scheduling. |
|
(define_attr "type" "none,integer,load,lr,la,larl,lm,stm, |
cs,vs,store,sem,idiv, |
imulhi,imulsi,imuldi, |
branch,jsr,fsimptf,fsimpdf,fsimpsf, |
floadtf,floaddf,floadsf,fstoredf,fstoresf, |
fmultf,fmuldf,fmulsf,fdivtf,fdivdf,fdivsf, |
ftoi,itof,fsqrttf,fsqrtdf,fsqrtsf, |
ftrunctf,ftruncdf,other" |
(cond [(eq_attr "op_type" "NN") (const_string "other") |
(eq_attr "op_type" "SS") (const_string "cs")] |
(const_string "integer"))) |
|
;; Another attribute used for scheduling purposes: |
;; agen: Instruction uses the address generation unit |
;; reg: Instruction does not use the agen unit |
|
(define_attr "atype" "agen,reg" |
(if_then_else (eq_attr "op_type" "E,RR,RI,RRE") |
(const_string "reg") |
(const_string "agen"))) |
|
;; Length in bytes. |
|
(define_attr "length" "" |
(cond [(eq_attr "op_type" "E,RR") (const_int 2) |
(eq_attr "op_type" "RX,RI,RRE,RS,RSI,S,SI") (const_int 4)] |
(const_int 6))) |
|
|
;; Processor type. This attribute must exactly match the processor_type |
;; enumeration in s390.h. The current machine description does not |
;; distinguish between g5 and g6, but there are differences between the two |
;; CPUs could in theory be modeled. |
|
(define_attr "cpu" "g5,g6,z900,z990,z9_109" |
(const (symbol_ref "s390_tune"))) |
|
;; Pipeline description for z900. For lack of anything better, |
;; this description is also used for the g5 and g6. |
(include "2064.md") |
|
;; Pipeline description for z990. |
(include "2084.md") |
|
;; Predicates |
(include "predicates.md") |
|
;; Constraint definitions |
(include "constraints.md") |
|
;; Other includes |
(include "tpf.md") |
|
;; Macros |
|
;; This mode macro allows floating point patterns to be generated from the |
;; same template. |
(define_mode_macro FPR [TF DF SF]) |
(define_mode_macro DSF [DF SF]) |
|
;; These mode macros allow 31-bit and 64-bit TDSI patterns to be generated |
;; from the same template. |
(define_mode_macro TDSI [(TI "TARGET_64BIT") DI SI]) |
|
;; These mode macros allow 31-bit and 64-bit GPR patterns to be generated |
;; from the same template. |
(define_mode_macro GPR [(DI "TARGET_64BIT") SI]) |
(define_mode_macro DSI [DI SI]) |
|
;; This mode macro allows :P to be used for patterns that operate on |
;; pointer-sized quantities. Exactly one of the two alternatives will match. |
(define_mode_macro DP [(TI "TARGET_64BIT") (DI "!TARGET_64BIT")]) |
(define_mode_macro P [(DI "TARGET_64BIT") (SI "!TARGET_64BIT")]) |
|
;; This mode macro allows the QI and HI patterns to be defined from |
;; the same template. |
(define_mode_macro HQI [HI QI]) |
|
;; This mode macro allows the integer patterns to be defined from the |
;; same template. |
(define_mode_macro INT [(DI "TARGET_64BIT") SI HI QI]) |
|
;; This macro allows to unify all 'bCOND' expander patterns. |
(define_code_macro COMPARE [eq ne gt gtu lt ltu ge geu le leu unordered |
ordered uneq unlt ungt unle unge ltgt]) |
|
;; This macro allows to unify all 'sCOND' patterns. |
(define_code_macro SCOND [ltu gtu leu geu]) |
|
;; This macro allows some 'ashift' and 'lshiftrt' pattern to be defined from |
;; the same template. |
(define_code_macro SHIFT [ashift lshiftrt]) |
|
;; These macros allow to combine most atomic operations. |
(define_code_macro ATOMIC [and ior xor plus minus mult]) |
(define_code_attr atomic [(and "and") (ior "ior") (xor "xor") |
(plus "add") (minus "sub") (mult "nand")]) |
|
|
;; In FPR templates, a string like "lt<de>br" will expand to "ltxbr" in TFmode, |
;; "ltdbr" in DFmode, and "ltebr" in SFmode. |
(define_mode_attr xde [(TF "x") (DF "d") (SF "e")]) |
|
;; In FPR templates, a string like "m<dee>br" will expand to "mxbr" in TFmode, |
;; "mdbr" in DFmode, and "meebr" in SFmode. |
(define_mode_attr xdee [(TF "x") (DF "d") (SF "ee")]) |
|
;; In FPR templates, "<RRe>" will expand to "RRE" in TFmode and "RR" otherwise. |
;; Likewise for "<RXe>". |
(define_mode_attr RRe [(TF "RRE") (DF "RR") (SF "RR")]) |
(define_mode_attr RXe [(TF "RXE") (DF "RX") (SF "RX")]) |
|
;; In FPR templates, "<Rf>" will expand to "f" in TFmode and "R" otherwise. |
;; This is used to disable the memory alternative in TFmode patterns. |
(define_mode_attr Rf [(TF "f") (DF "R") (SF "R")]) |
|
;; In GPR and P templates, a constraint like "<d0>" will expand to "d" in DImode |
;; and "0" in SImode. This allows to combine instructions of which the 31bit |
;; version only operates on one register. |
(define_mode_attr d0 [(DI "d") (SI "0")]) |
|
;; In combination with d0 this allows to combine instructions of which the 31bit |
;; version only operates on one register. The DImode version needs an additional |
;; register for the assembler output. |
(define_mode_attr 1 [(DI "%1,") (SI "")]) |
|
;; In SHIFT templates, a string like "s<lr>dl" will expand to "sldl" in |
;; 'ashift' and "srdl" in 'lshiftrt'. |
(define_code_attr lr [(ashift "l") (lshiftrt "r")]) |
|
;; In SHIFT templates, this attribute holds the correct standard name for the |
;; pattern itself and the corresponding function calls. |
(define_code_attr shift [(ashift "ashl") (lshiftrt "lshr")]) |
|
;; This attribute handles differences in the instruction 'type' and will result |
;; in "RRE" for DImode and "RR" for SImode. |
(define_mode_attr E [(DI "E") (SI "")]) |
|
;; This attribute handles differences in the instruction 'type' and makes RX<Y> |
;; to result in "RXY" for DImode and "RX" for SImode. |
(define_mode_attr Y [(DI "Y") (SI "")]) |
|
;; This attribute handles differences in the instruction 'type' and will result |
;; in "RSE" for TImode and "RS" for DImode. |
(define_mode_attr TE [(TI "E") (DI "")]) |
|
;; In GPR templates, a string like "lc<g>r" will expand to "lcgr" in DImode |
;; and "lcr" in SImode. |
(define_mode_attr g [(DI "g") (SI "")]) |
|
;; In GPR templates, a string like "sl<y>" will expand to "slg" in DImode |
;; and "sly" in SImode. This is useful because on 64bit the ..g instructions |
;; were enhanced with long displacements whereas 31bit instructions got a ..y |
;; variant for long displacements. |
(define_mode_attr y [(DI "g") (SI "y")]) |
|
;; In DP templates, a string like "cds<g>" will expand to "cdsg" in TImode |
;; and "cds" in DImode. |
(define_mode_attr tg [(TI "g") (DI "")]) |
|
;; In GPR templates, a string like "c<gf>dbr" will expand to "cgdbr" in DImode |
;; and "cfdbr" in SImode. |
(define_mode_attr gf [(DI "g") (SI "f")]) |
|
;; ICM mask required to load MODE value into the lowest subreg |
;; of a SImode register. |
(define_mode_attr icm_lo [(HI "3") (QI "1")]) |
|
;; In HQI templates, a string like "llg<hc>" will expand to "llgh" in |
;; HImode and "llgc" in QImode. |
(define_mode_attr hc [(HI "h") (QI "c")]) |
|
;; In P templates, the mode <DBL> will expand to "TI" in DImode and "DI" |
;; in SImode. |
(define_mode_attr DBL [(DI "TI") (SI "DI")]) |
|
;; Maximum unsigned integer that fits in MODE. |
(define_mode_attr max_uint [(HI "65535") (QI "255")]) |
|
|
;; |
;;- Compare instructions. |
;; |
|
(define_expand "cmp<mode>" |
[(set (reg:CC CC_REGNUM) |
(compare:CC (match_operand:GPR 0 "register_operand" "") |
(match_operand:GPR 1 "general_operand" "")))] |
"" |
{ |
s390_compare_op0 = operands[0]; |
s390_compare_op1 = operands[1]; |
DONE; |
}) |
|
(define_expand "cmp<mode>" |
[(set (reg:CC CC_REGNUM) |
(compare:CC (match_operand:FPR 0 "register_operand" "") |
(match_operand:FPR 1 "general_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
s390_compare_op0 = operands[0]; |
s390_compare_op1 = operands[1]; |
DONE; |
}) |
|
|
; Test-under-Mask instructions |
|
(define_insn "*tmqi_mem" |
[(set (reg CC_REGNUM) |
(compare (and:QI (match_operand:QI 0 "memory_operand" "Q,S") |
(match_operand:QI 1 "immediate_operand" "n,n")) |
(match_operand:QI 2 "immediate_operand" "n,n")))] |
"s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], false))" |
"@ |
tm\t%S0,%b1 |
tmy\t%S0,%b1" |
[(set_attr "op_type" "SI,SIY")]) |
|
(define_insn "*tmdi_reg" |
[(set (reg CC_REGNUM) |
(compare (and:DI (match_operand:DI 0 "nonimmediate_operand" "d,d,d,d") |
(match_operand:DI 1 "immediate_operand" |
"N0HD0,N1HD0,N2HD0,N3HD0")) |
(match_operand:DI 2 "immediate_operand" "n,n,n,n")))] |
"TARGET_64BIT |
&& s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], true)) |
&& s390_single_part (operands[1], DImode, HImode, 0) >= 0" |
"@ |
tmhh\t%0,%i1 |
tmhl\t%0,%i1 |
tmlh\t%0,%i1 |
tmll\t%0,%i1" |
[(set_attr "op_type" "RI")]) |
|
(define_insn "*tmsi_reg" |
[(set (reg CC_REGNUM) |
(compare (and:SI (match_operand:SI 0 "nonimmediate_operand" "d,d") |
(match_operand:SI 1 "immediate_operand" "N0HS0,N1HS0")) |
(match_operand:SI 2 "immediate_operand" "n,n")))] |
"s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], true)) |
&& s390_single_part (operands[1], SImode, HImode, 0) >= 0" |
"@ |
tmh\t%0,%i1 |
tml\t%0,%i1" |
[(set_attr "op_type" "RI")]) |
|
(define_insn "*tm<mode>_full" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HQI 0 "register_operand" "d") |
(match_operand:HQI 1 "immediate_operand" "n")))] |
"s390_match_ccmode (insn, s390_tm_ccmode (constm1_rtx, operands[1], true))" |
"tml\t%0,<max_uint>" |
[(set_attr "op_type" "RI")]) |
|
|
; |
; Load-and-Test instructions |
; |
|
; tst(di|si) instruction pattern(s). |
|
(define_insn "*tstdi_sign" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (ashift:DI (subreg:DI (match_operand:SI 0 "register_operand" "d") 0) |
(const_int 32)) (const_int 32)) |
(match_operand:DI 1 "const0_operand" ""))) |
(set (match_operand:DI 2 "register_operand" "=d") |
(sign_extend:DI (match_dup 0)))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" |
"ltgfr\t%2,%0" |
[(set_attr "op_type" "RRE")]) |
|
; ltr, lt, ltgr, ltg |
(define_insn "*tst<mode>_extimm" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 0 "nonimmediate_operand" "d,m") |
(match_operand:GPR 1 "const0_operand" ""))) |
(set (match_operand:GPR 2 "register_operand" "=d,d") |
(match_dup 0))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_EXTIMM" |
"@ |
lt<g>r\t%2,%0 |
lt<g>\t%2,%0" |
[(set_attr "op_type" "RR<E>,RXY")]) |
|
; ltr, lt, ltgr, ltg |
(define_insn "*tst<mode>_cconly_extimm" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 0 "nonimmediate_operand" "d,m") |
(match_operand:GPR 1 "const0_operand" ""))) |
(clobber (match_scratch:GPR 2 "=X,d"))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_EXTIMM" |
"@ |
lt<g>r\t%0,%0 |
lt<g>\t%2,%0" |
[(set_attr "op_type" "RR<E>,RXY")]) |
|
(define_insn "*tstdi" |
[(set (reg CC_REGNUM) |
(compare (match_operand:DI 0 "register_operand" "d") |
(match_operand:DI 1 "const0_operand" ""))) |
(set (match_operand:DI 2 "register_operand" "=d") |
(match_dup 0))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_64BIT && !TARGET_EXTIMM" |
"ltgr\t%2,%0" |
[(set_attr "op_type" "RRE")]) |
|
(define_insn "*tstsi" |
[(set (reg CC_REGNUM) |
(compare (match_operand:SI 0 "nonimmediate_operand" "d,Q,S") |
(match_operand:SI 1 "const0_operand" ""))) |
(set (match_operand:SI 2 "register_operand" "=d,d,d") |
(match_dup 0))] |
"s390_match_ccmode(insn, CCSmode) && !TARGET_EXTIMM" |
"@ |
ltr\t%2,%0 |
icm\t%2,15,%S0 |
icmy\t%2,15,%S0" |
[(set_attr "op_type" "RR,RS,RSY")]) |
|
(define_insn "*tstsi_cconly" |
[(set (reg CC_REGNUM) |
(compare (match_operand:SI 0 "nonimmediate_operand" "d,Q,S") |
(match_operand:SI 1 "const0_operand" ""))) |
(clobber (match_scratch:SI 2 "=X,d,d"))] |
"s390_match_ccmode(insn, CCSmode)" |
"@ |
ltr\t%0,%0 |
icm\t%2,15,%S0 |
icmy\t%2,15,%S0" |
[(set_attr "op_type" "RR,RS,RSY")]) |
|
(define_insn "*tstdi_cconly_31" |
[(set (reg CC_REGNUM) |
(compare (match_operand:DI 0 "register_operand" "d") |
(match_operand:DI 1 "const0_operand" "")))] |
"s390_match_ccmode(insn, CCSmode) && !TARGET_64BIT" |
"srda\t%0,0" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
; ltr, ltgr |
(define_insn "*tst<mode>_cconly2" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 0 "register_operand" "d") |
(match_operand:GPR 1 "const0_operand" "")))] |
"s390_match_ccmode(insn, CCSmode)" |
"lt<g>r\t%0,%0" |
[(set_attr "op_type" "RR<E>")]) |
|
; tst(hi|qi) instruction pattern(s). |
|
(define_insn "*tst<mode>CCT" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HQI 0 "nonimmediate_operand" "?Q,?S,d") |
(match_operand:HQI 1 "const0_operand" ""))) |
(set (match_operand:HQI 2 "register_operand" "=d,d,0") |
(match_dup 0))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
icm\t%2,<icm_lo>,%S0 |
icmy\t%2,<icm_lo>,%S0 |
tml\t%0,<max_uint>" |
[(set_attr "op_type" "RS,RSY,RI")]) |
|
(define_insn "*tsthiCCT_cconly" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HI 0 "nonimmediate_operand" "Q,S,d") |
(match_operand:HI 1 "const0_operand" ""))) |
(clobber (match_scratch:HI 2 "=d,d,X"))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
icm\t%2,3,%S0 |
icmy\t%2,3,%S0 |
tml\t%0,65535" |
[(set_attr "op_type" "RS,RSY,RI")]) |
|
(define_insn "*tstqiCCT_cconly" |
[(set (reg CC_REGNUM) |
(compare (match_operand:QI 0 "nonimmediate_operand" "?Q,?S,d") |
(match_operand:QI 1 "const0_operand" "")))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
cli\t%S0,0 |
cliy\t%S0,0 |
tml\t%0,255" |
[(set_attr "op_type" "SI,SIY,RI")]) |
|
(define_insn "*tst<mode>" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HQI 0 "s_operand" "Q,S") |
(match_operand:HQI 1 "const0_operand" ""))) |
(set (match_operand:HQI 2 "register_operand" "=d,d") |
(match_dup 0))] |
"s390_match_ccmode(insn, CCSmode)" |
"@ |
icm\t%2,<icm_lo>,%S0 |
icmy\t%2,<icm_lo>,%S0" |
[(set_attr "op_type" "RS,RSY")]) |
|
(define_insn "*tst<mode>_cconly" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HQI 0 "s_operand" "Q,S") |
(match_operand:HQI 1 "const0_operand" ""))) |
(clobber (match_scratch:HQI 2 "=d,d"))] |
"s390_match_ccmode(insn, CCSmode)" |
"@ |
icm\t%2,<icm_lo>,%S0 |
icmy\t%2,<icm_lo>,%S0" |
[(set_attr "op_type" "RS,RSY")]) |
|
|
; Compare (equality) instructions |
|
(define_insn "*cmpdi_cct" |
[(set (reg CC_REGNUM) |
(compare (match_operand:DI 0 "nonimmediate_operand" "%d,d,d,d,Q") |
(match_operand:DI 1 "general_operand" "d,K,Os,m,BQ")))] |
"s390_match_ccmode (insn, CCTmode) && TARGET_64BIT" |
"@ |
cgr\t%0,%1 |
cghi\t%0,%h1 |
cgfi\t%0,%1 |
cg\t%0,%1 |
#" |
[(set_attr "op_type" "RRE,RI,RIL,RXY,SS")]) |
|
(define_insn "*cmpsi_cct" |
[(set (reg CC_REGNUM) |
(compare (match_operand:SI 0 "nonimmediate_operand" "%d,d,d,d,d,Q") |
(match_operand:SI 1 "general_operand" "d,K,Os,R,T,BQ")))] |
"s390_match_ccmode (insn, CCTmode)" |
"@ |
cr\t%0,%1 |
chi\t%0,%h1 |
cfi\t%0,%1 |
c\t%0,%1 |
cy\t%0,%1 |
#" |
[(set_attr "op_type" "RR,RI,RIL,RX,RXY,SS")]) |
|
|
; Compare (signed) instructions |
|
(define_insn "*cmpdi_ccs_sign" |
[(set (reg CC_REGNUM) |
(compare (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")) |
(match_operand:DI 0 "register_operand" "d,d")))] |
"s390_match_ccmode(insn, CCSRmode) && TARGET_64BIT" |
"@ |
cgfr\t%0,%1 |
cgf\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*cmpsi_ccs_sign" |
[(set (reg CC_REGNUM) |
(compare (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,T")) |
(match_operand:SI 0 "register_operand" "d,d")))] |
"s390_match_ccmode(insn, CCSRmode)" |
"@ |
ch\t%0,%1 |
chy\t%0,%1" |
[(set_attr "op_type" "RX,RXY")]) |
|
; cr, chi, cfi, c, cy, cgr, cghi, cgfi, cg |
(define_insn "*cmp<mode>_ccs" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 0 "register_operand" "d,d,d,d,d") |
(match_operand:GPR 1 "general_operand" "d,K,Os,R,T")))] |
"s390_match_ccmode(insn, CCSmode)" |
"@ |
c<g>r\t%0,%1 |
c<g>hi\t%0,%h1 |
c<g>fi\t%0,%1 |
c<g>\t%0,%1 |
c<y>\t%0,%1" |
[(set_attr "op_type" "RR<E>,RI,RIL,RX<Y>,RXY")]) |
|
|
; Compare (unsigned) instructions |
|
(define_insn "*cmpdi_ccu_zero" |
[(set (reg CC_REGNUM) |
(compare (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")) |
(match_operand:DI 0 "register_operand" "d,d")))] |
"s390_match_ccmode (insn, CCURmode) && TARGET_64BIT" |
"@ |
clgfr\t%0,%1 |
clgf\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*cmpdi_ccu" |
[(set (reg CC_REGNUM) |
(compare (match_operand:DI 0 "nonimmediate_operand" "d,d,d,Q,BQ") |
(match_operand:DI 1 "general_operand" "d,Op,m,BQ,Q")))] |
"s390_match_ccmode (insn, CCUmode) && TARGET_64BIT" |
"@ |
clgr\t%0,%1 |
clgfi\t%0,%1 |
clg\t%0,%1 |
# |
#" |
[(set_attr "op_type" "RRE,RIL,RXY,SS,SS")]) |
|
(define_insn "*cmpsi_ccu" |
[(set (reg CC_REGNUM) |
(compare (match_operand:SI 0 "nonimmediate_operand" "d,d,d,d,Q,BQ") |
(match_operand:SI 1 "general_operand" "d,Os,R,T,BQ,Q")))] |
"s390_match_ccmode (insn, CCUmode)" |
"@ |
clr\t%0,%1 |
clfi\t%0,%o1 |
cl\t%0,%1 |
cly\t%0,%1 |
# |
#" |
[(set_attr "op_type" "RR,RIL,RX,RXY,SS,SS")]) |
|
(define_insn "*cmphi_ccu" |
[(set (reg CC_REGNUM) |
(compare (match_operand:HI 0 "nonimmediate_operand" "d,d,Q,BQ") |
(match_operand:HI 1 "general_operand" "Q,S,BQ,Q")))] |
"s390_match_ccmode (insn, CCUmode) |
&& !register_operand (operands[1], HImode)" |
"@ |
clm\t%0,3,%S1 |
clmy\t%0,3,%S1 |
# |
#" |
[(set_attr "op_type" "RS,RSY,SS,SS")]) |
|
(define_insn "*cmpqi_ccu" |
[(set (reg CC_REGNUM) |
(compare (match_operand:QI 0 "nonimmediate_operand" "d,d,Q,S,Q,BQ") |
(match_operand:QI 1 "general_operand" "Q,S,n,n,BQ,Q")))] |
"s390_match_ccmode (insn, CCUmode) |
&& !register_operand (operands[1], QImode)" |
"@ |
clm\t%0,1,%S1 |
clmy\t%0,1,%S1 |
cli\t%S0,%b1 |
cliy\t%S0,%b1 |
# |
#" |
[(set_attr "op_type" "RS,RSY,SI,SIY,SS,SS")]) |
|
|
; Block compare (CLC) instruction patterns. |
|
(define_insn "*clc" |
[(set (reg CC_REGNUM) |
(compare (match_operand:BLK 0 "memory_operand" "Q") |
(match_operand:BLK 1 "memory_operand" "Q"))) |
(use (match_operand 2 "const_int_operand" "n"))] |
"s390_match_ccmode (insn, CCUmode) |
&& INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" |
"clc\t%O0(%2,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_split |
[(set (reg CC_REGNUM) |
(compare (match_operand 0 "memory_operand" "") |
(match_operand 1 "memory_operand" "")))] |
"reload_completed |
&& s390_match_ccmode (insn, CCUmode) |
&& GET_MODE (operands[0]) == GET_MODE (operands[1]) |
&& GET_MODE_SIZE (GET_MODE (operands[0])) > 0" |
[(parallel |
[(set (match_dup 0) (match_dup 1)) |
(use (match_dup 2))])] |
{ |
operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); |
operands[0] = adjust_address (operands[0], BLKmode, 0); |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
|
operands[1] = gen_rtx_COMPARE (GET_MODE (SET_DEST (PATTERN (curr_insn))), |
operands[0], operands[1]); |
operands[0] = SET_DEST (PATTERN (curr_insn)); |
}) |
|
|
; (DF|SF) instructions |
|
; ltxbr, ltdbr, ltebr |
(define_insn "*cmp<mode>_ccs_0" |
[(set (reg CC_REGNUM) |
(compare (match_operand:FPR 0 "register_operand" "f") |
(match_operand:FPR 1 "const0_operand" "")))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lt<xde>br\t%0,%0" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; ltxr, ltdr, lter |
(define_insn "*cmp<mode>_ccs_0_ibm" |
[(set (reg CC_REGNUM) |
(compare (match_operand:FPR 0 "register_operand" "f") |
(match_operand:FPR 1 "const0_operand" "")))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"lt<xde>r\t%0,%0" |
[(set_attr "op_type" "<RRe>") |
(set_attr "type" "fsimp<mode>")]) |
|
; cxbr, cdbr, cebr, cxb, cdb, ceb |
(define_insn "*cmp<mode>_ccs" |
[(set (reg CC_REGNUM) |
(compare (match_operand:FPR 0 "register_operand" "f,f") |
(match_operand:FPR 1 "general_operand" "f,<Rf>")))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
c<xde>br\t%0,%1 |
c<xde>b\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; cxr, cdr, cer, cx, cd, ce |
(define_insn "*cmp<mode>_ccs_ibm" |
[(set (reg CC_REGNUM) |
(compare (match_operand:FPR 0 "register_operand" "f,f") |
(match_operand:FPR 1 "general_operand" "f,<Rf>")))] |
"s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
c<xde>r\t%0,%1 |
c<xde>\t%0,%1" |
[(set_attr "op_type" "<RRe>,<RXe>") |
(set_attr "type" "fsimp<mode>")]) |
|
|
;; |
;;- Move instructions. |
;; |
|
; |
; movti instruction pattern(s). |
; |
|
(define_insn "movti" |
[(set (match_operand:TI 0 "nonimmediate_operand" "=d,QS,d,o,Q") |
(match_operand:TI 1 "general_operand" "QS,d,dPm,d,Q"))] |
"TARGET_64BIT" |
"@ |
lmg\t%0,%N0,%S1 |
stmg\t%1,%N1,%S0 |
# |
# |
#" |
[(set_attr "op_type" "RSY,RSY,*,*,SS") |
(set_attr "type" "lm,stm,*,*,*")]) |
|
(define_split |
[(set (match_operand:TI 0 "nonimmediate_operand" "") |
(match_operand:TI 1 "general_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], TImode, 0)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 0, 0, TImode); |
operands[3] = operand_subword (operands[0], 1, 0, TImode); |
operands[4] = operand_subword (operands[1], 0, 0, TImode); |
operands[5] = operand_subword (operands[1], 1, 0, TImode); |
}) |
|
(define_split |
[(set (match_operand:TI 0 "nonimmediate_operand" "") |
(match_operand:TI 1 "general_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], TImode, 1)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 1, 0, TImode); |
operands[3] = operand_subword (operands[0], 0, 0, TImode); |
operands[4] = operand_subword (operands[1], 1, 0, TImode); |
operands[5] = operand_subword (operands[1], 0, 0, TImode); |
}) |
|
(define_split |
[(set (match_operand:TI 0 "register_operand" "") |
(match_operand:TI 1 "memory_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& !s_operand (operands[1], VOIDmode)" |
[(set (match_dup 0) (match_dup 1))] |
{ |
rtx addr = operand_subword (operands[0], 1, 0, TImode); |
s390_load_address (addr, XEXP (operands[1], 0)); |
operands[1] = replace_equiv_address (operands[1], addr); |
}) |
|
(define_expand "reload_outti" |
[(parallel [(match_operand:TI 0 "" "") |
(match_operand:TI 1 "register_operand" "d") |
(match_operand:DI 2 "register_operand" "=&a")])] |
"TARGET_64BIT" |
{ |
gcc_assert (MEM_P (operands[0])); |
s390_load_address (operands[2], find_replacement (&XEXP (operands[0], 0))); |
operands[0] = replace_equiv_address (operands[0], operands[2]); |
emit_move_insn (operands[0], operands[1]); |
DONE; |
}) |
|
; |
; movdi instruction pattern(s). |
; |
|
(define_expand "movdi" |
[(set (match_operand:DI 0 "general_operand" "") |
(match_operand:DI 1 "general_operand" ""))] |
"" |
{ |
/* Handle symbolic constants. */ |
if (TARGET_64BIT |
&& (SYMBOLIC_CONST (operands[1]) |
|| (GET_CODE (operands[1]) == PLUS |
&& XEXP (operands[1], 0) == pic_offset_table_rtx |
&& SYMBOLIC_CONST (XEXP (operands[1], 1))))) |
emit_symbolic_move (operands); |
}) |
|
(define_insn "*movdi_larl" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(match_operand:DI 1 "larl_operand" "X"))] |
"TARGET_64BIT |
&& !FP_REG_P (operands[0])" |
"larl\t%0,%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "larl")]) |
|
(define_insn "*movdi_64extimm" |
[(set (match_operand:DI 0 "nonimmediate_operand" |
"=d,d,d,d,d,d,d,d,d,d,d,m,!*f,!*f,!*f,!R,!T,d,t,Q,t,?Q") |
(match_operand:DI 1 "general_operand" |
"K,N0HD0,N1HD0,N2HD0,N3HD0,Os,N0SD0,N1SD0,L,d,m,d,*f,R,T,*f,*f,t,d,t,Q,?Q"))] |
"TARGET_64BIT && TARGET_EXTIMM" |
"@ |
lghi\t%0,%h1 |
llihh\t%0,%i1 |
llihl\t%0,%i1 |
llilh\t%0,%i1 |
llill\t%0,%i1 |
lgfi\t%0,%1 |
llihf\t%0,%k1 |
llilf\t%0,%k1 |
lay\t%0,%a1 |
lgr\t%0,%1 |
lg\t%0,%1 |
stg\t%1,%0 |
ldr\t%0,%1 |
ld\t%0,%1 |
ldy\t%0,%1 |
std\t%1,%0 |
stdy\t%1,%0 |
# |
# |
stam\t%1,%N1,%S0 |
lam\t%0,%N0,%S1 |
#" |
[(set_attr "op_type" "RI,RI,RI,RI,RI,RIL,RIL,RIL,RXY,RRE,RXY,RXY, |
RR,RX,RXY,RX,RXY,*,*,RS,RS,SS") |
(set_attr "type" "*,*,*,*,*,*,*,*,la,lr,load,store, |
floaddf,floaddf,floaddf,fstoredf,fstoredf,*,*,*,*,*")]) |
|
(define_insn "*movdi_64" |
[(set (match_operand:DI 0 "nonimmediate_operand" |
"=d,d,d,d,d,d,d,d,m,!*f,!*f,!*f,!R,!T,d,t,Q,t,?Q") |
(match_operand:DI 1 "general_operand" |
"K,N0HD0,N1HD0,N2HD0,N3HD0,L,d,m,d,*f,R,T,*f,*f,t,d,t,Q,?Q"))] |
"TARGET_64BIT && !TARGET_EXTIMM" |
"@ |
lghi\t%0,%h1 |
llihh\t%0,%i1 |
llihl\t%0,%i1 |
llilh\t%0,%i1 |
llill\t%0,%i1 |
lay\t%0,%a1 |
lgr\t%0,%1 |
lg\t%0,%1 |
stg\t%1,%0 |
ldr\t%0,%1 |
ld\t%0,%1 |
ldy\t%0,%1 |
std\t%1,%0 |
stdy\t%1,%0 |
# |
# |
stam\t%1,%N1,%S0 |
lam\t%0,%N0,%S1 |
#" |
[(set_attr "op_type" "RI,RI,RI,RI,RI,RXY,RRE,RXY,RXY, |
RR,RX,RXY,RX,RXY,*,*,RS,RS,SS") |
(set_attr "type" "*,*,*,*,*,la,lr,load,store, |
floaddf,floaddf,floaddf,fstoredf,fstoredf,*,*,*,*,*")]) |
|
(define_split |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "register_operand" ""))] |
"TARGET_64BIT && ACCESS_REG_P (operands[1])" |
[(set (match_dup 2) (match_dup 3)) |
(set (match_dup 0) (ashift:DI (match_dup 0) (const_int 32))) |
(set (strict_low_part (match_dup 2)) (match_dup 4))] |
"operands[2] = gen_lowpart (SImode, operands[0]); |
s390_split_access_reg (operands[1], &operands[4], &operands[3]);") |
|
(define_split |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "register_operand" ""))] |
"TARGET_64BIT && ACCESS_REG_P (operands[0]) |
&& dead_or_set_p (insn, operands[1])" |
[(set (match_dup 3) (match_dup 2)) |
(set (match_dup 1) (lshiftrt:DI (match_dup 1) (const_int 32))) |
(set (match_dup 4) (match_dup 2))] |
"operands[2] = gen_lowpart (SImode, operands[1]); |
s390_split_access_reg (operands[0], &operands[3], &operands[4]);") |
|
(define_split |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "register_operand" ""))] |
"TARGET_64BIT && ACCESS_REG_P (operands[0]) |
&& !dead_or_set_p (insn, operands[1])" |
[(set (match_dup 3) (match_dup 2)) |
(set (match_dup 1) (rotate:DI (match_dup 1) (const_int 32))) |
(set (match_dup 4) (match_dup 2)) |
(set (match_dup 1) (rotate:DI (match_dup 1) (const_int 32)))] |
"operands[2] = gen_lowpart (SImode, operands[1]); |
s390_split_access_reg (operands[0], &operands[3], &operands[4]);") |
|
(define_insn "*movdi_31" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,Q,S,d,o,!*f,!*f,!*f,!R,!T,Q") |
(match_operand:DI 1 "general_operand" "Q,S,d,d,dPm,d,*f,R,T,*f,*f,Q"))] |
"!TARGET_64BIT" |
"@ |
lm\t%0,%N0,%S1 |
lmy\t%0,%N0,%S1 |
stm\t%1,%N1,%S0 |
stmy\t%1,%N1,%S0 |
# |
# |
ldr\t%0,%1 |
ld\t%0,%1 |
ldy\t%0,%1 |
std\t%1,%0 |
stdy\t%1,%0 |
#" |
[(set_attr "op_type" "RS,RSY,RS,RSY,*,*,RR,RX,RXY,RX,RXY,SS") |
(set_attr "type" "lm,lm,stm,stm,*,*,floaddf,floaddf,floaddf,fstoredf,fstoredf,*")]) |
|
(define_split |
[(set (match_operand:DI 0 "nonimmediate_operand" "") |
(match_operand:DI 1 "general_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], DImode, 0)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 0, 0, DImode); |
operands[3] = operand_subword (operands[0], 1, 0, DImode); |
operands[4] = operand_subword (operands[1], 0, 0, DImode); |
operands[5] = operand_subword (operands[1], 1, 0, DImode); |
}) |
|
(define_split |
[(set (match_operand:DI 0 "nonimmediate_operand" "") |
(match_operand:DI 1 "general_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], DImode, 1)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 1, 0, DImode); |
operands[3] = operand_subword (operands[0], 0, 0, DImode); |
operands[4] = operand_subword (operands[1], 1, 0, DImode); |
operands[5] = operand_subword (operands[1], 0, 0, DImode); |
}) |
|
(define_split |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "memory_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& !FP_REG_P (operands[0]) |
&& !s_operand (operands[1], VOIDmode)" |
[(set (match_dup 0) (match_dup 1))] |
{ |
rtx addr = operand_subword (operands[0], 1, 0, DImode); |
s390_load_address (addr, XEXP (operands[1], 0)); |
operands[1] = replace_equiv_address (operands[1], addr); |
}) |
|
(define_expand "reload_outdi" |
[(parallel [(match_operand:DI 0 "" "") |
(match_operand:DI 1 "register_operand" "d") |
(match_operand:SI 2 "register_operand" "=&a")])] |
"!TARGET_64BIT" |
{ |
gcc_assert (MEM_P (operands[0])); |
s390_load_address (operands[2], find_replacement (&XEXP (operands[0], 0))); |
operands[0] = replace_equiv_address (operands[0], operands[2]); |
emit_move_insn (operands[0], operands[1]); |
DONE; |
}) |
|
(define_peephole2 |
[(set (match_operand:DI 0 "register_operand" "") |
(mem:DI (match_operand 1 "address_operand" "")))] |
"TARGET_64BIT |
&& !FP_REG_P (operands[0]) |
&& GET_CODE (operands[1]) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (operands[1]) |
&& get_pool_mode (operands[1]) == DImode |
&& legitimate_reload_constant_p (get_pool_constant (operands[1]))" |
[(set (match_dup 0) (match_dup 2))] |
"operands[2] = get_pool_constant (operands[1]);") |
|
(define_insn "*la_64" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(match_operand:QI 1 "address_operand" "U,W"))] |
"TARGET_64BIT" |
"@ |
la\t%0,%a1 |
lay\t%0,%a1" |
[(set_attr "op_type" "RX,RXY") |
(set_attr "type" "la")]) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:QI 1 "address_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_64BIT |
&& preferred_la_operand_p (operands[1], const0_rtx)" |
[(set (match_dup 0) (match_dup 1))] |
"") |
|
(define_peephole2 |
[(set (match_operand:DI 0 "register_operand" "") |
(match_operand:DI 1 "register_operand" "")) |
(parallel |
[(set (match_dup 0) |
(plus:DI (match_dup 0) |
(match_operand:DI 2 "nonmemory_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_64BIT |
&& !reg_overlap_mentioned_p (operands[0], operands[2]) |
&& preferred_la_operand_p (operands[1], operands[2])" |
[(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))] |
"") |
|
(define_expand "reload_indi" |
[(parallel [(match_operand:DI 0 "register_operand" "=a") |
(match_operand:DI 1 "s390_plus_operand" "") |
(match_operand:DI 2 "register_operand" "=&a")])] |
"TARGET_64BIT" |
{ |
s390_expand_plus_operand (operands[0], operands[1], operands[2]); |
DONE; |
}) |
|
; |
; movsi instruction pattern(s). |
; |
|
(define_expand "movsi" |
[(set (match_operand:SI 0 "general_operand" "") |
(match_operand:SI 1 "general_operand" ""))] |
"" |
{ |
/* Handle symbolic constants. */ |
if (!TARGET_64BIT |
&& (SYMBOLIC_CONST (operands[1]) |
|| (GET_CODE (operands[1]) == PLUS |
&& XEXP (operands[1], 0) == pic_offset_table_rtx |
&& SYMBOLIC_CONST (XEXP(operands[1], 1))))) |
emit_symbolic_move (operands); |
}) |
|
(define_insn "*movsi_larl" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(match_operand:SI 1 "larl_operand" "X"))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH |
&& !FP_REG_P (operands[0])" |
"larl\t%0,%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "larl")]) |
|
(define_insn "*movsi_zarch" |
[(set (match_operand:SI 0 "nonimmediate_operand" |
"=d,d,d,d,d,d,d,d,R,T,!*f,!*f,!*f,!R,!T,d,t,Q,t,?Q") |
(match_operand:SI 1 "general_operand" |
"K,N0HS0,N1HS0,Os,L,d,R,T,d,d,*f,R,T,*f,*f,t,d,t,Q,?Q"))] |
"TARGET_ZARCH" |
"@ |
lhi\t%0,%h1 |
llilh\t%0,%i1 |
llill\t%0,%i1 |
iilf\t%0,%o1 |
lay\t%0,%a1 |
lr\t%0,%1 |
l\t%0,%1 |
ly\t%0,%1 |
st\t%1,%0 |
sty\t%1,%0 |
ler\t%0,%1 |
le\t%0,%1 |
ley\t%0,%1 |
ste\t%1,%0 |
stey\t%1,%0 |
ear\t%0,%1 |
sar\t%0,%1 |
stam\t%1,%1,%S0 |
lam\t%0,%0,%S1 |
#" |
[(set_attr "op_type" "RI,RI,RI,RIL,RXY,RR,RX,RXY,RX,RXY, |
RR,RX,RXY,RX,RXY,RRE,RRE,RS,RS,SS") |
(set_attr "type" "*,*,*,*,la,lr,load,load,store,store, |
floadsf,floadsf,floadsf,fstoresf,fstoresf,*,*,*,*,*")]) |
|
(define_insn "*movsi_esa" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,R,!*f,!*f,!R,d,t,Q,t,?Q") |
(match_operand:SI 1 "general_operand" "K,d,R,d,*f,R,*f,t,d,t,Q,?Q"))] |
"!TARGET_ZARCH" |
"@ |
lhi\t%0,%h1 |
lr\t%0,%1 |
l\t%0,%1 |
st\t%1,%0 |
ler\t%0,%1 |
le\t%0,%1 |
ste\t%1,%0 |
ear\t%0,%1 |
sar\t%0,%1 |
stam\t%1,%1,%S0 |
lam\t%0,%0,%S1 |
#" |
[(set_attr "op_type" "RI,RR,RX,RX,RR,RX,RX,RRE,RRE,RS,RS,SS") |
(set_attr "type" "*,lr,load,store,floadsf,floadsf,fstoresf,*,*,*,*,*")]) |
|
(define_peephole2 |
[(set (match_operand:SI 0 "register_operand" "") |
(mem:SI (match_operand 1 "address_operand" "")))] |
"!FP_REG_P (operands[0]) |
&& GET_CODE (operands[1]) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (operands[1]) |
&& get_pool_mode (operands[1]) == SImode |
&& legitimate_reload_constant_p (get_pool_constant (operands[1]))" |
[(set (match_dup 0) (match_dup 2))] |
"operands[2] = get_pool_constant (operands[1]);") |
|
(define_insn "*la_31" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(match_operand:QI 1 "address_operand" "U,W"))] |
"!TARGET_64BIT && legitimate_la_operand_p (operands[1])" |
"@ |
la\t%0,%a1 |
lay\t%0,%a1" |
[(set_attr "op_type" "RX,RXY") |
(set_attr "type" "la")]) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:SI 0 "register_operand" "") |
(match_operand:QI 1 "address_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"!TARGET_64BIT |
&& preferred_la_operand_p (operands[1], const0_rtx)" |
[(set (match_dup 0) (match_dup 1))] |
"") |
|
(define_peephole2 |
[(set (match_operand:SI 0 "register_operand" "") |
(match_operand:SI 1 "register_operand" "")) |
(parallel |
[(set (match_dup 0) |
(plus:SI (match_dup 0) |
(match_operand:SI 2 "nonmemory_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"!TARGET_64BIT |
&& !reg_overlap_mentioned_p (operands[0], operands[2]) |
&& preferred_la_operand_p (operands[1], operands[2])" |
[(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))] |
"") |
|
(define_insn "*la_31_and" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(and:SI (match_operand:QI 1 "address_operand" "U,W") |
(const_int 2147483647)))] |
"!TARGET_64BIT" |
"@ |
la\t%0,%a1 |
lay\t%0,%a1" |
[(set_attr "op_type" "RX,RXY") |
(set_attr "type" "la")]) |
|
(define_insn_and_split "*la_31_and_cc" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(and:SI (match_operand:QI 1 "address_operand" "p") |
(const_int 2147483647))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) |
(and:SI (match_dup 1) (const_int 2147483647)))] |
"" |
[(set_attr "op_type" "RX") |
(set_attr "type" "la")]) |
|
(define_insn "force_la_31" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(match_operand:QI 1 "address_operand" "U,W")) |
(use (const_int 0))] |
"!TARGET_64BIT" |
"@ |
la\t%0,%a1 |
lay\t%0,%a1" |
[(set_attr "op_type" "RX") |
(set_attr "type" "la")]) |
|
(define_expand "reload_insi" |
[(parallel [(match_operand:SI 0 "register_operand" "=a") |
(match_operand:SI 1 "s390_plus_operand" "") |
(match_operand:SI 2 "register_operand" "=&a")])] |
"!TARGET_64BIT" |
{ |
s390_expand_plus_operand (operands[0], operands[1], operands[2]); |
DONE; |
}) |
|
; |
; movhi instruction pattern(s). |
; |
|
(define_expand "movhi" |
[(set (match_operand:HI 0 "nonimmediate_operand" "") |
(match_operand:HI 1 "general_operand" ""))] |
"" |
{ |
/* Make it explicit that loading a register from memory |
always sign-extends (at least) to SImode. */ |
if (optimize && !no_new_pseudos |
&& register_operand (operands[0], VOIDmode) |
&& GET_CODE (operands[1]) == MEM) |
{ |
rtx tmp = gen_reg_rtx (SImode); |
rtx ext = gen_rtx_SIGN_EXTEND (SImode, operands[1]); |
emit_insn (gen_rtx_SET (VOIDmode, tmp, ext)); |
operands[1] = gen_lowpart (HImode, tmp); |
} |
}) |
|
(define_insn "*movhi" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,T,?Q") |
(match_operand:HI 1 "general_operand" "d,n,R,T,d,d,?Q"))] |
"" |
"@ |
lr\t%0,%1 |
lhi\t%0,%h1 |
lh\t%0,%1 |
lhy\t%0,%1 |
sth\t%1,%0 |
sthy\t%1,%0 |
#" |
[(set_attr "op_type" "RR,RI,RX,RXY,RX,RXY,SS") |
(set_attr "type" "lr,*,*,*,store,store,*")]) |
|
(define_peephole2 |
[(set (match_operand:HI 0 "register_operand" "") |
(mem:HI (match_operand 1 "address_operand" "")))] |
"GET_CODE (operands[1]) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (operands[1]) |
&& get_pool_mode (operands[1]) == HImode |
&& GET_CODE (get_pool_constant (operands[1])) == CONST_INT" |
[(set (match_dup 0) (match_dup 2))] |
"operands[2] = get_pool_constant (operands[1]);") |
|
; |
; movqi instruction pattern(s). |
; |
|
(define_expand "movqi" |
[(set (match_operand:QI 0 "nonimmediate_operand" "") |
(match_operand:QI 1 "general_operand" ""))] |
"" |
{ |
/* On z/Architecture, zero-extending from memory to register |
is just as fast as a QImode load. */ |
if (TARGET_ZARCH && optimize && !no_new_pseudos |
&& register_operand (operands[0], VOIDmode) |
&& GET_CODE (operands[1]) == MEM) |
{ |
rtx tmp = gen_reg_rtx (word_mode); |
rtx ext = gen_rtx_ZERO_EXTEND (word_mode, operands[1]); |
emit_insn (gen_rtx_SET (VOIDmode, tmp, ext)); |
operands[1] = gen_lowpart (QImode, tmp); |
} |
}) |
|
(define_insn "*movqi" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,T,Q,S,?Q") |
(match_operand:QI 1 "general_operand" "d,n,R,T,d,d,n,n,?Q"))] |
"" |
"@ |
lr\t%0,%1 |
lhi\t%0,%b1 |
ic\t%0,%1 |
icy\t%0,%1 |
stc\t%1,%0 |
stcy\t%1,%0 |
mvi\t%S0,%b1 |
mviy\t%S0,%b1 |
#" |
[(set_attr "op_type" "RR,RI,RX,RXY,RX,RXY,SI,SIY,SS") |
(set_attr "type" "lr,*,*,*,store,store,store,store,*")]) |
|
(define_peephole2 |
[(set (match_operand:QI 0 "nonimmediate_operand" "") |
(mem:QI (match_operand 1 "address_operand" "")))] |
"GET_CODE (operands[1]) == SYMBOL_REF |
&& CONSTANT_POOL_ADDRESS_P (operands[1]) |
&& get_pool_mode (operands[1]) == QImode |
&& GET_CODE (get_pool_constant (operands[1])) == CONST_INT" |
[(set (match_dup 0) (match_dup 2))] |
"operands[2] = get_pool_constant (operands[1]);") |
|
; |
; movstrictqi instruction pattern(s). |
; |
|
(define_insn "*movstrictqi" |
[(set (strict_low_part (match_operand:QI 0 "register_operand" "+d,d")) |
(match_operand:QI 1 "memory_operand" "R,T"))] |
"" |
"@ |
ic\t%0,%1 |
icy\t%0,%1" |
[(set_attr "op_type" "RX,RXY")]) |
|
; |
; movstricthi instruction pattern(s). |
; |
|
(define_insn "*movstricthi" |
[(set (strict_low_part (match_operand:HI 0 "register_operand" "+d,d")) |
(match_operand:HI 1 "memory_operand" "Q,S")) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
icm\t%0,3,%S1 |
icmy\t%0,3,%S1" |
[(set_attr "op_type" "RS,RSY")]) |
|
; |
; movstrictsi instruction pattern(s). |
; |
|
(define_insn "movstrictsi" |
[(set (strict_low_part (match_operand:SI 0 "register_operand" "+d,d,d,d")) |
(match_operand:SI 1 "general_operand" "d,R,T,t"))] |
"TARGET_64BIT" |
"@ |
lr\t%0,%1 |
l\t%0,%1 |
ly\t%0,%1 |
ear\t%0,%1" |
[(set_attr "op_type" "RR,RX,RXY,RRE") |
(set_attr "type" "lr,load,load,*")]) |
|
; |
; movtf instruction pattern(s). |
; |
|
(define_expand "movtf" |
[(set (match_operand:TF 0 "nonimmediate_operand" "") |
(match_operand:TF 1 "general_operand" ""))] |
"" |
"") |
|
(define_insn "*movtf_64" |
[(set (match_operand:TF 0 "nonimmediate_operand" "=f,f,f,o,d,QS,d,o,Q") |
(match_operand:TF 1 "general_operand" "G,f,o,f,QS,d,dm,d,Q"))] |
"TARGET_64BIT" |
"@ |
lzxr\t%0 |
lxr\t%0,%1 |
# |
# |
lmg\t%0,%N0,%S1 |
stmg\t%1,%N1,%S0 |
# |
# |
#" |
[(set_attr "op_type" "RRE,RRE,*,*,RSY,RSY,*,*,*") |
(set_attr "type" "fsimptf,fsimptf,*,*,lm,stm,*,*,*")]) |
|
(define_insn "*movtf_31" |
[(set (match_operand:TF 0 "nonimmediate_operand" "=f,f,f,o,Q") |
(match_operand:TF 1 "general_operand" "G,f,o,f,Q"))] |
"!TARGET_64BIT" |
"@ |
lzxr\t%0 |
lxr\t%0,%1 |
# |
# |
#" |
[(set_attr "op_type" "RRE,RRE,*,*,*") |
(set_attr "type" "fsimptf,fsimptf,*,*,*")]) |
|
; TFmode in GPRs splitters |
|
(define_split |
[(set (match_operand:TF 0 "nonimmediate_operand" "") |
(match_operand:TF 1 "general_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], TFmode, 0)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 0, 0, TFmode); |
operands[3] = operand_subword (operands[0], 1, 0, TFmode); |
operands[4] = operand_subword (operands[1], 0, 0, TFmode); |
operands[5] = operand_subword (operands[1], 1, 0, TFmode); |
}) |
|
(define_split |
[(set (match_operand:TF 0 "nonimmediate_operand" "") |
(match_operand:TF 1 "general_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], TFmode, 1)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 1, 0, TFmode); |
operands[3] = operand_subword (operands[0], 0, 0, TFmode); |
operands[4] = operand_subword (operands[1], 1, 0, TFmode); |
operands[5] = operand_subword (operands[1], 0, 0, TFmode); |
}) |
|
(define_split |
[(set (match_operand:TF 0 "register_operand" "") |
(match_operand:TF 1 "memory_operand" ""))] |
"TARGET_64BIT && reload_completed |
&& !FP_REG_P (operands[0]) |
&& !s_operand (operands[1], VOIDmode)" |
[(set (match_dup 0) (match_dup 1))] |
{ |
rtx addr = operand_subword (operands[0], 1, 0, TFmode); |
s390_load_address (addr, XEXP (operands[1], 0)); |
operands[1] = replace_equiv_address (operands[1], addr); |
}) |
|
; TFmode in FPRs splitters |
|
(define_split |
[(set (match_operand:TF 0 "register_operand" "") |
(match_operand:TF 1 "memory_operand" ""))] |
"reload_completed && offsettable_memref_p (operands[1]) |
&& FP_REG_P (operands[0])" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = simplify_gen_subreg (DFmode, operands[0], TFmode, 0); |
operands[3] = simplify_gen_subreg (DFmode, operands[0], TFmode, 8); |
operands[4] = adjust_address_nv (operands[1], DFmode, 0); |
operands[5] = adjust_address_nv (operands[1], DFmode, 8); |
}) |
|
(define_split |
[(set (match_operand:TF 0 "memory_operand" "") |
(match_operand:TF 1 "register_operand" ""))] |
"reload_completed && offsettable_memref_p (operands[0]) |
&& FP_REG_P (operands[1])" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = adjust_address_nv (operands[0], DFmode, 0); |
operands[3] = adjust_address_nv (operands[0], DFmode, 8); |
operands[4] = simplify_gen_subreg (DFmode, operands[1], TFmode, 0); |
operands[5] = simplify_gen_subreg (DFmode, operands[1], TFmode, 8); |
}) |
|
(define_expand "reload_outtf" |
[(parallel [(match_operand:TF 0 "" "") |
(match_operand:TF 1 "register_operand" "f") |
(match_operand:SI 2 "register_operand" "=&a")])] |
"" |
{ |
rtx addr = gen_lowpart (Pmode, operands[2]); |
|
gcc_assert (MEM_P (operands[0])); |
s390_load_address (addr, find_replacement (&XEXP (operands[0], 0))); |
operands[0] = replace_equiv_address (operands[0], addr); |
emit_move_insn (operands[0], operands[1]); |
DONE; |
}) |
|
(define_expand "reload_intf" |
[(parallel [(match_operand:TF 0 "register_operand" "=f") |
(match_operand:TF 1 "" "") |
(match_operand:SI 2 "register_operand" "=&a")])] |
"" |
{ |
rtx addr = gen_lowpart (Pmode, operands[2]); |
|
gcc_assert (MEM_P (operands[1])); |
s390_load_address (addr, find_replacement (&XEXP (operands[1], 0))); |
operands[1] = replace_equiv_address (operands[1], addr); |
emit_move_insn (operands[0], operands[1]); |
DONE; |
}) |
|
; |
; movdf instruction pattern(s). |
; |
|
(define_expand "movdf" |
[(set (match_operand:DF 0 "nonimmediate_operand" "") |
(match_operand:DF 1 "general_operand" ""))] |
"" |
"") |
|
(define_insn "*movdf_64" |
[(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,f,R,T,d,d,m,?Q") |
(match_operand:DF 1 "general_operand" "G,f,R,T,f,f,d,m,d,?Q"))] |
"TARGET_64BIT" |
"@ |
lzdr\t%0 |
ldr\t%0,%1 |
ld\t%0,%1 |
ldy\t%0,%1 |
std\t%1,%0 |
stdy\t%1,%0 |
lgr\t%0,%1 |
lg\t%0,%1 |
stg\t%1,%0 |
#" |
[(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RRE,RXY,RXY,SS") |
(set_attr "type" "fsimpdf,floaddf,floaddf,floaddf,fstoredf,fstoredf,lr,load,store,*")]) |
|
(define_insn "*movdf_31" |
[(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,f,R,T,d,d,Q,S,d,o,Q") |
(match_operand:DF 1 "general_operand" "G,f,R,T,f,f,Q,S,d,d,dPm,d,Q"))] |
"!TARGET_64BIT" |
"@ |
lzdr\t%0 |
ldr\t%0,%1 |
ld\t%0,%1 |
ldy\t%0,%1 |
std\t%1,%0 |
stdy\t%1,%0 |
lm\t%0,%N0,%S1 |
lmy\t%0,%N0,%S1 |
stm\t%1,%N1,%S0 |
stmy\t%1,%N1,%S0 |
# |
# |
#" |
[(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RS,RSY,RS,RSY,*,*,SS") |
(set_attr "type" "fsimpdf,floaddf,floaddf,floaddf,fstoredf,fstoredf,\ |
lm,lm,stm,stm,*,*,*")]) |
|
(define_split |
[(set (match_operand:DF 0 "nonimmediate_operand" "") |
(match_operand:DF 1 "general_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], DFmode, 0)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 0, 0, DFmode); |
operands[3] = operand_subword (operands[0], 1, 0, DFmode); |
operands[4] = operand_subword (operands[1], 0, 0, DFmode); |
operands[5] = operand_subword (operands[1], 1, 0, DFmode); |
}) |
|
(define_split |
[(set (match_operand:DF 0 "nonimmediate_operand" "") |
(match_operand:DF 1 "general_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& s390_split_ok_p (operands[0], operands[1], DFmode, 1)" |
[(set (match_dup 2) (match_dup 4)) |
(set (match_dup 3) (match_dup 5))] |
{ |
operands[2] = operand_subword (operands[0], 1, 0, DFmode); |
operands[3] = operand_subword (operands[0], 0, 0, DFmode); |
operands[4] = operand_subword (operands[1], 1, 0, DFmode); |
operands[5] = operand_subword (operands[1], 0, 0, DFmode); |
}) |
|
(define_split |
[(set (match_operand:DF 0 "register_operand" "") |
(match_operand:DF 1 "memory_operand" ""))] |
"!TARGET_64BIT && reload_completed |
&& !FP_REG_P (operands[0]) |
&& !s_operand (operands[1], VOIDmode)" |
[(set (match_dup 0) (match_dup 1))] |
{ |
rtx addr = operand_subword (operands[0], 1, 0, DFmode); |
s390_load_address (addr, XEXP (operands[1], 0)); |
operands[1] = replace_equiv_address (operands[1], addr); |
}) |
|
(define_expand "reload_outdf" |
[(parallel [(match_operand:DF 0 "" "") |
(match_operand:DF 1 "register_operand" "d") |
(match_operand:SI 2 "register_operand" "=&a")])] |
"!TARGET_64BIT" |
{ |
gcc_assert (MEM_P (operands[0])); |
s390_load_address (operands[2], find_replacement (&XEXP (operands[0], 0))); |
operands[0] = replace_equiv_address (operands[0], operands[2]); |
emit_move_insn (operands[0], operands[1]); |
DONE; |
}) |
|
; |
; movsf instruction pattern(s). |
; |
|
(define_insn "movsf" |
[(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,f,R,T,d,d,d,R,T,?Q") |
(match_operand:SF 1 "general_operand" "G,f,R,T,f,f,d,R,T,d,d,?Q"))] |
"" |
"@ |
lzer\t%0 |
ler\t%0,%1 |
le\t%0,%1 |
ley\t%0,%1 |
ste\t%1,%0 |
stey\t%1,%0 |
lr\t%0,%1 |
l\t%0,%1 |
ly\t%0,%1 |
st\t%1,%0 |
sty\t%1,%0 |
#" |
[(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RR,RX,RXY,RX,RXY,SS") |
(set_attr "type" "fsimpsf,floadsf,floadsf,floadsf,fstoresf,fstoresf, |
lr,load,load,store,store,*")]) |
|
; |
; movcc instruction pattern |
; |
|
(define_insn "movcc" |
[(set (match_operand:CC 0 "nonimmediate_operand" "=d,c,d,d,d,R,T") |
(match_operand:CC 1 "nonimmediate_operand" "d,d,c,R,T,d,d"))] |
"" |
"@ |
lr\t%0,%1 |
tmh\t%1,12288 |
ipm\t%0 |
st\t%0,%1 |
sty\t%0,%1 |
l\t%1,%0 |
ly\t%1,%0" |
[(set_attr "op_type" "RR,RI,RRE,RX,RXY,RX,RXY") |
(set_attr "type" "lr,*,*,store,store,load,load")]) |
|
; |
; Block move (MVC) patterns. |
; |
|
(define_insn "*mvc" |
[(set (match_operand:BLK 0 "memory_operand" "=Q") |
(match_operand:BLK 1 "memory_operand" "Q")) |
(use (match_operand 2 "const_int_operand" "n"))] |
"INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" |
"mvc\t%O0(%2,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_split |
[(set (match_operand 0 "memory_operand" "") |
(match_operand 1 "memory_operand" ""))] |
"reload_completed |
&& GET_MODE (operands[0]) == GET_MODE (operands[1]) |
&& GET_MODE_SIZE (GET_MODE (operands[0])) > 0" |
[(parallel |
[(set (match_dup 0) (match_dup 1)) |
(use (match_dup 2))])] |
{ |
operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); |
operands[0] = adjust_address (operands[0], BLKmode, 0); |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
}) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "const_int_operand" ""))]) |
(parallel |
[(set (match_operand:BLK 3 "memory_operand" "") |
(match_operand:BLK 4 "memory_operand" "")) |
(use (match_operand 5 "const_int_operand" ""))])] |
"s390_offset_p (operands[0], operands[3], operands[2]) |
&& s390_offset_p (operands[1], operands[4], operands[2]) |
&& !s390_overlap_p (operands[0], operands[1], |
INTVAL (operands[2]) + INTVAL (operands[5])) |
&& INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" |
[(parallel |
[(set (match_dup 6) (match_dup 7)) |
(use (match_dup 8))])] |
"operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); |
operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); |
operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") |
|
|
; |
; load_multiple pattern(s). |
; |
; ??? Due to reload problems with replacing registers inside match_parallel |
; we currently support load_multiple/store_multiple only after reload. |
; |
|
(define_expand "load_multiple" |
[(match_par_dup 3 [(set (match_operand 0 "" "") |
(match_operand 1 "" "")) |
(use (match_operand 2 "" ""))])] |
"reload_completed" |
{ |
enum machine_mode mode; |
int regno; |
int count; |
rtx from; |
int i, off; |
|
/* Support only loading a constant number of fixed-point registers from |
memory and only bother with this if more than two */ |
if (GET_CODE (operands[2]) != CONST_INT |
|| INTVAL (operands[2]) < 2 |
|| INTVAL (operands[2]) > 16 |
|| GET_CODE (operands[1]) != MEM |
|| GET_CODE (operands[0]) != REG |
|| REGNO (operands[0]) >= 16) |
FAIL; |
|
count = INTVAL (operands[2]); |
regno = REGNO (operands[0]); |
mode = GET_MODE (operands[0]); |
if (mode != SImode && mode != word_mode) |
FAIL; |
|
operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); |
if (no_new_pseudos) |
{ |
if (GET_CODE (XEXP (operands[1], 0)) == REG) |
{ |
from = XEXP (operands[1], 0); |
off = 0; |
} |
else if (GET_CODE (XEXP (operands[1], 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == REG |
&& GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT) |
{ |
from = XEXP (XEXP (operands[1], 0), 0); |
off = INTVAL (XEXP (XEXP (operands[1], 0), 1)); |
} |
else |
FAIL; |
} |
else |
{ |
from = force_reg (Pmode, XEXP (operands[1], 0)); |
off = 0; |
} |
|
for (i = 0; i < count; i++) |
XVECEXP (operands[3], 0, i) |
= gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, regno + i), |
change_address (operands[1], mode, |
plus_constant (from, off + i * GET_MODE_SIZE (mode)))); |
}) |
|
(define_insn "*load_multiple_di" |
[(match_parallel 0 "load_multiple_operation" |
[(set (match_operand:DI 1 "register_operand" "=r") |
(match_operand:DI 2 "s_operand" "QS"))])] |
"reload_completed && word_mode == DImode" |
{ |
int words = XVECLEN (operands[0], 0); |
operands[0] = gen_rtx_REG (DImode, REGNO (operands[1]) + words - 1); |
return "lmg\t%1,%0,%S2"; |
} |
[(set_attr "op_type" "RSY") |
(set_attr "type" "lm")]) |
|
(define_insn "*load_multiple_si" |
[(match_parallel 0 "load_multiple_operation" |
[(set (match_operand:SI 1 "register_operand" "=r,r") |
(match_operand:SI 2 "s_operand" "Q,S"))])] |
"reload_completed" |
{ |
int words = XVECLEN (operands[0], 0); |
operands[0] = gen_rtx_REG (SImode, REGNO (operands[1]) + words - 1); |
return which_alternative == 0 ? "lm\t%1,%0,%S2" : "lmy\t%1,%0,%S2"; |
} |
[(set_attr "op_type" "RS,RSY") |
(set_attr "type" "lm")]) |
|
; |
; store multiple pattern(s). |
; |
|
(define_expand "store_multiple" |
[(match_par_dup 3 [(set (match_operand 0 "" "") |
(match_operand 1 "" "")) |
(use (match_operand 2 "" ""))])] |
"reload_completed" |
{ |
enum machine_mode mode; |
int regno; |
int count; |
rtx to; |
int i, off; |
|
/* Support only storing a constant number of fixed-point registers to |
memory and only bother with this if more than two. */ |
if (GET_CODE (operands[2]) != CONST_INT |
|| INTVAL (operands[2]) < 2 |
|| INTVAL (operands[2]) > 16 |
|| GET_CODE (operands[0]) != MEM |
|| GET_CODE (operands[1]) != REG |
|| REGNO (operands[1]) >= 16) |
FAIL; |
|
count = INTVAL (operands[2]); |
regno = REGNO (operands[1]); |
mode = GET_MODE (operands[1]); |
if (mode != SImode && mode != word_mode) |
FAIL; |
|
operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); |
|
if (no_new_pseudos) |
{ |
if (GET_CODE (XEXP (operands[0], 0)) == REG) |
{ |
to = XEXP (operands[0], 0); |
off = 0; |
} |
else if (GET_CODE (XEXP (operands[0], 0)) == PLUS |
&& GET_CODE (XEXP (XEXP (operands[0], 0), 0)) == REG |
&& GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT) |
{ |
to = XEXP (XEXP (operands[0], 0), 0); |
off = INTVAL (XEXP (XEXP (operands[0], 0), 1)); |
} |
else |
FAIL; |
} |
else |
{ |
to = force_reg (Pmode, XEXP (operands[0], 0)); |
off = 0; |
} |
|
for (i = 0; i < count; i++) |
XVECEXP (operands[3], 0, i) |
= gen_rtx_SET (VOIDmode, |
change_address (operands[0], mode, |
plus_constant (to, off + i * GET_MODE_SIZE (mode))), |
gen_rtx_REG (mode, regno + i)); |
}) |
|
(define_insn "*store_multiple_di" |
[(match_parallel 0 "store_multiple_operation" |
[(set (match_operand:DI 1 "s_operand" "=QS") |
(match_operand:DI 2 "register_operand" "r"))])] |
"reload_completed && word_mode == DImode" |
{ |
int words = XVECLEN (operands[0], 0); |
operands[0] = gen_rtx_REG (DImode, REGNO (operands[2]) + words - 1); |
return "stmg\t%2,%0,%S1"; |
} |
[(set_attr "op_type" "RSY") |
(set_attr "type" "stm")]) |
|
|
(define_insn "*store_multiple_si" |
[(match_parallel 0 "store_multiple_operation" |
[(set (match_operand:SI 1 "s_operand" "=Q,S") |
(match_operand:SI 2 "register_operand" "r,r"))])] |
"reload_completed" |
{ |
int words = XVECLEN (operands[0], 0); |
operands[0] = gen_rtx_REG (SImode, REGNO (operands[2]) + words - 1); |
return which_alternative == 0 ? "stm\t%2,%0,%S1" : "stmy\t%2,%0,%S1"; |
} |
[(set_attr "op_type" "RS,RSY") |
(set_attr "type" "stm")]) |
|
;; |
;; String instructions. |
;; |
|
(define_insn "*execute" |
[(match_parallel 0 "" |
[(unspec [(match_operand 1 "register_operand" "a") |
(match_operand:BLK 2 "memory_operand" "R") |
(match_operand 3 "" "")] UNSPEC_EXECUTE)])] |
"GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT |
&& GET_MODE_SIZE (GET_MODE (operands[1])) <= UNITS_PER_WORD" |
"ex\t%1,%2" |
[(set_attr "op_type" "RX") |
(set_attr "type" "cs")]) |
|
|
; |
; strlenM instruction pattern(s). |
; |
|
(define_expand "strlen<mode>" |
[(set (reg:SI 0) (match_operand:SI 2 "immediate_operand" "")) |
(parallel |
[(set (match_dup 4) |
(unspec:P [(const_int 0) |
(match_operand:BLK 1 "memory_operand" "") |
(reg:SI 0) |
(match_operand 3 "immediate_operand" "")] UNSPEC_SRST)) |
(clobber (scratch:P)) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_operand:P 0 "register_operand" "") |
(minus:P (match_dup 4) (match_dup 5))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
operands[4] = gen_reg_rtx (Pmode); |
operands[5] = gen_reg_rtx (Pmode); |
emit_move_insn (operands[5], force_operand (XEXP (operands[1], 0), NULL_RTX)); |
operands[1] = replace_equiv_address (operands[1], operands[5]); |
}) |
|
(define_insn "*strlen<mode>" |
[(set (match_operand:P 0 "register_operand" "=a") |
(unspec:P [(match_operand:P 2 "general_operand" "0") |
(mem:BLK (match_operand:P 3 "register_operand" "1")) |
(reg:SI 0) |
(match_operand 4 "immediate_operand" "")] UNSPEC_SRST)) |
(clobber (match_scratch:P 1 "=a")) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"srst\t%0,%1\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
; |
; cmpstrM instruction pattern(s). |
; |
|
(define_expand "cmpstrsi" |
[(set (reg:SI 0) (const_int 0)) |
(parallel |
[(clobber (match_operand 3 "" "")) |
(clobber (match_dup 4)) |
(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 1 "memory_operand" "") |
(match_operand:BLK 2 "memory_operand" ""))) |
(use (reg:SI 0))]) |
(parallel |
[(set (match_operand:SI 0 "register_operand" "=d") |
(unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_CMPINT)) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
/* As the result of CMPINT is inverted compared to what we need, |
we have to swap the operands. */ |
rtx op1 = operands[2]; |
rtx op2 = operands[1]; |
rtx addr1 = gen_reg_rtx (Pmode); |
rtx addr2 = gen_reg_rtx (Pmode); |
|
emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); |
emit_move_insn (addr2, force_operand (XEXP (op2, 0), NULL_RTX)); |
operands[1] = replace_equiv_address_nv (op1, addr1); |
operands[2] = replace_equiv_address_nv (op2, addr2); |
operands[3] = addr1; |
operands[4] = addr2; |
}) |
|
(define_insn "*cmpstr<mode>" |
[(clobber (match_operand:P 0 "register_operand" "=d")) |
(clobber (match_operand:P 1 "register_operand" "=d")) |
(set (reg:CCU CC_REGNUM) |
(compare:CCU (mem:BLK (match_operand:P 2 "register_operand" "0")) |
(mem:BLK (match_operand:P 3 "register_operand" "1")))) |
(use (reg:SI 0))] |
"" |
"clst\t%0,%1\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
; |
; movstr instruction pattern. |
; |
|
(define_expand "movstr" |
[(set (reg:SI 0) (const_int 0)) |
(parallel |
[(clobber (match_dup 3)) |
(set (match_operand:BLK 1 "memory_operand" "") |
(match_operand:BLK 2 "memory_operand" "")) |
(set (match_operand 0 "register_operand" "") |
(unspec [(match_dup 1) |
(match_dup 2) |
(reg:SI 0)] UNSPEC_MVST)) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
rtx addr1 = gen_reg_rtx (Pmode); |
rtx addr2 = gen_reg_rtx (Pmode); |
|
emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); |
emit_move_insn (addr2, force_operand (XEXP (operands[2], 0), NULL_RTX)); |
operands[1] = replace_equiv_address_nv (operands[1], addr1); |
operands[2] = replace_equiv_address_nv (operands[2], addr2); |
operands[3] = addr2; |
}) |
|
(define_insn "*movstr" |
[(clobber (match_operand:P 2 "register_operand" "=d")) |
(set (mem:BLK (match_operand:P 1 "register_operand" "0")) |
(mem:BLK (match_operand:P 3 "register_operand" "2"))) |
(set (match_operand:P 0 "register_operand" "=d") |
(unspec [(mem:BLK (match_dup 1)) |
(mem:BLK (match_dup 3)) |
(reg:SI 0)] UNSPEC_MVST)) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"mvst\t%1,%2\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
|
; |
; movmemM instruction pattern(s). |
; |
|
(define_expand "movmem<mode>" |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand:GPR 2 "general_operand" "")) |
(match_operand 3 "" "")] |
"" |
"s390_expand_movmem (operands[0], operands[1], operands[2]); DONE;") |
|
; Move a block that is up to 256 bytes in length. |
; The block length is taken as (operands[2] % 256) + 1. |
|
(define_expand "movmem_short" |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "nonmemory_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_dup 3))])] |
"" |
"operands[3] = gen_rtx_SCRATCH (Pmode);") |
|
(define_insn "*movmem_short" |
[(set (match_operand:BLK 0 "memory_operand" "=Q,Q,Q") |
(match_operand:BLK 1 "memory_operand" "Q,Q,Q")) |
(use (match_operand 2 "nonmemory_operand" "n,a,a")) |
(use (match_operand 3 "immediate_operand" "X,R,X")) |
(clobber (match_scratch 4 "=X,X,&a"))] |
"(GET_MODE (operands[2]) == Pmode || GET_MODE (operands[2]) == VOIDmode) |
&& GET_MODE (operands[4]) == Pmode" |
"#" |
[(set_attr "type" "cs")]) |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "const_int_operand" "")) |
(use (match_operand 3 "immediate_operand" "")) |
(clobber (scratch))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (match_dup 1)) |
(use (match_dup 2))])] |
"operands[2] = GEN_INT ((INTVAL (operands[2]) & 0xff) + 1);") |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "register_operand" "")) |
(use (match_operand 3 "memory_operand" "")) |
(clobber (scratch))] |
"reload_completed" |
[(parallel |
[(unspec [(match_dup 2) (match_dup 3) |
(const_int 0)] UNSPEC_EXECUTE) |
(set (match_dup 0) (match_dup 1)) |
(use (const_int 1))])] |
"") |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "register_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_operand 3 "register_operand" ""))] |
"reload_completed && TARGET_CPU_ZARCH" |
[(set (match_dup 3) (label_ref (match_dup 4))) |
(parallel |
[(unspec [(match_dup 2) (mem:BLK (match_dup 3)) |
(label_ref (match_dup 4))] UNSPEC_EXECUTE) |
(set (match_dup 0) (match_dup 1)) |
(use (const_int 1))])] |
"operands[4] = gen_label_rtx ();") |
|
; Move a block of arbitrary length. |
|
(define_expand "movmem_long" |
[(parallel |
[(clobber (match_dup 2)) |
(clobber (match_dup 3)) |
(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" "")) |
(use (match_operand 2 "general_operand" "")) |
(use (match_dup 3)) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
enum machine_mode dword_mode = word_mode == DImode ? TImode : DImode; |
rtx reg0 = gen_reg_rtx (dword_mode); |
rtx reg1 = gen_reg_rtx (dword_mode); |
rtx addr0 = gen_lowpart (Pmode, gen_highpart (word_mode, reg0)); |
rtx addr1 = gen_lowpart (Pmode, gen_highpart (word_mode, reg1)); |
rtx len0 = gen_lowpart (Pmode, reg0); |
rtx len1 = gen_lowpart (Pmode, reg1); |
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, reg0)); |
emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); |
emit_move_insn (len0, operands[2]); |
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, reg1)); |
emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); |
emit_move_insn (len1, operands[2]); |
|
operands[0] = replace_equiv_address_nv (operands[0], addr0); |
operands[1] = replace_equiv_address_nv (operands[1], addr1); |
operands[2] = reg0; |
operands[3] = reg1; |
}) |
|
(define_insn "*movmem_long" |
[(clobber (match_operand:<DBL> 0 "register_operand" "=d")) |
(clobber (match_operand:<DBL> 1 "register_operand" "=d")) |
(set (mem:BLK (subreg:P (match_operand:<DBL> 2 "register_operand" "0") 0)) |
(mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "1") 0))) |
(use (match_dup 2)) |
(use (match_dup 3)) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"mvcle\t%0,%1,0\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
; |
; setmemM instruction pattern(s). |
; |
|
(define_expand "setmem<mode>" |
[(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand:QI 2 "general_operand" "")) |
(use (match_operand:GPR 1 "general_operand" "")) |
(match_operand 3 "" "")] |
"" |
"s390_expand_setmem (operands[0], operands[1], operands[2]); DONE;") |
|
; Clear a block that is up to 256 bytes in length. |
; The block length is taken as (operands[1] % 256) + 1. |
|
(define_expand "clrmem_short" |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 1 "nonmemory_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_dup 2)) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"operands[2] = gen_rtx_SCRATCH (Pmode);") |
|
(define_insn "*clrmem_short" |
[(set (match_operand:BLK 0 "memory_operand" "=Q,Q,Q") |
(const_int 0)) |
(use (match_operand 1 "nonmemory_operand" "n,a,a")) |
(use (match_operand 2 "immediate_operand" "X,R,X")) |
(clobber (match_scratch 3 "=X,X,&a")) |
(clobber (reg:CC CC_REGNUM))] |
"(GET_MODE (operands[1]) == Pmode || GET_MODE (operands[1]) == VOIDmode) |
&& GET_MODE (operands[3]) == Pmode" |
"#" |
[(set_attr "type" "cs")]) |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 1 "const_int_operand" "")) |
(use (match_operand 2 "immediate_operand" "")) |
(clobber (scratch)) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (const_int 0)) |
(use (match_dup 1)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[1] = GEN_INT ((INTVAL (operands[1]) & 0xff) + 1);") |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 1 "register_operand" "")) |
(use (match_operand 2 "memory_operand" "")) |
(clobber (scratch)) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(unspec [(match_dup 1) (match_dup 2) |
(const_int 0)] UNSPEC_EXECUTE) |
(set (match_dup 0) (const_int 0)) |
(use (const_int 1)) |
(clobber (reg:CC CC_REGNUM))])] |
"") |
|
(define_split |
[(set (match_operand:BLK 0 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 1 "register_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_operand 2 "register_operand" "")) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed && TARGET_CPU_ZARCH" |
[(set (match_dup 2) (label_ref (match_dup 3))) |
(parallel |
[(unspec [(match_dup 1) (mem:BLK (match_dup 2)) |
(label_ref (match_dup 3))] UNSPEC_EXECUTE) |
(set (match_dup 0) (const_int 0)) |
(use (const_int 1)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[3] = gen_label_rtx ();") |
|
; Initialize a block of arbitrary length with (operands[2] % 256). |
|
(define_expand "setmem_long" |
[(parallel |
[(clobber (match_dup 1)) |
(set (match_operand:BLK 0 "memory_operand" "") |
(match_operand 2 "shift_count_or_setmem_operand" "")) |
(use (match_operand 1 "general_operand" "")) |
(use (match_dup 3)) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
enum machine_mode dword_mode = word_mode == DImode ? TImode : DImode; |
rtx reg0 = gen_reg_rtx (dword_mode); |
rtx reg1 = gen_reg_rtx (dword_mode); |
rtx addr0 = gen_lowpart (Pmode, gen_highpart (word_mode, reg0)); |
rtx len0 = gen_lowpart (Pmode, reg0); |
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, reg0)); |
emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); |
emit_move_insn (len0, operands[1]); |
|
emit_move_insn (reg1, const0_rtx); |
|
operands[0] = replace_equiv_address_nv (operands[0], addr0); |
operands[1] = reg0; |
operands[3] = reg1; |
}) |
|
(define_insn "*setmem_long" |
[(clobber (match_operand:<DBL> 0 "register_operand" "=d")) |
(set (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "0") 0)) |
(match_operand 2 "shift_count_or_setmem_operand" "Y")) |
(use (match_dup 3)) |
(use (match_operand:<DBL> 1 "register_operand" "d")) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"mvcle\t%0,%1,%Y2\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
(define_insn "*setmem_long_and" |
[(clobber (match_operand:<DBL> 0 "register_operand" "=d")) |
(set (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "0") 0)) |
(and (match_operand 2 "shift_count_or_setmem_operand" "Y") |
(match_operand 4 "const_int_operand" "n"))) |
(use (match_dup 3)) |
(use (match_operand:<DBL> 1 "register_operand" "d")) |
(clobber (reg:CC CC_REGNUM))] |
"(INTVAL (operands[4]) & 255) == 255" |
"mvcle\t%0,%1,%Y2\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
; |
; cmpmemM instruction pattern(s). |
; |
|
(define_expand "cmpmemsi" |
[(set (match_operand:SI 0 "register_operand" "") |
(compare:SI (match_operand:BLK 1 "memory_operand" "") |
(match_operand:BLK 2 "memory_operand" "") ) ) |
(use (match_operand:SI 3 "general_operand" "")) |
(use (match_operand:SI 4 "" ""))] |
"" |
"s390_expand_cmpmem (operands[0], operands[1], |
operands[2], operands[3]); DONE;") |
|
; Compare a block that is up to 256 bytes in length. |
; The block length is taken as (operands[2] % 256) + 1. |
|
(define_expand "cmpmem_short" |
[(parallel |
[(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "nonmemory_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_dup 3))])] |
"" |
"operands[3] = gen_rtx_SCRATCH (Pmode);") |
|
(define_insn "*cmpmem_short" |
[(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "Q,Q,Q") |
(match_operand:BLK 1 "memory_operand" "Q,Q,Q"))) |
(use (match_operand 2 "nonmemory_operand" "n,a,a")) |
(use (match_operand 3 "immediate_operand" "X,R,X")) |
(clobber (match_scratch 4 "=X,X,&a"))] |
"(GET_MODE (operands[2]) == Pmode || GET_MODE (operands[2]) == VOIDmode) |
&& GET_MODE (operands[4]) == Pmode" |
"#" |
[(set_attr "type" "cs")]) |
|
(define_split |
[(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "const_int_operand" "")) |
(use (match_operand 3 "immediate_operand" "")) |
(clobber (scratch))] |
"reload_completed" |
[(parallel |
[(set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) |
(use (match_dup 2))])] |
"operands[2] = GEN_INT ((INTVAL (operands[2]) & 0xff) + 1);") |
|
(define_split |
[(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "register_operand" "")) |
(use (match_operand 3 "memory_operand" "")) |
(clobber (scratch))] |
"reload_completed" |
[(parallel |
[(unspec [(match_dup 2) (match_dup 3) |
(const_int 0)] UNSPEC_EXECUTE) |
(set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) |
(use (const_int 1))])] |
"") |
|
(define_split |
[(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "register_operand" "")) |
(use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) |
(clobber (match_operand 3 "register_operand" ""))] |
"reload_completed && TARGET_CPU_ZARCH" |
[(set (match_dup 3) (label_ref (match_dup 4))) |
(parallel |
[(unspec [(match_dup 2) (mem:BLK (match_dup 3)) |
(label_ref (match_dup 4))] UNSPEC_EXECUTE) |
(set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) |
(use (const_int 1))])] |
"operands[4] = gen_label_rtx ();") |
|
; Compare a block of arbitrary length. |
|
(define_expand "cmpmem_long" |
[(parallel |
[(clobber (match_dup 2)) |
(clobber (match_dup 3)) |
(set (reg:CCU CC_REGNUM) |
(compare:CCU (match_operand:BLK 0 "memory_operand" "") |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "general_operand" "")) |
(use (match_dup 3))])] |
"" |
{ |
enum machine_mode dword_mode = word_mode == DImode ? TImode : DImode; |
rtx reg0 = gen_reg_rtx (dword_mode); |
rtx reg1 = gen_reg_rtx (dword_mode); |
rtx addr0 = gen_lowpart (Pmode, gen_highpart (word_mode, reg0)); |
rtx addr1 = gen_lowpart (Pmode, gen_highpart (word_mode, reg1)); |
rtx len0 = gen_lowpart (Pmode, reg0); |
rtx len1 = gen_lowpart (Pmode, reg1); |
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, reg0)); |
emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); |
emit_move_insn (len0, operands[2]); |
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, reg1)); |
emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); |
emit_move_insn (len1, operands[2]); |
|
operands[0] = replace_equiv_address_nv (operands[0], addr0); |
operands[1] = replace_equiv_address_nv (operands[1], addr1); |
operands[2] = reg0; |
operands[3] = reg1; |
}) |
|
(define_insn "*cmpmem_long" |
[(clobber (match_operand:<DBL> 0 "register_operand" "=d")) |
(clobber (match_operand:<DBL> 1 "register_operand" "=d")) |
(set (reg:CCU CC_REGNUM) |
(compare:CCU (mem:BLK (subreg:P (match_operand:<DBL> 2 "register_operand" "0") 0)) |
(mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "1") 0)))) |
(use (match_dup 2)) |
(use (match_dup 3))] |
"" |
"clcle\t%0,%1,0\;jo\t.-4" |
[(set_attr "length" "8") |
(set_attr "type" "vs")]) |
|
; Convert CCUmode condition code to integer. |
; Result is zero if EQ, positive if LTU, negative if GTU. |
|
(define_insn_and_split "cmpint" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(unspec:SI [(match_operand:CCU 1 "register_operand" "0")] |
UNSPEC_CMPINT)) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"#" |
"reload_completed" |
[(set (match_dup 0) (ashift:SI (match_dup 0) (const_int 2))) |
(parallel |
[(set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 30))) |
(clobber (reg:CC CC_REGNUM))])]) |
|
(define_insn_and_split "*cmpint_cc" |
[(set (reg CC_REGNUM) |
(compare (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] |
UNSPEC_CMPINT) |
(const_int 0))) |
(set (match_operand:SI 0 "register_operand" "=d") |
(unspec:SI [(match_dup 1)] UNSPEC_CMPINT))] |
"s390_match_ccmode (insn, CCSmode)" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (ashift:SI (match_dup 0) (const_int 2))) |
(parallel |
[(set (match_dup 2) (match_dup 3)) |
(set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 30)))])] |
{ |
rtx result = gen_rtx_ASHIFTRT (SImode, operands[0], GEN_INT (30)); |
operands[2] = SET_DEST (XVECEXP (PATTERN (curr_insn), 0, 0)); |
operands[3] = gen_rtx_COMPARE (GET_MODE (operands[2]), result, const0_rtx); |
}) |
|
(define_insn_and_split "*cmpint_sign" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(sign_extend:DI (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] |
UNSPEC_CMPINT))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (ashift:DI (match_dup 0) (const_int 34))) |
(parallel |
[(set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 62))) |
(clobber (reg:CC CC_REGNUM))])]) |
|
(define_insn_and_split "*cmpint_sign_cc" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (ashift:DI (subreg:DI |
(unspec:SI [(match_operand:CCU 1 "register_operand" "0")] |
UNSPEC_CMPINT) 0) |
(const_int 32)) (const_int 32)) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(sign_extend:DI (unspec:SI [(match_dup 1)] UNSPEC_CMPINT)))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (ashift:DI (match_dup 0) (const_int 34))) |
(parallel |
[(set (match_dup 2) (match_dup 3)) |
(set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 62)))])] |
{ |
rtx result = gen_rtx_ASHIFTRT (DImode, operands[0], GEN_INT (62)); |
operands[2] = SET_DEST (XVECEXP (PATTERN (curr_insn), 0, 0)); |
operands[3] = gen_rtx_COMPARE (GET_MODE (operands[2]), result, const0_rtx); |
}) |
|
|
;; |
;;- Conversion instructions. |
;; |
|
(define_insn "*sethighpartsi" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(unspec:SI [(match_operand:BLK 1 "s_operand" "Q,S") |
(match_operand 2 "const_int_operand" "n,n")] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
icm\t%0,%2,%S1 |
icmy\t%0,%2,%S1" |
[(set_attr "op_type" "RS,RSY")]) |
|
(define_insn "*sethighpartdi_64" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(unspec:DI [(match_operand:BLK 1 "s_operand" "QS") |
(match_operand 2 "const_int_operand" "n")] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"icmh\t%0,%2,%S1" |
[(set_attr "op_type" "RSY")]) |
|
(define_insn "*sethighpartdi_31" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(unspec:DI [(match_operand:BLK 1 "s_operand" "Q,S") |
(match_operand 2 "const_int_operand" "n,n")] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT" |
"@ |
icm\t%0,%2,%S1 |
icmy\t%0,%2,%S1" |
[(set_attr "op_type" "RS,RSY")]) |
|
(define_insn_and_split "*extzv<mode>" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(zero_extract:GPR (match_operand:QI 1 "s_operand" "QS") |
(match_operand 2 "const_int_operand" "n") |
(const_int 0))) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[2]) > 0 |
&& INTVAL (operands[2]) <= GET_MODE_BITSIZE (SImode)" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 0) (unspec:GPR [(match_dup 1) (match_dup 3)] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))]) |
(set (match_dup 0) (lshiftrt:GPR (match_dup 0) (match_dup 2)))] |
{ |
int bitsize = INTVAL (operands[2]); |
int size = (bitsize - 1) / BITS_PER_UNIT + 1; /* round up */ |
int mask = ((1ul << size) - 1) << (GET_MODE_SIZE (SImode) - size); |
|
operands[1] = adjust_address (operands[1], BLKmode, 0); |
set_mem_size (operands[1], GEN_INT (size)); |
operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - bitsize); |
operands[3] = GEN_INT (mask); |
}) |
|
(define_insn_and_split "*extv<mode>" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(sign_extract:GPR (match_operand:QI 1 "s_operand" "QS") |
(match_operand 2 "const_int_operand" "n") |
(const_int 0))) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[2]) > 0 |
&& INTVAL (operands[2]) <= GET_MODE_BITSIZE (SImode)" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 0) (unspec:GPR [(match_dup 1) (match_dup 3)] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_dup 0) (ashiftrt:GPR (match_dup 0) (match_dup 2))) |
(clobber (reg:CC CC_REGNUM))])] |
{ |
int bitsize = INTVAL (operands[2]); |
int size = (bitsize - 1) / BITS_PER_UNIT + 1; /* round up */ |
int mask = ((1ul << size) - 1) << (GET_MODE_SIZE (SImode) - size); |
|
operands[1] = adjust_address (operands[1], BLKmode, 0); |
set_mem_size (operands[1], GEN_INT (size)); |
operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - bitsize); |
operands[3] = GEN_INT (mask); |
}) |
|
; |
; insv instruction patterns |
; |
|
(define_expand "insv" |
[(set (zero_extract (match_operand 0 "nonimmediate_operand" "") |
(match_operand 1 "const_int_operand" "") |
(match_operand 2 "const_int_operand" "")) |
(match_operand 3 "general_operand" ""))] |
"" |
{ |
if (s390_expand_insv (operands[0], operands[1], operands[2], operands[3])) |
DONE; |
FAIL; |
}) |
|
(define_insn "*insv<mode>_mem_reg" |
[(set (zero_extract:P (match_operand:QI 0 "memory_operand" "+Q,S") |
(match_operand 1 "const_int_operand" "n,n") |
(const_int 0)) |
(match_operand:P 2 "register_operand" "d,d"))] |
"INTVAL (operands[1]) > 0 |
&& INTVAL (operands[1]) <= GET_MODE_BITSIZE (SImode) |
&& INTVAL (operands[1]) % BITS_PER_UNIT == 0" |
{ |
int size = INTVAL (operands[1]) / BITS_PER_UNIT; |
|
operands[1] = GEN_INT ((1ul << size) - 1); |
return (which_alternative == 0) ? "stcm\t%2,%1,%S0" |
: "stcmy\t%2,%1,%S0"; |
} |
[(set_attr "op_type" "RS,RSY")]) |
|
(define_insn "*insvdi_mem_reghigh" |
[(set (zero_extract:DI (match_operand:QI 0 "memory_operand" "+QS") |
(match_operand 1 "const_int_operand" "n") |
(const_int 0)) |
(lshiftrt:DI (match_operand:DI 2 "register_operand" "d") |
(const_int 32)))] |
"TARGET_64BIT |
&& INTVAL (operands[1]) > 0 |
&& INTVAL (operands[1]) <= GET_MODE_BITSIZE (SImode) |
&& INTVAL (operands[1]) % BITS_PER_UNIT == 0" |
{ |
int size = INTVAL (operands[1]) / BITS_PER_UNIT; |
|
operands[1] = GEN_INT ((1ul << size) - 1); |
return "stcmh\t%2,%1,%S0"; |
} |
[(set_attr "op_type" "RSY")]) |
|
(define_insn "*insv<mode>_reg_imm" |
[(set (zero_extract:P (match_operand:P 0 "register_operand" "+d") |
(const_int 16) |
(match_operand 1 "const_int_operand" "n")) |
(match_operand:P 2 "const_int_operand" "n"))] |
"TARGET_ZARCH |
&& INTVAL (operands[1]) >= 0 |
&& INTVAL (operands[1]) < BITS_PER_WORD |
&& INTVAL (operands[1]) % 16 == 0" |
{ |
switch (BITS_PER_WORD - INTVAL (operands[1])) |
{ |
case 64: return "iihh\t%0,%x2"; break; |
case 48: return "iihl\t%0,%x2"; break; |
case 32: return "iilh\t%0,%x2"; break; |
case 16: return "iill\t%0,%x2"; break; |
default: gcc_unreachable(); |
} |
} |
[(set_attr "op_type" "RI")]) |
|
(define_insn "*insv<mode>_reg_extimm" |
[(set (zero_extract:P (match_operand:P 0 "register_operand" "+d") |
(const_int 32) |
(match_operand 1 "const_int_operand" "n")) |
(match_operand:P 2 "const_int_operand" "n"))] |
"TARGET_EXTIMM |
&& INTVAL (operands[1]) >= 0 |
&& INTVAL (operands[1]) < BITS_PER_WORD |
&& INTVAL (operands[1]) % 32 == 0" |
{ |
switch (BITS_PER_WORD - INTVAL (operands[1])) |
{ |
case 64: return "iihf\t%0,%o2"; break; |
case 32: return "iilf\t%0,%o2"; break; |
default: gcc_unreachable(); |
} |
} |
[(set_attr "op_type" "RIL")]) |
|
; |
; extendsidi2 instruction pattern(s). |
; |
|
(define_expand "extendsidi2" |
[(set (match_operand:DI 0 "register_operand" "") |
(sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))] |
"" |
{ |
if (!TARGET_64BIT) |
{ |
emit_insn (gen_rtx_CLOBBER (VOIDmode, operands[0])); |
emit_move_insn (gen_highpart (SImode, operands[0]), operands[1]); |
emit_move_insn (gen_lowpart (SImode, operands[0]), const0_rtx); |
emit_insn (gen_ashrdi3 (operands[0], operands[0], GEN_INT (32))); |
DONE; |
} |
}) |
|
(define_insn "*extendsidi2" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")))] |
"TARGET_64BIT" |
"@ |
lgfr\t%0,%1 |
lgf\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; |
; extend(hi|qi)(si|di)2 instruction pattern(s). |
; |
|
(define_expand "extend<HQI:mode><DSI:mode>2" |
[(set (match_operand:DSI 0 "register_operand" "") |
(sign_extend:DSI (match_operand:HQI 1 "nonimmediate_operand" "")))] |
"" |
{ |
if (<DSI:MODE>mode == DImode && !TARGET_64BIT) |
{ |
rtx tmp = gen_reg_rtx (SImode); |
emit_insn (gen_extend<HQI:mode>si2 (tmp, operands[1])); |
emit_insn (gen_extendsidi2 (operands[0], tmp)); |
DONE; |
} |
else if (!TARGET_EXTIMM) |
{ |
rtx bitcount = GEN_INT (GET_MODE_BITSIZE (<DSI:MODE>mode) - |
GET_MODE_BITSIZE (<HQI:MODE>mode)); |
|
operands[1] = gen_lowpart (<DSI:MODE>mode, operands[1]); |
emit_insn (gen_ashl<DSI:mode>3 (operands[0], operands[1], bitcount)); |
emit_insn (gen_ashr<DSI:mode>3 (operands[0], operands[0], bitcount)); |
DONE; |
} |
}) |
|
; |
; extendhidi2 instruction pattern(s). |
; |
|
(define_insn "*extendhidi2_extimm" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(sign_extend:DI (match_operand:HI 1 "nonimmediate_operand" "d,m")))] |
"TARGET_64BIT && TARGET_EXTIMM" |
"@ |
lghr\t%0,%1 |
lgh\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*extendhidi2" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(sign_extend:DI (match_operand:HI 1 "memory_operand" "m")))] |
"TARGET_64BIT" |
"lgh\t%0,%1" |
[(set_attr "op_type" "RXY")]) |
|
; |
; extendhisi2 instruction pattern(s). |
; |
|
(define_insn "*extendhisi2_extimm" |
[(set (match_operand:SI 0 "register_operand" "=d,d,d") |
(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,T")))] |
"TARGET_EXTIMM" |
"@ |
lhr\t%0,%1 |
lh\t%0,%1 |
lhy\t%0,%1" |
[(set_attr "op_type" "RRE,RX,RXY")]) |
|
(define_insn "*extendhisi2" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(sign_extend:SI (match_operand:HI 1 "memory_operand" "R,T")))] |
"!TARGET_EXTIMM" |
"@ |
lh\t%0,%1 |
lhy\t%0,%1" |
[(set_attr "op_type" "RX,RXY")]) |
|
; |
; extendqi(si|di)2 instruction pattern(s). |
; |
|
; lbr, lgbr, lb, lgb |
(define_insn "*extendqi<mode>2_extimm" |
[(set (match_operand:GPR 0 "register_operand" "=d,d") |
(sign_extend:GPR (match_operand:QI 1 "nonimmediate_operand" "d,m")))] |
"TARGET_EXTIMM" |
"@ |
l<g>br\t%0,%1 |
l<g>b\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; lb, lgb |
(define_insn "*extendqi<mode>2" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(sign_extend:GPR (match_operand:QI 1 "memory_operand" "m")))] |
"!TARGET_EXTIMM && TARGET_LONG_DISPLACEMENT" |
"l<g>b\t%0,%1" |
[(set_attr "op_type" "RXY")]) |
|
(define_insn_and_split "*extendqi<mode>2_short_displ" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(sign_extend:GPR (match_operand:QI 1 "s_operand" "Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_EXTIMM && !TARGET_LONG_DISPLACEMENT" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 0) (unspec:GPR [(match_dup 1) (const_int 8)] UNSPEC_ICM)) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_dup 0) (ashiftrt:GPR (match_dup 0) (match_dup 2))) |
(clobber (reg:CC CC_REGNUM))])] |
{ |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
set_mem_size (operands[1], GEN_INT (GET_MODE_SIZE (QImode))); |
operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) |
- GET_MODE_BITSIZE (QImode)); |
}) |
|
; |
; zero_extendsidi2 instruction pattern(s). |
; |
|
(define_expand "zero_extendsidi2" |
[(set (match_operand:DI 0 "register_operand" "") |
(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))] |
"" |
{ |
if (!TARGET_64BIT) |
{ |
emit_insn (gen_rtx_CLOBBER (VOIDmode, operands[0])); |
emit_move_insn (gen_lowpart (SImode, operands[0]), operands[1]); |
emit_move_insn (gen_highpart (SImode, operands[0]), const0_rtx); |
DONE; |
} |
}) |
|
(define_insn "*zero_extendsidi2" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")))] |
"TARGET_64BIT" |
"@ |
llgfr\t%0,%1 |
llgf\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; |
; LLGT-type instructions (zero-extend from 31 bit to 64 bit). |
; |
|
(define_insn "*llgt_sidi" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(and:DI (subreg:DI (match_operand:SI 1 "memory_operand" "m") 0) |
(const_int 2147483647)))] |
"TARGET_64BIT" |
"llgt\t%0,%1" |
[(set_attr "op_type" "RXE")]) |
|
(define_insn_and_split "*llgt_sidi_split" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(and:DI (subreg:DI (match_operand:SI 1 "memory_operand" "m") 0) |
(const_int 2147483647))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) |
(and:DI (subreg:DI (match_dup 1) 0) |
(const_int 2147483647)))] |
"") |
|
(define_insn "*llgt_sisi" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(and:SI (match_operand:SI 1 "nonimmediate_operand" "d,m") |
(const_int 2147483647)))] |
"TARGET_ZARCH" |
"@ |
llgtr\t%0,%1 |
llgt\t%0,%1" |
[(set_attr "op_type" "RRE,RXE")]) |
|
(define_insn "*llgt_didi" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(and:DI (match_operand:DI 1 "nonimmediate_operand" "d,o") |
(const_int 2147483647)))] |
"TARGET_64BIT" |
"@ |
llgtr\t%0,%1 |
llgt\t%0,%N1" |
[(set_attr "op_type" "RRE,RXE")]) |
|
(define_split |
[(set (match_operand:GPR 0 "register_operand" "") |
(and:GPR (match_operand:GPR 1 "nonimmediate_operand" "") |
(const_int 2147483647))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && reload_completed" |
[(set (match_dup 0) |
(and:GPR (match_dup 1) |
(const_int 2147483647)))] |
"") |
|
; |
; zero_extend(hi|qi)(si|di)2 instruction pattern(s). |
; |
|
(define_expand "zero_extend<mode>di2" |
[(set (match_operand:DI 0 "register_operand" "") |
(zero_extend:DI (match_operand:HQI 1 "nonimmediate_operand" "")))] |
"" |
{ |
if (!TARGET_64BIT) |
{ |
rtx tmp = gen_reg_rtx (SImode); |
emit_insn (gen_zero_extend<mode>si2 (tmp, operands[1])); |
emit_insn (gen_zero_extendsidi2 (operands[0], tmp)); |
DONE; |
} |
else if (!TARGET_EXTIMM) |
{ |
rtx bitcount = GEN_INT (GET_MODE_BITSIZE(DImode) - |
GET_MODE_BITSIZE(<MODE>mode)); |
operands[1] = gen_lowpart (DImode, operands[1]); |
emit_insn (gen_ashldi3 (operands[0], operands[1], bitcount)); |
emit_insn (gen_lshrdi3 (operands[0], operands[0], bitcount)); |
DONE; |
} |
}) |
|
(define_expand "zero_extend<mode>si2" |
[(set (match_operand:SI 0 "register_operand" "") |
(zero_extend:SI (match_operand:HQI 1 "nonimmediate_operand" "")))] |
"" |
{ |
if (!TARGET_EXTIMM) |
{ |
operands[1] = gen_lowpart (SImode, operands[1]); |
emit_insn (gen_andsi3 (operands[0], operands[1], |
GEN_INT ((1 << GET_MODE_BITSIZE(<MODE>mode)) - 1))); |
DONE; |
} |
}) |
|
; llhr, llcr, llghr, llgcr, llh, llc, llgh, llgc |
(define_insn "*zero_extend<HQI:mode><GPR:mode>2_extimm" |
[(set (match_operand:GPR 0 "register_operand" "=d,d") |
(zero_extend:GPR (match_operand:HQI 1 "nonimmediate_operand" "d,m")))] |
"TARGET_EXTIMM" |
"@ |
ll<g><hc>r\t%0,%1 |
ll<g><hc>\t%0,%1" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; llgh, llgc |
(define_insn "*zero_extend<HQI:mode><GPR:mode>2" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(zero_extend:GPR (match_operand:HQI 1 "memory_operand" "m")))] |
"TARGET_ZARCH && !TARGET_EXTIMM" |
"llg<hc>\t%0,%1" |
[(set_attr "op_type" "RXY")]) |
|
(define_insn_and_split "*zero_extendhisi2_31" |
[(set (match_operand:SI 0 "register_operand" "=&d") |
(zero_extend:SI (match_operand:HI 1 "s_operand" "QS"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (const_int 0)) |
(parallel |
[(set (strict_low_part (match_dup 2)) (match_dup 1)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[2] = gen_lowpart (HImode, operands[0]);") |
|
(define_insn_and_split "*zero_extendqisi2_31" |
[(set (match_operand:SI 0 "register_operand" "=&d") |
(zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))] |
"!TARGET_ZARCH" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (const_int 0)) |
(set (strict_low_part (match_dup 2)) (match_dup 1))] |
"operands[2] = gen_lowpart (QImode, operands[0]);") |
|
; |
; zero_extendqihi2 instruction pattern(s). |
; |
|
(define_expand "zero_extendqihi2" |
[(set (match_operand:HI 0 "register_operand" "") |
(zero_extend:HI (match_operand:QI 1 "register_operand" "")))] |
"TARGET_ZARCH && !TARGET_EXTIMM" |
{ |
operands[1] = gen_lowpart (HImode, operands[1]); |
emit_insn (gen_andhi3 (operands[0], operands[1], GEN_INT (0xff))); |
DONE; |
}) |
|
(define_insn "*zero_extendqihi2_64" |
[(set (match_operand:HI 0 "register_operand" "=d") |
(zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))] |
"TARGET_ZARCH && !TARGET_EXTIMM" |
"llgc\t%0,%1" |
[(set_attr "op_type" "RXY")]) |
|
(define_insn_and_split "*zero_extendqihi2_31" |
[(set (match_operand:HI 0 "register_operand" "=&d") |
(zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))] |
"!TARGET_ZARCH" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (const_int 0)) |
(set (strict_low_part (match_dup 2)) (match_dup 1))] |
"operands[2] = gen_lowpart (QImode, operands[0]);") |
|
|
; |
; fixuns_trunc(sf|df)(si|di)2 and fix_trunc(sf|df)(si|di)2 instruction pattern(s). |
; |
|
(define_expand "fixuns_trunc<FPR:mode><GPR:mode>2" |
[(set (match_operand:GPR 0 "register_operand" "") |
(unsigned_fix:GPR (match_operand:FPR 1 "register_operand" "")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
{ |
rtx label1 = gen_label_rtx (); |
rtx label2 = gen_label_rtx (); |
rtx temp = gen_reg_rtx (<FPR:MODE>mode); |
REAL_VALUE_TYPE cmp, sub; |
|
operands[1] = force_reg (<FPR:MODE>mode, operands[1]); |
real_2expN (&cmp, GET_MODE_BITSIZE(<GPR:MODE>mode) - 1); |
real_2expN (&sub, GET_MODE_BITSIZE(<GPR:MODE>mode)); |
|
emit_insn (gen_cmp<FPR:mode> (operands[1], |
CONST_DOUBLE_FROM_REAL_VALUE (cmp, <FPR:MODE>mode))); |
emit_jump_insn (gen_blt (label1)); |
emit_insn (gen_sub<FPR:mode>3 (temp, operands[1], |
CONST_DOUBLE_FROM_REAL_VALUE (sub, <FPR:MODE>mode))); |
emit_insn (gen_fix_trunc<FPR:mode><GPR:mode>2_ieee (operands[0], temp, |
GEN_INT(7))); |
emit_jump (label2); |
|
emit_label (label1); |
emit_insn (gen_fix_trunc<FPR:mode><GPR:mode>2_ieee (operands[0], |
operands[1], GEN_INT(5))); |
emit_label (label2); |
DONE; |
}) |
|
(define_expand "fix_trunc<mode>di2" |
[(set (match_operand:DI 0 "register_operand" "") |
(fix:DI (match_operand:DSF 1 "nonimmediate_operand" "")))] |
"TARGET_64BIT && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
{ |
operands[1] = force_reg (<MODE>mode, operands[1]); |
emit_insn (gen_fix_trunc<mode>di2_ieee (operands[0], operands[1], |
GEN_INT(5))); |
DONE; |
}) |
|
; cgxbr, cgdbr, cgebr, cfxbr, cfdbr, cfebr |
(define_insn "fix_trunc<FPR:mode><GPR:mode>2_ieee" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(fix:GPR (match_operand:FPR 1 "register_operand" "f"))) |
(unspec:GPR [(match_operand:GPR 2 "immediate_operand" "K")] UNSPEC_ROUND) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"c<GPR:gf><FPR:xde>br\t%0,%h2,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "ftoi")]) |
|
; |
; fix_trunctf(si|di)2 instruction pattern(s). |
; |
|
(define_expand "fix_trunctf<mode>2" |
[(parallel [(set (match_operand:GPR 0 "register_operand" "") |
(fix:GPR (match_operand:TF 1 "register_operand" ""))) |
(unspec:GPR [(const_int 5)] UNSPEC_ROUND) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"") |
|
; |
; fix_truncdfsi2 instruction pattern(s). |
; |
|
(define_expand "fix_truncdfsi2" |
[(set (match_operand:SI 0 "register_operand" "") |
(fix:SI (match_operand:DF 1 "nonimmediate_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
if (TARGET_IBM_FLOAT) |
{ |
/* This is the algorithm from POP chapter A.5.7.2. */ |
|
rtx temp = assign_stack_local (BLKmode, 8, BITS_PER_WORD); |
rtx two31r = s390_gen_rtx_const_DI (0x4f000000, 0x08000000); |
rtx two32 = s390_gen_rtx_const_DI (0x4e000001, 0x00000000); |
|
operands[1] = force_reg (DFmode, operands[1]); |
emit_insn (gen_fix_truncdfsi2_ibm (operands[0], operands[1], |
two31r, two32, temp)); |
} |
else |
{ |
operands[1] = force_reg (DFmode, operands[1]); |
emit_insn (gen_fix_truncdfsi2_ieee (operands[0], operands[1], GEN_INT (5))); |
} |
|
DONE; |
}) |
|
(define_insn "fix_truncdfsi2_ibm" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(fix:SI (match_operand:DF 1 "nonimmediate_operand" "+f"))) |
(use (match_operand:DI 2 "immediate_operand" "m")) |
(use (match_operand:DI 3 "immediate_operand" "m")) |
(use (match_operand:BLK 4 "memory_operand" "m")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
{ |
output_asm_insn ("sd\t%1,%2", operands); |
output_asm_insn ("aw\t%1,%3", operands); |
output_asm_insn ("std\t%1,%4", operands); |
output_asm_insn ("xi\t%N4,128", operands); |
return "l\t%0,%N4"; |
} |
[(set_attr "length" "20")]) |
|
; |
; fix_truncsfsi2 instruction pattern(s). |
; |
|
(define_expand "fix_truncsfsi2" |
[(set (match_operand:SI 0 "register_operand" "") |
(fix:SI (match_operand:SF 1 "nonimmediate_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
if (TARGET_IBM_FLOAT) |
{ |
/* Convert to DFmode and then use the POP algorithm. */ |
rtx temp = gen_reg_rtx (DFmode); |
emit_insn (gen_extendsfdf2 (temp, operands[1])); |
emit_insn (gen_fix_truncdfsi2 (operands[0], temp)); |
} |
else |
{ |
operands[1] = force_reg (SFmode, operands[1]); |
emit_insn (gen_fix_truncsfsi2_ieee (operands[0], operands[1], GEN_INT (5))); |
} |
|
DONE; |
}) |
|
; |
; float(si|di)(tf|df|sf)2 instruction pattern(s). |
; |
|
; cxgbr, cdgbr, cegbr |
(define_insn "floatdi<mode>2" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(float:FPR (match_operand:DI 1 "register_operand" "d")))] |
"TARGET_64BIT && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"c<xde>gbr\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "itof" )]) |
|
; cxfbr, cdfbr, cefbr |
(define_insn "floatsi<mode>2_ieee" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(float:FPR (match_operand:SI 1 "register_operand" "d")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"c<xde>fbr\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "itof" )]) |
|
|
; |
; floatsi(tf|df)2 instruction pattern(s). |
; |
|
(define_expand "floatsitf2" |
[(set (match_operand:TF 0 "register_operand" "") |
(float:TF (match_operand:SI 1 "register_operand" "")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"") |
|
(define_expand "floatsidf2" |
[(set (match_operand:DF 0 "register_operand" "") |
(float:DF (match_operand:SI 1 "register_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
if (TARGET_IBM_FLOAT) |
{ |
/* This is the algorithm from POP chapter A.5.7.1. */ |
|
rtx temp = assign_stack_local (BLKmode, 8, BITS_PER_WORD); |
rtx two31 = s390_gen_rtx_const_DI (0x4e000000, 0x80000000); |
|
emit_insn (gen_floatsidf2_ibm (operands[0], operands[1], two31, temp)); |
DONE; |
} |
}) |
|
(define_insn "floatsidf2_ibm" |
[(set (match_operand:DF 0 "register_operand" "=f") |
(float:DF (match_operand:SI 1 "register_operand" "d"))) |
(use (match_operand:DI 2 "immediate_operand" "m")) |
(use (match_operand:BLK 3 "memory_operand" "m")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
{ |
output_asm_insn ("st\t%1,%N3", operands); |
output_asm_insn ("xi\t%N3,128", operands); |
output_asm_insn ("mvc\t%O3(4,%R3),%2", operands); |
output_asm_insn ("ld\t%0,%3", operands); |
return "sd\t%0,%2"; |
} |
[(set_attr "length" "20")]) |
|
; |
; floatsisf2 instruction pattern(s). |
; |
|
(define_expand "floatsisf2" |
[(set (match_operand:SF 0 "register_operand" "") |
(float:SF (match_operand:SI 1 "register_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
if (TARGET_IBM_FLOAT) |
{ |
/* Use the POP algorithm to convert to DFmode and then truncate. */ |
rtx temp = gen_reg_rtx (DFmode); |
emit_insn (gen_floatsidf2 (temp, operands[1])); |
emit_insn (gen_truncdfsf2 (operands[0], temp)); |
DONE; |
} |
}) |
|
; |
; truncdfsf2 instruction pattern(s). |
; |
|
(define_expand "truncdfsf2" |
[(set (match_operand:SF 0 "register_operand" "") |
(float_truncate:SF (match_operand:DF 1 "register_operand" "")))] |
"TARGET_HARD_FLOAT" |
"") |
|
(define_insn "truncdfsf2_ieee" |
[(set (match_operand:SF 0 "register_operand" "=f") |
(float_truncate:SF (match_operand:DF 1 "register_operand" "f")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"ledbr\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "ftruncdf")]) |
|
(define_insn "truncdfsf2_ibm" |
[(set (match_operand:SF 0 "register_operand" "=f,f") |
(float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
ler\t%0,%1 |
le\t%0,%1" |
[(set_attr "op_type" "RR,RX") |
(set_attr "type" "floadsf")]) |
|
; |
; trunctfdf2 instruction pattern(s). |
; |
|
(define_expand "trunctfdf2" |
[(parallel |
[(set (match_operand:DF 0 "register_operand" "") |
(float_truncate:DF (match_operand:TF 1 "register_operand" ""))) |
(clobber (match_scratch:TF 2 "=f"))])] |
"TARGET_HARD_FLOAT" |
"") |
|
(define_insn "*trunctfdf2_ieee" |
[(set (match_operand:DF 0 "register_operand" "=f") |
(float_truncate:DF (match_operand:TF 1 "register_operand" "f"))) |
(clobber (match_scratch:TF 2 "=f"))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"ldxbr\t%2,%1\;ldr\t%0,%2" |
[(set_attr "length" "6") |
(set_attr "type" "ftrunctf")]) |
|
(define_insn "*trunctfdf2_ibm" |
[(set (match_operand:DF 0 "register_operand" "=f") |
(float_truncate:DF (match_operand:TF 1 "register_operand" "f"))) |
(clobber (match_scratch:TF 2 "=f"))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"ldxr\t%2,%1\;ldr\t%0,%2" |
[(set_attr "length" "4") |
(set_attr "type" "ftrunctf")]) |
|
; |
; trunctfsf2 instruction pattern(s). |
; |
|
(define_expand "trunctfsf2" |
[(parallel |
[(set (match_operand:SF 0 "register_operand" "=f") |
(float_truncate:SF (match_operand:TF 1 "register_operand" "f"))) |
(clobber (match_scratch:TF 2 "=f"))])] |
"TARGET_HARD_FLOAT" |
"") |
|
(define_insn "*trunctfsf2_ieee" |
[(set (match_operand:SF 0 "register_operand" "=f") |
(float_truncate:SF (match_operand:TF 1 "register_operand" "f"))) |
(clobber (match_scratch:TF 2 "=f"))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lexbr\t%2,%1\;ler\t%0,%2" |
[(set_attr "length" "6") |
(set_attr "type" "ftrunctf")]) |
|
(define_insn "*trunctfsf2_ibm" |
[(set (match_operand:SF 0 "register_operand" "=f") |
(float_truncate:SF (match_operand:TF 1 "register_operand" "f"))) |
(clobber (match_scratch:TF 2 "=f"))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"lexr\t%2,%1\;ler\t%0,%2" |
[(set_attr "length" "6") |
(set_attr "type" "ftrunctf")]) |
|
; |
; extendsfdf2 instruction pattern(s). |
; |
|
(define_expand "extendsfdf2" |
[(set (match_operand:DF 0 "register_operand" "") |
(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "")))] |
"TARGET_HARD_FLOAT" |
{ |
if (TARGET_IBM_FLOAT) |
{ |
emit_insn (gen_extendsfdf2_ibm (operands[0], operands[1])); |
DONE; |
} |
}) |
|
(define_insn "extendsfdf2_ieee" |
[(set (match_operand:DF 0 "register_operand" "=f,f") |
(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
ldebr\t%0,%1 |
ldeb\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimpsf, floadsf")]) |
|
(define_insn "extendsfdf2_ibm" |
[(set (match_operand:DF 0 "register_operand" "=f,f") |
(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "f,R"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
sdr\t%0,%0\;ler\t%0,%1 |
sdr\t%0,%0\;le\t%0,%1" |
[(set_attr "length" "4,6") |
(set_attr "type" "floadsf")]) |
|
; |
; extenddftf2 instruction pattern(s). |
; |
|
(define_expand "extenddftf2" |
[(set (match_operand:TF 0 "register_operand" "") |
(float_extend:TF (match_operand:DF 1 "nonimmediate_operand" "")))] |
"TARGET_HARD_FLOAT" |
"") |
|
(define_insn "*extenddftf2_ieee" |
[(set (match_operand:TF 0 "register_operand" "=f,f") |
(float_extend:TF (match_operand:DF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
lxdbr\t%0,%1 |
lxdb\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimptf, floadtf")]) |
|
(define_insn "*extenddftf2_ibm" |
[(set (match_operand:TF 0 "register_operand" "=f,f") |
(float_extend:TF (match_operand:DF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
lxdr\t%0,%1 |
lxd\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimptf, floadtf")]) |
|
; |
; extendsftf2 instruction pattern(s). |
; |
|
(define_expand "extendsftf2" |
[(set (match_operand:TF 0 "register_operand" "") |
(float_extend:TF (match_operand:SF 1 "nonimmediate_operand" "")))] |
"TARGET_HARD_FLOAT" |
"") |
|
(define_insn "*extendsftf2_ieee" |
[(set (match_operand:TF 0 "register_operand" "=f,f") |
(float_extend:TF (match_operand:SF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
lxebr\t%0,%1 |
lxeb\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimptf, floadtf")]) |
|
(define_insn "*extendsftf2_ibm" |
[(set (match_operand:TF 0 "register_operand" "=f,f") |
(float_extend:TF (match_operand:SF 1 "nonimmediate_operand" "f,R")))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
lxer\t%0,%1 |
lxe\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimptf, floadtf")]) |
|
|
;; |
;; ARITHMETIC OPERATIONS |
;; |
; arithmetic operations set the ConditionCode, |
; because of unpredictable Bits in Register for Halfword and Byte |
; the ConditionCode can be set wrong in operations for Halfword and Byte |
|
;; |
;;- Add instructions. |
;; |
|
; |
; addti3 instruction pattern(s). |
; |
|
(define_insn_and_split "addti3" |
[(set (match_operand:TI 0 "register_operand" "=&d") |
(plus:TI (match_operand:TI 1 "nonimmediate_operand" "%0") |
(match_operand:TI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (reg:CCL1 CC_REGNUM) |
(compare:CCL1 (plus:DI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (plus:DI (match_dup 7) (match_dup 8)))]) |
(parallel |
[(set (match_dup 3) (plus:DI (plus:DI (match_dup 4) (match_dup 5)) |
(ltu:DI (reg:CCL1 CC_REGNUM) (const_int 0)))) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[3] = operand_subword (operands[0], 0, 0, TImode); |
operands[4] = operand_subword (operands[1], 0, 0, TImode); |
operands[5] = operand_subword (operands[2], 0, 0, TImode); |
operands[6] = operand_subword (operands[0], 1, 0, TImode); |
operands[7] = operand_subword (operands[1], 1, 0, TImode); |
operands[8] = operand_subword (operands[2], 1, 0, TImode);") |
|
; |
; adddi3 instruction pattern(s). |
; |
|
(define_expand "adddi3" |
[(parallel |
[(set (match_operand:DI 0 "register_operand" "") |
(plus:DI (match_operand:DI 1 "nonimmediate_operand" "") |
(match_operand:DI 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*adddi3_sign" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(plus:DI (sign_extend:DI (match_operand:SI 2 "general_operand" "d,m")) |
(match_operand:DI 1 "register_operand" "0,0"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"@ |
agfr\t%0,%2 |
agf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*adddi3_zero_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) |
(match_operand:DI 1 "register_operand" "0,0")) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d,d") |
(plus:DI (zero_extend:DI (match_dup 2)) (match_dup 1)))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" |
"@ |
algfr\t%0,%2 |
algf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*adddi3_zero_cconly" |
[(set (reg CC_REGNUM) |
(compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) |
(match_operand:DI 1 "register_operand" "0,0")) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d,d"))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" |
"@ |
algfr\t%0,%2 |
algf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*adddi3_zero" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) |
(match_operand:DI 1 "register_operand" "0,0"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"@ |
algfr\t%0,%2 |
algf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn_and_split "*adddi3_31z" |
[(set (match_operand:DI 0 "register_operand" "=&d") |
(plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") |
(match_operand:DI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (reg:CCL1 CC_REGNUM) |
(compare:CCL1 (plus:SI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (plus:SI (match_dup 7) (match_dup 8)))]) |
(parallel |
[(set (match_dup 3) (plus:SI (plus:SI (match_dup 4) (match_dup 5)) |
(ltu:SI (reg:CCL1 CC_REGNUM) (const_int 0)))) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[3] = operand_subword (operands[0], 0, 0, DImode); |
operands[4] = operand_subword (operands[1], 0, 0, DImode); |
operands[5] = operand_subword (operands[2], 0, 0, DImode); |
operands[6] = operand_subword (operands[0], 1, 0, DImode); |
operands[7] = operand_subword (operands[1], 1, 0, DImode); |
operands[8] = operand_subword (operands[2], 1, 0, DImode);") |
|
(define_insn_and_split "*adddi3_31" |
[(set (match_operand:DI 0 "register_operand" "=&d") |
(plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") |
(match_operand:DI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 3) (plus:SI (match_dup 4) (match_dup 5))) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (reg:CCL1 CC_REGNUM) |
(compare:CCL1 (plus:SI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (plus:SI (match_dup 7) (match_dup 8)))]) |
(set (pc) |
(if_then_else (ltu (reg:CCL1 CC_REGNUM) (const_int 0)) |
(pc) |
(label_ref (match_dup 9)))) |
(parallel |
[(set (match_dup 3) (plus:SI (match_dup 3) (const_int 1))) |
(clobber (reg:CC CC_REGNUM))]) |
(match_dup 9)] |
"operands[3] = operand_subword (operands[0], 0, 0, DImode); |
operands[4] = operand_subword (operands[1], 0, 0, DImode); |
operands[5] = operand_subword (operands[2], 0, 0, DImode); |
operands[6] = operand_subword (operands[0], 1, 0, DImode); |
operands[7] = operand_subword (operands[1], 1, 0, DImode); |
operands[8] = operand_subword (operands[2], 1, 0, DImode); |
operands[9] = gen_label_rtx ();") |
|
; |
; addsi3 instruction pattern(s). |
; |
|
(define_expand "addsi3" |
[(parallel |
[(set (match_operand:SI 0 "register_operand" "") |
(plus:SI (match_operand:SI 1 "nonimmediate_operand" "") |
(match_operand:SI 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*addsi3_sign" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(plus:SI (sign_extend:SI (match_operand:HI 2 "memory_operand" "R,T")) |
(match_operand:SI 1 "register_operand" "0,0"))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
ah\t%0,%2 |
ahy\t%0,%2" |
[(set_attr "op_type" "RX,RXY")]) |
|
; |
; add(di|si)3 instruction pattern(s). |
; |
|
; ar, ahi, alfi, slfi, a, ay, agr, aghi, algfi, slgfi, ag |
(define_insn "*add<mode>3" |
[(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d,d") |
(plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0,0,0,0") |
(match_operand:GPR 2 "general_operand" "d,K,Op,On,R,T") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
a<g>r\t%0,%2 |
a<g>hi\t%0,%h2 |
al<g>fi\t%0,%2 |
sl<g>fi\t%0,%n2 |
a<g>\t%0,%2 |
a<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RI,RIL,RIL,RX<Y>,RXY")]) |
|
; alr, alfi, slfi, al, aly, algr, algfi, slgfi, alg |
(define_insn "*add<mode>3_carry1_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:GPR 2 "general_operand" "d,Op,On,R,T")) |
(match_dup 1))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d") |
(plus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCL1mode)" |
"@ |
al<g>r\t%0,%2 |
al<g>fi\t%0,%2 |
sl<g>fi\t%0,%n2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RIL,RIL,RX<Y>,RXY")]) |
|
; alr, al, aly, algr, alg |
(define_insn "*add<mode>3_carry1_cconly" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(match_dup 1))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCL1mode)" |
"@ |
al<g>r\t%0,%2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; alr, alfi, slfi, al, aly, algr, algfi, slgfi, alg |
(define_insn "*add<mode>3_carry2_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:GPR 2 "general_operand" "d,Op,On,R,T")) |
(match_dup 2))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d") |
(plus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCL1mode)" |
"@ |
al<g>r\t%0,%2 |
al<g>fi\t%0,%2 |
sl<g>fi\t%0,%n2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RIL,RIL,RX<Y>,RXY")]) |
|
; alr, al, aly, algr, alg |
(define_insn "*add<mode>3_carry2_cconly" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(match_dup 2))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCL1mode)" |
"@ |
al<g>r\t%0,%2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; alr, alfi, slfi, al, aly, algr, algfi, slgfi, alg |
(define_insn "*add<mode>3_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:GPR 2 "general_operand" "d,Op,On,R,T")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d") |
(plus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCLmode)" |
"@ |
al<g>r\t%0,%2 |
al<g>fi\t%0,%2 |
sl<g>fi\t%0,%n2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RIL,RIL,RX<Y>,RXY")]) |
|
; alr, al, aly, algr, alg |
(define_insn "*add<mode>3_cconly" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCLmode)" |
"@ |
al<g>r\t%0,%2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; alr, al, aly, algr, alg |
(define_insn "*add<mode>3_cconly2" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 1 "nonimmediate_operand" "%0,0,0") |
(neg:GPR (match_operand:GPR 2 "general_operand" "d,R,T")))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode(insn, CCLmode)" |
"@ |
al<g>r\t%0,%2 |
al<g>\t%0,%2 |
al<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; ahi, afi, aghi, agfi |
(define_insn "*add<mode>3_imm_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") |
(match_operand:GPR 2 "const_int_operand" "K,Os")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d,d") |
(plus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCAmode) |
&& (CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'K', \"K\") |
|| CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'O', \"Os\")) |
&& INTVAL (operands[2]) != -((HOST_WIDE_INT)1 << (GET_MODE_BITSIZE(<MODE>mode) - 1))" |
"@ |
a<g>hi\t%0,%h2 |
a<g>fi\t%0,%2" |
[(set_attr "op_type" "RI,RIL")]) |
|
; |
; add(df|sf)3 instruction pattern(s). |
; |
|
(define_expand "add<mode>3" |
[(parallel |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(plus:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>"))) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_HARD_FLOAT" |
"") |
|
; axbr, adbr, aebr, axb, adb, aeb |
(define_insn "*add<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(plus:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
a<xde>br\t%0,%2 |
a<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; axbr, adbr, aebr, axb, adb, aeb |
(define_insn "*add<mode>3_cc" |
[(set (reg CC_REGNUM) |
(compare (plus:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")) |
(match_operand:FPR 3 "const0_operand" ""))) |
(set (match_operand:FPR 0 "register_operand" "=f,f") |
(plus:FPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
a<xde>br\t%0,%2 |
a<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; axbr, adbr, aebr, axb, adb, aeb |
(define_insn "*add<mode>3_cconly" |
[(set (reg CC_REGNUM) |
(compare (plus:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")) |
(match_operand:FPR 3 "const0_operand" ""))) |
(clobber (match_scratch:FPR 0 "=f,f"))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
a<xde>br\t%0,%2 |
a<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; axr, adr, aer, ax, ad, ae |
(define_insn "*add<mode>3_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(plus:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
a<xde>r\t%0,%2 |
a<xde>\t%0,%2" |
[(set_attr "op_type" "<RRe>,<RXe>") |
(set_attr "type" "fsimp<mode>")]) |
|
|
;; |
;;- Subtract instructions. |
;; |
|
; |
; subti3 instruction pattern(s). |
; |
|
(define_insn_and_split "subti3" |
[(set (match_operand:TI 0 "register_operand" "=&d") |
(minus:TI (match_operand:TI 1 "register_operand" "0") |
(match_operand:TI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (reg:CCL2 CC_REGNUM) |
(compare:CCL2 (minus:DI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (minus:DI (match_dup 7) (match_dup 8)))]) |
(parallel |
[(set (match_dup 3) (minus:DI (minus:DI (match_dup 4) (match_dup 5)) |
(gtu:DI (reg:CCL2 CC_REGNUM) (const_int 0)))) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[3] = operand_subword (operands[0], 0, 0, TImode); |
operands[4] = operand_subword (operands[1], 0, 0, TImode); |
operands[5] = operand_subword (operands[2], 0, 0, TImode); |
operands[6] = operand_subword (operands[0], 1, 0, TImode); |
operands[7] = operand_subword (operands[1], 1, 0, TImode); |
operands[8] = operand_subword (operands[2], 1, 0, TImode);") |
|
; |
; subdi3 instruction pattern(s). |
; |
|
(define_expand "subdi3" |
[(parallel |
[(set (match_operand:DI 0 "register_operand" "") |
(minus:DI (match_operand:DI 1 "register_operand" "") |
(match_operand:DI 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*subdi3_sign" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(minus:DI (match_operand:DI 1 "register_operand" "0,0") |
(sign_extend:DI (match_operand:SI 2 "general_operand" "d,m")))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"@ |
sgfr\t%0,%2 |
sgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*subdi3_zero_cc" |
[(set (reg CC_REGNUM) |
(compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") |
(zero_extend:DI (match_operand:SI 2 "general_operand" "d,m"))) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d,d") |
(minus:DI (match_dup 1) (zero_extend:DI (match_dup 2))))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" |
"@ |
slgfr\t%0,%2 |
slgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*subdi3_zero_cconly" |
[(set (reg CC_REGNUM) |
(compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") |
(zero_extend:DI (match_operand:SI 2 "general_operand" "d,m"))) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d,d"))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" |
"@ |
slgfr\t%0,%2 |
slgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*subdi3_zero" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(minus:DI (match_operand:DI 1 "register_operand" "0,0") |
(zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"@ |
slgfr\t%0,%2 |
slgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn_and_split "*subdi3_31z" |
[(set (match_operand:DI 0 "register_operand" "=&d") |
(minus:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:DI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (reg:CCL2 CC_REGNUM) |
(compare:CCL2 (minus:SI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (minus:SI (match_dup 7) (match_dup 8)))]) |
(parallel |
[(set (match_dup 3) (minus:SI (minus:SI (match_dup 4) (match_dup 5)) |
(gtu:SI (reg:CCL2 CC_REGNUM) (const_int 0)))) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[3] = operand_subword (operands[0], 0, 0, DImode); |
operands[4] = operand_subword (operands[1], 0, 0, DImode); |
operands[5] = operand_subword (operands[2], 0, 0, DImode); |
operands[6] = operand_subword (operands[0], 1, 0, DImode); |
operands[7] = operand_subword (operands[1], 1, 0, DImode); |
operands[8] = operand_subword (operands[2], 1, 0, DImode);") |
|
(define_insn_and_split "*subdi3_31" |
[(set (match_operand:DI 0 "register_operand" "=&d") |
(minus:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:DI 2 "general_operand" "do") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 3) (minus:SI (match_dup 4) (match_dup 5))) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (reg:CCL2 CC_REGNUM) |
(compare:CCL2 (minus:SI (match_dup 7) (match_dup 8)) |
(match_dup 7))) |
(set (match_dup 6) (minus:SI (match_dup 7) (match_dup 8)))]) |
(set (pc) |
(if_then_else (gtu (reg:CCL2 CC_REGNUM) (const_int 0)) |
(pc) |
(label_ref (match_dup 9)))) |
(parallel |
[(set (match_dup 3) (plus:SI (match_dup 3) (const_int -1))) |
(clobber (reg:CC CC_REGNUM))]) |
(match_dup 9)] |
"operands[3] = operand_subword (operands[0], 0, 0, DImode); |
operands[4] = operand_subword (operands[1], 0, 0, DImode); |
operands[5] = operand_subword (operands[2], 0, 0, DImode); |
operands[6] = operand_subword (operands[0], 1, 0, DImode); |
operands[7] = operand_subword (operands[1], 1, 0, DImode); |
operands[8] = operand_subword (operands[2], 1, 0, DImode); |
operands[9] = gen_label_rtx ();") |
|
; |
; subsi3 instruction pattern(s). |
; |
|
(define_expand "subsi3" |
[(parallel |
[(set (match_operand:SI 0 "register_operand" "") |
(minus:SI (match_operand:SI 1 "register_operand" "") |
(match_operand:SI 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*subsi3_sign" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(minus:SI (match_operand:SI 1 "register_operand" "0,0") |
(sign_extend:SI (match_operand:HI 2 "memory_operand" "R,T")))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
sh\t%0,%2 |
shy\t%0,%2" |
[(set_attr "op_type" "RX,RXY")]) |
|
; |
; sub(di|si)3 instruction pattern(s). |
; |
|
; sr, s, sy, sgr, sg |
(define_insn "*sub<mode>3" |
[(set (match_operand:GPR 0 "register_operand" "=d,d,d") |
(minus:GPR (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T") ) ) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"@ |
s<g>r\t%0,%2 |
s<g>\t%0,%2 |
s<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_borrow_cc" |
[(set (reg CC_REGNUM) |
(compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(match_dup 1))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d") |
(minus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCL2mode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_borrow_cconly" |
[(set (reg CC_REGNUM) |
(compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(match_dup 1))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCL2mode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_cc" |
[(set (reg CC_REGNUM) |
(compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d") |
(minus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCLmode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_cc2" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T"))) |
(set (match_operand:GPR 0 "register_operand" "=d,d,d") |
(minus:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCL3mode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_cconly" |
[(set (reg CC_REGNUM) |
(compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T")) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCLmode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; slr, sl, sly, slgr, slg |
(define_insn "*sub<mode>3_cconly2" |
[(set (reg CC_REGNUM) |
(compare (match_operand:GPR 1 "register_operand" "0,0,0") |
(match_operand:GPR 2 "general_operand" "d,R,T"))) |
(clobber (match_scratch:GPR 0 "=d,d,d"))] |
"s390_match_ccmode (insn, CCL3mode)" |
"@ |
sl<g>r\t%0,%2 |
sl<g>\t%0,%2 |
sl<y>\t%0,%2" |
[(set_attr "op_type" "RR<E>,RX<Y>,RXY")]) |
|
; |
; sub(df|sf)3 instruction pattern(s). |
; |
|
(define_expand "sub<mode>3" |
[(parallel |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(minus:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,R"))) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_HARD_FLOAT" |
"") |
|
; sxbr, sdbr, sebr, sxb, sdb, seb |
(define_insn "*sub<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(minus:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
s<xde>br\t%0,%2 |
s<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; sxbr, sdbr, sebr, sxb, sdb, seb |
(define_insn "*sub<mode>3_cc" |
[(set (reg CC_REGNUM) |
(compare (minus:FPR (match_operand:FPR 1 "nonimmediate_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")) |
(match_operand:FPR 3 "const0_operand" ""))) |
(set (match_operand:FPR 0 "register_operand" "=f,f") |
(minus:FPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
s<xde>br\t%0,%2 |
s<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; sxbr, sdbr, sebr, sxb, sdb, seb |
(define_insn "*sub<mode>3_cconly" |
[(set (reg CC_REGNUM) |
(compare (minus:FPR (match_operand:FPR 1 "nonimmediate_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")) |
(match_operand:FPR 3 "const0_operand" ""))) |
(clobber (match_scratch:FPR 0 "=f,f"))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
s<xde>br\t%0,%2 |
s<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsimp<mode>")]) |
|
; sxr, sdr, ser, sx, sd, se |
(define_insn "*sub<mode>3_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(minus:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
s<xde>r\t%0,%2 |
s<xde>\t%0,%2" |
[(set_attr "op_type" "<RRe>,<RXe>") |
(set_attr "type" "fsimp<mode>")]) |
|
|
;; |
;;- Conditional add/subtract instructions. |
;; |
|
; |
; add(di|si)cc instruction pattern(s). |
; |
|
; alcr, alc, alcgr, alcg |
(define_insn "*add<mode>3_alc_cc" |
[(set (reg CC_REGNUM) |
(compare |
(plus:GPR (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:GPR 2 "general_operand" "d,m")) |
(match_operand:GPR 3 "s390_alc_comparison" "")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d,d") |
(plus:GPR (plus:GPR (match_dup 1) (match_dup 2)) (match_dup 3)))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_CPU_ZARCH" |
"@ |
alc<g>r\t%0,%2 |
alc<g>\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; alcr, alc, alcgr, alcg |
(define_insn "*add<mode>3_alc" |
[(set (match_operand:GPR 0 "register_operand" "=d,d") |
(plus:GPR (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:GPR 2 "general_operand" "d,m")) |
(match_operand:GPR 3 "s390_alc_comparison" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_CPU_ZARCH" |
"@ |
alc<g>r\t%0,%2 |
alc<g>\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; slbr, slb, slbgr, slbg |
(define_insn "*sub<mode>3_slb_cc" |
[(set (reg CC_REGNUM) |
(compare |
(minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") |
(match_operand:GPR 2 "general_operand" "d,m")) |
(match_operand:GPR 3 "s390_slb_comparison" "")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d,d") |
(minus:GPR (minus:GPR (match_dup 1) (match_dup 2)) (match_dup 3)))] |
"s390_match_ccmode (insn, CCLmode) && TARGET_CPU_ZARCH" |
"@ |
slb<g>r\t%0,%2 |
slb<g>\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
; slbr, slb, slbgr, slbg |
(define_insn "*sub<mode>3_slb" |
[(set (match_operand:GPR 0 "register_operand" "=d,d") |
(minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") |
(match_operand:GPR 2 "general_operand" "d,m")) |
(match_operand:GPR 3 "s390_slb_comparison" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_CPU_ZARCH" |
"@ |
slb<g>r\t%0,%2 |
slb<g>\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_expand "add<mode>cc" |
[(match_operand:GPR 0 "register_operand" "") |
(match_operand 1 "comparison_operator" "") |
(match_operand:GPR 2 "register_operand" "") |
(match_operand:GPR 3 "const_int_operand" "")] |
"TARGET_CPU_ZARCH" |
"if (!s390_expand_addcc (GET_CODE (operands[1]), |
s390_compare_op0, s390_compare_op1, |
operands[0], operands[2], |
operands[3])) FAIL; DONE;") |
|
; |
; scond instruction pattern(s). |
; |
|
(define_insn_and_split "*scond<mode>" |
[(set (match_operand:GPR 0 "register_operand" "=&d") |
(match_operand:GPR 1 "s390_alc_comparison" "")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (const_int 0)) |
(parallel |
[(set (match_dup 0) (plus:GPR (plus:GPR (match_dup 0) (match_dup 0)) |
(match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"") |
|
(define_insn_and_split "*scond<mode>_neg" |
[(set (match_operand:GPR 0 "register_operand" "=&d") |
(match_operand:GPR 1 "s390_slb_comparison" "")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_CPU_ZARCH" |
"#" |
"&& reload_completed" |
[(set (match_dup 0) (const_int 0)) |
(parallel |
[(set (match_dup 0) (minus:GPR (minus:GPR (match_dup 0) (match_dup 0)) |
(match_dup 1))) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_dup 0) (neg:GPR (match_dup 0))) |
(clobber (reg:CC CC_REGNUM))])] |
"") |
|
|
(define_expand "s<code>" |
[(set (match_operand:SI 0 "register_operand" "") |
(SCOND (match_dup 0) |
(match_dup 0)))] |
"TARGET_CPU_ZARCH" |
"if (!s390_expand_addcc (<CODE>, s390_compare_op0, s390_compare_op1, |
operands[0], const0_rtx, const1_rtx)) FAIL; DONE;") |
|
(define_expand "seq" |
[(parallel |
[(set (match_operand:SI 0 "register_operand" "=d") |
(match_dup 1)) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_dup 0) (xor:SI (match_dup 0) (const_int 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
{ |
if (!s390_compare_emitted || GET_MODE (s390_compare_emitted) != CCZ1mode) |
FAIL; |
operands[1] = s390_emit_compare (NE, s390_compare_op0, s390_compare_op1); |
PUT_MODE (operands[1], SImode); |
}) |
|
(define_insn_and_split "*sne" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(ne:SI (match_operand:CCZ1 1 "register_operand" "0") |
(const_int 0))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"#" |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 28))) |
(clobber (reg:CC CC_REGNUM))])]) |
|
|
;; |
;;- Multiply instructions. |
;; |
|
; |
; muldi3 instruction pattern(s). |
; |
|
(define_insn "*muldi3_sign" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(mult:DI (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "d,m")) |
(match_operand:DI 1 "register_operand" "0,0")))] |
"TARGET_64BIT" |
"@ |
msgfr\t%0,%2 |
msgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "imuldi")]) |
|
(define_insn "muldi3" |
[(set (match_operand:DI 0 "register_operand" "=d,d,d") |
(mult:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:DI 2 "general_operand" "d,K,m")))] |
"TARGET_64BIT" |
"@ |
msgr\t%0,%2 |
mghi\t%0,%h2 |
msg\t%0,%2" |
[(set_attr "op_type" "RRE,RI,RXY") |
(set_attr "type" "imuldi")]) |
|
; |
; mulsi3 instruction pattern(s). |
; |
|
(define_insn "*mulsi3_sign" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(mult:SI (sign_extend:SI (match_operand:HI 2 "memory_operand" "R")) |
(match_operand:SI 1 "register_operand" "0")))] |
"" |
"mh\t%0,%2" |
[(set_attr "op_type" "RX") |
(set_attr "type" "imulhi")]) |
|
(define_insn "mulsi3" |
[(set (match_operand:SI 0 "register_operand" "=d,d,d,d") |
(mult:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "d,K,R,T")))] |
"" |
"@ |
msr\t%0,%2 |
mhi\t%0,%h2 |
ms\t%0,%2 |
msy\t%0,%2" |
[(set_attr "op_type" "RRE,RI,RX,RXY") |
(set_attr "type" "imulsi,imulhi,imulsi,imulsi")]) |
|
; |
; mulsidi3 instruction pattern(s). |
; |
|
(define_insn "mulsidi3" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(mult:DI (sign_extend:DI |
(match_operand:SI 1 "register_operand" "%0,0")) |
(sign_extend:DI |
(match_operand:SI 2 "nonimmediate_operand" "d,R"))))] |
"!TARGET_64BIT" |
"@ |
mr\t%0,%2 |
m\t%0,%2" |
[(set_attr "op_type" "RR,RX") |
(set_attr "type" "imulsi")]) |
|
; |
; umulsidi3 instruction pattern(s). |
; |
|
(define_insn "umulsidi3" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(mult:DI (zero_extend:DI |
(match_operand:SI 1 "register_operand" "%0,0")) |
(zero_extend:DI |
(match_operand:SI 2 "nonimmediate_operand" "d,m"))))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH" |
"@ |
mlr\t%0,%2 |
ml\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "imulsi")]) |
|
; |
; mul(df|sf)3 instruction pattern(s). |
; |
|
(define_expand "mul<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(mult:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT" |
"") |
|
; mxbr mdbr, meebr, mxb, mxb, meeb |
(define_insn "*mul<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(mult:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
m<xdee>br\t%0,%2 |
m<xdee>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fmul<mode>")]) |
|
; mxr, mdr, mer, mx, md, me |
(define_insn "*mul<mode>3_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(mult:FPR (match_operand:FPR 1 "nonimmediate_operand" "%0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
m<xde>r\t%0,%2 |
m<xde>\t%0,%2" |
[(set_attr "op_type" "<RRe>,<RXe>") |
(set_attr "type" "fmul<mode>")]) |
|
; maxbr, madbr, maebr, maxb, madb, maeb |
(define_insn "*fmadd<mode>" |
[(set (match_operand:DSF 0 "register_operand" "=f,f") |
(plus:DSF (mult:DSF (match_operand:DSF 1 "register_operand" "%f,f") |
(match_operand:DSF 2 "nonimmediate_operand" "f,R")) |
(match_operand:DSF 3 "register_operand" "0,0")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD" |
"@ |
ma<xde>br\t%0,%1,%2 |
ma<xde>b\t%0,%1,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fmul<mode>")]) |
|
; msxbr, msdbr, msebr, msxb, msdb, mseb |
(define_insn "*fmsub<mode>" |
[(set (match_operand:DSF 0 "register_operand" "=f,f") |
(minus:DSF (mult:DSF (match_operand:DSF 1 "register_operand" "f,f") |
(match_operand:DSF 2 "nonimmediate_operand" "f,R")) |
(match_operand:DSF 3 "register_operand" "0,0")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD" |
"@ |
ms<xde>br\t%0,%1,%2 |
ms<xde>b\t%0,%1,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fmul<mode>")]) |
|
;; |
;;- Divide and modulo instructions. |
;; |
|
; |
; divmoddi4 instruction pattern(s). |
; |
|
(define_expand "divmoddi4" |
[(parallel [(set (match_operand:DI 0 "general_operand" "") |
(div:DI (match_operand:DI 1 "register_operand" "") |
(match_operand:DI 2 "general_operand" ""))) |
(set (match_operand:DI 3 "general_operand" "") |
(mod:DI (match_dup 1) (match_dup 2)))]) |
(clobber (match_dup 4))] |
"TARGET_64BIT" |
{ |
rtx insn, div_equal, mod_equal; |
|
div_equal = gen_rtx_DIV (DImode, operands[1], operands[2]); |
mod_equal = gen_rtx_MOD (DImode, operands[1], operands[2]); |
|
operands[4] = gen_reg_rtx(TImode); |
emit_insn (gen_divmodtidi3 (operands[4], operands[1], operands[2])); |
|
insn = emit_move_insn (operands[0], gen_lowpart (DImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, div_equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[3], gen_highpart (DImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, mod_equal, REG_NOTES (insn)); |
|
DONE; |
}) |
|
(define_insn "divmodtidi3" |
[(set (match_operand:TI 0 "register_operand" "=d,d") |
(ior:TI |
(ashift:TI |
(zero_extend:TI |
(mod:DI (match_operand:DI 1 "register_operand" "0,0") |
(match_operand:DI 2 "general_operand" "d,m"))) |
(const_int 64)) |
(zero_extend:TI (div:DI (match_dup 1) (match_dup 2)))))] |
"TARGET_64BIT" |
"@ |
dsgr\t%0,%2 |
dsg\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "idiv")]) |
|
(define_insn "divmodtisi3" |
[(set (match_operand:TI 0 "register_operand" "=d,d") |
(ior:TI |
(ashift:TI |
(zero_extend:TI |
(mod:DI (match_operand:DI 1 "register_operand" "0,0") |
(sign_extend:DI |
(match_operand:SI 2 "nonimmediate_operand" "d,m")))) |
(const_int 64)) |
(zero_extend:TI |
(div:DI (match_dup 1) (sign_extend:DI (match_dup 2))))))] |
"TARGET_64BIT" |
"@ |
dsgfr\t%0,%2 |
dsgf\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "idiv")]) |
|
; |
; udivmoddi4 instruction pattern(s). |
; |
|
(define_expand "udivmoddi4" |
[(parallel [(set (match_operand:DI 0 "general_operand" "") |
(udiv:DI (match_operand:DI 1 "general_operand" "") |
(match_operand:DI 2 "nonimmediate_operand" ""))) |
(set (match_operand:DI 3 "general_operand" "") |
(umod:DI (match_dup 1) (match_dup 2)))]) |
(clobber (match_dup 4))] |
"TARGET_64BIT" |
{ |
rtx insn, div_equal, mod_equal, equal; |
|
div_equal = gen_rtx_UDIV (DImode, operands[1], operands[2]); |
mod_equal = gen_rtx_UMOD (DImode, operands[1], operands[2]); |
equal = gen_rtx_IOR (TImode, |
gen_rtx_ASHIFT (TImode, |
gen_rtx_ZERO_EXTEND (TImode, mod_equal), |
GEN_INT (64)), |
gen_rtx_ZERO_EXTEND (TImode, div_equal)); |
|
operands[4] = gen_reg_rtx(TImode); |
emit_insn (gen_rtx_CLOBBER (VOIDmode, operands[4])); |
emit_move_insn (gen_lowpart (DImode, operands[4]), operands[1]); |
emit_move_insn (gen_highpart (DImode, operands[4]), const0_rtx); |
insn = emit_insn (gen_udivmodtidi3 (operands[4], operands[4], operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], gen_lowpart (DImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, div_equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[3], gen_highpart (DImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, mod_equal, REG_NOTES (insn)); |
|
DONE; |
}) |
|
(define_insn "udivmodtidi3" |
[(set (match_operand:TI 0 "register_operand" "=d,d") |
(ior:TI |
(ashift:TI |
(zero_extend:TI |
(truncate:DI |
(umod:TI (match_operand:TI 1 "register_operand" "0,0") |
(zero_extend:TI |
(match_operand:DI 2 "nonimmediate_operand" "d,m"))))) |
(const_int 64)) |
(zero_extend:TI |
(truncate:DI |
(udiv:TI (match_dup 1) (zero_extend:TI (match_dup 2)))))))] |
"TARGET_64BIT" |
"@ |
dlgr\t%0,%2 |
dlg\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "idiv")]) |
|
; |
; divmodsi4 instruction pattern(s). |
; |
|
(define_expand "divmodsi4" |
[(parallel [(set (match_operand:SI 0 "general_operand" "") |
(div:SI (match_operand:SI 1 "general_operand" "") |
(match_operand:SI 2 "nonimmediate_operand" ""))) |
(set (match_operand:SI 3 "general_operand" "") |
(mod:SI (match_dup 1) (match_dup 2)))]) |
(clobber (match_dup 4))] |
"!TARGET_64BIT" |
{ |
rtx insn, div_equal, mod_equal, equal; |
|
div_equal = gen_rtx_DIV (SImode, operands[1], operands[2]); |
mod_equal = gen_rtx_MOD (SImode, operands[1], operands[2]); |
equal = gen_rtx_IOR (DImode, |
gen_rtx_ASHIFT (DImode, |
gen_rtx_ZERO_EXTEND (DImode, mod_equal), |
GEN_INT (32)), |
gen_rtx_ZERO_EXTEND (DImode, div_equal)); |
|
operands[4] = gen_reg_rtx(DImode); |
emit_insn (gen_extendsidi2 (operands[4], operands[1])); |
insn = emit_insn (gen_divmoddisi3 (operands[4], operands[4], operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], gen_lowpart (SImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, div_equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[3], gen_highpart (SImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, mod_equal, REG_NOTES (insn)); |
|
DONE; |
}) |
|
(define_insn "divmoddisi3" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(ior:DI |
(ashift:DI |
(zero_extend:DI |
(truncate:SI |
(mod:DI (match_operand:DI 1 "register_operand" "0,0") |
(sign_extend:DI |
(match_operand:SI 2 "nonimmediate_operand" "d,R"))))) |
(const_int 32)) |
(zero_extend:DI |
(truncate:SI |
(div:DI (match_dup 1) (sign_extend:DI (match_dup 2)))))))] |
"!TARGET_64BIT" |
"@ |
dr\t%0,%2 |
d\t%0,%2" |
[(set_attr "op_type" "RR,RX") |
(set_attr "type" "idiv")]) |
|
; |
; udivsi3 and umodsi3 instruction pattern(s). |
; |
|
(define_expand "udivmodsi4" |
[(parallel [(set (match_operand:SI 0 "general_operand" "") |
(udiv:SI (match_operand:SI 1 "general_operand" "") |
(match_operand:SI 2 "nonimmediate_operand" ""))) |
(set (match_operand:SI 3 "general_operand" "") |
(umod:SI (match_dup 1) (match_dup 2)))]) |
(clobber (match_dup 4))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH" |
{ |
rtx insn, div_equal, mod_equal, equal; |
|
div_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); |
mod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); |
equal = gen_rtx_IOR (DImode, |
gen_rtx_ASHIFT (DImode, |
gen_rtx_ZERO_EXTEND (DImode, mod_equal), |
GEN_INT (32)), |
gen_rtx_ZERO_EXTEND (DImode, div_equal)); |
|
operands[4] = gen_reg_rtx(DImode); |
emit_insn (gen_rtx_CLOBBER (VOIDmode, operands[4])); |
emit_move_insn (gen_lowpart (SImode, operands[4]), operands[1]); |
emit_move_insn (gen_highpart (SImode, operands[4]), const0_rtx); |
insn = emit_insn (gen_udivmoddisi3 (operands[4], operands[4], operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], gen_lowpart (SImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, div_equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[3], gen_highpart (SImode, operands[4])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, mod_equal, REG_NOTES (insn)); |
|
DONE; |
}) |
|
(define_insn "udivmoddisi3" |
[(set (match_operand:DI 0 "register_operand" "=d,d") |
(ior:DI |
(ashift:DI |
(zero_extend:DI |
(truncate:SI |
(umod:DI (match_operand:DI 1 "register_operand" "0,0") |
(zero_extend:DI |
(match_operand:SI 2 "nonimmediate_operand" "d,m"))))) |
(const_int 32)) |
(zero_extend:DI |
(truncate:SI |
(udiv:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))))] |
"!TARGET_64BIT && TARGET_CPU_ZARCH" |
"@ |
dlr\t%0,%2 |
dl\t%0,%2" |
[(set_attr "op_type" "RRE,RXY") |
(set_attr "type" "idiv")]) |
|
(define_expand "udivsi3" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(udiv:SI (match_operand:SI 1 "general_operand" "") |
(match_operand:SI 2 "general_operand" ""))) |
(clobber (match_dup 3))] |
"!TARGET_64BIT && !TARGET_CPU_ZARCH" |
{ |
rtx insn, udiv_equal, umod_equal, equal; |
|
udiv_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); |
umod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); |
equal = gen_rtx_IOR (DImode, |
gen_rtx_ASHIFT (DImode, |
gen_rtx_ZERO_EXTEND (DImode, umod_equal), |
GEN_INT (32)), |
gen_rtx_ZERO_EXTEND (DImode, udiv_equal)); |
|
operands[3] = gen_reg_rtx (DImode); |
|
if (CONSTANT_P (operands[2])) |
{ |
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) |
{ |
rtx label1 = gen_label_rtx (); |
|
operands[1] = make_safe_from (operands[1], operands[0]); |
emit_move_insn (operands[0], const0_rtx); |
emit_insn (gen_cmpsi (operands[1], operands[2])); |
emit_jump_insn (gen_bltu (label1)); |
emit_move_insn (operands[0], const1_rtx); |
emit_label (label1); |
} |
else |
{ |
operands[2] = force_reg (SImode, operands[2]); |
operands[2] = make_safe_from (operands[2], operands[0]); |
|
emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); |
insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], |
operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], |
gen_lowpart (SImode, operands[3])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, |
udiv_equal, REG_NOTES (insn)); |
} |
} |
else |
{ |
rtx label1 = gen_label_rtx (); |
rtx label2 = gen_label_rtx (); |
rtx label3 = gen_label_rtx (); |
|
operands[1] = force_reg (SImode, operands[1]); |
operands[1] = make_safe_from (operands[1], operands[0]); |
operands[2] = force_reg (SImode, operands[2]); |
operands[2] = make_safe_from (operands[2], operands[0]); |
|
emit_move_insn (operands[0], const0_rtx); |
emit_insn (gen_cmpsi (operands[2], operands[1])); |
emit_jump_insn (gen_bgtu (label3)); |
emit_insn (gen_cmpsi (operands[2], const0_rtx)); |
emit_jump_insn (gen_blt (label2)); |
emit_insn (gen_cmpsi (operands[2], const1_rtx)); |
emit_jump_insn (gen_beq (label1)); |
emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); |
insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], |
operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], |
gen_lowpart (SImode, operands[3])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, |
udiv_equal, REG_NOTES (insn)); |
emit_jump (label3); |
emit_label (label1); |
emit_move_insn (operands[0], operands[1]); |
emit_jump (label3); |
emit_label (label2); |
emit_move_insn (operands[0], const1_rtx); |
emit_label (label3); |
} |
emit_move_insn (operands[0], operands[0]); |
DONE; |
}) |
|
(define_expand "umodsi3" |
[(set (match_operand:SI 0 "register_operand" "=d") |
(umod:SI (match_operand:SI 1 "nonimmediate_operand" "") |
(match_operand:SI 2 "nonimmediate_operand" ""))) |
(clobber (match_dup 3))] |
"!TARGET_64BIT && !TARGET_CPU_ZARCH" |
{ |
rtx insn, udiv_equal, umod_equal, equal; |
|
udiv_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); |
umod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); |
equal = gen_rtx_IOR (DImode, |
gen_rtx_ASHIFT (DImode, |
gen_rtx_ZERO_EXTEND (DImode, umod_equal), |
GEN_INT (32)), |
gen_rtx_ZERO_EXTEND (DImode, udiv_equal)); |
|
operands[3] = gen_reg_rtx (DImode); |
|
if (CONSTANT_P (operands[2])) |
{ |
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) <= 0) |
{ |
rtx label1 = gen_label_rtx (); |
|
operands[1] = make_safe_from (operands[1], operands[0]); |
emit_move_insn (operands[0], operands[1]); |
emit_insn (gen_cmpsi (operands[0], operands[2])); |
emit_jump_insn (gen_bltu (label1)); |
emit_insn (gen_abssi2 (operands[0], operands[2])); |
emit_insn (gen_addsi3 (operands[0], operands[0], operands[1])); |
emit_label (label1); |
} |
else |
{ |
operands[2] = force_reg (SImode, operands[2]); |
operands[2] = make_safe_from (operands[2], operands[0]); |
|
emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); |
insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], |
operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], |
gen_highpart (SImode, operands[3])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, |
umod_equal, REG_NOTES (insn)); |
} |
} |
else |
{ |
rtx label1 = gen_label_rtx (); |
rtx label2 = gen_label_rtx (); |
rtx label3 = gen_label_rtx (); |
|
operands[1] = force_reg (SImode, operands[1]); |
operands[1] = make_safe_from (operands[1], operands[0]); |
operands[2] = force_reg (SImode, operands[2]); |
operands[2] = make_safe_from (operands[2], operands[0]); |
|
emit_move_insn(operands[0], operands[1]); |
emit_insn (gen_cmpsi (operands[2], operands[1])); |
emit_jump_insn (gen_bgtu (label3)); |
emit_insn (gen_cmpsi (operands[2], const0_rtx)); |
emit_jump_insn (gen_blt (label2)); |
emit_insn (gen_cmpsi (operands[2], const1_rtx)); |
emit_jump_insn (gen_beq (label1)); |
emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); |
insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], |
operands[2])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, equal, REG_NOTES (insn)); |
|
insn = emit_move_insn (operands[0], |
gen_highpart (SImode, operands[3])); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, |
umod_equal, REG_NOTES (insn)); |
emit_jump (label3); |
emit_label (label1); |
emit_move_insn (operands[0], const0_rtx); |
emit_jump (label3); |
emit_label (label2); |
emit_insn (gen_subsi3 (operands[0], operands[0], operands[2])); |
emit_label (label3); |
} |
DONE; |
}) |
|
; |
; div(df|sf)3 instruction pattern(s). |
; |
|
(define_expand "div<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(div:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT" |
"") |
|
; dxbr, ddbr, debr, dxb, ddb, deb |
(define_insn "*div<mode>3" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(div:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
d<xde>br\t%0,%2 |
d<xde>b\t%0,%2" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fdiv<mode>")]) |
|
; dxr, ddr, der, dx, dd, de |
(define_insn "*div<mode>3_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(div:FPR (match_operand:FPR 1 "register_operand" "0,0") |
(match_operand:FPR 2 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"@ |
d<xde>r\t%0,%2 |
d<xde>\t%0,%2" |
[(set_attr "op_type" "<RRe>,<RXe>") |
(set_attr "type" "fdiv<mode>")]) |
|
|
;; |
;;- And instructions. |
;; |
|
(define_expand "and<mode>3" |
[(set (match_operand:INT 0 "nonimmediate_operand" "") |
(and:INT (match_operand:INT 1 "nonimmediate_operand" "") |
(match_operand:INT 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"s390_expand_logical_operator (AND, <MODE>mode, operands); DONE;") |
|
; |
; anddi3 instruction pattern(s). |
; |
|
(define_insn "*anddi3_cc" |
[(set (reg CC_REGNUM) |
(compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d,d") |
(and:DI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT" |
"@ |
ngr\t%0,%2 |
ng\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*anddi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d,d"))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT |
/* Do not steal TM patterns. */ |
&& s390_single_part (operands[2], DImode, HImode, 0) < 0" |
"@ |
ngr\t%0,%2 |
ng\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*anddi3_extimm" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,d,d,d,d,AQ,Q") |
(and:DI (match_operand:DI 1 "nonimmediate_operand" |
"%d,o,0,0,0,0,0,0,0,0,0,0") |
(match_operand:DI 2 "general_operand" |
"M,M,N0HDF,N1HDF,N2HDF,N3HDF,N0SDF,N1SDF,d,m,NxQDF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
# |
# |
nihh\t%0,%j2 |
nihl\t%0,%j2 |
nilh\t%0,%j2 |
nill\t%0,%j2 |
nihf\t%0,%m2 |
nilf\t%0,%m2 |
ngr\t%0,%2 |
ng\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RRE,RXE,RI,RI,RI,RI,RIL,RIL,RRE,RXY,SI,SS")]) |
|
(define_insn "*anddi3" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,d,d,AQ,Q") |
(and:DI (match_operand:DI 1 "nonimmediate_operand" |
"%d,o,0,0,0,0,0,0,0,0") |
(match_operand:DI 2 "general_operand" |
"M,M,N0HDF,N1HDF,N2HDF,N3HDF,d,m,NxQDF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && !TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
# |
# |
nihh\t%0,%j2 |
nihl\t%0,%j2 |
nilh\t%0,%j2 |
nill\t%0,%j2 |
ngr\t%0,%2 |
ng\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RRE,RXE,RI,RI,RI,RI,RRE,RXY,SI,SS")]) |
|
(define_split |
[(set (match_operand:DI 0 "s_operand" "") |
(and:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") |
|
|
; |
; andsi3 instruction pattern(s). |
; |
|
(define_insn "*andsi3_cc" |
[(set (reg CC_REGNUM) |
(compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(set (match_operand:SI 0 "register_operand" "=d,d,d,d") |
(and:SI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
nilf\t%0,%o2 |
nr\t%0,%2 |
n\t%0,%2 |
ny\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*andsi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(clobber (match_scratch:SI 0 "=d,d,d,d"))] |
"s390_match_ccmode(insn, CCTmode) |
/* Do not steal TM patterns. */ |
&& s390_single_part (operands[2], SImode, HImode, 0) < 0" |
"@ |
nilf\t%0,%o2 |
nr\t%0,%2 |
n\t%0,%2 |
ny\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*andsi3_zarch" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,d,d,AQ,Q") |
(and:SI (match_operand:SI 1 "nonimmediate_operand" |
"%d,o,0,0,0,0,0,0,0,0") |
(match_operand:SI 2 "general_operand" |
"M,M,N0HSF,N1HSF,Os,d,R,T,NxQSF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
# |
# |
nilh\t%0,%j2 |
nill\t%0,%j2 |
nilf\t%0,%o2 |
nr\t%0,%2 |
n\t%0,%2 |
ny\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RRE,RXE,RI,RI,RIL,RR,RX,RXY,SI,SS")]) |
|
(define_insn "*andsi3_esa" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "d,R,NxQSF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
nr\t%0,%2 |
n\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RR,RX,SI,SS")]) |
|
(define_split |
[(set (match_operand:SI 0 "s_operand" "") |
(and:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") |
|
; |
; andhi3 instruction pattern(s). |
; |
|
(define_insn "*andhi3_zarch" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:HI 2 "general_operand" "d,n,NxQHF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
nr\t%0,%2 |
nill\t%0,%x2 |
# |
#" |
[(set_attr "op_type" "RR,RI,SI,SS")]) |
|
(define_insn "*andhi3_esa" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,AQ,Q") |
(and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:HI 2 "general_operand" "d,NxQHF,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
nr\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RR,SI,SS")]) |
|
(define_split |
[(set (match_operand:HI 0 "s_operand" "") |
(and:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") |
|
; |
; andqi3 instruction pattern(s). |
; |
|
(define_insn "*andqi3_zarch" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,Q,S,Q") |
(and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:QI 2 "general_operand" "d,n,n,n,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
nr\t%0,%2 |
nill\t%0,%b2 |
ni\t%S0,%b2 |
niy\t%S0,%b2 |
#" |
[(set_attr "op_type" "RR,RI,SI,SIY,SS")]) |
|
(define_insn "*andqi3_esa" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,Q,Q") |
(and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:QI 2 "general_operand" "d,n,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
nr\t%0,%2 |
ni\t%S0,%b2 |
#" |
[(set_attr "op_type" "RR,SI,SS")]) |
|
; |
; Block and (NC) patterns. |
; |
|
(define_insn "*nc" |
[(set (match_operand:BLK 0 "memory_operand" "=Q") |
(and:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" "Q"))) |
(use (match_operand 2 "const_int_operand" "n")) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" |
"nc\t%O0(%2,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_split |
[(set (match_operand 0 "memory_operand" "") |
(and (match_dup 0) |
(match_operand 1 "memory_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed |
&& GET_MODE (operands[0]) == GET_MODE (operands[1]) |
&& GET_MODE_SIZE (GET_MODE (operands[0])) > 0" |
[(parallel |
[(set (match_dup 0) (and:BLK (match_dup 0) (match_dup 1))) |
(use (match_dup 2)) |
(clobber (reg:CC CC_REGNUM))])] |
{ |
operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); |
operands[0] = adjust_address (operands[0], BLKmode, 0); |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
}) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(and:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_operand:BLK 3 "memory_operand" "") |
(and:BLK (match_dup 3) |
(match_operand:BLK 4 "memory_operand" ""))) |
(use (match_operand 5 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_offset_p (operands[0], operands[3], operands[2]) |
&& s390_offset_p (operands[1], operands[4], operands[2]) |
&& !s390_overlap_p (operands[0], operands[1], |
INTVAL (operands[2]) + INTVAL (operands[5])) |
&& INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" |
[(parallel |
[(set (match_dup 6) (and:BLK (match_dup 6) (match_dup 7))) |
(use (match_dup 8)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); |
operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); |
operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") |
|
|
;; |
;;- Bit set (inclusive or) instructions. |
;; |
|
(define_expand "ior<mode>3" |
[(set (match_operand:INT 0 "nonimmediate_operand" "") |
(ior:INT (match_operand:INT 1 "nonimmediate_operand" "") |
(match_operand:INT 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"s390_expand_logical_operator (IOR, <MODE>mode, operands); DONE;") |
|
; |
; iordi3 instruction pattern(s). |
; |
|
(define_insn "*iordi3_cc" |
[(set (reg CC_REGNUM) |
(compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d,d") |
(ior:DI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT" |
"@ |
ogr\t%0,%2 |
og\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*iordi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d,d"))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT" |
"@ |
ogr\t%0,%2 |
og\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*iordi3_extimm" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,d,d,AQ,Q") |
(ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,0,0,0,0,0,0,0") |
(match_operand:DI 2 "general_operand" |
"N0HD0,N1HD0,N2HD0,N3HD0,N0SD0,N1SD0,d,m,NxQD0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
oihh\t%0,%i2 |
oihl\t%0,%i2 |
oilh\t%0,%i2 |
oill\t%0,%i2 |
oihf\t%0,%k2 |
oilf\t%0,%k2 |
ogr\t%0,%2 |
og\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RI,RI,RI,RI,RIL,RIL,RRE,RXY,SI,SS")]) |
|
(define_insn "*iordi3" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,AQ,Q") |
(ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,0,0,0,0,0") |
(match_operand:DI 2 "general_operand" |
"N0HD0,N1HD0,N2HD0,N3HD0,d,m,NxQD0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && !TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
oihh\t%0,%i2 |
oihl\t%0,%i2 |
oilh\t%0,%i2 |
oill\t%0,%i2 |
ogr\t%0,%2 |
og\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RI,RI,RI,RI,RRE,RXY,SI,SS")]) |
|
(define_split |
[(set (match_operand:DI 0 "s_operand" "") |
(ior:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") |
|
; |
; iorsi3 instruction pattern(s). |
; |
|
(define_insn "*iorsi3_cc" |
[(set (reg CC_REGNUM) |
(compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(set (match_operand:SI 0 "register_operand" "=d,d,d,d") |
(ior:SI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
oilf\t%0,%o2 |
or\t%0,%2 |
o\t%0,%2 |
oy\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*iorsi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(clobber (match_scratch:SI 0 "=d,d,d,d"))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
oilf\t%0,%o2 |
or\t%0,%2 |
o\t%0,%2 |
oy\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*iorsi3_zarch" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,AQ,Q") |
(ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0,0,0,0,0") |
(match_operand:SI 2 "general_operand" "N0HS0,N1HS0,Os,d,R,T,NxQS0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
oilh\t%0,%i2 |
oill\t%0,%i2 |
oilf\t%0,%o2 |
or\t%0,%2 |
o\t%0,%2 |
oy\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RI,RI,RIL,RR,RX,RXY,SI,SS")]) |
|
(define_insn "*iorsi3_esa" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "d,R,NxQS0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
or\t%0,%2 |
o\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RR,RX,SI,SS")]) |
|
(define_split |
[(set (match_operand:SI 0 "s_operand" "") |
(ior:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") |
|
; |
; iorhi3 instruction pattern(s). |
; |
|
(define_insn "*iorhi3_zarch" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:HI 2 "general_operand" "d,n,NxQH0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
or\t%0,%2 |
oill\t%0,%x2 |
# |
#" |
[(set_attr "op_type" "RR,RI,SI,SS")]) |
|
(define_insn "*iorhi3_esa" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,AQ,Q") |
(ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:HI 2 "general_operand" "d,NxQH0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
or\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RR,SI,SS")]) |
|
(define_split |
[(set (match_operand:HI 0 "s_operand" "") |
(ior:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") |
|
; |
; iorqi3 instruction pattern(s). |
; |
|
(define_insn "*iorqi3_zarch" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,Q,S,Q") |
(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:QI 2 "general_operand" "d,n,n,n,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
or\t%0,%2 |
oill\t%0,%b2 |
oi\t%S0,%b2 |
oiy\t%S0,%b2 |
#" |
[(set_attr "op_type" "RR,RI,SI,SIY,SS")]) |
|
(define_insn "*iorqi3_esa" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,Q,Q") |
(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") |
(match_operand:QI 2 "general_operand" "d,n,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" |
"@ |
or\t%0,%2 |
oi\t%S0,%b2 |
#" |
[(set_attr "op_type" "RR,SI,SS")]) |
|
; |
; Block inclusive or (OC) patterns. |
; |
|
(define_insn "*oc" |
[(set (match_operand:BLK 0 "memory_operand" "=Q") |
(ior:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" "Q"))) |
(use (match_operand 2 "const_int_operand" "n")) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" |
"oc\t%O0(%2,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_split |
[(set (match_operand 0 "memory_operand" "") |
(ior (match_dup 0) |
(match_operand 1 "memory_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed |
&& GET_MODE (operands[0]) == GET_MODE (operands[1]) |
&& GET_MODE_SIZE (GET_MODE (operands[0])) > 0" |
[(parallel |
[(set (match_dup 0) (ior:BLK (match_dup 0) (match_dup 1))) |
(use (match_dup 2)) |
(clobber (reg:CC CC_REGNUM))])] |
{ |
operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); |
operands[0] = adjust_address (operands[0], BLKmode, 0); |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
}) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(ior:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_operand:BLK 3 "memory_operand" "") |
(ior:BLK (match_dup 3) |
(match_operand:BLK 4 "memory_operand" ""))) |
(use (match_operand 5 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_offset_p (operands[0], operands[3], operands[2]) |
&& s390_offset_p (operands[1], operands[4], operands[2]) |
&& !s390_overlap_p (operands[0], operands[1], |
INTVAL (operands[2]) + INTVAL (operands[5])) |
&& INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" |
[(parallel |
[(set (match_dup 6) (ior:BLK (match_dup 6) (match_dup 7))) |
(use (match_dup 8)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); |
operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); |
operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") |
|
|
;; |
;;- Xor instructions. |
;; |
|
(define_expand "xor<mode>3" |
[(set (match_operand:INT 0 "nonimmediate_operand" "") |
(xor:INT (match_operand:INT 1 "nonimmediate_operand" "") |
(match_operand:INT 2 "general_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"s390_expand_logical_operator (XOR, <MODE>mode, operands); DONE;") |
|
; |
; xordi3 instruction pattern(s). |
; |
|
(define_insn "*xordi3_cc" |
[(set (reg CC_REGNUM) |
(compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d,d") |
(xor:DI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT" |
"@ |
xgr\t%0,%2 |
xg\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*xordi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") |
(match_operand:DI 2 "general_operand" "d,m")) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d,d"))] |
"s390_match_ccmode(insn, CCTmode) && TARGET_64BIT" |
"@ |
xgr\t%0,%2 |
xg\t%0,%2" |
[(set_attr "op_type" "RRE,RXY")]) |
|
(define_insn "*xordi3_extimm" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,AQ,Q") |
(xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,0,0,0") |
(match_operand:DI 2 "general_operand" "N0SD0,N1SD0,d,m,NxQD0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
xihf\t%0,%k2 |
xilf\t%0,%k2 |
xgr\t%0,%2 |
xg\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RIL,RIL,RRE,RXY,SI,SS")]) |
|
(define_insn "*xordi3" |
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:DI 2 "general_operand" "d,m,NxQD0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT && !TARGET_EXTIMM && s390_logical_operator_ok_p (operands)" |
"@ |
xgr\t%0,%2 |
xg\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RRE,RXY,SI,SS")]) |
|
(define_split |
[(set (match_operand:DI 0 "s_operand" "") |
(xor:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") |
|
; |
; xorsi3 instruction pattern(s). |
; |
|
(define_insn "*xorsi3_cc" |
[(set (reg CC_REGNUM) |
(compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(set (match_operand:SI 0 "register_operand" "=d,d,d,d") |
(xor:SI (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
xilf\t%0,%o2 |
xr\t%0,%2 |
x\t%0,%2 |
xy\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*xorsi3_cconly" |
[(set (reg CC_REGNUM) |
(compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T")) |
(const_int 0))) |
(clobber (match_scratch:SI 0 "=d,d,d,d"))] |
"s390_match_ccmode(insn, CCTmode)" |
"@ |
xilf\t%0,%o2 |
xr\t%0,%2 |
x\t%0,%2 |
xy\t%0,%2" |
[(set_attr "op_type" "RIL,RR,RX,RXY")]) |
|
(define_insn "*xorsi3" |
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,AQ,Q") |
(xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0,0,0") |
(match_operand:SI 2 "general_operand" "Os,d,R,T,NxQS0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"s390_logical_operator_ok_p (operands)" |
"@ |
xilf\t%0,%o2 |
xr\t%0,%2 |
x\t%0,%2 |
xy\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RIL,RR,RX,RXY,SI,SS")]) |
|
(define_split |
[(set (match_operand:SI 0 "s_operand" "") |
(xor:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") |
|
; |
; xorhi3 instruction pattern(s). |
; |
|
(define_insn "*xorhi3" |
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,AQ,Q") |
(xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0,0") |
(match_operand:HI 2 "general_operand" "Os,d,NxQH0,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"s390_logical_operator_ok_p (operands)" |
"@ |
xilf\t%0,%x2 |
xr\t%0,%2 |
# |
#" |
[(set_attr "op_type" "RIL,RR,SI,SS")]) |
|
(define_split |
[(set (match_operand:HI 0 "s_operand" "") |
(xor:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed" |
[(parallel |
[(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") |
|
; |
; xorqi3 instruction pattern(s). |
; |
|
(define_insn "*xorqi3" |
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,Q,S,Q") |
(xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0,0,0") |
(match_operand:QI 2 "general_operand" "Os,d,n,n,Q"))) |
(clobber (reg:CC CC_REGNUM))] |
"s390_logical_operator_ok_p (operands)" |
"@ |
xilf\t%0,%b2 |
xr\t%0,%2 |
xi\t%S0,%b2 |
xiy\t%S0,%b2 |
#" |
[(set_attr "op_type" "RIL,RR,SI,SIY,SS")]) |
|
; |
; Block exclusive or (XC) patterns. |
; |
|
(define_insn "*xc" |
[(set (match_operand:BLK 0 "memory_operand" "=Q") |
(xor:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" "Q"))) |
(use (match_operand 2 "const_int_operand" "n")) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" |
"xc\t%O0(%2,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_split |
[(set (match_operand 0 "memory_operand" "") |
(xor (match_dup 0) |
(match_operand 1 "memory_operand" ""))) |
(clobber (reg:CC CC_REGNUM))] |
"reload_completed |
&& GET_MODE (operands[0]) == GET_MODE (operands[1]) |
&& GET_MODE_SIZE (GET_MODE (operands[0])) > 0" |
[(parallel |
[(set (match_dup 0) (xor:BLK (match_dup 0) (match_dup 1))) |
(use (match_dup 2)) |
(clobber (reg:CC CC_REGNUM))])] |
{ |
operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); |
operands[0] = adjust_address (operands[0], BLKmode, 0); |
operands[1] = adjust_address (operands[1], BLKmode, 0); |
}) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(xor:BLK (match_dup 0) |
(match_operand:BLK 1 "memory_operand" ""))) |
(use (match_operand 2 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_operand:BLK 3 "memory_operand" "") |
(xor:BLK (match_dup 3) |
(match_operand:BLK 4 "memory_operand" ""))) |
(use (match_operand 5 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_offset_p (operands[0], operands[3], operands[2]) |
&& s390_offset_p (operands[1], operands[4], operands[2]) |
&& !s390_overlap_p (operands[0], operands[1], |
INTVAL (operands[2]) + INTVAL (operands[5])) |
&& INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" |
[(parallel |
[(set (match_dup 6) (xor:BLK (match_dup 6) (match_dup 7))) |
(use (match_dup 8)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); |
operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); |
operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") |
|
; |
; Block xor (XC) patterns with src == dest. |
; |
|
(define_insn "*xc_zero" |
[(set (match_operand:BLK 0 "memory_operand" "=Q") |
(const_int 0)) |
(use (match_operand 1 "const_int_operand" "n")) |
(clobber (reg:CC CC_REGNUM))] |
"INTVAL (operands[1]) >= 1 && INTVAL (operands[1]) <= 256" |
"xc\t%O0(%1,%R0),%S0" |
[(set_attr "op_type" "SS")]) |
|
(define_peephole2 |
[(parallel |
[(set (match_operand:BLK 0 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 1 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (match_operand:BLK 2 "memory_operand" "") |
(const_int 0)) |
(use (match_operand 3 "const_int_operand" "")) |
(clobber (reg:CC CC_REGNUM))])] |
"s390_offset_p (operands[0], operands[2], operands[1]) |
&& INTVAL (operands[1]) + INTVAL (operands[3]) <= 256" |
[(parallel |
[(set (match_dup 4) (const_int 0)) |
(use (match_dup 5)) |
(clobber (reg:CC CC_REGNUM))])] |
"operands[4] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); |
operands[5] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[3]));") |
|
|
;; |
;;- Negate instructions. |
;; |
|
; |
; neg(di|si)2 instruction pattern(s). |
; |
|
(define_expand "neg<mode>2" |
[(parallel |
[(set (match_operand:DSI 0 "register_operand" "=d") |
(neg:DSI (match_operand:DSI 1 "register_operand" "d"))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*negdi2_sign_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:DI (ashiftrt:DI (ashift:DI (subreg:DI |
(match_operand:SI 1 "register_operand" "d") 0) |
(const_int 32)) (const_int 32))) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(neg:DI (sign_extend:DI (match_dup 1))))] |
"TARGET_64BIT && s390_match_ccmode (insn, CCAmode)" |
"lcgfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
(define_insn "*negdi2_sign" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(neg:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"lcgfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
; lcr, lcgr |
(define_insn "*neg<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:GPR (match_operand:GPR 1 "register_operand" "d")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d") |
(neg:GPR (match_dup 1)))] |
"s390_match_ccmode (insn, CCAmode)" |
"lc<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lcr, lcgr |
(define_insn "*neg<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (neg:GPR (match_operand:GPR 1 "register_operand" "d")) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d"))] |
"s390_match_ccmode (insn, CCAmode)" |
"lc<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lcr, lcgr |
(define_insn "*neg<mode>2" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(neg:GPR (match_operand:GPR 1 "register_operand" "d"))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"lc<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
(define_insn_and_split "*negdi2_31" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(neg:DI (match_operand:DI 1 "register_operand" "d"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT" |
"#" |
"&& reload_completed" |
[(parallel |
[(set (match_dup 2) (neg:SI (match_dup 3))) |
(clobber (reg:CC CC_REGNUM))]) |
(parallel |
[(set (reg:CCAP CC_REGNUM) |
(compare:CCAP (neg:SI (match_dup 5)) (const_int 0))) |
(set (match_dup 4) (neg:SI (match_dup 5)))]) |
(set (pc) |
(if_then_else (ne (reg:CCAP CC_REGNUM) (const_int 0)) |
(pc) |
(label_ref (match_dup 6)))) |
(parallel |
[(set (match_dup 2) (plus:SI (match_dup 2) (const_int -1))) |
(clobber (reg:CC CC_REGNUM))]) |
(match_dup 6)] |
"operands[2] = operand_subword (operands[0], 0, 0, DImode); |
operands[3] = operand_subword (operands[1], 0, 0, DImode); |
operands[4] = operand_subword (operands[0], 1, 0, DImode); |
operands[5] = operand_subword (operands[1], 1, 0, DImode); |
operands[6] = gen_label_rtx ();") |
|
; |
; neg(df|sf)2 instruction pattern(s). |
; |
|
(define_expand "neg<mode>2" |
[(parallel |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_HARD_FLOAT" |
"") |
|
; lcxbr, lcdbr, lcebr |
(define_insn "*neg<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:FPR (match_operand:FPR 1 "register_operand" "f")) |
(match_operand:FPR 2 "const0_operand" ""))) |
(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (match_dup 1)))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lc<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lcxbr, lcdbr, lcebr |
(define_insn "*neg<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (neg:FPR (match_operand:FPR 1 "register_operand" "f")) |
(match_operand:FPR 2 "const0_operand" ""))) |
(clobber (match_scratch:FPR 0 "=f"))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lc<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lcxbr, lcdbr, lcebr |
(define_insn "*neg<mode>2" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lc<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lcxr, lcdr, lcer |
(define_insn "*neg<mode>2_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"lc<xde>r\t%0,%1" |
[(set_attr "op_type" "<RRe>") |
(set_attr "type" "fsimp<mode>")]) |
|
|
;; |
;;- Absolute value instructions. |
;; |
|
; |
; abs(di|si)2 instruction pattern(s). |
; |
|
(define_insn "*absdi2_sign_cc" |
[(set (reg CC_REGNUM) |
(compare (abs:DI (ashiftrt:DI (ashift:DI (subreg:DI |
(match_operand:SI 1 "register_operand" "d") 0) |
(const_int 32)) (const_int 32))) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(abs:DI (sign_extend:DI (match_dup 1))))] |
"TARGET_64BIT && s390_match_ccmode (insn, CCAmode)" |
"lpgfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
(define_insn "*absdi2_sign" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(abs:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"lpgfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
; lpr, lpgr |
(define_insn "*abs<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (abs:GPR (match_operand:DI 1 "register_operand" "d")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d") |
(abs:GPR (match_dup 1)))] |
"s390_match_ccmode (insn, CCAmode)" |
"lp<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lpr, lpgr |
(define_insn "*abs<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (abs:GPR (match_operand:GPR 1 "register_operand" "d")) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d"))] |
"s390_match_ccmode (insn, CCAmode)" |
"lp<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lpr, lpgr |
(define_insn "abs<mode>2" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(abs:GPR (match_operand:GPR 1 "register_operand" "d"))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"lp<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; |
; abs(df|sf)2 instruction pattern(s). |
; |
|
(define_expand "abs<mode>2" |
[(parallel |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(abs:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))])] |
"TARGET_HARD_FLOAT" |
"") |
|
; lpxbr, lpdbr, lpebr |
(define_insn "*abs<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (abs:FPR (match_operand:FPR 1 "register_operand" "f")) |
(match_operand:FPR 2 "const0_operand" ""))) |
(set (match_operand:FPR 0 "register_operand" "=f") |
(abs:FPR (match_dup 1)))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lp<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lpxbr, lpdbr, lpebr |
(define_insn "*abs<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (abs:FPR (match_operand:FPR 1 "register_operand" "f")) |
(match_operand:FPR 2 "const0_operand" ""))) |
(clobber (match_scratch:FPR 0 "=f"))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lp<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lpxbr, lpdbr, lpebr |
(define_insn "*abs<mode>2" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(abs:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"lp<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lpxr, lpdr, lper |
(define_insn "*abs<mode>2_ibm" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(abs:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" |
"lp<xde>r\t%0,%1" |
[(set_attr "op_type" "<RRe>") |
(set_attr "type" "fsimp<mode>")]) |
|
;; |
;;- Negated absolute value instructions |
;; |
|
; |
; Integer |
; |
|
(define_insn "*negabsdi2_sign_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:DI (abs:DI (ashiftrt:DI (ashift:DI (subreg:DI |
(match_operand:SI 1 "register_operand" "d") 0) |
(const_int 32)) (const_int 32)))) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(neg:DI (abs:DI (sign_extend:DI (match_dup 1)))))] |
"TARGET_64BIT && s390_match_ccmode (insn, CCAmode)" |
"lngfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
(define_insn "*negabsdi2_sign" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(neg:DI (abs:DI (sign_extend:DI |
(match_operand:SI 1 "register_operand" "d"))))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
"lngfr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
; lnr, lngr |
(define_insn "*negabs<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d"))) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d") |
(neg:GPR (abs:GPR (match_dup 1))))] |
"s390_match_ccmode (insn, CCAmode)" |
"ln<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lnr, lngr |
(define_insn "*negabs<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d"))) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d"))] |
"s390_match_ccmode (insn, CCAmode)" |
"ln<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; lnr, lngr |
(define_insn "*negabs<mode>2" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d")))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"ln<g>r\t%0,%1" |
[(set_attr "op_type" "RR<E>")]) |
|
; |
; Floating point |
; |
|
; lnxbr, lndbr, lnebr |
(define_insn "*negabs<mode>2_cc" |
[(set (reg CC_REGNUM) |
(compare (neg:FPR (abs:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(match_operand:FPR 2 "const0_operand" ""))) |
(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (abs:FPR (match_dup 1))))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"ln<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lnxbr, lndbr, lnebr |
(define_insn "*negabs<mode>2_cconly" |
[(set (reg CC_REGNUM) |
(compare (neg:FPR (abs:FPR (match_operand:FPR 1 "register_operand" "f"))) |
(match_operand:FPR 2 "const0_operand" ""))) |
(clobber (match_scratch:FPR 0 "=f"))] |
"s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"ln<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
; lnxbr, lndbr, lnebr |
(define_insn "*negabs<mode>2" |
[(set (match_operand:FPR 0 "register_operand" "=f") |
(neg:FPR (abs:FPR (match_operand:FPR 1 "register_operand" "f")))) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"ln<xde>br\t%0,%1" |
[(set_attr "op_type" "RRE") |
(set_attr "type" "fsimp<mode>")]) |
|
;; |
;;- Square root instructions. |
;; |
|
; |
; sqrt(df|sf)2 instruction pattern(s). |
; |
|
; sqxbr, sqdbr, sqebr, sqxb, sqdb, sqeb |
(define_insn "sqrt<mode>2" |
[(set (match_operand:FPR 0 "register_operand" "=f,f") |
(sqrt:FPR (match_operand:FPR 1 "general_operand" "f,<Rf>")))] |
"TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" |
"@ |
sq<xde>br\t%0,%1 |
sq<xde>b\t%0,%1" |
[(set_attr "op_type" "RRE,RXE") |
(set_attr "type" "fsqrt<mode>")]) |
|
|
;; |
;;- One complement instructions. |
;; |
|
; |
; one_cmpl(di|si|hi|qi)2 instruction pattern(s). |
; |
|
(define_expand "one_cmpl<mode>2" |
[(parallel |
[(set (match_operand:INT 0 "register_operand" "") |
(xor:INT (match_operand:INT 1 "register_operand" "") |
(const_int -1))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
|
;; |
;; Find leftmost bit instructions. |
;; |
|
(define_expand "clzdi2" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(clz:DI (match_operand:DI 1 "register_operand" "d")))] |
"TARGET_EXTIMM && TARGET_64BIT" |
{ |
rtx insn, clz_equal; |
rtx wide_reg = gen_reg_rtx (TImode); |
rtx msb = gen_rtx_CONST_INT (DImode, (unsigned HOST_WIDE_INT) 1 << 63); |
|
clz_equal = gen_rtx_CLZ (DImode, operands[1]); |
|
emit_insn (gen_clztidi2 (wide_reg, operands[1], msb)); |
|
insn = emit_move_insn (operands[0], gen_highpart (DImode, wide_reg)); |
REG_NOTES (insn) = |
gen_rtx_EXPR_LIST (REG_EQUAL, clz_equal, REG_NOTES (insn)); |
|
DONE; |
}) |
|
(define_insn "clztidi2" |
[(set (match_operand:TI 0 "register_operand" "=d") |
(ior:TI |
(ashift:TI |
(zero_extend:TI |
(xor:DI (match_operand:DI 1 "register_operand" "d") |
(lshiftrt (match_operand:DI 2 "const_int_operand" "") |
(subreg:SI (clz:DI (match_dup 1)) 4)))) |
|
(const_int 64)) |
(zero_extend:TI (clz:DI (match_dup 1))))) |
(clobber (reg:CC CC_REGNUM))] |
"(unsigned HOST_WIDE_INT) INTVAL (operands[2]) |
== (unsigned HOST_WIDE_INT) 1 << 63 |
&& TARGET_EXTIMM && TARGET_64BIT" |
"flogr\t%0,%1" |
[(set_attr "op_type" "RRE")]) |
|
|
;; |
;;- Rotate instructions. |
;; |
|
; |
; rotl(di|si)3 instruction pattern(s). |
; |
|
; rll, rllg |
(define_insn "rotl<mode>3" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(rotate:GPR (match_operand:GPR 1 "register_operand" "d") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")))] |
"TARGET_CPU_ZARCH" |
"rll<g>\t%0,%1,%Y2" |
[(set_attr "op_type" "RSE") |
(set_attr "atype" "reg")]) |
|
; rll, rllg |
(define_insn "*rotl<mode>3_and" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(rotate:GPR (match_operand:GPR 1 "register_operand" "d") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))))] |
"TARGET_CPU_ZARCH && (INTVAL (operands[3]) & 63) == 63" |
"rll<g>\t%0,%1,%Y2" |
[(set_attr "op_type" "RSE") |
(set_attr "atype" "reg")]) |
|
|
;; |
;;- Shift instructions. |
;; |
|
; |
; (ashl|lshr)(di|si)3 instruction pattern(s). |
; |
|
(define_expand "<shift><mode>3" |
[(set (match_operand:DSI 0 "register_operand" "") |
(SHIFT:DSI (match_operand:DSI 1 "register_operand" "") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "")))] |
"" |
"") |
|
; sldl, srdl |
(define_insn "*<shift>di3_31" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(SHIFT:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")))] |
"!TARGET_64BIT" |
"s<lr>dl\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
; sll, srl, sllg, srlg |
(define_insn "*<shift><mode>3" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(SHIFT:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")))] |
"" |
"s<lr>l<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; sldl, srdl |
(define_insn "*<shift>di3_31_and" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(SHIFT:DI (match_operand:DI 1 "register_operand" "0") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))))] |
"!TARGET_64BIT && (INTVAL (operands[3]) & 63) == 63" |
"s<lr>dl\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
; sll, srl, sllg, srlg |
(define_insn "*<shift><mode>3_and" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(SHIFT:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))))] |
"(INTVAL (operands[3]) & 63) == 63" |
"s<lr>l<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; |
; ashr(di|si)3 instruction pattern(s). |
; |
|
(define_expand "ashr<mode>3" |
[(parallel |
[(set (match_operand:DSI 0 "register_operand" "") |
(ashiftrt:DSI (match_operand:DSI 1 "register_operand" "") |
(match_operand:SI 2 "shift_count_or_setmem_operand" ""))) |
(clobber (reg:CC CC_REGNUM))])] |
"" |
"") |
|
(define_insn "*ashrdi3_cc_31" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(ashiftrt:DI (match_dup 1) (match_dup 2)))] |
"!TARGET_64BIT && s390_match_ccmode(insn, CCSmode)" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
(define_insn "*ashrdi3_cconly_31" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d"))] |
"!TARGET_64BIT && s390_match_ccmode(insn, CCSmode)" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
(define_insn "*ashrdi3_31" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y"))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3_cc" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d") |
(ashiftrt:GPR (match_dup 1) (match_dup 2)))] |
"s390_match_ccmode(insn, CCSmode)" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3_cconly" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d"))] |
"s390_match_ccmode(insn, CCSmode)" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(match_operand:SI 2 "shift_count_or_setmem_operand" "Y"))) |
(clobber (reg:CC CC_REGNUM))] |
"" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
|
; shift pattern with implicit ANDs |
|
(define_insn "*ashrdi3_cc_31_and" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))) |
(const_int 0))) |
(set (match_operand:DI 0 "register_operand" "=d") |
(ashiftrt:DI (match_dup 1) (and:SI (match_dup 2) (match_dup 3))))] |
"!TARGET_64BIT && s390_match_ccmode(insn, CCSmode) |
&& (INTVAL (operands[3]) & 63) == 63" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
(define_insn "*ashrdi3_cconly_31_and" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))) |
(const_int 0))) |
(clobber (match_scratch:DI 0 "=d"))] |
"!TARGET_64BIT && s390_match_ccmode(insn, CCSmode) |
&& (INTVAL (operands[3]) & 63) == 63" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
(define_insn "*ashrdi3_31_and" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(ashiftrt:DI (match_operand:DI 1 "register_operand" "0") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n")))) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_64BIT && (INTVAL (operands[3]) & 63) == 63" |
"srda\t%0,%Y2" |
[(set_attr "op_type" "RS") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3_cc_and" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))) |
(const_int 0))) |
(set (match_operand:GPR 0 "register_operand" "=d") |
(ashiftrt:GPR (match_dup 1) (and:SI (match_dup 2) (match_dup 3))))] |
"s390_match_ccmode(insn, CCSmode) && (INTVAL (operands[3]) & 63) == 63" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3_cconly_and" |
[(set (reg CC_REGNUM) |
(compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n"))) |
(const_int 0))) |
(clobber (match_scratch:GPR 0 "=d"))] |
"s390_match_ccmode(insn, CCSmode) && (INTVAL (operands[3]) & 63) == 63" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
; sra, srag |
(define_insn "*ashr<mode>3_and" |
[(set (match_operand:GPR 0 "register_operand" "=d") |
(ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>") |
(and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") |
(match_operand:SI 3 "const_int_operand" "n")))) |
(clobber (reg:CC CC_REGNUM))] |
"(INTVAL (operands[3]) & 63) == 63" |
"sra<g>\t%0,<1>%Y2" |
[(set_attr "op_type" "RS<E>") |
(set_attr "atype" "reg")]) |
|
|
;; |
;; Branch instruction patterns. |
;; |
|
(define_expand "b<code>" |
[(set (pc) |
(if_then_else (COMPARE (match_operand 0 "" "") |
(const_int 0)) |
(match_dup 0) |
(pc)))] |
"" |
"s390_emit_jump (operands[0], |
s390_emit_compare (<CODE>, s390_compare_op0, s390_compare_op1)); DONE;") |
|
|
;; |
;;- Conditional jump instructions. |
;; |
|
(define_insn "*cjump_64" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"TARGET_CPU_ZARCH" |
{ |
if (get_attr_length (insn) == 4) |
return "j%C1\t%l0"; |
else |
return "jg%C1\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)))]) |
|
(define_insn "*cjump_31" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(label_ref (match_operand 0 "" "")) |
(pc)))] |
"!TARGET_CPU_ZARCH" |
{ |
gcc_assert (get_attr_length (insn) == 4); |
return "j%C1\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 8))))]) |
|
(define_insn "*cjump_long" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(match_operand 0 "address_operand" "U") |
(pc)))] |
"" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "b%C1r\t%0"; |
else |
return "b%C1\t%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
|
;; |
;;- Negated conditional jump instructions. |
;; |
|
(define_insn "*icjump_64" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(pc) |
(label_ref (match_operand 0 "" ""))))] |
"TARGET_CPU_ZARCH" |
{ |
if (get_attr_length (insn) == 4) |
return "j%D1\t%l0"; |
else |
return "jg%D1\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)))]) |
|
(define_insn "*icjump_31" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(pc) |
(label_ref (match_operand 0 "" ""))))] |
"!TARGET_CPU_ZARCH" |
{ |
gcc_assert (get_attr_length (insn) == 4); |
return "j%D1\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 8))))]) |
|
(define_insn "*icjump_long" |
[(set (pc) |
(if_then_else |
(match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(pc) |
(match_operand 0 "address_operand" "U")))] |
"" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "b%D1r\t%0"; |
else |
return "b%D1\t%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
;; |
;;- Trap instructions. |
;; |
|
(define_insn "trap" |
[(trap_if (const_int 1) (const_int 0))] |
"" |
"j\t.+2" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch")]) |
|
(define_expand "conditional_trap" |
[(trap_if (match_operand 0 "comparison_operator" "") |
(match_operand 1 "general_operand" ""))] |
"" |
{ |
if (operands[1] != const0_rtx) FAIL; |
operands[0] = s390_emit_compare (GET_CODE (operands[0]), |
s390_compare_op0, s390_compare_op1); |
}) |
|
(define_insn "*trap" |
[(trap_if (match_operator 0 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) |
(const_int 0))] |
"" |
"j%C0\t.+2"; |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch")]) |
|
;; |
;;- Loop instructions. |
;; |
;; This is all complicated by the fact that since this is a jump insn |
;; we must handle our own output reloads. |
|
(define_expand "doloop_end" |
[(use (match_operand 0 "" "")) ; loop pseudo |
(use (match_operand 1 "" "")) ; iterations; zero if unknown |
(use (match_operand 2 "" "")) ; max iterations |
(use (match_operand 3 "" "")) ; loop level |
(use (match_operand 4 "" ""))] ; label |
"" |
{ |
if (GET_MODE (operands[0]) == SImode && !TARGET_CPU_ZARCH) |
emit_jump_insn (gen_doloop_si31 (operands[4], operands[0], operands[0])); |
else if (GET_MODE (operands[0]) == SImode && TARGET_CPU_ZARCH) |
emit_jump_insn (gen_doloop_si64 (operands[4], operands[0], operands[0])); |
else if (GET_MODE (operands[0]) == DImode && TARGET_64BIT) |
emit_jump_insn (gen_doloop_di (operands[4], operands[0], operands[0])); |
else |
FAIL; |
|
DONE; |
}) |
|
(define_insn_and_split "doloop_si64" |
[(set (pc) |
(if_then_else |
(ne (match_operand:SI 1 "register_operand" "d,d,d") |
(const_int 1)) |
(label_ref (match_operand 0 "" "")) |
(pc))) |
(set (match_operand:SI 2 "nonimmediate_operand" "=1,?X,?X") |
(plus:SI (match_dup 1) (const_int -1))) |
(clobber (match_scratch:SI 3 "=X,&1,&?d")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_CPU_ZARCH" |
{ |
if (which_alternative != 0) |
return "#"; |
else if (get_attr_length (insn) == 4) |
return "brct\t%1,%l0"; |
else |
return "ahi\t%1,-1\;jgne\t%l0"; |
} |
"&& reload_completed |
&& (! REG_P (operands[2]) |
|| ! rtx_equal_p (operands[1], operands[2]))" |
[(set (match_dup 3) (match_dup 1)) |
(parallel [(set (reg:CCAN CC_REGNUM) |
(compare:CCAN (plus:SI (match_dup 3) (const_int -1)) |
(const_int 0))) |
(set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) |
(set (match_dup 2) (match_dup 3)) |
(set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) |
(label_ref (match_dup 0)) |
(pc)))] |
"" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 10)))]) |
|
(define_insn_and_split "doloop_si31" |
[(set (pc) |
(if_then_else |
(ne (match_operand:SI 1 "register_operand" "d,d,d") |
(const_int 1)) |
(label_ref (match_operand 0 "" "")) |
(pc))) |
(set (match_operand:SI 2 "nonimmediate_operand" "=1,?X,?X") |
(plus:SI (match_dup 1) (const_int -1))) |
(clobber (match_scratch:SI 3 "=X,&1,&?d")) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_CPU_ZARCH" |
{ |
if (which_alternative != 0) |
return "#"; |
else if (get_attr_length (insn) == 4) |
return "brct\t%1,%l0"; |
else |
gcc_unreachable (); |
} |
"&& reload_completed |
&& (! REG_P (operands[2]) |
|| ! rtx_equal_p (operands[1], operands[2]))" |
[(set (match_dup 3) (match_dup 1)) |
(parallel [(set (reg:CCAN CC_REGNUM) |
(compare:CCAN (plus:SI (match_dup 3) (const_int -1)) |
(const_int 0))) |
(set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) |
(set (match_dup 2) (match_dup 3)) |
(set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) |
(label_ref (match_dup 0)) |
(pc)))] |
"" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 8))))]) |
|
(define_insn "*doloop_si_long" |
[(set (pc) |
(if_then_else |
(ne (match_operand:SI 1 "register_operand" "d") |
(const_int 1)) |
(match_operand 0 "address_operand" "U") |
(pc))) |
(set (match_operand:SI 2 "register_operand" "=1") |
(plus:SI (match_dup 1) (const_int -1))) |
(clobber (match_scratch:SI 3 "=X")) |
(clobber (reg:CC CC_REGNUM))] |
"!TARGET_CPU_ZARCH" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "bctr\t%1,%0"; |
else |
return "bct\t%1,%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
(define_insn_and_split "doloop_di" |
[(set (pc) |
(if_then_else |
(ne (match_operand:DI 1 "register_operand" "d,d,d") |
(const_int 1)) |
(label_ref (match_operand 0 "" "")) |
(pc))) |
(set (match_operand:DI 2 "nonimmediate_operand" "=1,?X,?X") |
(plus:DI (match_dup 1) (const_int -1))) |
(clobber (match_scratch:DI 3 "=X,&1,&?d")) |
(clobber (reg:CC CC_REGNUM))] |
"TARGET_64BIT" |
{ |
if (which_alternative != 0) |
return "#"; |
else if (get_attr_length (insn) == 4) |
return "brctg\t%1,%l0"; |
else |
return "aghi\t%1,-1\;jgne\t%l0"; |
} |
"&& reload_completed |
&& (! REG_P (operands[2]) |
|| ! rtx_equal_p (operands[1], operands[2]))" |
[(set (match_dup 3) (match_dup 1)) |
(parallel [(set (reg:CCAN CC_REGNUM) |
(compare:CCAN (plus:DI (match_dup 3) (const_int -1)) |
(const_int 0))) |
(set (match_dup 3) (plus:DI (match_dup 3) (const_int -1)))]) |
(set (match_dup 2) (match_dup 3)) |
(set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) |
(label_ref (match_dup 0)) |
(pc)))] |
"" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 10)))]) |
|
;; |
;;- Unconditional jump instructions. |
;; |
|
; |
; jump instruction pattern(s). |
; |
|
(define_expand "jump" |
[(match_operand 0 "" "")] |
"" |
"s390_emit_jump (operands[0], NULL_RTX); DONE;") |
|
(define_insn "*jump64" |
[(set (pc) (label_ref (match_operand 0 "" "")))] |
"TARGET_CPU_ZARCH" |
{ |
if (get_attr_length (insn) == 4) |
return "j\t%l0"; |
else |
return "jg\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)))]) |
|
(define_insn "*jump31" |
[(set (pc) (label_ref (match_operand 0 "" "")))] |
"!TARGET_CPU_ZARCH" |
{ |
gcc_assert (get_attr_length (insn) == 4); |
return "j\t%l0"; |
} |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch") |
(set (attr "length") |
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 6)) |
(if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) |
(const_int 4) (const_int 8))))]) |
|
; |
; indirect-jump instruction pattern(s). |
; |
|
(define_insn "indirect_jump" |
[(set (pc) (match_operand 0 "address_operand" "U"))] |
"" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "br\t%0"; |
else |
return "b\t%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
; |
; casesi instruction pattern(s). |
; |
|
(define_insn "casesi_jump" |
[(set (pc) (match_operand 0 "address_operand" "U")) |
(use (label_ref (match_operand 1 "" "")))] |
"" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "br\t%0"; |
else |
return "b\t%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
(define_expand "casesi" |
[(match_operand:SI 0 "general_operand" "") |
(match_operand:SI 1 "general_operand" "") |
(match_operand:SI 2 "general_operand" "") |
(label_ref (match_operand 3 "" "")) |
(label_ref (match_operand 4 "" ""))] |
"" |
{ |
rtx index = gen_reg_rtx (SImode); |
rtx base = gen_reg_rtx (Pmode); |
rtx target = gen_reg_rtx (Pmode); |
|
emit_move_insn (index, operands[0]); |
emit_insn (gen_subsi3 (index, index, operands[1])); |
emit_cmp_and_jump_insns (index, operands[2], GTU, NULL_RTX, SImode, 1, |
operands[4]); |
|
if (Pmode != SImode) |
index = convert_to_mode (Pmode, index, 1); |
if (GET_CODE (index) != REG) |
index = copy_to_mode_reg (Pmode, index); |
|
if (TARGET_64BIT) |
emit_insn (gen_ashldi3 (index, index, GEN_INT (3))); |
else |
emit_insn (gen_ashlsi3 (index, index, const2_rtx)); |
|
emit_move_insn (base, gen_rtx_LABEL_REF (Pmode, operands[3])); |
|
index = gen_const_mem (Pmode, gen_rtx_PLUS (Pmode, base, index)); |
emit_move_insn (target, index); |
|
if (flag_pic) |
target = gen_rtx_PLUS (Pmode, base, target); |
emit_jump_insn (gen_casesi_jump (target, operands[3])); |
|
DONE; |
}) |
|
|
;; |
;;- Jump to subroutine. |
;; |
;; |
|
; |
; untyped call instruction pattern(s). |
; |
|
;; Call subroutine returning any type. |
(define_expand "untyped_call" |
[(parallel [(call (match_operand 0 "" "") |
(const_int 0)) |
(match_operand 1 "" "") |
(match_operand 2 "" "")])] |
"" |
{ |
int i; |
|
emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx)); |
|
for (i = 0; i < XVECLEN (operands[2], 0); i++) |
{ |
rtx set = XVECEXP (operands[2], 0, i); |
emit_move_insn (SET_DEST (set), SET_SRC (set)); |
} |
|
/* The optimizer does not know that the call sets the function value |
registers we stored in the result block. We avoid problems by |
claiming that all hard registers are used and clobbered at this |
point. */ |
emit_insn (gen_blockage ()); |
|
DONE; |
}) |
|
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and |
;; all of memory. This blocks insns from being moved across this point. |
|
(define_insn "blockage" |
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] |
"" |
"" |
[(set_attr "type" "none") |
(set_attr "length" "0")]) |
|
; |
; sibcall patterns |
; |
|
(define_expand "sibcall" |
[(call (match_operand 0 "" "") |
(match_operand 1 "" ""))] |
"" |
{ |
s390_emit_call (XEXP (operands[0], 0), NULL_RTX, NULL_RTX, NULL_RTX); |
DONE; |
}) |
|
(define_insn "*sibcall_br" |
[(call (mem:QI (reg SIBCALL_REGNUM)) |
(match_operand 0 "const_int_operand" "n"))] |
"SIBLING_CALL_P (insn) |
&& GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode" |
"br\t%%r1" |
[(set_attr "op_type" "RR") |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
(define_insn "*sibcall_brc" |
[(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) |
(match_operand 1 "const_int_operand" "n"))] |
"SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC" |
"j\t%0" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch")]) |
|
(define_insn "*sibcall_brcl" |
[(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) |
(match_operand 1 "const_int_operand" "n"))] |
"SIBLING_CALL_P (insn) && TARGET_CPU_ZARCH" |
"jg\t%0" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "branch")]) |
|
; |
; sibcall_value patterns |
; |
|
(define_expand "sibcall_value" |
[(set (match_operand 0 "" "") |
(call (match_operand 1 "" "") |
(match_operand 2 "" "")))] |
"" |
{ |
s390_emit_call (XEXP (operands[1], 0), NULL_RTX, operands[0], NULL_RTX); |
DONE; |
}) |
|
(define_insn "*sibcall_value_br" |
[(set (match_operand 0 "" "") |
(call (mem:QI (reg SIBCALL_REGNUM)) |
(match_operand 1 "const_int_operand" "n")))] |
"SIBLING_CALL_P (insn) |
&& GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode" |
"br\t%%r1" |
[(set_attr "op_type" "RR") |
(set_attr "type" "branch") |
(set_attr "atype" "agen")]) |
|
(define_insn "*sibcall_value_brc" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand 2 "const_int_operand" "n")))] |
"SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC" |
"j\t%1" |
[(set_attr "op_type" "RI") |
(set_attr "type" "branch")]) |
|
(define_insn "*sibcall_value_brcl" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand 2 "const_int_operand" "n")))] |
"SIBLING_CALL_P (insn) && TARGET_CPU_ZARCH" |
"jg\t%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "branch")]) |
|
|
; |
; call instruction pattern(s). |
; |
|
(define_expand "call" |
[(call (match_operand 0 "" "") |
(match_operand 1 "" "")) |
(use (match_operand 2 "" ""))] |
"" |
{ |
s390_emit_call (XEXP (operands[0], 0), NULL_RTX, NULL_RTX, |
gen_rtx_REG (Pmode, RETURN_REGNUM)); |
DONE; |
}) |
|
(define_insn "*bras" |
[(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) |
(match_operand 1 "const_int_operand" "n")) |
(clobber (match_operand 2 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_SMALL_EXEC |
&& GET_MODE (operands[2]) == Pmode" |
"bras\t%2,%0" |
[(set_attr "op_type" "RI") |
(set_attr "type" "jsr")]) |
|
(define_insn "*brasl" |
[(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) |
(match_operand 1 "const_int_operand" "n")) |
(clobber (match_operand 2 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_CPU_ZARCH |
&& GET_MODE (operands[2]) == Pmode" |
"brasl\t%2,%0" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "jsr")]) |
|
(define_insn "*basr" |
[(call (mem:QI (match_operand 0 "address_operand" "U")) |
(match_operand 1 "const_int_operand" "n")) |
(clobber (match_operand 2 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "basr\t%2,%0"; |
else |
return "bas\t%2,%a0"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 0 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "jsr") |
(set_attr "atype" "agen")]) |
|
; |
; call_value instruction pattern(s). |
; |
|
(define_expand "call_value" |
[(set (match_operand 0 "" "") |
(call (match_operand 1 "" "") |
(match_operand 2 "" ""))) |
(use (match_operand 3 "" ""))] |
"" |
{ |
s390_emit_call (XEXP (operands[1], 0), NULL_RTX, operands[0], |
gen_rtx_REG (Pmode, RETURN_REGNUM)); |
DONE; |
}) |
|
(define_insn "*bras_r" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand:SI 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_SMALL_EXEC |
&& GET_MODE (operands[3]) == Pmode" |
"bras\t%3,%1" |
[(set_attr "op_type" "RI") |
(set_attr "type" "jsr")]) |
|
(define_insn "*brasl_r" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_CPU_ZARCH |
&& GET_MODE (operands[3]) == Pmode" |
"brasl\t%3,%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "jsr")]) |
|
(define_insn "*basr_r" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "address_operand" "U")) |
(match_operand 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r"))] |
"!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "basr\t%3,%1"; |
else |
return "bas\t%3,%a1"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 1 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "jsr") |
(set_attr "atype" "agen")]) |
|
;; |
;;- Thread-local storage support. |
;; |
|
(define_expand "get_tp_64" |
[(set (match_operand:DI 0 "nonimmediate_operand" "") (reg:DI TP_REGNUM))] |
"TARGET_64BIT" |
"") |
|
(define_expand "get_tp_31" |
[(set (match_operand:SI 0 "nonimmediate_operand" "") (reg:SI TP_REGNUM))] |
"!TARGET_64BIT" |
"") |
|
(define_expand "set_tp_64" |
[(set (reg:DI TP_REGNUM) (match_operand:DI 0 "nonimmediate_operand" "")) |
(set (reg:DI TP_REGNUM) (unspec_volatile:DI [(reg:DI TP_REGNUM)] UNSPECV_SET_TP))] |
"TARGET_64BIT" |
"") |
|
(define_expand "set_tp_31" |
[(set (reg:SI TP_REGNUM) (match_operand:SI 0 "nonimmediate_operand" "")) |
(set (reg:SI TP_REGNUM) (unspec_volatile:SI [(reg:SI TP_REGNUM)] UNSPECV_SET_TP))] |
"!TARGET_64BIT" |
"") |
|
(define_insn "*set_tp" |
[(set (reg TP_REGNUM) (unspec_volatile [(reg TP_REGNUM)] UNSPECV_SET_TP))] |
"" |
"" |
[(set_attr "type" "none") |
(set_attr "length" "0")]) |
|
(define_insn "*tls_load_64" |
[(set (match_operand:DI 0 "register_operand" "=d") |
(unspec:DI [(match_operand:DI 1 "memory_operand" "m") |
(match_operand:DI 2 "" "")] |
UNSPEC_TLS_LOAD))] |
"TARGET_64BIT" |
"lg\t%0,%1%J2" |
[(set_attr "op_type" "RXE")]) |
|
(define_insn "*tls_load_31" |
[(set (match_operand:SI 0 "register_operand" "=d,d") |
(unspec:SI [(match_operand:SI 1 "memory_operand" "R,T") |
(match_operand:SI 2 "" "")] |
UNSPEC_TLS_LOAD))] |
"!TARGET_64BIT" |
"@ |
l\t%0,%1%J2 |
ly\t%0,%1%J2" |
[(set_attr "op_type" "RX,RXY")]) |
|
(define_insn "*bras_tls" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r")) |
(use (match_operand 4 "" ""))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_SMALL_EXEC |
&& GET_MODE (operands[3]) == Pmode" |
"bras\t%3,%1%J4" |
[(set_attr "op_type" "RI") |
(set_attr "type" "jsr")]) |
|
(define_insn "*brasl_tls" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "bras_sym_operand" "X")) |
(match_operand 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r")) |
(use (match_operand 4 "" ""))] |
"!SIBLING_CALL_P (insn) |
&& TARGET_CPU_ZARCH |
&& GET_MODE (operands[3]) == Pmode" |
"brasl\t%3,%1%J4" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "jsr")]) |
|
(define_insn "*basr_tls" |
[(set (match_operand 0 "" "") |
(call (mem:QI (match_operand 1 "address_operand" "U")) |
(match_operand 2 "const_int_operand" "n"))) |
(clobber (match_operand 3 "register_operand" "=r")) |
(use (match_operand 4 "" ""))] |
"!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode" |
{ |
if (get_attr_op_type (insn) == OP_TYPE_RR) |
return "basr\t%3,%1%J4"; |
else |
return "bas\t%3,%a1%J4"; |
} |
[(set (attr "op_type") |
(if_then_else (match_operand 1 "register_operand" "") |
(const_string "RR") (const_string "RX"))) |
(set_attr "type" "jsr") |
(set_attr "atype" "agen")]) |
|
;; |
;;- Atomic operations |
;; |
|
; |
; memory barrier pattern. |
; |
|
(define_expand "memory_barrier" |
[(set (mem:BLK (match_dup 0)) |
(unspec_volatile:BLK [(mem:BLK (match_dup 0))] UNSPECV_MB))] |
"" |
{ |
operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (DImode)); |
MEM_VOLATILE_P (operands[0]) = 1; |
}) |
|
(define_insn "*memory_barrier" |
[(set (match_operand:BLK 0 "" "") |
(unspec_volatile:BLK [(match_operand:BLK 1 "" "")] UNSPECV_MB))] |
"" |
"bcr\t15,0" |
[(set_attr "op_type" "RR")]) |
|
; |
; compare and swap patterns. |
; |
|
(define_expand "sync_compare_and_swap<mode>" |
[(parallel |
[(set (match_operand:TDSI 0 "register_operand" "") |
(match_operand:TDSI 1 "memory_operand" "")) |
(set (match_dup 1) |
(unspec_volatile:TDSI |
[(match_dup 1) |
(match_operand:TDSI 2 "register_operand" "") |
(match_operand:TDSI 3 "register_operand" "")] |
UNSPECV_CAS)) |
(set (reg:CCZ1 CC_REGNUM) |
(compare:CCZ1 (match_dup 1) (match_dup 2)))])] |
"") |
|
(define_expand "sync_compare_and_swap<mode>" |
[(parallel |
[(set (match_operand:HQI 0 "register_operand" "") |
(match_operand:HQI 1 "memory_operand" "")) |
(set (match_dup 1) |
(unspec_volatile:HQI |
[(match_dup 1) |
(match_operand:HQI 2 "general_operand" "") |
(match_operand:HQI 3 "general_operand" "")] |
UNSPECV_CAS)) |
(set (reg:CCZ1 CC_REGNUM) |
(compare:CCZ1 (match_dup 1) (match_dup 2)))])] |
"" |
"s390_expand_cs_hqi (<MODE>mode, operands[0], operands[1], |
operands[2], operands[3]); DONE;") |
|
(define_expand "sync_compare_and_swap_cc<mode>" |
[(parallel |
[(set (match_operand:TDSI 0 "register_operand" "") |
(match_operand:TDSI 1 "memory_operand" "")) |
(set (match_dup 1) |
(unspec_volatile:TDSI |
[(match_dup 1) |
(match_operand:TDSI 2 "register_operand" "") |
(match_operand:TDSI 3 "register_operand" "")] |
UNSPECV_CAS)) |
(set (match_dup 4) |
(compare:CCZ1 (match_dup 1) (match_dup 2)))])] |
"" |
{ |
/* Emulate compare. */ |
operands[4] = gen_rtx_REG (CCZ1mode, CC_REGNUM); |
s390_compare_op0 = operands[1]; |
s390_compare_op1 = operands[2]; |
s390_compare_emitted = operands[4]; |
}) |
|
; cds, cdsg |
(define_insn "*sync_compare_and_swap<mode>" |
[(set (match_operand:DP 0 "register_operand" "=r") |
(match_operand:DP 1 "memory_operand" "+Q")) |
(set (match_dup 1) |
(unspec_volatile:DP |
[(match_dup 1) |
(match_operand:DP 2 "register_operand" "0") |
(match_operand:DP 3 "register_operand" "r")] |
UNSPECV_CAS)) |
(set (reg:CCZ1 CC_REGNUM) |
(compare:CCZ1 (match_dup 1) (match_dup 2)))] |
"" |
"cds<tg>\t%0,%3,%S1" |
[(set_attr "op_type" "RS<TE>") |
(set_attr "type" "sem")]) |
|
; cs, csg |
(define_insn "*sync_compare_and_swap<mode>" |
[(set (match_operand:GPR 0 "register_operand" "=r") |
(match_operand:GPR 1 "memory_operand" "+Q")) |
(set (match_dup 1) |
(unspec_volatile:GPR |
[(match_dup 1) |
(match_operand:GPR 2 "register_operand" "0") |
(match_operand:GPR 3 "register_operand" "r")] |
UNSPECV_CAS)) |
(set (reg:CCZ1 CC_REGNUM) |
(compare:CCZ1 (match_dup 1) (match_dup 2)))] |
"" |
"cs<g>\t%0,%3,%S1" |
[(set_attr "op_type" "RS<E>") |
(set_attr "type" "sem")]) |
|
|
; |
; Other atomic instruction patterns. |
; |
|
(define_expand "sync_lock_test_and_set<mode>" |
[(match_operand:HQI 0 "register_operand") |
(match_operand:HQI 1 "memory_operand") |
(match_operand:HQI 2 "general_operand")] |
"" |
"s390_expand_atomic (<MODE>mode, SET, operands[0], operands[1], |
operands[2], false); DONE;") |
|
(define_expand "sync_<atomic><mode>" |
[(set (match_operand:HQI 0 "memory_operand") |
(ATOMIC:HQI (match_dup 0) |
(match_operand:HQI 1 "general_operand")))] |
"" |
"s390_expand_atomic (<MODE>mode, <CODE>, NULL_RTX, operands[0], |
operands[1], false); DONE;") |
|
(define_expand "sync_old_<atomic><mode>" |
[(set (match_operand:HQI 0 "register_operand") |
(match_operand:HQI 1 "memory_operand")) |
(set (match_dup 1) |
(ATOMIC:HQI (match_dup 1) |
(match_operand:HQI 2 "general_operand")))] |
"" |
"s390_expand_atomic (<MODE>mode, <CODE>, operands[0], operands[1], |
operands[2], false); DONE;") |
|
(define_expand "sync_new_<atomic><mode>" |
[(set (match_operand:HQI 0 "register_operand") |
(ATOMIC:HQI (match_operand:HQI 1 "memory_operand") |
(match_operand:HQI 2 "general_operand"))) |
(set (match_dup 1) (ATOMIC:HQI (match_dup 1) (match_dup 2)))] |
"" |
"s390_expand_atomic (<MODE>mode, <CODE>, operands[0], operands[1], |
operands[2], true); DONE;") |
|
;; |
;;- Miscellaneous instructions. |
;; |
|
; |
; allocate stack instruction pattern(s). |
; |
|
(define_expand "allocate_stack" |
[(match_operand 0 "general_operand" "") |
(match_operand 1 "general_operand" "")] |
"TARGET_BACKCHAIN" |
{ |
rtx temp = gen_reg_rtx (Pmode); |
|
emit_move_insn (temp, s390_back_chain_rtx ()); |
anti_adjust_stack (operands[1]); |
emit_move_insn (s390_back_chain_rtx (), temp); |
|
emit_move_insn (operands[0], virtual_stack_dynamic_rtx); |
DONE; |
}) |
|
|
; |
; setjmp instruction pattern. |
; |
|
(define_expand "builtin_setjmp_receiver" |
[(match_operand 0 "" "")] |
"flag_pic" |
{ |
emit_insn (s390_load_got ()); |
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); |
DONE; |
}) |
|
;; These patterns say how to save and restore the stack pointer. We need not |
;; save the stack pointer at function level since we are careful to |
;; preserve the backchain. At block level, we have to restore the backchain |
;; when we restore the stack pointer. |
;; |
;; For nonlocal gotos, we must save both the stack pointer and its |
;; backchain and restore both. Note that in the nonlocal case, the |
;; save area is a memory location. |
|
(define_expand "save_stack_function" |
[(match_operand 0 "general_operand" "") |
(match_operand 1 "general_operand" "")] |
"" |
"DONE;") |
|
(define_expand "restore_stack_function" |
[(match_operand 0 "general_operand" "") |
(match_operand 1 "general_operand" "")] |
"" |
"DONE;") |
|
(define_expand "restore_stack_block" |
[(match_operand 0 "register_operand" "") |
(match_operand 1 "register_operand" "")] |
"TARGET_BACKCHAIN" |
{ |
rtx temp = gen_reg_rtx (Pmode); |
|
emit_move_insn (temp, s390_back_chain_rtx ()); |
emit_move_insn (operands[0], operands[1]); |
emit_move_insn (s390_back_chain_rtx (), temp); |
|
DONE; |
}) |
|
(define_expand "save_stack_nonlocal" |
[(match_operand 0 "memory_operand" "") |
(match_operand 1 "register_operand" "")] |
"" |
{ |
enum machine_mode mode = TARGET_64BIT ? OImode : TImode; |
rtx base = gen_rtx_REG (Pmode, BASE_REGNUM); |
|
/* Copy the backchain to the first word, sp to the second and the |
literal pool base to the third. */ |
|
if (TARGET_BACKCHAIN) |
{ |
rtx temp = force_reg (Pmode, s390_back_chain_rtx ()); |
emit_move_insn (operand_subword (operands[0], 0, 0, mode), temp); |
} |
|
emit_move_insn (operand_subword (operands[0], 1, 0, mode), operands[1]); |
emit_move_insn (operand_subword (operands[0], 2, 0, mode), base); |
|
DONE; |
}) |
|
(define_expand "restore_stack_nonlocal" |
[(match_operand 0 "register_operand" "") |
(match_operand 1 "memory_operand" "")] |
"" |
{ |
enum machine_mode mode = TARGET_64BIT ? OImode : TImode; |
rtx base = gen_rtx_REG (Pmode, BASE_REGNUM); |
rtx temp = NULL_RTX; |
|
/* Restore the backchain from the first word, sp from the second and the |
literal pool base from the third. */ |
|
if (TARGET_BACKCHAIN) |
temp = force_reg (Pmode, operand_subword (operands[1], 0, 0, mode)); |
|
emit_move_insn (base, operand_subword (operands[1], 2, 0, mode)); |
emit_move_insn (operands[0], operand_subword (operands[1], 1, 0, mode)); |
|
if (temp) |
emit_move_insn (s390_back_chain_rtx (), temp); |
|
emit_insn (gen_rtx_USE (VOIDmode, base)); |
DONE; |
}) |
|
(define_expand "exception_receiver" |
[(const_int 0)] |
"" |
{ |
s390_set_has_landing_pad_p (true); |
DONE; |
}) |
|
; |
; nop instruction pattern(s). |
; |
|
(define_insn "nop" |
[(const_int 0)] |
"" |
"lr\t0,0" |
[(set_attr "op_type" "RR")]) |
|
|
; |
; Special literal pool access instruction pattern(s). |
; |
|
(define_insn "*pool_entry" |
[(unspec_volatile [(match_operand 0 "consttable_operand" "X")] |
UNSPECV_POOL_ENTRY)] |
"" |
{ |
enum machine_mode mode = GET_MODE (PATTERN (insn)); |
unsigned int align = GET_MODE_BITSIZE (mode); |
s390_output_pool_entry (operands[0], mode, align); |
return ""; |
} |
[(set (attr "length") |
(symbol_ref "GET_MODE_SIZE (GET_MODE (PATTERN (insn)))"))]) |
|
(define_insn "pool_align" |
[(unspec_volatile [(match_operand 0 "const_int_operand" "n")] |
UNSPECV_POOL_ALIGN)] |
"" |
".align\t%0" |
[(set (attr "length") (symbol_ref "INTVAL (operands[0])"))]) |
|
(define_insn "pool_section_start" |
[(unspec_volatile [(const_int 1)] UNSPECV_POOL_SECTION)] |
"" |
".section\t.rodata" |
[(set_attr "length" "0")]) |
|
(define_insn "pool_section_end" |
[(unspec_volatile [(const_int 0)] UNSPECV_POOL_SECTION)] |
"" |
".previous" |
[(set_attr "length" "0")]) |
|
(define_insn "main_base_31_small" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE))] |
"!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" |
"basr\t%0,0" |
[(set_attr "op_type" "RR") |
(set_attr "type" "la")]) |
|
(define_insn "main_base_31_large" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE)) |
(set (pc) (label_ref (match_operand 2 "" "")))] |
"!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" |
"bras\t%0,%2" |
[(set_attr "op_type" "RI")]) |
|
(define_insn "main_base_64" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE))] |
"TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" |
"larl\t%0,%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "larl")]) |
|
(define_insn "main_pool" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec_volatile [(const_int 0)] UNSPECV_MAIN_POOL))] |
"GET_MODE (operands[0]) == Pmode" |
{ |
gcc_unreachable (); |
} |
[(set (attr "type") |
(if_then_else (ne (symbol_ref "TARGET_CPU_ZARCH") (const_int 0)) |
(const_string "larl") (const_string "la")))]) |
|
(define_insn "reload_base_31" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_RELOAD_BASE))] |
"!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" |
"basr\t%0,0\;la\t%0,%1-.(%0)" |
[(set_attr "length" "6") |
(set_attr "type" "la")]) |
|
(define_insn "reload_base_64" |
[(set (match_operand 0 "register_operand" "=a") |
(unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_RELOAD_BASE))] |
"TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" |
"larl\t%0,%1" |
[(set_attr "op_type" "RIL") |
(set_attr "type" "larl")]) |
|
(define_insn "pool" |
[(unspec_volatile [(match_operand 0 "const_int_operand" "n")] UNSPECV_POOL)] |
"" |
{ |
gcc_unreachable (); |
} |
[(set (attr "length") (symbol_ref "INTVAL (operands[0])"))]) |
|
;; |
;; Insns related to generating the function prologue and epilogue. |
;; |
|
|
(define_expand "prologue" |
[(use (const_int 0))] |
"" |
"s390_emit_prologue (); DONE;") |
|
(define_expand "epilogue" |
[(use (const_int 1))] |
"" |
"s390_emit_epilogue (false); DONE;") |
|
(define_expand "sibcall_epilogue" |
[(use (const_int 0))] |
"" |
"s390_emit_epilogue (true); DONE;") |
|
(define_insn "*return" |
[(return) |
(use (match_operand 0 "register_operand" "a"))] |
"GET_MODE (operands[0]) == Pmode" |
"br\t%0" |
[(set_attr "op_type" "RR") |
(set_attr "type" "jsr") |
(set_attr "atype" "agen")]) |
|
|
;; Instruction definition to extend a 31-bit pointer into a 64-bit |
;; pointer. This is used for compatibility. |
|
(define_expand "ptr_extend" |
[(set (match_operand:DI 0 "register_operand" "=r") |
(match_operand:SI 1 "register_operand" "r"))] |
"TARGET_64BIT" |
{ |
emit_insn (gen_anddi3 (operands[0], |
gen_lowpart (DImode, operands[1]), |
GEN_INT (0x7fffffff))); |
DONE; |
}) |
|
;; Instruction definition to expand eh_return macro to support |
;; swapping in special linkage return addresses. |
|
(define_expand "eh_return" |
[(use (match_operand 0 "register_operand" ""))] |
"TARGET_TPF" |
{ |
s390_emit_tpf_eh_return (operands[0]); |
DONE; |
}) |
|
; |
; Stack Protector Patterns |
; |
|
(define_expand "stack_protect_set" |
[(set (match_operand 0 "memory_operand" "") |
(match_operand 1 "memory_operand" ""))] |
"" |
{ |
#ifdef TARGET_THREAD_SSP_OFFSET |
operands[1] |
= gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), |
GEN_INT (TARGET_THREAD_SSP_OFFSET))); |
#endif |
if (TARGET_64BIT) |
emit_insn (gen_stack_protect_setdi (operands[0], operands[1])); |
else |
emit_insn (gen_stack_protect_setsi (operands[0], operands[1])); |
|
DONE; |
}) |
|
(define_insn "stack_protect_set<mode>" |
[(set (match_operand:DSI 0 "memory_operand" "=Q") |
(unspec:DSI [(match_operand:DSI 1 "memory_operand" "Q")] UNSPEC_SP_SET))] |
"" |
"mvc\t%O0(%G0,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
|
(define_expand "stack_protect_test" |
[(set (reg:CC CC_REGNUM) |
(compare (match_operand 0 "memory_operand" "") |
(match_operand 1 "memory_operand" ""))) |
(match_operand 2 "" "")] |
"" |
{ |
#ifdef TARGET_THREAD_SSP_OFFSET |
operands[1] |
= gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), |
GEN_INT (TARGET_THREAD_SSP_OFFSET))); |
#endif |
s390_compare_op0 = operands[0]; |
s390_compare_op1 = operands[1]; |
s390_compare_emitted = gen_rtx_REG (CCZmode, CC_REGNUM); |
|
if (TARGET_64BIT) |
emit_insn (gen_stack_protect_testdi (operands[0], operands[1])); |
else |
emit_insn (gen_stack_protect_testsi (operands[0], operands[1])); |
|
emit_jump_insn (gen_beq (operands[2])); |
|
DONE; |
}) |
|
(define_insn "stack_protect_test<mode>" |
[(set (reg:CCZ CC_REGNUM) |
(unspec:CCZ [(match_operand:DSI 0 "memory_operand" "Q") |
(match_operand:DSI 1 "memory_operand" "Q")] UNSPEC_SP_TEST))] |
"" |
"clc\t%O0(%G0,%R0),%S1" |
[(set_attr "op_type" "SS")]) |
/s390.opt
0,0 → 1,99
; Options for the S/390 / zSeries port of the compiler. |
|
; Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. |
; |
; 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/>. |
|
m31 |
Target Report RejectNegative InverseMask(64BIT) |
31 bit ABI |
|
m64 |
Target Report RejectNegative Mask(64BIT) |
64 bit ABI |
|
march= |
Target RejectNegative Joined Var(s390_arch_string) |
Generate code for given CPU |
|
mbackchain |
Target Report Mask(BACKCHAIN) |
Maintain backchain pointer |
|
mdebug |
Target Report Mask(DEBUG_ARG) |
Additional debug prints |
|
mesa |
Target Report RejectNegative InverseMask(ZARCH) |
ESA/390 architecture |
|
mfused-madd |
Target Report Mask(FUSED_MADD) |
Enable fused multiply/add instructions |
|
mhard-float |
Target Report RejectNegative Mask(HARD_FLOAT) |
Use hardware fp |
|
mlong-double-128 |
Target Report RejectNegative Mask(LONG_DOUBLE_128) |
Use 128-bit long double |
|
mlong-double-64 |
Target Report RejectNegative InverseMask(LONG_DOUBLE_128) |
Use 64-bit long double |
|
mpacked-stack |
Target Report Mask(PACKED_STACK) |
Use packed stack layout |
|
msmall-exec |
Target Report Mask(SMALL_EXEC) |
Use bras for executable < 64k |
|
msoft-float |
Target Report RejectNegative InverseMask(HARD_FLOAT, SOFT_FLOAT) |
Don't use hardware fp |
|
mstack-guard= |
Target RejectNegative Joined |
Set the max. number of bytes which has to be left to stack size before a trap instruction is triggered |
|
mstack-size= |
Target RejectNegative Joined |
Emit extra code in the function prologue in order to trap if the stack size exceeds the given limit |
|
mtune= |
Target RejectNegative Joined |
Schedule code for given CPU |
|
mmvcle |
Target Report Mask(MVCLE) |
mvcle use |
|
mwarn-dynamicstack |
Target RejectNegative Var(s390_warn_dynamicstack_p) |
Warn if a function uses alloca or creates an array with dynamic size |
|
mwarn-framesize= |
Target RejectNegative Joined |
Warn if a single function's framesize exceeds the given framesize |
|
mzarch |
Target Report RejectNegative Mask(ZARCH) |
z/Architecture |
/s390-modes.def
0,0 → 1,174
/* Definitions of target machine for GNU compiler, for IBM S/390 |
Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@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/>. */ |
|
/* 256-bit integer mode is needed for STACK_SAVEAREA_MODE. */ |
INT_MODE (OI, 32); |
|
/* Define TFmode to work around reload problem PR 20927. */ |
FLOAT_MODE (TF, 16, ieee_quad_format); |
|
/* Add any extra modes needed to represent the condition code. */ |
|
/* |
|
Condition Codes |
|
Check for zero |
|
CCZ: EQ NE NE NE |
CCZ1: EQ NE (CS) |
|
Unsigned compares |
|
CCU: EQ LTU GTU NE (CLG/R, CL/R/Y, CLM/Y, CLI/Y) |
CCUR: EQ GTU LTU NE (CLGF/R) |
|
Signed compares |
|
CCS: EQ LT GT UNORDERED (LTGFR, LTGR, LTR, ICM/Y, |
LTDBR, LTDR, LTEBR, LTER, |
CG/R, C/R/Y, CGHI, CHI, |
CDB/R, CD/R, CEB/R, CE/R, |
ADB/R, AEB/R, SDB/R, SEB/R, |
SRAG, SRA, SRDA) |
CCSR: EQ GT LT UNORDERED (CGF/R, CH/Y) |
|
Condition codes resulting from add with overflow |
|
CCA: EQ LT GT Overflow |
CCAP: EQ LT GT LT (AGHI, AHI) |
CCAN: EQ LT GT GT (AGHI, AHI) |
|
Condition codes of unsigned adds and subs |
|
CCL: EQ NE EQ NE (ALGF/R, ALG/R, AL/R/Y, |
ALCG/R, ALC/R, |
SLGF/R, SLG/R, SL/R/Y, |
SLBG/R, SLB/R) |
CCL1: GEU GEU LTU LTU (ALG/R, AL/R/Y) |
CCL2: GTU GTU LEU LEU (SLG/R, SL/R/Y) |
CCL3: EQ LTU EQ GTU (SLG/R, SL/R/Y) |
|
Test under mask checks |
|
CCT: EQ NE NE NE (ICM/Y, TML, CG/R, CGHI, |
C/R/Y, CHI, NG/R, N/R/Y, |
OG/R, O/R/Y, XG/R, X/R/Y) |
CCT1: NE EQ NE NE (TMH, TML) |
CCT2: NE NE EQ NE (TMH, TML) |
CCT3: NE NE NE EQ (TMH, TML) |
|
CCA and CCT modes are request only modes. These modes are never returned by |
s390_select_cc_mode. They are only intended to match other modes. |
|
Requested mode -> Destination CC register mode |
|
CCS, CCU, CCT, CCSR, CCUR -> CCZ |
CCA -> CCAP, CCAN |
|
|
*** Comments *** |
|
CCAP, CCAN |
|
The CC obtained from add instruction usually can't be used for comparisons |
because its coupling with overflow flag. In case of an overflow the |
less than/greater than data are lost. Nevertheless a comparison can be done |
whenever immediate values are involved because they are known at compile time. |
If you know whether the used constant is positive or negative you can predict |
the sign of the result even in case of an overflow. |
|
|
CCT, CCT1, CCT2, CCT3 |
|
If bits of an integer masked with an AND instruction are checked, the test under |
mask instructions turn out to be very handy for a set of special cases. |
The simple cases are checks whether all masked bits are zero or ones: |
|
int a; |
if ((a & (16 + 128)) == 0) -> CCT/CCZ |
if ((a & (16 + 128)) == 16 + 128) -> CCT3 |
|
Using two extra modes makes it possible to do complete checks on two bits of an |
integer (This is possible on register operands only. TM does not provide the |
information necessary for CCT1 and CCT2 modes.): |
|
int a; |
if ((a & (16 + 128)) == 16) -> CCT1 |
if ((a & (16 + 128)) == 128) -> CCT2 |
|
|
CCSR, CCUR |
|
There are several instructions comparing 32 bit with 64 bit unsigned/signed |
values. Such instructions can be considered to have a builtin zero/sign_extend. |
The problem is that in the RTL (to be canonical) the zero/sign extended operand |
has to be the first one but the machine instructions like it the other way |
around. The following both modes can be considered as CCS and CCU modes with |
exchanged operands. |
|
|
CCL1, CCL2 |
|
These modes represent the result of overflow checks. |
|
if (a + b < a) -> CCL1 state of the carry bit (CC2 | CC3) |
if (a - b > a) -> CCL2 state of the borrow bit (CC0 | CC1) |
|
They are used when multi word numbers are computed dealing one SImode part after |
another or whenever manual overflow checks like the examples above are |
compiled. |
|
|
CCL3 |
|
A logical subtract instruction sets the borrow bit in case of an overflow. |
The resulting condition code of those instructions is represented by the |
CCL3 mode. Together with the CCU mode this mode is used for jumpless |
implementations of several if-constructs - see s390_expand_addcc for more |
details. |
|
CCZ1 |
|
The compare and swap instructions sets the condition code to 0/1 if the |
operands were equal/unequal. The CCZ1 mode ensures the result can be |
effectively placed into a register. |
|
*/ |
|
|
CC_MODE (CCZ); |
CC_MODE (CCZ1); |
CC_MODE (CCA); |
CC_MODE (CCAP); |
CC_MODE (CCAN); |
CC_MODE (CCL); |
CC_MODE (CCL1); |
CC_MODE (CCL2); |
CC_MODE (CCL3); |
CC_MODE (CCU); |
CC_MODE (CCUR); |
CC_MODE (CCS); |
CC_MODE (CCSR); |
CC_MODE (CCT); |
CC_MODE (CCT1); |
CC_MODE (CCT2); |
CC_MODE (CCT3); |
/t-linux
0,0 → 1,3
# Override t-slibgcc-elf-ver to export some libgcc symbols with |
# the symbol versions that glibc used. |
SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver $(srcdir)/config/s390/libgcc-glibc.ver |
/t-crtstuff
0,0 → 1,5
# crtend*.o cannot be compiled without -fno-asynchronous-unwind-tables, |
# because then __FRAME_END__ might not be the last thing in .eh_frame |
# section. |
CRTSTUFF_T_CFLAGS = -fno-asynchronous-unwind-tables |
TARGET_LIBGCC2_CFLAGS += -mlong-double-128 |
/fixdfdi.h
0,0 → 1,456
/* Definitions of target machine for GNU compiler, for IBM S/390 |
Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@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/>. */ |
|
#ifdef L_fixunstfdi |
|
#define EXPD(fp) (((fp.l.i[0]) >> 16) & 0x7FFF) |
#define EXPONENT_BIAS 16383 |
#define MANTISSA_BITS 112 |
#define PRECISION (MANTISSA_BITS + 1) |
#define SIGNBIT 0x80000000 |
#define SIGND(fp) ((fp.l.i[0]) & SIGNBIT) |
#define MANTD_HIGH_LL(fp) ((fp.ll[0] & HIGH_LL_FRAC_MASK) | HIGH_LL_UNIT_BIT) |
#define MANTD_LOW_LL(fp) (fp.ll[1]) |
#define FRACD_ZERO_P(fp) (!fp.ll[1] && !(fp.ll[0] & HIGH_LL_FRAC_MASK)) |
#define HIGH_LL_FRAC_BITS 48 |
#define HIGH_LL_UNIT_BIT ((UDItype_x)1 << HIGH_LL_FRAC_BITS) |
#define HIGH_LL_FRAC_MASK (HIGH_LL_UNIT_BIT - 1) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union double_long { |
long double d; |
struct { |
SItype_x i[4]; /* 32 bit parts: 0 upper ... 3 lowest */ |
} l; |
UDItype_x ll[2]; /* 64 bit parts: 0 upper, 1 lower */ |
}; |
|
UDItype_x __fixunstfdi (long double a1); |
|
/* convert double to unsigned int */ |
UDItype_x |
__fixunstfdi (long double a1) |
{ |
register union double_long dl1; |
register int exp; |
register UDItype_x l; |
|
dl1.d = a1; |
|
/* +/- 0, denormalized, negative */ |
if (!EXPD (dl1) || SIGND(dl1)) |
return 0; |
|
/* The exponent - considered the binary point at the right end of |
the mantissa. */ |
exp = EXPD (dl1) - EXPONENT_BIAS - MANTISSA_BITS; |
|
/* number < 1: If the mantissa would need to be right-shifted more bits than |
its size (plus the implied one bit on the left) the result would be |
zero. */ |
if (exp <= -PRECISION) |
return 0; |
|
/* NaN: All exponent bits set and a nonzero fraction. */ |
if ((EXPD(dl1) == 0x7fff) && !FRACD_ZERO_P (dl1)) |
return 0x0ULL; |
|
/* If the upper ll part of the mantissa isn't |
zeroed out after shifting the number would be to large. */ |
if (exp >= -HIGH_LL_FRAC_BITS) |
return 0xFFFFFFFFFFFFFFFFULL; |
|
exp += HIGH_LL_FRAC_BITS + 1; |
|
l = MANTD_LOW_LL (dl1) >> (HIGH_LL_FRAC_BITS + 1) |
| MANTD_HIGH_LL (dl1) << (64 - (HIGH_LL_FRAC_BITS + 1)); |
|
return l >> -exp; |
} |
#define __fixunstfdi ___fixunstfdi |
#endif |
#undef L_fixunstfdi |
|
#ifdef L_fixtfdi |
#define EXPD(fp) (((fp.l.i[0]) >> 16) & 0x7FFF) |
#define EXPONENT_BIAS 16383 |
#define MANTISSA_BITS 112 |
#define PRECISION (MANTISSA_BITS + 1) |
#define SIGNBIT 0x80000000 |
#define SIGND(fp) ((fp.l.i[0]) & SIGNBIT) |
#define MANTD_HIGH_LL(fp) ((fp.ll[0] & HIGH_LL_FRAC_MASK) | HIGH_LL_UNIT_BIT) |
#define MANTD_LOW_LL(fp) (fp.ll[1]) |
#define FRACD_ZERO_P(fp) (!fp.ll[1] && !(fp.ll[0] & HIGH_LL_FRAC_MASK)) |
#define HIGH_LL_FRAC_BITS 48 |
#define HIGH_LL_UNIT_BIT ((UDItype_x)1 << HIGH_LL_FRAC_BITS) |
#define HIGH_LL_FRAC_MASK (HIGH_LL_UNIT_BIT - 1) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union double_long { |
long double d; |
struct { |
SItype_x i[4]; /* 32 bit parts: 0 upper ... 3 lowest */ |
} l; |
DItype_x ll[2]; /* 64 bit parts: 0 upper, 1 lower */ |
}; |
|
DItype_x __fixtfdi (long double a1); |
|
/* convert double to unsigned int */ |
DItype_x |
__fixtfdi (long double a1) |
{ |
register union double_long dl1; |
register int exp; |
register UDItype_x l; |
|
dl1.d = a1; |
|
/* +/- 0, denormalized */ |
if (!EXPD (dl1)) |
return 0; |
|
/* The exponent - considered the binary point at the right end of |
the mantissa. */ |
exp = EXPD (dl1) - EXPONENT_BIAS - MANTISSA_BITS; |
|
/* number < 1: If the mantissa would need to be right-shifted more bits than |
its size the result would be zero. */ |
if (exp <= -PRECISION) |
return 0; |
|
/* NaN: All exponent bits set and a nonzero fraction. */ |
if ((EXPD(dl1) == 0x7fff) && !FRACD_ZERO_P (dl1)) |
return 0x8000000000000000ULL; |
|
/* If the upper ll part of the mantissa isn't |
zeroed out after shifting the number would be to large. */ |
if (exp >= -HIGH_LL_FRAC_BITS) |
{ |
l = (long long)1 << 63; /* long int min */ |
return SIGND (dl1) ? l : l - 1; |
} |
|
/* The extra bit is needed for the sign bit. */ |
exp += HIGH_LL_FRAC_BITS + 1; |
|
l = MANTD_LOW_LL (dl1) >> (HIGH_LL_FRAC_BITS + 1) |
| MANTD_HIGH_LL (dl1) << (64 - (HIGH_LL_FRAC_BITS + 1)); |
|
return SIGND (dl1) ? -(l >> -exp) : l >> -exp; |
} |
#define __fixtfdi ___fixtfdi |
#endif |
#undef L_fixtfdi |
|
#ifdef L_fixunsdfdi |
#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) |
#define EXCESSD 1022 |
#define SIGNBIT 0x80000000 |
#define SIGND(fp) ((fp.l.upper) & SIGNBIT) |
#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) |
#define FRACD_LL(fp) (fp.ll & (HIDDEND_LL-1)) |
#define HIDDEND_LL ((UDItype_x)1 << 52) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union double_long { |
double d; |
struct { |
SItype_x upper; |
USItype_x lower; |
} l; |
UDItype_x ll; |
}; |
|
UDItype_x __fixunsdfdi (double a1); |
|
/* convert double to unsigned int */ |
UDItype_x |
__fixunsdfdi (double a1) |
{ |
register union double_long dl1; |
register int exp; |
register UDItype_x l; |
|
dl1.d = a1; |
|
/* +/- 0, denormalized, negative */ |
|
if (!EXPD (dl1) || SIGND(dl1)) |
return 0; |
|
exp = EXPD (dl1) - EXCESSD - 53; |
|
/* number < 1 */ |
|
if (exp < -53) |
return 0; |
|
/* NaN */ |
|
if ((EXPD(dl1) == 0x7ff) && (FRACD_LL(dl1) != 0)) /* NaN */ |
return 0x0ULL; |
|
/* Number big number & + inf */ |
|
if (exp >= 12) { |
return 0xFFFFFFFFFFFFFFFFULL; |
} |
|
l = MANTD_LL(dl1); |
|
/* shift down until exp < 12 or l = 0 */ |
if (exp > 0) |
l <<= exp; |
else |
l >>= -exp; |
|
return l; |
} |
#define __fixunsdfdi ___fixunsdfdi |
#endif |
#undef L_fixunsdfdi |
|
#ifdef L_fixdfdi |
#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) |
#define EXCESSD 1022 |
#define SIGNBIT 0x80000000 |
#define SIGND(fp) ((fp.l.upper) & SIGNBIT) |
#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) |
#define FRACD_LL(fp) (fp.ll & (HIDDEND_LL-1)) |
#define HIDDEND_LL ((UDItype_x)1 << 52) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union double_long { |
double d; |
struct { |
SItype_x upper; |
USItype_x lower; |
} l; |
UDItype_x ll; |
}; |
|
DItype_x __fixdfdi (double a1); |
|
/* convert double to int */ |
DItype_x |
__fixdfdi (double a1) |
{ |
register union double_long dl1; |
register int exp; |
register DItype_x l; |
|
dl1.d = a1; |
|
/* +/- 0, denormalized */ |
|
if (!EXPD (dl1)) |
return 0; |
|
exp = EXPD (dl1) - EXCESSD - 53; |
|
/* number < 1 */ |
|
if (exp < -53) |
return 0; |
|
/* NaN */ |
|
if ((EXPD(dl1) == 0x7ff) && (FRACD_LL(dl1) != 0)) /* NaN */ |
return 0x8000000000000000ULL; |
|
/* Number big number & +/- inf */ |
|
if (exp >= 11) { |
l = (long long)1<<63; |
if (!SIGND(dl1)) |
l--; |
return l; |
} |
|
l = MANTD_LL(dl1); |
|
/* shift down until exp < 12 or l = 0 */ |
if (exp > 0) |
l <<= exp; |
else |
l >>= -exp; |
|
return (SIGND (dl1) ? -l : l); |
} |
#define __fixdfdi ___fixdfdi |
#endif |
#undef L_fixdfdi |
|
#ifdef L_fixunssfdi |
#define EXP(fp) (((fp.l) >> 23) & 0xFF) |
#define EXCESS 126 |
#define SIGNBIT 0x80000000 |
#define SIGN(fp) ((fp.l) & SIGNBIT) |
#define HIDDEN (1 << 23) |
#define MANT(fp) (((fp.l) & 0x7FFFFF) | HIDDEN) |
#define FRAC(fp) ((fp.l) & 0x7FFFFF) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union float_long |
{ |
float f; |
USItype_x l; |
}; |
|
UDItype_x __fixunssfdi (float a1); |
|
/* convert float to unsigned int */ |
UDItype_x |
__fixunssfdi (float a1) |
{ |
register union float_long fl1; |
register int exp; |
register UDItype_x l; |
|
fl1.f = a1; |
|
/* +/- 0, denormalized, negative */ |
|
if (!EXP (fl1) || SIGN(fl1)) |
return 0; |
|
exp = EXP (fl1) - EXCESS - 24; |
|
/* number < 1 */ |
|
if (exp < -24) |
return 0; |
|
/* NaN */ |
|
if ((EXP(fl1) == 0xff) && (FRAC(fl1) != 0)) /* NaN */ |
return 0x0ULL; |
|
/* Number big number & + inf */ |
|
if (exp >= 41) { |
return 0xFFFFFFFFFFFFFFFFULL; |
} |
|
l = MANT(fl1); |
|
if (exp > 0) |
l <<= exp; |
else |
l >>= -exp; |
|
return l; |
} |
#define __fixunssfdi ___fixunssfdi |
#endif |
#undef L_fixunssfdi |
|
#ifdef L_fixsfdi |
#define EXP(fp) (((fp.l) >> 23) & 0xFF) |
#define EXCESS 126 |
#define SIGNBIT 0x80000000 |
#define SIGN(fp) ((fp.l) & SIGNBIT) |
#define HIDDEN (1 << 23) |
#define MANT(fp) (((fp.l) & 0x7FFFFF) | HIDDEN) |
#define FRAC(fp) ((fp.l) & 0x7FFFFF) |
|
typedef int DItype_x __attribute__ ((mode (DI))); |
typedef unsigned int UDItype_x __attribute__ ((mode (DI))); |
typedef int SItype_x __attribute__ ((mode (SI))); |
typedef unsigned int USItype_x __attribute__ ((mode (SI))); |
|
union float_long |
{ |
float f; |
USItype_x l; |
}; |
|
DItype_x __fixsfdi (float a1); |
|
/* convert double to int */ |
DItype_x |
__fixsfdi (float a1) |
{ |
register union float_long fl1; |
register int exp; |
register DItype_x l; |
|
fl1.f = a1; |
|
/* +/- 0, denormalized */ |
|
if (!EXP (fl1)) |
return 0; |
|
exp = EXP (fl1) - EXCESS - 24; |
|
/* number < 1 */ |
|
if (exp < -24) |
return 0; |
|
/* NaN */ |
|
if ((EXP(fl1) == 0xff) && (FRAC(fl1) != 0)) /* NaN */ |
return 0x8000000000000000ULL; |
|
/* Number big number & +/- inf */ |
|
if (exp >= 40) { |
l = (long long)1<<63; |
if (!SIGN(fl1)) |
l--; |
return l; |
} |
|
l = MANT(fl1); |
|
if (exp > 0) |
l <<= exp; |
else |
l >>= -exp; |
|
return (SIGN (fl1) ? -l : l); |
} |
#define __fixsfdi ___fixsfdi |
#endif |
#undef L_fixsfdi |
/constraints.md
0,0 → 1,437
;; Constraints definitions belonging to the gcc backend for IBM S/390. |
;; Copyright (C) 2006, 2007 Free Software Foundation, Inc. |
;; Written by Wolfgang Gellerich, using code and information found in |
;; files s390.md, s390.h, and s390.c. |
;; |
;; 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/>. |
|
|
;; |
;; Special constraints for s/390 machine description: |
;; |
;; a -- Any address register from 1 to 15. |
;; c -- Condition code register 33. |
;; d -- Any register from 0 to 15. |
;; f -- Floating point registers. |
;; t -- Access registers 36 and 37. |
;; G -- Const double zero operand |
;; I -- An 8-bit constant (0..255). |
;; J -- A 12-bit constant (0..4095). |
;; K -- A 16-bit constant (-32768..32767). |
;; L -- Value appropriate as displacement. |
;; (0..4095) for short displacement |
;; (-524288..524287) for long displacement |
;; M -- Constant integer with a value of 0x7fffffff. |
;; N -- Multiple letter constraint followed by 4 parameter letters. |
;; 0..9,x: number of the part counting from most to least significant |
;; H,Q: mode of the part |
;; D,S,H: mode of the containing operand |
;; 0,F: value of the other parts (F - all bits set) |
;; |
;; The constraint matches if the specified part of a constant |
;; has a value different from its other parts. If the letter x |
;; is specified instead of a part number, the constraint matches |
;; if there is any single part with non-default value. |
;; O -- Multiple letter constraint followed by 1 parameter. |
;; s: Signed extended immediate value (-2G .. 2G-1). |
;; p: Positive extended immediate value (0 .. 4G-1). |
;; n: Negative extended immediate value (-4G .. -1). |
;; These constraints do not accept any operand if the machine does |
;; not provide the extended-immediate facility. |
;; P -- Any integer constant that can be loaded without literal pool. |
;; Q -- Memory reference without index register and with short displacement. |
;; R -- Memory reference with index register and short displacement. |
;; S -- Memory reference without index register but with long displacement. |
;; T -- Memory reference with index register and long displacement. |
;; A -- Multiple letter constraint followed by Q, R, S, or T: |
;; Offsettable memory reference of type specified by second letter. |
;; B -- Multiple letter constraint followed by Q, R, S, or T: |
;; Memory reference of the type specified by second letter that |
;; does *not* refer to a literal pool entry. |
;; U -- Pointer with short displacement. |
;; W -- Pointer with long displacement. |
;; Y -- Shift count operand. |
;; |
|
|
;; |
;; Register constraints. |
;; |
|
(define_register_constraint "a" |
"ADDR_REGS" |
"Any address register from 1 to 15.") |
|
|
(define_register_constraint "c" |
"CC_REGS" |
"Condition code register 33") |
|
|
(define_register_constraint "d" |
"GENERAL_REGS" |
"Any register from 0 to 15") |
|
|
(define_register_constraint "f" |
"FP_REGS" |
"Floating point registers") |
|
|
(define_register_constraint "t" |
"ACCESS_REGS" |
"@internal |
Access registers 36 and 37") |
|
|
;; |
;; General constraints for constants. |
;; |
|
(define_constraint "G" |
"@internal |
Const double zero operand" |
(and (match_code "const_double") |
(match_test "s390_float_const_zero_p (op)"))) |
|
|
(define_constraint "I" |
"An 8-bit constant (0..255)" |
(and (match_code "const_int") |
(match_test "(unsigned int) ival <= 255"))) |
|
|
(define_constraint "J" |
"A 12-bit constant (0..4095)" |
(and (match_code "const_int") |
(match_test "(unsigned int) ival <= 4095"))) |
|
|
(define_constraint "K" |
"A 16-bit constant (-32768..32767)" |
(and (match_code "const_int") |
(match_test "ival >= -32768 && ival <= 32767"))) |
|
|
|
(define_constraint "L" |
"Value appropriate as displacement. |
(0..4095) for short displacement |
(-524288..524287) for long displacement" |
(and (match_code "const_int") |
(match_test "TARGET_LONG_DISPLACEMENT ? |
(ival >= -524288 && ival <= 524287) |
: (ival >= 0 && ival <= 4095)"))) |
|
|
(define_constraint "M" |
"Constant integer with a value of 0x7fffffff" |
(and (match_code "const_int") |
(match_test "ival == 2147483647"))) |
|
|
(define_constraint "P" |
"@internal |
Any integer constant that can be loaded without literal pool" |
(and (match_code "const_int") |
(match_test "legitimate_reload_constant_p (GEN_INT (ival))"))) |
|
|
(define_address_constraint "Y" |
"Shift count operand" |
|
;; Simply check for the basic form of a shift count. Reload will |
;; take care of making sure we have a proper base register. |
|
(match_test "s390_decompose_shift_count (op, NULL, NULL)" )) |
|
|
;; N -- Multiple letter constraint followed by 4 parameter letters. |
;; 0..9,x: number of the part counting from most to least significant |
;; H,Q: mode of the part |
;; D,S,H: mode of the containing operand |
;; 0,F: value of the other parts (F = all bits set) |
;; |
;; The constraint matches if the specified part of a constant |
;; has a value different from its other parts. If the letter x |
;; is specified instead of a part number, the constraint matches |
;; if there is any single part with non-default value. |
;; |
;; The following patterns define only those constraints that are actually |
;; used in s390.md. If you need an additional one, simply add it in the |
;; obvious way. Function s390_N_constraint_str is ready to handle all |
;; combinations. |
;; |
|
|
(define_constraint "NxQS0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQS0\", ival)"))) |
|
|
(define_constraint "NxQD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQD0\", ival)"))) |
|
|
(define_constraint "N3HD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"3HD0\", ival)"))) |
|
|
(define_constraint "N2HD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"2HD0\", ival)"))) |
|
|
(define_constraint "N1SD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1SD0\", ival)"))) |
|
|
(define_constraint "N1HS0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1HS0\", ival)"))) |
|
|
(define_constraint "N1HD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1HD0\", ival)"))) |
|
|
(define_constraint "N0SD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0SD0\", ival)"))) |
|
|
(define_constraint "N0HS0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0HS0\", ival)"))) |
|
|
(define_constraint "N0HD0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0HD0\", ival)"))) |
|
|
(define_constraint "NxQDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQDF\", ival)"))) |
|
|
(define_constraint "N1SDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1SDF\", ival)"))) |
|
|
(define_constraint "N0SDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0SDF\", ival)"))) |
|
|
(define_constraint "N3HDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"3HDF\", ival)"))) |
|
|
(define_constraint "N2HDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"2HDF\", ival)"))) |
|
|
(define_constraint "N1HDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1HDF\", ival)"))) |
|
|
(define_constraint "N0HDF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0HDF\", ival)"))) |
|
|
(define_constraint "N0HSF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"0HSF\", ival)"))) |
|
|
(define_constraint "N1HSF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"1HSF\", ival)"))) |
|
|
(define_constraint "NxQSF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQSF\", ival)"))) |
|
|
(define_constraint "NxQHF" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQHF\", ival)"))) |
|
|
(define_constraint "NxQH0" |
"@internal" |
(and (match_code "const_int") |
(match_test "s390_N_constraint_str (\"xQH0\", ival)"))) |
|
|
|
|
;; |
;; Double-letter constraints starting with O follow. |
;; |
|
|
(define_constraint "Os" |
"@internal |
Signed extended immediate value (-2G .. 2G-1). |
This constraint will only match if the machine provides |
the extended-immediate facility." |
(and (match_code "const_int") |
(match_test "s390_O_constraint_str ('s', ival)"))) |
|
|
(define_constraint "Op" |
"@internal |
Positive extended immediate value (0 .. 4G-1). |
This constraint will only match if the machine provides |
the extended-immediate facility." |
(and (match_code "const_int") |
(match_test "s390_O_constraint_str ('p', ival)"))) |
|
|
(define_constraint "On" |
"@internal |
Negative extended immediate value (-4G .. -1). |
This constraint will only match if the machine provides |
the extended-immediate facility." |
(and (match_code "const_int") |
(match_test "s390_O_constraint_str ('n', ival)"))) |
|
|
|
|
;; |
;; Memory constraints follow. |
;; |
|
(define_memory_constraint "Q" |
"Memory reference without index register and with short displacement" |
(match_test "s390_mem_constraint (\"Q\", op)")) |
|
|
|
(define_memory_constraint "R" |
"Memory reference with index register and short displacement" |
(match_test "s390_mem_constraint (\"R\", op)")) |
|
|
(define_memory_constraint "S" |
"Memory reference without index register but with long displacement" |
(match_test "s390_mem_constraint (\"S\", op)")) |
|
|
(define_memory_constraint "T" |
"Memory reference with index register and long displacement" |
(match_test "s390_mem_constraint (\"T\", op)")) |
|
|
|
(define_memory_constraint "AQ" |
"@internal |
Offsettable memory reference without index register and with short displacement" |
(match_test "s390_mem_constraint (\"AQ\", op)")) |
|
|
(define_memory_constraint "AR" |
"@internal |
Offsettable memory reference with index register and short displacement" |
(match_test "s390_mem_constraint (\"AR\", op)")) |
|
|
(define_memory_constraint "AS" |
"@internal |
Offsettable memory reference without index register but with long displacement" |
(match_test "s390_mem_constraint (\"AS\", op)")) |
|
|
(define_memory_constraint "AT" |
"@internal |
Offsettable memory reference with index register and long displacement" |
(match_test "s390_mem_constraint (\"AT\", op)")) |
|
|
|
(define_constraint "BQ" |
"@internal |
Memory reference without index register and with short |
displacement that does *not* refer to a literal pool entry." |
(match_test "s390_mem_constraint (\"BQ\", op)")) |
|
|
(define_constraint "BR" |
"@internal |
Memory reference with index register and short displacement that |
does *not* refer to a literal pool entry. " |
(match_test "s390_mem_constraint (\"BR\", op)")) |
|
|
(define_constraint "BS" |
"@internal |
Memory reference without index register but with long displacement |
that does *not* refer to a literal pool entry. " |
(match_test "s390_mem_constraint (\"BS\", op)")) |
|
|
(define_constraint "BT" |
"@internal |
Memory reference with index register and long displacement that |
does *not* refer to a literal pool entry. " |
(match_test "s390_mem_constraint (\"BT\", op)")) |
|
|
|
(define_address_constraint "U" |
"Pointer with short displacement" |
(match_test "s390_mem_constraint (\"U\", op)")) |
|
|
|
(define_address_constraint "W" |
"Pointer with long displacement" |
(match_test "s390_mem_constraint (\"W\", op)")) |
/s390-protos.h
0,0 → 1,128
/* Definitions of target machine for GNU compiler, for IBM S/390. |
Copyright (C) 2000, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@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/>. */ |
|
|
|
/* Prototypes of functions used for constraint evaluation in |
constraints.c. */ |
|
extern int s390_mem_constraint (const char *str, rtx op); |
extern int s390_O_constraint_str (const char c, HOST_WIDE_INT value); |
extern int s390_N_constraint_str (const char *str, HOST_WIDE_INT value); |
extern int s390_float_const_zero_p (rtx value); |
|
|
|
/* Declare functions in s390.c. */ |
|
extern void optimization_options (int, int); |
extern void override_options (void); |
extern bool s390_can_eliminate (int, int); |
extern HOST_WIDE_INT s390_initial_elimination_offset (int, int); |
extern void s390_emit_prologue (void); |
extern void s390_emit_epilogue (bool); |
extern void s390_function_profiler (FILE *, int); |
extern void s390_conditional_register_usage (void); |
extern void s390_set_has_landing_pad_p (bool); |
extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode); |
extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int); |
extern bool s390_class_max_nregs (enum reg_class, enum machine_mode); |
|
#ifdef RTX_CODE |
extern int s390_extra_constraint_str (rtx, int, const char *); |
extern int s390_const_ok_for_constraint_p (HOST_WIDE_INT, int, const char *); |
extern int s390_const_double_ok_for_constraint_p (rtx, int, const char *); |
extern int s390_single_part (rtx, enum machine_mode, enum machine_mode, int); |
extern unsigned HOST_WIDE_INT s390_extract_part (rtx, enum machine_mode, int); |
extern bool s390_split_ok_p (rtx, rtx, enum machine_mode, int); |
extern bool s390_overlap_p (rtx, rtx, HOST_WIDE_INT); |
extern bool s390_offset_p (rtx, rtx, rtx); |
extern int tls_symbolic_operand (rtx); |
|
extern bool s390_match_ccmode (rtx, enum machine_mode); |
extern enum machine_mode s390_tm_ccmode (rtx, rtx, bool); |
extern enum machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx); |
extern void s390_canonicalize_comparison (enum rtx_code *, rtx *, rtx *); |
extern rtx s390_emit_compare (enum rtx_code, rtx, rtx); |
extern void s390_emit_jump (rtx, rtx); |
extern bool symbolic_reference_mentioned_p (rtx); |
extern bool tls_symbolic_reference_mentioned_p (rtx); |
extern bool legitimate_la_operand_p (rtx); |
extern bool preferred_la_operand_p (rtx, rtx); |
extern int legitimate_pic_operand_p (rtx); |
extern int legitimate_constant_p (rtx); |
extern bool legitimate_reload_constant_p (rtx); |
extern bool legitimate_address_p (enum machine_mode, rtx, int); |
extern rtx legitimize_pic_address (rtx, rtx); |
extern rtx legitimize_address (rtx, rtx, enum machine_mode); |
extern rtx legitimize_reload_address (rtx, enum machine_mode, int, int); |
extern enum reg_class s390_preferred_reload_class (rtx, enum reg_class); |
extern enum reg_class s390_secondary_input_reload_class (enum reg_class, |
enum machine_mode, |
rtx); |
extern enum reg_class s390_secondary_output_reload_class (enum reg_class, |
enum machine_mode, |
rtx); |
extern void s390_expand_plus_operand (rtx, rtx, rtx); |
extern void emit_symbolic_move (rtx *); |
extern void s390_load_address (rtx, rtx); |
extern void s390_expand_movmem (rtx, rtx, rtx); |
extern void s390_expand_setmem (rtx, rtx, rtx); |
extern void s390_expand_cmpmem (rtx, rtx, rtx, rtx); |
extern bool s390_expand_addcc (enum rtx_code, rtx, rtx, rtx, rtx, rtx); |
extern bool s390_expand_insv (rtx, rtx, rtx, rtx); |
extern void s390_expand_cs_hqi (enum machine_mode, rtx, rtx, rtx, rtx); |
extern void s390_expand_atomic (enum machine_mode, enum rtx_code, |
rtx, rtx, rtx, bool); |
extern rtx s390_return_addr_rtx (int, rtx); |
extern rtx s390_back_chain_rtx (void); |
extern rtx s390_emit_call (rtx, rtx, rtx, rtx); |
extern void s390_expand_logical_operator (enum rtx_code, |
enum machine_mode, rtx *); |
extern bool s390_logical_operator_ok_p (rtx *); |
extern void s390_narrow_logical_operator (enum rtx_code, rtx *, rtx *); |
extern void s390_split_access_reg (rtx, rtx *, rtx *); |
|
extern bool s390_output_addr_const_extra (FILE*, rtx); |
extern void print_operand_address (FILE *, rtx); |
extern void print_operand (FILE *, rtx, int); |
extern void s390_output_pool_entry (rtx, enum machine_mode, unsigned int); |
extern void s390_trampoline_template (FILE *); |
extern void s390_initialize_trampoline (rtx, rtx, rtx); |
extern rtx s390_gen_rtx_const_DI (int, int); |
extern int s390_agen_dep_p (rtx, rtx); |
extern rtx s390_load_got (void); |
extern rtx s390_get_thread_pointer (void); |
extern void s390_emit_tpf_eh_return (rtx); |
extern bool s390_legitimate_address_without_index_p (rtx); |
extern bool s390_decompose_shift_count (rtx, rtx *, HOST_WIDE_INT *); |
extern int s390_branch_condition_mask (rtx); |
|
#endif /* RTX_CODE */ |
|
#ifdef TREE_CODE |
extern void s390_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, |
tree, int); |
#ifdef RTX_CODE |
extern rtx s390_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree, int); |
extern rtx s390_function_value (tree, enum machine_mode); |
extern void s390_va_start (tree, rtx); |
#endif /* RTX_CODE */ |
#endif /* TREE_CODE */ |
/t-linux64
0,0 → 1,8
MULTILIB_OPTIONS = m64/m31 |
MULTILIB_DIRNAMES = 64 32 |
MULTILIB_OSDIRNAMES = ../lib64 ../lib |
|
LIBGCC = stmp-multilib |
INSTALL_LIBGCC = install-multilib |
|
EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o |
/s390x.h
0,0 → 1,27
/* Definitions of target machine for IBM zSeries 64-bit |
Copyright (C) 2002, 2007 Free Software Foundation, Inc. |
Contributed by Hartmut Penner (hpenner@de.ibm.com) and |
Ulrich Weigand (uweigand@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/>. */ |
|
#ifndef _S390X_H |
#define _S390X_H |
|
#define DEFAULT_TARGET_64BIT |
|
#endif |