xref: /netbsd-src/external/gpl3/gcc/dist/gcc/config/loongarch/sync.md (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1;; Machine description for LoongArch atomic operations.
2;; Copyright (C) 2021-2022 Free Software Foundation, Inc.
3;; Contributed by Loongson Ltd.
4;; Based on MIPS and RISC-V target for GNU compiler.
5
6;; This file is part of GCC.
7
8;; GCC is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 3, or (at your option)
11;; any later version.
12
13;; GCC is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with GCC; see the file COPYING3.  If not see
20;; <http://www.gnu.org/licenses/>.
21
22(define_c_enum "unspec" [
23  UNSPEC_COMPARE_AND_SWAP
24  UNSPEC_COMPARE_AND_SWAP_ADD
25  UNSPEC_COMPARE_AND_SWAP_SUB
26  UNSPEC_COMPARE_AND_SWAP_AND
27  UNSPEC_COMPARE_AND_SWAP_XOR
28  UNSPEC_COMPARE_AND_SWAP_OR
29  UNSPEC_COMPARE_AND_SWAP_NAND
30  UNSPEC_SYNC_OLD_OP
31  UNSPEC_SYNC_EXCHANGE
32  UNSPEC_ATOMIC_STORE
33  UNSPEC_MEMORY_BARRIER
34])
35
36(define_code_iterator any_atomic [plus ior xor and])
37(define_code_attr atomic_optab
38  [(plus "add") (ior "or") (xor "xor") (and "and")])
39
40;; This attribute gives the format suffix for atomic memory operations.
41(define_mode_attr amo [(SI "w") (DI "d")])
42
43;; <amop> expands to the name of the atomic operand that implements a
44;; particular code.
45(define_code_attr amop [(ior "or") (xor "xor") (and "and") (plus "add")])
46
47;; Memory barriers.
48
49(define_expand "mem_thread_fence"
50  [(match_operand:SI 0 "const_int_operand" "")] ;; model
51  ""
52{
53  if (INTVAL (operands[0]) != MEMMODEL_RELAXED)
54    {
55      rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
56      MEM_VOLATILE_P (mem) = 1;
57      emit_insn (gen_mem_thread_fence_1 (mem, operands[0]));
58    }
59  DONE;
60})
61
62;; Until the LoongArch memory model (hence its mapping from C++) is finalized,
63;; conservatively emit a full FENCE.
64(define_insn "mem_thread_fence_1"
65  [(set (match_operand:BLK 0 "" "")
66	(unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))
67   (match_operand:SI 1 "const_int_operand" "")] ;; model
68  ""
69  "dbar\t0")
70
71;; Atomic memory operations.
72
73;; Implement atomic stores with amoswap.  Fall back to fences for atomic loads.
74(define_insn "atomic_store<mode>"
75  [(set (match_operand:GPR 0 "memory_operand" "+ZB")
76    (unspec_volatile:GPR
77      [(match_operand:GPR 1 "reg_or_0_operand" "rJ")
78       (match_operand:SI 2 "const_int_operand")]      ;; model
79      UNSPEC_ATOMIC_STORE))]
80  ""
81  "amswap%A2.<amo>\t$zero,%z1,%0"
82  [(set (attr "length") (const_int 8))])
83
84(define_insn "atomic_<atomic_optab><mode>"
85  [(set (match_operand:GPR 0 "memory_operand" "+ZB")
86	(unspec_volatile:GPR
87	  [(any_atomic:GPR (match_dup 0)
88			   (match_operand:GPR 1 "reg_or_0_operand" "rJ"))
89	   (match_operand:SI 2 "const_int_operand")] ;; model
90	 UNSPEC_SYNC_OLD_OP))]
91  ""
92  "am<amop>%A2.<amo>\t$zero,%z1,%0"
93  [(set (attr "length") (const_int 8))])
94
95(define_insn "atomic_fetch_<atomic_optab><mode>"
96  [(set (match_operand:GPR 0 "register_operand" "=&r")
97	(match_operand:GPR 1 "memory_operand" "+ZB"))
98   (set (match_dup 1)
99	(unspec_volatile:GPR
100	  [(any_atomic:GPR (match_dup 1)
101		     (match_operand:GPR 2 "reg_or_0_operand" "rJ"))
102	   (match_operand:SI 3 "const_int_operand")] ;; model
103	 UNSPEC_SYNC_OLD_OP))]
104  ""
105  "am<amop>%A3.<amo>\t%0,%z2,%1"
106  [(set (attr "length") (const_int 8))])
107
108(define_insn "atomic_exchange<mode>"
109  [(set (match_operand:GPR 0 "register_operand" "=&r")
110	(unspec_volatile:GPR
111	  [(match_operand:GPR 1 "memory_operand" "+ZB")
112	   (match_operand:SI 3 "const_int_operand")] ;; model
113	  UNSPEC_SYNC_EXCHANGE))
114   (set (match_dup 1)
115	(match_operand:GPR 2 "register_operand" "r"))]
116  ""
117  "amswap%A3.<amo>\t%0,%z2,%1"
118  [(set (attr "length") (const_int 8))])
119
120(define_insn "atomic_cas_value_strong<mode>"
121  [(set (match_operand:GPR 0 "register_operand" "=&r")
122	(match_operand:GPR 1 "memory_operand" "+ZC"))
123   (set (match_dup 1)
124	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
125			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")
126			      (match_operand:SI 4 "const_int_operand")  ;; mod_s
127			      (match_operand:SI 5 "const_int_operand")] ;; mod_f
128	 UNSPEC_COMPARE_AND_SWAP))
129   (clobber (match_scratch:GPR 6 "=&r"))]
130  ""
131{
132  output_asm_insn ("1:", operands);
133  output_asm_insn ("ll.<amo>\t%0,%1", operands);
134
135  /* Like the test case atomic-cas-int.C, in loongarch64, O1 and higher, the
136     return value of the val_without_const_folding will not be truncated and
137     will be passed directly to the function compare_exchange_strong.
138     However, the instruction 'bne' does not distinguish between 32-bit and
139     64-bit operations.  so if the upper 32 bits of the register are not
140     extended by the 32nd bit symbol, then the comparison may not be valid
141     here.  This will affect the result of the operation.  */
142
143  if (TARGET_64BIT && REG_P (operands[2])
144      && GET_MODE (operands[2]) == SImode)
145    {
146      output_asm_insn ("addi.w\t%6,%2,0", operands);
147      output_asm_insn ("bne\t%0,%6,2f", operands);
148    }
149  else
150    output_asm_insn ("bne\t%0,%z2,2f", operands);
151
152  output_asm_insn ("or%i3\t%6,$zero,%3", operands);
153  output_asm_insn ("sc.<amo>\t%6,%1", operands);
154  output_asm_insn ("beqz\t%6,1b", operands);
155  output_asm_insn ("b\t3f", operands);
156  output_asm_insn ("2:", operands);
157  output_asm_insn ("%G5", operands);
158  output_asm_insn ("3:", operands);
159
160  return "";
161}
162  [(set (attr "length")
163     (if_then_else
164	(and (match_test "GET_MODE (operands[2]) == SImode")
165	     (match_test "REG_P (operands[2])"))
166	(const_int 32)
167	(const_int 28)))])
168
169(define_expand "atomic_compare_and_swap<mode>"
170  [(match_operand:SI 0 "register_operand" "")   ;; bool output
171   (match_operand:GPR 1 "register_operand" "")  ;; val output
172   (match_operand:GPR 2 "memory_operand" "")    ;; memory
173   (match_operand:GPR 3 "reg_or_0_operand" "")  ;; expected value
174   (match_operand:GPR 4 "reg_or_0_operand" "")  ;; desired value
175   (match_operand:SI 5 "const_int_operand" "")  ;; is_weak
176   (match_operand:SI 6 "const_int_operand" "")  ;; mod_s
177   (match_operand:SI 7 "const_int_operand" "")] ;; mod_f
178  ""
179{
180  emit_insn (gen_atomic_cas_value_strong<mode> (operands[1], operands[2],
181						operands[3], operands[4],
182						operands[6], operands[7]));
183
184  rtx compare = operands[1];
185  if (operands[3] != const0_rtx)
186    {
187      rtx difference = gen_rtx_MINUS (<MODE>mode, operands[1], operands[3]);
188      compare = gen_reg_rtx (<MODE>mode);
189      emit_insn (gen_rtx_SET (compare, difference));
190    }
191
192  if (word_mode != <MODE>mode)
193    {
194      rtx reg = gen_reg_rtx (word_mode);
195      emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
196      compare = reg;
197    }
198
199  emit_insn (gen_rtx_SET (operands[0],
200			  gen_rtx_EQ (SImode, compare, const0_rtx)));
201  DONE;
202})
203
204(define_expand "atomic_test_and_set"
205  [(match_operand:QI 0 "register_operand" "")     ;; bool output
206   (match_operand:QI 1 "memory_operand" "+ZB")    ;; memory
207   (match_operand:SI 2 "const_int_operand" "")]   ;; model
208  ""
209{
210  /* We have no QImode atomics, so use the address LSBs to form a mask,
211     then use an aligned SImode atomic.  */
212  rtx result = operands[0];
213  rtx mem = operands[1];
214  rtx model = operands[2];
215  rtx addr = force_reg (Pmode, XEXP (mem, 0));
216  rtx tmp_reg = gen_reg_rtx (Pmode);
217  rtx zero_reg = gen_rtx_REG (Pmode, 0);
218
219  rtx aligned_addr = gen_reg_rtx (Pmode);
220  emit_move_insn (tmp_reg, gen_rtx_PLUS (Pmode, zero_reg, GEN_INT (-4)));
221  emit_move_insn (aligned_addr, gen_rtx_AND (Pmode, addr, tmp_reg));
222
223  rtx aligned_mem = change_address (mem, SImode, aligned_addr);
224  set_mem_alias_set (aligned_mem, 0);
225
226  rtx offset = gen_reg_rtx (SImode);
227  emit_move_insn (offset, gen_rtx_AND (SImode, gen_lowpart (SImode, addr),
228				       GEN_INT (3)));
229
230  rtx tmp = gen_reg_rtx (SImode);
231  emit_move_insn (tmp, GEN_INT (1));
232
233  rtx shmt = gen_reg_rtx (SImode);
234  emit_move_insn (shmt, gen_rtx_ASHIFT (SImode, offset, GEN_INT (3)));
235
236  rtx word = gen_reg_rtx (SImode);
237  emit_move_insn (word, gen_rtx_ASHIFT (SImode, tmp, shmt));
238
239  tmp = gen_reg_rtx (SImode);
240  emit_insn (gen_atomic_fetch_orsi (tmp, aligned_mem, word, model));
241
242  emit_move_insn (gen_lowpart (SImode, result),
243		  gen_rtx_LSHIFTRT (SImode, tmp, shmt));
244  DONE;
245})
246
247(define_insn "atomic_cas_value_cmp_and_7_<mode>"
248  [(set (match_operand:GPR 0 "register_operand" "=&r")
249	(match_operand:GPR 1 "memory_operand" "+ZC"))
250   (set (match_dup 1)
251	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
252			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")
253			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")
254			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")
255			      (match_operand:SI 6 "const_int_operand")] ;; model
256	 UNSPEC_COMPARE_AND_SWAP))
257   (clobber (match_scratch:GPR 7 "=&r"))]
258  ""
259{
260  return "1:\\n\\t"
261	 "ll.<amo>\\t%0,%1\\n\\t"
262	 "and\\t%7,%0,%2\\n\\t"
263	 "bne\\t%7,%z4,2f\\n\\t"
264	 "and\\t%7,%0,%z3\\n\\t"
265	 "or%i5\\t%7,%7,%5\\n\\t"
266	 "sc.<amo>\\t%7,%1\\n\\t"
267	 "beq\\t$zero,%7,1b\\n\\t"
268	 "b\\t3f\\n\\t"
269	 "2:\\n\\t"
270	 "%G6\\n\\t"
271	 "3:\\n\\t";
272}
273  [(set (attr "length") (const_int 36))])
274
275(define_expand "atomic_compare_and_swap<mode>"
276  [(match_operand:SI 0 "register_operand" "")   ;; bool output
277   (match_operand:SHORT 1 "register_operand" "")  ;; val output
278   (match_operand:SHORT 2 "memory_operand" "")    ;; memory
279   (match_operand:SHORT 3 "reg_or_0_operand" "")  ;; expected value
280   (match_operand:SHORT 4 "reg_or_0_operand" "")  ;; desired value
281   (match_operand:SI 5 "const_int_operand" "")  ;; is_weak
282   (match_operand:SI 6 "const_int_operand" "")  ;; mod_s
283   (match_operand:SI 7 "const_int_operand" "")] ;; mod_f
284  ""
285{
286  union loongarch_gen_fn_ptrs generator;
287  generator.fn_7 = gen_atomic_cas_value_cmp_and_7_si;
288  loongarch_expand_atomic_qihi (generator, operands[1], operands[2],
289				operands[3], operands[4], operands[7]);
290
291  rtx compare = operands[1];
292  if (operands[3] != const0_rtx)
293    {
294      machine_mode mode = GET_MODE (operands[3]);
295      rtx op1 = convert_modes (SImode, mode, operands[1], true);
296      rtx op3 = convert_modes (SImode, mode, operands[3], true);
297      rtx difference = gen_rtx_MINUS (SImode, op1, op3);
298      compare = gen_reg_rtx (SImode);
299      emit_insn (gen_rtx_SET (compare, difference));
300    }
301
302  if (word_mode != <MODE>mode)
303    {
304      rtx reg = gen_reg_rtx (word_mode);
305      emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
306      compare = reg;
307    }
308
309  emit_insn (gen_rtx_SET (operands[0],
310			  gen_rtx_EQ (SImode, compare, const0_rtx)));
311  DONE;
312})
313
314(define_insn "atomic_cas_value_add_7_<mode>"
315  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
316	(match_operand:GPR 1 "memory_operand" "+ZC"))
317   (set (match_dup 1)
318	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
319			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
320			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
321			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
322			      (match_operand:SI 6 "const_int_operand")]		;; model
323	 UNSPEC_COMPARE_AND_SWAP_ADD))
324   (clobber (match_scratch:GPR 7 "=&r"))
325   (clobber (match_scratch:GPR 8 "=&r"))]
326  ""
327{
328  return "1:\\n\\t"
329	 "ll.<amo>\\t%0,%1\\n\\t"
330	 "and\\t%7,%0,%3\\n\\t"
331	 "add.w\\t%8,%0,%z5\\n\\t"
332	 "and\\t%8,%8,%z2\\n\\t"
333	 "or%i8\\t%7,%7,%8\\n\\t"
334	 "sc.<amo>\\t%7,%1\\n\\t"
335	 "beq\\t$zero,%7,1b";
336}
337
338  [(set (attr "length") (const_int 28))])
339
340(define_insn "atomic_cas_value_sub_7_<mode>"
341  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
342	(match_operand:GPR 1 "memory_operand" "+ZC"))
343   (set (match_dup 1)
344	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
345			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
346			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
347			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
348			      (match_operand:SI 6 "const_int_operand")]		;; model
349	 UNSPEC_COMPARE_AND_SWAP_SUB))
350   (clobber (match_scratch:GPR 7 "=&r"))
351   (clobber (match_scratch:GPR 8 "=&r"))]
352  ""
353{
354  return "1:\\n\\t"
355	 "ll.<amo>\\t%0,%1\\n\\t"
356	 "and\\t%7,%0,%3\\n\\t"
357	 "sub.w\\t%8,%0,%z5\\n\\t"
358	 "and\\t%8,%8,%z2\\n\\t"
359	 "or%i8\\t%7,%7,%8\\n\\t"
360	 "sc.<amo>\\t%7,%1\\n\\t"
361	 "beq\\t$zero,%7,1b";
362}
363  [(set (attr "length") (const_int 28))])
364
365(define_insn "atomic_cas_value_and_7_<mode>"
366  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
367	(match_operand:GPR 1 "memory_operand" "+ZC"))
368   (set (match_dup 1)
369	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
370			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
371			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
372			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
373			      (match_operand:SI 6 "const_int_operand")]		;; model
374	 UNSPEC_COMPARE_AND_SWAP_AND))
375   (clobber (match_scratch:GPR 7 "=&r"))
376   (clobber (match_scratch:GPR 8 "=&r"))]
377  ""
378{
379  return "1:\\n\\t"
380	 "ll.<amo>\\t%0,%1\\n\\t"
381	 "and\\t%7,%0,%3\\n\\t"
382	 "and\\t%8,%0,%z5\\n\\t"
383	 "and\\t%8,%8,%z2\\n\\t"
384	 "or%i8\\t%7,%7,%8\\n\\t"
385	 "sc.<amo>\\t%7,%1\\n\\t"
386	 "beq\\t$zero,%7,1b";
387}
388  [(set (attr "length") (const_int 28))])
389
390(define_insn "atomic_cas_value_xor_7_<mode>"
391  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
392	(match_operand:GPR 1 "memory_operand" "+ZC"))
393   (set (match_dup 1)
394	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
395			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
396			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
397			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
398			      (match_operand:SI 6 "const_int_operand")]		;; model
399	 UNSPEC_COMPARE_AND_SWAP_XOR))
400   (clobber (match_scratch:GPR 7 "=&r"))
401   (clobber (match_scratch:GPR 8 "=&r"))]
402  ""
403{
404  return "1:\\n\\t"
405	 "ll.<amo>\\t%0,%1\\n\\t"
406	 "and\\t%7,%0,%3\\n\\t"
407	 "xor\\t%8,%0,%z5\\n\\t"
408	 "and\\t%8,%8,%z2\\n\\t"
409	 "or%i8\\t%7,%7,%8\\n\\t"
410	 "sc.<amo>\\t%7,%1\\n\\t"
411	 "beq\\t$zero,%7,1b";
412}
413
414  [(set (attr "length") (const_int 28))])
415
416(define_insn "atomic_cas_value_or_7_<mode>"
417  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
418	(match_operand:GPR 1 "memory_operand" "+ZC"))
419   (set (match_dup 1)
420	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
421			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
422			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
423			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
424			      (match_operand:SI 6 "const_int_operand")]		;; model
425	 UNSPEC_COMPARE_AND_SWAP_OR))
426   (clobber (match_scratch:GPR 7 "=&r"))
427   (clobber (match_scratch:GPR 8 "=&r"))]
428  ""
429{
430  return "1:\\n\\t"
431	 "ll.<amo>\\t%0,%1\\n\\t"
432	 "and\\t%7,%0,%3\\n\\t"
433	 "or\\t%8,%0,%z5\\n\\t"
434	 "and\\t%8,%8,%z2\\n\\t"
435	 "or%i8\\t%7,%7,%8\\n\\t"
436	 "sc.<amo>\\t%7,%1\\n\\t"
437	 "beq\\t$zero,%7,1b";
438}
439
440  [(set (attr "length") (const_int 28))])
441
442(define_insn "atomic_cas_value_nand_7_<mode>"
443  [(set (match_operand:GPR 0 "register_operand" "=&r")				;; res
444	(match_operand:GPR 1 "memory_operand" "+ZC"))
445   (set (match_dup 1)
446	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")	;; mask
447			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")	;; inverted_mask
448			      (match_operand:GPR 4 "reg_or_0_operand"  "rJ")	;; old val
449			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")	;; new val
450			      (match_operand:SI 6 "const_int_operand")]		;; model
451	 UNSPEC_COMPARE_AND_SWAP_NAND))
452   (clobber (match_scratch:GPR 7 "=&r"))
453   (clobber (match_scratch:GPR 8 "=&r"))]
454  ""
455{
456  return "1:\\n\\t"
457	 "ll.<amo>\\t%0,%1\\n\\t"
458	 "and\\t%7,%0,%3\\n\\t"
459	 "and\\t%8,%0,%z5\\n\\t"
460	 "xor\\t%8,%8,%z2\\n\\t"
461	 "or%i8\\t%7,%7,%8\\n\\t"
462	 "sc.<amo>\\t%7,%1\\n\\t"
463	 "beq\\t$zero,%7,1b";
464}
465  [(set (attr "length") (const_int 28))])
466
467(define_insn "atomic_cas_value_exchange_7_<mode>"
468  [(set (match_operand:GPR 0 "register_operand" "=&r")
469	(match_operand:GPR 1 "memory_operand" "+ZC"))
470   (set (match_dup 1)
471	(unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
472			      (match_operand:GPR 3 "reg_or_0_operand" "rJ")
473			      (match_operand:GPR 4 "reg_or_0_operand" "rJ")
474			      (match_operand:GPR 5 "reg_or_0_operand"  "rJ")
475			      (match_operand:SI 6 "const_int_operand")] ;; model
476	 UNSPEC_SYNC_EXCHANGE))
477   (clobber (match_scratch:GPR 7 "=&r"))]
478  ""
479{
480  return "1:\\n\\t"
481	 "ll.<amo>\\t%0,%1\\n\\t"
482	 "and\\t%7,%0,%z3\\n\\t"
483	 "or%i5\\t%7,%7,%5\\n\\t"
484	 "sc.<amo>\\t%7,%1\\n\\t"
485	 "beqz\\t%7,1b\\n\\t";
486}
487  [(set (attr "length") (const_int 20))])
488
489(define_expand "atomic_exchange<mode>"
490  [(set (match_operand:SHORT 0 "register_operand")
491	(unspec_volatile:SHORT
492	  [(match_operand:SHORT 1 "memory_operand")
493	   (match_operand:SI 3 "const_int_operand")] ;; model
494	  UNSPEC_SYNC_EXCHANGE))
495   (set (match_dup 1)
496	(match_operand:SHORT 2 "register_operand"))]
497  ""
498{
499  union loongarch_gen_fn_ptrs generator;
500  generator.fn_7 = gen_atomic_cas_value_exchange_7_si;
501  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
502				const0_rtx, operands[2], operands[3]);
503  DONE;
504})
505
506(define_expand "atomic_fetch_add<mode>"
507  [(set (match_operand:SHORT 0 "register_operand" "=&r")
508	(match_operand:SHORT 1 "memory_operand" "+ZB"))
509   (set (match_dup 1)
510	(unspec_volatile:SHORT
511	  [(plus:SHORT (match_dup 1)
512		       (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
513	   (match_operand:SI 3 "const_int_operand")] ;; model
514	 UNSPEC_SYNC_OLD_OP))]
515  ""
516{
517  union loongarch_gen_fn_ptrs generator;
518  generator.fn_7 = gen_atomic_cas_value_add_7_si;
519  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
520				operands[1], operands[2], operands[3]);
521  DONE;
522})
523
524(define_expand "atomic_fetch_sub<mode>"
525  [(set (match_operand:SHORT 0 "register_operand" "=&r")
526	(match_operand:SHORT 1 "memory_operand" "+ZB"))
527   (set (match_dup 1)
528	(unspec_volatile:SHORT
529	  [(minus:SHORT (match_dup 1)
530			(match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
531	   (match_operand:SI 3 "const_int_operand")] ;; model
532	 UNSPEC_SYNC_OLD_OP))]
533  ""
534{
535  union loongarch_gen_fn_ptrs generator;
536  generator.fn_7 = gen_atomic_cas_value_sub_7_si;
537  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
538				operands[1], operands[2], operands[3]);
539  DONE;
540})
541
542(define_expand "atomic_fetch_and<mode>"
543  [(set (match_operand:SHORT 0 "register_operand" "=&r")
544	(match_operand:SHORT 1 "memory_operand" "+ZB"))
545   (set (match_dup 1)
546	(unspec_volatile:SHORT
547	  [(and:SHORT (match_dup 1)
548		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
549	   (match_operand:SI 3 "const_int_operand")] ;; model
550	 UNSPEC_SYNC_OLD_OP))]
551  ""
552{
553  union loongarch_gen_fn_ptrs generator;
554  generator.fn_7 = gen_atomic_cas_value_and_7_si;
555  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
556				operands[1], operands[2], operands[3]);
557  DONE;
558})
559
560(define_expand "atomic_fetch_xor<mode>"
561  [(set (match_operand:SHORT 0 "register_operand" "=&r")
562	(match_operand:SHORT 1 "memory_operand" "+ZB"))
563   (set (match_dup 1)
564	(unspec_volatile:SHORT
565	  [(xor:SHORT (match_dup 1)
566		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
567	   (match_operand:SI 3 "const_int_operand")] ;; model
568	 UNSPEC_SYNC_OLD_OP))]
569  ""
570{
571  union loongarch_gen_fn_ptrs generator;
572  generator.fn_7 = gen_atomic_cas_value_xor_7_si;
573  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
574				operands[1], operands[2], operands[3]);
575  DONE;
576})
577
578(define_expand "atomic_fetch_or<mode>"
579  [(set (match_operand:SHORT 0 "register_operand" "=&r")
580	(match_operand:SHORT 1 "memory_operand" "+ZB"))
581   (set (match_dup 1)
582	(unspec_volatile:SHORT
583	  [(ior:SHORT (match_dup 1)
584		      (match_operand:SHORT 2 "reg_or_0_operand" "rJ"))
585	   (match_operand:SI 3 "const_int_operand")] ;; model
586	 UNSPEC_SYNC_OLD_OP))]
587  ""
588{
589  union loongarch_gen_fn_ptrs generator;
590  generator.fn_7 = gen_atomic_cas_value_or_7_si;
591  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
592				operands[1], operands[2], operands[3]);
593  DONE;
594})
595
596(define_expand "atomic_fetch_nand<mode>"
597  [(set (match_operand:SHORT 0 "register_operand" "=&r")
598	(match_operand:SHORT 1 "memory_operand" "+ZB"))
599   (set (match_dup 1)
600	(unspec_volatile:SHORT
601	  [(not:SHORT (and:SHORT (match_dup 1)
602				 (match_operand:SHORT 2 "reg_or_0_operand" "rJ")))
603	   (match_operand:SI 3 "const_int_operand")] ;; model
604	 UNSPEC_SYNC_OLD_OP))]
605  ""
606{
607  union loongarch_gen_fn_ptrs generator;
608  generator.fn_7 = gen_atomic_cas_value_nand_7_si;
609  loongarch_expand_atomic_qihi (generator, operands[0], operands[1],
610				operands[1], operands[2], operands[3]);
611  DONE;
612})
613