@@ -12,43 +12,17 @@ pub const panic = common.panic;
12
12
// Some architectures support atomic load/stores but no CAS, but we ignore this
13
13
// detail to keep the export logic clean and because we need some kind of CAS to
14
14
// implement the spinlocks.
15
- const supports_atomic_ops = switch (arch ) {
16
- .msp430 , .avr , .bpfel , .bpfeb = > false ,
17
- .arm , .armeb , .thumb , .thumbeb = >
18
- // The ARM v6m ISA has no ldrex/strex and so it's impossible to do CAS
19
- // operations (unless we're targeting Linux, the kernel provides a way to
20
- // perform CAS operations).
21
- // XXX: The Linux code path is not implemented yet.
22
- ! builtin .cpu .has (.arm , .has_v6m ),
23
- else = > true ,
24
- };
25
-
26
- // The size (in bytes) of the biggest object that the architecture can
27
- // load/store atomically.
28
- // Objects bigger than this threshold require the use of a lock.
29
- const largest_atomic_size = switch (arch ) {
30
- // On SPARC systems that lacks CAS and/or swap instructions, the only
31
- // available atomic operation is a test-and-set (`ldstub`), so we force
32
- // every atomic memory access to go through the lock.
33
- .sparc = > if (builtin .cpu .has (.sparc , .hasleoncasa )) @sizeOf (usize ) else 0 ,
34
-
35
- // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b
36
- // and set this parameter accordingly.
37
- else = > @sizeOf (usize ),
38
- };
39
-
40
- // The size (in bytes) of the smallest atomic object that the architecture can
41
- // perform fetch/exchange atomically. Note, this does not encompass load and store.
42
- // Objects smaller than this threshold are implemented in terms of compare-exchange
43
- // of a larger value.
44
- const smallest_atomic_fetch_exch_size = switch (arch ) {
45
- // On AMDGCN, there are no instructions for atomic operations other than load and store
46
- // (as of LLVM 15), and so these need to be implemented in terms of atomic CAS.
47
- .amdgcn = > @sizeOf (u32 ),
48
- else = > @sizeOf (u8 ),
49
- };
50
-
51
- const cache_line_size = 64 ;
15
+ const supports_atomic_ops =
16
+ std .atomic .Op .supported (.{ .cmpxchg = .strong }, usize ) or
17
+ // We have a specialized SPARC spinlock implementation
18
+ builtin .cpu .arch .isSPARC ();
19
+
20
+ // This is the size of the smallest value that the target can perform a compare-and-swap on.
21
+ // The function `wideUpdate` can be used to implement RMW operations on types smaller than this.
22
+ const wide_update_size = std .atomic .Op .supportedSizes (
23
+ .{ .cmpxchg = .weak },
24
+ builtin .cpu .arch ,
25
+ ).findMin (builtin .cpu .features );
52
26
53
27
const SpinlockTable = struct {
54
28
// Allocate ~4096 bytes of memory for the spinlock table
@@ -63,7 +37,7 @@ const SpinlockTable = struct {
63
37
64
38
// Prevent false sharing by providing enough padding between two
65
39
// consecutive spinlock elements
66
- v : if (arch .isSPARC ()) sparc_lock else other_lock align (cache_line_size ) = .Unlocked ,
40
+ v : if (arch .isSPARC ()) sparc_lock else other_lock align (std . atomic . cache_line ) = .Unlocked ,
67
41
68
42
fn acquire (self : * @This ()) void {
69
43
while (true ) {
@@ -165,12 +139,12 @@ fn __atomic_compare_exchange(
165
139
// aligned.
166
140
inline fn atomic_load_N (comptime T : type , src : * T , model : i32 ) T {
167
141
_ = model ;
168
- if (@sizeOf (T ) > largest_atomic_size ) {
142
+ if (comptime std .atomic .Op .supported (.load , T )) {
143
+ return @atomicLoad (T , src , .seq_cst );
144
+ } else {
169
145
var sl = spinlocks .get (@intFromPtr (src ));
170
146
defer sl .release ();
171
147
return src .* ;
172
- } else {
173
- return @atomicLoad (T , src , .seq_cst );
174
148
}
175
149
}
176
150
@@ -196,12 +170,12 @@ fn __atomic_load_16(src: *u128, model: i32) callconv(.c) u128 {
196
170
197
171
inline fn atomic_store_N (comptime T : type , dst : * T , value : T , model : i32 ) void {
198
172
_ = model ;
199
- if (@sizeOf (T ) > largest_atomic_size ) {
173
+ if (comptime std .atomic .Op .supported (.store , T )) {
174
+ @atomicStore (T , dst , value , .seq_cst );
175
+ } else {
200
176
var sl = spinlocks .get (@intFromPtr (dst ));
201
177
defer sl .release ();
202
178
dst .* = value ;
203
- } else {
204
- @atomicStore (T , dst , value , .seq_cst );
205
179
}
206
180
}
207
181
@@ -226,13 +200,14 @@ fn __atomic_store_16(dst: *u128, value: u128, model: i32) callconv(.c) void {
226
200
}
227
201
228
202
fn wideUpdate (comptime T : type , ptr : * T , val : T , update : anytype ) T {
229
- const WideAtomic = std .meta .Int (.unsigned , smallest_atomic_fetch_exch_size * 8 );
203
+ comptime std .debug .assert (@sizeOf (T ) < wide_update_size );
204
+ const WideAtomic = std .meta .Int (.unsigned , wide_update_size * 8 );
230
205
231
206
const addr = @intFromPtr (ptr );
232
- const wide_addr = addr & ~ (@as (T , smallest_atomic_fetch_exch_size ) - 1 );
233
- const wide_ptr : * align (smallest_atomic_fetch_exch_size ) WideAtomic = @alignCast (@as (* WideAtomic , @ptrFromInt (wide_addr )));
207
+ const wide_addr = addr & ~ (@as (T , wide_update_size ) - 1 );
208
+ const wide_ptr : * align (wide_update_size ) WideAtomic = @alignCast (@as (* WideAtomic , @ptrFromInt (wide_addr )));
234
209
235
- const inner_offset = addr & (@as (T , smallest_atomic_fetch_exch_size ) - 1 );
210
+ const inner_offset = addr & (@as (T , wide_update_size ) - 1 );
236
211
const inner_shift = @as (std .math .Log2Int (T ), @intCast (inner_offset * 8 ));
237
212
238
213
const mask = @as (WideAtomic , std .math .maxInt (T )) << inner_shift ;
@@ -252,13 +227,9 @@ fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T {
252
227
253
228
inline fn atomic_exchange_N (comptime T : type , ptr : * T , val : T , model : i32 ) T {
254
229
_ = model ;
255
- if (@sizeOf (T ) > largest_atomic_size ) {
256
- var sl = spinlocks .get (@intFromPtr (ptr ));
257
- defer sl .release ();
258
- const value = ptr .* ;
259
- ptr .* = val ;
260
- return value ;
261
- } else if (@sizeOf (T ) < smallest_atomic_fetch_exch_size ) {
230
+ if (comptime std .atomic .Op .supported (.{ .rmw = .Xchg }, T )) {
231
+ return @atomicRmw (T , ptr , .Xchg , val , .seq_cst );
232
+ } else if (@sizeOf (T ) < wide_update_size ) {
262
233
// Machine does not support this type, but it does support a larger type.
263
234
const Updater = struct {
264
235
fn update (new : T , old : T ) T {
@@ -268,7 +239,11 @@ inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T {
268
239
};
269
240
return wideUpdate (T , ptr , val , Updater .update );
270
241
} else {
271
- return @atomicRmw (T , ptr , .Xchg , val , .seq_cst );
242
+ var sl = spinlocks .get (@intFromPtr (ptr ));
243
+ defer sl .release ();
244
+ const value = ptr .* ;
245
+ ptr .* = val ;
246
+ return value ;
272
247
}
273
248
}
274
249
@@ -302,7 +277,13 @@ inline fn atomic_compare_exchange_N(
302
277
) i32 {
303
278
_ = success ;
304
279
_ = failure ;
305
- if (@sizeOf (T ) > largest_atomic_size ) {
280
+ if (comptime std .atomic .Op .supported (.{ .cmpxchg = .strong }, T )) {
281
+ if (@cmpxchgStrong (T , ptr , expected .* , desired , .seq_cst , .seq_cst )) | old_value | {
282
+ expected .* = old_value ;
283
+ return 0 ;
284
+ }
285
+ return 1 ;
286
+ } else {
306
287
var sl = spinlocks .get (@intFromPtr (ptr ));
307
288
defer sl .release ();
308
289
const value = ptr .* ;
@@ -312,12 +293,6 @@ inline fn atomic_compare_exchange_N(
312
293
}
313
294
expected .* = value ;
314
295
return 0 ;
315
- } else {
316
- if (@cmpxchgStrong (T , ptr , expected .* , desired , .seq_cst , .seq_cst )) | old_value | {
317
- expected .* = old_value ;
318
- return 0 ;
319
- }
320
- return 1 ;
321
296
}
322
297
}
323
298
@@ -359,19 +334,19 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr
359
334
}
360
335
};
361
336
362
- if (@sizeOf (T ) > largest_atomic_size ) {
337
+ if (comptime std .atomic .Op .supported (.{ .rmw = op }, T )) {
338
+ return @atomicRmw (T , ptr , op , val , .seq_cst );
339
+ } else if (@sizeOf (T ) < wide_update_size ) {
340
+ // Machine does not support this type, but it does support a larger type.
341
+ return wideUpdate (T , ptr , val , Updater .update );
342
+ } else {
363
343
var sl = spinlocks .get (@intFromPtr (ptr ));
364
344
defer sl .release ();
365
345
366
346
const value = ptr .* ;
367
347
ptr .* = Updater .update (val , value );
368
348
return value ;
369
- } else if (@sizeOf (T ) < smallest_atomic_fetch_exch_size ) {
370
- // Machine does not support this type, but it does support a larger type.
371
- return wideUpdate (T , ptr , val , Updater .update );
372
349
}
373
-
374
- return @atomicRmw (T , ptr , op , val , .seq_cst );
375
350
}
376
351
377
352
fn __atomic_fetch_add_1 (ptr : * u8 , val : u8 , model : i32 ) callconv (.c ) u8 {
0 commit comments