;; Hardware Transactional Memory (HTM) patterns.
;; Copyright (C) 2013-2016 Free Software Foundation, Inc.
;; Contributed by Peter Bergner <bergner@vnet.ibm.com>.

;; This file is part of GCC.

;; GCC is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published
;; by the Free Software Foundation; either version 3, or (at your
;; option) any later version.

;; GCC is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
;; License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3.  If not see
;; <http://www.gnu.org/licenses/>.

(define_constants
  [(TFHAR_SPR		128)
   (TFIAR_SPR		129)
   (TEXASR_SPR		130)
   (TEXASRU_SPR		131)
   (MAX_HTM_OPERANDS	4)
  ])

;;
;; UNSPEC usage
;;

(define_c_enum "unspec"
  [UNSPEC_HTM_FENCE
  ])

;;
;; UNSPEC_VOLATILE usage
;;

(define_c_enum "unspecv"
  [UNSPECV_HTM_TABORT
   UNSPECV_HTM_TABORTXC
   UNSPECV_HTM_TABORTXCI
   UNSPECV_HTM_TBEGIN
   UNSPECV_HTM_TCHECK
   UNSPECV_HTM_TEND
   UNSPECV_HTM_TRECHKPT
   UNSPECV_HTM_TRECLAIM
   UNSPECV_HTM_TSR
   UNSPECV_HTM_TTEST
   UNSPECV_HTM_MFSPR
   UNSPECV_HTM_MTSPR
  ])

(define_expand "tabort"
  [(parallel
     [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand:SI 0 "base_reg_operand" "b")]
			       UNSPECV_HTM_TABORT))
      (set (match_dup 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[2]) = 1;
})

(define_insn "*tabort"
  [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand:SI 0 "base_reg_operand" "b")]
			    UNSPECV_HTM_TABORT))
   (set (match_operand:BLK 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tabort. %0"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "tabort<wd>c"
  [(parallel
     [(set (match_operand:CC 3 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand 0 "u5bit_cint_operand" "n")
				(match_operand:GPR 1 "gpc_reg_operand" "r")
				(match_operand:GPR 2 "gpc_reg_operand" "r")]
			       UNSPECV_HTM_TABORTXC))
      (set (match_dup 4) (unspec:BLK [(match_dup 4)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[4] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[4]) = 1;
})

(define_insn "*tabort<wd>c"
  [(set (match_operand:CC 3 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand 0 "u5bit_cint_operand" "n")
			     (match_operand:GPR 1 "gpc_reg_operand" "r")
			     (match_operand:GPR 2 "gpc_reg_operand" "r")]
			    UNSPECV_HTM_TABORTXC))
   (set (match_operand:BLK 4) (unspec:BLK [(match_dup 4)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tabort<wd>c. %0,%1,%2"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "tabort<wd>ci"
  [(parallel
     [(set (match_operand:CC 3 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand 0 "u5bit_cint_operand" "n")
				(match_operand:GPR 1 "gpc_reg_operand" "r")
				(match_operand 2 "s5bit_cint_operand" "n")]
			       UNSPECV_HTM_TABORTXCI))
      (set (match_dup 4) (unspec:BLK [(match_dup 4)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[4] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[4]) = 1;
})

(define_insn "*tabort<wd>ci"
  [(set (match_operand:CC 3 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand 0 "u5bit_cint_operand" "n")
			     (match_operand:GPR 1 "gpc_reg_operand" "r")
			     (match_operand 2 "s5bit_cint_operand" "n")]
			    UNSPECV_HTM_TABORTXCI))
   (set (match_operand:BLK 4) (unspec:BLK [(match_dup 4)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tabort<wd>ci. %0,%1,%2"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "tbegin"
  [(parallel
     [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			       UNSPECV_HTM_TBEGIN))
      (set (match_dup 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[2]) = 1;
})

(define_insn "*tbegin"
  [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			    UNSPECV_HTM_TBEGIN))
   (set (match_operand:BLK 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tbegin. %0"
  [(set_attr "type" "htm")
   (set_attr "length" "4")])

(define_expand "tcheck"
  [(parallel
     [(set (match_operand:CC 0 "cc_reg_operand" "=y")
	   (unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TCHECK))
      (set (match_dup 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[1] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[1]) = 1;
})

(define_insn "*tcheck"
  [(set (match_operand:CC 0 "cc_reg_operand" "=y")
	(unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TCHECK))
   (set (match_operand:BLK 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tcheck %0"
  [(set_attr "type" "htm")
   (set_attr "length" "4")])

(define_expand "tend"
  [(parallel
     [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			       UNSPECV_HTM_TEND))
      (set (match_dup 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[2]) = 1;
})

(define_insn "*tend"
  [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			    UNSPECV_HTM_TEND))
   (set (match_operand:BLK 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tend. %0"
  [(set_attr "type" "htm")
   (set_attr "length" "4")])

(define_expand "trechkpt"
  [(parallel
     [(set (match_operand:CC 0 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TRECHKPT))
      (set (match_dup 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[1] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[1]) = 1;
})

(define_insn "*trechkpt"
  [(set (match_operand:CC 0 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TRECHKPT))
   (set (match_operand:BLK 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "trechkpt."
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "treclaim"
  [(parallel
     [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand:SI 0 "gpc_reg_operand" "r")]
			       UNSPECV_HTM_TRECLAIM))
      (set (match_dup 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[2]) = 1;
})

(define_insn "*treclaim"
  [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand:SI 0 "gpc_reg_operand" "r")]
			    UNSPECV_HTM_TRECLAIM))
   (set (match_operand:BLK 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "treclaim. %0"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "tsr"
  [(parallel
     [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			       UNSPECV_HTM_TSR))
      (set (match_dup 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[2]) = 1;
})

(define_insn "*tsr"
  [(set (match_operand:CC 1 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(match_operand 0 "const_0_to_1_operand" "n")]
			    UNSPECV_HTM_TSR))
   (set (match_operand:BLK 2) (unspec:BLK [(match_dup 2)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tsr. %0"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_expand "ttest"
  [(parallel
     [(set (match_operand:CC 0 "cc_reg_operand" "=x")
	   (unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TTEST))
      (set (match_dup 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))])]
  "TARGET_HTM"
{
  operands[1] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[1]) = 1;
})

(define_insn "*ttest"
  [(set (match_operand:CC 0 "cc_reg_operand" "=x")
	(unspec_volatile:CC [(const_int 0)] UNSPECV_HTM_TTEST))
   (set (match_operand:BLK 1) (unspec:BLK [(match_dup 1)] UNSPEC_HTM_FENCE))]
  "TARGET_HTM"
  "tabortwci. 0,1,0"
  [(set_attr "type" "htmsimple")
   (set_attr "length" "4")])

(define_insn "htm_mfspr_<mode>"
  [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
        (unspec_volatile:GPR [(match_operand 1 "u10bit_cint_operand" "n")
			      (match_operand:GPR 2 "htm_spr_reg_operand" "")]
			     UNSPECV_HTM_MFSPR))]
  "TARGET_HTM"
  "mfspr %0,%1";
  [(set_attr "type" "htm")
   (set_attr "length" "4")])

(define_insn "htm_mtspr_<mode>"
  [(set (match_operand:GPR 2 "htm_spr_reg_operand" "")
        (unspec_volatile:GPR [(match_operand:GPR 0 "gpc_reg_operand" "r")
			      (match_operand 1 "u10bit_cint_operand" "n")]
			     UNSPECV_HTM_MTSPR))]
  "TARGET_HTM"
  "mtspr %1,%0";
  [(set_attr "type" "htm")
   (set_attr "length" "4")])
