| 1 | ;; GCC machine description for i386 synchronization instructions. |
| 2 | ;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 |
| 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}") |