1 ;; Machine description for RISC-V atomic operations.
2 ;; Copyright (C) 2011-2024 Free Software Foundation, Inc.
3 ;; Contributed by Andrew Waterman (andrew@sifive.com).
4 ;; Based on MIPS target for GNU compiler.
6 ;; This file is part of GCC.
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)
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.
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/>.
22 (define_c_enum "unspec" [
23 UNSPEC_COMPARE_AND_SWAP
24 UNSPEC_COMPARE_AND_SWAP_SUBWORD
26 UNSPEC_SYNC_OLD_OP_SUBWORD
28 UNSPEC_SYNC_EXCHANGE_SUBWORD
36 (define_expand "mem_thread_fence"
37 [(match_operand:SI 0 "const_int_operand" "")] ;; model
40 enum memmodel model = memmodel_base (INTVAL (operands[0]));
42 if (TARGET_ZTSO && model == MEMMODEL_SEQ_CST)
44 rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
45 MEM_VOLATILE_P (mem) = 1;
46 emit_insn (gen_mem_thread_fence_ztso (mem, operands[0]));
48 else if (!TARGET_ZTSO && model != MEMMODEL_RELAXED)
50 rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
51 MEM_VOLATILE_P (mem) = 1;
52 emit_insn (gen_mem_thread_fence_rvwmo (mem, operands[0]));
57 ;; Atomic memory operations.
59 (define_expand "atomic_load<mode>"
60 [(match_operand:GPR 0 "register_operand")
61 (match_operand:GPR 1 "memory_operand")
62 (match_operand:SI 2 "const_int_operand")] ;; model
66 emit_insn (gen_atomic_load_ztso<mode> (operands[0], operands[1],
69 emit_insn (gen_atomic_load_rvwmo<mode> (operands[0], operands[1],
74 (define_expand "atomic_store<mode>"
75 [(match_operand:GPR 0 "memory_operand")
76 (match_operand:GPR 1 "reg_or_0_operand")
77 (match_operand:SI 2 "const_int_operand")] ;; model
81 emit_insn (gen_atomic_store_ztso<mode> (operands[0], operands[1],
84 emit_insn (gen_atomic_store_rvwmo<mode> (operands[0], operands[1],
89 (define_insn "atomic_<atomic_optab><mode>"
90 [(set (match_operand:GPR 0 "memory_operand" "+A")
92 [(any_atomic:GPR (match_dup 0)
93 (match_operand:GPR 1 "reg_or_0_operand" "rJ"))
94 (match_operand:SI 2 "const_int_operand")] ;; model
97 "amo<insn>.<amo>%A2\tzero,%z1,%0"
98 [(set_attr "type" "atomic")
99 (set (attr "length") (const_int 4))])
101 (define_insn "atomic_fetch_<atomic_optab><mode>"
102 [(set (match_operand:GPR 0 "register_operand" "=&r")
103 (match_operand:GPR 1 "memory_operand" "+A"))
106 [(any_atomic:GPR (match_dup 1)
107 (match_operand:GPR 2 "reg_or_0_operand" "rJ"))
108 (match_operand:SI 3 "const_int_operand")] ;; model
109 UNSPEC_SYNC_OLD_OP))]
111 "amo<insn>.<amo>%A3\t%0,%z2,%1"
112 [(set_attr "type" "atomic")
113 (set (attr "length") (const_int 4))])
115 (define_insn "subword_atomic_fetch_strong_<atomic_optab>"
116 [(set (match_operand:SI 0 "register_operand" "=&r") ;; old value at mem
117 (match_operand:SI 1 "memory_operand" "+A")) ;; mem location
120 [(any_atomic:SI (match_dup 1)
121 (match_operand:SI 2 "register_operand" "rI")) ;; value for op
122 (match_operand:SI 3 "const_int_operand")] ;; model
123 UNSPEC_SYNC_OLD_OP_SUBWORD))
124 (match_operand:SI 4 "register_operand" "rI") ;; mask
125 (match_operand:SI 5 "register_operand" "rI") ;; not_mask
126 (clobber (match_scratch:SI 6 "=&r")) ;; tmp_1
127 (clobber (match_scratch:SI 7 "=&r"))] ;; tmp_2
128 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
132 "<insn>\t%6, %0, %2\;"
136 "sc.w%J3\t%6, %7, %1\;"
139 [(set_attr "type" "multi")
140 (set (attr "length") (const_int 28))])
142 (define_expand "atomic_fetch_nand<mode>"
143 [(match_operand:SHORT 0 "register_operand") ;; old value at mem
144 (not:SHORT (and:SHORT (match_operand:SHORT 1 "memory_operand") ;; mem location
145 (match_operand:SHORT 2 "reg_or_0_operand"))) ;; value for op
146 (match_operand:SI 3 "const_int_operand")] ;; model
147 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
149 /* We have no QImode/HImode atomics, so form a mask, then use
150 subword_atomic_fetch_strong_nand to implement a LR/SC version of the
153 /* Logic duplicated in gcc/libgcc/config/riscv/atomic.c for use when inlining
156 rtx old = gen_reg_rtx (SImode);
157 rtx mem = operands[1];
158 rtx value = operands[2];
159 rtx model = operands[3];
160 rtx aligned_mem = gen_reg_rtx (SImode);
161 rtx shift = gen_reg_rtx (SImode);
162 rtx mask = gen_reg_rtx (SImode);
163 rtx not_mask = gen_reg_rtx (SImode);
165 riscv_subword_address (mem, &aligned_mem, &shift, &mask, ¬_mask);
167 rtx shifted_value = gen_reg_rtx (SImode);
168 riscv_lshift_subword (<MODE>mode, value, shift, &shifted_value);
170 emit_insn (gen_subword_atomic_fetch_strong_nand (old, aligned_mem,
171 shifted_value, model,
174 emit_move_insn (old, gen_rtx_ASHIFTRT (SImode, old,
175 gen_lowpart (QImode, shift)));
177 emit_move_insn (operands[0], gen_lowpart (<MODE>mode, old));
182 (define_insn "subword_atomic_fetch_strong_nand"
183 [(set (match_operand:SI 0 "register_operand" "=&r") ;; old value at mem
184 (match_operand:SI 1 "memory_operand" "+A")) ;; mem location
187 [(not:SI (and:SI (match_dup 1)
188 (match_operand:SI 2 "register_operand" "rI"))) ;; value for op
189 (match_operand:SI 3 "const_int_operand")] ;; mask
190 UNSPEC_SYNC_OLD_OP_SUBWORD))
191 (match_operand:SI 4 "register_operand" "rI") ;; mask
192 (match_operand:SI 5 "register_operand" "rI") ;; not_mask
193 (clobber (match_scratch:SI 6 "=&r")) ;; tmp_1
194 (clobber (match_scratch:SI 7 "=&r"))] ;; tmp_2
195 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
204 "sc.w%J3\t%6, %7, %1\;"
207 [(set_attr "type" "multi")
208 (set (attr "length") (const_int 32))])
210 (define_expand "atomic_fetch_<atomic_optab><mode>"
211 [(match_operand:SHORT 0 "register_operand") ;; old value at mem
212 (any_atomic:SHORT (match_operand:SHORT 1 "memory_operand") ;; mem location
213 (match_operand:SHORT 2 "reg_or_0_operand")) ;; value for op
214 (match_operand:SI 3 "const_int_operand")] ;; model
215 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
217 /* We have no QImode/HImode atomics, so form a mask, then use
218 subword_atomic_fetch_strong_<mode> to implement a LR/SC version of the
221 /* Logic duplicated in gcc/libgcc/config/riscv/atomic.c for use when inlining
224 rtx old = gen_reg_rtx (SImode);
225 rtx mem = operands[1];
226 rtx value = operands[2];
227 rtx model = operands[3];
228 rtx aligned_mem = gen_reg_rtx (SImode);
229 rtx shift = gen_reg_rtx (SImode);
230 rtx mask = gen_reg_rtx (SImode);
231 rtx not_mask = gen_reg_rtx (SImode);
233 riscv_subword_address (mem, &aligned_mem, &shift, &mask, ¬_mask);
235 rtx shifted_value = gen_reg_rtx (SImode);
236 riscv_lshift_subword (<MODE>mode, value, shift, &shifted_value);
238 emit_insn (gen_subword_atomic_fetch_strong_<atomic_optab> (old, aligned_mem,
243 emit_move_insn (old, gen_rtx_ASHIFTRT (SImode, old,
244 gen_lowpart (QImode, shift)));
246 emit_move_insn (operands[0], gen_lowpart (<MODE>mode, old));
251 (define_insn "atomic_exchange<mode>"
252 [(set (match_operand:GPR 0 "register_operand" "=&r")
254 [(match_operand:GPR 1 "memory_operand" "+A")
255 (match_operand:SI 3 "const_int_operand")] ;; model
256 UNSPEC_SYNC_EXCHANGE))
258 (match_operand:GPR 2 "register_operand" "0"))]
260 "amoswap.<amo>%A3\t%0,%z2,%1"
261 [(set_attr "type" "atomic")
262 (set (attr "length") (const_int 4))])
264 (define_expand "atomic_exchange<mode>"
265 [(match_operand:SHORT 0 "register_operand") ;; old value at mem
266 (match_operand:SHORT 1 "memory_operand") ;; mem location
267 (match_operand:SHORT 2 "register_operand") ;; value
268 (match_operand:SI 3 "const_int_operand")] ;; model
269 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
271 rtx old = gen_reg_rtx (SImode);
272 rtx mem = operands[1];
273 rtx value = operands[2];
274 rtx model = operands[3];
275 rtx aligned_mem = gen_reg_rtx (SImode);
276 rtx shift = gen_reg_rtx (SImode);
277 rtx mask = gen_reg_rtx (SImode);
278 rtx not_mask = gen_reg_rtx (SImode);
280 riscv_subword_address (mem, &aligned_mem, &shift, &mask, ¬_mask);
282 rtx shifted_value = gen_reg_rtx (SImode);
283 riscv_lshift_subword (<MODE>mode, value, shift, &shifted_value);
285 emit_insn (gen_subword_atomic_exchange_strong (old, aligned_mem,
286 shifted_value, model,
289 emit_move_insn (old, gen_rtx_ASHIFTRT (SImode, old,
290 gen_lowpart (QImode, shift)));
292 emit_move_insn (operands[0], gen_lowpart (<MODE>mode, old));
296 (define_insn "subword_atomic_exchange_strong"
297 [(set (match_operand:SI 0 "register_operand" "=&r") ;; old value at mem
298 (match_operand:SI 1 "memory_operand" "+A")) ;; mem location
301 [(match_operand:SI 2 "reg_or_0_operand" "rI") ;; value
302 (match_operand:SI 3 "const_int_operand")] ;; model
303 UNSPEC_SYNC_EXCHANGE_SUBWORD))
304 (match_operand:SI 4 "reg_or_0_operand" "rI") ;; not_mask
305 (clobber (match_scratch:SI 5 "=&r"))] ;; tmp_1
306 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
312 "sc.w%J3\t%5, %5, %1\;"
315 [(set_attr "type" "multi")
316 (set (attr "length") (const_int 20))])
318 (define_insn "atomic_cas_value_strong<mode>"
319 [(set (match_operand:GPR 0 "register_operand" "=&r")
320 (match_operand:GPR 1 "memory_operand" "+A"))
322 (unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
323 (match_operand:GPR 3 "reg_or_0_operand" "rJ")
324 (match_operand:SI 4 "const_int_operand") ;; mod_s
325 (match_operand:SI 5 "const_int_operand")] ;; mod_f
326 UNSPEC_COMPARE_AND_SWAP))
327 (clobber (match_scratch:GPR 6 "=&r"))]
330 enum memmodel model_success = (enum memmodel) INTVAL (operands[4]);
331 enum memmodel model_failure = (enum memmodel) INTVAL (operands[5]);
332 /* Find the union of the two memory models so we can satisfy both success
333 and failure memory models. */
334 operands[5] = GEN_INT (riscv_union_memmodels (model_success, model_failure));
336 "lr.<amo>%I5\t%0,%1\;"
338 "sc.<amo>%J5\t%6,%z3,%1\;"
342 [(set_attr "type" "multi")
343 (set (attr "length") (const_int 16))])
345 (define_expand "atomic_compare_and_swap<mode>"
346 [(match_operand:SI 0 "register_operand" "") ;; bool output
347 (match_operand:GPR 1 "register_operand" "") ;; val output
348 (match_operand:GPR 2 "memory_operand" "") ;; memory
349 (match_operand:GPR 3 "reg_or_0_operand" "") ;; expected value
350 (match_operand:GPR 4 "reg_or_0_operand" "") ;; desired value
351 (match_operand:SI 5 "const_int_operand" "") ;; is_weak
352 (match_operand:SI 6 "const_int_operand" "") ;; mod_s
353 (match_operand:SI 7 "const_int_operand" "")] ;; mod_f
356 emit_insn (gen_atomic_cas_value_strong<mode> (operands[1], operands[2],
357 operands[3], operands[4],
358 operands[6], operands[7]));
360 rtx compare = operands[1];
361 if (operands[3] != const0_rtx)
363 rtx difference = gen_rtx_MINUS (<MODE>mode, operands[1], operands[3]);
364 compare = gen_reg_rtx (<MODE>mode);
365 emit_insn (gen_rtx_SET (compare, difference));
368 if (word_mode != <MODE>mode)
370 rtx reg = gen_reg_rtx (word_mode);
371 emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
375 emit_insn (gen_rtx_SET (operands[0], gen_rtx_EQ (SImode, compare, const0_rtx)));
379 (define_expand "atomic_compare_and_swap<mode>"
380 [(match_operand:SI 0 "register_operand") ;; bool output
381 (match_operand:SHORT 1 "register_operand") ;; val output
382 (match_operand:SHORT 2 "memory_operand") ;; memory
383 (match_operand:SHORT 3 "reg_or_0_operand") ;; expected value
384 (match_operand:SHORT 4 "reg_or_0_operand") ;; desired value
385 (match_operand:SI 5 "const_int_operand") ;; is_weak
386 (match_operand:SI 6 "const_int_operand") ;; mod_s
387 (match_operand:SI 7 "const_int_operand")] ;; mod_f
388 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
390 emit_insn (gen_atomic_cas_value_strong<mode> (operands[1], operands[2],
391 operands[3], operands[4],
392 operands[6], operands[7]));
394 rtx val = gen_reg_rtx (SImode);
395 if (operands[1] != const0_rtx)
396 emit_move_insn (val, gen_rtx_SIGN_EXTEND (SImode, operands[1]));
398 emit_move_insn (val, const0_rtx);
400 rtx exp = gen_reg_rtx (SImode);
401 if (operands[3] != const0_rtx)
402 emit_move_insn (exp, gen_rtx_SIGN_EXTEND (SImode, operands[3]));
404 emit_move_insn (exp, const0_rtx);
407 if (exp != const0_rtx)
409 rtx difference = gen_rtx_MINUS (SImode, val, exp);
410 compare = gen_reg_rtx (SImode);
411 emit_move_insn (compare, difference);
414 if (word_mode != SImode)
416 rtx reg = gen_reg_rtx (word_mode);
417 emit_move_insn (reg, gen_rtx_SIGN_EXTEND (word_mode, compare));
421 emit_move_insn (operands[0], gen_rtx_EQ (SImode, compare, const0_rtx));
425 (define_expand "atomic_cas_value_strong<mode>"
426 [(match_operand:SHORT 0 "register_operand") ;; val output
427 (match_operand:SHORT 1 "memory_operand") ;; memory
428 (match_operand:SHORT 2 "reg_or_0_operand") ;; expected value
429 (match_operand:SHORT 3 "reg_or_0_operand") ;; desired value
430 (match_operand:SI 4 "const_int_operand") ;; mod_s
431 (match_operand:SI 5 "const_int_operand") ;; mod_f
432 (match_scratch:SHORT 6)]
433 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
435 /* We have no QImode/HImode atomics, so form a mask, then use
436 subword_atomic_cas_strong<mode> to implement a LR/SC version of the
439 /* Logic duplicated in gcc/libgcc/config/riscv/atomic.c for use when inlining
442 rtx old = gen_reg_rtx (SImode);
443 rtx mem = operands[1];
444 rtx aligned_mem = gen_reg_rtx (SImode);
445 rtx shift = gen_reg_rtx (SImode);
446 rtx mask = gen_reg_rtx (SImode);
447 rtx not_mask = gen_reg_rtx (SImode);
449 riscv_subword_address (mem, &aligned_mem, &shift, &mask, ¬_mask);
453 rtx shifted_o = gen_reg_rtx (SImode);
454 rtx shifted_n = gen_reg_rtx (SImode);
456 riscv_lshift_subword (<MODE>mode, o, shift, &shifted_o);
457 riscv_lshift_subword (<MODE>mode, n, shift, &shifted_n);
459 emit_move_insn (shifted_o, gen_rtx_AND (SImode, shifted_o, mask));
460 emit_move_insn (shifted_n, gen_rtx_AND (SImode, shifted_n, mask));
462 enum memmodel model_success = (enum memmodel) INTVAL (operands[4]);
463 enum memmodel model_failure = (enum memmodel) INTVAL (operands[5]);
464 /* Find the union of the two memory models so we can satisfy both success
465 and failure memory models. */
466 rtx model = GEN_INT (riscv_union_memmodels (model_success, model_failure));
468 emit_insn (gen_subword_atomic_cas_strong (old, aligned_mem,
469 shifted_o, shifted_n,
470 model, mask, not_mask));
472 emit_move_insn (old, gen_rtx_ASHIFTRT (SImode, old,
473 gen_lowpart (QImode, shift)));
475 emit_move_insn (operands[0], gen_lowpart (<MODE>mode, old));
480 (define_insn "subword_atomic_cas_strong"
481 [(set (match_operand:SI 0 "register_operand" "=&r") ;; old value at mem
482 (match_operand:SI 1 "memory_operand" "+A")) ;; mem location
484 (unspec_volatile:SI [(match_operand:SI 2 "reg_or_0_operand" "rJ") ;; expected value
485 (match_operand:SI 3 "reg_or_0_operand" "rJ")] ;; desired value
486 UNSPEC_COMPARE_AND_SWAP_SUBWORD))
487 (match_operand:SI 4 "const_int_operand") ;; model
488 (match_operand:SI 5 "register_operand" "rI") ;; mask
489 (match_operand:SI 6 "register_operand" "rI") ;; not_mask
490 (clobber (match_scratch:SI 7 "=&r"))] ;; tmp_1
491 "TARGET_ATOMIC && TARGET_INLINE_SUBWORD_ATOMIC"
499 "sc.w%J4\t%7, %7, %1\;"
503 [(set_attr "type" "multi")
504 (set (attr "length") (const_int 28))])
506 (define_expand "atomic_test_and_set"
507 [(match_operand:QI 0 "register_operand" "") ;; bool output
508 (match_operand:QI 1 "memory_operand" "+A") ;; memory
509 (match_operand:SI 2 "const_int_operand" "")] ;; model
512 /* We have no QImode atomics, so use the address LSBs to form a mask,
513 then use an aligned SImode atomic. */
514 rtx old = gen_reg_rtx (SImode);
515 rtx mem = operands[1];
516 rtx model = operands[2];
517 rtx set = gen_reg_rtx (QImode);
518 rtx aligned_mem = gen_reg_rtx (SImode);
519 rtx shift = gen_reg_rtx (SImode);
522 rtx _mask = gen_reg_rtx (SImode);
523 rtx _not_mask = gen_reg_rtx (SImode);
525 riscv_subword_address (mem, &aligned_mem, &shift, &_mask, &_not_mask);
527 emit_move_insn (set, GEN_INT (1));
528 rtx shifted_set = gen_reg_rtx (SImode);
529 riscv_lshift_subword (QImode, set, shift, &shifted_set);
531 emit_insn (gen_atomic_fetch_orsi (old, aligned_mem, shifted_set, model));
533 emit_move_insn (old, gen_rtx_ASHIFTRT (SImode, old,
534 gen_lowpart (QImode, shift)));
536 emit_move_insn (operands[0], gen_lowpart (QImode, old));