| author | |
| committer | |
| log | 110ef2e52825656fc048cba020f0fc36a1e58d13 |
| tree | cae3ae95a6ce44f85650c44b90365f7cc5cb4045 |
| parent | 956ba8b0e7ada08f2f85cd41ee73af6453db0c16 |
| signature |
6 files changed, 185 insertions(+), 3 deletions(-)
doc/langref.html.in+22-3| ... | ... | @@ -6612,14 +6612,14 @@ async fn func(y: *i32) void { |
| 6612 | 6612 | This builtin function atomically dereferences a pointer and returns the value. |
| 6613 | 6613 | </p> |
| 6614 | 6614 | <p> |
| 6615 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, | |
| 6616 | or an integer whose bit count meets these requirements: | |
| 6615 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} | |
| 6616 | an integer whose bit count meets these requirements: | |
| 6617 | 6617 | </p> |
| 6618 | 6618 | <ul> |
| 6619 | 6619 | <li>At least 8</li> |
| 6620 | 6620 | <li>At most the same as usize</li> |
| 6621 | 6621 | <li>Power of 2</li> |
| 6622 | </ul> | |
| 6622 | </ul> or an enum with a valid integer tag type. | |
| 6623 | 6623 | <p> |
| 6624 | 6624 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe |
| 6625 | 6625 | we can remove this restriction |
| ... | ... | @@ -6660,6 +6660,25 @@ async fn func(y: *i32) void { |
| 6660 | 6660 | <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li> |
| 6661 | 6661 | </ul> |
| 6662 | 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 | 6682 | {#header_open|@bitCast#} |
| 6664 | 6683 | <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> |
| 6665 | 6684 | <p> |
src/all_types.hpp+12| ... | ... | @@ -1700,6 +1700,7 @@ enum BuiltinFnId { |
| 1700 | 1700 | BuiltinFnIdErrorReturnTrace, |
| 1701 | 1701 | BuiltinFnIdAtomicRmw, |
| 1702 | 1702 | BuiltinFnIdAtomicLoad, |
| 1703 | BuiltinFnIdAtomicStore, | |
| 1703 | 1704 | BuiltinFnIdHasDecl, |
| 1704 | 1705 | BuiltinFnIdUnionInit, |
| 1705 | 1706 | BuiltinFnIdFrameAddress, |
| ... | ... | @@ -2569,6 +2570,7 @@ enum IrInstructionId { |
| 2569 | 2570 | IrInstructionIdErrorUnion, |
| 2570 | 2571 | IrInstructionIdAtomicRmw, |
| 2571 | 2572 | IrInstructionIdAtomicLoad, |
| 2573 | IrInstructionIdAtomicStore, | |
| 2572 | 2574 | IrInstructionIdSaveErrRetAddr, |
| 2573 | 2575 | IrInstructionIdAddImplicitReturnType, |
| 2574 | 2576 | IrInstructionIdErrSetCast, |
| ... | ... | @@ -3713,6 +3715,16 @@ struct IrInstructionAtomicLoad { |
| 3713 | 3715 | AtomicOrder resolved_ordering; |
| 3714 | 3716 | }; |
| 3715 | 3717 | |
| 3718 | struct IrInstructionAtomicStore { | |
| 3719 | IrInstruction base; | |
| 3720 | ||
| 3721 | IrInstruction *operand_type; | |
| 3722 | IrInstruction *ptr; | |
| 3723 | IrInstruction *value; | |
| 3724 | IrInstruction *ordering; | |
| 3725 | AtomicOrder resolved_ordering; | |
| 3726 | }; | |
| 3727 | ||
| 3716 | 3728 | struct IrInstructionSaveErrRetAddr { |
| 3717 | 3729 | IrInstruction base; |
| 3718 | 3730 | }; |
src/codegen.cpp+14| ... | ... | @@ -5650,6 +5650,17 @@ static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, |
| 5650 | 5650 | return load_inst; |
| 5651 | 5651 | } |
| 5652 | 5652 | |
| 5653 | static LLVMValueRef ir_render_atomic_store(CodeGen *g, IrExecutable *executable, | |
| 5654 | IrInstructionAtomicStore *instruction) | |
| 5655 | { | |
| 5656 | LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); | |
| 5657 | LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); | |
| 5658 | LLVMValueRef value = ir_llvm_value(g, instruction->value); | |
| 5659 | LLVMValueRef store_inst = gen_store(g, value, ptr, instruction->ptr->value.type); | |
| 5660 | LLVMSetOrdering(store_inst, ordering); | |
| 5661 | return nullptr; | |
| 5662 | } | |
| 5663 | ||
| 5653 | 5664 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { |
| 5654 | 5665 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); |
| 5655 | 5666 | assert(instruction->base.value.type->id == ZigTypeIdFloat); |
| ... | ... | @@ -6253,6 +6264,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 6253 | 6264 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); |
| 6254 | 6265 | case IrInstructionIdAtomicLoad: |
| 6255 | 6266 | return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); |
| 6267 | case IrInstructionIdAtomicStore: | |
| 6268 | return ir_render_atomic_store(g, executable, (IrInstructionAtomicStore *)instruction); | |
| 6256 | 6269 | case IrInstructionIdSaveErrRetAddr: |
| 6257 | 6270 | return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); |
| 6258 | 6271 | case IrInstructionIdFloatOp: |
| ... | ... | @@ -8064,6 +8077,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8064 | 8077 | create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); |
| 8065 | 8078 | create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); |
| 8066 | 8079 | create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); |
| 8080 | create_builtin_fn(g, BuiltinFnIdAtomicStore, "atomicStore", 4); | |
| 8067 | 8081 | create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); |
| 8068 | 8082 | create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); |
| 8069 | 8083 | create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); |
src/ir.cpp+103| ... | ... | @@ -1009,6 +1009,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { |
| 1009 | 1009 | return IrInstructionIdAtomicLoad; |
| 1010 | 1010 | } |
| 1011 | 1011 | |
| 1012 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicStore *) { | |
| 1013 | return IrInstructionIdAtomicStore; | |
| 1014 | } | |
| 1015 | ||
| 1012 | 1016 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { |
| 1013 | 1017 | return IrInstructionIdSaveErrRetAddr; |
| 1014 | 1018 | } |
| ... | ... | @@ -3186,6 +3190,25 @@ static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode |
| 3186 | 3190 | return &instruction->base; |
| 3187 | 3191 | } |
| 3188 | 3192 | |
| 3193 | static IrInstruction *ir_build_atomic_store(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 3194 | IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *value, | |
| 3195 | IrInstruction *ordering, AtomicOrder resolved_ordering) | |
| 3196 | { | |
| 3197 | IrInstructionAtomicStore *instruction = ir_build_instruction<IrInstructionAtomicStore>(irb, scope, source_node); | |
| 3198 | instruction->operand_type = operand_type; | |
| 3199 | instruction->ptr = ptr; | |
| 3200 | instruction->value = value; | |
| 3201 | instruction->ordering = ordering; | |
| 3202 | instruction->resolved_ordering = resolved_ordering; | |
| 3203 | ||
| 3204 | if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); | |
| 3205 | ir_ref_instruction(ptr, irb->current_basic_block); | |
| 3206 | ir_ref_instruction(value, irb->current_basic_block); | |
| 3207 | if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); | |
| 3208 | ||
| 3209 | return &instruction->base; | |
| 3210 | } | |
| 3211 | ||
| 3189 | 3212 | static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 3190 | 3213 | IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node); |
| 3191 | 3214 | return &instruction->base; |
| ... | ... | @@ -5730,6 +5753,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5730 | 5753 | AtomicOrderMonotonic); |
| 5731 | 5754 | return ir_lval_wrap(irb, scope, inst, lval, result_loc); |
| 5732 | 5755 | } |
| 5756 | case BuiltinFnIdAtomicStore: | |
| 5757 | { | |
| 5758 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 5759 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 5760 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 5761 | return arg0_value; | |
| 5762 | ||
| 5763 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 5764 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 5765 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 5766 | return arg1_value; | |
| 5767 | ||
| 5768 | AstNode *arg2_node = node->data.fn_call_expr.params.at(2); | |
| 5769 | IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); | |
| 5770 | if (arg2_value == irb->codegen->invalid_instruction) | |
| 5771 | return arg2_value; | |
| 5772 | ||
| 5773 | AstNode *arg3_node = node->data.fn_call_expr.params.at(3); | |
| 5774 | IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); | |
| 5775 | if (arg3_value == irb->codegen->invalid_instruction) | |
| 5776 | return arg3_value; | |
| 5777 | ||
| 5778 | IrInstruction *inst = ir_build_atomic_store(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, | |
| 5779 | // this value does not mean anything since we passed non-null values for other arg | |
| 5780 | AtomicOrderMonotonic); | |
| 5781 | return ir_lval_wrap(irb, scope, inst, lval, result_loc); | |
| 5782 | } | |
| 5733 | 5783 | case BuiltinFnIdIntToEnum: |
| 5734 | 5784 | { |
| 5735 | 5785 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -25748,6 +25798,56 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr |
| 25748 | 25798 | return result; |
| 25749 | 25799 | } |
| 25750 | 25800 | |
| 25801 | static IrInstruction *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstructionAtomicStore *instruction) { | |
| 25802 | ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); | |
| 25803 | if (type_is_invalid(operand_type)) | |
| 25804 | return ira->codegen->invalid_instruction; | |
| 25805 | ||
| 25806 | IrInstruction *ptr_inst = instruction->ptr->child; | |
| 25807 | if (type_is_invalid(ptr_inst->value.type)) | |
| 25808 | return ira->codegen->invalid_instruction; | |
| 25809 | ||
| 25810 | ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); | |
| 25811 | IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); | |
| 25812 | if (type_is_invalid(casted_ptr->value.type)) | |
| 25813 | return ira->codegen->invalid_instruction; | |
| 25814 | ||
| 25815 | IrInstruction *value = instruction->value->child; | |
| 25816 | if (type_is_invalid(value->value.type)) | |
| 25817 | return ira->codegen->invalid_instruction; | |
| 25818 | ||
| 25819 | IrInstruction *casted_value = ir_implicit_cast(ira, value, operand_type); | |
| 25820 | if (type_is_invalid(casted_value->value.type)) | |
| 25821 | return ira->codegen->invalid_instruction; | |
| 25822 | ||
| 25823 | ||
| 25824 | AtomicOrder ordering; | |
| 25825 | if (instruction->ordering == nullptr) { | |
| 25826 | ordering = instruction->resolved_ordering; | |
| 25827 | } else { | |
| 25828 | if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) | |
| 25829 | return ira->codegen->invalid_instruction; | |
| 25830 | } | |
| 25831 | ||
| 25832 | if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { | |
| 25833 | ir_assert(instruction->ordering != nullptr, &instruction->base); | |
| 25834 | ir_add_error(ira, instruction->ordering, | |
| 25835 | buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); | |
| 25836 | return ira->codegen->invalid_instruction; | |
| 25837 | } | |
| 25838 | ||
| 25839 | if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { | |
| 25840 | IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr, nullptr); | |
| 25841 | ir_assert(result->value.type != nullptr, &instruction->base); | |
| 25842 | return result; | |
| 25843 | } | |
| 25844 | ||
| 25845 | IrInstruction *result = ir_build_atomic_store(&ira->new_irb, instruction->base.scope, | |
| 25846 | instruction->base.source_node, nullptr, casted_ptr, casted_value, nullptr, ordering); | |
| 25847 | result->value.type = ira->codegen->builtin_types.entry_void; | |
| 25848 | return result; | |
| 25849 | } | |
| 25850 | ||
| 25751 | 25851 | static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { |
| 25752 | 25852 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, |
| 25753 | 25853 | instruction->base.source_node); |
| ... | ... | @@ -26782,6 +26882,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 26782 | 26882 | return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); |
| 26783 | 26883 | case IrInstructionIdAtomicLoad: |
| 26784 | 26884 | return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); |
| 26885 | case IrInstructionIdAtomicStore: | |
| 26886 | return ir_analyze_instruction_atomic_store(ira, (IrInstructionAtomicStore *)instruction); | |
| 26785 | 26887 | case IrInstructionIdSaveErrRetAddr: |
| 26786 | 26888 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); |
| 26787 | 26889 | case IrInstructionIdAddImplicitReturnType: |
| ... | ... | @@ -26962,6 +27064,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 26962 | 27064 | case IrInstructionIdSaveErrRetAddr: |
| 26963 | 27065 | case IrInstructionIdAddImplicitReturnType: |
| 26964 | 27066 | case IrInstructionIdAtomicRmw: |
| 27067 | case IrInstructionIdAtomicStore: | |
| 26965 | 27068 | case IrInstructionIdCmpxchgGen: |
| 26966 | 27069 | case IrInstructionIdCmpxchgSrc: |
| 26967 | 27070 | case IrInstructionIdAssertZero: |
src/ir_print.cpp+26| ... | ... | @@ -324,6 +324,8 @@ const char* ir_instruction_type_str(IrInstructionId id) { |
| 324 | 324 | return "AtomicRmw"; |
| 325 | 325 | case IrInstructionIdAtomicLoad: |
| 326 | 326 | return "AtomicLoad"; |
| 327 | case IrInstructionIdAtomicStore: | |
| 328 | return "AtomicStore"; | |
| 327 | 329 | case IrInstructionIdSaveErrRetAddr: |
| 328 | 330 | return "SaveErrRetAddr"; |
| 329 | 331 | case IrInstructionIdAddImplicitReturnType: |
| ... | ... | @@ -1871,6 +1873,27 @@ static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruct |
| 1871 | 1873 | fprintf(irp->f, ")"); |
| 1872 | 1874 | } |
| 1873 | 1875 | |
| 1876 | static void ir_print_atomic_store(IrPrint *irp, IrInstructionAtomicStore *instruction) { | |
| 1877 | fprintf(irp->f, "@atomicStore("); | |
| 1878 | if (instruction->operand_type != nullptr) { | |
| 1879 | ir_print_other_instruction(irp, instruction->operand_type); | |
| 1880 | } else { | |
| 1881 | fprintf(irp->f, "[TODO print]"); | |
| 1882 | } | |
| 1883 | fprintf(irp->f, ","); | |
| 1884 | ir_print_other_instruction(irp, instruction->ptr); | |
| 1885 | fprintf(irp->f, ","); | |
| 1886 | ir_print_other_instruction(irp, instruction->value); | |
| 1887 | fprintf(irp->f, ","); | |
| 1888 | if (instruction->ordering != nullptr) { | |
| 1889 | ir_print_other_instruction(irp, instruction->ordering); | |
| 1890 | } else { | |
| 1891 | fprintf(irp->f, "[TODO print]"); | |
| 1892 | } | |
| 1893 | fprintf(irp->f, ")"); | |
| 1894 | } | |
| 1895 | ||
| 1896 | ||
| 1874 | 1897 | static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) { |
| 1875 | 1898 | fprintf(irp->f, "@saveErrRetAddr()"); |
| 1876 | 1899 | } |
| ... | ... | @@ -2431,6 +2454,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool |
| 2431 | 2454 | case IrInstructionIdAtomicLoad: |
| 2432 | 2455 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); |
| 2433 | 2456 | break; |
| 2457 | case IrInstructionIdAtomicStore: | |
| 2458 | ir_print_atomic_store(irp, (IrInstructionAtomicStore *)instruction); | |
| 2459 | break; | |
| 2434 | 2460 | case IrInstructionIdEnumToInt: |
| 2435 | 2461 | ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); |
| 2436 | 2462 | break; |
test/stage1/behavior/atomics.zig+8| ... | ... | @@ -123,3 +123,11 @@ test "atomic load and rmw with enum" { |
| 123 | 123 | expect(@atomicLoad(Value, &x, .SeqCst) != .a); |
| 124 | 124 | expect(@atomicLoad(Value, &x, .SeqCst) != .b); |
| 125 | 125 | } |
| 126 | ||
| 127 | test "atomic store" { | |
| 128 | var x: u32 = 0; | |
| 129 | @atomicStore(u32, &x, 1, .SeqCst); | |
| 130 | expect(@atomicLoad(u32, &x, .SeqCst) == 1); | |
| 131 | @atomicStore(u32, &x, 12345678, .SeqCst); | |
| 132 | expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | |
| 133 | } | |
| \ No newline at end of file |