1;; GCC machine description for Tilera TILE-Gx synchronization
2;; instructions.
3;; Copyright (C) 2011-2020 Free Software Foundation, Inc.
4;; Contributed by Walter Lee (walt@tilera.com)
5;;
6;; This file is part of GCC.
7;;
8;; GCC is free software; you can redistribute it and/or modify it
9;; under the terms of the GNU General Public License as published
10;; by the Free Software Foundation; either version 3, or (at your
11;; option) any later version.
12;;
13;; GCC is distributed in the hope that it will be useful, but WITHOUT
14;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
16;; 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_code_iterator fetchop [plus ior and])
23(define_code_attr fetchop_name [(plus "add") (ior "or") (and "and")])
24
25(define_insn "mtspr_cmpexch<mode>"
26  [(set (reg:I48MODE TILEGX_CMPEXCH_REG)
27        (unspec_volatile:I48MODE
28         [(match_operand:I48MODE 0 "reg_or_0_operand" "rO")]
29         UNSPEC_SPR_MOVE))]
30  ""
31  "mtspr\tCMPEXCH_VALUE, %r0"
32  [(set_attr "type" "X1")])
33
34
35(define_expand "atomic_compare_and_swap<mode>"
36  [(match_operand:DI 0 "register_operand" "")		;; bool output
37   (match_operand:I48MODE 1 "register_operand" "")	;; val output
38   (match_operand:I48MODE 2 "nonautoincmem_operand" "") ;; memory
39   (match_operand:I48MODE 3 "reg_or_0_operand" "")	;; expected value
40   (match_operand:I48MODE 4 "reg_or_0_operand" "")	;; desired value
41   (match_operand:SI 5 "const_int_operand" "")		;; is_weak
42   (match_operand:SI 6 "const_int_operand" "")		;; mod_s
43   (match_operand:SI 7 "const_int_operand" "")]		;; mod_f
44  ""
45{
46  enum memmodel mod_s = (enum memmodel) INTVAL (operands[6]);
47
48  if (operands[3] != const0_rtx)
49    operands[3] = force_reg (<MODE>mode, operands[3]);
50  if (operands[4] != const0_rtx)
51    operands[4] = force_reg (<MODE>mode, operands[4]);
52
53  tilegx_pre_atomic_barrier (mod_s);
54  emit_insn (gen_mtspr_cmpexch<mode> (operands[3]));
55  emit_insn (gen_atomic_compare_and_swap_bare<mode> (operands[1], operands[2],
56                                                   operands[4]));
57  tilegx_post_atomic_barrier (mod_s);
58  emit_insn (gen_insn_cmpeq_<mode>di (operands[0], operands[1], operands[3]));
59  DONE;
60})
61
62
63(define_insn "atomic_compare_and_swap_bare<mode>"
64  [(set (match_operand:I48MODE 0 "register_operand" "=r")
65        (match_operand:I48MODE 1 "nonautoincmem_operand" "+U"))
66   (set (match_dup 1)
67        (unspec_volatile:I48MODE
68         [(match_dup 1)
69         (reg:I48MODE TILEGX_CMPEXCH_REG)
70         (match_operand:I48MODE 2 "reg_or_0_operand" "rO")]
71         UNSPEC_CMPXCHG))]
72  ""
73  "cmpexch<four_if_si>\t%0, %1, %r2"
74  [(set_attr "type" "X1_remote")])
75
76
77(define_expand "atomic_exchange<mode>"
78  [(match_operand:I48MODE 0 "register_operand" "")      ;; result
79   (match_operand:I48MODE 1 "nonautoincmem_operand" "") ;; memory
80   (match_operand:I48MODE 2 "reg_or_0_operand" "")      ;; input
81   (match_operand:SI 3 "const_int_operand" "")]         ;; model
82  ""
83{
84  enum memmodel model = (enum memmodel) INTVAL (operands[3]);
85
86  tilegx_pre_atomic_barrier (model);
87  emit_insn (gen_atomic_exchange_bare<mode> (operands[0], operands[1],
88                                             operands[2]));
89  tilegx_post_atomic_barrier (model);
90  DONE;
91})
92
93
94(define_insn "atomic_exchange_bare<mode>"
95  [(set (match_operand:I48MODE 0 "register_operand" "=r")
96	(match_operand:I48MODE 1 "nonautoincmem_operand" "+U"))
97   (set (match_dup 1)
98	(unspec_volatile:I48MODE
99	 [(match_operand:I48MODE 2 "reg_or_0_operand" "rO")]
100	 UNSPEC_XCHG))]
101  ""
102  "exch<four_if_si>\t%0, %1, %r2"
103  [(set_attr "type" "X1_remote")])
104
105
106(define_expand "atomic_fetch_<fetchop_name><mode>"
107  [(match_operand:I48MODE 0 "register_operand" "")      ;; result
108   (match_operand:I48MODE 1 "nonautoincmem_operand" "") ;; memory
109   (unspec_volatile:I48MODE
110    [(fetchop:I48MODE
111      (match_dup 1)
112      (match_operand:I48MODE 2 "reg_or_0_operand" ""))] ;; value
113    UNSPEC_ATOMIC)
114   (match_operand:SI 3 "const_int_operand" "")]         ;; model
115  ""
116{
117  enum memmodel model = (enum memmodel) INTVAL (operands[3]);
118
119  tilegx_pre_atomic_barrier (model);
120  emit_insn (gen_atomic_fetch_<fetchop_name>_bare<mode> (operands[0],
121                                                         operands[1],
122                                                         operands[2]));
123  tilegx_post_atomic_barrier (model);
124  DONE;
125})
126
127
128(define_insn "atomic_fetch_<fetchop_name>_bare<mode>"
129  [(set (match_operand:I48MODE 0 "register_operand" "=r")
130	(match_operand:I48MODE 1 "nonautoincmem_operand" "+U"))
131   (set (match_dup 1)
132	(unspec_volatile:I48MODE
133	 [(fetchop:I48MODE
134	   (match_dup 1)
135	   (match_operand:I48MODE 2 "reg_or_0_operand" "rO"))]
136	   UNSPEC_ATOMIC))]
137  ""
138  "fetch<fetchop_name><four_if_si>\t%0, %1, %r2"
139  [(set_attr "type" "X1_remote")])
140
141
142(define_expand "atomic_fetch_sub<mode>"
143  [(match_operand:I48MODE 0 "register_operand" "")      ;; result
144   (match_operand:I48MODE 1 "nonautoincmem_operand" "") ;; memory
145   (unspec_volatile:I48MODE
146    [(minus:I48MODE
147      (match_dup 1)
148      (match_operand:I48MODE 2 "reg_or_0_operand" ""))] ;; value
149    UNSPEC_ATOMIC)
150   (match_operand:SI 3 "const_int_operand" "")]         ;; model
151  ""
152{
153  rtx addend;
154  enum memmodel model = (enum memmodel) INTVAL (operands[3]);
155
156  if (operands[2] != const0_rtx)
157    {
158       addend = gen_reg_rtx (<MODE>mode);
159       emit_move_insn (addend,
160                       gen_rtx_MINUS (<MODE>mode, const0_rtx, operands[2]));
161    }
162  else
163    addend = operands[2];
164
165  tilegx_pre_atomic_barrier (model);
166  emit_insn (gen_atomic_fetch_add_bare<mode> (operands[0],
167                                              operands[1],
168                                              addend));
169  tilegx_post_atomic_barrier (model);
170  DONE;
171})
172
173
174(define_expand "atomic_test_and_set"
175  [(match_operand:QI 0 "register_operand" "")           ;; bool output
176   (match_operand:QI 1 "nonautoincmem_operand" "+U")    ;; memory
177   (match_operand:SI 2 "const_int_operand" "")]         ;; model
178  ""
179{
180  rtx addr, aligned_addr, aligned_mem, offset, word, shmt, tmp;
181  rtx result = operands[0];
182  rtx mem = operands[1];
183  enum memmodel model = (enum memmodel) INTVAL (operands[2]);
184
185  addr = force_reg (Pmode, XEXP (mem, 0));
186
187  aligned_addr = gen_reg_rtx (Pmode);
188  emit_move_insn (aligned_addr, gen_rtx_AND (Pmode, addr, GEN_INT (-8)));
189
190  aligned_mem = change_address (mem, DImode, aligned_addr);
191  set_mem_alias_set (aligned_mem, 0);
192
193  tmp = gen_reg_rtx (Pmode);
194  if (BYTES_BIG_ENDIAN)
195    {
196      emit_move_insn (gen_lowpart (DImode, tmp),
197                      gen_rtx_NOT (DImode, gen_lowpart (DImode, addr)));
198    }
199  else
200    {
201      tmp = addr;
202    }
203
204  offset = gen_reg_rtx (DImode);
205  emit_move_insn (offset, gen_rtx_AND (DImode, gen_lowpart (DImode, tmp),
206                                       GEN_INT (7)));
207
208  tmp = gen_reg_rtx (DImode);
209  emit_move_insn (tmp, GEN_INT (1));
210
211  shmt = gen_reg_rtx (DImode);
212  emit_move_insn (shmt, gen_rtx_ASHIFT (DImode, offset, GEN_INT (3)));
213
214  word = gen_reg_rtx (DImode);
215  emit_move_insn (word, gen_rtx_ASHIFT (DImode, tmp,
216                                        gen_lowpart (SImode, shmt)));
217
218  tmp = gen_reg_rtx (DImode);
219  tilegx_pre_atomic_barrier (model);
220  emit_insn (gen_atomic_fetch_or_baredi (tmp, aligned_mem, word));
221  tilegx_post_atomic_barrier (model);
222
223  emit_move_insn (gen_lowpart (DImode, result),
224                  gen_rtx_LSHIFTRT (DImode, tmp,
225                                    gen_lowpart (SImode, shmt)));
226  DONE;
227})
228