authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-13 03:06:55+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-11-13 03:06:55+00:00
log8bae70454dabe77dfe7e5344e59ca2180d63af51
treebfb8f584993bf720414f5ab493b42aadd3c24e72
parent32b37e695aa0581b863a395e0a28b7b4aa76c07d
parent41914321b4593e3ed246cadda705e1076ab670d7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3675 from Vexu/atomic-store

Add @atomicStore builtin

16 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, maybe6624 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 restriction6625 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" {
199199
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();
205205
lib/std/atomic/stack.zig+1-1
...@@ -128,7 +128,7 @@ test "std.atomic.stack" {...@@ -128,7 +128,7 @@ test "std.atomic.stack" {
128128
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 {
161161
162 fn dispatch(self: *SelfChannel) void {162 fn dispatch(self: *SelfChannel) void {
163 // set the "need dispatch" flag163 // set the "need dispatch" flag
164 _ = @atomicRmw(u8, &self.need_dispatch, .Xchg, 1, .SeqCst);164 @atomicStore(u8, &self.need_dispatch, 1, .SeqCst);
165165
166 lock: while (true) {166 lock: while (true) {
167 // set the lock flag167 // 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;
170170
171 // clear the need_dispatch flag since we're about to do it171 // 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);
173173
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 }
3232
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);
3636
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 }
5757
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);
6161
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 {
101101
102 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor102 // 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);
105105
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 }
4242
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 }
7373
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);
7676
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 {
113113
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);
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(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 {
144144
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);
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(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 }
183183
...@@ -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 anyway532 // 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) syscall533 // 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 available535 // 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,
1212
13 pub fn release(self: Held) void {13 pub fn release(self: Held) void {
14 // TODO: @atomicStore() https://github.com/ziglang/zig/issues/299514 @atomicStore(u8, &self.spinlock.lock, 0, .Release);
15 assert(@atomicRmw(u8, &self.spinlock.lock, .Xchg, 0, .Release) == 1);
16 }15 }
17 };16 };
1817
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};
37163718
3719struct IrInstructionAtomicStore {
3720 IrInstruction base;
3721
3722 IrInstruction *operand_type;
3723 IrInstruction *ptr;
3724 IrInstruction *value;
3725 IrInstruction *ordering;
3726 AtomicOrder resolved_ordering;
3727};
3728
3717struct IrInstructionSaveErrRetAddr {3729struct 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}
56575657
5658static 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
5658static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) {5669static 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}
10121012
1013static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicStore *) {
1014 return IrInstructionIdAtomicStore;
1015}
1016
1013static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {1017static 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}
31903194
3195static 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
3191static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) {3214static 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}
2585025900
25901static 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
25851static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {25951static 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}
18731875
1876static 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
1874static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) {1897static 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");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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
127test "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
135test "atomic store comptime" {
136 comptime testAtomicStore();
137 testAtomicStore();
138}
139
140fn 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