xref: /qemu/accel/tcg/atomic_template.h (revision 7bedee3243b995281837a30ae7bf8982b4fd570c)
1c482cb11SRichard Henderson /*
2c482cb11SRichard Henderson  * Atomic helper templates
3c482cb11SRichard Henderson  * Included from tcg-runtime.c and cputlb.c.
4c482cb11SRichard Henderson  *
5c482cb11SRichard Henderson  * Copyright (c) 2016 Red Hat, Inc
6c482cb11SRichard Henderson  *
7c482cb11SRichard Henderson  * This library is free software; you can redistribute it and/or
8c482cb11SRichard Henderson  * modify it under the terms of the GNU Lesser General Public
9c482cb11SRichard Henderson  * License as published by the Free Software Foundation; either
10fb0343d5SThomas Huth  * version 2.1 of the License, or (at your option) any later version.
11c482cb11SRichard Henderson  *
12c482cb11SRichard Henderson  * This library is distributed in the hope that it will be useful,
13c482cb11SRichard Henderson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14c482cb11SRichard Henderson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15c482cb11SRichard Henderson  * Lesser General Public License for more details.
16c482cb11SRichard Henderson  *
17c482cb11SRichard Henderson  * You should have received a copy of the GNU Lesser General Public
18c482cb11SRichard Henderson  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19c482cb11SRichard Henderson  */
20c482cb11SRichard Henderson 
21e6d86bedSEmilio G. Cota #include "qemu/plugin.h"
22d071f4cdSEmilio G. Cota 
237ebee43eSRichard Henderson #if DATA_SIZE == 16
247ebee43eSRichard Henderson # define SUFFIX     o
257ebee43eSRichard Henderson # define DATA_TYPE  Int128
267ebee43eSRichard Henderson # define BSWAP      bswap128
27d071f4cdSEmilio G. Cota # define SHIFT      4
287ebee43eSRichard Henderson #elif DATA_SIZE == 8
29c482cb11SRichard Henderson # define SUFFIX     q
309ef0c6d6SRichard Henderson # define DATA_TYPE  aligned_uint64_t
319ef0c6d6SRichard Henderson # define SDATA_TYPE aligned_int64_t
32c482cb11SRichard Henderson # define BSWAP      bswap64
33d071f4cdSEmilio G. Cota # define SHIFT      3
34c482cb11SRichard Henderson #elif DATA_SIZE == 4
35c482cb11SRichard Henderson # define SUFFIX     l
36c482cb11SRichard Henderson # define DATA_TYPE  uint32_t
375507c2bfSRichard Henderson # define SDATA_TYPE int32_t
38c482cb11SRichard Henderson # define BSWAP      bswap32
39d071f4cdSEmilio G. Cota # define SHIFT      2
40c482cb11SRichard Henderson #elif DATA_SIZE == 2
41c482cb11SRichard Henderson # define SUFFIX     w
42c482cb11SRichard Henderson # define DATA_TYPE  uint16_t
435507c2bfSRichard Henderson # define SDATA_TYPE int16_t
44c482cb11SRichard Henderson # define BSWAP      bswap16
45d071f4cdSEmilio G. Cota # define SHIFT      1
46c482cb11SRichard Henderson #elif DATA_SIZE == 1
47c482cb11SRichard Henderson # define SUFFIX     b
48c482cb11SRichard Henderson # define DATA_TYPE  uint8_t
495507c2bfSRichard Henderson # define SDATA_TYPE int8_t
50c482cb11SRichard Henderson # define BSWAP
51d071f4cdSEmilio G. Cota # define SHIFT      0
52c482cb11SRichard Henderson #else
53c482cb11SRichard Henderson # error unsupported data size
54c482cb11SRichard Henderson #endif
55c482cb11SRichard Henderson 
56c482cb11SRichard Henderson #if DATA_SIZE >= 4
57c482cb11SRichard Henderson # define ABI_TYPE  DATA_TYPE
58c482cb11SRichard Henderson #else
59c482cb11SRichard Henderson # define ABI_TYPE  uint32_t
60c482cb11SRichard Henderson #endif
61c482cb11SRichard Henderson 
62c482cb11SRichard Henderson /* Define host-endian atomic operations.  Note that END is used within
63c482cb11SRichard Henderson    the ATOMIC_NAME macro, and redefined below.  */
64c482cb11SRichard Henderson #if DATA_SIZE == 1
65c482cb11SRichard Henderson # define END
66e03b5686SMarc-André Lureau #elif HOST_BIG_ENDIAN
67c482cb11SRichard Henderson # define END  _be
68c482cb11SRichard Henderson #else
69c482cb11SRichard Henderson # define END  _le
70c482cb11SRichard Henderson #endif
71c482cb11SRichard Henderson 
72c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
7348688fafSRichard Henderson                               ABI_TYPE cmpv, ABI_TYPE newv,
749002ffcbSRichard Henderson                               MemOpIdx oi, uintptr_t retaddr)
75c482cb11SRichard Henderson {
76*7bedee32SRichard Henderson     DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
77d071f4cdSEmilio G. Cota     DATA_TYPE ret;
78d071f4cdSEmilio G. Cota 
79e6cd4bb5SRichard Henderson #if DATA_SIZE == 16
80e6cd4bb5SRichard Henderson     ret = atomic16_cmpxchg(haddr, cmpv, newv);
81e6cd4bb5SRichard Henderson #else
82d73415a3SStefan Hajnoczi     ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv);
83e6cd4bb5SRichard Henderson #endif
84ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;
85c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);
86ec603b55SRichard Henderson     return ret;
87c482cb11SRichard Henderson }
88c482cb11SRichard Henderson 
89ec4a9629SRichard Henderson #if DATA_SIZE < 16
9048688fafSRichard Henderson ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
919002ffcbSRichard Henderson                            MemOpIdx oi, uintptr_t retaddr)
92c482cb11SRichard Henderson {
93*7bedee32SRichard Henderson     DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
94d071f4cdSEmilio G. Cota     DATA_TYPE ret;
95d071f4cdSEmilio G. Cota 
96d73415a3SStefan Hajnoczi     ret = qatomic_xchg__nocheck(haddr, val);
97ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;
98c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);
99ec603b55SRichard Henderson     return ret;
100c482cb11SRichard Henderson }
101c482cb11SRichard Henderson 
102c482cb11SRichard Henderson #define GEN_ATOMIC_HELPER(X)                                        \
103c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
1049002ffcbSRichard Henderson                         ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
105c482cb11SRichard Henderson {                                                                   \
106*7bedee32SRichard Henderson     DATA_TYPE *haddr, ret;                                          \
107*7bedee32SRichard Henderson     haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);   \
108d73415a3SStefan Hajnoczi     ret = qatomic_##X(haddr, val);                                  \
109ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;                                             \
110c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);                           \
111ec603b55SRichard Henderson     return ret;                                                     \
112ec603b55SRichard Henderson }
113c482cb11SRichard Henderson 
114c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_add)
115c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_and)
116c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_or)
117c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_xor)
118c482cb11SRichard Henderson GEN_ATOMIC_HELPER(add_fetch)
119c482cb11SRichard Henderson GEN_ATOMIC_HELPER(and_fetch)
120c482cb11SRichard Henderson GEN_ATOMIC_HELPER(or_fetch)
121c482cb11SRichard Henderson GEN_ATOMIC_HELPER(xor_fetch)
122c482cb11SRichard Henderson 
123c482cb11SRichard Henderson #undef GEN_ATOMIC_HELPER
1245507c2bfSRichard Henderson 
125a754f7f3SRichard Henderson /*
126a754f7f3SRichard Henderson  * These helpers are, as a whole, full barriers.  Within the helper,
1275507c2bfSRichard Henderson  * the leading barrier is explicit and the trailing barrier is within
1285507c2bfSRichard Henderson  * cmpxchg primitive.
129d071f4cdSEmilio G. Cota  *
130d071f4cdSEmilio G. Cota  * Trace this load + RMW loop as a single RMW op. This way, regardless
131d071f4cdSEmilio G. Cota  * of CF_PARALLEL's value, we'll trace just a read and a write.
1325507c2bfSRichard Henderson  */
1335507c2bfSRichard Henderson #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \
1345507c2bfSRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
1359002ffcbSRichard Henderson                         ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
1365507c2bfSRichard Henderson {                                                                   \
137*7bedee32SRichard Henderson     XDATA_TYPE *haddr, cmp, old, new, val = xval;                   \
138*7bedee32SRichard Henderson     haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);   \
1395507c2bfSRichard Henderson     smp_mb();                                                       \
140d73415a3SStefan Hajnoczi     cmp = qatomic_read__nocheck(haddr);                             \
1415507c2bfSRichard Henderson     do {                                                            \
1425507c2bfSRichard Henderson         old = cmp; new = FN(old, val);                              \
143d73415a3SStefan Hajnoczi         cmp = qatomic_cmpxchg__nocheck(haddr, old, new);            \
1445507c2bfSRichard Henderson     } while (cmp != old);                                           \
1455507c2bfSRichard Henderson     ATOMIC_MMU_CLEANUP;                                             \
146c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);                           \
1475507c2bfSRichard Henderson     return RET;                                                     \
1485507c2bfSRichard Henderson }
1495507c2bfSRichard Henderson 
1505507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
1515507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old)
1525507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
1535507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old)
1545507c2bfSRichard Henderson 
1555507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
1565507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new)
1575507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
1585507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new)
1595507c2bfSRichard Henderson 
1605507c2bfSRichard Henderson #undef GEN_ATOMIC_HELPER_FN
161ec4a9629SRichard Henderson #endif /* DATA SIZE < 16 */
1627ebee43eSRichard Henderson 
163c482cb11SRichard Henderson #undef END
164c482cb11SRichard Henderson 
165c482cb11SRichard Henderson #if DATA_SIZE > 1
166c482cb11SRichard Henderson 
167c482cb11SRichard Henderson /* Define reverse-host-endian atomic operations.  Note that END is used
168c482cb11SRichard Henderson    within the ATOMIC_NAME macro.  */
169e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
170c482cb11SRichard Henderson # define END  _le
171c482cb11SRichard Henderson #else
172c482cb11SRichard Henderson # define END  _be
173c482cb11SRichard Henderson #endif
174c482cb11SRichard Henderson 
175c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
17648688fafSRichard Henderson                               ABI_TYPE cmpv, ABI_TYPE newv,
1779002ffcbSRichard Henderson                               MemOpIdx oi, uintptr_t retaddr)
178c482cb11SRichard Henderson {
179*7bedee32SRichard Henderson     DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
180d071f4cdSEmilio G. Cota     DATA_TYPE ret;
181d071f4cdSEmilio G. Cota 
182e6cd4bb5SRichard Henderson #if DATA_SIZE == 16
183e6cd4bb5SRichard Henderson     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
184e6cd4bb5SRichard Henderson #else
185d73415a3SStefan Hajnoczi     ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
186e6cd4bb5SRichard Henderson #endif
187ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;
188c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);
189ec603b55SRichard Henderson     return BSWAP(ret);
190c482cb11SRichard Henderson }
191c482cb11SRichard Henderson 
192ec4a9629SRichard Henderson #if DATA_SIZE < 16
19348688fafSRichard Henderson ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
1949002ffcbSRichard Henderson                            MemOpIdx oi, uintptr_t retaddr)
195c482cb11SRichard Henderson {
196*7bedee32SRichard Henderson     DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
197d071f4cdSEmilio G. Cota     ABI_TYPE ret;
198d071f4cdSEmilio G. Cota 
199d73415a3SStefan Hajnoczi     ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
200ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;
201c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);
202ec603b55SRichard Henderson     return BSWAP(ret);
203c482cb11SRichard Henderson }
204c482cb11SRichard Henderson 
205c482cb11SRichard Henderson #define GEN_ATOMIC_HELPER(X)                                        \
206c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
2079002ffcbSRichard Henderson                         ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
208c482cb11SRichard Henderson {                                                                   \
209*7bedee32SRichard Henderson     DATA_TYPE *haddr, ret;                                          \
210*7bedee32SRichard Henderson     haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);   \
211d73415a3SStefan Hajnoczi     ret = qatomic_##X(haddr, BSWAP(val));                           \
212ec603b55SRichard Henderson     ATOMIC_MMU_CLEANUP;                                             \
213c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);                           \
214ec603b55SRichard Henderson     return BSWAP(ret);                                              \
215c482cb11SRichard Henderson }
216c482cb11SRichard Henderson 
217c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_and)
218c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_or)
219c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_xor)
220c482cb11SRichard Henderson GEN_ATOMIC_HELPER(and_fetch)
221c482cb11SRichard Henderson GEN_ATOMIC_HELPER(or_fetch)
222c482cb11SRichard Henderson GEN_ATOMIC_HELPER(xor_fetch)
223c482cb11SRichard Henderson 
224c482cb11SRichard Henderson #undef GEN_ATOMIC_HELPER
225c482cb11SRichard Henderson 
2265507c2bfSRichard Henderson /* These helpers are, as a whole, full barriers.  Within the helper,
2275507c2bfSRichard Henderson  * the leading barrier is explicit and the trailing barrier is within
2285507c2bfSRichard Henderson  * cmpxchg primitive.
229d071f4cdSEmilio G. Cota  *
230d071f4cdSEmilio G. Cota  * Trace this load + RMW loop as a single RMW op. This way, regardless
231d071f4cdSEmilio G. Cota  * of CF_PARALLEL's value, we'll trace just a read and a write.
2325507c2bfSRichard Henderson  */
2335507c2bfSRichard Henderson #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \
2345507c2bfSRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
2359002ffcbSRichard Henderson                         ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
2365507c2bfSRichard Henderson {                                                                   \
237*7bedee32SRichard Henderson     XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval;              \
238*7bedee32SRichard Henderson     haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);   \
2395507c2bfSRichard Henderson     smp_mb();                                                       \
240d73415a3SStefan Hajnoczi     ldn = qatomic_read__nocheck(haddr);                             \
2415507c2bfSRichard Henderson     do {                                                            \
2425507c2bfSRichard Henderson         ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \
243d73415a3SStefan Hajnoczi         ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));     \
2445507c2bfSRichard Henderson     } while (ldo != ldn);                                           \
2455507c2bfSRichard Henderson     ATOMIC_MMU_CLEANUP;                                             \
246c3e83e37SRichard Henderson     atomic_trace_rmw_post(env, addr, oi);                           \
2475507c2bfSRichard Henderson     return RET;                                                     \
2485507c2bfSRichard Henderson }
2495507c2bfSRichard Henderson 
2505507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
2515507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old)
2525507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
2535507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old)
2545507c2bfSRichard Henderson 
2555507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
2565507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new)
2575507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
2585507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new)
2595507c2bfSRichard Henderson 
26058edf9eeSRichard Henderson /* Note that for addition, we need to use a separate cmpxchg loop instead
26158edf9eeSRichard Henderson    of bswaps for the reverse-host-endian helpers.  */
26258edf9eeSRichard Henderson #define ADD(X, Y)   (X + Y)
26358edf9eeSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
26458edf9eeSRichard Henderson GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
26558edf9eeSRichard Henderson #undef ADD
26658edf9eeSRichard Henderson 
2675507c2bfSRichard Henderson #undef GEN_ATOMIC_HELPER_FN
268ec4a9629SRichard Henderson #endif /* DATA_SIZE < 16 */
269c482cb11SRichard Henderson 
270c482cb11SRichard Henderson #undef END
271c482cb11SRichard Henderson #endif /* DATA_SIZE > 1 */
272c482cb11SRichard Henderson 
273c482cb11SRichard Henderson #undef BSWAP
274c482cb11SRichard Henderson #undef ABI_TYPE
275c482cb11SRichard Henderson #undef DATA_TYPE
2765507c2bfSRichard Henderson #undef SDATA_TYPE
277c482cb11SRichard Henderson #undef SUFFIX
278c482cb11SRichard Henderson #undef DATA_SIZE
279d071f4cdSEmilio G. Cota #undef SHIFT
280