summaryrefslogtreecommitdiff
path: root/gcc/config/s390
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/s390')
-rw-r--r--gcc/config/s390/htmintrin.h57
-rw-r--r--gcc/config/s390/htmxlintrin.h182
-rw-r--r--gcc/config/s390/predicates.md12
-rw-r--r--gcc/config/s390/s390-modes.def9
-rw-r--r--gcc/config/s390/s390-protos.h3
-rw-r--r--gcc/config/s390/s390.c564
-rw-r--r--gcc/config/s390/s390.h37
-rw-r--r--gcc/config/s390/s390.md281
-rw-r--r--gcc/config/s390/s390.opt4
-rw-r--r--gcc/config/s390/s390intrin.h33
10 files changed, 1106 insertions, 76 deletions
diff --git a/gcc/config/s390/htmintrin.h b/gcc/config/s390/htmintrin.h
new file mode 100644
index 00000000000..7aaa9f5bf7c
--- /dev/null
+++ b/gcc/config/s390/htmintrin.h
@@ -0,0 +1,57 @@
+/* GNU compiler hardware transactional execution intrinsics
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Contributed by Andreas Krebbel (Andreas.Krebbel@de.ibm.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef _HTMINTRIN_H
+#define _HTMINTRIN_H
+
+
+/* Condition codes generated by tbegin */
+#define _HTM_TBEGIN_STARTED 0
+#define _HTM_TBEGIN_INDETERMINATE 1
+#define _HTM_TBEGIN_TRANSIENT 2
+#define _HTM_TBEGIN_PERSISTENT 3
+
+/* The abort codes below this threshold are reserved for machine
+ use. */
+#define _HTM_FIRST_USER_ABORT_CODE 256
+
+/* The transaction diagnostic block is it is defined in the Principles
+ of Operation chapter 5-91. */
+
+struct __htm_tdb {
+ unsigned char format; /* 0 */
+ unsigned char flags;
+ unsigned char reserved1[4];
+ unsigned short nesting_depth;
+ unsigned long long abort_code; /* 8 */
+ unsigned long long conflict_token; /* 16 */
+ unsigned long long atia; /* 24 */
+ unsigned char eaid; /* 32 */
+ unsigned char dxc;
+ unsigned char reserved2[2];
+ unsigned int program_int_id;
+ unsigned long long exception_id; /* 40 */
+ unsigned long long bea; /* 48 */
+ unsigned char reserved3[72]; /* 56 */
+ unsigned long long gprs[16]; /* 128 */
+} __attribute__((__packed__, __aligned__ (8)));
+
+
+#endif /* _HTMINTRIN_H */
diff --git a/gcc/config/s390/htmxlintrin.h b/gcc/config/s390/htmxlintrin.h
new file mode 100644
index 00000000000..bb142195b2b
--- /dev/null
+++ b/gcc/config/s390/htmxlintrin.h
@@ -0,0 +1,182 @@
+/* XL compiler hardware transactional execution intrinsics
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Contributed by Andreas Krebbel (Andreas.Krebbel@de.ibm.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef _HTMXLINTRIN_H
+#define _HTMXLINTRIN_H
+
+#include <stdint.h>
+
+#include <htmintrin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These intrinsics are being made available for compatibility with
+ the IBM XL compiler. For documentation please see the "z/OS XL
+ C/C++ Programming Guide" publically available on the web. */
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_simple_begin ()
+{
+ return __builtin_tbegin_nofloat (0);
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_begin (void* const tdb)
+{
+ return __builtin_tbegin_nofloat (tdb);
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_end ()
+{
+ return __builtin_tend ();
+}
+
+extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_abort ()
+{
+ return __builtin_tabort (_HTM_FIRST_USER_ABORT_CODE);
+}
+
+extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_named_abort (unsigned char const code)
+{
+ return __builtin_tabort ((int)_HTM_FIRST_USER_ABORT_CODE + code);
+}
+
+extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_non_transactional_store (void* const addr, long long const value)
+{
+ __builtin_non_tx_store ((uint64_t*)addr, (uint64_t)value);
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_nesting_depth (void* const tdb_ptr)
+{
+ int depth = __builtin_tx_nesting_depth ();
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ if (depth != 0)
+ return depth;
+
+ if (tdb->format == 0)
+ return 0;
+ return tdb->nesting_depth;
+}
+
+/* Transaction failure diagnostics */
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_user_abort (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ if (tdb->format == 0)
+ return 0;
+
+ return !!(tdb->abort_code >= _HTM_FIRST_USER_ABORT_CODE);
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_named_user_abort (void* const tdb_ptr, unsigned char* code)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ if (tdb->format == 0)
+ return 0;
+
+ if (tdb->abort_code >= _HTM_FIRST_USER_ABORT_CODE)
+ {
+ *code = tdb->abort_code - _HTM_FIRST_USER_ABORT_CODE;
+ return 1;
+ }
+ return 0;
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_illegal (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ return (tdb->format == 0
+ && (tdb->abort_code == 4 /* unfiltered program interruption */
+ || tdb->abort_code == 11 /* restricted instruction */));
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_footprint_exceeded (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ return (tdb->format == 0
+ && (tdb->abort_code == 7 /* fetch overflow */
+ || tdb->abort_code == 8 /* store overflow */));
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_nested_too_deep (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ return tdb->format == 0 && tdb->abort_code == 13; /* depth exceeded */
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_conflict (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ return (tdb->format == 0
+ && (tdb->abort_code == 9 /* fetch conflict */
+ || tdb->abort_code == 10 /* store conflict */));
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_is_failure_persistent (long const result)
+{
+ return result == _HTM_TBEGIN_PERSISTENT;
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_failure_address (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+#ifdef __s390x__
+ return tdb->atia;
+#else
+ return tdb->atia & 0xffffffff;
+#endif
+}
+
+extern __inline long __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+__TM_failure_code (void* const tdb_ptr)
+{
+ struct __htm_tdb *tdb = (struct __htm_tdb*)tdb_ptr;
+
+ return tdb->abort_code;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTMXLINTRIN_H */
diff --git a/gcc/config/s390/predicates.md b/gcc/config/s390/predicates.md
index 523326e177d..069b42489a7 100644
--- a/gcc/config/s390/predicates.md
+++ b/gcc/config/s390/predicates.md
@@ -176,7 +176,11 @@
{
if (GET_CODE (XEXP (op, 0)) != REG
|| REGNO (XEXP (op, 0)) != CC_REGNUM
- || XEXP (op, 1) != const0_rtx)
+ || (XEXP (op, 1) != const0_rtx
+ && !(CONST_INT_P (XEXP (op, 1))
+ && GET_MODE (XEXP (op, 0)) == CCRAWmode
+ && INTVAL (XEXP (op, 1)) >= 0
+ && INTVAL (XEXP (op, 1)) <= 15)))
return false;
return (s390_branch_condition_mask (op) >= 0);
@@ -224,7 +228,11 @@
if (GET_CODE (XEXP (op, 0)) != REG
|| REGNO (XEXP (op, 0)) != CC_REGNUM
- || XEXP (op, 1) != const0_rtx)
+ || (XEXP (op, 1) != const0_rtx
+ && !(CONST_INT_P (XEXP (op, 1))
+ && GET_MODE (XEXP (op, 0)) == CCRAWmode
+ && INTVAL (XEXP (op, 1)) >= 0
+ && INTVAL (XEXP (op, 1)) <= 15)))
return false;
switch (GET_MODE (XEXP (op, 0)))
diff --git a/gcc/config/s390/s390-modes.def b/gcc/config/s390/s390-modes.def
index 419108fb473..5e0b50cafa1 100644
--- a/gcc/config/s390/s390-modes.def
+++ b/gcc/config/s390/s390-modes.def
@@ -152,6 +152,14 @@ 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.
+CCRAW
+
+The cc mode generated by a non-compare instruction. The condition
+code mask for the CC consumer is determined by the comparison operator
+(only EQ and NE allowed) and the immediate value given as second
+operand to the operator. For the other CC modes this value used to be
+0.
+
*/
@@ -172,3 +180,4 @@ CC_MODE (CCT);
CC_MODE (CCT1);
CC_MODE (CCT2);
CC_MODE (CCT3);
+CC_MODE (CCRAW);
diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h
index 1a8205359e4..67283df4553 100644
--- a/gcc/config/s390/s390-protos.h
+++ b/gcc/config/s390/s390-protos.h
@@ -58,7 +58,7 @@ 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 rtx s390_emit_compare (enum rtx_code, rtx, rtx);
-extern void s390_emit_jump (rtx, rtx);
+extern rtx 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);
@@ -87,6 +87,7 @@ extern void s390_expand_cs_hqi (enum machine_mode, rtx, rtx, rtx,
rtx, rtx, bool);
extern void s390_expand_atomic (enum machine_mode, enum rtx_code,
rtx, rtx, rtx, bool);
+extern void s390_expand_tbegin (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);
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 06d1228ff06..2cacf6f52ad 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -367,6 +367,10 @@ struct GTY(()) machine_function
const char *some_ld_name;
bool has_landing_pad_p;
+
+ /* True if the current function may contain a tbegin clobbering
+ FPRs. */
+ bool tbegin_p;
};
/* Few accessor macros for struct cfun->machine->s390_frame_layout. */
@@ -824,9 +828,9 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
*op1 = constm1_rtx;
}
- /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible. */
+ /* Remove redundant UNSPEC_STRCMPCC_TO_INT conversions if possible. */
if (GET_CODE (*op0) == UNSPEC
- && XINT (*op0, 1) == UNSPEC_CCU_TO_INT
+ && XINT (*op0, 1) == UNSPEC_STRCMPCC_TO_INT
&& XVECLEN (*op0, 0) == 1
&& GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
&& GET_CODE (XVECEXP (*op0, 0, 0)) == REG
@@ -852,25 +856,35 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
}
}
- /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible. */
+ /* Remove redundant UNSPEC_CC_TO_INT conversions if possible. */
if (GET_CODE (*op0) == UNSPEC
- && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT
+ && XINT (*op0, 1) == UNSPEC_CC_TO_INT
&& XVECLEN (*op0, 0) == 1
- && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode
&& GET_CODE (XVECEXP (*op0, 0, 0)) == REG
&& REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
- && *op1 == const0_rtx)
+ && CONST_INT_P (*op1))
{
enum rtx_code new_code = UNKNOWN;
- switch (*code)
+ switch (GET_MODE (XVECEXP (*op0, 0, 0)))
{
- case EQ: new_code = EQ; break;
- case NE: new_code = NE; break;
- default: break;
+ case CCZmode:
+ case CCRAWmode:
+ switch (*code)
+ {
+ case EQ: new_code = EQ; break;
+ case NE: new_code = NE; break;
+ default: break;
+ }
+ break;
+ default: break;
}
if (new_code != UNKNOWN)
{
+ /* For CCRAWmode put the required cc mask into the second
+ operand. */
+ if (GET_MODE (XVECEXP (*op0, 0, 0)) == CCRAWmode)
+ *op1 = gen_rtx_CONST_INT (VOIDmode, 1 << (3 - INTVAL (*op1)));
*op0 = XVECEXP (*op0, 0, 0);
*code = new_code;
}
@@ -942,10 +956,11 @@ s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem,
const0_rtx);
}
-/* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an
- unconditional jump, else a conditional jump under condition COND. */
+/* Emit a jump instruction to TARGET and return it. If COND is
+ NULL_RTX, emit an unconditional jump, else a conditional jump under
+ condition COND. */
-void
+rtx
s390_emit_jump (rtx target, rtx cond)
{
rtx insn;
@@ -955,7 +970,7 @@ s390_emit_jump (rtx target, rtx 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 emit_jump_insn (insn);
}
/* Return branch condition mask to implement a branch
@@ -971,7 +986,10 @@ s390_branch_condition_mask (rtx code)
gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
- gcc_assert (XEXP (code, 1) == const0_rtx);
+ gcc_assert (XEXP (code, 1) == const0_rtx
+ || (GET_MODE (XEXP (code, 0)) == CCRAWmode
+ && CONST_INT_P (XEXP (code, 1))));
+
switch (GET_MODE (XEXP (code, 0)))
{
@@ -1145,6 +1163,17 @@ s390_branch_condition_mask (rtx code)
}
break;
+ case CCRAWmode:
+ switch (GET_CODE (code))
+ {
+ case EQ:
+ return INTVAL (XEXP (code, 1));
+ case NE:
+ return (INTVAL (XEXP (code, 1))) ^ 0xf;
+ default:
+ gcc_unreachable ();
+ }
+
default:
return -1;
}
@@ -1204,7 +1233,9 @@ s390_branch_condition_mnemonic (rtx code, int inv)
if (GET_CODE (XEXP (code, 0)) == REG
&& REGNO (XEXP (code, 0)) == CC_REGNUM
- && XEXP (code, 1) == const0_rtx)
+ && (XEXP (code, 1) == const0_rtx
+ || (GET_MODE (XEXP (code, 0)) == CCRAWmode
+ && CONST_INT_P (XEXP (code, 1)))))
mask = s390_branch_condition_mask (code);
else
mask = s390_compare_and_branch_condition_mask (code);
@@ -1602,6 +1633,11 @@ s390_option_override (void)
if (!(target_flags_explicit & MASK_HARD_DFP) && TARGET_DFP)
target_flags |= MASK_HARD_DFP;
+ /* Enable hardware transactions if available and not explicitly
+ disabled by user. E.g. with -m31 -march=zEC12 -mzarch */
+ if (!(target_flags_explicit & MASK_OPT_HTM) && TARGET_CPU_HTM && TARGET_ZARCH)
+ target_flags |= MASK_OPT_HTM;
+
if (TARGET_HARD_DFP && !TARGET_DFP)
{
if (target_flags_explicit & MASK_HARD_DFP)
@@ -7334,11 +7370,11 @@ s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *d
if (GET_CODE (setreg) == SUBREG)
{
rtx inner = SUBREG_REG (setreg);
- if (!GENERAL_REG_P (inner))
+ if (!GENERAL_REG_P (inner) && !FP_REG_P (inner))
return;
regno = subreg_regno (setreg);
}
- else if (GENERAL_REG_P (setreg))
+ else if (GENERAL_REG_P (setreg) || FP_REG_P (setreg))
regno = REGNO (setreg);
else
return;
@@ -7361,13 +7397,13 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
rtx cur_insn;
unsigned int i;
- memset (regs_ever_clobbered, 0, 16 * sizeof (int));
+ memset (regs_ever_clobbered, 0, 32 * sizeof (int));
/* For non-leaf functions we have to consider all call clobbered regs to be
clobbered. */
if (!crtl->is_leaf)
{
- for (i = 0; i < 16; i++)
+ for (i = 0; i < 32; i++)
regs_ever_clobbered[i] = call_really_used_regs[i];
}
@@ -7389,7 +7425,7 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
See expand_builtin_unwind_init. For regs_ever_live this is done by
reload. */
if (cfun->has_nonlocal_label)
- for (i = 0; i < 16; i++)
+ for (i = 0; i < 32; i++)
if (!call_really_used_regs[i])
regs_ever_clobbered[i] = 1;
@@ -7455,17 +7491,6 @@ 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 (df_regs_ever_live_p (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.
@@ -7474,6 +7499,29 @@ s390_register_info (int clobbered_regs[])
s390_regs_ever_clobbered (clobbered_regs);
+ /* fprs 8 - 15 are call saved for 64 Bit ABI. */
+ if (!epilogue_completed)
+ {
+ cfun_frame_layout.fpr_bitmap = 0;
+ cfun_frame_layout.high_fprs = 0;
+ if (TARGET_64BIT)
+ for (i = 24; i < 32; i++)
+ /* During reload we have to use the df_regs_ever_live infos
+ since reload is marking FPRs used as spill slots there as
+ live before actually making the code changes. Without
+ this we fail during elimination offset verification. */
+ if ((clobbered_regs[i]
+ || (df_regs_ever_live_p (i)
+ && (lra_in_progress
+ || reload_in_progress
+ || crtl->saves_all_registers)))
+ && !global_regs[i])
+ {
+ cfun_set_fpr_bit (i - 16);
+ cfun_frame_layout.high_fprs++;
+ }
+ }
+
for (i = 0; i < 16; i++)
clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
@@ -7724,7 +7772,7 @@ s390_init_frame_layout (void)
{
HOST_WIDE_INT frame_size;
int base_used;
- int clobbered_regs[16];
+ int clobbered_regs[32];
/* On S/390 machines, we may need to perform branch splitting, which
will require both base and return address register. We have no
@@ -7759,6 +7807,157 @@ s390_init_frame_layout (void)
while (frame_size != cfun_frame_layout.frame_size);
}
+/* Remove the FPR clobbers from a tbegin insn if it can be proven that
+ the TX is nonescaping. A transaction is considered escaping if
+ there is at least one path from tbegin returning CC0 to the
+ function exit block without an tend.
+
+ The check so far has some limitations:
+ - only single tbegin/tend BBs are supported
+ - the first cond jump after tbegin must separate the CC0 path from ~CC0
+ - when CC is copied to a GPR and the CC0 check is done with the GPR
+ this is not supported
+*/
+
+static void
+s390_optimize_nonescaping_tx (void)
+{
+ const unsigned int CC0 = 1 << 3;
+ basic_block tbegin_bb = NULL;
+ basic_block tend_bb = NULL;
+ basic_block bb;
+ rtx insn;
+ bool result = true;
+ int bb_index;
+ rtx tbegin_insn = NULL_RTX;
+
+ if (!cfun->machine->tbegin_p)
+ return;
+
+ for (bb_index = 0; bb_index < n_basic_blocks; bb_index++)
+ {
+ bb = BASIC_BLOCK (bb_index);
+
+ FOR_BB_INSNS (bb, insn)
+ {
+ rtx ite, cc, pat, target;
+ unsigned HOST_WIDE_INT mask;
+
+ if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+ continue;
+
+ pat = PATTERN (insn);
+
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+
+ if (GET_CODE (pat) != SET
+ || GET_CODE (SET_SRC (pat)) != UNSPEC_VOLATILE)
+ continue;
+
+ if (XINT (SET_SRC (pat), 1) == UNSPECV_TBEGIN)
+ {
+ rtx tmp;
+
+ tbegin_insn = insn;
+
+ /* Just return if the tbegin doesn't have clobbers. */
+ if (GET_CODE (PATTERN (insn)) != PARALLEL)
+ return;
+
+ if (tbegin_bb != NULL)
+ return;
+
+ /* Find the next conditional jump. */
+ for (tmp = NEXT_INSN (insn);
+ tmp != NULL_RTX;
+ tmp = NEXT_INSN (tmp))
+ {
+ if (reg_set_p (gen_rtx_REG (CCmode, CC_REGNUM), tmp))
+ return;
+ if (!JUMP_P (tmp))
+ continue;
+
+ ite = SET_SRC (PATTERN (tmp));
+ if (GET_CODE (ite) != IF_THEN_ELSE)
+ continue;
+
+ cc = XEXP (XEXP (ite, 0), 0);
+ if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc))
+ || GET_MODE (cc) != CCRAWmode
+ || GET_CODE (XEXP (XEXP (ite, 0), 1)) != CONST_INT)
+ return;
+
+ if (bb->succs->length () != 2)
+ return;
+
+ mask = INTVAL (XEXP (XEXP (ite, 0), 1));
+ if (GET_CODE (XEXP (ite, 0)) == NE)
+ mask ^= 0xf;
+
+ if (mask == CC0)
+ target = XEXP (ite, 1);
+ else if (mask == (CC0 ^ 0xf))
+ target = XEXP (ite, 2);
+ else
+ return;
+
+ {
+ edge_iterator ei;
+ edge e1, e2;
+
+ ei = ei_start (bb->succs);
+ e1 = ei_safe_edge (ei);
+ ei_next (&ei);
+ e2 = ei_safe_edge (ei);
+
+ if (e2->flags & EDGE_FALLTHRU)
+ {
+ e2 = e1;
+ e1 = ei_safe_edge (ei);
+ }
+
+ if (!(e1->flags & EDGE_FALLTHRU))
+ return;
+
+ tbegin_bb = (target == pc_rtx) ? e1->dest : e2->dest;
+ }
+ if (tmp == BB_END (bb))
+ break;
+ }
+ }
+
+ if (XINT (SET_SRC (pat), 1) == UNSPECV_TEND)
+ {
+ if (tend_bb != NULL)
+ return;
+ tend_bb = bb;
+ }
+ }
+ }
+
+ /* Either we successfully remove the FPR clobbers here or we are not
+ able to do anything for this TX. Both cases don't qualify for
+ another look. */
+ cfun->machine->tbegin_p = false;
+
+ if (tbegin_bb == NULL || tend_bb == NULL)
+ return;
+
+ calculate_dominance_info (CDI_POST_DOMINATORS);
+ result = dominated_by_p (CDI_POST_DOMINATORS, tbegin_bb, tend_bb);
+ free_dominance_info (CDI_POST_DOMINATORS);
+
+ if (!result)
+ return;
+
+ PATTERN (tbegin_insn) = XVECEXP (PATTERN (tbegin_insn), 0, 0);
+ INSN_CODE (tbegin_insn) = -1;
+ df_insn_rescan (tbegin_insn);
+
+ return;
+}
+
/* 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
@@ -7767,7 +7966,7 @@ s390_init_frame_layout (void)
static void
s390_update_frame_layout (void)
{
- int clobbered_regs[16];
+ int clobbered_regs[32];
s390_register_info (clobbered_regs);
@@ -8204,8 +8403,10 @@ s390_emit_prologue (void)
int offset;
int next_fpr = 0;
- /* Complete frame layout. */
+ /* Try to get rid of the FPR clobbers. */
+ s390_optimize_nonescaping_tx ();
+ /* Complete frame layout. */
s390_update_frame_layout ();
/* Annotate all constant pool references to let the scheduler know
@@ -9353,6 +9554,294 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
return build_va_arg_indirect_ref (addr);
}
+/* Emit rtl for the tbegin or tbegin_retry (RETRY != NULL_RTX)
+ expanders.
+ DEST - Register location where CC will be stored.
+ TDB - Pointer to a 256 byte area where to store the transaction.
+ diagnostic block. NULL if TDB is not needed.
+ RETRY - Retry count value. If non-NULL a retry loop for CC2
+ is emitted
+ CLOBBER_FPRS_P - If true clobbers for all FPRs are emitted as part
+ of the tbegin instruction pattern. */
+
+void
+s390_expand_tbegin (rtx dest, rtx tdb, rtx retry, bool clobber_fprs_p)
+{
+ const int CC0 = 1 << 3;
+ const int CC1 = 1 << 2;
+ const int CC3 = 1 << 0;
+ rtx abort_label = gen_label_rtx ();
+ rtx leave_label = gen_label_rtx ();
+ rtx retry_reg = gen_reg_rtx (SImode);
+ rtx retry_label = NULL_RTX;
+ rtx jump;
+ rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+
+ if (retry != NULL_RTX)
+ {
+ emit_move_insn (retry_reg, retry);
+ retry_label = gen_label_rtx ();
+ emit_label (retry_label);
+ }
+
+ if (clobber_fprs_p)
+ emit_insn (gen_tbegin_1 (tdb,
+ gen_rtx_CONST_INT (VOIDmode, TBEGIN_MASK)));
+ else
+ emit_insn (gen_tbegin_nofloat_1 (tdb,
+ gen_rtx_CONST_INT (VOIDmode, TBEGIN_MASK)));
+
+ jump = s390_emit_jump (abort_label,
+ gen_rtx_NE (VOIDmode,
+ gen_rtx_REG (CCRAWmode, CC_REGNUM),
+ gen_rtx_CONST_INT (VOIDmode, CC0)));
+
+ JUMP_LABEL (jump) = abort_label;
+ LABEL_NUSES (abort_label) = 1;
+ add_reg_note (jump, REG_BR_PROB, very_unlikely);
+
+ /* Initialize CC return value. */
+ emit_move_insn (dest, const0_rtx);
+
+ s390_emit_jump (leave_label, NULL_RTX);
+ LABEL_NUSES (leave_label) = 1;
+ emit_barrier ();
+
+ /* Abort handler code. */
+
+ emit_label (abort_label);
+ if (retry != NULL_RTX)
+ {
+ rtx count = gen_reg_rtx (SImode);
+ jump = s390_emit_jump (leave_label,
+ gen_rtx_EQ (VOIDmode,
+ gen_rtx_REG (CCRAWmode, CC_REGNUM),
+ gen_rtx_CONST_INT (VOIDmode, CC1 | CC3)));
+ LABEL_NUSES (leave_label) = 2;
+ add_reg_note (jump, REG_BR_PROB, very_unlikely);
+
+ /* CC2 - transient failure. Perform retry with ppa. */
+ emit_move_insn (count, retry);
+ emit_insn (gen_subsi3 (count, count, retry_reg));
+ emit_insn (gen_tx_assist (count));
+ jump = emit_jump_insn (gen_doloop_si64 (retry_label,
+ retry_reg,
+ retry_reg));
+ JUMP_LABEL (jump) = retry_label;
+ LABEL_NUSES (retry_label) = 1;
+ }
+
+ emit_move_insn (dest, gen_rtx_UNSPEC (SImode,
+ gen_rtvec (1, gen_rtx_REG (CCRAWmode,
+ CC_REGNUM)),
+ UNSPEC_CC_TO_INT));
+ emit_label (leave_label);
+}
+
+/* Builtins. */
+
+enum s390_builtin
+{
+ S390_BUILTIN_TBEGIN,
+ S390_BUILTIN_TBEGIN_NOFLOAT,
+ S390_BUILTIN_TBEGIN_RETRY,
+ S390_BUILTIN_TBEGIN_RETRY_NOFLOAT,
+ S390_BUILTIN_TBEGINC,
+ S390_BUILTIN_TEND,
+ S390_BUILTIN_TABORT,
+ S390_BUILTIN_NON_TX_STORE,
+ S390_BUILTIN_TX_NESTING_DEPTH,
+ S390_BUILTIN_TX_ASSIST,
+
+ S390_BUILTIN_max
+};
+
+static enum insn_code const code_for_builtin[S390_BUILTIN_max] = {
+ CODE_FOR_tbegin,
+ CODE_FOR_tbegin_nofloat,
+ CODE_FOR_tbegin_retry,
+ CODE_FOR_tbegin_retry_nofloat,
+ CODE_FOR_tbeginc,
+ CODE_FOR_tend,
+ CODE_FOR_tabort,
+ CODE_FOR_ntstg,
+ CODE_FOR_etnd,
+ CODE_FOR_tx_assist
+};
+
+static void
+s390_init_builtins (void)
+{
+ tree ftype, uint64_type;
+
+ /* void foo (void) */
+ ftype = build_function_type_list (void_type_node, NULL_TREE);
+ add_builtin_function ("__builtin_tbeginc", ftype, S390_BUILTIN_TBEGINC,
+ BUILT_IN_MD, NULL, NULL_TREE);
+
+ /* void foo (int) */
+ ftype = build_function_type_list (void_type_node, integer_type_node,
+ NULL_TREE);
+ add_builtin_function ("__builtin_tabort", ftype,
+ S390_BUILTIN_TABORT, BUILT_IN_MD, NULL, NULL_TREE);
+ add_builtin_function ("__builtin_tx_assist", ftype,
+ S390_BUILTIN_TX_ASSIST, BUILT_IN_MD, NULL, NULL_TREE);
+
+ /* int foo (void *) */
+ ftype = build_function_type_list (integer_type_node, ptr_type_node, NULL_TREE);
+ add_builtin_function ("__builtin_tbegin", ftype, S390_BUILTIN_TBEGIN,
+ BUILT_IN_MD, NULL, NULL_TREE);
+ add_builtin_function ("__builtin_tbegin_nofloat", ftype,
+ S390_BUILTIN_TBEGIN_NOFLOAT,
+ BUILT_IN_MD, NULL, NULL_TREE);
+
+ /* int foo (void *, int) */
+ ftype = build_function_type_list (integer_type_node, ptr_type_node,
+ integer_type_node, NULL_TREE);
+ add_builtin_function ("__builtin_tbegin_retry", ftype,
+ S390_BUILTIN_TBEGIN_RETRY,
+ BUILT_IN_MD,
+ NULL, NULL_TREE);
+ add_builtin_function ("__builtin_tbegin_retry_nofloat", ftype,
+ S390_BUILTIN_TBEGIN_RETRY_NOFLOAT,
+ BUILT_IN_MD,
+ NULL, NULL_TREE);
+
+ /* int foo (void) */
+ ftype = build_function_type_list (integer_type_node, NULL_TREE);
+ add_builtin_function ("__builtin_tx_nesting_depth", ftype,
+ S390_BUILTIN_TX_NESTING_DEPTH,
+ BUILT_IN_MD, NULL, NULL_TREE);
+ add_builtin_function ("__builtin_tend", ftype,
+ S390_BUILTIN_TEND, BUILT_IN_MD, NULL, NULL_TREE);
+
+ /* void foo (uint64_t *, uint64_t) */
+ if (TARGET_64BIT)
+ uint64_type = long_unsigned_type_node;
+ else
+ uint64_type = long_long_unsigned_type_node;
+
+ ftype = build_function_type_list (void_type_node,
+ build_pointer_type (uint64_type),
+ uint64_type, NULL_TREE);
+ add_builtin_function ("__builtin_non_tx_store", ftype,
+ S390_BUILTIN_NON_TX_STORE,
+ 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
+
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ enum insn_code icode;
+ rtx op[MAX_ARGS], pat;
+ int arity;
+ bool nonvoid;
+ tree arg;
+ call_expr_arg_iterator iter;
+
+ if (fcode >= S390_BUILTIN_max)
+ internal_error ("bad builtin fcode");
+ icode = code_for_builtin[fcode];
+ if (icode == 0)
+ internal_error ("bad builtin fcode");
+
+ if (!TARGET_ZEC12)
+ error ("Transactional execution builtins require zEC12 or later\n");
+
+ if (!TARGET_HTM && TARGET_ZEC12)
+ error ("Transactional execution builtins not enabled (-mtx)\n");
+
+ /* Set a flag in the machine specific cfun part in order to support
+ saving/restoring of FPRs. */
+ if (fcode == S390_BUILTIN_TBEGIN || fcode == S390_BUILTIN_TBEGIN_RETRY)
+ cfun->machine->tbegin_p = true;
+
+ nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+
+ arity = 0;
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ {
+ const struct insn_operand_data *insn_op;
+
+ 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, EXPAND_NORMAL);
+
+ if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+ {
+ if (insn_op->predicate == memory_operand)
+ {
+ /* Don't move a NULL pointer into a register. Otherwise
+ we have to rely on combine being able to move it back
+ in order to get an immediate 0 in the instruction. */
+ if (op[arity] != const0_rtx)
+ op[arity] = copy_to_mode_reg (Pmode, op[arity]);
+ op[arity] = gen_rtx_MEM (insn_op->mode, op[arity]);
+ }
+ else
+ op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+ }
+
+ 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:
+ if (nonvoid)
+ pat = GEN_FCN (icode) (target, op[0], op[1]);
+ else
+ pat = GEN_FCN (icode) (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.
@@ -11008,6 +11497,11 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
#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_ADDR_CONST_EXTRA
#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA s390_output_addr_const_extra
diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
index b0e530f0ed4..d53fed7a6f2 100644
--- a/gcc/config/s390/s390.h
+++ b/gcc/config/s390/s390.h
@@ -34,7 +34,8 @@ enum processor_flags
PF_DFP = 16,
PF_Z10 = 32,
PF_Z196 = 64,
- PF_ZEC12 = 128
+ PF_ZEC12 = 128,
+ PF_TX = 256
};
/* This is necessary to avoid a warning about comparing different enum
@@ -61,6 +62,8 @@ enum processor_flags
(s390_arch_flags & PF_Z196)
#define TARGET_CPU_ZEC12 \
(s390_arch_flags & PF_ZEC12)
+#define TARGET_CPU_HTM \
+ (s390_arch_flags & PF_TX)
/* These flags indicate that the generated code should run on a cpu
providing the respective hardware facility when run in
@@ -78,6 +81,8 @@ enum processor_flags
(TARGET_ZARCH && TARGET_CPU_Z196)
#define TARGET_ZEC12 \
(TARGET_ZARCH && TARGET_CPU_ZEC12)
+#define TARGET_HTM \
+ (TARGET_ZARCH && TARGET_CPU_HTM && TARGET_OPT_HTM)
#define TARGET_AVOID_CMP_AND_BRANCH (s390_tune == PROCESSOR_2817_Z196)
@@ -93,23 +98,25 @@ enum processor_flags
#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_ZARCH) \
- builtin_define ("__zarch__"); \
- if (TARGET_64BIT) \
- builtin_define ("__s390x__"); \
- if (TARGET_LONG_DOUBLE_128) \
- builtin_define ("__LONG_DOUBLE_128__"); \
- } \
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_assert ("cpu=s390"); \
+ builtin_assert ("machine=s390"); \
+ builtin_define ("__s390__"); \
+ if (TARGET_ZARCH) \
+ builtin_define ("__zarch__"); \
+ if (TARGET_64BIT) \
+ builtin_define ("__s390x__"); \
+ if (TARGET_LONG_DOUBLE_128) \
+ builtin_define ("__LONG_DOUBLE_128__"); \
+ if (TARGET_HTM) \
+ builtin_define ("__HTM__"); \
+ } \
while (0)
#ifdef DEFAULT_TARGET_64BIT
-#define TARGET_DEFAULT (MASK_64BIT | MASK_ZARCH | MASK_HARD_DFP)
+#define TARGET_DEFAULT (MASK_64BIT | MASK_ZARCH | MASK_HARD_DFP | MASK_OPT_HTM)
#else
#define TARGET_DEFAULT 0
#endif
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 0141b9813ef..e12d1538a50 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -59,11 +59,17 @@
(define_c_enum "unspec" [
; Miscellaneous
UNSPEC_ROUND
- UNSPEC_CCU_TO_INT
- UNSPEC_CCZ_TO_INT
UNSPEC_ICM
UNSPEC_TIE
+ ; Convert CC into a str comparison result and copy it into an
+ ; integer register
+ ; cc0->0, cc1->1, cc2->-1, (cc3->-1)
+ UNSPEC_STRCMPCC_TO_INT
+
+ ; Copy CC as is into the lower 2 bits of an integer register
+ UNSPEC_CC_TO_INT
+
; GOT/PLT and lt-relative accesses
UNSPEC_LTREL_OFFSET
UNSPEC_LTREL_BASE
@@ -138,6 +144,15 @@
; Atomic Support
UNSPECV_CAS
UNSPECV_ATOMIC_OP
+
+ ; Transactional Execution support
+ UNSPECV_TBEGIN
+ UNSPECV_TBEGINC
+ UNSPECV_TEND
+ UNSPECV_TABORT
+ UNSPECV_ETND
+ UNSPECV_NTSTG
+ UNSPECV_PPA
])
;;
@@ -191,6 +206,9 @@
(PFPO_OP1_TYPE_SHIFT 8)
])
+; Immediate operands for tbegin and tbeginc
+(define_constants [(TBEGIN_MASK 65292)]) ; 0xff0c
+(define_constants [(TBEGINC_MASK 65288)]) ; 0xff08
;; Instruction operand type as used in the Principles of Operation.
;; Used to determine defaults for length and other attribute values.
@@ -2251,7 +2269,7 @@
(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"))]
+ (match_operand:CC 1 "nonimmediate_operand" " d,d,c,R,T,d,d"))]
""
"@
lr\t%0,%1
@@ -2583,7 +2601,7 @@
(use (reg:SI 0))])
(parallel
[(set (match_operand:SI 0 "register_operand" "=d")
- (unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_CCU_TO_INT))
+ (unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_STRCMPCC_TO_INT))
(clobber (reg:CC CC_REGNUM))])]
""
{
@@ -2825,7 +2843,7 @@
(match_dup 2)]
UNSPEC_TDC_INSN))
(set (match_operand:SI 0 "register_operand" "=d")
- (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CCZ_TO_INT))]
+ (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CC_TO_INT))]
"TARGET_HARD_FLOAT"
{
operands[2] = GEN_INT (S390_TDC_SIGNBIT_SET);
@@ -2837,12 +2855,21 @@
(match_dup 2)]
UNSPEC_TDC_INSN))
(set (match_operand:SI 0 "register_operand" "=d")
- (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CCZ_TO_INT))]
+ (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CC_TO_INT))]
"TARGET_HARD_FLOAT"
{
operands[2] = GEN_INT (S390_TDC_INFINITY);
})
+(define_insn_and_split "*cc_to_int"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand 1 "register_operand" "0")]
+ UNSPEC_CC_TO_INT))]
+ "operands != NULL"
+ "#"
+ "reload_completed"
+ [(set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 28)))])
+
; This insn is used to generate all variants of the Test Data Class
; instruction, namely tcxb, tcdb, and tceb. The insn's first operand
; is the register to be tested and the second one is the bit mask
@@ -2858,14 +2885,6 @@
[(set_attr "op_type" "RXE")
(set_attr "type" "fsimp<mode>")])
-(define_insn_and_split "*ccz_to_int"
- [(set (match_operand:SI 0 "register_operand" "=d")
- (unspec:SI [(match_operand:CCZ 1 "register_operand" "0")]
- UNSPEC_CCZ_TO_INT))]
- ""
- "#"
- "reload_completed"
- [(set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 28)))])
;
@@ -3210,7 +3229,7 @@
(define_insn_and_split "cmpint"
[(set (match_operand:SI 0 "register_operand" "=d")
(unspec:SI [(match_operand:CCU 1 "register_operand" "0")]
- UNSPEC_CCU_TO_INT))
+ UNSPEC_STRCMPCC_TO_INT))
(clobber (reg:CC CC_REGNUM))]
""
"#"
@@ -3223,10 +3242,10 @@
(define_insn_and_split "*cmpint_cc"
[(set (reg CC_REGNUM)
(compare (unspec:SI [(match_operand:CCU 1 "register_operand" "0")]
- UNSPEC_CCU_TO_INT)
+ UNSPEC_STRCMPCC_TO_INT)
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=d")
- (unspec:SI [(match_dup 1)] UNSPEC_CCU_TO_INT))]
+ (unspec:SI [(match_dup 1)] UNSPEC_STRCMPCC_TO_INT))]
"s390_match_ccmode (insn, CCSmode)"
"#"
"&& reload_completed"
@@ -3243,7 +3262,7 @@
(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_CCU_TO_INT)))
+ UNSPEC_STRCMPCC_TO_INT)))
(clobber (reg:CC CC_REGNUM))]
"TARGET_ZARCH"
"#"
@@ -3257,11 +3276,11 @@
[(set (reg CC_REGNUM)
(compare (ashiftrt:DI (ashift:DI (subreg:DI
(unspec:SI [(match_operand:CCU 1 "register_operand" "0")]
- UNSPEC_CCU_TO_INT) 0)
+ UNSPEC_STRCMPCC_TO_INT) 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_CCU_TO_INT)))]
+ (sign_extend:DI (unspec:SI [(match_dup 1)] UNSPEC_STRCMPCC_TO_INT)))]
"s390_match_ccmode (insn, CCSmode) && TARGET_ZARCH"
"#"
"&& reload_completed"
@@ -5512,7 +5531,7 @@
(if_then_else:GPR
(match_operator 1 "s390_comparison"
[(match_operand 2 "cc_reg_operand" " c,c, c, c, c, c, c")
- (const_int 0)])
+ (match_operand 5 "const_int_operand" "")])
(match_operand:GPR 3 "nonimmediate_operand" " d,0,QS, 0, d, 0,QS")
(match_operand:GPR 4 "nonimmediate_operand" " 0,d, 0,QS, 0, d,QS")))]
"TARGET_Z196"
@@ -7912,7 +7931,8 @@
(define_insn "*cjump_64"
[(set (pc)
(if_then_else
- (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
+ (match_operator 1 "s390_comparison" [(reg CC_REGNUM)
+ (match_operand 2 "const_int_operand" "")])
(label_ref (match_operand 0 "" ""))
(pc)))]
"TARGET_CPU_ZARCH"
@@ -7931,7 +7951,8 @@
(define_insn "*cjump_31"
[(set (pc)
(if_then_else
- (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
+ (match_operator 1 "s390_comparison" [(reg CC_REGNUM)
+ (match_operand 2 "const_int_operand" "")])
(label_ref (match_operand 0 "" ""))
(pc)))]
"!TARGET_CPU_ZARCH"
@@ -9800,3 +9821,217 @@
"cpsdr\t%0,%2,%1"
[(set_attr "op_type" "RRF")
(set_attr "type" "fsimp<mode>")])
+
+
+;;
+;;- Transactional execution instructions
+;;
+
+; This splitter helps combine to make use of CC directly when
+; comparing the integer result of a tbegin builtin with a constant.
+; The unspec is already removed by canonicalize_comparison. So this
+; splitters only job is to turn the PARALLEL into separate insns
+; again. Unfortunately this only works with the very first cc/int
+; compare since combine is not able to deal with data flow across
+; basic block boundaries.
+
+; It needs to be an insn pattern as well since combine does not apply
+; the splitter directly. Combine would only use it if it actually
+; would reduce the number of instructions.
+(define_insn_and_split "*ccraw_to_int"
+ [(set (pc)
+ (if_then_else
+ (match_operator 0 "s390_eqne_operator"
+ [(reg:CCRAW CC_REGNUM)
+ (match_operand 1 "const_int_operand" "")])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
+ ""
+ "#"
+ ""
+ [(set (match_dup 3)
+ (unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))
+ (set (pc)
+ (if_then_else (match_op_dup 0 [(reg:CCRAW CC_REGNUM) (match_dup 1)])
+ (label_ref (match_dup 2))
+ (pc)))]
+ "")
+
+; Non-constrained transaction begin
+
+(define_expand "tbegin"
+ [(match_operand:SI 0 "register_operand" "=d")
+ (match_operand:BLK 1 "memory_operand" "=Q")]
+ "TARGET_HTM"
+{
+ s390_expand_tbegin (operands[0], operands[1], NULL_RTX, true);
+ DONE;
+})
+
+(define_expand "tbegin_nofloat"
+ [(match_operand:SI 0 "register_operand" "=d")
+ (match_operand:BLK 1 "memory_operand" "=Q")]
+ "TARGET_HTM"
+{
+ s390_expand_tbegin (operands[0], operands[1], NULL_RTX, false);
+ DONE;
+})
+
+(define_expand "tbegin_retry"
+ [(match_operand:SI 0 "register_operand" "=d")
+ (match_operand:BLK 1 "memory_operand" "=Q")
+ (match_operand 2 "const_int_operand")]
+ "TARGET_HTM"
+{
+ s390_expand_tbegin (operands[0], operands[1], operands[2], true);
+ DONE;
+})
+
+(define_expand "tbegin_retry_nofloat"
+ [(match_operand:SI 0 "register_operand" "=d")
+ (match_operand:BLK 1 "memory_operand" "=Q")
+ (match_operand 2 "const_int_operand")]
+ "TARGET_HTM"
+{
+ s390_expand_tbegin (operands[0], operands[1], operands[2], false);
+ DONE;
+})
+
+(define_insn "tbegin_1"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(match_operand:BLK 0 "memory_operand" "=Q")
+ (match_operand 1 "const_int_operand" " D")]
+ UNSPECV_TBEGIN))
+ (clobber (reg:DF 16))
+ (clobber (reg:DF 17))
+ (clobber (reg:DF 18))
+ (clobber (reg:DF 19))
+ (clobber (reg:DF 20))
+ (clobber (reg:DF 21))
+ (clobber (reg:DF 22))
+ (clobber (reg:DF 23))
+ (clobber (reg:DF 24))
+ (clobber (reg:DF 25))
+ (clobber (reg:DF 26))
+ (clobber (reg:DF 27))
+ (clobber (reg:DF 28))
+ (clobber (reg:DF 29))
+ (clobber (reg:DF 30))
+ (clobber (reg:DF 31))]
+; CONST_OK_FOR_CONSTRAINT_P does not work with D constraint since D is
+; not supposed to be used for immediates (see genpreds.c).
+ "TARGET_HTM && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 0xffff"
+ "tbegin\t%0,%x1"
+ [(set_attr "op_type" "SIL")])
+
+; Same as above but without the FPR clobbers
+(define_insn "tbegin_nofloat_1"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(match_operand:BLK 0 "memory_operand" "=Q")
+ (match_operand 1 "const_int_operand" " D")]
+ UNSPECV_TBEGIN))]
+ "TARGET_HTM && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 0xffff"
+ "tbegin\t%0,%x1"
+ [(set_attr "op_type" "SIL")])
+
+
+; Constrained transaction begin
+
+(define_expand "tbeginc"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(const_int TBEGINC_MASK)]
+ UNSPECV_TBEGINC))]
+ "TARGET_HTM"
+ "")
+
+(define_insn "*tbeginc_1"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(match_operand 0 "const_int_operand" " D")]
+ UNSPECV_TBEGINC))]
+ "TARGET_HTM && INTVAL (operands[0]) >= 0 && INTVAL (operands[0]) <= 0xffff"
+ "tbeginc\t0,%x0"
+ [(set_attr "op_type" "SIL")])
+
+; Transaction end
+
+(define_expand "tend"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(const_int 0)] UNSPECV_TEND))
+ (set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
+ "TARGET_HTM"
+ "")
+
+(define_insn "*tend_1"
+ [(set (reg:CCRAW CC_REGNUM)
+ (unspec_volatile:CCRAW [(const_int 0)] UNSPECV_TEND))]
+ "TARGET_HTM"
+ "tend"
+ [(set_attr "op_type" "S")])
+
+; Transaction abort
+
+(define_expand "tabort"
+ [(unspec_volatile [(match_operand 0 "shift_count_or_setmem_operand" "")]
+ UNSPECV_TABORT)]
+ "TARGET_HTM && operands != NULL"
+{
+ if (CONST_INT_P (operands[0])
+ && INTVAL (operands[0]) >= 0 && INTVAL (operands[0]) <= 255)
+ {
+ error ("Invalid transaction abort code: " HOST_WIDE_INT_PRINT_DEC
+ ". Values in range 0 through 255 are reserved.",
+ INTVAL (operands[0]));
+ FAIL;
+ }
+})
+
+(define_insn "*tabort_1"
+ [(unspec_volatile [(match_operand 0 "shift_count_or_setmem_operand" "")]
+ UNSPECV_TABORT)]
+ "TARGET_HTM && operands != NULL"
+ "tabort\t%Y0"
+ [(set_attr "op_type" "S")])
+
+; Transaction extract nesting depth
+
+(define_insn "etnd"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec_volatile:SI [(const_int 0)] UNSPECV_ETND))]
+ "TARGET_HTM"
+ "etnd\t%0"
+ [(set_attr "op_type" "RRE")])
+
+; Non-transactional store
+
+(define_insn "ntstg"
+ [(set (match_operand:DI 0 "memory_operand" "=RT")
+ (unspec_volatile:DI [(match_operand:DI 1 "register_operand" "d")]
+ UNSPECV_NTSTG))]
+ "TARGET_HTM"
+ "ntstg\t%1,%0"
+ [(set_attr "op_type" "RXY")])
+
+; Transaction perform processor assist
+
+(define_expand "tx_assist"
+ [(set (match_dup 1) (const_int 0))
+ (unspec_volatile [(match_operand:SI 0 "register_operand" "d")
+ (match_dup 1)
+ (const_int 1)]
+ UNSPECV_PPA)]
+ "TARGET_HTM"
+{
+ operands[1] = gen_reg_rtx (SImode);
+})
+
+(define_insn "*ppa"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "d")
+ (match_operand:SI 1 "register_operand" "d")
+ (match_operand 2 "const_int_operand" "I")]
+ UNSPECV_PPA)]
+ "TARGET_HTM && INTVAL (operands[2]) < 16"
+ "ppa\t%0,%1,1"
+ [(set_attr "op_type" "RRF")])
diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt
index ba38e6e14ed..7dedb836701 100644
--- a/gcc/config/s390/s390.opt
+++ b/gcc/config/s390/s390.opt
@@ -104,6 +104,10 @@ mlong-double-64
Target Report RejectNegative Negative(mlong-double-128) InverseMask(LONG_DOUBLE_128)
Use 64-bit long double
+mhtm
+Target Report Mask(OPT_HTM)
+Use hardware transactional execution instructions
+
mpacked-stack
Target Report Mask(PACKED_STACK)
Use packed stack layout
diff --git a/gcc/config/s390/s390intrin.h b/gcc/config/s390/s390intrin.h
new file mode 100644
index 00000000000..e1a00ce58e3
--- /dev/null
+++ b/gcc/config/s390/s390intrin.h
@@ -0,0 +1,33 @@
+/* S/390 System z specific intrinsics
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Contributed by Andreas Krebbel (Andreas.Krebbel@de.ibm.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef _S390INTRIN_H
+#define _S390INTRIN_H
+
+#ifndef __s390__
+ #error s390intrin.h included on wrong platform/compiler
+#endif
+
+#ifdef __HTM__
+#include <htmintrin.h>
+#endif
+
+
+#endif /* _S390INTRIN_H*/