authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-12 17:45:29+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-11-12 17:45:29+00:00
loge32b4829f4f91ee412ead7a3851f6e271d9fb07e
tree2f04d9d06b0ee3973c1f83afdf6018fcac4d99b7
parent710ccacfa3307fc642f4bac71f894e3d8a18764a
parent5194fc57d1c206d71654b4f3e43bfcb300bf43c5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3670 from Vexu/atomics-enum

Support atomic operations with enums

6 files changed, 99 insertions(+), 58 deletions(-)

lib/std/event/future.zig+11-10
...@@ -12,12 +12,13 @@ pub fn Future(comptime T: type) type {...@@ -12,12 +12,13 @@ pub fn Future(comptime T: type) type {
12 return struct {12 return struct {
13 lock: Lock,13 lock: Lock,
14 data: T,14 data: T,
15 available: Available,
1516
16 /// TODO make this an enum17 const Available = enum(u8) {
17 /// 0 - not started18 NotStarted,
18 /// 1 - started19 Started,
19 /// 2 - finished20 Finished,
20 available: u8,21 };
2122
22 const Self = @This();23 const Self = @This();
23 const Queue = std.atomic.Queue(anyframe);24 const Queue = std.atomic.Queue(anyframe);
...@@ -34,7 +35,7 @@ pub fn Future(comptime T: type) type {...@@ -34,7 +35,7 @@ pub fn Future(comptime T: type) type {
34 /// available.35 /// available.
35 /// Thread-safe.36 /// Thread-safe.
36 pub async fn get(self: *Self) *T {37 pub async fn get(self: *Self) *T {
37 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {38 if (@atomicLoad(Available, &self.available, .SeqCst) == .Finished) {
38 return &self.data;39 return &self.data;
39 }40 }
40 const held = self.lock.acquire();41 const held = self.lock.acquire();
...@@ -46,7 +47,7 @@ pub fn Future(comptime T: type) type {...@@ -46,7 +47,7 @@ pub fn Future(comptime T: type) type {
46 /// Gets the data without waiting for it. If it's available, a pointer is47 /// Gets the data without waiting for it. If it's available, a pointer is
47 /// returned. Otherwise, null is returned.48 /// returned. Otherwise, null is returned.
48 pub fn getOrNull(self: *Self) ?*T {49 pub fn getOrNull(self: *Self) ?*T {
49 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {50 if (@atomicLoad(Available, &self.available, .SeqCst) == .Finished) {
50 return &self.data;51 return &self.data;
51 } else {52 } else {
52 return null;53 return null;
...@@ -59,7 +60,7 @@ pub fn Future(comptime T: type) type {...@@ -59,7 +60,7 @@ pub fn Future(comptime T: type) type {
59 /// It's not required to call start() before resolve() but it can be useful since60 /// It's not required to call start() before resolve() but it can be useful since
60 /// this method is thread-safe.61 /// this method is thread-safe.
61 pub async fn start(self: *Self) ?*T {62 pub async fn start(self: *Self) ?*T {
62 const state = @cmpxchgStrong(u8, &self.available, 0, 1, .SeqCst, .SeqCst) orelse return null;63 const state = @cmpxchgStrong(Available, &self.available, .NotStarted, .Started, .SeqCst, .SeqCst) orelse return null;
63 switch (state) {64 switch (state) {
64 1 => {65 1 => {
65 const held = self.lock.acquire();66 const held = self.lock.acquire();
...@@ -74,8 +75,8 @@ pub fn Future(comptime T: type) type {...@@ -74,8 +75,8 @@ pub fn Future(comptime T: type) type {
74 /// Make the data become available. May be called only once.75 /// Make the data become available. May be called only once.
75 /// Before calling this, modify the `data` property.76 /// Before calling this, modify the `data` property.
76 pub fn resolve(self: *Self) void {77 pub fn resolve(self: *Self) void {
77 const prev = @atomicRmw(u8, &self.available, .Xchg, 2, .SeqCst);78 const prev = @atomicRmw(Available, &self.available, .Xchg, .Finished, .SeqCst);
78 assert(prev == 0 or prev == 1); // resolve() called twice79 assert(prev != .Finished); // resolve() called twice
79 Lock.Held.release(Lock.Held{ .lock = &self.lock });80 Lock.Held.release(Lock.Held{ .lock = &self.lock });
80 }81 }
81 };82 };
lib/std/event/rwlock.zig+16-16
...@@ -13,17 +13,17 @@ const Loop = std.event.Loop;...@@ -13,17 +13,17 @@ const Loop = std.event.Loop;
13/// When a write lock is held, it will not be released until the writer queue is empty.13/// When a write lock is held, it will not be released until the writer queue is empty.
14/// TODO: make this API also work in blocking I/O mode14/// TODO: make this API also work in blocking I/O mode
15pub const RwLock = struct {15pub const RwLock = struct {
16 shared_state: u8, // TODO make this an enum16 shared_state: State,
17 writer_queue: Queue,17 writer_queue: Queue,
18 reader_queue: Queue,18 reader_queue: Queue,
19 writer_queue_empty_bit: u8, // TODO make this a bool19 writer_queue_empty_bit: u8, // TODO make this a bool
20 reader_queue_empty_bit: u8, // TODO make this a bool20 reader_queue_empty_bit: u8, // TODO make this a bool
21 reader_lock_count: usize,21 reader_lock_count: usize,
2222
23 const State = struct {23 const State = enum(u8) {
24 const Unlocked = 0;24 Unlocked,
25 const WriteLock = 1;25 WriteLock,
26 const ReadLock = 2;26 ReadLock,
27 };27 };
2828
29 const Queue = std.atomic.Queue(anyframe);29 const Queue = std.atomic.Queue(anyframe);
...@@ -41,7 +41,7 @@ pub const RwLock = struct {...@@ -41,7 +41,7 @@ pub const RwLock = struct {
41 }41 }
4242
43 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst);43 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
44 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {44 if (@cmpxchgStrong(State, &self.lock.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) {
45 // Didn't unlock. Someone else's problem.45 // Didn't unlock. Someone else's problem.
46 return;46 return;
47 }47 }
...@@ -64,7 +64,7 @@ pub const RwLock = struct {...@@ -64,7 +64,7 @@ pub const RwLock = struct {
64 // We need to release the write lock. Check if any readers are waiting to grab the lock.64 // We need to release the write lock. Check if any readers are waiting to grab the lock.
65 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, .SeqCst) == 0) {65 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, .SeqCst) == 0) {
66 // Switch to a read lock.66 // Switch to a read lock.
67 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.ReadLock, .SeqCst);67 _ = @atomicRmw(State, &self.lock.shared_state, .Xchg, .ReadLock, .SeqCst);
68 while (self.lock.reader_queue.get()) |node| {68 while (self.lock.reader_queue.get()) |node| {
69 global_event_loop.onNextTick(node);69 global_event_loop.onNextTick(node);
70 }70 }
...@@ -72,7 +72,7 @@ pub const RwLock = struct {...@@ -72,7 +72,7 @@ pub const RwLock = struct {
72 }72 }
7373
74 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst);74 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
75 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst);75 _ = @atomicRmw(State, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst);
7676
77 self.lock.commonPostUnlock();77 self.lock.commonPostUnlock();
78 }78 }
...@@ -80,7 +80,7 @@ pub const RwLock = struct {...@@ -80,7 +80,7 @@ pub const RwLock = struct {
8080
81 pub fn init() RwLock {81 pub fn init() RwLock {
82 return RwLock{82 return RwLock{
83 .shared_state = State.Unlocked,83 .shared_state = .Unlocked,
84 .writer_queue = Queue.init(),84 .writer_queue = Queue.init(),
85 .writer_queue_empty_bit = 1,85 .writer_queue_empty_bit = 1,
86 .reader_queue = Queue.init(),86 .reader_queue = Queue.init(),
...@@ -92,7 +92,7 @@ pub const RwLock = struct {...@@ -92,7 +92,7 @@ pub const RwLock = struct {
92 /// Must be called when not locked. Not thread safe.92 /// Must be called when not locked. Not thread safe.
93 /// All calls to acquire() and release() must complete before calling deinit().93 /// All calls to acquire() and release() must complete before calling deinit().
94 pub fn deinit(self: *RwLock) void {94 pub fn deinit(self: *RwLock) void {
95 assert(self.shared_state == State.Unlocked);95 assert(self.shared_state == .Unlocked);
96 while (self.writer_queue.get()) |node| resume node.data;96 while (self.writer_queue.get()) |node| resume node.data;
97 while (self.reader_queue.get()) |node| resume node.data;97 while (self.reader_queue.get()) |node| resume node.data;
98 }98 }
...@@ -116,7 +116,7 @@ pub const RwLock = struct {...@@ -116,7 +116,7 @@ pub const RwLock = struct {
116 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 0, .SeqCst);116 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 0, .SeqCst);
117117
118 // Here we don't care if we are the one to do the locking or if it was already locked for reading.118 // Here we don't care if we are the one to do the locking or if it was already locked for reading.
119 const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == State.ReadLock else true;119 const have_read_lock = if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == .ReadLock else true;
120 if (have_read_lock) {120 if (have_read_lock) {
121 // Give out all the read locks.121 // Give out all the read locks.
122 if (self.reader_queue.get()) |first_node| {122 if (self.reader_queue.get()) |first_node| {
...@@ -147,7 +147,7 @@ pub const RwLock = struct {...@@ -147,7 +147,7 @@ pub const RwLock = struct {
147 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 0, .SeqCst);147 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 0, .SeqCst);
148148
149 // Here we must be the one to acquire the write lock. It cannot already be locked.149 // Here we must be the one to acquire the write lock. It cannot already be locked.
150 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) == null) {150 if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) == null) {
151 // We now have a write lock.151 // We now have a write lock.
152 if (self.writer_queue.get()) |node| {152 if (self.writer_queue.get()) |node| {
153 // Whether this node is us or someone else, we tail resume it.153 // Whether this node is us or someone else, we tail resume it.
...@@ -166,7 +166,7 @@ pub const RwLock = struct {...@@ -166,7 +166,7 @@ pub const RwLock = struct {
166 // But if there's a writer_queue item or a reader_queue item,166 // But if there's a writer_queue item or a reader_queue item,
167 // we are the actor which must loop and attempt to grab the lock again.167 // we are the actor which must loop and attempt to grab the lock again.
168 if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) {168 if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) {
169 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) != null) {169 if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) != null) {
170 // We did not obtain the lock. Great, the queues are someone else's problem.170 // We did not obtain the lock. Great, the queues are someone else's problem.
171 return;171 return;
172 }172 }
...@@ -177,12 +177,12 @@ pub const RwLock = struct {...@@ -177,12 +177,12 @@ pub const RwLock = struct {
177 }177 }
178 // Release the lock again.178 // Release the lock again.
179 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst);179 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
180 _ = @atomicRmw(u8, &self.shared_state, .Xchg, State.Unlocked, .SeqCst);180 _ = @atomicRmw(State, &self.shared_state, .Xchg, .Unlocked, .SeqCst);
181 continue;181 continue;
182 }182 }
183183
184 if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) {184 if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) {
185 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst) != null) {185 if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst) != null) {
186 // We did not obtain the lock. Great, the queues are someone else's problem.186 // We did not obtain the lock. Great, the queues are someone else's problem.
187 return;187 return;
188 }188 }
...@@ -196,7 +196,7 @@ pub const RwLock = struct {...@@ -196,7 +196,7 @@ pub const RwLock = struct {
196 }196 }
197 // Release the lock again.197 // Release the lock again.
198 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst);198 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
199 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {199 if (@cmpxchgStrong(State, &self.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) {
200 // Didn't unlock. Someone else's problem.200 // Didn't unlock. Someone else's problem.
201 return;201 return;
202 }202 }
lib/std/lazy_init.zig+13-11
...@@ -1,24 +1,26 @@...@@ -1,24 +1,26 @@
1const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;2const assert = std.debug.assert;
4const testing = std.testing;3const testing = std.testing;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
74
8/// Thread-safe initialization of global data.5/// Thread-safe initialization of global data.
9/// TODO use a mutex instead of a spinlock6/// TODO use a mutex instead of a spinlock
10pub fn lazyInit(comptime T: type) LazyInit(T) {7pub fn lazyInit(comptime T: type) LazyInit(T) {
11 return LazyInit(T){8 return LazyInit(T){
12 .data = undefined,9 .data = undefined,
13 .state = 0,
14 };10 };
15}11}
1612
17fn LazyInit(comptime T: type) type {13fn LazyInit(comptime T: type) type {
18 return struct {14 return struct {
19 state: u8, // TODO make this an enum15 state: State = .NotResolved,
20 data: Data,16 data: Data,
2117
18 const State = enum(u8) {
19 NotResolved,
20 Resolving,
21 Resolved,
22 };
23
22 const Self = @This();24 const Self = @This();
2325
24 // TODO this isn't working for void, investigate and then remove this special case26 // TODO this isn't working for void, investigate and then remove this special case
...@@ -30,14 +32,14 @@ fn LazyInit(comptime T: type) type {...@@ -30,14 +32,14 @@ fn LazyInit(comptime T: type) type {
30 /// perform the initialization and then call resolve().32 /// perform the initialization and then call resolve().
31 pub fn get(self: *Self) ?Ptr {33 pub fn get(self: *Self) ?Ptr {
32 while (true) {34 while (true) {
33 var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;35 var state = @cmpxchgWeak(State, &self.state, .NotResolved, .Resolving, .SeqCst, .SeqCst) orelse return null;
34 switch (state) {36 switch (state) {
35 0 => continue,37 .NotResolved => continue,
36 1 => {38 .Resolving => {
37 // TODO mutex instead of a spinlock39 // TODO mutex instead of a spinlock
38 continue;40 continue;
39 },41 },
40 2 => {42 .Resolved => {
41 if (@sizeOf(T) == 0) {43 if (@sizeOf(T) == 0) {
42 return @as(T, undefined);44 return @as(T, undefined);
43 } else {45 } else {
...@@ -50,8 +52,8 @@ fn LazyInit(comptime T: type) type {...@@ -50,8 +52,8 @@ fn LazyInit(comptime T: type) type {
50 }52 }
5153
52 pub fn resolve(self: *Self) void {54 pub fn resolve(self: *Self) void {
53 const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);55 const prev = @atomicRmw(State, &self.state, .Xchg, .Resolved, .SeqCst);
54 assert(prev == 1); // resolve() called twice56 assert(prev != .Resolved); // resolve() called twice
55 }57 }
56 };58 };
57}59}
lib/std/mutex.zig+22-20
...@@ -39,12 +39,14 @@ pub const Mutex = if (builtin.single_threaded)...@@ -39,12 +39,14 @@ pub const Mutex = if (builtin.single_threaded)
39 }39 }
40else40else
41 struct {41 struct {
42 state: u32, // TODO: make this an enum42 state: State, // TODO: make this an enum
43 parker: ThreadParker,43 parker: ThreadParker,
4444
45 const Unlocked = 0;45 const State = enum(u32) {
46 const Sleeping = 1;46 Unlocked,
47 const Locked = 2;47 Sleeping,
48 Locked,
49 };
4850
49 /// number of iterations to spin yielding the cpu51 /// number of iterations to spin yielding the cpu
50 const SPIN_CPU = 4;52 const SPIN_CPU = 4;
...@@ -57,7 +59,7 @@ else...@@ -57,7 +59,7 @@ else
5759
58 pub fn init() Mutex {60 pub fn init() Mutex {
59 return Mutex{61 return Mutex{
60 .state = Unlocked,62 .state = .Unlocked,
61 .parker = ThreadParker.init(),63 .parker = ThreadParker.init(),
62 };64 };
63 }65 }
...@@ -70,10 +72,10 @@ else...@@ -70,10 +72,10 @@ else
70 mutex: *Mutex,72 mutex: *Mutex,
7173
72 pub fn release(self: Held) void {74 pub fn release(self: Held) void {
73 switch (@atomicRmw(u32, &self.mutex.state, .Xchg, Unlocked, .Release)) {75 switch (@atomicRmw(State, &self.mutex.state, .Xchg, .Unlocked, .Release)) {
74 Locked => {},76 .Locked => {},
75 Sleeping => self.mutex.parker.unpark(&self.mutex.state),77 .Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)),
76 Unlocked => unreachable, // unlocking an unlocked mutex78 .Unlocked => unreachable, // unlocking an unlocked mutex
77 else => unreachable, // should never be anything else79 else => unreachable, // should never be anything else
78 }80 }
79 }81 }
...@@ -83,34 +85,34 @@ else...@@ -83,34 +85,34 @@ else
83 // Try and speculatively grab the lock.85 // Try and speculatively grab the lock.
84 // If it fails, the state is either Locked or Sleeping86 // If it fails, the state is either Locked or Sleeping
85 // depending on if theres a thread stuck sleeping below.87 // depending on if theres a thread stuck sleeping below.
86 var state = @atomicRmw(u32, &self.state, .Xchg, Locked, .Acquire);88 var state = @atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire);
87 if (state == Unlocked)89 if (state == .Unlocked)
88 return Held{ .mutex = self };90 return Held{ .mutex = self };
8991
90 while (true) {92 while (true) {
91 // try and acquire the lock using cpu spinning on failure93 // try and acquire the lock using cpu spinning on failure
92 var spin: usize = 0;94 var spin: usize = 0;
93 while (spin < SPIN_CPU) : (spin += 1) {95 while (spin < SPIN_CPU) : (spin += 1) {
94 var value = @atomicLoad(u32, &self.state, .Monotonic);96 var value = @atomicLoad(State, &self.state, .Monotonic);
95 while (value == Unlocked)97 while (value == .Unlocked)
96 value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };98 value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
97 SpinLock.yield(SPIN_CPU_COUNT);99 SpinLock.yield(SPIN_CPU_COUNT);
98 }100 }
99101
100 // try and acquire the lock using thread rescheduling on failure102 // try and acquire the lock using thread rescheduling on failure
101 spin = 0;103 spin = 0;
102 while (spin < SPIN_THREAD) : (spin += 1) {104 while (spin < SPIN_THREAD) : (spin += 1) {
103 var value = @atomicLoad(u32, &self.state, .Monotonic);105 var value = @atomicLoad(State, &self.state, .Monotonic);
104 while (value == Unlocked)106 while (value == .Unlocked)
105 value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };107 value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
106 std.os.sched_yield() catch std.time.sleep(1);108 std.os.sched_yield() catch std.time.sleep(1);
107 }109 }
108110
109 // failed to acquire the lock, go to sleep until woken up by `Held.release()`111 // failed to acquire the lock, go to sleep until woken up by `Held.release()`
110 if (@atomicRmw(u32, &self.state, .Xchg, Sleeping, .Acquire) == Unlocked)112 if (@atomicRmw(State, &self.state, .Xchg, .Sleeping, .Acquire) == .Unlocked)
111 return Held{ .mutex = self };113 return Held{ .mutex = self };
112 state = Sleeping;114 state = .Sleeping;
113 self.parker.park(&self.state, Sleeping);115 self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping));
114 }116 }
115 }117 }
116 };118 };
src/ir.cpp+21-1
...@@ -25621,9 +25621,29 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op...@@ -25621,9 +25621,29 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op
25621 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));25621 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
25622 return ira->codegen->builtin_types.entry_invalid;25622 return ira->codegen->builtin_types.entry_invalid;
25623 }25623 }
25624 } else if (operand_type->id == ZigTypeIdEnum) {
25625 ZigType *int_type = operand_type->data.enumeration.tag_int_type;
25626 if (int_type->data.integral.bit_count < 8) {
25627 ir_add_error(ira, op,
25628 buf_sprintf("expected enum tag type 8 bits or larger, found %" PRIu32 "-bit tag type",
25629 int_type->data.integral.bit_count));
25630 return ira->codegen->builtin_types.entry_invalid;
25631 }
25632 uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
25633 if (int_type->data.integral.bit_count > max_atomic_bits) {
25634 ir_add_error(ira, op,
25635 buf_sprintf("expected %" PRIu32 "-bit enum tag type or smaller, found %" PRIu32 "-bit tag type",
25636 max_atomic_bits, int_type->data.integral.bit_count));
25637 return ira->codegen->builtin_types.entry_invalid;
25638 }
25639 if (!is_power_of_2(int_type->data.integral.bit_count)) {
25640 ir_add_error(ira, op,
25641 buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count));
25642 return ira->codegen->builtin_types.entry_invalid;
25643 }
25624 } else if (get_codegen_ptr_type(operand_type) == nullptr) {25644 } else if (get_codegen_ptr_type(operand_type) == nullptr) {
25625 ir_add_error(ira, op,25645 ir_add_error(ira, op,
25626 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));25646 buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
25627 return ira->codegen->builtin_types.entry_invalid;25647 return ira->codegen->builtin_types.entry_invalid;
25628 }25648 }
2562925649
test/stage1/behavior/atomics.zig+16
...@@ -107,3 +107,19 @@ test "cmpxchg on a global variable" {...@@ -107,3 +107,19 @@ test "cmpxchg on a global variable" {
107 _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);107 _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);
108 expectEqual(@as(u32, 42), a_global_variable);108 expectEqual(@as(u32, 42), a_global_variable);
109}109}
110
111test "atomic load and rmw with enum" {
112 const Value = enum(u8) {
113 a,
114 b,
115 c,
116 };
117 var x = Value.a;
118
119 expect(@atomicLoad(Value, &x, .SeqCst) != .b);
120
121 _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst);
122 expect(@atomicLoad(Value, &x, .SeqCst) == .c);
123 expect(@atomicLoad(Value, &x, .SeqCst) != .a);
124 expect(@atomicLoad(Value, &x, .SeqCst) != .b);
125}