sync.md 14 KB

;; Machine description for PowerPC synchronization instructions. ;; Copyright (C) 2005-2015 Free Software Foundation, Inc. ;; Contributed by Geoffrey Keating.

;; 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_mode_attr larx [(QI "lbarx")

        (HI "lharx")
        (SI "lwarx")
        (DI "ldarx")
        (TI "lqarx")])

(define_mode_attr stcx [(QI "stbcx.")

        (HI "sthcx.")
        (SI "stwcx.")
        (DI "stdcx.")
        (TI "stqcx.")])

(define_code_iterator FETCHOP [plus minus ior xor and]) (define_code_attr fetchop_name [(plus "add") (minus "sub") (ior "or") (xor "xor") (and "and")]) (define_code_attr fetchop_pred [(plus "add_operand") (minus "int_reg_operand") (ior "logical_operand") (xor "logical_operand") (and "and_operand")])

(define_expand "mem_thread_fence" [(match_operand:SI 0 "const_int_operand" "")] ;; model "" { enum memmodel model = (enum memmodel) INTVAL (operands[0]); switch (model)

{
case MEMMODEL_RELAXED:
  break;
case MEMMODEL_CONSUME:
case MEMMODEL_ACQUIRE:
case MEMMODEL_RELEASE:
case MEMMODEL_ACQ_REL:
  emit_insn (gen_lwsync ());
  break;
case MEMMODEL_SEQ_CST:
  emit_insn (gen_hwsync ());
  break;
default:
  gcc_unreachable ();
}

DONE; })

(define_expand "hwsync" [(set (match_dup 0)

(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]

"" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; })

(define_insn "*hwsync" [(set (match_operand:BLK 0 "" "")

(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]

"" "sync" [(set_attr "type" "sync")])

(define_expand "lwsync" [(set (match_dup 0)

(unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))]

"" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; })

(define_insn "*lwsync" [(set (match_operand:BLK 0 "" "")

(unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))]

"" { /* Some AIX assemblers don't accept lwsync, so we use a .long. */ if (TARGET_NO_LWSYNC)

return "sync";

else if (TARGET_LWSYNC_INSTRUCTION)

return "lwsync";

else

return ".long 0x7c2004ac";

} [(set_attr "type" "sync")])

(define_insn "isync" [(unspec_volatile:BLK [(const_int 0)] UNSPECV_ISYNC)] "" "isync" [(set_attr "type" "isync")])

;; Types that we should provide atomic instructions for. (define_mode_iterator AINT [QI

            HI
            SI
            (DI "TARGET_POWERPC64")
            (TI "TARGET_SYNC_TI")])

;; The control dependency used for load dependency described ;; in B.2.3 of the Power ISA 2.06B. (defineinsn "loadsync" [(unspec_volatile:BLK [(match_operand:AINT 0 "register_operand" "r")]

        UNSPECV_ISYNC)

(clobber (match_scratch:CC 1 "=y"))] "" "cmpw %1,%0,%0\;bne- %1,$+4\;isync" [(set_attr "type" "isync") (set_attr "length" "12")])

(define_insn "load_quadpti" [(set (match_operand:PTI 0 "quad_int_reg_operand" "=&r")

(unspec:PTI
 [(match_operand:TI 1 "quad_memory_operand" "wQ")] UNSPEC_LSQ))]

"TARGET_SYNC_TI && !reg_mentioned_p (operands[0], operands[1])" "lq %0,%1" [(set_attr "type" "load") (set_attr "length" "4")])

(define_expand "atomic_load" [(set (match_operand:AINT 0 "register_operand" "") ;; output

(match_operand:AINT 1 "memory_operand" ""))     ;; memory

(use (match_operand:SI 2 "const_int_operand" ""))] ;; model "" { if (mode == TImode && !TARGET_SYNC_TI)

FAIL;

enum memmodel model = (enum memmodel) INTVAL (operands[2]);

if (model == MEMMODEL_SEQ_CST)

emit_insn (gen_hwsync ());

if (mode != TImode)

emit_move_insn (operands[0], operands[1]);

else

{
  rtx op0 = operands[0];
  rtx op1 = operands[1];
  rtx pti_reg = gen_reg_rtx (PTImode);

  // Can't have indexed address for 'lq'
  if (indexed_address (XEXP (op1, 0), TImode))
{
  rtx old_addr = XEXP (op1, 0);
  rtx new_addr = force_reg (Pmode, old_addr);
  operands[1] = op1 = replace_equiv_address (op1, new_addr);
}

  emit_insn (gen_load_quadpti (pti_reg, op1));

  if (WORDS_BIG_ENDIAN)
emit_move_insn (op0, gen_lowpart (TImode, pti_reg));
  else
{
  emit_move_insn (gen_lowpart (DImode, op0), gen_highpart (DImode, pti_reg));
  emit_move_insn (gen_highpart (DImode, op0), gen_lowpart (DImode, pti_reg));
}
}

switch (model)

{
case MEMMODEL_RELAXED:
  break;
case MEMMODEL_CONSUME:
case MEMMODEL_ACQUIRE:
case MEMMODEL_SEQ_CST:
  emit_insn (gen_loadsync_<mode> (operands[0]));
  break;
default:
  gcc_unreachable ();
}

DONE; })

(define_insn "store_quadpti" [(set (match_operand:PTI 0 "quad_memory_operand" "=wQ")

(unspec:PTI
 [(match_operand:PTI 1 "quad_int_reg_operand" "r")] UNSPEC_LSQ))]

"TARGET_SYNC_TI" "stq %1,%0" [(set_attr "type" "store") (set_attr "length" "4")])

(define_expand "atomic_store" [(set (match_operand:AINT 0 "memory_operand" "") ;; memory

(match_operand:AINT 1 "register_operand" ""))       ;; input

(use (match_operand:SI 2 "const_int_operand" ""))] ;; model "" { if (mode == TImode && !TARGET_SYNC_TI)

FAIL;

enum memmodel model = (enum memmodel) INTVAL (operands[2]); switch (model)

{
case MEMMODEL_RELAXED:
  break;
case MEMMODEL_RELEASE:
  emit_insn (gen_lwsync ());
  break;
case MEMMODEL_SEQ_CST:
  emit_insn (gen_hwsync ());
  break;
default:
  gcc_unreachable ();
}

if (mode != TImode)

emit_move_insn (operands[0], operands[1]);

else

{
  rtx op0 = operands[0];
  rtx op1 = operands[1];
  rtx pti_reg = gen_reg_rtx (PTImode);

  // Can't have indexed address for 'stq'
  if (indexed_address (XEXP (op0, 0), TImode))
{
  rtx old_addr = XEXP (op0, 0);
  rtx new_addr = force_reg (Pmode, old_addr);
  operands[0] = op0 = replace_equiv_address (op0, new_addr);
}

  if (WORDS_BIG_ENDIAN)
emit_move_insn (pti_reg, gen_lowpart (PTImode, op1));
  else
{
  emit_move_insn (gen_lowpart (DImode, pti_reg), gen_highpart (DImode, op1));
  emit_move_insn (gen_highpart (DImode, pti_reg), gen_lowpart (DImode, op1));
}

  emit_insn (gen_store_quadpti (gen_lowpart (PTImode, op0), pti_reg));
}

DONE; })

;; Any supported integer mode that has atomic larx/stcx. instrucitons ;; other than the quad memory operations, which have special restrictions. ;; Byte/halfword atomic instructions were added in ISA 2.06B, but were phased ;; in and did not show up until power8. TImode atomic lqarx/stqcx. require ;; special handling due to even/odd register requirements. (define_mode_iterator ATOMIC [(QI "TARGET_SYNC_HI_QI")

              (HI "TARGET_SYNC_HI_QI")
              SI
              (DI "TARGET_POWERPC64")])

(define_insn "load_locked" [(set (match_operand:ATOMIC 0 "int_reg_operand" "=r")

(unspec_volatile:ATOMIC
     [(match_operand:ATOMIC 1 "memory_operand" "Z")] UNSPECV_LL))]

"" " %0,%y1" [(set_attr "type" "load_l")])

(define_insn "load_lockedQHI:mode_si" [(set (match_operand:SI 0 "int_reg_operand" "=r")

(unspec_volatile:SI
  [(match_operand:QHI 1 "memory_operand" "Z")] UNSPECV_LL))]

"TARGET_SYNC_HI_QI" "QHI:larx %0,%y1" [(set_attr "type" "load_l")])

;; Use PTImode to get even/odd register pairs. ;; Use a temporary register to force getting an even register for the ;; lqarx/stqcrx. instructions. Normal optimizations will eliminate this extra ;; copy on big endian systems.

;; On little endian systems where non-atomic quad word load/store instructions ;; are not used, the address can be register+offset, so make sure the address ;; is indexed or indirect before register allocation.

(define_expand "load_lockedti" [(use (match_operand:TI 0 "quad_int_reg_operand" "")) (use (match_operand:TI 1 "memory_operand" ""))] "TARGET_SYNC_TI" { rtx op0 = operands[0]; rtx op1 = operands[1]; rtx pti = gen_reg_rtx (PTImode);

if (!indexed_or_indirect_operand (op1, TImode))

{
  rtx old_addr = XEXP (op1, 0);
  rtx new_addr = force_reg (Pmode, old_addr);
  operands[1] = op1 = change_address (op1, TImode, new_addr);
}

emit_insn (gen_load_lockedpti (pti, op1)); if (WORDS_BIG_ENDIAN)

emit_move_insn (op0, gen_lowpart (TImode, pti));

else

{
  emit_move_insn (gen_lowpart (DImode, op0), gen_highpart (DImode, pti));
  emit_move_insn (gen_highpart (DImode, op0), gen_lowpart (DImode, pti));
}

DONE; })

(define_insn "load_lockedpti" [(set (match_operand:PTI 0 "quad_int_reg_operand" "=&r")

(unspec_volatile:PTI
     [(match_operand:TI 1 "indexed_or_indirect_operand" "Z")] UNSPECV_LL))]

"TARGET_SYNC_TI && !reg_mentioned_p (operands[0], operands[1]) && quad_int_reg_operand (operands[0], PTImode)" "lqarx %0,%y1" [(set_attr "type" "load_l")])

(define_insn "store_conditional" [(set (match_operand:CC 0 "cc_reg_operand" "=x")

(unspec_volatile:CC [(const_int 0)] UNSPECV_SC))

(set (match_operand:ATOMIC 1 "memory_operand" "=Z")

(match_operand:ATOMIC 2 "int_reg_operand" "r"))]

"" " %2,%y1" [(set_attr "type" "store_c")])

;; Use a temporary register to force getting an even register for the ;; lqarx/stqcrx. instructions. Normal optimizations will eliminate this extra ;; copy on big endian systems.

;; On little endian systems where non-atomic quad word load/store instructions ;; are not used, the address can be register+offset, so make sure the address ;; is indexed or indirect before register allocation.

(define_expand "store_conditionalti" [(use (match_operand:CC 0 "cc_reg_operand" "")) (use (match_operand:TI 1 "memory_operand" "")) (use (match_operand:TI 2 "quad_int_reg_operand" ""))] "TARGET_SYNC_TI" { rtx op0 = operands[0]; rtx op1 = operands[1]; rtx op2 = operands[2]; rtx addr = XEXP (op1, 0); rtx pti_mem; rtx pti_reg;

if (!indexed_or_indirect_operand (op1, TImode))

{
  rtx new_addr = force_reg (Pmode, addr);
  operands[1] = op1 = change_address (op1, TImode, new_addr);
  addr = new_addr;
}

pti_mem = change_address (op1, PTImode, addr); pti_reg = gen_reg_rtx (PTImode);

if (WORDS_BIG_ENDIAN)

emit_move_insn (pti_reg, gen_lowpart (PTImode, op2));

else

{
  emit_move_insn (gen_lowpart (DImode, pti_reg), gen_highpart (DImode, op2));
  emit_move_insn (gen_highpart (DImode, pti_reg), gen_lowpart (DImode, op2));
}

emit_insn (gen_store_conditionalpti (op0, pti_mem, pti_reg)); DONE; })

(define_insn "store_conditionalpti" [(set (match_operand:CC 0 "cc_reg_operand" "=x")

(unspec_volatile:CC [(const_int 0)] UNSPECV_SC))

(set (match_operand:PTI 1 "indexed_or_indirect_operand" "=Z")

(match_operand:PTI 2 "quad_int_reg_operand" "r"))]

"TARGET_SYNC_TI && quad_int_reg_operand (operands[2], PTImode)" "stqcx. %2,%y1" [(set_attr "type" "store_c")])

(define_expand "atomic_compare_and_swap" [(match_operand:SI 0 "int_reg_operand" "") ;; bool out (match_operand:AINT 1 "int_reg_operand" "") ;; val out (match_operand:AINT 2 "memory_operand" "") ;; memory (match_operand:AINT 3 "reg_or_short_operand" "") ;; expected (match_operand:AINT 4 "int_reg_operand" "") ;; desired (match_operand:SI 5 "const_int_operand" "") ;; is_weak (match_operand:SI 6 "const_int_operand" "") ;; model succ (match_operand:SI 7 "const_int_operand" "")] ;; model fail "" { rs6000_expand_atomic_compare_and_swap (operands); DONE; })

(define_expand "atomic_exchange" [(match_operand:AINT 0 "int_reg_operand" "") ;; output (match_operand:AINT 1 "memory_operand" "") ;; memory (match_operand:AINT 2 "int_reg_operand" "") ;; input (match_operand:SI 3 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_exchange (operands); DONE; })

(defineexpand "atomic" [(match_operand:AINT 0 "memory_operand" "") ;; memory (FETCHOP:AINT (match_dup 0)

 (match_operand:AINT 1 "<fetchop_pred>" ""))    ;; operand

(match_operand:SI 2 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (, operands[0], operands[1],

           NULL_RTX, NULL_RTX, operands[2]);

DONE; })

(define_expand "atomic_nand" [(match_operand:AINT 0 "memory_operand" "") ;; memory (match_operand:AINT 1 "int_reg_operand" "") ;; operand (match_operand:SI 2 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (NOT, operands[0], operands[1],

           NULL_RTX, NULL_RTX, operands[2]);

DONE; })

(define_expand "atomicfetch" [(match_operand:AINT 0 "int_reg_operand" "") ;; output (match_operand:AINT 1 "memory_operand" "") ;; memory (FETCHOP:AINT (match_dup 1)

 (match_operand:AINT 2 "<fetchop_pred>" ""))    ;; operand

(match_operand:SI 3 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (, operands[1], operands[2],

           operands[0], NULL_RTX, operands[3]);

DONE; })

(define_expand "atomic_fetch_nand" [(match_operand:AINT 0 "int_reg_operand" "") ;; output (match_operand:AINT 1 "memory_operand" "") ;; memory (match_operand:AINT 2 "int_reg_operand" "") ;; operand (match_operand:SI 3 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (NOT, operands[1], operands[2],

           operands[0], NULL_RTX, operands[3]);

DONE; })

(defineexpand "atomic_fetch" [(match_operand:AINT 0 "int_reg_operand" "") ;; output (match_operand:AINT 1 "memory_operand" "") ;; memory (FETCHOP:AINT (match_dup 1)

 (match_operand:AINT 2 "<fetchop_pred>" ""))    ;; operand

(match_operand:SI 3 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (, operands[1], operands[2],

           NULL_RTX, operands[0], operands[3]);

DONE; })

(define_expand "atomic_nand_fetch" [(match_operand:AINT 0 "int_reg_operand" "") ;; output (match_operand:AINT 1 "memory_operand" "") ;; memory (match_operand:AINT 2 "int_reg_operand" "") ;; operand (match_operand:SI 3 "const_int_operand" "")] ;; model "" { rs6000_expand_atomic_op (NOT, operands[1], operands[2],

           NULL_RTX, operands[0], operands[3]);

DONE; })