| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | | const arch = builtin.cpu.arch; |
| 3 | const cpu = builtin.cpu; |
| 4 | const arch = cpu.arch; |
| 4 | 5 | |
| 5 | 6 | const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; |
| 6 | 7 | |
| ... | ... | @@ -24,6 +25,11 @@ const supports_atomic_ops = switch (arch) { |
| 24 | 25 | // load/store atomically. |
| 25 | 26 | // Objects bigger than this threshold require the use of a lock. |
| 26 | 27 | const largest_atomic_size = switch (arch) { |
| 28 | // On SPARC systems that lacks CAS and/or swap instructions, the only |
| 29 | // available atomic operation is a test-and-set (`ldstub`), so we force |
| 30 | // every atomic memory access to go through the lock. |
| 31 | .sparc, .sparcel => if (cpu.features.featureSetHas(.hasleoncasa)) @sizeOf(usize) else 0, |
| 32 | |
| 27 | 33 | // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b |
| 28 | 34 | // and set this parameter accordingly. |
| 29 | 35 | else => @sizeOf(usize), |
| ... | ... | @@ -36,20 +42,44 @@ const SpinlockTable = struct { |
| 36 | 42 | const max_spinlocks = 64; |
| 37 | 43 | |
| 38 | 44 | const Spinlock = struct { |
| 45 | // SPARC ldstub instruction will write a 255 into the memory location. |
| 46 | // We'll use that as a sign that the lock is currently held. |
| 47 | // See also: Section B.7 in SPARCv8 spec & A.29 in SPARCv9 spec. |
| 48 | const sparc_lock: type = enum(u8) { Unlocked = 0, Locked = 255 }; |
| 49 | const other_lock: type = enum(usize) { Unlocked = 0, Locked }; |
| 50 | |
| 39 | 51 | // Prevent false sharing by providing enough padding between two |
| 40 | 52 | // consecutive spinlock elements |
| 41 | | v: enum(usize) { Unlocked = 0, Locked } align(cache_line_size) = .Unlocked, |
| 53 | v: if (arch.isSPARC()) sparc_lock else other_lock align(cache_line_size) = .Unlocked, |
| 42 | 54 | |
| 43 | 55 | fn acquire(self: *@This()) void { |
| 44 | 56 | while (true) { |
| 45 | | switch (@atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire)) { |
| 57 | const flag = if (comptime arch.isSPARC()) flag: { |
| 58 | break :flag asm volatile ("ldstub [%[addr]], %[flag]" |
| 59 | : [flag] "=r" (-> @TypeOf(self.v)), |
| 60 | : [addr] "r" (&self.v), |
| 61 | : "memory" |
| 62 | ); |
| 63 | } else flag: { |
| 64 | break :flag @atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire); |
| 65 | }; |
| 66 | |
| 67 | switch (flag) { |
| 46 | 68 | .Unlocked => break, |
| 47 | 69 | .Locked => {}, |
| 48 | 70 | } |
| 49 | 71 | } |
| 50 | 72 | } |
| 51 | 73 | fn release(self: *@This()) void { |
| 52 | | @atomicStore(@TypeOf(self.v), &self.v, .Unlocked, .Release); |
| 74 | if (comptime arch.isSPARC()) { |
| 75 | _ = asm volatile ("clrb [%[addr]]" |
| 76 | : |
| 77 | : [addr] "r" (&self.v), |
| 78 | : "memory" |
| 79 | ); |
| 80 | } else { |
| 81 | @atomicStore(@TypeOf(self.v), &self.v, .Unlocked, .Release); |
| 82 | } |
| 53 | 83 | } |
| 54 | 84 | }; |
| 55 | 85 | |