WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / rseq / rseq-x86.h
blob640411518e4664aae241d9abdc2ea277470c2789
1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3 * rseq-x86.h
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 */
8 #include <stdint.h>
11 * RSEQ_SIG is used with the following reserved undefined instructions, which
12 * trap in user-space:
14 * x86-32: 0f b9 3d 53 30 05 53 ud1 0x53053053,%edi
15 * x86-64: 0f b9 3d 53 30 05 53 ud1 0x53053053(%rip),%edi
17 #define RSEQ_SIG 0x53053053
20 * Due to a compiler optimization bug in gcc-8 with asm goto and TLS asm input
21 * operands, we cannot use "m" input operands, and rather pass the __rseq_abi
22 * address through a "r" input operand.
25 /* Offset of cpu_id and rseq_cs fields in struct rseq. */
26 #define RSEQ_CPU_ID_OFFSET 4
27 #define RSEQ_CS_OFFSET 8
29 #ifdef __x86_64__
31 #define rseq_smp_mb() \
32 __asm__ __volatile__ ("lock; addl $0,-128(%%rsp)" ::: "memory", "cc")
33 #define rseq_smp_rmb() rseq_barrier()
34 #define rseq_smp_wmb() rseq_barrier()
36 #define rseq_smp_load_acquire(p) \
37 __extension__ ({ \
38 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
39 rseq_barrier(); \
40 ____p1; \
43 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
45 #define rseq_smp_store_release(p, v) \
46 do { \
47 rseq_barrier(); \
48 RSEQ_WRITE_ONCE(*p, v); \
49 } while (0)
51 #ifdef RSEQ_SKIP_FASTPATH
52 #include "rseq-skip.h"
53 #else /* !RSEQ_SKIP_FASTPATH */
55 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
56 start_ip, post_commit_offset, abort_ip) \
57 ".pushsection __rseq_cs, \"aw\"\n\t" \
58 ".balign 32\n\t" \
59 __rseq_str(label) ":\n\t" \
60 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
61 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
62 ".popsection\n\t" \
63 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
64 ".quad " __rseq_str(label) "b\n\t" \
65 ".popsection\n\t"
68 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
69 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
70 (post_commit_ip - start_ip), abort_ip)
73 * Exit points of a rseq critical section consist of all instructions outside
74 * of the critical section where a critical section can either branch to or
75 * reach through the normal course of its execution. The abort IP and the
76 * post-commit IP are already part of the __rseq_cs section and should not be
77 * explicitly defined as additional exit points. Knowing all exit points is
78 * useful to assist debuggers stepping over the critical section.
80 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
81 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
82 ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
83 ".popsection\n\t"
85 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
86 RSEQ_INJECT_ASM(1) \
87 "leaq " __rseq_str(cs_label) "(%%rip), %%rax\n\t" \
88 "movq %%rax, " __rseq_str(rseq_cs) "\n\t" \
89 __rseq_str(label) ":\n\t"
91 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
92 RSEQ_INJECT_ASM(2) \
93 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \
94 "jnz " __rseq_str(label) "\n\t"
96 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
97 ".pushsection __rseq_failure, \"ax\"\n\t" \
98 /* Disassembler-friendly signature: ud1 <sig>(%rip),%edi. */ \
99 ".byte 0x0f, 0xb9, 0x3d\n\t" \
100 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
101 __rseq_str(label) ":\n\t" \
102 teardown \
103 "jmp %l[" __rseq_str(abort_label) "]\n\t" \
104 ".popsection\n\t"
106 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
107 ".pushsection __rseq_failure, \"ax\"\n\t" \
108 __rseq_str(label) ":\n\t" \
109 teardown \
110 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \
111 ".popsection\n\t"
113 static inline __attribute__((always_inline))
114 int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
116 RSEQ_INJECT_C(9)
118 __asm__ __volatile__ goto (
119 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
120 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
121 #ifdef RSEQ_COMPARE_TWICE
122 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
123 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
124 #endif
125 /* Start rseq by storing table entry pointer into rseq_cs. */
126 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
127 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
128 RSEQ_INJECT_ASM(3)
129 "cmpq %[v], %[expect]\n\t"
130 "jnz %l[cmpfail]\n\t"
131 RSEQ_INJECT_ASM(4)
132 #ifdef RSEQ_COMPARE_TWICE
133 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
134 "cmpq %[v], %[expect]\n\t"
135 "jnz %l[error2]\n\t"
136 #endif
137 /* final store */
138 "movq %[newv], %[v]\n\t"
139 "2:\n\t"
140 RSEQ_INJECT_ASM(5)
141 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
142 : /* gcc asm goto does not allow outputs */
143 : [cpu_id] "r" (cpu),
144 [rseq_abi] "r" (&__rseq_abi),
145 [v] "m" (*v),
146 [expect] "r" (expect),
147 [newv] "r" (newv)
148 : "memory", "cc", "rax"
149 RSEQ_INJECT_CLOBBER
150 : abort, cmpfail
151 #ifdef RSEQ_COMPARE_TWICE
152 , error1, error2
153 #endif
155 return 0;
156 abort:
157 RSEQ_INJECT_FAILED
158 return -1;
159 cmpfail:
160 return 1;
161 #ifdef RSEQ_COMPARE_TWICE
162 error1:
163 rseq_bug("cpu_id comparison failed");
164 error2:
165 rseq_bug("expected value comparison failed");
166 #endif
170 * Compare @v against @expectnot. When it does _not_ match, load @v
171 * into @load, and store the content of *@v + voffp into @v.
173 static inline __attribute__((always_inline))
174 int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
175 off_t voffp, intptr_t *load, int cpu)
177 RSEQ_INJECT_C(9)
179 __asm__ __volatile__ goto (
180 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
181 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
182 #ifdef RSEQ_COMPARE_TWICE
183 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
184 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
185 #endif
186 /* Start rseq by storing table entry pointer into rseq_cs. */
187 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
188 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
189 RSEQ_INJECT_ASM(3)
190 "movq %[v], %%rbx\n\t"
191 "cmpq %%rbx, %[expectnot]\n\t"
192 "je %l[cmpfail]\n\t"
193 RSEQ_INJECT_ASM(4)
194 #ifdef RSEQ_COMPARE_TWICE
195 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
196 "movq %[v], %%rbx\n\t"
197 "cmpq %%rbx, %[expectnot]\n\t"
198 "je %l[error2]\n\t"
199 #endif
200 "movq %%rbx, %[load]\n\t"
201 "addq %[voffp], %%rbx\n\t"
202 "movq (%%rbx), %%rbx\n\t"
203 /* final store */
204 "movq %%rbx, %[v]\n\t"
205 "2:\n\t"
206 RSEQ_INJECT_ASM(5)
207 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
208 : /* gcc asm goto does not allow outputs */
209 : [cpu_id] "r" (cpu),
210 [rseq_abi] "r" (&__rseq_abi),
211 /* final store input */
212 [v] "m" (*v),
213 [expectnot] "r" (expectnot),
214 [voffp] "er" (voffp),
215 [load] "m" (*load)
216 : "memory", "cc", "rax", "rbx"
217 RSEQ_INJECT_CLOBBER
218 : abort, cmpfail
219 #ifdef RSEQ_COMPARE_TWICE
220 , error1, error2
221 #endif
223 return 0;
224 abort:
225 RSEQ_INJECT_FAILED
226 return -1;
227 cmpfail:
228 return 1;
229 #ifdef RSEQ_COMPARE_TWICE
230 error1:
231 rseq_bug("cpu_id comparison failed");
232 error2:
233 rseq_bug("expected value comparison failed");
234 #endif
237 static inline __attribute__((always_inline))
238 int rseq_addv(intptr_t *v, intptr_t count, int cpu)
240 RSEQ_INJECT_C(9)
242 __asm__ __volatile__ goto (
243 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
244 #ifdef RSEQ_COMPARE_TWICE
245 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
246 #endif
247 /* Start rseq by storing table entry pointer into rseq_cs. */
248 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
249 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
250 RSEQ_INJECT_ASM(3)
251 #ifdef RSEQ_COMPARE_TWICE
252 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
253 #endif
254 /* final store */
255 "addq %[count], %[v]\n\t"
256 "2:\n\t"
257 RSEQ_INJECT_ASM(4)
258 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
259 : /* gcc asm goto does not allow outputs */
260 : [cpu_id] "r" (cpu),
261 [rseq_abi] "r" (&__rseq_abi),
262 /* final store input */
263 [v] "m" (*v),
264 [count] "er" (count)
265 : "memory", "cc", "rax"
266 RSEQ_INJECT_CLOBBER
267 : abort
268 #ifdef RSEQ_COMPARE_TWICE
269 , error1
270 #endif
272 return 0;
273 abort:
274 RSEQ_INJECT_FAILED
275 return -1;
276 #ifdef RSEQ_COMPARE_TWICE
277 error1:
278 rseq_bug("cpu_id comparison failed");
279 #endif
282 #define RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
285 * pval = *(ptr+off)
286 * *pval += inc;
288 static inline __attribute__((always_inline))
289 int rseq_offset_deref_addv(intptr_t *ptr, off_t off, intptr_t inc, int cpu)
291 RSEQ_INJECT_C(9)
293 __asm__ __volatile__ goto (
294 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
295 #ifdef RSEQ_COMPARE_TWICE
296 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
297 #endif
298 /* Start rseq by storing table entry pointer into rseq_cs. */
299 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
300 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
301 RSEQ_INJECT_ASM(3)
302 #ifdef RSEQ_COMPARE_TWICE
303 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
304 #endif
305 /* get p+v */
306 "movq %[ptr], %%rbx\n\t"
307 "addq %[off], %%rbx\n\t"
308 /* get pv */
309 "movq (%%rbx), %%rcx\n\t"
310 /* *pv += inc */
311 "addq %[inc], (%%rcx)\n\t"
312 "2:\n\t"
313 RSEQ_INJECT_ASM(4)
314 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
315 : /* gcc asm goto does not allow outputs */
316 : [cpu_id] "r" (cpu),
317 [rseq_abi] "r" (&__rseq_abi),
318 /* final store input */
319 [ptr] "m" (*ptr),
320 [off] "er" (off),
321 [inc] "er" (inc)
322 : "memory", "cc", "rax", "rbx", "rcx"
323 RSEQ_INJECT_CLOBBER
324 : abort
325 #ifdef RSEQ_COMPARE_TWICE
326 , error1
327 #endif
329 return 0;
330 abort:
331 RSEQ_INJECT_FAILED
332 return -1;
333 #ifdef RSEQ_COMPARE_TWICE
334 error1:
335 rseq_bug("cpu_id comparison failed");
336 #endif
339 static inline __attribute__((always_inline))
340 int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
341 intptr_t *v2, intptr_t newv2,
342 intptr_t newv, int cpu)
344 RSEQ_INJECT_C(9)
346 __asm__ __volatile__ goto (
347 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
348 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
349 #ifdef RSEQ_COMPARE_TWICE
350 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
351 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
352 #endif
353 /* Start rseq by storing table entry pointer into rseq_cs. */
354 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
355 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
356 RSEQ_INJECT_ASM(3)
357 "cmpq %[v], %[expect]\n\t"
358 "jnz %l[cmpfail]\n\t"
359 RSEQ_INJECT_ASM(4)
360 #ifdef RSEQ_COMPARE_TWICE
361 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
362 "cmpq %[v], %[expect]\n\t"
363 "jnz %l[error2]\n\t"
364 #endif
365 /* try store */
366 "movq %[newv2], %[v2]\n\t"
367 RSEQ_INJECT_ASM(5)
368 /* final store */
369 "movq %[newv], %[v]\n\t"
370 "2:\n\t"
371 RSEQ_INJECT_ASM(6)
372 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
373 : /* gcc asm goto does not allow outputs */
374 : [cpu_id] "r" (cpu),
375 [rseq_abi] "r" (&__rseq_abi),
376 /* try store input */
377 [v2] "m" (*v2),
378 [newv2] "r" (newv2),
379 /* final store input */
380 [v] "m" (*v),
381 [expect] "r" (expect),
382 [newv] "r" (newv)
383 : "memory", "cc", "rax"
384 RSEQ_INJECT_CLOBBER
385 : abort, cmpfail
386 #ifdef RSEQ_COMPARE_TWICE
387 , error1, error2
388 #endif
390 return 0;
391 abort:
392 RSEQ_INJECT_FAILED
393 return -1;
394 cmpfail:
395 return 1;
396 #ifdef RSEQ_COMPARE_TWICE
397 error1:
398 rseq_bug("cpu_id comparison failed");
399 error2:
400 rseq_bug("expected value comparison failed");
401 #endif
404 /* x86-64 is TSO. */
405 static inline __attribute__((always_inline))
406 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
407 intptr_t *v2, intptr_t newv2,
408 intptr_t newv, int cpu)
410 return rseq_cmpeqv_trystorev_storev(v, expect, v2, newv2, newv, cpu);
413 static inline __attribute__((always_inline))
414 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
415 intptr_t *v2, intptr_t expect2,
416 intptr_t newv, int cpu)
418 RSEQ_INJECT_C(9)
420 __asm__ __volatile__ goto (
421 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
422 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
423 #ifdef RSEQ_COMPARE_TWICE
424 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
425 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
426 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
427 #endif
428 /* Start rseq by storing table entry pointer into rseq_cs. */
429 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
430 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
431 RSEQ_INJECT_ASM(3)
432 "cmpq %[v], %[expect]\n\t"
433 "jnz %l[cmpfail]\n\t"
434 RSEQ_INJECT_ASM(4)
435 "cmpq %[v2], %[expect2]\n\t"
436 "jnz %l[cmpfail]\n\t"
437 RSEQ_INJECT_ASM(5)
438 #ifdef RSEQ_COMPARE_TWICE
439 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
440 "cmpq %[v], %[expect]\n\t"
441 "jnz %l[error2]\n\t"
442 "cmpq %[v2], %[expect2]\n\t"
443 "jnz %l[error3]\n\t"
444 #endif
445 /* final store */
446 "movq %[newv], %[v]\n\t"
447 "2:\n\t"
448 RSEQ_INJECT_ASM(6)
449 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
450 : /* gcc asm goto does not allow outputs */
451 : [cpu_id] "r" (cpu),
452 [rseq_abi] "r" (&__rseq_abi),
453 /* cmp2 input */
454 [v2] "m" (*v2),
455 [expect2] "r" (expect2),
456 /* final store input */
457 [v] "m" (*v),
458 [expect] "r" (expect),
459 [newv] "r" (newv)
460 : "memory", "cc", "rax"
461 RSEQ_INJECT_CLOBBER
462 : abort, cmpfail
463 #ifdef RSEQ_COMPARE_TWICE
464 , error1, error2, error3
465 #endif
467 return 0;
468 abort:
469 RSEQ_INJECT_FAILED
470 return -1;
471 cmpfail:
472 return 1;
473 #ifdef RSEQ_COMPARE_TWICE
474 error1:
475 rseq_bug("cpu_id comparison failed");
476 error2:
477 rseq_bug("1st expected value comparison failed");
478 error3:
479 rseq_bug("2nd expected value comparison failed");
480 #endif
483 static inline __attribute__((always_inline))
484 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
485 void *dst, void *src, size_t len,
486 intptr_t newv, int cpu)
488 uint64_t rseq_scratch[3];
490 RSEQ_INJECT_C(9)
492 __asm__ __volatile__ goto (
493 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
494 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
495 #ifdef RSEQ_COMPARE_TWICE
496 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
497 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
498 #endif
499 "movq %[src], %[rseq_scratch0]\n\t"
500 "movq %[dst], %[rseq_scratch1]\n\t"
501 "movq %[len], %[rseq_scratch2]\n\t"
502 /* Start rseq by storing table entry pointer into rseq_cs. */
503 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
504 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
505 RSEQ_INJECT_ASM(3)
506 "cmpq %[v], %[expect]\n\t"
507 "jnz 5f\n\t"
508 RSEQ_INJECT_ASM(4)
509 #ifdef RSEQ_COMPARE_TWICE
510 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f)
511 "cmpq %[v], %[expect]\n\t"
512 "jnz 7f\n\t"
513 #endif
514 /* try memcpy */
515 "test %[len], %[len]\n\t" \
516 "jz 333f\n\t" \
517 "222:\n\t" \
518 "movb (%[src]), %%al\n\t" \
519 "movb %%al, (%[dst])\n\t" \
520 "inc %[src]\n\t" \
521 "inc %[dst]\n\t" \
522 "dec %[len]\n\t" \
523 "jnz 222b\n\t" \
524 "333:\n\t" \
525 RSEQ_INJECT_ASM(5)
526 /* final store */
527 "movq %[newv], %[v]\n\t"
528 "2:\n\t"
529 RSEQ_INJECT_ASM(6)
530 /* teardown */
531 "movq %[rseq_scratch2], %[len]\n\t"
532 "movq %[rseq_scratch1], %[dst]\n\t"
533 "movq %[rseq_scratch0], %[src]\n\t"
534 RSEQ_ASM_DEFINE_ABORT(4,
535 "movq %[rseq_scratch2], %[len]\n\t"
536 "movq %[rseq_scratch1], %[dst]\n\t"
537 "movq %[rseq_scratch0], %[src]\n\t",
538 abort)
539 RSEQ_ASM_DEFINE_CMPFAIL(5,
540 "movq %[rseq_scratch2], %[len]\n\t"
541 "movq %[rseq_scratch1], %[dst]\n\t"
542 "movq %[rseq_scratch0], %[src]\n\t",
543 cmpfail)
544 #ifdef RSEQ_COMPARE_TWICE
545 RSEQ_ASM_DEFINE_CMPFAIL(6,
546 "movq %[rseq_scratch2], %[len]\n\t"
547 "movq %[rseq_scratch1], %[dst]\n\t"
548 "movq %[rseq_scratch0], %[src]\n\t",
549 error1)
550 RSEQ_ASM_DEFINE_CMPFAIL(7,
551 "movq %[rseq_scratch2], %[len]\n\t"
552 "movq %[rseq_scratch1], %[dst]\n\t"
553 "movq %[rseq_scratch0], %[src]\n\t",
554 error2)
555 #endif
556 : /* gcc asm goto does not allow outputs */
557 : [cpu_id] "r" (cpu),
558 [rseq_abi] "r" (&__rseq_abi),
559 /* final store input */
560 [v] "m" (*v),
561 [expect] "r" (expect),
562 [newv] "r" (newv),
563 /* try memcpy input */
564 [dst] "r" (dst),
565 [src] "r" (src),
566 [len] "r" (len),
567 [rseq_scratch0] "m" (rseq_scratch[0]),
568 [rseq_scratch1] "m" (rseq_scratch[1]),
569 [rseq_scratch2] "m" (rseq_scratch[2])
570 : "memory", "cc", "rax"
571 RSEQ_INJECT_CLOBBER
572 : abort, cmpfail
573 #ifdef RSEQ_COMPARE_TWICE
574 , error1, error2
575 #endif
577 return 0;
578 abort:
579 RSEQ_INJECT_FAILED
580 return -1;
581 cmpfail:
582 return 1;
583 #ifdef RSEQ_COMPARE_TWICE
584 error1:
585 rseq_bug("cpu_id comparison failed");
586 error2:
587 rseq_bug("expected value comparison failed");
588 #endif
591 /* x86-64 is TSO. */
592 static inline __attribute__((always_inline))
593 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
594 void *dst, void *src, size_t len,
595 intptr_t newv, int cpu)
597 return rseq_cmpeqv_trymemcpy_storev(v, expect, dst, src, len,
598 newv, cpu);
601 #endif /* !RSEQ_SKIP_FASTPATH */
603 #elif __i386__
605 #define rseq_smp_mb() \
606 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
607 #define rseq_smp_rmb() \
608 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
609 #define rseq_smp_wmb() \
610 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
612 #define rseq_smp_load_acquire(p) \
613 __extension__ ({ \
614 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
615 rseq_smp_mb(); \
616 ____p1; \
619 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
621 #define rseq_smp_store_release(p, v) \
622 do { \
623 rseq_smp_mb(); \
624 RSEQ_WRITE_ONCE(*p, v); \
625 } while (0)
627 #ifdef RSEQ_SKIP_FASTPATH
628 #include "rseq-skip.h"
629 #else /* !RSEQ_SKIP_FASTPATH */
632 * Use eax as scratch register and take memory operands as input to
633 * lessen register pressure. Especially needed when compiling in O0.
635 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
636 start_ip, post_commit_offset, abort_ip) \
637 ".pushsection __rseq_cs, \"aw\"\n\t" \
638 ".balign 32\n\t" \
639 __rseq_str(label) ":\n\t" \
640 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
641 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
642 ".popsection\n\t" \
643 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
644 ".long " __rseq_str(label) "b, 0x0\n\t" \
645 ".popsection\n\t"
647 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
648 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
649 (post_commit_ip - start_ip), abort_ip)
652 * Exit points of a rseq critical section consist of all instructions outside
653 * of the critical section where a critical section can either branch to or
654 * reach through the normal course of its execution. The abort IP and the
655 * post-commit IP are already part of the __rseq_cs section and should not be
656 * explicitly defined as additional exit points. Knowing all exit points is
657 * useful to assist debuggers stepping over the critical section.
659 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
660 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
661 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) ", 0x0\n\t" \
662 ".popsection\n\t"
664 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
665 RSEQ_INJECT_ASM(1) \
666 "movl $" __rseq_str(cs_label) ", " __rseq_str(rseq_cs) "\n\t" \
667 __rseq_str(label) ":\n\t"
669 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
670 RSEQ_INJECT_ASM(2) \
671 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \
672 "jnz " __rseq_str(label) "\n\t"
674 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
675 ".pushsection __rseq_failure, \"ax\"\n\t" \
676 /* Disassembler-friendly signature: ud1 <sig>,%edi. */ \
677 ".byte 0x0f, 0xb9, 0x3d\n\t" \
678 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
679 __rseq_str(label) ":\n\t" \
680 teardown \
681 "jmp %l[" __rseq_str(abort_label) "]\n\t" \
682 ".popsection\n\t"
684 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
685 ".pushsection __rseq_failure, \"ax\"\n\t" \
686 __rseq_str(label) ":\n\t" \
687 teardown \
688 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \
689 ".popsection\n\t"
691 static inline __attribute__((always_inline))
692 int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
694 RSEQ_INJECT_C(9)
696 __asm__ __volatile__ goto (
697 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
698 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
699 #ifdef RSEQ_COMPARE_TWICE
700 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
701 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
702 #endif
703 /* Start rseq by storing table entry pointer into rseq_cs. */
704 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
705 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
706 RSEQ_INJECT_ASM(3)
707 "cmpl %[v], %[expect]\n\t"
708 "jnz %l[cmpfail]\n\t"
709 RSEQ_INJECT_ASM(4)
710 #ifdef RSEQ_COMPARE_TWICE
711 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
712 "cmpl %[v], %[expect]\n\t"
713 "jnz %l[error2]\n\t"
714 #endif
715 /* final store */
716 "movl %[newv], %[v]\n\t"
717 "2:\n\t"
718 RSEQ_INJECT_ASM(5)
719 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
720 : /* gcc asm goto does not allow outputs */
721 : [cpu_id] "r" (cpu),
722 [rseq_abi] "r" (&__rseq_abi),
723 [v] "m" (*v),
724 [expect] "r" (expect),
725 [newv] "r" (newv)
726 : "memory", "cc", "eax"
727 RSEQ_INJECT_CLOBBER
728 : abort, cmpfail
729 #ifdef RSEQ_COMPARE_TWICE
730 , error1, error2
731 #endif
733 return 0;
734 abort:
735 RSEQ_INJECT_FAILED
736 return -1;
737 cmpfail:
738 return 1;
739 #ifdef RSEQ_COMPARE_TWICE
740 error1:
741 rseq_bug("cpu_id comparison failed");
742 error2:
743 rseq_bug("expected value comparison failed");
744 #endif
748 * Compare @v against @expectnot. When it does _not_ match, load @v
749 * into @load, and store the content of *@v + voffp into @v.
751 static inline __attribute__((always_inline))
752 int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
753 off_t voffp, intptr_t *load, int cpu)
755 RSEQ_INJECT_C(9)
757 __asm__ __volatile__ goto (
758 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
759 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
760 #ifdef RSEQ_COMPARE_TWICE
761 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
762 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
763 #endif
764 /* Start rseq by storing table entry pointer into rseq_cs. */
765 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
766 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
767 RSEQ_INJECT_ASM(3)
768 "movl %[v], %%ebx\n\t"
769 "cmpl %%ebx, %[expectnot]\n\t"
770 "je %l[cmpfail]\n\t"
771 RSEQ_INJECT_ASM(4)
772 #ifdef RSEQ_COMPARE_TWICE
773 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
774 "movl %[v], %%ebx\n\t"
775 "cmpl %%ebx, %[expectnot]\n\t"
776 "je %l[error2]\n\t"
777 #endif
778 "movl %%ebx, %[load]\n\t"
779 "addl %[voffp], %%ebx\n\t"
780 "movl (%%ebx), %%ebx\n\t"
781 /* final store */
782 "movl %%ebx, %[v]\n\t"
783 "2:\n\t"
784 RSEQ_INJECT_ASM(5)
785 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
786 : /* gcc asm goto does not allow outputs */
787 : [cpu_id] "r" (cpu),
788 [rseq_abi] "r" (&__rseq_abi),
789 /* final store input */
790 [v] "m" (*v),
791 [expectnot] "r" (expectnot),
792 [voffp] "ir" (voffp),
793 [load] "m" (*load)
794 : "memory", "cc", "eax", "ebx"
795 RSEQ_INJECT_CLOBBER
796 : abort, cmpfail
797 #ifdef RSEQ_COMPARE_TWICE
798 , error1, error2
799 #endif
801 return 0;
802 abort:
803 RSEQ_INJECT_FAILED
804 return -1;
805 cmpfail:
806 return 1;
807 #ifdef RSEQ_COMPARE_TWICE
808 error1:
809 rseq_bug("cpu_id comparison failed");
810 error2:
811 rseq_bug("expected value comparison failed");
812 #endif
815 static inline __attribute__((always_inline))
816 int rseq_addv(intptr_t *v, intptr_t count, int cpu)
818 RSEQ_INJECT_C(9)
820 __asm__ __volatile__ goto (
821 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
822 #ifdef RSEQ_COMPARE_TWICE
823 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
824 #endif
825 /* Start rseq by storing table entry pointer into rseq_cs. */
826 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
827 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
828 RSEQ_INJECT_ASM(3)
829 #ifdef RSEQ_COMPARE_TWICE
830 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
831 #endif
832 /* final store */
833 "addl %[count], %[v]\n\t"
834 "2:\n\t"
835 RSEQ_INJECT_ASM(4)
836 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
837 : /* gcc asm goto does not allow outputs */
838 : [cpu_id] "r" (cpu),
839 [rseq_abi] "r" (&__rseq_abi),
840 /* final store input */
841 [v] "m" (*v),
842 [count] "ir" (count)
843 : "memory", "cc", "eax"
844 RSEQ_INJECT_CLOBBER
845 : abort
846 #ifdef RSEQ_COMPARE_TWICE
847 , error1
848 #endif
850 return 0;
851 abort:
852 RSEQ_INJECT_FAILED
853 return -1;
854 #ifdef RSEQ_COMPARE_TWICE
855 error1:
856 rseq_bug("cpu_id comparison failed");
857 #endif
860 static inline __attribute__((always_inline))
861 int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
862 intptr_t *v2, intptr_t newv2,
863 intptr_t newv, int cpu)
865 RSEQ_INJECT_C(9)
867 __asm__ __volatile__ goto (
868 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
869 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
870 #ifdef RSEQ_COMPARE_TWICE
871 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
872 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
873 #endif
874 /* Start rseq by storing table entry pointer into rseq_cs. */
875 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
876 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
877 RSEQ_INJECT_ASM(3)
878 "cmpl %[v], %[expect]\n\t"
879 "jnz %l[cmpfail]\n\t"
880 RSEQ_INJECT_ASM(4)
881 #ifdef RSEQ_COMPARE_TWICE
882 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
883 "cmpl %[v], %[expect]\n\t"
884 "jnz %l[error2]\n\t"
885 #endif
886 /* try store */
887 "movl %[newv2], %%eax\n\t"
888 "movl %%eax, %[v2]\n\t"
889 RSEQ_INJECT_ASM(5)
890 /* final store */
891 "movl %[newv], %[v]\n\t"
892 "2:\n\t"
893 RSEQ_INJECT_ASM(6)
894 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
895 : /* gcc asm goto does not allow outputs */
896 : [cpu_id] "r" (cpu),
897 [rseq_abi] "r" (&__rseq_abi),
898 /* try store input */
899 [v2] "m" (*v2),
900 [newv2] "m" (newv2),
901 /* final store input */
902 [v] "m" (*v),
903 [expect] "r" (expect),
904 [newv] "r" (newv)
905 : "memory", "cc", "eax"
906 RSEQ_INJECT_CLOBBER
907 : abort, cmpfail
908 #ifdef RSEQ_COMPARE_TWICE
909 , error1, error2
910 #endif
912 return 0;
913 abort:
914 RSEQ_INJECT_FAILED
915 return -1;
916 cmpfail:
917 return 1;
918 #ifdef RSEQ_COMPARE_TWICE
919 error1:
920 rseq_bug("cpu_id comparison failed");
921 error2:
922 rseq_bug("expected value comparison failed");
923 #endif
926 static inline __attribute__((always_inline))
927 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
928 intptr_t *v2, intptr_t newv2,
929 intptr_t newv, int cpu)
931 RSEQ_INJECT_C(9)
933 __asm__ __volatile__ goto (
934 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
935 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
936 #ifdef RSEQ_COMPARE_TWICE
937 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
938 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
939 #endif
940 /* Start rseq by storing table entry pointer into rseq_cs. */
941 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
942 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
943 RSEQ_INJECT_ASM(3)
944 "movl %[expect], %%eax\n\t"
945 "cmpl %[v], %%eax\n\t"
946 "jnz %l[cmpfail]\n\t"
947 RSEQ_INJECT_ASM(4)
948 #ifdef RSEQ_COMPARE_TWICE
949 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
950 "movl %[expect], %%eax\n\t"
951 "cmpl %[v], %%eax\n\t"
952 "jnz %l[error2]\n\t"
953 #endif
954 /* try store */
955 "movl %[newv2], %[v2]\n\t"
956 RSEQ_INJECT_ASM(5)
957 "lock; addl $0,-128(%%esp)\n\t"
958 /* final store */
959 "movl %[newv], %[v]\n\t"
960 "2:\n\t"
961 RSEQ_INJECT_ASM(6)
962 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
963 : /* gcc asm goto does not allow outputs */
964 : [cpu_id] "r" (cpu),
965 [rseq_abi] "r" (&__rseq_abi),
966 /* try store input */
967 [v2] "m" (*v2),
968 [newv2] "r" (newv2),
969 /* final store input */
970 [v] "m" (*v),
971 [expect] "m" (expect),
972 [newv] "r" (newv)
973 : "memory", "cc", "eax"
974 RSEQ_INJECT_CLOBBER
975 : abort, cmpfail
976 #ifdef RSEQ_COMPARE_TWICE
977 , error1, error2
978 #endif
980 return 0;
981 abort:
982 RSEQ_INJECT_FAILED
983 return -1;
984 cmpfail:
985 return 1;
986 #ifdef RSEQ_COMPARE_TWICE
987 error1:
988 rseq_bug("cpu_id comparison failed");
989 error2:
990 rseq_bug("expected value comparison failed");
991 #endif
995 static inline __attribute__((always_inline))
996 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
997 intptr_t *v2, intptr_t expect2,
998 intptr_t newv, int cpu)
1000 RSEQ_INJECT_C(9)
1002 __asm__ __volatile__ goto (
1003 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
1004 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
1005 #ifdef RSEQ_COMPARE_TWICE
1006 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
1007 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
1008 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
1009 #endif
1010 /* Start rseq by storing table entry pointer into rseq_cs. */
1011 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
1012 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
1013 RSEQ_INJECT_ASM(3)
1014 "cmpl %[v], %[expect]\n\t"
1015 "jnz %l[cmpfail]\n\t"
1016 RSEQ_INJECT_ASM(4)
1017 "cmpl %[expect2], %[v2]\n\t"
1018 "jnz %l[cmpfail]\n\t"
1019 RSEQ_INJECT_ASM(5)
1020 #ifdef RSEQ_COMPARE_TWICE
1021 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1])
1022 "cmpl %[v], %[expect]\n\t"
1023 "jnz %l[error2]\n\t"
1024 "cmpl %[expect2], %[v2]\n\t"
1025 "jnz %l[error3]\n\t"
1026 #endif
1027 "movl %[newv], %%eax\n\t"
1028 /* final store */
1029 "movl %%eax, %[v]\n\t"
1030 "2:\n\t"
1031 RSEQ_INJECT_ASM(6)
1032 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
1033 : /* gcc asm goto does not allow outputs */
1034 : [cpu_id] "r" (cpu),
1035 [rseq_abi] "r" (&__rseq_abi),
1036 /* cmp2 input */
1037 [v2] "m" (*v2),
1038 [expect2] "r" (expect2),
1039 /* final store input */
1040 [v] "m" (*v),
1041 [expect] "r" (expect),
1042 [newv] "m" (newv)
1043 : "memory", "cc", "eax"
1044 RSEQ_INJECT_CLOBBER
1045 : abort, cmpfail
1046 #ifdef RSEQ_COMPARE_TWICE
1047 , error1, error2, error3
1048 #endif
1050 return 0;
1051 abort:
1052 RSEQ_INJECT_FAILED
1053 return -1;
1054 cmpfail:
1055 return 1;
1056 #ifdef RSEQ_COMPARE_TWICE
1057 error1:
1058 rseq_bug("cpu_id comparison failed");
1059 error2:
1060 rseq_bug("1st expected value comparison failed");
1061 error3:
1062 rseq_bug("2nd expected value comparison failed");
1063 #endif
1066 /* TODO: implement a faster memcpy. */
1067 static inline __attribute__((always_inline))
1068 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
1069 void *dst, void *src, size_t len,
1070 intptr_t newv, int cpu)
1072 uint32_t rseq_scratch[3];
1074 RSEQ_INJECT_C(9)
1076 __asm__ __volatile__ goto (
1077 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
1078 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
1079 #ifdef RSEQ_COMPARE_TWICE
1080 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
1081 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
1082 #endif
1083 "movl %[src], %[rseq_scratch0]\n\t"
1084 "movl %[dst], %[rseq_scratch1]\n\t"
1085 "movl %[len], %[rseq_scratch2]\n\t"
1086 /* Start rseq by storing table entry pointer into rseq_cs. */
1087 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
1088 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
1089 RSEQ_INJECT_ASM(3)
1090 "movl %[expect], %%eax\n\t"
1091 "cmpl %%eax, %[v]\n\t"
1092 "jnz 5f\n\t"
1093 RSEQ_INJECT_ASM(4)
1094 #ifdef RSEQ_COMPARE_TWICE
1095 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f)
1096 "movl %[expect], %%eax\n\t"
1097 "cmpl %%eax, %[v]\n\t"
1098 "jnz 7f\n\t"
1099 #endif
1100 /* try memcpy */
1101 "test %[len], %[len]\n\t" \
1102 "jz 333f\n\t" \
1103 "222:\n\t" \
1104 "movb (%[src]), %%al\n\t" \
1105 "movb %%al, (%[dst])\n\t" \
1106 "inc %[src]\n\t" \
1107 "inc %[dst]\n\t" \
1108 "dec %[len]\n\t" \
1109 "jnz 222b\n\t" \
1110 "333:\n\t" \
1111 RSEQ_INJECT_ASM(5)
1112 "movl %[newv], %%eax\n\t"
1113 /* final store */
1114 "movl %%eax, %[v]\n\t"
1115 "2:\n\t"
1116 RSEQ_INJECT_ASM(6)
1117 /* teardown */
1118 "movl %[rseq_scratch2], %[len]\n\t"
1119 "movl %[rseq_scratch1], %[dst]\n\t"
1120 "movl %[rseq_scratch0], %[src]\n\t"
1121 RSEQ_ASM_DEFINE_ABORT(4,
1122 "movl %[rseq_scratch2], %[len]\n\t"
1123 "movl %[rseq_scratch1], %[dst]\n\t"
1124 "movl %[rseq_scratch0], %[src]\n\t",
1125 abort)
1126 RSEQ_ASM_DEFINE_CMPFAIL(5,
1127 "movl %[rseq_scratch2], %[len]\n\t"
1128 "movl %[rseq_scratch1], %[dst]\n\t"
1129 "movl %[rseq_scratch0], %[src]\n\t",
1130 cmpfail)
1131 #ifdef RSEQ_COMPARE_TWICE
1132 RSEQ_ASM_DEFINE_CMPFAIL(6,
1133 "movl %[rseq_scratch2], %[len]\n\t"
1134 "movl %[rseq_scratch1], %[dst]\n\t"
1135 "movl %[rseq_scratch0], %[src]\n\t",
1136 error1)
1137 RSEQ_ASM_DEFINE_CMPFAIL(7,
1138 "movl %[rseq_scratch2], %[len]\n\t"
1139 "movl %[rseq_scratch1], %[dst]\n\t"
1140 "movl %[rseq_scratch0], %[src]\n\t",
1141 error2)
1142 #endif
1143 : /* gcc asm goto does not allow outputs */
1144 : [cpu_id] "r" (cpu),
1145 [rseq_abi] "r" (&__rseq_abi),
1146 /* final store input */
1147 [v] "m" (*v),
1148 [expect] "m" (expect),
1149 [newv] "m" (newv),
1150 /* try memcpy input */
1151 [dst] "r" (dst),
1152 [src] "r" (src),
1153 [len] "r" (len),
1154 [rseq_scratch0] "m" (rseq_scratch[0]),
1155 [rseq_scratch1] "m" (rseq_scratch[1]),
1156 [rseq_scratch2] "m" (rseq_scratch[2])
1157 : "memory", "cc", "eax"
1158 RSEQ_INJECT_CLOBBER
1159 : abort, cmpfail
1160 #ifdef RSEQ_COMPARE_TWICE
1161 , error1, error2
1162 #endif
1164 return 0;
1165 abort:
1166 RSEQ_INJECT_FAILED
1167 return -1;
1168 cmpfail:
1169 return 1;
1170 #ifdef RSEQ_COMPARE_TWICE
1171 error1:
1172 rseq_bug("cpu_id comparison failed");
1173 error2:
1174 rseq_bug("expected value comparison failed");
1175 #endif
1178 /* TODO: implement a faster memcpy. */
1179 static inline __attribute__((always_inline))
1180 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
1181 void *dst, void *src, size_t len,
1182 intptr_t newv, int cpu)
1184 uint32_t rseq_scratch[3];
1186 RSEQ_INJECT_C(9)
1188 __asm__ __volatile__ goto (
1189 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
1190 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
1191 #ifdef RSEQ_COMPARE_TWICE
1192 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
1193 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
1194 #endif
1195 "movl %[src], %[rseq_scratch0]\n\t"
1196 "movl %[dst], %[rseq_scratch1]\n\t"
1197 "movl %[len], %[rseq_scratch2]\n\t"
1198 /* Start rseq by storing table entry pointer into rseq_cs. */
1199 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi]))
1200 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f)
1201 RSEQ_INJECT_ASM(3)
1202 "movl %[expect], %%eax\n\t"
1203 "cmpl %%eax, %[v]\n\t"
1204 "jnz 5f\n\t"
1205 RSEQ_INJECT_ASM(4)
1206 #ifdef RSEQ_COMPARE_TWICE
1207 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f)
1208 "movl %[expect], %%eax\n\t"
1209 "cmpl %%eax, %[v]\n\t"
1210 "jnz 7f\n\t"
1211 #endif
1212 /* try memcpy */
1213 "test %[len], %[len]\n\t" \
1214 "jz 333f\n\t" \
1215 "222:\n\t" \
1216 "movb (%[src]), %%al\n\t" \
1217 "movb %%al, (%[dst])\n\t" \
1218 "inc %[src]\n\t" \
1219 "inc %[dst]\n\t" \
1220 "dec %[len]\n\t" \
1221 "jnz 222b\n\t" \
1222 "333:\n\t" \
1223 RSEQ_INJECT_ASM(5)
1224 "lock; addl $0,-128(%%esp)\n\t"
1225 "movl %[newv], %%eax\n\t"
1226 /* final store */
1227 "movl %%eax, %[v]\n\t"
1228 "2:\n\t"
1229 RSEQ_INJECT_ASM(6)
1230 /* teardown */
1231 "movl %[rseq_scratch2], %[len]\n\t"
1232 "movl %[rseq_scratch1], %[dst]\n\t"
1233 "movl %[rseq_scratch0], %[src]\n\t"
1234 RSEQ_ASM_DEFINE_ABORT(4,
1235 "movl %[rseq_scratch2], %[len]\n\t"
1236 "movl %[rseq_scratch1], %[dst]\n\t"
1237 "movl %[rseq_scratch0], %[src]\n\t",
1238 abort)
1239 RSEQ_ASM_DEFINE_CMPFAIL(5,
1240 "movl %[rseq_scratch2], %[len]\n\t"
1241 "movl %[rseq_scratch1], %[dst]\n\t"
1242 "movl %[rseq_scratch0], %[src]\n\t",
1243 cmpfail)
1244 #ifdef RSEQ_COMPARE_TWICE
1245 RSEQ_ASM_DEFINE_CMPFAIL(6,
1246 "movl %[rseq_scratch2], %[len]\n\t"
1247 "movl %[rseq_scratch1], %[dst]\n\t"
1248 "movl %[rseq_scratch0], %[src]\n\t",
1249 error1)
1250 RSEQ_ASM_DEFINE_CMPFAIL(7,
1251 "movl %[rseq_scratch2], %[len]\n\t"
1252 "movl %[rseq_scratch1], %[dst]\n\t"
1253 "movl %[rseq_scratch0], %[src]\n\t",
1254 error2)
1255 #endif
1256 : /* gcc asm goto does not allow outputs */
1257 : [cpu_id] "r" (cpu),
1258 [rseq_abi] "r" (&__rseq_abi),
1259 /* final store input */
1260 [v] "m" (*v),
1261 [expect] "m" (expect),
1262 [newv] "m" (newv),
1263 /* try memcpy input */
1264 [dst] "r" (dst),
1265 [src] "r" (src),
1266 [len] "r" (len),
1267 [rseq_scratch0] "m" (rseq_scratch[0]),
1268 [rseq_scratch1] "m" (rseq_scratch[1]),
1269 [rseq_scratch2] "m" (rseq_scratch[2])
1270 : "memory", "cc", "eax"
1271 RSEQ_INJECT_CLOBBER
1272 : abort, cmpfail
1273 #ifdef RSEQ_COMPARE_TWICE
1274 , error1, error2
1275 #endif
1277 return 0;
1278 abort:
1279 RSEQ_INJECT_FAILED
1280 return -1;
1281 cmpfail:
1282 return 1;
1283 #ifdef RSEQ_COMPARE_TWICE
1284 error1:
1285 rseq_bug("cpu_id comparison failed");
1286 error2:
1287 rseq_bug("expected value comparison failed");
1288 #endif
1291 #endif /* !RSEQ_SKIP_FASTPATH */
1293 #endif