| author | |
| committer | |
| log | 73e535e1125d76bcd4e85123defea8b76412ab09 |
| tree | e347bc78a7ea39a2009b490e3be97074d05f9fcb |
| parent | cbfd66f68a5a390abcf99e8cc6923d056ee1e4fa |
| parent | 2252951066f772ac6b77ab183ad3bc074ae8f09f |
| signature |
Support floats with some atomic operations7 files changed, 145 insertions(+), 17 deletions(-)
doc/langref.html.in+3-3| ... | ... | @@ -6699,7 +6699,7 @@ async fn func(y: *i32) void { |
| 6699 | 6699 | This builtin function atomically dereferences a pointer and returns the value. |
| 6700 | 6700 | </p> |
| 6701 | 6701 | <p> |
| 6702 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} | |
| 6702 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, | |
| 6703 | 6703 | an integer whose bit count meets these requirements: |
| 6704 | 6704 | </p> |
| 6705 | 6705 | <ul> |
| ... | ... | @@ -6734,7 +6734,7 @@ async fn func(y: *i32) void { |
| 6734 | 6734 | Supported operations: |
| 6735 | 6735 | </p> |
| 6736 | 6736 | <ul> |
| 6737 | <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li> | |
| 6737 | <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified. Supports enums, integers and floats.</li> | |
| 6738 | 6738 | <li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition. |
| 6739 | 6739 | Also supports {#link|Floats#}.</li> |
| 6740 | 6740 | <li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction. |
| ... | ... | @@ -6753,7 +6753,7 @@ async fn func(y: *i32) void { |
| 6753 | 6753 | This builtin function atomically stores a value. |
| 6754 | 6754 | </p> |
| 6755 | 6755 | <p> |
| 6756 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} | |
| 6756 | {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, | |
| 6757 | 6757 | an integer whose bit count meets these requirements: |
| 6758 | 6758 | </p> |
| 6759 | 6759 | <ul> |
src/codegen.cpp+16-13| ... | ... | @@ -5132,19 +5132,21 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { |
| 5132 | 5132 | zig_unreachable(); |
| 5133 | 5133 | } |
| 5134 | 5134 | |
| 5135 | static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) { | |
| 5135 | static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) { | |
| 5136 | 5136 | switch (op) { |
| 5137 | case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg; | |
| 5138 | case AtomicRmwOp_add: return LLVMAtomicRMWBinOpAdd; | |
| 5139 | case AtomicRmwOp_sub: return LLVMAtomicRMWBinOpSub; | |
| 5140 | case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd; | |
| 5141 | case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand; | |
| 5142 | case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr; | |
| 5143 | case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor; | |
| 5137 | case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg; | |
| 5138 | case AtomicRmwOp_add: | |
| 5139 | return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd; | |
| 5140 | case AtomicRmwOp_sub: | |
| 5141 | return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub; | |
| 5142 | case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd; | |
| 5143 | case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand; | |
| 5144 | case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr; | |
| 5145 | case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor; | |
| 5144 | 5146 | case AtomicRmwOp_max: |
| 5145 | return is_signed ? LLVMAtomicRMWBinOpMax : LLVMAtomicRMWBinOpUMax; | |
| 5147 | return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax; | |
| 5146 | 5148 | case AtomicRmwOp_min: |
| 5147 | return is_signed ? LLVMAtomicRMWBinOpMin : LLVMAtomicRMWBinOpUMin; | |
| 5149 | return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin; | |
| 5148 | 5150 | } |
| 5149 | 5151 | zig_unreachable(); |
| 5150 | 5152 | } |
| ... | ... | @@ -5738,25 +5740,26 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, |
| 5738 | 5740 | { |
| 5739 | 5741 | bool is_signed; |
| 5740 | 5742 | ZigType *operand_type = instruction->operand->value->type; |
| 5743 | bool is_float = operand_type->id == ZigTypeIdFloat; | |
| 5741 | 5744 | if (operand_type->id == ZigTypeIdInt) { |
| 5742 | 5745 | is_signed = operand_type->data.integral.is_signed; |
| 5743 | 5746 | } else { |
| 5744 | 5747 | is_signed = false; |
| 5745 | 5748 | } |
| 5746 | LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed); | |
| 5749 | enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->resolved_op, is_signed, is_float); | |
| 5747 | 5750 | LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); |
| 5748 | 5751 | LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); |
| 5749 | 5752 | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); |
| 5750 | 5753 | |
| 5751 | 5754 | if (get_codegen_ptr_type(operand_type) == nullptr) { |
| 5752 | return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); | |
| 5755 | return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); | |
| 5753 | 5756 | } |
| 5754 | 5757 | |
| 5755 | 5758 | // it's a pointer but we need to treat it as an int |
| 5756 | 5759 | LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, |
| 5757 | 5760 | LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), ""); |
| 5758 | 5761 | LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, ""); |
| 5759 | LLVMValueRef uncasted_result = LLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, | |
| 5762 | LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, | |
| 5760 | 5763 | g->is_single_threaded); |
| 5761 | 5764 | return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); |
| 5762 | 5765 | } |
src/ir.cpp+19-1| ... | ... | @@ -23959,6 +23959,12 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi |
| 23959 | 23959 | if (type_is_invalid(operand_type)) |
| 23960 | 23960 | return ira->codegen->invalid_instruction; |
| 23961 | 23961 | |
| 23962 | if (operand_type->id == ZigTypeIdFloat) { | |
| 23963 | ir_add_error(ira, instruction->type_value->child, | |
| 23964 | buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); | |
| 23965 | return ira->codegen->invalid_instruction; | |
| 23966 | } | |
| 23967 | ||
| 23962 | 23968 | IrInstruction *ptr = instruction->ptr->child; |
| 23963 | 23969 | if (type_is_invalid(ptr->value->type)) |
| 23964 | 23970 | return ira->codegen->invalid_instruction; |
| ... | ... | @@ -27440,9 +27446,17 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op |
| 27440 | 27446 | buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count)); |
| 27441 | 27447 | return ira->codegen->builtin_types.entry_invalid; |
| 27442 | 27448 | } |
| 27449 | } else if (operand_type->id == ZigTypeIdFloat) { | |
| 27450 | uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); | |
| 27451 | if (operand_type->data.floating.bit_count > max_atomic_bits) { | |
| 27452 | ir_add_error(ira, op, | |
| 27453 | buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", | |
| 27454 | max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); | |
| 27455 | return ira->codegen->builtin_types.entry_invalid; | |
| 27456 | } | |
| 27443 | 27457 | } else if (get_codegen_ptr_type(operand_type) == nullptr) { |
| 27444 | 27458 | ir_add_error(ira, op, |
| 27445 | buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); | |
| 27459 | buf_sprintf("expected integer, float, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); | |
| 27446 | 27460 | return ira->codegen->builtin_types.entry_invalid; |
| 27447 | 27461 | } |
| 27448 | 27462 | |
| ... | ... | @@ -27477,6 +27491,10 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru |
| 27477 | 27491 | ir_add_error(ira, instruction->op, |
| 27478 | 27492 | buf_sprintf("@atomicRmw on enum only works with .Xchg")); |
| 27479 | 27493 | return ira->codegen->invalid_instruction; |
| 27494 | } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { | |
| 27495 | ir_add_error(ira, instruction->op, | |
| 27496 | buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub")); | |
| 27497 | return ira->codegen->invalid_instruction; | |
| 27480 | 27498 | } |
| 27481 | 27499 | |
| 27482 | 27500 | IrInstruction *operand = instruction->operand->child; |
src/zig_llvm.cpp+50| ... | ... | @@ -1096,6 +1096,56 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ |
| 1096 | 1096 | abort(); |
| 1097 | 1097 | } |
| 1098 | 1098 | |
| 1099 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { | |
| 1100 | switch (BinOp) { | |
| 1101 | default: | |
| 1102 | case ZigLLVMAtomicRMWBinOpXchg: return AtomicRMWInst::Xchg; | |
| 1103 | case ZigLLVMAtomicRMWBinOpAdd: return AtomicRMWInst::Add; | |
| 1104 | case ZigLLVMAtomicRMWBinOpSub: return AtomicRMWInst::Sub; | |
| 1105 | case ZigLLVMAtomicRMWBinOpAnd: return AtomicRMWInst::And; | |
| 1106 | case ZigLLVMAtomicRMWBinOpNand: return AtomicRMWInst::Nand; | |
| 1107 | case ZigLLVMAtomicRMWBinOpOr: return AtomicRMWInst::Or; | |
| 1108 | case ZigLLVMAtomicRMWBinOpXor: return AtomicRMWInst::Xor; | |
| 1109 | case ZigLLVMAtomicRMWBinOpMax: return AtomicRMWInst::Max; | |
| 1110 | case ZigLLVMAtomicRMWBinOpMin: return AtomicRMWInst::Min; | |
| 1111 | case ZigLLVMAtomicRMWBinOpUMax: return AtomicRMWInst::UMax; | |
| 1112 | case ZigLLVMAtomicRMWBinOpUMin: return AtomicRMWInst::UMin; | |
| 1113 | case ZigLLVMAtomicRMWBinOpFAdd: return AtomicRMWInst::FAdd; | |
| 1114 | case ZigLLVMAtomicRMWBinOpFSub: return AtomicRMWInst::FSub; | |
| 1115 | } | |
| 1116 | } | |
| 1117 | ||
| 1118 | static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) { | |
| 1119 | switch (Ordering) { | |
| 1120 | default: | |
| 1121 | case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic; | |
| 1122 | case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered; | |
| 1123 | case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic; | |
| 1124 | case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire; | |
| 1125 | case LLVMAtomicOrderingRelease: return AtomicOrdering::Release; | |
| 1126 | case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease; | |
| 1127 | case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent; | |
| 1128 | } | |
| 1129 | } | |
| 1130 | ||
| 1131 | inline LLVMAttributeRef wrap(Attribute Attr) { | |
| 1132 | return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer()); | |
| 1133 | } | |
| 1134 | ||
| 1135 | inline Attribute unwrap(LLVMAttributeRef Attr) { | |
| 1136 | return Attribute::fromRawPointer(Attr); | |
| 1137 | } | |
| 1138 | ||
| 1139 | LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, | |
| 1140 | LLVMValueRef PTR, LLVMValueRef Val, | |
| 1141 | LLVMAtomicOrdering ordering, LLVMBool singleThread) | |
| 1142 | { | |
| 1143 | AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op); | |
| 1144 | return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR), | |
| 1145 | unwrap(Val), toLLVMOrdering(ordering), | |
| 1146 | singleThread ? SyncScope::SingleThread : SyncScope::System)); | |
| 1147 | } | |
| 1148 | ||
| 1099 | 1149 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); |
| 1100 | 1150 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); |
| 1101 | 1151 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); |
src/zig_llvm.h+20| ... | ... | @@ -422,6 +422,26 @@ enum ZigLLVM_ObjectFormatType { |
| 422 | 422 | ZigLLVM_XCOFF, |
| 423 | 423 | }; |
| 424 | 424 | |
| 425 | enum ZigLLVM_AtomicRMWBinOp { | |
| 426 | ZigLLVMAtomicRMWBinOpXchg, | |
| 427 | ZigLLVMAtomicRMWBinOpAdd, | |
| 428 | ZigLLVMAtomicRMWBinOpSub, | |
| 429 | ZigLLVMAtomicRMWBinOpAnd, | |
| 430 | ZigLLVMAtomicRMWBinOpNand, | |
| 431 | ZigLLVMAtomicRMWBinOpOr, | |
| 432 | ZigLLVMAtomicRMWBinOpXor, | |
| 433 | ZigLLVMAtomicRMWBinOpMax, | |
| 434 | ZigLLVMAtomicRMWBinOpMin, | |
| 435 | ZigLLVMAtomicRMWBinOpUMax, | |
| 436 | ZigLLVMAtomicRMWBinOpUMin, | |
| 437 | ZigLLVMAtomicRMWBinOpFAdd, | |
| 438 | ZigLLVMAtomicRMWBinOpFSub, | |
| 439 | }; | |
| 440 | ||
| 441 | LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, | |
| 442 | LLVMValueRef PTR, LLVMValueRef Val, | |
| 443 | LLVMAtomicOrdering ordering, LLVMBool singleThread); | |
| 444 | ||
| 425 | 445 | #define ZigLLVM_DIFlags_Zero 0U |
| 426 | 446 | #define ZigLLVM_DIFlags_Private 1U |
| 427 | 447 | #define ZigLLVM_DIFlags_Protected 2U |
test/compile_errors.zig+20| ... | ... | @@ -31,6 +31,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 31 | 31 | "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel", |
| 32 | 32 | }); |
| 33 | 33 | |
| 34 | cases.add( | |
| 35 | "cmpxchg with float", | |
| 36 | \\export fn entry() void { | |
| 37 | \\ var x: f32 = 0; | |
| 38 | \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); | |
| 39 | \\} | |
| 40 | , &[_][]const u8{ | |
| 41 | "tmp.zig:3:22: error: expected integer, enum or pointer type, found 'f32'", | |
| 42 | }); | |
| 43 | ||
| 44 | cases.add( | |
| 45 | "atomicrmw with float op not .Xchg, .Add or .Sub", | |
| 46 | \\export fn entry() void { | |
| 47 | \\ var x: f32 = 0; | |
| 48 | \\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); | |
| 49 | \\} | |
| 50 | , &[_][]const u8{ | |
| 51 | "tmp.zig:3:29: error: @atomicRmw with float only works with .Xchg, .Add and .Sub", | |
| 52 | }); | |
| 53 | ||
| 34 | 54 | cases.add("intToPtr with misaligned address", |
| 35 | 55 | \\pub fn main() void { |
| 36 | 56 | \\ var y = @intToPtr([*]align(4) u8, 5); |
test/stage1/behavior/atomics.zig+17| ... | ... | @@ -144,3 +144,20 @@ fn testAtomicStore() void { |
| 144 | 144 | @atomicStore(u32, &x, 12345678, .SeqCst); |
| 145 | 145 | expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); |
| 146 | 146 | } |
| 147 | ||
| 148 | test "atomicrmw with floats" { | |
| 149 | if (builtin.arch == .aarch64 or builtin.arch == .arm) | |
| 150 | return; | |
| 151 | testAtomicRmwFloat(); | |
| 152 | } | |
| 153 | ||
| 154 | fn testAtomicRmwFloat() void { | |
| 155 | var x: f32 = 0; | |
| 156 | expect(x == 0); | |
| 157 | _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); | |
| 158 | expect(x == 1); | |
| 159 | _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); | |
| 160 | expect(x == 6); | |
| 161 | _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); | |
| 162 | expect(x == 4); | |
| 163 | } |