| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const arch = builtin.cpu.arch; | 3 | const cpu = builtin.cpu; |
| | 4 | const arch = cpu.arch; |
| 4 | | 5 | |
| 5 | const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; | 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,6 +25,11 @@ const supports_atomic_ops = switch (arch) { |
| 24 | // load/store atomically. | 25 | // load/store atomically. |
| 25 | // Objects bigger than this threshold require the use of a lock. | 26 | // Objects bigger than this threshold require the use of a lock. |
| 26 | const largest_atomic_size = switch (arch) { | 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 | // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b | 33 | // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b |
| 28 | // and set this parameter accordingly. | 34 | // and set this parameter accordingly. |
| 29 | else => @sizeOf(usize), | 35 | else => @sizeOf(usize), |
| ... | @@ -36,20 +42,44 @@ const SpinlockTable = struct { | ... | @@ -36,20 +42,44 @@ const SpinlockTable = struct { |
| 36 | const max_spinlocks = 64; | 42 | const max_spinlocks = 64; |
| 37 | | 43 | |
| 38 | const Spinlock = struct { | 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 | // Prevent false sharing by providing enough padding between two | 51 | // Prevent false sharing by providing enough padding between two |
| 40 | // consecutive spinlock elements | 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 | fn acquire(self: *@This()) void { | 55 | fn acquire(self: *@This()) void { |
| 44 | while (true) { | 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 | .Unlocked => break, | 68 | .Unlocked => break, |
| 47 | .Locked => {}, | 69 | .Locked => {}, |
| 48 | } | 70 | } |
| 49 | } | 71 | } |
| 50 | } | 72 | } |
| 51 | fn release(self: *@This()) void { | 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 | |