1;; Machine description for PowerPC synchronization instructions.
2;; Copyright (C) 2005-2020 Free Software Foundation, Inc.
3;; Contributed by Geoffrey Keating.
4
5;; This file is part of GCC.
6
7;; GCC is free software; you can redistribute it and/or modify it
8;; under the terms of the GNU General Public License as published
9;; by the Free Software Foundation; either version 3, or (at your
10;; option) any later version.
11
12;; GCC is distributed in the hope that it will be useful, but WITHOUT
13;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15;; License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GCC; see the file COPYING3.  If not see
19;; <http://www.gnu.org/licenses/>.
20
21(define_mode_attr larx [(QI "lbarx")
22			(HI "lharx")
23			(SI "lwarx")
24			(DI "ldarx")
25			(TI "lqarx")])
26
27(define_mode_attr stcx [(QI "stbcx.")
28			(HI "sthcx.")
29			(SI "stwcx.")
30			(DI "stdcx.")
31			(TI "stqcx.")])
32
33(define_code_iterator FETCHOP [plus minus ior xor and])
34(define_code_attr fetchop_name
35  [(plus "add") (minus "sub") (ior "or") (xor "xor") (and "and")])
36(define_code_attr fetchop_pred
37  [(plus "add_operand") (minus "int_reg_operand")
38   (ior "logical_operand") (xor "logical_operand") (and "and_operand")])
39
40(define_expand "mem_thread_fence"
41  [(match_operand:SI 0 "const_int_operand")]		;; model
42  ""
43{
44  enum memmodel model = memmodel_base (INTVAL (operands[0]));
45  switch (model)
46    {
47    case MEMMODEL_RELAXED:
48      break;
49    case MEMMODEL_CONSUME:
50    case MEMMODEL_ACQUIRE:
51    case MEMMODEL_RELEASE:
52    case MEMMODEL_ACQ_REL:
53      emit_insn (gen_lwsync ());
54      break;
55    case MEMMODEL_SEQ_CST:
56      emit_insn (gen_hwsync ());
57      break;
58    default:
59      gcc_unreachable ();
60    }
61  DONE;
62})
63
64(define_expand "hwsync"
65  [(set (match_dup 0)
66	(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]
67  ""
68{
69  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
70  MEM_VOLATILE_P (operands[0]) = 1;
71})
72
73(define_insn "*hwsync"
74  [(set (match_operand:BLK 0 "" "")
75	(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]
76  ""
77  "sync"
78  [(set_attr "type" "sync")])
79
80(define_expand "lwsync"
81  [(set (match_dup 0)
82	(unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))]
83  ""
84{
85  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
86  MEM_VOLATILE_P (operands[0]) = 1;
87})
88
89(define_insn "*lwsync"
90  [(set (match_operand:BLK 0 "" "")
91	(unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))]
92  ""
93{
94  if (TARGET_NO_LWSYNC)
95    return "sync";
96  else
97    return "lwsync";
98}
99  [(set_attr "type" "sync")])
100
101(define_insn "isync"
102  [(unspec_volatile:BLK [(const_int 0)] UNSPECV_ISYNC)]
103  ""
104  "isync"
105  [(set_attr "type" "isync")])
106
107;; Types that we should provide atomic instructions for.
108(define_mode_iterator AINT [QI
109			    HI
110			    SI
111			    (DI "TARGET_POWERPC64")
112			    (TI "TARGET_SYNC_TI")])
113
114;; The control dependency used for load dependency described
115;; in B.2.3 of the Power ISA 2.06B.
116(define_insn "loadsync_<mode>"
117  [(unspec_volatile:BLK [(match_operand:AINT 0 "register_operand" "r")]
118			UNSPECV_ISYNC)
119   (clobber (match_scratch:CC 1 "=y"))]
120  ""
121  "cmpw %1,%0,%0\;bne- %1,$+4\;isync"
122  [(set_attr "type" "isync")
123   (set_attr "length" "12")])
124
125;; If TARGET_PREFIXED, always use plq rather than lq.
126(define_insn "load_quadpti"
127  [(set (match_operand:PTI 0 "quad_int_reg_operand" "=&r")
128	(unspec:PTI
129	 [(match_operand:TI 1 "quad_memory_operand" "wQ")] UNSPEC_LSQ))]
130  "TARGET_SYNC_TI
131   && !reg_mentioned_p (operands[0], operands[1])"
132  "lq %0,%1"
133  [(set_attr "type" "load")
134   (set (attr "prefixed") (if_then_else (match_test "TARGET_PREFIXED")
135					(const_string "yes")
136					(const_string "no")))])
137
138;; Pattern load_quadpti will always use plq for atomic TImode if
139;; TARGET_PREFIXED.  It has the correct doubleword ordering on either LE
140;; or BE, so we can just move the result into the output register and
141;; do not need to do the doubleword swap for LE.  Also this avoids any
142;; confusion about whether the lq vs plq might be used based on whether
143;; op1 has PC-relative addressing.  We could potentially allow BE to
144;; use lq because it doesn't have the doubleword ordering problem.
145(define_expand "atomic_load<mode>"
146  [(set (match_operand:AINT 0 "register_operand")		;; output
147	(match_operand:AINT 1 "memory_operand"))		;; memory
148   (use (match_operand:SI 2 "const_int_operand"))]		;; model
149  ""
150{
151  if (<MODE>mode == TImode && !TARGET_SYNC_TI)
152    FAIL;
153
154  enum memmodel model = memmodel_base (INTVAL (operands[2]));
155
156  if (is_mm_seq_cst (model))
157    emit_insn (gen_hwsync ());
158
159  if (<MODE>mode != TImode)
160    emit_move_insn (operands[0], operands[1]);
161  else
162    {
163      rtx op0 = operands[0];
164      rtx op1 = operands[1];
165      rtx pti_reg = gen_reg_rtx (PTImode);
166
167      if (!quad_address_p (XEXP (op1, 0), TImode, false))
168	{
169	  rtx old_addr = XEXP (op1, 0);
170	  rtx new_addr = force_reg (Pmode, old_addr);
171	  operands[1] = op1 = replace_equiv_address (op1, new_addr);
172	}
173
174      emit_insn (gen_load_quadpti (pti_reg, op1));
175
176      if (WORDS_BIG_ENDIAN || TARGET_PREFIXED)
177	emit_move_insn (op0, gen_lowpart (TImode, pti_reg));
178      else
179	{
180	  emit_move_insn (gen_lowpart (DImode, op0), gen_highpart (DImode, pti_reg));
181	  emit_move_insn (gen_highpart (DImode, op0), gen_lowpart (DImode, pti_reg));
182	}
183    }
184
185  switch (model)
186    {
187    case MEMMODEL_RELAXED:
188      break;
189    case MEMMODEL_CONSUME:
190    case MEMMODEL_ACQUIRE:
191    case MEMMODEL_SEQ_CST:
192      emit_insn (gen_loadsync_<mode> (operands[0]));
193      break;
194    default:
195      gcc_unreachable ();
196    }
197  DONE;
198})
199
200;; If TARGET_PREFIXED, always use pstq rather than stq.
201(define_insn "store_quadpti"
202  [(set (match_operand:PTI 0 "quad_memory_operand" "=wQ")
203	(unspec:PTI
204	 [(match_operand:PTI 1 "quad_int_reg_operand" "r")] UNSPEC_LSQ))]
205  "TARGET_SYNC_TI"
206  "stq %1,%0"
207  [(set_attr "type" "store")
208   (set (attr "prefixed") (if_then_else (match_test "TARGET_PREFIXED")
209					(const_string "yes")
210					(const_string "no")))])
211
212;; Pattern store_quadpti will always use pstq if TARGET_PREFIXED,
213;; so the doubleword swap is never needed in that case.
214(define_expand "atomic_store<mode>"
215  [(set (match_operand:AINT 0 "memory_operand")		;; memory
216	(match_operand:AINT 1 "register_operand"))	;; input
217   (use (match_operand:SI 2 "const_int_operand"))]	;; model
218  ""
219{
220  if (<MODE>mode == TImode && !TARGET_SYNC_TI)
221    FAIL;
222
223  enum memmodel model = memmodel_base (INTVAL (operands[2]));
224  switch (model)
225    {
226    case MEMMODEL_RELAXED:
227      break;
228    case MEMMODEL_RELEASE:
229      emit_insn (gen_lwsync ());
230      break;
231    case MEMMODEL_SEQ_CST:
232      emit_insn (gen_hwsync ());
233      break;
234    default:
235      gcc_unreachable ();
236    }
237  if (<MODE>mode != TImode)
238    emit_move_insn (operands[0], operands[1]);
239  else
240    {
241      rtx op0 = operands[0];
242      rtx op1 = operands[1];
243      rtx pti_reg = gen_reg_rtx (PTImode);
244
245      if (!quad_address_p (XEXP (op0, 0), TImode, false))
246	{
247	  rtx old_addr = XEXP (op0, 0);
248	  rtx new_addr = force_reg (Pmode, old_addr);
249	  operands[0] = op0 = replace_equiv_address (op0, new_addr);
250	}
251
252      if (WORDS_BIG_ENDIAN || TARGET_PREFIXED)
253	emit_move_insn (pti_reg, gen_lowpart (PTImode, op1));
254      else
255	{
256	  emit_move_insn (gen_lowpart (DImode, pti_reg), gen_highpart (DImode, op1));
257	  emit_move_insn (gen_highpart (DImode, pti_reg), gen_lowpart (DImode, op1));
258	}
259
260      emit_insn (gen_store_quadpti (gen_lowpart (PTImode, op0), pti_reg));
261    }
262
263  DONE;
264})
265
266;; Any supported integer mode that has atomic l<x>arx/st<x>cx. instrucitons
267;; other than the quad memory operations, which have special restrictions.
268;; Byte/halfword atomic instructions were added in ISA 2.06B, but were phased
269;; in and did not show up until power8.  TImode atomic lqarx/stqcx. require
270;; special handling due to even/odd register requirements.
271(define_mode_iterator ATOMIC [(QI "TARGET_SYNC_HI_QI")
272			      (HI "TARGET_SYNC_HI_QI")
273			      SI
274			      (DI "TARGET_POWERPC64")])
275
276(define_insn "load_locked<mode>"
277  [(set (match_operand:ATOMIC 0 "int_reg_operand" "=r")
278	(unspec_volatile:ATOMIC
279         [(match_operand:ATOMIC 1 "memory_operand" "Z")] UNSPECV_LL))]
280  ""
281  "<larx> %0,%y1"
282  [(set_attr "type" "load_l")])
283
284(define_insn "load_locked<QHI:mode>_si"
285  [(set (match_operand:SI 0 "int_reg_operand" "=r")
286	(unspec_volatile:SI
287	  [(match_operand:QHI 1 "memory_operand" "Z")] UNSPECV_LL))]
288  "TARGET_SYNC_HI_QI"
289  "<QHI:larx> %0,%y1"
290  [(set_attr "type" "load_l")])
291
292;; Use PTImode to get even/odd register pairs.
293;; Use a temporary register to force getting an even register for the
294;; lqarx/stqcrx. instructions.  Normal optimizations will eliminate this extra
295;; copy on big endian systems.
296
297;; On little endian systems where non-atomic quad word load/store instructions
298;; are not used, the address can be register+offset, so make sure the address
299;; is indexed or indirect before register allocation.
300
301(define_expand "load_lockedti"
302  [(use (match_operand:TI 0 "quad_int_reg_operand"))
303   (use (match_operand:TI 1 "memory_operand"))]
304  "TARGET_SYNC_TI"
305{
306  rtx op0 = operands[0];
307  rtx op1 = operands[1];
308  rtx pti = gen_reg_rtx (PTImode);
309
310  if (!indexed_or_indirect_operand (op1, TImode))
311    {
312      rtx old_addr = XEXP (op1, 0);
313      rtx new_addr = force_reg (Pmode, old_addr);
314      operands[1] = op1 = change_address (op1, TImode, new_addr);
315    }
316
317  emit_insn (gen_load_lockedpti (pti, op1));
318  if (WORDS_BIG_ENDIAN)
319    emit_move_insn (op0, gen_lowpart (TImode, pti));
320  else
321    {
322      emit_move_insn (gen_lowpart (DImode, op0), gen_highpart (DImode, pti));
323      emit_move_insn (gen_highpart (DImode, op0), gen_lowpart (DImode, pti));
324    }
325  DONE;
326})
327
328(define_insn "load_lockedpti"
329  [(set (match_operand:PTI 0 "quad_int_reg_operand" "=&r")
330	(unspec_volatile:PTI
331         [(match_operand:TI 1 "indexed_or_indirect_operand" "Z")] UNSPECV_LL))]
332  "TARGET_SYNC_TI
333   && !reg_mentioned_p (operands[0], operands[1])
334   && quad_int_reg_operand (operands[0], PTImode)"
335  "lqarx %0,%y1"
336  [(set_attr "type" "load_l")])
337
338(define_insn "store_conditional<mode>"
339  [(set (match_operand:CC 0 "cc_reg_operand" "=x")
340	(unspec_volatile:CC [(const_int 0)] UNSPECV_SC))
341   (set (match_operand:ATOMIC 1 "memory_operand" "=Z")
342	(match_operand:ATOMIC 2 "int_reg_operand" "r"))]
343  ""
344  "<stcx> %2,%y1"
345  [(set_attr "type" "store_c")])
346
347;; Use a temporary register to force getting an even register for the
348;; lqarx/stqcrx. instructions.  Normal optimizations will eliminate this extra
349;; copy on big endian systems.
350
351;; On little endian systems where non-atomic quad word load/store instructions
352;; are not used, the address can be register+offset, so make sure the address
353;; is indexed or indirect before register allocation.
354
355(define_expand "store_conditionalti"
356  [(use (match_operand:CC 0 "cc_reg_operand"))
357   (use (match_operand:TI 1 "memory_operand"))
358   (use (match_operand:TI 2 "quad_int_reg_operand"))]
359  "TARGET_SYNC_TI"
360{
361  rtx op0 = operands[0];
362  rtx op1 = operands[1];
363  rtx op2 = operands[2];
364  rtx addr = XEXP (op1, 0);
365  rtx pti_mem;
366  rtx pti_reg;
367
368  if (!indexed_or_indirect_operand (op1, TImode))
369    {
370      rtx new_addr = force_reg (Pmode, addr);
371      operands[1] = op1 = change_address (op1, TImode, new_addr);
372      addr = new_addr;
373    }
374
375  pti_mem = change_address (op1, PTImode, addr);
376  pti_reg = gen_reg_rtx (PTImode);
377
378  if (WORDS_BIG_ENDIAN)
379    emit_move_insn (pti_reg, gen_lowpart (PTImode, op2));
380  else
381    {
382      emit_move_insn (gen_lowpart (DImode, pti_reg), gen_highpart (DImode, op2));
383      emit_move_insn (gen_highpart (DImode, pti_reg), gen_lowpart (DImode, op2));
384    }
385
386  emit_insn (gen_store_conditionalpti (op0, pti_mem, pti_reg));
387  DONE;
388})
389
390(define_insn "store_conditionalpti"
391  [(set (match_operand:CC 0 "cc_reg_operand" "=x")
392	(unspec_volatile:CC [(const_int 0)] UNSPECV_SC))
393   (set (match_operand:PTI 1 "indexed_or_indirect_operand" "=Z")
394	(match_operand:PTI 2 "quad_int_reg_operand" "r"))]
395  "TARGET_SYNC_TI && quad_int_reg_operand (operands[2], PTImode)"
396  "stqcx. %2,%y1"
397  [(set_attr "type" "store_c")])
398
399(define_expand "atomic_compare_and_swap<mode>"
400  [(match_operand:SI 0 "int_reg_operand")		;; bool out
401   (match_operand:AINT 1 "int_reg_operand")		;; val out
402   (match_operand:AINT 2 "memory_operand")		;; memory
403   (match_operand:AINT 3 "reg_or_short_operand")	;; expected
404   (match_operand:AINT 4 "int_reg_operand")		;; desired
405   (match_operand:SI 5 "const_int_operand")		;; is_weak
406   (match_operand:SI 6 "const_int_operand")		;; model succ
407   (match_operand:SI 7 "const_int_operand")]		;; model fail
408  ""
409{
410  rs6000_expand_atomic_compare_and_swap (operands);
411  DONE;
412})
413
414(define_expand "atomic_exchange<mode>"
415  [(match_operand:AINT 0 "int_reg_operand")		;; output
416   (match_operand:AINT 1 "memory_operand")		;; memory
417   (match_operand:AINT 2 "int_reg_operand")		;; input
418   (match_operand:SI 3 "const_int_operand")]		;; model
419  ""
420{
421  rs6000_expand_atomic_exchange (operands);
422  DONE;
423})
424
425(define_expand "atomic_<fetchop_name><mode>"
426  [(match_operand:AINT 0 "memory_operand")		;; memory
427   (FETCHOP:AINT (match_dup 0)
428     (match_operand:AINT 1 "<fetchop_pred>"))		;; operand
429   (match_operand:SI 2 "const_int_operand")]		;; model
430  ""
431{
432  rs6000_expand_atomic_op (<CODE>, operands[0], operands[1],
433			   NULL_RTX, NULL_RTX, operands[2]);
434  DONE;
435})
436
437(define_expand "atomic_nand<mode>"
438  [(match_operand:AINT 0 "memory_operand")		;; memory
439   (match_operand:AINT 1 "int_reg_operand")		;; operand
440   (match_operand:SI 2 "const_int_operand")]		;; model
441  ""
442{
443  rs6000_expand_atomic_op (NOT, operands[0], operands[1],
444			   NULL_RTX, NULL_RTX, operands[2]);
445  DONE;
446})
447
448(define_expand "atomic_fetch_<fetchop_name><mode>"
449  [(match_operand:AINT 0 "int_reg_operand")		;; output
450   (match_operand:AINT 1 "memory_operand")		;; memory
451   (FETCHOP:AINT (match_dup 1)
452     (match_operand:AINT 2 "<fetchop_pred>"))		;; operand
453   (match_operand:SI 3 "const_int_operand")]		;; model
454  ""
455{ 
456  rs6000_expand_atomic_op (<CODE>, operands[1], operands[2],
457			   operands[0], NULL_RTX, operands[3]);
458  DONE;
459})
460
461(define_expand "atomic_fetch_nand<mode>"
462  [(match_operand:AINT 0 "int_reg_operand")		;; output
463   (match_operand:AINT 1 "memory_operand")		;; memory
464   (match_operand:AINT 2 "int_reg_operand")		;; operand
465   (match_operand:SI 3 "const_int_operand")]		;; model
466  ""
467{
468  rs6000_expand_atomic_op (NOT, operands[1], operands[2],
469			   operands[0], NULL_RTX, operands[3]);
470  DONE;
471})
472
473(define_expand "atomic_<fetchop_name>_fetch<mode>"
474  [(match_operand:AINT 0 "int_reg_operand")		;; output
475   (match_operand:AINT 1 "memory_operand")		;; memory
476   (FETCHOP:AINT (match_dup 1)
477     (match_operand:AINT 2 "<fetchop_pred>"))		;; operand
478   (match_operand:SI 3 "const_int_operand")]		;; model
479  ""
480{
481  rs6000_expand_atomic_op (<CODE>, operands[1], operands[2],
482			   NULL_RTX, operands[0], operands[3]);
483  DONE;
484})
485
486(define_expand "atomic_nand_fetch<mode>"
487  [(match_operand:AINT 0 "int_reg_operand")		;; output
488   (match_operand:AINT 1 "memory_operand")		;; memory
489   (match_operand:AINT 2 "int_reg_operand")		;; operand
490   (match_operand:SI 3 "const_int_operand")]		;; model
491  ""
492{
493  rs6000_expand_atomic_op (NOT, operands[1], operands[2],
494			   NULL_RTX, operands[0], operands[3]);
495  DONE;
496})
497