authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-18 12:16:42-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-18 12:16:42-04:00
logf1f998e07124f141312289ff82e0ad8d99af1cf7
tree3c01eb14a0bd8e4cdd83f376097264f45efae2ff
parent96ebd8b23b39e2d4019a8019a6774d7c3d20149d

improve cmpxchg

* remove @cmpxchg, add @cmpxchgWeak and @cmpxchgStrong - See explanations in the langref. * add operand type as first parameter * return type is ?T where T is the operand type closes #461

8 files changed, 148 insertions(+), 63 deletions(-)

doc/langref.html.in+49-5
...@@ -4065,16 +4065,60 @@ comptime {...@@ -4065,16 +4065,60 @@ comptime {
4065 </p>4065 </p>
40664066
4067 {#header_close#}4067 {#header_close#}
4068 {#header_open|@cmpxchg#}4068 {#header_open|@cmpxchgStrong#}
4069 <pre><code class="zig">@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; bool</code></pre>4069 <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4070 <p>4070 <p>
4071 This function performs an atomic compare exchange operation.4071 This function performs a strong atomic compare exchange operation. It's the equivalent of this code,
4072 except atomic:
4073 </p>
4074 {#code_begin|syntax#}
4075fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4076 const old_value = *ptr;
4077 if (old_value == expected_value) {
4078 *ptr = new_value;
4079 return null;
4080 } else {
4081 return old_value;
4082 }
4083}
4084 {#code_end#}
4085 <p>
4086 If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented
4087 more efficiently in machine instructions.
4072 </p>4088 </p>
4073 <p>4089 <p>
4074 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.4090 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4075 </p>4091 </p>
4076 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>4092 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4077 {#see_also|Compile Variables#}4093 {#see_also|Compile Variables|cmpxchgWeak#}
4094 {#header_close#}
4095 {#header_open|@cmpxchgWeak#}
4096 <pre><code class="zig">@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4097 <p>
4098 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
4099 except atomic:
4100 </p>
4101 {#code_begin|syntax#}
4102fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4103 const old_value = *ptr;
4104 if (old_value == expected_value and usuallyTrueButSometimesFalse()) {
4105 *ptr = new_value;
4106 return null;
4107 } else {
4108 return old_value;
4109 }
4110}
4111 {#code_end#}
4112 <p>
4113 If you are using cmpxchg in a loop, the sporadic failure will be no problem, and <code>cmpxchgWeak</code>
4114 is the better choice, because it can be implemented more efficiently in machine instructions.
4115 However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
4116 </p>
4117 <p>
4118 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4119 </p>
4120 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4121 {#see_also|Compile Variables|cmpxchgStrong#}
4078 {#header_close#}4122 {#header_close#}
4079 {#header_open|@compileError#}4123 {#header_open|@compileError#}
4080 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>4124 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>
...@@ -6020,7 +6064,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -6020,7 +6064,7 @@ hljs.registerLanguage("zig", function(t) {
6020 a = t.IR + "\\s*\\(",6064 a = t.IR + "\\s*\\(",
6021 c = {6065 c = {
6022 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",6066 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
6023 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",6067 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt",
6024 literal: "true false null undefined"6068 literal: "true false null undefined"
6025 },6069 },
6026 n = [e, t.CLCM, t.CBCM, s, r];6070 n = [e, t.CLCM, t.CBCM, s, r];
src/all_types.hpp+8-1
...@@ -1310,7 +1310,8 @@ enum BuiltinFnId {...@@ -1310,7 +1310,8 @@ enum BuiltinFnId {
1310 BuiltinFnIdReturnAddress,1310 BuiltinFnIdReturnAddress,
1311 BuiltinFnIdFrameAddress,1311 BuiltinFnIdFrameAddress,
1312 BuiltinFnIdEmbedFile,1312 BuiltinFnIdEmbedFile,
1313 BuiltinFnIdCmpExchange,1313 BuiltinFnIdCmpxchgWeak,
1314 BuiltinFnIdCmpxchgStrong,
1314 BuiltinFnIdFence,1315 BuiltinFnIdFence,
1315 BuiltinFnIdDivExact,1316 BuiltinFnIdDivExact,
1316 BuiltinFnIdDivTrunc,1317 BuiltinFnIdDivTrunc,
...@@ -2528,6 +2529,7 @@ struct IrInstructionEmbedFile {...@@ -2528,6 +2529,7 @@ struct IrInstructionEmbedFile {
2528struct IrInstructionCmpxchg {2529struct IrInstructionCmpxchg {
2529 IrInstruction base;2530 IrInstruction base;
25302531
2532 IrInstruction *type_value;
2531 IrInstruction *ptr;2533 IrInstruction *ptr;
2532 IrInstruction *cmp_value;2534 IrInstruction *cmp_value;
2533 IrInstruction *new_value;2535 IrInstruction *new_value;
...@@ -2535,8 +2537,13 @@ struct IrInstructionCmpxchg {...@@ -2535,8 +2537,13 @@ struct IrInstructionCmpxchg {
2535 IrInstruction *failure_order_value;2537 IrInstruction *failure_order_value;
25362538
2537 // if this instruction gets to runtime then we know these values:2539 // if this instruction gets to runtime then we know these values:
2540 TypeTableEntry *type;
2538 AtomicOrder success_order;2541 AtomicOrder success_order;
2539 AtomicOrder failure_order;2542 AtomicOrder failure_order;
2543
2544 bool is_weak;
2545
2546 LLVMValueRef tmp_ptr;
2540};2547};
25412548
2542struct IrInstructionFence {2549struct IrInstructionFence {
src/codegen.cpp+18-3
...@@ -3558,9 +3558,20 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn...@@ -3558,9 +3558,20 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
3558 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);3558 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
35593559
3560 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,3560 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
3561 success_order, failure_order);3561 success_order, failure_order, instruction->is_weak);
35623562
3563 return LLVMBuildExtractValue(g->builder, result_val, 1, "");3563 assert(instruction->tmp_ptr != nullptr);
3564 assert(type_has_bits(instruction->type));
3565
3566 LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
3567 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
3568 gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
3569
3570 LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
3571 LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
3572 LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
3573 gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
3574 return instruction->tmp_ptr;
3564}3575}
35653576
3566static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {3577static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {
...@@ -5588,6 +5599,9 @@ static void do_code_gen(CodeGen *g) {...@@ -5588,6 +5599,9 @@ static void do_code_gen(CodeGen *g) {
5588 } else if (instruction->id == IrInstructionIdErrWrapCode) {5599 } else if (instruction->id == IrInstructionIdErrWrapCode) {
5589 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;5600 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
5590 slot = &err_wrap_code_instruction->tmp_ptr;5601 slot = &err_wrap_code_instruction->tmp_ptr;
5602 } else if (instruction->id == IrInstructionIdCmpxchg) {
5603 IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
5604 slot = &cmpxchg_instruction->tmp_ptr;
5591 } else {5605 } else {
5592 zig_unreachable();5606 zig_unreachable();
5593 }5607 }
...@@ -6115,7 +6129,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6115,7 +6129,8 @@ static void define_builtin_fns(CodeGen *g) {
6115 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);6129 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
6116 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);6130 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
6117 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);6131 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
6118 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);6132 create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
6133 create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
6119 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);6134 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
6120 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);6135 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
6121 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);6136 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
src/ir.cpp+45-40
...@@ -110,6 +110,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_...@@ -110,6 +110,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);
111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);
113static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
113114
114ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {115ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
115 assert(const_val->type->id == TypeTableEntryIdPointer);116 assert(const_val->type->id == TypeTableEntryIdPointer);
...@@ -1832,38 +1833,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode...@@ -1832,38 +1833,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode
1832 return &instruction->base;1833 return &instruction->base;
1833}1834}
18341835
1835static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr,1836static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value,
1836 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,1837 IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
1837 AtomicOrder success_order, AtomicOrder failure_order)1838 IrInstruction *success_order_value, IrInstruction *failure_order_value,
1839 bool is_weak,
1840 TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order)
1838{1841{
1839 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);1842 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);
1843 instruction->type_value = type_value;
1840 instruction->ptr = ptr;1844 instruction->ptr = ptr;
1841 instruction->cmp_value = cmp_value;1845 instruction->cmp_value = cmp_value;
1842 instruction->new_value = new_value;1846 instruction->new_value = new_value;
1843 instruction->success_order_value = success_order_value;1847 instruction->success_order_value = success_order_value;
1844 instruction->failure_order_value = failure_order_value;1848 instruction->failure_order_value = failure_order_value;
1849 instruction->is_weak = is_weak;
1850 instruction->type = type;
1845 instruction->success_order = success_order;1851 instruction->success_order = success_order;
1846 instruction->failure_order = failure_order;1852 instruction->failure_order = failure_order;
18471853
1854 if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block);
1848 ir_ref_instruction(ptr, irb->current_basic_block);1855 ir_ref_instruction(ptr, irb->current_basic_block);
1849 ir_ref_instruction(cmp_value, irb->current_basic_block);1856 ir_ref_instruction(cmp_value, irb->current_basic_block);
1850 ir_ref_instruction(new_value, irb->current_basic_block);1857 ir_ref_instruction(new_value, irb->current_basic_block);
1851 ir_ref_instruction(success_order_value, irb->current_basic_block);1858 if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block);
1852 ir_ref_instruction(failure_order_value, irb->current_basic_block);1859 if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block);
18531860
1854 return &instruction->base;1861 return &instruction->base;
1855}1862}
18561863
1857static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr,
1858 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
1859 AtomicOrder success_order, AtomicOrder failure_order)
1860{
1861 IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node,
1862 ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order);
1863 ir_link_new_instruction(new_instruction, old_instruction);
1864 return new_instruction;
1865}
1866
1867static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {1864static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
1868 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);1865 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
1869 instruction->order_value = order_value;1866 instruction->order_value = order_value;
...@@ -3771,7 +3768,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3771,7 +3768,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
37713768
3772 return ir_build_embed_file(irb, scope, node, arg0_value);3769 return ir_build_embed_file(irb, scope, node, arg0_value);
3773 }3770 }
3774 case BuiltinFnIdCmpExchange:3771 case BuiltinFnIdCmpxchgWeak:
3772 case BuiltinFnIdCmpxchgStrong:
3775 {3773 {
3776 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3774 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3777 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);3775 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
...@@ -3798,9 +3796,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3798,9 +3796,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3798 if (arg4_value == irb->codegen->invalid_instruction)3796 if (arg4_value == irb->codegen->invalid_instruction)
3799 return arg4_value;3797 return arg4_value;
38003798
3799 AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
3800 IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope);
3801 if (arg5_value == irb->codegen->invalid_instruction)
3802 return arg5_value;
3803
3801 return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,3804 return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,
3802 arg2_value, arg3_value, arg4_value,3805 arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
3803 AtomicOrderUnordered, AtomicOrderUnordered);3806 nullptr, AtomicOrderUnordered, AtomicOrderUnordered);
3804 }3807 }
3805 case BuiltinFnIdFence:3808 case BuiltinFnIdFence:
3806 {3809 {
...@@ -15730,10 +15733,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr...@@ -15730,10 +15733,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
15730}15733}
1573115734
15732static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {15735static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {
15736 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other);
15737 if (type_is_invalid(operand_type))
15738 return ira->codegen->builtin_types.entry_invalid;
15739
15733 IrInstruction *ptr = instruction->ptr->other;15740 IrInstruction *ptr = instruction->ptr->other;
15734 if (type_is_invalid(ptr->value.type))15741 if (type_is_invalid(ptr->value.type))
15735 return ira->codegen->builtin_types.entry_invalid;15742 return ira->codegen->builtin_types.entry_invalid;
1573615743
15744 // TODO let this be volatile
15745 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
15746 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type);
15747 if (type_is_invalid(casted_ptr->value.type))
15748 return ira->codegen->builtin_types.entry_invalid;
15749
15737 IrInstruction *cmp_value = instruction->cmp_value->other;15750 IrInstruction *cmp_value = instruction->cmp_value->other;
15738 if (type_is_invalid(cmp_value->value.type))15751 if (type_is_invalid(cmp_value->value.type))
15739 return ira->codegen->builtin_types.entry_invalid;15752 return ira->codegen->builtin_types.entry_invalid;
...@@ -15758,28 +15771,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15758,28 +15771,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15758 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))15771 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
15759 return ira->codegen->builtin_types.entry_invalid;15772 return ira->codegen->builtin_types.entry_invalid;
1576015773
15761 if (ptr->value.type->id != TypeTableEntryIdPointer) {15774 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type);
15762 ir_add_error(ira, instruction->ptr,
15763 buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name)));
15764 return ira->codegen->builtin_types.entry_invalid;
15765 }
15766
15767 TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type;
15768
15769 uint32_t align_bytes = ptr->value.type->data.pointer.alignment;
15770 uint64_t size_bytes = type_size(ira->codegen, child_type);
15771 if (align_bytes < size_bytes) {
15772 ir_add_error(ira, instruction->ptr,
15773 buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32,
15774 size_bytes, align_bytes));
15775 return ira->codegen->builtin_types.entry_invalid;
15776 }
15777
15778 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type);
15779 if (type_is_invalid(casted_cmp_value->value.type))15775 if (type_is_invalid(casted_cmp_value->value.type))
15780 return ira->codegen->builtin_types.entry_invalid;15776 return ira->codegen->builtin_types.entry_invalid;
1578115777
15782 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type);15778 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type);
15783 if (type_is_invalid(casted_new_value->value.type))15779 if (type_is_invalid(casted_new_value->value.type))
15784 return ira->codegen->builtin_types.entry_invalid;15780 return ira->codegen->builtin_types.entry_invalid;
1578515781
...@@ -15804,9 +15800,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15804,9 +15800,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15804 return ira->codegen->builtin_types.entry_invalid;15800 return ira->codegen->builtin_types.entry_invalid;
15805 }15801 }
1580615802
15807 ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value,15803 if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
15808 success_order_value, failure_order_value, success_order, failure_order);15804 zig_panic("TODO compile-time execution of cmpxchg");
15809 return ira->codegen->builtin_types.entry_bool;15805 }
15806
15807 IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
15808 nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak,
15809 operand_type, success_order, failure_order);
15810 result->value.type = get_maybe_type(ira->codegen, operand_type);
15811 ir_link_new_instruction(result, &instruction->base);
15812 ir_add_alloca(ira, result, result->value.type);
15813 return result->value.type;
15810}15814}
1581115815
15812static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {15816static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
...@@ -17981,6 +17985,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -17981,6 +17985,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
17981 if (type_is_invalid(ptr_inst->value.type))17985 if (type_is_invalid(ptr_inst->value.type))
17982 return ira->codegen->builtin_types.entry_invalid;17986 return ira->codegen->builtin_types.entry_invalid;
1798317987
17988 // TODO let this be volatile
17984 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);17989 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
17985 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);17990 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
17986 if (type_is_invalid(casted_ptr->value.type))17991 if (type_is_invalid(casted_ptr->value.type))
src/zig_llvm.cpp+5-3
...@@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {...@@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
765765
766LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,766LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
767 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,767 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
768 LLVMAtomicOrdering failure_ordering)768 LLVMAtomicOrdering failure_ordering, bool is_weak)
769{769{
770 return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),770 AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
771 mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)));771 unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
772 inst->setWeak(is_weak);
773 return wrap(inst);
772}774}
773775
774LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,776LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
src/zig_llvm.h+1-1
...@@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL...@@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL
6666
67ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,67ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
68 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,68 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
69 LLVMAtomicOrdering failure_ordering);69 LLVMAtomicOrdering failure_ordering, bool is_weak);
7070
71ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,71ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
72 const char *name);72 const char *name);
test/cases/atomics.zig+14-2
...@@ -1,12 +1,24 @@...@@ -1,12 +1,24 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
2const assert = std.debug.assert;
2const builtin = @import("builtin");3const builtin = @import("builtin");
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
4const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
56
6test "cmpxchg" {7test "cmpxchg" {
7 var x: i32 = 1234;8 var x: i32 = 1234;
8 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}9 if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
10 assert(x1 == 1234);
11 } else {
12 @panic("cmpxchg should have failed");
13 }
14
15 while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
16 assert(x1 == 1234);
17 }
9 assert(x == 5678);18 assert(x == 5678);
19
20 assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
21 assert(x == 42);
10}22}
1123
12test "fence" {24test "fence" {
test/compile_errors.zig+8-8
...@@ -1394,17 +1394,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1394,17 +1394,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1394 \\const AtomicOrder = @import("builtin").AtomicOrder;1394 \\const AtomicOrder = @import("builtin").AtomicOrder;
1395 \\export fn f() void {1395 \\export fn f() void {
1396 \\ var x: i32 = 1234;1396 \\ var x: i32 = 1234;
1397 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}1397 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}
1398 \\}1398 \\}
1399 , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success");1399 , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success");
14001400
1401 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",1401 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",
1402 \\const AtomicOrder = @import("builtin").AtomicOrder;1402 \\const AtomicOrder = @import("builtin").AtomicOrder;
1403 \\export fn f() void {1403 \\export fn f() void {
1404 \\ var x: i32 = 1234;1404 \\ var x: i32 = 1234;
1405 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}1405 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}
1406 \\}1406 \\}
1407 , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter");1407 , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter");
14081408
1409 cases.add("negation overflow in function evaluation",1409 cases.add("negation overflow in function evaluation",
1410 \\const y = neg(-128);1410 \\const y = neg(-128);
...@@ -2460,11 +2460,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2460,11 +2460,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2460 \\const AtomicOrder = @import("builtin").AtomicOrder;2460 \\const AtomicOrder = @import("builtin").AtomicOrder;
2461 \\export fn entry() bool {2461 \\export fn entry() bool {
2462 \\ var x: i32 align(1) = 1234;2462 \\ var x: i32 align(1) = 1234;
2463 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}2463 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}
2464 \\ return x == 5678;2464 \\ return x == 5678;
2465 \\}2465 \\}
2466 ,2466 ,
2467 ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1");2467 ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'");
24682468
2469 cases.add("wrong size to an array literal",2469 cases.add("wrong size to an array literal",
2470 \\comptime {2470 \\comptime {
...@@ -2534,10 +2534,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2534,10 +2534,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2534 cases.add("wrong types given to atomic order args in cmpxchg",2534 cases.add("wrong types given to atomic order args in cmpxchg",
2535 \\export fn entry() void {2535 \\export fn entry() void {
2536 \\ var x: i32 = 1234;2536 \\ var x: i32 = 1234;
2537 \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {}2537 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {}
2538 \\}2538 \\}
2539 ,2539 ,
2540 ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'");2540 ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'");
25412541
2542 cases.add("wrong types given to @export",2542 cases.add("wrong types given to @export",
2543 \\extern fn entry() void { }2543 \\extern fn entry() void { }