s390.c: Rename UNSPEC_CCU_TO_INT to UNSPEC_STRCMPCC_TO_INT and UNSPEC_CCZ_TO_INT to...
2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com> * config/s390/s390.c: Rename UNSPEC_CCU_TO_INT to UNSPEC_STRCMPCC_TO_INT and UNSPEC_CCZ_TO_INT to UNSPEC_CC_TO_INT. (struct machine_function): Add tbegin_p. (s390_canonicalize_comparison): Fold CC mode compares to conditional jump if possible. (s390_emit_jump): Return the emitted jump. (s390_branch_condition_mask, s390_branch_condition_mnemonic): Handle CCRAWmode compares. (s390_option_override): Default to -mhtm if available. (s390_reg_clobbered_rtx): Handle floating point regs as well. (s390_regs_ever_clobbered): Use s390_regs_ever_clobbered also for FPRs instead of df_regs_ever_live_p. (s390_optimize_nonescaping_tx): New function. (s390_init_frame_layout): Extend clobbered_regs array to cover FPRs as well. (s390_emit_prologue): Call s390_optimize_nonescaping_tx. (s390_expand_tbegin): New function. (enum s390_builtin): New enum definition. (code_for_builtin): New array definition. (s390_init_builtins): New function. (s390_expand_builtin): New function. (TARGET_INIT_BUILTINS): Define. (TARGET_EXPAND_BUILTIN): Define. * common/config/s390/s390-common.c (processor_flags_table): Add PF_TX. * config/s390/predicates.md (s390_comparison): Handle CCRAWmode. (s390_alc_comparison): Likewise. * config/s390/s390-modes.def: Add CCRAWmode. * config/s390/s390.h (processor_flags): Add PF_TX. (TARGET_CPU_HTM): Define macro. (TARGET_HTM): Define macro. (TARGET_CPU_CPP_BUILTINS): Define __HTM__ for htm. * config/s390/s390.md: Rename UNSPEC_CCU_TO_INT to UNSPEC_STRCMPCC_TO_INT and UNSPEC_CCZ_TO_INT to UNSPEC_CC_TO_INT. (UNSPECV_TBEGIN, UNSPECV_TBEGINC, UNSPECV_TEND, UNSPECV_TABORT) (UNSPECV_ETND, UNSPECV_NTSTG, UNSPECV_PPA): New unspecv enum values. (TBEGIN_MASK, TBEGINC_MASK): New constants. ("*cc_to_int"): Move up. ("*mov<mode>cc", "*cjump_64", "*cjump_31"): Accept integer constants other than 0. ("*ccraw_to_int"): New insn and splitter definition. ("tbegin", "tbegin_nofloat", "tbegin_retry") ("tbegin_retry_nofloat", "tbeginc", "tend", "tabort") ("tx_assist"): New expander. ("tbegin_1", "tbegin_nofloat_1", "*tbeginc_1", "*tend_1") ("*tabort_1", "etnd", "ntstg", "*ppa"): New insn definition. * config/s390/s390.opt: Add -mhtm option. * config/s390/s390-protos.h (s390_emit_jump): Add return type. * config/s390/htmxlintrin.h: New file. * config/s390/htmintrin.h: New file. * config/s390/s390intrin.h: New file. * doc/extend.texi: Document htm builtins. * config.gcc: Add the new header files to extra_headers. 2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com> * gcc.target/s390/htm-1.c: New file. * gcc.target/s390/htm-nofloat-1.c: New file. * gcc.target/s390/htm-xl-intrin-1.c: New file. 2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com> * config/s390/target.h: Include htmintrin.h. (_HTM_ITM_RETRIES): New macro definition. (htm_available, htm_init, htm_begin, htm_begin_success) (htm_commit, htm_abort, htm_abort_should_retry): New functions. From-SVN: r200454
This commit is contained in:
parent
b47a3fef9c
commit
5a3fe9b607
20 changed files with 1438 additions and 77 deletions
|
@ -1,3 +1,60 @@
|
|||
2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
|
||||
|
||||
* config/s390/s390.c: Rename UNSPEC_CCU_TO_INT to
|
||||
UNSPEC_STRCMPCC_TO_INT and UNSPEC_CCZ_TO_INT to UNSPEC_CC_TO_INT.
|
||||
(struct machine_function): Add tbegin_p.
|
||||
(s390_canonicalize_comparison): Fold CC mode compares to
|
||||
conditional jump if possible.
|
||||
(s390_emit_jump): Return the emitted jump.
|
||||
(s390_branch_condition_mask, s390_branch_condition_mnemonic):
|
||||
Handle CCRAWmode compares.
|
||||
(s390_option_override): Default to -mhtm if available.
|
||||
(s390_reg_clobbered_rtx): Handle floating point regs as well.
|
||||
(s390_regs_ever_clobbered): Use s390_regs_ever_clobbered also for
|
||||
FPRs instead of df_regs_ever_live_p.
|
||||
(s390_optimize_nonescaping_tx): New function.
|
||||
(s390_init_frame_layout): Extend clobbered_regs array to cover
|
||||
FPRs as well.
|
||||
(s390_emit_prologue): Call s390_optimize_nonescaping_tx.
|
||||
(s390_expand_tbegin): New function.
|
||||
(enum s390_builtin): New enum definition.
|
||||
(code_for_builtin): New array definition.
|
||||
(s390_init_builtins): New function.
|
||||
(s390_expand_builtin): New function.
|
||||
(TARGET_INIT_BUILTINS): Define.
|
||||
(TARGET_EXPAND_BUILTIN): Define.
|
||||
* common/config/s390/s390-common.c (processor_flags_table): Add
|
||||
PF_TX.
|
||||
* config/s390/predicates.md (s390_comparison): Handle CCRAWmode.
|
||||
(s390_alc_comparison): Likewise.
|
||||
* config/s390/s390-modes.def: Add CCRAWmode.
|
||||
* config/s390/s390.h (processor_flags): Add PF_TX.
|
||||
(TARGET_CPU_HTM): Define macro.
|
||||
(TARGET_HTM): Define macro.
|
||||
(TARGET_CPU_CPP_BUILTINS): Define __HTM__ for htm.
|
||||
* config/s390/s390.md: Rename UNSPEC_CCU_TO_INT to
|
||||
UNSPEC_STRCMPCC_TO_INT and UNSPEC_CCZ_TO_INT to UNSPEC_CC_TO_INT.
|
||||
(UNSPECV_TBEGIN, UNSPECV_TBEGINC, UNSPECV_TEND, UNSPECV_TABORT)
|
||||
(UNSPECV_ETND, UNSPECV_NTSTG, UNSPECV_PPA): New unspecv enum
|
||||
values.
|
||||
(TBEGIN_MASK, TBEGINC_MASK): New constants.
|
||||
("*cc_to_int"): Move up.
|
||||
("*mov<mode>cc", "*cjump_64", "*cjump_31"): Accept integer
|
||||
constants other than 0.
|
||||
("*ccraw_to_int"): New insn and splitter definition.
|
||||
("tbegin", "tbegin_nofloat", "tbegin_retry")
|
||||
("tbegin_retry_nofloat", "tbeginc", "tend", "tabort")
|
||||
("tx_assist"): New expander.
|
||||
("tbegin_1", "tbegin_nofloat_1", "*tbeginc_1", "*tend_1")
|
||||
("*tabort_1", "etnd", "ntstg", "*ppa"): New insn definition.
|
||||
* config/s390/s390.opt: Add -mhtm option.
|
||||
* config/s390/s390-protos.h (s390_emit_jump): Add return type.
|
||||
* config/s390/htmxlintrin.h: New file.
|
||||
* config/s390/htmintrin.h: New file.
|
||||
* config/s390/s390intrin.h: New file.
|
||||
* doc/extend.texi: Document htm builtins.
|
||||
* config.gcc: Add the new header files to extra_headers.
|
||||
|
||||
2013-06-26 Thomas Schwinge <thomas@codesourcery.com>
|
||||
|
||||
* config/i386/gnu.h [TARGET_LIBC_PROVIDES_SSP]
|
||||
|
|
|
@ -42,7 +42,7 @@ EXPORTED_CONST int processor_flags_table[] =
|
|||
/* z196 */ PF_IEEE_FLOAT | PF_ZARCH | PF_LONG_DISPLACEMENT
|
||||
| PF_EXTIMM | PF_DFP | PF_Z10 | PF_Z196,
|
||||
/* zEC12 */ PF_IEEE_FLOAT | PF_ZARCH | PF_LONG_DISPLACEMENT
|
||||
| PF_EXTIMM | PF_DFP | PF_Z10 | PF_Z196 | PF_ZEC12
|
||||
| PF_EXTIMM | PF_DFP | PF_Z10 | PF_Z196 | PF_ZEC12 | PF_TX
|
||||
};
|
||||
|
||||
/* Change optimizations to be performed, depending on the
|
||||
|
|
|
@ -453,6 +453,7 @@ s390*-*-*)
|
|||
cpu_type=s390
|
||||
need_64bit_hwint=yes
|
||||
extra_options="${extra_options} fused-madd.opt"
|
||||
extra_headers="s390intrin.h htmintrin.h htmxlintrin.h"
|
||||
;;
|
||||
# Note the 'l'; we need to be able to match e.g. "shle" or "shl".
|
||||
sh[123456789lbe]*-*-* | sh-*-*)
|
||||
|
|
57
gcc/config/s390/htmintrin.h
Normal file
57
gcc/config/s390/htmintrin.h
Normal file
|
@ -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 */
|
182
gcc/config/s390/htmxlintrin.h
Normal file
182
gcc/config/s390/htmxlintrin.h
Normal file
|
@ -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 */
|
|
@ -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)))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")])
|
||||
|
|
|
@ -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
|
||||
|
|
33
gcc/config/s390/s390intrin.h
Normal file
33
gcc/config/s390/s390intrin.h
Normal file
|
@ -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*/
|
|
@ -8847,6 +8847,7 @@ instructions, but allow the compiler to schedule those calls.
|
|||
* PowerPC Built-in Functions::
|
||||
* PowerPC AltiVec/VSX Built-in Functions::
|
||||
* RX Built-in Functions::
|
||||
* S/390 System z Built-in Functions::
|
||||
* SH Built-in Functions::
|
||||
* SPARC VIS Built-in Functions::
|
||||
* SPU Built-in Functions::
|
||||
|
@ -14422,6 +14423,120 @@ bit in the processor status word.
|
|||
Generates the @code{wait} machine instruction.
|
||||
@end deftypefn
|
||||
|
||||
@node S/390 System z Built-in Functions
|
||||
@subsection S/390 System z Built-in Functions
|
||||
@deftypefn {Built-in Function} int __builtin_tbegin (void*)
|
||||
Generates the @code{tbegin} machine instruction starting a
|
||||
non-constraint hardware transaction. If the parameter is non-NULL the
|
||||
memory area is used to store the transaction diagnostic buffer and
|
||||
will be passed as first operand to @code{tbegin}. This buffer can be
|
||||
defined using the @code{struct __htm_tdb} C struct defined in
|
||||
@code{htmintrin.h} and must reside on a double-word boundary. The
|
||||
second tbegin operand is set to @code{0xff0c}. This enables
|
||||
save/restore of all GPRs and disables aborts for FPR and AR
|
||||
manipulations inside the transaction body. The condition code set by
|
||||
the tbegin instruction is returned as integer value. The tbegin
|
||||
instruction by definition overwrites the content of all FPRs. The
|
||||
compiler will generate code which saves and restores the FPRs. For
|
||||
soft-float code it is recommended to used the @code{*_nofloat}
|
||||
variant. In order to prevent a TDB from being written it is required
|
||||
to pass an constant zero value as parameter. Passing the zero value
|
||||
through a variable is not sufficient. Although modifications of
|
||||
access registers inside the transaction will not trigger an
|
||||
transaction abort it is not supported to actually modify them. Access
|
||||
registers do not get saved when entering a transaction. They will have
|
||||
undefined state when reaching the abort code.
|
||||
@end deftypefn
|
||||
|
||||
Macros for the possible return codes of tbegin are defined in the
|
||||
@code{htmintrin.h} header file:
|
||||
|
||||
@table @code
|
||||
@item _HTM_TBEGIN_STARTED
|
||||
@code{tbegin} has been executed as part of normal processing. The
|
||||
transaction body is supposed to be executed.
|
||||
@item _HTM_TBEGIN_INDETERMINATE
|
||||
The transaction was aborted due to an indeterminate condition which
|
||||
might be persistent.
|
||||
@item _HTM_TBEGIN_TRANSIENT
|
||||
The transaction aborted due to a transient failure. The transaction
|
||||
should be re-executed in that case.
|
||||
@item _HTM_TBEGIN_PERSISTENT
|
||||
The transaction aborted due to a persistent failure. Re-execution
|
||||
under same circumstances will not be productive.
|
||||
@end table
|
||||
|
||||
@defmac _HTM_FIRST_USER_ABORT_CODE
|
||||
The @code{_HTM_FIRST_USER_ABORT_CODE} defined in @code{htmintrin.h}
|
||||
specifies the first abort code which can be used for
|
||||
@code{__builtin_tabort}. Values below this threshold are reserved for
|
||||
machine use.
|
||||
@end defmac
|
||||
|
||||
@deftp {Data type} {struct __htm_tdb}
|
||||
The @code{struct __htm_tdb} defined in @code{htmintrin.h} describes
|
||||
the structure of the transaction diagnostic block as specified in the
|
||||
Principles of Operation manual chapter 5-91.
|
||||
@end deftp
|
||||
|
||||
@deftypefn {Built-in Function} int __builtin_tbegin_nofloat (void*)
|
||||
Same as @code{__builtin_tbegin} but without FPR saves and restores.
|
||||
Using this variant in code making use of FPRs will leave the FPRs in
|
||||
undefined state when entering the transaction abort handler code.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} int __builtin_tbegin_retry (void*, int)
|
||||
In addition to @code{__builtin_tbegin} a loop for transient failures
|
||||
is generated. If tbegin returns a condition code of 2 the transaction
|
||||
will be retried as often as specified in the second argument. The
|
||||
perform processor assist instruction is used to tell the CPU about the
|
||||
number of fails so far.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} int __builtin_tbegin_retry_nofloat (void*, int)
|
||||
Same as @code{__builtin_tbegin_retry} but without FPR saves and
|
||||
restores. Using this variant in code making use of FPRs will leave
|
||||
the FPRs in undefined state when entering the transaction abort
|
||||
handler code.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} void __builtin_tbeginc (void)
|
||||
Generates the @code{tbeginc} machine instruction starting a constraint
|
||||
hardware transaction. The second operand is set to @code{0xff08}.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} int __builtin_tend (void)
|
||||
Generates the @code{tend} machine instruction finishing a transaction
|
||||
and making the changes visible to other threads. The condition code
|
||||
generated by tend is returned as integer value.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} void __builtin_tabort (int)
|
||||
Generates the @code{tabort} machine instruction with the specified
|
||||
abort code. Abort codes from 0 through 255 are reserved and will
|
||||
result in an error message.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} void __builtin_tx_assist (int)
|
||||
Generates the @code{ppa rX,rY,1} machine instruction. Where the
|
||||
integer parameter is loaded into rX and a value of zero is loaded into
|
||||
rY. The integer parameter specifies the number of times the
|
||||
transaction repeatedly aborted.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} int __builtin_tx_nesting_depth (void)
|
||||
Generates the @code{etnd} machine instruction. The current nesting
|
||||
depth is returned as integer value. For a nesting depth of 0 the code
|
||||
is not executed as part of an transaction.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} void __builtin_non_tx_store (unsigned long long *, unsigned long long)
|
||||
|
||||
Generates the @code{ntstg} machine instruction. The second argument
|
||||
is written to the first arguments location. The store operation will
|
||||
not be rolled-back in case of an transaction abort.
|
||||
@end deftypefn
|
||||
|
||||
@node SH Built-in Functions
|
||||
@subsection SH Built-in Functions
|
||||
The following built-in functions are supported on the SH1, SH2, SH3 and SH4
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
|
||||
|
||||
* gcc.target/s390/htm-1.c: New file.
|
||||
* gcc.target/s390/htm-nofloat-1.c: New file.
|
||||
* gcc.target/s390/htm-xl-intrin-1.c: New file.
|
||||
|
||||
2013-06-26 Tobias Burnus <burnus@net-b.de>
|
||||
|
||||
PR fortran/29800
|
||||
|
|
37
gcc/testsuite/gcc.target/s390/htm-1.c
Normal file
37
gcc/testsuite/gcc.target/s390/htm-1.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* This checks the availability of the low-level builtins introduced
|
||||
for transactional execution. */
|
||||
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=zEC12 -mzarch" } */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <htmintrin.h>
|
||||
|
||||
int a = 0;
|
||||
uint64_t g;
|
||||
|
||||
int
|
||||
foo (struct __htm_tdb* tdb)
|
||||
{
|
||||
|
||||
int cc;
|
||||
int n;
|
||||
|
||||
cc = __builtin_tbegin (0);
|
||||
cc = __builtin_tbegin (tdb);
|
||||
cc = __builtin_tbegin_nofloat (0);
|
||||
cc = __builtin_tbegin_nofloat (tdb);
|
||||
cc = __builtin_tbegin_retry (0, 42);
|
||||
cc = __builtin_tbegin_retry (tdb, 42);
|
||||
cc = __builtin_tbegin_retry_nofloat (0, 42);
|
||||
cc = __builtin_tbegin_retry_nofloat (tdb, 42);
|
||||
__builtin_tbeginc ();
|
||||
n = __builtin_tx_nesting_depth();
|
||||
__builtin_non_tx_store(&g, n);
|
||||
__builtin_tabort (42 + 255);
|
||||
__builtin_tend();
|
||||
__builtin_tx_assist (23);
|
||||
}
|
||||
/* Make sure the tdb NULL argument ends up as immediate value in the
|
||||
instruction. */
|
||||
/* { dg-final { scan-assembler-times "tbegin\t0," 4 } } */
|
12
gcc/testsuite/gcc.target/s390/htm-nofloat-1.c
Normal file
12
gcc/testsuite/gcc.target/s390/htm-nofloat-1.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=zEC12 -mzarch" } */
|
||||
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
__builtin_tbegin_nofloat (0);
|
||||
__builtin_tbegin_retry_nofloat (0, 42);
|
||||
}
|
||||
/* Make sure no FPR saves/restores are emitted. */
|
||||
/* { dg-final { scan-assembler-not "std" } } */
|
||||
/* { dg-final { scan-assembler-not "ld" } } */
|
37
gcc/testsuite/gcc.target/s390/htm-xl-intrin-1.c
Normal file
37
gcc/testsuite/gcc.target/s390/htm-xl-intrin-1.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* This checks the availability of the XL compiler intrinsics for
|
||||
transactional execution with the expected prototypes. */
|
||||
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=zEC12 -mzarch" } */
|
||||
|
||||
#include <htmxlintrin.h>
|
||||
|
||||
int a = 0;
|
||||
unsigned long g;
|
||||
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
struct __htm_tdb *tdb_struct;
|
||||
void * const tdb = tdb_struct;
|
||||
long result;
|
||||
unsigned char code;
|
||||
|
||||
result = __TM_simple_begin ();
|
||||
result = __TM_begin (tdb);
|
||||
result = __TM_end ();
|
||||
__TM_abort ();
|
||||
__TM_named_abort (42);
|
||||
__TM_non_transactional_store (&g, 42);
|
||||
result = __TM_nesting_depth (tdb);
|
||||
|
||||
result = __TM_is_user_abort (tdb);
|
||||
result = __TM_is_named_user_abort (tdb, &code);
|
||||
result = __TM_is_illegal (tdb);
|
||||
result = __TM_is_footprint_exceeded (tdb);
|
||||
result = __TM_is_nested_too_deep (tdb);
|
||||
result = __TM_is_conflict (tdb);
|
||||
result = __TM_is_failure_persistent (result);
|
||||
result = __TM_failure_address (tdb);
|
||||
result = __TM_failure_code (tdb);
|
||||
}
|
|
@ -1,3 +1,10 @@
|
|||
2013-06-27 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
|
||||
|
||||
* config/s390/target.h: Include htmintrin.h.
|
||||
(_HTM_ITM_RETRIES): New macro definition.
|
||||
(htm_available, htm_init, htm_begin, htm_begin_success)
|
||||
(htm_commit, htm_abort, htm_abort_should_retry): New functions.
|
||||
|
||||
2013-06-20 Iain Sandoe <iain@codesourcery.com>
|
||||
Cesar Philippidis <cesar@codesourcery.com>
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <htmintrin.h>
|
||||
|
||||
/* Number of retries for transient failures. */
|
||||
#define _HTM_ITM_RETRIES 10
|
||||
|
||||
namespace GTM HIDDEN {
|
||||
|
||||
#define HW_CACHELINE_SIZE 256
|
||||
|
@ -52,4 +58,57 @@ cpu_relax (void)
|
|||
__asm volatile ("" : : : "memory");
|
||||
}
|
||||
|
||||
#ifdef __HTM__
|
||||
#define USE_HTM_FASTPATH
|
||||
|
||||
static inline bool
|
||||
htm_available ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
htm_init ()
|
||||
{
|
||||
return htm_available () ? _HTM_ITM_RETRIES : 0;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
htm_begin ()
|
||||
{
|
||||
return __builtin_tbegin_nofloat (NULL);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
htm_begin_success (uint32_t begin_ret)
|
||||
{
|
||||
return begin_ret == _HTM_TBEGIN_STARTED;
|
||||
}
|
||||
|
||||
static inline void
|
||||
htm_commit ()
|
||||
{
|
||||
__builtin_tend ();
|
||||
}
|
||||
|
||||
static inline void
|
||||
htm_abort ()
|
||||
{
|
||||
__builtin_tabort (_HTM_FIRST_USER_ABORT_CODE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
htm_abort_should_retry (uint32_t begin_ret)
|
||||
{
|
||||
return begin_ret == _HTM_TBEGIN_TRANSIENT;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
htm_transaction_active ()
|
||||
{
|
||||
return __builtin_tx_nesting_depth() != 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace GTM
|
||||
|
|
Loading…
Add table
Reference in a new issue