authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-28 21:19:51-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-28 21:19:51-05:00
log807a5e94e976f03058426e04dceef449a5bf7ed8
tree3717696ae449bcc5c5a83ca4e1da965a63de864d
parent36eadb569a31a87b610b9b70e225a981dc181df4

add atomicrmw builtin function


7 files changed, 297 insertions(+), 12 deletions(-)

doc/langref.html.in+20-1
...@@ -3775,6 +3775,25 @@ pub fn main() void {...@@ -3775,6 +3775,25 @@ pub fn main() void {
3775 {#header_open|@ArgType#}3775 {#header_open|@ArgType#}
3776 <p>TODO</p>3776 <p>TODO</p>
3777 {#header_close#}3777 {#header_close#}
3778 {#header_open|@atomicRmw#}
3779 <pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3780 <p>
3781 This builtin function atomically modifies memory and then returns the previous value.
3782 </p>
3783 <p>
3784 <code>T</code> must be a pointer type, a <code>bool</code>,
3785 or an integer whose bit count meets these requirements:
3786 </p>
3787 <ul>
3788 <li>At least 8</li>
3789 <li>At most the same as usize</li>
3790 <li>Power of 2</li>
3791 </ul>
3792 <p>
3793 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
3794 we can remove this restriction
3795 </p>
3796 {#header_close#}
3778 {#header_open|@bitCast#}3797 {#header_open|@bitCast#}
3779 <pre><code class="zig">@bitCast(comptime DestType: type, value: var) -&gt; DestType</code></pre>3798 <pre><code class="zig">@bitCast(comptime DestType: type, value: var) -&gt; DestType</code></pre>
3780 <p>3799 <p>
...@@ -5859,7 +5878,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -5859,7 +5878,7 @@ hljs.registerLanguage("zig", function(t) {
5859 a = t.IR + "\\s*\\(",5878 a = t.IR + "\\s*\\(",
5860 c = {5879 c = {
5861 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",5880 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",
5862 built_in: "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",5881 built_in: "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",
5863 literal: "true false null undefined"5882 literal: "true false null undefined"
5864 },5883 },
5865 n = [e, t.CLCM, t.CBCM, s, r];5884 n = [e, t.CLCM, t.CBCM, s, r];
src/all_types.hpp+27
...@@ -1338,6 +1338,7 @@ enum BuiltinFnId {...@@ -1338,6 +1338,7 @@ enum BuiltinFnId {
1338 BuiltinFnIdArgType,1338 BuiltinFnIdArgType,
1339 BuiltinFnIdExport,1339 BuiltinFnIdExport,
1340 BuiltinFnIdErrorReturnTrace,1340 BuiltinFnIdErrorReturnTrace,
1341 BuiltinFnIdAtomicRmw,
1341};1342};
13421343
1343struct BuiltinFnEntry {1344struct BuiltinFnEntry {
...@@ -1857,6 +1858,19 @@ enum AtomicOrder {...@@ -1857,6 +1858,19 @@ enum AtomicOrder {
1857 AtomicOrderSeqCst,1858 AtomicOrderSeqCst,
1858};1859};
18591860
1861// synchronized with the code in define_builtin_compile_vars
1862enum AtomicRmwOp {
1863 AtomicRmwOp_xchg,
1864 AtomicRmwOp_add,
1865 AtomicRmwOp_sub,
1866 AtomicRmwOp_and,
1867 AtomicRmwOp_nand,
1868 AtomicRmwOp_or,
1869 AtomicRmwOp_xor,
1870 AtomicRmwOp_max,
1871 AtomicRmwOp_min,
1872};
1873
1860// A basic block contains no branching. Branches send control flow1874// A basic block contains no branching. Branches send control flow
1861// to another basic block.1875// to another basic block.
1862// Phi instructions must be first in a basic block.1876// Phi instructions must be first in a basic block.
...@@ -2006,6 +2020,7 @@ enum IrInstructionId {...@@ -2006,6 +2020,7 @@ enum IrInstructionId {
2006 IrInstructionIdCoroResume,2020 IrInstructionIdCoroResume,
2007 IrInstructionIdCoroSave,2021 IrInstructionIdCoroSave,
2008 IrInstructionIdCoroAllocHelper,2022 IrInstructionIdCoroAllocHelper,
2023 IrInstructionIdAtomicRmw,
2009};2024};
20102025
2011struct IrInstruction {2026struct IrInstruction {
...@@ -2929,6 +2944,18 @@ struct IrInstructionCoroAllocHelper {...@@ -2929,6 +2944,18 @@ struct IrInstructionCoroAllocHelper {
2929 IrInstruction *coro_size;2944 IrInstruction *coro_size;
2930};2945};
29312946
2947struct IrInstructionAtomicRmw {
2948 IrInstruction base;
2949
2950 IrInstruction *operand_type;
2951 IrInstruction *ptr;
2952 IrInstruction *op;
2953 AtomicRmwOp resolved_op;
2954 IrInstruction *operand;
2955 IrInstruction *ordering;
2956 AtomicOrder resolved_ordering;
2957};
2958
2932static const size_t slice_ptr_index = 0;2959static const size_t slice_ptr_index = 0;
2933static const size_t slice_len_index = 1;2960static const size_t slice_len_index = 1;
29342961
src/codegen.cpp+50
...@@ -3311,6 +3311,23 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {...@@ -3311,6 +3311,23 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
3311 zig_unreachable();3311 zig_unreachable();
3312}3312}
33133313
3314static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) {
3315 switch (op) {
3316 case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg;
3317 case AtomicRmwOp_add: return LLVMAtomicRMWBinOpAdd;
3318 case AtomicRmwOp_sub: return LLVMAtomicRMWBinOpSub;
3319 case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd;
3320 case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand;
3321 case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr;
3322 case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor;
3323 case AtomicRmwOp_max:
3324 return is_signed ? LLVMAtomicRMWBinOpMax : LLVMAtomicRMWBinOpUMax;
3325 case AtomicRmwOp_min:
3326 return is_signed ? LLVMAtomicRMWBinOpMin : LLVMAtomicRMWBinOpUMin;
3327 }
3328 zig_unreachable();
3329}
3330
3314static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) {3331static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) {
3315 LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr);3332 LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr);
3316 LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value);3333 LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value);
...@@ -4111,6 +4128,22 @@ static LLVMValueRef ir_render_coro_alloc_helper(CodeGen *g, IrExecutable *execut...@@ -4111,6 +4128,22 @@ static LLVMValueRef ir_render_coro_alloc_helper(CodeGen *g, IrExecutable *execut
4111 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");4128 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
4112}4129}
41134130
4131static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
4132 IrInstructionAtomicRmw *instruction)
4133{
4134 bool is_signed;
4135 if (instruction->operand->value.type->id == TypeTableEntryIdInt) {
4136 is_signed = instruction->operand->value.type->data.integral.is_signed;
4137 } else {
4138 is_signed = false;
4139 }
4140 LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed);
4141 LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
4142 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
4143 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
4144 return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, false);
4145}
4146
4114static void set_debug_location(CodeGen *g, IrInstruction *instruction) {4147static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
4115 AstNode *source_node = instruction->source_node;4148 AstNode *source_node = instruction->source_node;
4116 Scope *scope = instruction->scope;4149 Scope *scope = instruction->scope;
...@@ -4318,6 +4351,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4318,6 +4351,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4318 return ir_render_coro_save(g, executable, (IrInstructionCoroSave *)instruction);4351 return ir_render_coro_save(g, executable, (IrInstructionCoroSave *)instruction);
4319 case IrInstructionIdCoroAllocHelper:4352 case IrInstructionIdCoroAllocHelper:
4320 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);4353 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
4354 case IrInstructionIdAtomicRmw:
4355 return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
4321 }4356 }
4322 zig_unreachable();4357 zig_unreachable();
4323}4358}
...@@ -5810,6 +5845,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5810,6 +5845,7 @@ static void define_builtin_fns(CodeGen *g) {
5810 create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);5845 create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
5811 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);5846 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
5812 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);5847 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
5848 create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
5813}5849}
58145850
5815static const char *bool_to_str(bool b) {5851static const char *bool_to_str(bool b) {
...@@ -5939,6 +5975,20 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -5939,6 +5975,20 @@ static void define_builtin_compile_vars(CodeGen *g) {
5939 " SeqCst,\n"5975 " SeqCst,\n"
5940 "};\n\n");5976 "};\n\n");
5941 }5977 }
5978 {
5979 buf_appendf(contents,
5980 "pub const AtomicRmwOp = enum {\n"
5981 " Xchg,\n"
5982 " Add,\n"
5983 " Sub,\n"
5984 " And,\n"
5985 " Nand,\n"
5986 " Or,\n"
5987 " Xor,\n"
5988 " Max,\n"
5989 " Min,\n"
5990 "};\n\n");
5991 }
5942 {5992 {
5943 buf_appendf(contents,5993 buf_appendf(contents,
5944 "pub const Mode = enum {\n"5994 "pub const Mode = enum {\n"
src/ir.cpp+149
...@@ -701,6 +701,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocHelper...@@ -701,6 +701,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocHelper
701 return IrInstructionIdCoroAllocHelper;701 return IrInstructionIdCoroAllocHelper;
702}702}
703703
704static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
705 return IrInstructionIdAtomicRmw;
706}
707
704template<typename T>708template<typename T>
705static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {709static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
706 T *special_instruction = allocate<T>(1);710 T *special_instruction = allocate<T>(1);
...@@ -2614,6 +2618,28 @@ static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, A...@@ -2614,6 +2618,28 @@ static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, A
2614 return &instruction->base;2618 return &instruction->base;
2615}2619}
26162620
2621static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode *source_node,
2622 IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *op, IrInstruction *operand,
2623 IrInstruction *ordering, AtomicRmwOp resolved_op, AtomicOrder resolved_ordering)
2624{
2625 IrInstructionAtomicRmw *instruction = ir_build_instruction<IrInstructionAtomicRmw>(irb, scope, source_node);
2626 instruction->operand_type = operand_type;
2627 instruction->ptr = ptr;
2628 instruction->op = op;
2629 instruction->operand = operand;
2630 instruction->ordering = ordering;
2631 instruction->resolved_op = resolved_op;
2632 instruction->resolved_ordering = resolved_ordering;
2633
2634 if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
2635 ir_ref_instruction(ptr, irb->current_basic_block);
2636 if (op != nullptr) ir_ref_instruction(op, irb->current_basic_block);
2637 ir_ref_instruction(operand, irb->current_basic_block);
2638 if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
2639
2640 return &instruction->base;
2641}
2642
2617static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2643static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2618 results[ReturnKindUnconditional] = 0;2644 results[ReturnKindUnconditional] = 0;
2619 results[ReturnKindError] = 0;2645 results[ReturnKindError] = 0;
...@@ -4094,6 +4120,38 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4094,6 +4120,38 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4094 {4120 {
4095 return ir_build_error_return_trace(irb, scope, node);4121 return ir_build_error_return_trace(irb, scope, node);
4096 }4122 }
4123 case BuiltinFnIdAtomicRmw:
4124 {
4125 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4126 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4127 if (arg0_value == irb->codegen->invalid_instruction)
4128 return arg0_value;
4129
4130 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4131 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4132 if (arg1_value == irb->codegen->invalid_instruction)
4133 return arg1_value;
4134
4135 AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
4136 IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
4137 if (arg2_value == irb->codegen->invalid_instruction)
4138 return arg2_value;
4139
4140 AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
4141 IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope);
4142 if (arg3_value == irb->codegen->invalid_instruction)
4143 return arg3_value;
4144
4145 AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
4146 IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope);
4147 if (arg4_value == irb->codegen->invalid_instruction)
4148 return arg4_value;
4149
4150 return ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value,
4151 arg4_value,
4152 // these 2 values don't mean anything since we passed non-null values for other args
4153 AtomicRmwOp_xchg, AtomicOrderMonotonic);
4154 }
4097 }4155 }
4098 zig_unreachable();4156 zig_unreachable();
4099}4157}
...@@ -9730,6 +9788,26 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic...@@ -9730,6 +9788,26 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic
9730 return true;9788 return true;
9731}9789}
97329790
9791static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, AtomicRmwOp *out) {
9792 if (type_is_invalid(value->value.type))
9793 return false;
9794
9795 ConstExprValue *atomic_rmw_op_val = get_builtin_value(ira->codegen, "AtomicRmwOp");
9796 assert(atomic_rmw_op_val->type->id == TypeTableEntryIdMetaType);
9797 TypeTableEntry *atomic_rmw_op_type = atomic_rmw_op_val->data.x_type;
9798
9799 IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type);
9800 if (type_is_invalid(casted_value->value.type))
9801 return false;
9802
9803 ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
9804 if (!const_val)
9805 return false;
9806
9807 *out = (AtomicRmwOp)bigint_as_unsigned(&const_val->data.x_enum_tag);
9808 return true;
9809}
9810
9733static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) {9811static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) {
9734 if (type_is_invalid(value->value.type))9812 if (type_is_invalid(value->value.type))
9735 return false;9813 return false;
...@@ -17316,6 +17394,74 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,...@@ -17316,6 +17394,74 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
17316 return result->value.type;17394 return result->value.type;
17317}17395}
1731817396
17397static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
17398 TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other);
17399 if (type_is_invalid(operand_type)) {
17400 return ira->codegen->builtin_types.entry_invalid;
17401 }
17402 if (operand_type->id == TypeTableEntryIdInt) {
17403 if (operand_type->data.integral.bit_count < 8) {
17404 ir_add_error(ira, &instruction->base,
17405 buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
17406 operand_type->data.integral.bit_count));
17407 return ira->codegen->builtin_types.entry_invalid;
17408 }
17409 if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
17410 ir_add_error(ira, &instruction->base,
17411 buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
17412 operand_type->data.integral.bit_count));
17413 return ira->codegen->builtin_types.entry_invalid;
17414 }
17415 if (!is_power_of_2(operand_type->data.integral.bit_count)) {
17416 ir_add_error(ira, &instruction->base,
17417 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
17418 return ira->codegen->builtin_types.entry_invalid;
17419 }
17420 } else if (get_codegen_ptr_type(operand_type) == nullptr) {
17421 ir_add_error(ira, &instruction->base,
17422 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
17423 return ira->codegen->builtin_types.entry_invalid;
17424 }
17425
17426 IrInstruction *ptr_inst = instruction->ptr->other;
17427 if (type_is_invalid(ptr_inst->value.type))
17428 return ira->codegen->builtin_types.entry_invalid;
17429
17430 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
17431 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
17432 if (type_is_invalid(casted_ptr->value.type))
17433 return ira->codegen->builtin_types.entry_invalid;
17434
17435 AtomicRmwOp op;
17436 if (!ir_resolve_atomic_rmw_op(ira, instruction->op->other, &op)) {
17437 return ira->codegen->builtin_types.entry_invalid;
17438 }
17439
17440 IrInstruction *operand = instruction->operand->other;
17441 if (type_is_invalid(operand->value.type))
17442 return ira->codegen->builtin_types.entry_invalid;
17443
17444 IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type);
17445 if (type_is_invalid(casted_ptr->value.type))
17446 return ira->codegen->builtin_types.entry_invalid;
17447
17448 AtomicOrder ordering;
17449 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
17450 return ira->codegen->builtin_types.entry_invalid;
17451
17452 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
17453 {
17454 zig_panic("TODO compile-time execution of atomicRmw");
17455 }
17456
17457 IrInstruction *result = ir_build_atomic_rmw(&ira->new_irb, instruction->base.scope,
17458 instruction->base.source_node, nullptr, casted_ptr, nullptr, casted_operand, nullptr,
17459 op, ordering);
17460 ir_link_new_instruction(result, &instruction->base);
17461 result->value.type = operand_type;
17462 return result->value.type;
17463}
17464
1731917465
17320static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {17466static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
17321 switch (instruction->id) {17467 switch (instruction->id) {
...@@ -17545,6 +17691,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -17545,6 +17691,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
17545 return ir_analyze_instruction_coro_save(ira, (IrInstructionCoroSave *)instruction);17691 return ir_analyze_instruction_coro_save(ira, (IrInstructionCoroSave *)instruction);
17546 case IrInstructionIdCoroAllocHelper:17692 case IrInstructionIdCoroAllocHelper:
17547 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);17693 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
17694 case IrInstructionIdAtomicRmw:
17695 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
17548 }17696 }
17549 zig_unreachable();17697 zig_unreachable();
17550}17698}
...@@ -17748,6 +17896,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -17748,6 +17896,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
17748 case IrInstructionIdCoroSize:17896 case IrInstructionIdCoroSize:
17749 case IrInstructionIdCoroSuspend:17897 case IrInstructionIdCoroSuspend:
17750 case IrInstructionIdCoroFree:17898 case IrInstructionIdCoroFree:
17899 case IrInstructionIdAtomicRmw:
17751 return false;17900 return false;
1775217901
17753 case IrInstructionIdAsm:17902 case IrInstructionIdAsm:
src/ir_print.cpp+29
...@@ -1113,6 +1113,32 @@ static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelpe...@@ -1113,6 +1113,32 @@ static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelpe
1113 fprintf(irp->f, ")");1113 fprintf(irp->f, ")");
1114}1114}
11151115
1116static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instruction) {
1117 fprintf(irp->f, "@atomicRmw(");
1118 if (instruction->operand_type != nullptr) {
1119 ir_print_other_instruction(irp, instruction->operand_type);
1120 } else {
1121 fprintf(irp->f, "[TODO print]");
1122 }
1123 fprintf(irp->f, ",");
1124 ir_print_other_instruction(irp, instruction->ptr);
1125 fprintf(irp->f, ",");
1126 if (instruction->op != nullptr) {
1127 ir_print_other_instruction(irp, instruction->op);
1128 } else {
1129 fprintf(irp->f, "[TODO print]");
1130 }
1131 fprintf(irp->f, ",");
1132 ir_print_other_instruction(irp, instruction->operand);
1133 fprintf(irp->f, ",");
1134 if (instruction->ordering != nullptr) {
1135 ir_print_other_instruction(irp, instruction->ordering);
1136 } else {
1137 fprintf(irp->f, "[TODO print]");
1138 }
1139 fprintf(irp->f, ")");
1140}
1141
1116static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1142static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1117 ir_print_prefix(irp, instruction);1143 ir_print_prefix(irp, instruction);
1118 switch (instruction->id) {1144 switch (instruction->id) {
...@@ -1472,6 +1498,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1472,6 +1498,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1472 case IrInstructionIdCoroAllocHelper:1498 case IrInstructionIdCoroAllocHelper:
1473 ir_print_coro_alloc_helper(irp, (IrInstructionCoroAllocHelper *)instruction);1499 ir_print_coro_alloc_helper(irp, (IrInstructionCoroAllocHelper *)instruction);
1474 break;1500 break;
1501 case IrInstructionIdAtomicRmw:
1502 ir_print_atomic_rmw(irp, (IrInstructionAtomicRmw *)instruction);
1503 break;
1475 }1504 }
1476 fprintf(irp->f, "\n");1505 fprintf(irp->f, "\n");
1477}1506}
std/debug/index.zig+8-10
...@@ -97,21 +97,18 @@ pub fn assertOrPanic(ok: bool) void {...@@ -97,21 +97,18 @@ pub fn assertOrPanic(ok: bool) void {
97 }97 }
98}98}
9999
100var panicking = false;100var panicking: u8 = 0; // TODO make this a bool
101/// This is the default panic implementation.101/// This is the default panic implementation.
102pub fn panic(comptime format: []const u8, args: ...) noreturn {102pub fn panic(comptime format: []const u8, args: ...) noreturn {
103 // TODO an intrinsic that labels this as unlikely to be reached103 @setCold(true);
104104
105 // TODO105 if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {
106 // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { }
107 if (panicking) {
108 // Panicked during a panic.106 // Panicked during a panic.
107
109 // TODO detect if a different thread caused the panic, because in that case108 // TODO detect if a different thread caused the panic, because in that case
110 // we would want to return here instead of calling abort, so that the thread109 // we would want to return here instead of calling abort, so that the thread
111 // which first called panic can finish printing a stack trace.110 // which first called panic can finish printing a stack trace.
112 os.abort();111 os.abort();
113 } else {
114 panicking = true;
115 }112 }
116113
117 const stderr = getStderrStream() catch os.abort();114 const stderr = getStderrStream() catch os.abort();
...@@ -122,10 +119,11 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {...@@ -122,10 +119,11 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {
122}119}
123120
124pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {121pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {
125 if (panicking) {122 @setCold(true);
123
124 if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {
125 // See TODO in above function
126 os.abort();126 os.abort();
127 } else {
128 panicking = true;
129 }127 }
130 const stderr = getStderrStream() catch os.abort();128 const stderr = getStderrStream() catch os.abort();
131 stderr.print(format ++ "\n", args) catch os.abort();129 stderr.print(format ++ "\n", args) catch os.abort();
test/cases/atomics.zig+14-1
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
2const AtomicOrder = @import("builtin").AtomicOrder;2const builtin = @import("builtin");
3const AtomicRmwOp = builtin.AtomicRmwOp;
4const AtomicOrder = builtin.AtomicOrder;
35
4test "cmpxchg" {6test "cmpxchg" {
5 var x: i32 = 1234;7 var x: i32 = 1234;
...@@ -12,3 +14,14 @@ test "fence" {...@@ -12,3 +14,14 @@ test "fence" {
12 @fence(AtomicOrder.SeqCst);14 @fence(AtomicOrder.SeqCst);
13 x = 5678;15 x = 5678;
14}16}
17
18test "atomicrmw" {
19 var data: u8 = 200;
20 testAtomicRmw(&data);
21 assert(data == 42);
22}
23
24fn testAtomicRmw(ptr: &u8) void {
25 const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
26 assert(prev_value == 200);
27}