| author | |
| committer | |
| log | 8bae70454dabe77dfe7e5344e59ca2180d63af51 |
| tree | bfb8f584993bf720414f5ab493b42aadd3c24e72 |
| parent | 32b37e695aa0581b863a395e0a28b7b4aa76c07d |
| parent | 41914321b4593e3ed246cadda705e1076ab670d7 |
| signature |
Add @atomicStore builtin16 files changed, 232 insertions(+), 28 deletions(-)
doc/langref.html.in+22-3| ... | @@ -6612,14 +6612,14 @@ async fn func(y: *i32) void { | ... | @@ -6612,14 +6612,14 @@ async fn func(y: *i32) void { |
| 6612 | This builtin function atomically dereferences a pointer and returns the value. | 6612 | This builtin function atomically dereferences a pointer and returns the value. |
| 6613 | </p> | 6613 | </p> |
| 6614 | <p> | 6614 | <p> |
| 6615 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, | 6615 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} |
| 6616 | or an integer whose bit count meets these requirements: | 6616 | an integer whose bit count meets these requirements: |
| 6617 | </p> | 6617 | </p> |
| 6618 | <ul> | 6618 | <ul> |
| 6619 | <li>At least 8</li> | 6619 | <li>At least 8</li> |
| 6620 | <li>At most the same as usize</li> | 6620 | <li>At most the same as usize</li> |
| 6621 | <li>Power of 2</li> | 6621 | <li>Power of 2</li> |
| 6622 | </ul> | 6622 | </ul> or an enum with a valid integer tag type. |
| 6623 | <p> | 6623 | <p> |
| 6624 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe | 6624 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe |
| 6625 | we can remove this restriction | 6625 | we can remove this restriction |
| ... | @@ -6660,6 +6660,25 @@ async fn func(y: *i32) void { | ... | @@ -6660,6 +6660,25 @@ async fn func(y: *i32) void { |
| 6660 | <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li> | 6660 | <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li> |
| 6661 | </ul> | 6661 | </ul> |
| 6662 | {#header_close#} | 6662 | {#header_close#} |
| 6663 | {#header_open|@atomicStore#} | ||
| 6664 | <pre>{#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}</pre> | ||
| 6665 | <p> | ||
| 6666 | This builtin function atomically stores a value. | ||
| 6667 | </p> | ||
| 6668 | <p> | ||
| 6669 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} | ||
| 6670 | an integer whose bit count meets these requirements: | ||
| 6671 | </p> | ||
| 6672 | <ul> | ||
| 6673 | <li>At least 8</li> | ||
| 6674 | <li>At most the same as usize</li> | ||
| 6675 | <li>Power of 2</li> | ||
| 6676 | </ul> or an enum with a valid integer tag type. | ||
| 6677 | <p> | ||
| 6678 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe | ||
| 6679 | we can remove this restriction | ||
| 6680 | </p> | ||
| 6681 | {#header_close#} | ||
| 6663 | {#header_open|@bitCast#} | 6682 | {#header_open|@bitCast#} |
| 6664 | <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> | 6683 | <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> |
| 6665 | <p> | 6684 | <p> |
lib/std/atomic/queue.zig+1-1| ... | @@ -199,7 +199,7 @@ test "std.atomic.Queue" { | ... | @@ -199,7 +199,7 @@ test "std.atomic.Queue" { |
| 199 | 199 | ||
| 200 | for (putters) |t| | 200 | for (putters) |t| |
| 201 | t.wait(); | 201 | t.wait(); |
| 202 | _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 202 | @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); |
| 203 | for (getters) |t| | 203 | for (getters) |t| |
| 204 | t.wait(); | 204 | t.wait(); |
| 205 | 205 |
lib/std/atomic/stack.zig+1-1| ... | @@ -128,7 +128,7 @@ test "std.atomic.stack" { | ... | @@ -128,7 +128,7 @@ test "std.atomic.stack" { |
| 128 | 128 | ||
| 129 | for (putters) |t| | 129 | for (putters) |t| |
| 130 | t.wait(); | 130 | t.wait(); |
| 131 | _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 131 | @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); |
| 132 | for (getters) |t| | 132 | for (getters) |t| |
| 133 | t.wait(); | 133 | t.wait(); |
| 134 | } | 134 | } |
lib/std/event/channel.zig+2-2| ... | @@ -161,7 +161,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -161,7 +161,7 @@ pub fn Channel(comptime T: type) type { |
| 161 | 161 | ||
| 162 | fn dispatch(self: *SelfChannel) void { | 162 | fn dispatch(self: *SelfChannel) void { |
| 163 | // set the "need dispatch" flag | 163 | // set the "need dispatch" flag |
| 164 | _ = @atomicRmw(u8, &self.need_dispatch, .Xchg, 1, .SeqCst); | 164 | @atomicStore(u8, &self.need_dispatch, 1, .SeqCst); |
| 165 | 165 | ||
| 166 | lock: while (true) { | 166 | lock: while (true) { |
| 167 | // set the lock flag | 167 | // set the lock flag |
| ... | @@ -169,7 +169,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -169,7 +169,7 @@ pub fn Channel(comptime T: type) type { |
| 169 | if (prev_lock != 0) return; | 169 | if (prev_lock != 0) return; |
| 170 | 170 | ||
| 171 | // clear the need_dispatch flag since we're about to do it | 171 | // clear the need_dispatch flag since we're about to do it |
| 172 | _ = @atomicRmw(u8, &self.need_dispatch, .Xchg, 0, .SeqCst); | 172 | @atomicStore(u8, &self.need_dispatch, 0, .SeqCst); |
| 173 | 173 | ||
| 174 | while (true) { | 174 | while (true) { |
| 175 | one_dispatch: { | 175 | one_dispatch: { |
lib/std/event/future.zig+2-2| ... | @@ -62,12 +62,12 @@ pub fn Future(comptime T: type) type { | ... | @@ -62,12 +62,12 @@ pub fn Future(comptime T: type) type { |
| 62 | pub async fn start(self: *Self) ?*T { | 62 | pub async fn start(self: *Self) ?*T { |
| 63 | const state = @cmpxchgStrong(Available, &self.available, .NotStarted, .Started, .SeqCst, .SeqCst) orelse return null; | 63 | const state = @cmpxchgStrong(Available, &self.available, .NotStarted, .Started, .SeqCst, .SeqCst) orelse return null; |
| 64 | switch (state) { | 64 | switch (state) { |
| 65 | 1 => { | 65 | .Started => { |
| 66 | const held = self.lock.acquire(); | 66 | const held = self.lock.acquire(); |
| 67 | held.release(); | 67 | held.release(); |
| 68 | return &self.data; | 68 | return &self.data; |
| 69 | }, | 69 | }, |
| 70 | 2 => return &self.data, | 70 | .Finished => return &self.data, |
| 71 | else => unreachable, | 71 | else => unreachable, |
| 72 | } | 72 | } |
| 73 | } | 73 | } |
lib/std/event/lock.zig+5-5| ... | @@ -31,8 +31,8 @@ pub const Lock = struct { | ... | @@ -31,8 +31,8 @@ pub const Lock = struct { |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | // We need to release the lock. | 33 | // We need to release the lock. |
| 34 | _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst); | 34 | @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); |
| 35 | _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst); | 35 | @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); |
| 36 | 36 | ||
| 37 | // There might be a queue item. If we know the queue is empty, we can be done, | 37 | // There might be a queue item. If we know the queue is empty, we can be done, |
| 38 | // because the other actor will try to obtain the lock. | 38 | // because the other actor will try to obtain the lock. |
| ... | @@ -56,8 +56,8 @@ pub const Lock = struct { | ... | @@ -56,8 +56,8 @@ pub const Lock = struct { |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | // Release the lock again. | 58 | // Release the lock again. |
| 59 | _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst); | 59 | @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); |
| 60 | _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst); | 60 | @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); |
| 61 | 61 | ||
| 62 | // Find out if we can be done. | 62 | // Find out if we can be done. |
| 63 | if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) { | 63 | if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) { |
| ... | @@ -101,7 +101,7 @@ pub const Lock = struct { | ... | @@ -101,7 +101,7 @@ pub const Lock = struct { |
| 101 | 101 | ||
| 102 | // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor | 102 | // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor |
| 103 | // will attempt to grab the lock. | 103 | // will attempt to grab the lock. |
| 104 | _ = @atomicRmw(u8, &self.queue_empty_bit, .Xchg, 0, .SeqCst); | 104 | @atomicStore(u8, &self.queue_empty_bit, 0, .SeqCst); |
| 105 | 105 | ||
| 106 | const old_bit = @atomicRmw(u8, &self.shared_bit, .Xchg, 1, .SeqCst); | 106 | const old_bit = @atomicRmw(u8, &self.shared_bit, .Xchg, 1, .SeqCst); |
| 107 | if (old_bit == 0) { | 107 | if (old_bit == 0) { |
lib/std/event/loop.zig+2-2| ... | @@ -814,7 +814,7 @@ pub const Loop = struct { | ... | @@ -814,7 +814,7 @@ pub const Loop = struct { |
| 814 | _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; | 814 | _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; |
| 815 | }, | 815 | }, |
| 816 | .linux => { | 816 | .linux => { |
| 817 | _ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 817 | @atomicStore(i32, &self.os_data.fs_queue_item, 1, AtomicOrder.SeqCst); |
| 818 | const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1); | 818 | const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1); |
| 819 | switch (os.linux.getErrno(rc)) { | 819 | switch (os.linux.getErrno(rc)) { |
| 820 | 0 => {}, | 820 | 0 => {}, |
| ... | @@ -837,7 +837,7 @@ pub const Loop = struct { | ... | @@ -837,7 +837,7 @@ pub const Loop = struct { |
| 837 | fn posixFsRun(self: *Loop) void { | 837 | fn posixFsRun(self: *Loop) void { |
| 838 | while (true) { | 838 | while (true) { |
| 839 | if (builtin.os == .linux) { | 839 | if (builtin.os == .linux) { |
| 840 | _ = @atomicRmw(i32, &self.os_data.fs_queue_item, .Xchg, 0, .SeqCst); | 840 | @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); |
| 841 | } | 841 | } |
| 842 | while (self.os_data.fs_queue.get()) |node| { | 842 | while (self.os_data.fs_queue.get()) |node| { |
| 843 | switch (node.data.msg) { | 843 | switch (node.data.msg) { |
lib/std/event/rwlock.zig+9-9| ... | @@ -40,7 +40,7 @@ pub const RwLock = struct { | ... | @@ -40,7 +40,7 @@ pub const RwLock = struct { |
| 40 | return; | 40 | return; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst); | 43 | @atomicStore(u8, &self.lock.reader_queue_empty_bit, 1, .SeqCst); |
| 44 | if (@cmpxchgStrong(State, &self.lock.shared_state, .ReadLock, .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; |
| ... | @@ -64,15 +64,15 @@ pub const RwLock = struct { | ... | @@ -64,15 +64,15 @@ 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(State, &self.lock.shared_state, .Xchg, .ReadLock, .SeqCst); | 67 | @atomicStore(State, &self.lock.shared_state, .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 | } |
| 71 | return; | 71 | return; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst); | 74 | @atomicStore(u8, &self.lock.writer_queue_empty_bit, 1, .SeqCst); |
| 75 | _ = @atomicRmw(State, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst); | 75 | @atomicStore(State, &self.lock.shared_state, .Unlocked, .SeqCst); |
| 76 | 76 | ||
| 77 | self.lock.commonPostUnlock(); | 77 | self.lock.commonPostUnlock(); |
| 78 | } | 78 | } |
| ... | @@ -113,7 +113,7 @@ pub const RwLock = struct { | ... | @@ -113,7 +113,7 @@ pub const RwLock = struct { |
| 113 | 113 | ||
| 114 | // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1, | 114 | // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1, |
| 115 | // some actor will attempt to grab the lock. | 115 | // some actor will attempt to grab the lock. |
| 116 | _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 0, .SeqCst); | 116 | @atomicStore(u8, &self.reader_queue_empty_bit, 0, .SeqCst); |
| 117 | 117 | ||
| 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(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst)) |old_state| old_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; |
| ... | @@ -144,7 +144,7 @@ pub const RwLock = struct { | ... | @@ -144,7 +144,7 @@ pub const RwLock = struct { |
| 144 | 144 | ||
| 145 | // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1, | 145 | // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1, |
| 146 | // some actor will attempt to grab the lock. | 146 | // some actor will attempt to grab the lock. |
| 147 | _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 0, .SeqCst); | 147 | @atomicStore(u8, &self.writer_queue_empty_bit, 0, .SeqCst); |
| 148 | 148 | ||
| 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(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) == null) { | 150 | if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) == null) { |
| ... | @@ -176,8 +176,8 @@ pub const RwLock = struct { | ... | @@ -176,8 +176,8 @@ pub const RwLock = struct { |
| 176 | return; | 176 | return; |
| 177 | } | 177 | } |
| 178 | // Release the lock again. | 178 | // Release the lock again. |
| 179 | _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst); | 179 | @atomicStore(u8, &self.writer_queue_empty_bit, 1, .SeqCst); |
| 180 | _ = @atomicRmw(State, &self.shared_state, .Xchg, .Unlocked, .SeqCst); | 180 | @atomicStore(State, &self.shared_state, .Unlocked, .SeqCst); |
| 181 | continue; | 181 | continue; |
| 182 | } | 182 | } |
| 183 | 183 | ||
| ... | @@ -195,7 +195,7 @@ pub const RwLock = struct { | ... | @@ -195,7 +195,7 @@ pub const RwLock = struct { |
| 195 | return; | 195 | return; |
| 196 | } | 196 | } |
| 197 | // Release the lock again. | 197 | // Release the lock again. |
| 198 | _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst); | 198 | @atomicStore(u8, &self.reader_queue_empty_bit, 1, .SeqCst); |
| 199 | if (@cmpxchgStrong(State, &self.shared_state, .ReadLock, .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; |
lib/std/os/linux.zig+1-1| ... | @@ -531,7 +531,7 @@ extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { | ... | @@ -531,7 +531,7 @@ extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { |
| 531 | const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); | 531 | const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); |
| 532 | // Note that we may not have a VDSO at all, update the stub address anyway | 532 | // Note that we may not have a VDSO at all, update the stub address anyway |
| 533 | // so that clock_gettime will fall back on the good old (and slow) syscall | 533 | // so that clock_gettime will fall back on the good old (and slow) syscall |
| 534 | _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic); | 534 | @atomicStore(?*const c_void, &vdso_clock_gettime, ptr, .Monotonic); |
| 535 | // Call into the VDSO if available | 535 | // Call into the VDSO if available |
| 536 | if (ptr) |fn_ptr| { | 536 | if (ptr) |fn_ptr| { |
| 537 | const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); | 537 | const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); |
lib/std/spinlock.zig+1-2| ... | @@ -11,8 +11,7 @@ pub const SpinLock = struct { | ... | @@ -11,8 +11,7 @@ pub const SpinLock = struct { |
| 11 | spinlock: *SpinLock, | 11 | spinlock: *SpinLock, |
| 12 | 12 | ||
| 13 | pub fn release(self: Held) void { | 13 | pub fn release(self: Held) void { |
| 14 | // TODO: @atomicStore() https://github.com/ziglang/zig/issues/2995 | 14 | @atomicStore(u8, &self.spinlock.lock, 0, .Release); |
| 15 | assert(@atomicRmw(u8, &self.spinlock.lock, .Xchg, 0, .Release) == 1); | ||
| 16 | } | 15 | } |
| 17 | }; | 16 | }; |
| 18 | 17 |
src/all_types.hpp+12| ... | @@ -1700,6 +1700,7 @@ enum BuiltinFnId { | ... | @@ -1700,6 +1700,7 @@ enum BuiltinFnId { |
| 1700 | BuiltinFnIdErrorReturnTrace, | 1700 | BuiltinFnIdErrorReturnTrace, |
| 1701 | BuiltinFnIdAtomicRmw, | 1701 | BuiltinFnIdAtomicRmw, |
| 1702 | BuiltinFnIdAtomicLoad, | 1702 | BuiltinFnIdAtomicLoad, |
| 1703 | BuiltinFnIdAtomicStore, | ||
| 1703 | BuiltinFnIdHasDecl, | 1704 | BuiltinFnIdHasDecl, |
| 1704 | BuiltinFnIdUnionInit, | 1705 | BuiltinFnIdUnionInit, |
| 1705 | BuiltinFnIdFrameAddress, | 1706 | BuiltinFnIdFrameAddress, |
| ... | @@ -2569,6 +2570,7 @@ enum IrInstructionId { | ... | @@ -2569,6 +2570,7 @@ enum IrInstructionId { |
| 2569 | IrInstructionIdErrorUnion, | 2570 | IrInstructionIdErrorUnion, |
| 2570 | IrInstructionIdAtomicRmw, | 2571 | IrInstructionIdAtomicRmw, |
| 2571 | IrInstructionIdAtomicLoad, | 2572 | IrInstructionIdAtomicLoad, |
| 2573 | IrInstructionIdAtomicStore, | ||
| 2572 | IrInstructionIdSaveErrRetAddr, | 2574 | IrInstructionIdSaveErrRetAddr, |
| 2573 | IrInstructionIdAddImplicitReturnType, | 2575 | IrInstructionIdAddImplicitReturnType, |
| 2574 | IrInstructionIdErrSetCast, | 2576 | IrInstructionIdErrSetCast, |
| ... | @@ -3714,6 +3716,16 @@ struct IrInstructionAtomicLoad { | ... | @@ -3714,6 +3716,16 @@ struct IrInstructionAtomicLoad { |
| 3714 | AtomicOrder resolved_ordering; | 3716 | AtomicOrder resolved_ordering; |
| 3715 | }; | 3717 | }; |
| 3716 | 3718 | ||
| 3719 | struct IrInstructionAtomicStore { | ||
| 3720 | IrInstruction base; | ||
| 3721 | |||
| 3722 | IrInstruction *operand_type; | ||
| 3723 | IrInstruction *ptr; | ||
| 3724 | IrInstruction *value; | ||
| 3725 | IrInstruction *ordering; | ||
| 3726 | AtomicOrder resolved_ordering; | ||
| 3727 | }; | ||
| 3728 | |||
| 3717 | struct IrInstructionSaveErrRetAddr { | 3729 | struct IrInstructionSaveErrRetAddr { |
| 3718 | IrInstruction base; | 3730 | IrInstruction base; |
| 3719 | }; | 3731 | }; |
src/codegen.cpp+14| ... | @@ -5655,6 +5655,17 @@ static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, | ... | @@ -5655,6 +5655,17 @@ static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, |
| 5655 | return load_inst; | 5655 | return load_inst; |
| 5656 | } | 5656 | } |
| 5657 | 5657 | ||
| 5658 | static LLVMValueRef ir_render_atomic_store(CodeGen *g, IrExecutable *executable, | ||
| 5659 | IrInstructionAtomicStore *instruction) | ||
| 5660 | { | ||
| 5661 | LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); | ||
| 5662 | LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); | ||
| 5663 | LLVMValueRef value = ir_llvm_value(g, instruction->value); | ||
| 5664 | LLVMValueRef store_inst = gen_store(g, value, ptr, instruction->ptr->value.type); | ||
| 5665 | LLVMSetOrdering(store_inst, ordering); | ||
| 5666 | return nullptr; | ||
| 5667 | } | ||
| 5668 | |||
| 5658 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { | 5669 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { |
| 5659 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); | 5670 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); |
| 5660 | assert(instruction->base.value.type->id == ZigTypeIdFloat); | 5671 | assert(instruction->base.value.type->id == ZigTypeIdFloat); |
| ... | @@ -6258,6 +6269,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -6258,6 +6269,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 6258 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); | 6269 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); |
| 6259 | case IrInstructionIdAtomicLoad: | 6270 | case IrInstructionIdAtomicLoad: |
| 6260 | return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); | 6271 | return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); |
| 6272 | case IrInstructionIdAtomicStore: | ||
| 6273 | return ir_render_atomic_store(g, executable, (IrInstructionAtomicStore *)instruction); | ||
| 6261 | case IrInstructionIdSaveErrRetAddr: | 6274 | case IrInstructionIdSaveErrRetAddr: |
| 6262 | return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); | 6275 | return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); |
| 6263 | case IrInstructionIdFloatOp: | 6276 | case IrInstructionIdFloatOp: |
| ... | @@ -8074,6 +8087,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -8074,6 +8087,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8074 | create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); | 8087 | create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); |
| 8075 | create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); | 8088 | create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); |
| 8076 | create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); | 8089 | create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); |
| 8090 | create_builtin_fn(g, BuiltinFnIdAtomicStore, "atomicStore", 4); | ||
| 8077 | create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); | 8091 | create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); |
| 8078 | create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); | 8092 | create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); |
| 8079 | create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); | 8093 | create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); |
src/ir.cpp+103| ... | @@ -1010,6 +1010,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { | ... | @@ -1010,6 +1010,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { |
| 1010 | return IrInstructionIdAtomicLoad; | 1010 | return IrInstructionIdAtomicLoad; |
| 1011 | } | 1011 | } |
| 1012 | 1012 | ||
| 1013 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicStore *) { | ||
| 1014 | return IrInstructionIdAtomicStore; | ||
| 1015 | } | ||
| 1016 | |||
| 1013 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { | 1017 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { |
| 1014 | return IrInstructionIdSaveErrRetAddr; | 1018 | return IrInstructionIdSaveErrRetAddr; |
| 1015 | } | 1019 | } |
| ... | @@ -3188,6 +3192,25 @@ static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -3188,6 +3192,25 @@ static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode |
| 3188 | return &instruction->base; | 3192 | return &instruction->base; |
| 3189 | } | 3193 | } |
| 3190 | 3194 | ||
| 3195 | static IrInstruction *ir_build_atomic_store(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 3196 | IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *value, | ||
| 3197 | IrInstruction *ordering, AtomicOrder resolved_ordering) | ||
| 3198 | { | ||
| 3199 | IrInstructionAtomicStore *instruction = ir_build_instruction<IrInstructionAtomicStore>(irb, scope, source_node); | ||
| 3200 | instruction->operand_type = operand_type; | ||
| 3201 | instruction->ptr = ptr; | ||
| 3202 | instruction->value = value; | ||
| 3203 | instruction->ordering = ordering; | ||
| 3204 | instruction->resolved_ordering = resolved_ordering; | ||
| 3205 | |||
| 3206 | if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); | ||
| 3207 | ir_ref_instruction(ptr, irb->current_basic_block); | ||
| 3208 | ir_ref_instruction(value, irb->current_basic_block); | ||
| 3209 | if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); | ||
| 3210 | |||
| 3211 | return &instruction->base; | ||
| 3212 | } | ||
| 3213 | |||
| 3191 | static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 3214 | static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 3192 | IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node); | 3215 | IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node); |
| 3193 | return &instruction->base; | 3216 | return &instruction->base; |
| ... | @@ -5732,6 +5755,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -5732,6 +5755,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5732 | AtomicOrderMonotonic); | 5755 | AtomicOrderMonotonic); |
| 5733 | return ir_lval_wrap(irb, scope, inst, lval, result_loc); | 5756 | return ir_lval_wrap(irb, scope, inst, lval, result_loc); |
| 5734 | } | 5757 | } |
| 5758 | case BuiltinFnIdAtomicStore: | ||
| 5759 | { | ||
| 5760 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 5761 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 5762 | if (arg0_value == irb->codegen->invalid_instruction) | ||
| 5763 | return arg0_value; | ||
| 5764 | |||
| 5765 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | ||
| 5766 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | ||
| 5767 | if (arg1_value == irb->codegen->invalid_instruction) | ||
| 5768 | return arg1_value; | ||
| 5769 | |||
| 5770 | AstNode *arg2_node = node->data.fn_call_expr.params.at(2); | ||
| 5771 | IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); | ||
| 5772 | if (arg2_value == irb->codegen->invalid_instruction) | ||
| 5773 | return arg2_value; | ||
| 5774 | |||
| 5775 | AstNode *arg3_node = node->data.fn_call_expr.params.at(3); | ||
| 5776 | IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); | ||
| 5777 | if (arg3_value == irb->codegen->invalid_instruction) | ||
| 5778 | return arg3_value; | ||
| 5779 | |||
| 5780 | IrInstruction *inst = ir_build_atomic_store(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, | ||
| 5781 | // this value does not mean anything since we passed non-null values for other arg | ||
| 5782 | AtomicOrderMonotonic); | ||
| 5783 | return ir_lval_wrap(irb, scope, inst, lval, result_loc); | ||
| 5784 | } | ||
| 5735 | case BuiltinFnIdIntToEnum: | 5785 | case BuiltinFnIdIntToEnum: |
| 5736 | { | 5786 | { |
| 5737 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 5787 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | @@ -25848,6 +25898,56 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr | ... | @@ -25848,6 +25898,56 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr |
| 25848 | return result; | 25898 | return result; |
| 25849 | } | 25899 | } |
| 25850 | 25900 | ||
| 25901 | static IrInstruction *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstructionAtomicStore *instruction) { | ||
| 25902 | ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); | ||
| 25903 | if (type_is_invalid(operand_type)) | ||
| 25904 | return ira->codegen->invalid_instruction; | ||
| 25905 | |||
| 25906 | IrInstruction *ptr_inst = instruction->ptr->child; | ||
| 25907 | if (type_is_invalid(ptr_inst->value.type)) | ||
| 25908 | return ira->codegen->invalid_instruction; | ||
| 25909 | |||
| 25910 | ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); | ||
| 25911 | IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); | ||
| 25912 | if (type_is_invalid(casted_ptr->value.type)) | ||
| 25913 | return ira->codegen->invalid_instruction; | ||
| 25914 | |||
| 25915 | IrInstruction *value = instruction->value->child; | ||
| 25916 | if (type_is_invalid(value->value.type)) | ||
| 25917 | return ira->codegen->invalid_instruction; | ||
| 25918 | |||
| 25919 | IrInstruction *casted_value = ir_implicit_cast(ira, value, operand_type); | ||
| 25920 | if (type_is_invalid(casted_value->value.type)) | ||
| 25921 | return ira->codegen->invalid_instruction; | ||
| 25922 | |||
| 25923 | |||
| 25924 | AtomicOrder ordering; | ||
| 25925 | if (instruction->ordering == nullptr) { | ||
| 25926 | ordering = instruction->resolved_ordering; | ||
| 25927 | } else { | ||
| 25928 | if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) | ||
| 25929 | return ira->codegen->invalid_instruction; | ||
| 25930 | } | ||
| 25931 | |||
| 25932 | if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { | ||
| 25933 | ir_assert(instruction->ordering != nullptr, &instruction->base); | ||
| 25934 | ir_add_error(ira, instruction->ordering, | ||
| 25935 | buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); | ||
| 25936 | return ira->codegen->invalid_instruction; | ||
| 25937 | } | ||
| 25938 | |||
| 25939 | if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { | ||
| 25940 | IrInstruction *result = ir_analyze_store_ptr(ira, &instruction->base, casted_ptr, value, false); | ||
| 25941 | result->value.type = ira->codegen->builtin_types.entry_void; | ||
| 25942 | return result; | ||
| 25943 | } | ||
| 25944 | |||
| 25945 | IrInstruction *result = ir_build_atomic_store(&ira->new_irb, instruction->base.scope, | ||
| 25946 | instruction->base.source_node, nullptr, casted_ptr, casted_value, nullptr, ordering); | ||
| 25947 | result->value.type = ira->codegen->builtin_types.entry_void; | ||
| 25948 | return result; | ||
| 25949 | } | ||
| 25950 | |||
| 25851 | static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { | 25951 | static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { |
| 25852 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, | 25952 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, |
| 25853 | instruction->base.source_node); | 25953 | instruction->base.source_node); |
| ... | @@ -26882,6 +26982,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction | ... | @@ -26882,6 +26982,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 26882 | return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); | 26982 | return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); |
| 26883 | case IrInstructionIdAtomicLoad: | 26983 | case IrInstructionIdAtomicLoad: |
| 26884 | return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); | 26984 | return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); |
| 26985 | case IrInstructionIdAtomicStore: | ||
| 26986 | return ir_analyze_instruction_atomic_store(ira, (IrInstructionAtomicStore *)instruction); | ||
| 26885 | case IrInstructionIdSaveErrRetAddr: | 26987 | case IrInstructionIdSaveErrRetAddr: |
| 26886 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); | 26988 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); |
| 26887 | case IrInstructionIdAddImplicitReturnType: | 26989 | case IrInstructionIdAddImplicitReturnType: |
| ... | @@ -27062,6 +27164,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -27062,6 +27164,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 27062 | case IrInstructionIdSaveErrRetAddr: | 27164 | case IrInstructionIdSaveErrRetAddr: |
| 27063 | case IrInstructionIdAddImplicitReturnType: | 27165 | case IrInstructionIdAddImplicitReturnType: |
| 27064 | case IrInstructionIdAtomicRmw: | 27166 | case IrInstructionIdAtomicRmw: |
| 27167 | case IrInstructionIdAtomicStore: | ||
| 27065 | case IrInstructionIdCmpxchgGen: | 27168 | case IrInstructionIdCmpxchgGen: |
| 27066 | case IrInstructionIdCmpxchgSrc: | 27169 | case IrInstructionIdCmpxchgSrc: |
| 27067 | case IrInstructionIdAssertZero: | 27170 | case IrInstructionIdAssertZero: |
src/ir_print.cpp+26| ... | @@ -324,6 +324,8 @@ const char* ir_instruction_type_str(IrInstructionId id) { | ... | @@ -324,6 +324,8 @@ const char* ir_instruction_type_str(IrInstructionId id) { |
| 324 | return "AtomicRmw"; | 324 | return "AtomicRmw"; |
| 325 | case IrInstructionIdAtomicLoad: | 325 | case IrInstructionIdAtomicLoad: |
| 326 | return "AtomicLoad"; | 326 | return "AtomicLoad"; |
| 327 | case IrInstructionIdAtomicStore: | ||
| 328 | return "AtomicStore"; | ||
| 327 | case IrInstructionIdSaveErrRetAddr: | 329 | case IrInstructionIdSaveErrRetAddr: |
| 328 | return "SaveErrRetAddr"; | 330 | return "SaveErrRetAddr"; |
| 329 | case IrInstructionIdAddImplicitReturnType: | 331 | case IrInstructionIdAddImplicitReturnType: |
| ... | @@ -1871,6 +1873,27 @@ static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruct | ... | @@ -1871,6 +1873,27 @@ static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruct |
| 1871 | fprintf(irp->f, ")"); | 1873 | fprintf(irp->f, ")"); |
| 1872 | } | 1874 | } |
| 1873 | 1875 | ||
| 1876 | static void ir_print_atomic_store(IrPrint *irp, IrInstructionAtomicStore *instruction) { | ||
| 1877 | fprintf(irp->f, "@atomicStore("); | ||
| 1878 | if (instruction->operand_type != nullptr) { | ||
| 1879 | ir_print_other_instruction(irp, instruction->operand_type); | ||
| 1880 | } else { | ||
| 1881 | fprintf(irp->f, "[TODO print]"); | ||
| 1882 | } | ||
| 1883 | fprintf(irp->f, ","); | ||
| 1884 | ir_print_other_instruction(irp, instruction->ptr); | ||
| 1885 | fprintf(irp->f, ","); | ||
| 1886 | ir_print_other_instruction(irp, instruction->value); | ||
| 1887 | fprintf(irp->f, ","); | ||
| 1888 | if (instruction->ordering != nullptr) { | ||
| 1889 | ir_print_other_instruction(irp, instruction->ordering); | ||
| 1890 | } else { | ||
| 1891 | fprintf(irp->f, "[TODO print]"); | ||
| 1892 | } | ||
| 1893 | fprintf(irp->f, ")"); | ||
| 1894 | } | ||
| 1895 | |||
| 1896 | |||
| 1874 | static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) { | 1897 | static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) { |
| 1875 | fprintf(irp->f, "@saveErrRetAddr()"); | 1898 | fprintf(irp->f, "@saveErrRetAddr()"); |
| 1876 | } | 1899 | } |
| ... | @@ -2431,6 +2454,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool | ... | @@ -2431,6 +2454,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool |
| 2431 | case IrInstructionIdAtomicLoad: | 2454 | case IrInstructionIdAtomicLoad: |
| 2432 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); | 2455 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); |
| 2433 | break; | 2456 | break; |
| 2457 | case IrInstructionIdAtomicStore: | ||
| 2458 | ir_print_atomic_store(irp, (IrInstructionAtomicStore *)instruction); | ||
| 2459 | break; | ||
| 2434 | case IrInstructionIdEnumToInt: | 2460 | case IrInstructionIdEnumToInt: |
| 2435 | ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); | 2461 | ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); |
| 2436 | break; | 2462 | break; |
test/compile_errors.zig+10| ... | @@ -2,6 +2,16 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,16 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | ||
| 6 | "atomic orderings of atomicStore Acquire or AcqRel", | ||
| 7 | \\export fn entry() void { | ||
| 8 | \\ var x: u32 = 0; | ||
| 9 | \\ @atomicStore(u32, &x, 1, .Acquire); | ||
| 10 | \\} | ||
| 11 | , | ||
| 12 | "tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel", | ||
| 13 | ); | ||
| 14 | |||
| 5 | cases.add( | 15 | cases.add( |
| 6 | "missing const in slice with nested array type", | 16 | "missing const in slice with nested array type", |
| 7 | \\const Geo3DTex2D = struct { vertices: [][2]f32 }; | 17 | \\const Geo3DTex2D = struct { vertices: [][2]f32 }; |
test/stage1/behavior/atomics.zig+21| ... | @@ -123,3 +123,24 @@ test "atomic load and rmw with enum" { | ... | @@ -123,3 +123,24 @@ test "atomic load and rmw with enum" { |
| 123 | expect(@atomicLoad(Value, &x, .SeqCst) != .a); | 123 | expect(@atomicLoad(Value, &x, .SeqCst) != .a); |
| 124 | expect(@atomicLoad(Value, &x, .SeqCst) != .b); | 124 | expect(@atomicLoad(Value, &x, .SeqCst) != .b); |
| 125 | } | 125 | } |
| 126 | |||
| 127 | test "atomic store" { | ||
| 128 | var x: u32 = 0; | ||
| 129 | @atomicStore(u32, &x, 1, .SeqCst); | ||
| 130 | expect(@atomicLoad(u32, &x, .SeqCst) == 1); | ||
| 131 | @atomicStore(u32, &x, 12345678, .SeqCst); | ||
| 132 | expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | ||
| 133 | } | ||
| 134 | |||
| 135 | test "atomic store comptime" { | ||
| 136 | comptime testAtomicStore(); | ||
| 137 | testAtomicStore(); | ||
| 138 | } | ||
| 139 | |||
| 140 | fn testAtomicStore() void { | ||
| 141 | var x: u32 = 0; | ||
| 142 | @atomicStore(u32, &x, 1, .SeqCst); | ||
| 143 | expect(@atomicLoad(u32, &x, .SeqCst) == 1); | ||
| 144 | @atomicStore(u32, &x, 12345678, .SeqCst); | ||
| 145 | expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | ||
| 146 | } | ||
| \ No newline at end of file | |||