0
|
1 ;; GCC machine description for i386 synchronization instructions.
|
|
2 ;; Copyright (C) 2005, 2006, 2007, 2008, 2009
|
|
3 ;; Free Software Foundation, Inc.
|
|
4 ;;
|
|
5 ;; This file is part of GCC.
|
|
6 ;;
|
|
7 ;; GCC is free software; you can redistribute it and/or modify
|
|
8 ;; it under the terms of the GNU General Public License as published by
|
|
9 ;; the Free Software Foundation; either version 3, or (at your option)
|
|
10 ;; any later version.
|
|
11 ;;
|
|
12 ;; GCC is distributed in the hope that it will be useful,
|
|
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 ;; GNU General Public 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_iterator IMODE [QI HI SI (DI "TARGET_64BIT")])
|
|
22 (define_mode_attr modesuffix [(QI "b") (HI "w") (SI "l") (DI "q")])
|
|
23 (define_mode_attr modeconstraint [(QI "q") (HI "r") (SI "r") (DI "r")])
|
|
24 (define_mode_attr immconstraint [(QI "i") (HI "i") (SI "i") (DI "e")])
|
|
25
|
|
26 (define_mode_iterator CASMODE [QI HI SI (DI "TARGET_64BIT || TARGET_CMPXCHG8B")
|
|
27 (TI "TARGET_64BIT && TARGET_CMPXCHG16B")])
|
|
28 (define_mode_iterator DCASMODE
|
|
29 [(DI "!TARGET_64BIT && TARGET_CMPXCHG8B && !flag_pic")
|
|
30 (TI "TARGET_64BIT && TARGET_CMPXCHG16B")])
|
|
31 (define_mode_attr doublemodesuffix [(DI "8") (TI "16")])
|
|
32 (define_mode_attr DCASHMODE [(DI "SI") (TI "DI")])
|
|
33
|
|
34 (define_expand "memory_barrier"
|
|
35 [(set (match_dup 0)
|
|
36 (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))]
|
|
37 ""
|
|
38 {
|
|
39 operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
|
|
40 MEM_VOLATILE_P (operands[0]) = 1;
|
|
41
|
|
42 if (!(TARGET_64BIT || TARGET_SSE2))
|
|
43 {
|
|
44 emit_insn (gen_memory_barrier_nosse (operands[0]));
|
|
45 DONE;
|
|
46 }
|
|
47 })
|
|
48
|
|
49 (define_insn "memory_barrier_nosse"
|
|
50 [(set (match_operand:BLK 0 "" "")
|
|
51 (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))
|
|
52 (clobber (reg:CC FLAGS_REG))]
|
|
53 "!(TARGET_64BIT || TARGET_SSE2)"
|
|
54 "lock{%;| }or{l}\t{$0, (%%esp)|DWORD PTR [esp], 0}"
|
|
55 [(set_attr "memory" "unknown")])
|
|
56
|
|
57 ;; ??? It would be possible to use cmpxchg8b on pentium for DImode
|
|
58 ;; changes. It's complicated because the insn uses ecx:ebx as the
|
|
59 ;; new value; note that the registers are reversed from the order
|
|
60 ;; that they'd be in with (reg:DI 2 ecx). Similarly for TImode
|
|
61 ;; data in 64-bit mode.
|
|
62
|
|
63 (define_expand "sync_compare_and_swap<mode>"
|
|
64 [(parallel
|
|
65 [(set (match_operand:CASMODE 0 "register_operand" "")
|
|
66 (match_operand:CASMODE 1 "memory_operand" ""))
|
|
67 (set (match_dup 1)
|
|
68 (unspec_volatile:CASMODE
|
|
69 [(match_dup 1)
|
|
70 (match_operand:CASMODE 2 "register_operand" "")
|
|
71 (match_operand:CASMODE 3 "register_operand" "")]
|
|
72 UNSPECV_CMPXCHG))
|
|
73 (clobber (reg:CC FLAGS_REG))])]
|
|
74 "TARGET_CMPXCHG"
|
|
75 {
|
|
76 if ((<MODE>mode == DImode && !TARGET_64BIT) || <MODE>mode == TImode)
|
|
77 {
|
|
78 enum machine_mode hmode = <MODE>mode == DImode ? SImode : DImode;
|
|
79 rtx low = simplify_gen_subreg (hmode, operands[3], <MODE>mode, 0);
|
|
80 rtx high = simplify_gen_subreg (hmode, operands[3], <MODE>mode,
|
|
81 GET_MODE_SIZE (hmode));
|
|
82 low = force_reg (hmode, low);
|
|
83 high = force_reg (hmode, high);
|
|
84 if (<MODE>mode == DImode)
|
|
85 {
|
|
86 if (flag_pic && !cmpxchg8b_pic_memory_operand (operands[1], DImode))
|
|
87 operands[1] = replace_equiv_address (operands[1],
|
|
88 force_reg (Pmode,
|
|
89 XEXP (operands[1],
|
|
90 0)));
|
|
91 emit_insn (gen_sync_double_compare_and_swapdi
|
|
92 (operands[0], operands[1], operands[2], low, high));
|
|
93 }
|
|
94 else if (<MODE>mode == TImode)
|
|
95 emit_insn (gen_sync_double_compare_and_swapti
|
|
96 (operands[0], operands[1], operands[2], low, high));
|
|
97 else
|
|
98 gcc_unreachable ();
|
|
99 DONE;
|
|
100 }
|
|
101 })
|
|
102
|
|
103 (define_insn "*sync_compare_and_swap<mode>"
|
|
104 [(set (match_operand:IMODE 0 "register_operand" "=a")
|
|
105 (match_operand:IMODE 1 "memory_operand" "+m"))
|
|
106 (set (match_dup 1)
|
|
107 (unspec_volatile:IMODE
|
|
108 [(match_dup 1)
|
|
109 (match_operand:IMODE 2 "register_operand" "a")
|
|
110 (match_operand:IMODE 3 "register_operand" "<modeconstraint>")]
|
|
111 UNSPECV_CMPXCHG))
|
|
112 (clobber (reg:CC FLAGS_REG))]
|
|
113 "TARGET_CMPXCHG"
|
|
114 "lock{%;| }cmpxchg{<modesuffix>}\t{%3, %1|%1, %3}")
|
|
115
|
|
116 (define_insn "sync_double_compare_and_swap<mode>"
|
|
117 [(set (match_operand:DCASMODE 0 "register_operand" "=A")
|
|
118 (match_operand:DCASMODE 1 "memory_operand" "+m"))
|
|
119 (set (match_dup 1)
|
|
120 (unspec_volatile:DCASMODE
|
|
121 [(match_dup 1)
|
|
122 (match_operand:DCASMODE 2 "register_operand" "A")
|
|
123 (match_operand:<DCASHMODE> 3 "register_operand" "b")
|
|
124 (match_operand:<DCASHMODE> 4 "register_operand" "c")]
|
|
125 UNSPECV_CMPXCHG))
|
|
126 (clobber (reg:CC FLAGS_REG))]
|
|
127 ""
|
|
128 "lock{%;| }cmpxchg<doublemodesuffix>b\t%1")
|
|
129
|
|
130 ;; Theoretically we'd like to use constraint "r" (any reg) for operand
|
|
131 ;; 3, but that includes ecx. If operand 3 and 4 are the same (like when
|
|
132 ;; the input is -1LL) GCC might chose to allocate operand 3 to ecx, like
|
|
133 ;; operand 4. This breaks, as the xchg will move the PIC register contents
|
|
134 ;; to %ecx then --> boom. Operands 3 and 4 really need to be different
|
|
135 ;; registers, which in this case means operand 3 must not be ecx.
|
|
136 ;; Instead of playing tricks with fake early clobbers or the like we
|
|
137 ;; just enumerate all regs possible here, which (as this is !TARGET_64BIT)
|
|
138 ;; are just esi and edi.
|
|
139 (define_insn "*sync_double_compare_and_swapdi_pic"
|
|
140 [(set (match_operand:DI 0 "register_operand" "=A")
|
|
141 (match_operand:DI 1 "cmpxchg8b_pic_memory_operand" "+m"))
|
|
142 (set (match_dup 1)
|
|
143 (unspec_volatile:DI
|
|
144 [(match_dup 1)
|
|
145 (match_operand:DI 2 "register_operand" "A")
|
|
146 (match_operand:SI 3 "register_operand" "SD")
|
|
147 (match_operand:SI 4 "register_operand" "c")]
|
|
148 UNSPECV_CMPXCHG))
|
|
149 (clobber (reg:CC FLAGS_REG))]
|
|
150 "!TARGET_64BIT && TARGET_CMPXCHG8B && flag_pic"
|
|
151 "xchg{l}\t%%ebx, %3\;lock{%;| }cmpxchg8b\t%1\;xchg{l}\t%%ebx, %3")
|
|
152
|
|
153 (define_expand "sync_compare_and_swap_cc<mode>"
|
|
154 [(parallel
|
|
155 [(set (match_operand:CASMODE 0 "register_operand" "")
|
|
156 (match_operand:CASMODE 1 "memory_operand" ""))
|
|
157 (set (match_dup 1)
|
|
158 (unspec_volatile:CASMODE
|
|
159 [(match_dup 1)
|
|
160 (match_operand:CASMODE 2 "register_operand" "")
|
|
161 (match_operand:CASMODE 3 "register_operand" "")]
|
|
162 UNSPECV_CMPXCHG))
|
|
163 (set (match_dup 4)
|
|
164 (compare:CCZ
|
|
165 (unspec_volatile:CASMODE
|
|
166 [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG)
|
|
167 (match_dup 2)))])]
|
|
168 "TARGET_CMPXCHG"
|
|
169 {
|
|
170 operands[4] = gen_rtx_REG (CCZmode, FLAGS_REG);
|
|
171 ix86_compare_op0 = operands[3];
|
|
172 ix86_compare_op1 = NULL;
|
|
173 ix86_compare_emitted = operands[4];
|
|
174 if ((<MODE>mode == DImode && !TARGET_64BIT) || <MODE>mode == TImode)
|
|
175 {
|
|
176 enum machine_mode hmode = <MODE>mode == DImode ? SImode : DImode;
|
|
177 rtx low = simplify_gen_subreg (hmode, operands[3], <MODE>mode, 0);
|
|
178 rtx high = simplify_gen_subreg (hmode, operands[3], <MODE>mode,
|
|
179 GET_MODE_SIZE (hmode));
|
|
180 low = force_reg (hmode, low);
|
|
181 high = force_reg (hmode, high);
|
|
182 if (<MODE>mode == DImode)
|
|
183 {
|
|
184 if (flag_pic && !cmpxchg8b_pic_memory_operand (operands[1], DImode))
|
|
185 operands[1] = replace_equiv_address (operands[1],
|
|
186 force_reg (Pmode,
|
|
187 XEXP (operands[1],
|
|
188 0)));
|
|
189 emit_insn (gen_sync_double_compare_and_swap_ccdi
|
|
190 (operands[0], operands[1], operands[2], low, high));
|
|
191 }
|
|
192 else if (<MODE>mode == TImode)
|
|
193 emit_insn (gen_sync_double_compare_and_swap_ccti
|
|
194 (operands[0], operands[1], operands[2], low, high));
|
|
195 else
|
|
196 gcc_unreachable ();
|
|
197 DONE;
|
|
198 }
|
|
199 })
|
|
200
|
|
201 (define_insn "*sync_compare_and_swap_cc<mode>"
|
|
202 [(set (match_operand:IMODE 0 "register_operand" "=a")
|
|
203 (match_operand:IMODE 1 "memory_operand" "+m"))
|
|
204 (set (match_dup 1)
|
|
205 (unspec_volatile:IMODE
|
|
206 [(match_dup 1)
|
|
207 (match_operand:IMODE 2 "register_operand" "a")
|
|
208 (match_operand:IMODE 3 "register_operand" "<modeconstraint>")]
|
|
209 UNSPECV_CMPXCHG))
|
|
210 (set (reg:CCZ FLAGS_REG)
|
|
211 (compare:CCZ
|
|
212 (unspec_volatile:IMODE
|
|
213 [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG)
|
|
214 (match_dup 2)))]
|
|
215 "TARGET_CMPXCHG"
|
|
216 "lock{%;| }cmpxchg{<modesuffix>}\t{%3, %1|%1, %3}")
|
|
217
|
|
218 (define_insn "sync_double_compare_and_swap_cc<mode>"
|
|
219 [(set (match_operand:DCASMODE 0 "register_operand" "=A")
|
|
220 (match_operand:DCASMODE 1 "memory_operand" "+m"))
|
|
221 (set (match_dup 1)
|
|
222 (unspec_volatile:DCASMODE
|
|
223 [(match_dup 1)
|
|
224 (match_operand:DCASMODE 2 "register_operand" "A")
|
|
225 (match_operand:<DCASHMODE> 3 "register_operand" "b")
|
|
226 (match_operand:<DCASHMODE> 4 "register_operand" "c")]
|
|
227 UNSPECV_CMPXCHG))
|
|
228 (set (reg:CCZ FLAGS_REG)
|
|
229 (compare:CCZ
|
|
230 (unspec_volatile:DCASMODE
|
|
231 [(match_dup 1) (match_dup 2) (match_dup 3) (match_dup 4)]
|
|
232 UNSPECV_CMPXCHG)
|
|
233 (match_dup 2)))]
|
|
234 ""
|
|
235 "lock{%;| }cmpxchg<doublemodesuffix>b\t%1")
|
|
236
|
|
237 ;; See above for the explanation of using the constraint "SD" for
|
|
238 ;; operand 3.
|
|
239 (define_insn "*sync_double_compare_and_swap_ccdi_pic"
|
|
240 [(set (match_operand:DI 0 "register_operand" "=A")
|
|
241 (match_operand:DI 1 "cmpxchg8b_pic_memory_operand" "+m"))
|
|
242 (set (match_dup 1)
|
|
243 (unspec_volatile:DI
|
|
244 [(match_dup 1)
|
|
245 (match_operand:DI 2 "register_operand" "A")
|
|
246 (match_operand:SI 3 "register_operand" "SD")
|
|
247 (match_operand:SI 4 "register_operand" "c")]
|
|
248 UNSPECV_CMPXCHG))
|
|
249 (set (reg:CCZ FLAGS_REG)
|
|
250 (compare:CCZ
|
|
251 (unspec_volatile:DI
|
|
252 [(match_dup 1) (match_dup 2) (match_dup 3) (match_dup 4)]
|
|
253 UNSPECV_CMPXCHG)
|
|
254 (match_dup 2)))]
|
|
255 "!TARGET_64BIT && TARGET_CMPXCHG8B && flag_pic"
|
|
256 "xchg{l}\t%%ebx, %3\;lock{%;| }cmpxchg8b\t%1\;xchg{l}\t%%ebx, %3")
|
|
257
|
|
258 (define_insn "sync_old_add<mode>"
|
|
259 [(set (match_operand:IMODE 0 "register_operand" "=<modeconstraint>")
|
|
260 (unspec_volatile:IMODE
|
|
261 [(match_operand:IMODE 1 "memory_operand" "+m")] UNSPECV_XCHG))
|
|
262 (set (match_dup 1)
|
|
263 (plus:IMODE (match_dup 1)
|
|
264 (match_operand:IMODE 2 "register_operand" "0")))
|
|
265 (clobber (reg:CC FLAGS_REG))]
|
|
266 "TARGET_XADD"
|
|
267 "lock{%;| }xadd{<modesuffix>}\t{%0, %1|%1, %0}")
|
|
268
|
|
269 ;; Recall that xchg implicitly sets LOCK#, so adding it again wastes space.
|
|
270 (define_insn "sync_lock_test_and_set<mode>"
|
|
271 [(set (match_operand:IMODE 0 "register_operand" "=<modeconstraint>")
|
|
272 (unspec_volatile:IMODE
|
|
273 [(match_operand:IMODE 1 "memory_operand" "+m")] UNSPECV_XCHG))
|
|
274 (set (match_dup 1)
|
|
275 (match_operand:IMODE 2 "register_operand" "0"))]
|
|
276 ""
|
|
277 "xchg{<modesuffix>}\t{%1, %0|%0, %1}")
|
|
278
|
|
279 (define_insn "sync_add<mode>"
|
|
280 [(set (match_operand:IMODE 0 "memory_operand" "+m")
|
|
281 (unspec_volatile:IMODE
|
|
282 [(plus:IMODE (match_dup 0)
|
|
283 (match_operand:IMODE 1 "nonmemory_operand" "<modeconstraint><immconstraint>"))]
|
|
284 UNSPECV_LOCK))
|
|
285 (clobber (reg:CC FLAGS_REG))]
|
|
286 ""
|
|
287 {
|
|
288 if (TARGET_USE_INCDEC)
|
|
289 {
|
|
290 if (operands[1] == const1_rtx)
|
|
291 return "lock{%;| }inc{<modesuffix>}\t%0";
|
|
292 if (operands[1] == constm1_rtx)
|
|
293 return "lock{%;| }dec{<modesuffix>}\t%0";
|
|
294 }
|
|
295
|
|
296 return "lock{%;| }add{<modesuffix>}\t{%1, %0|%0, %1}";
|
|
297 })
|
|
298
|
|
299 (define_insn "sync_sub<mode>"
|
|
300 [(set (match_operand:IMODE 0 "memory_operand" "+m")
|
|
301 (unspec_volatile:IMODE
|
|
302 [(minus:IMODE (match_dup 0)
|
|
303 (match_operand:IMODE 1 "nonmemory_operand" "<modeconstraint><immconstraint>"))]
|
|
304 UNSPECV_LOCK))
|
|
305 (clobber (reg:CC FLAGS_REG))]
|
|
306 ""
|
|
307 {
|
|
308 if (TARGET_USE_INCDEC)
|
|
309 {
|
|
310 if (operands[1] == const1_rtx)
|
|
311 return "lock{%;| }dec{<modesuffix>}\t%0";
|
|
312 if (operands[1] == constm1_rtx)
|
|
313 return "lock{%;| }inc{<modesuffix>}\t%0";
|
|
314 }
|
|
315
|
|
316 return "lock{%;| }sub{<modesuffix>}\t{%1, %0|%0, %1}";
|
|
317 })
|
|
318
|
|
319 (define_insn "sync_ior<mode>"
|
|
320 [(set (match_operand:IMODE 0 "memory_operand" "+m")
|
|
321 (unspec_volatile:IMODE
|
|
322 [(ior:IMODE (match_dup 0)
|
|
323 (match_operand:IMODE 1 "nonmemory_operand" "<modeconstraint><immconstraint>"))]
|
|
324 UNSPECV_LOCK))
|
|
325 (clobber (reg:CC FLAGS_REG))]
|
|
326 ""
|
|
327 "lock{%;| }or{<modesuffix>}\t{%1, %0|%0, %1}")
|
|
328
|
|
329 (define_insn "sync_and<mode>"
|
|
330 [(set (match_operand:IMODE 0 "memory_operand" "+m")
|
|
331 (unspec_volatile:IMODE
|
|
332 [(and:IMODE (match_dup 0)
|
|
333 (match_operand:IMODE 1 "nonmemory_operand" "<modeconstraint><immconstraint>"))]
|
|
334 UNSPECV_LOCK))
|
|
335 (clobber (reg:CC FLAGS_REG))]
|
|
336 ""
|
|
337 "lock{%;| }and{<modesuffix>}\t{%1, %0|%0, %1}")
|
|
338
|
|
339 (define_insn "sync_xor<mode>"
|
|
340 [(set (match_operand:IMODE 0 "memory_operand" "+m")
|
|
341 (unspec_volatile:IMODE
|
|
342 [(xor:IMODE (match_dup 0)
|
|
343 (match_operand:IMODE 1 "nonmemory_operand" "<modeconstraint><immconstraint>"))]
|
|
344 UNSPECV_LOCK))
|
|
345 (clobber (reg:CC FLAGS_REG))]
|
|
346 ""
|
|
347 "lock{%;| }xor{<modesuffix>}\t{%1, %0|%0, %1}")
|