zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 73e535e1125d76bcd4e85123defea8b76412ab09 (tree)
parent cbfd66f68a5a390abcf99e8cc6923d056ee1e4fa
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Mon, 30 Dec 2019 19:19:05 -0500

Merge pull request #3683 from Vexu/atomic-float

Support floats with some atomic operations
Diffstat:
Mdoc/langref.html.in | 6+++---
Msrc/codegen.cpp | 29++++++++++++++++-------------
Msrc/ir.cpp | 20+++++++++++++++++++-
Msrc/zig_llvm.cpp | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/zig_llvm.h | 20++++++++++++++++++++
Mtest/compile_errors.zig | 20++++++++++++++++++++
Mtest/stage1/behavior/atomics.zig | 17+++++++++++++++++
7 files changed, 145 insertions(+), 17 deletions(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in @@ -6699,7 +6699,7 @@ async fn func(y: *i32) void { This builtin function atomically dereferences a pointer and returns the value. </p> <p> - {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, an integer whose bit count meets these requirements: </p> <ul> @@ -6734,7 +6734,7 @@ async fn func(y: *i32) void { Supported operations: </p> <ul> - <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li> + <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified. Supports enums, integers and floats.</li> <li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition. Also supports {#link|Floats#}.</li> <li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction. @@ -6753,7 +6753,7 @@ async fn func(y: *i32) void { This builtin function atomically stores a value. </p> <p> - {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#} + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, an integer whose bit count meets these requirements: </p> <ul> diff --git a/src/codegen.cpp b/src/codegen.cpp @@ -5132,19 +5132,21 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { zig_unreachable(); } -static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) { +static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) { switch (op) { - case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg; - case AtomicRmwOp_add: return LLVMAtomicRMWBinOpAdd; - case AtomicRmwOp_sub: return LLVMAtomicRMWBinOpSub; - case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd; - case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand; - case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr; - case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor; + case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg; + case AtomicRmwOp_add: + return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd; + case AtomicRmwOp_sub: + return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub; + case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd; + case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand; + case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr; + case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor; case AtomicRmwOp_max: - return is_signed ? LLVMAtomicRMWBinOpMax : LLVMAtomicRMWBinOpUMax; + return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax; case AtomicRmwOp_min: - return is_signed ? LLVMAtomicRMWBinOpMin : LLVMAtomicRMWBinOpUMin; + return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin; } zig_unreachable(); } @@ -5738,25 +5740,26 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, { bool is_signed; ZigType *operand_type = instruction->operand->value->type; + bool is_float = operand_type->id == ZigTypeIdFloat; if (operand_type->id == ZigTypeIdInt) { is_signed = operand_type->data.integral.is_signed; } else { is_signed = false; } - LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed); + enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->resolved_op, is_signed, is_float); LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); LLVMValueRef operand = ir_llvm_value(g, instruction->operand); if (get_codegen_ptr_type(operand_type) == nullptr) { - return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); + return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); } // it's a pointer but we need to treat it as an int LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), ""); LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, ""); - LLVMValueRef uncasted_result = LLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, + LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, g->is_single_threaded); return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); } diff --git a/src/ir.cpp b/src/ir.cpp @@ -23959,6 +23959,12 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; + if (operand_type->id == ZigTypeIdFloat) { + ir_add_error(ira, instruction->type_value->child, + buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_instruction; @@ -27440,9 +27446,17 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } + } else if (operand_type->id == ZigTypeIdFloat) { + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + if (operand_type->data.floating.bit_count > max_atomic_bits) { + ir_add_error(ira, op, + buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", + max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } } else if (get_codegen_ptr_type(operand_type) == nullptr) { ir_add_error(ira, op, - buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); + buf_sprintf("expected integer, float, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } @@ -27477,6 +27491,10 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru ir_add_error(ira, instruction->op, buf_sprintf("@atomicRmw on enum only works with .Xchg")); return ira->codegen->invalid_instruction; + } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { + ir_add_error(ira, instruction->op, + buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub")); + return ira->codegen->invalid_instruction; } IrInstruction *operand = instruction->operand->child; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp @@ -1096,6 +1096,56 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ abort(); } +static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { + switch (BinOp) { + default: + case ZigLLVMAtomicRMWBinOpXchg: return AtomicRMWInst::Xchg; + case ZigLLVMAtomicRMWBinOpAdd: return AtomicRMWInst::Add; + case ZigLLVMAtomicRMWBinOpSub: return AtomicRMWInst::Sub; + case ZigLLVMAtomicRMWBinOpAnd: return AtomicRMWInst::And; + case ZigLLVMAtomicRMWBinOpNand: return AtomicRMWInst::Nand; + case ZigLLVMAtomicRMWBinOpOr: return AtomicRMWInst::Or; + case ZigLLVMAtomicRMWBinOpXor: return AtomicRMWInst::Xor; + case ZigLLVMAtomicRMWBinOpMax: return AtomicRMWInst::Max; + case ZigLLVMAtomicRMWBinOpMin: return AtomicRMWInst::Min; + case ZigLLVMAtomicRMWBinOpUMax: return AtomicRMWInst::UMax; + case ZigLLVMAtomicRMWBinOpUMin: return AtomicRMWInst::UMin; + case ZigLLVMAtomicRMWBinOpFAdd: return AtomicRMWInst::FAdd; + case ZigLLVMAtomicRMWBinOpFSub: return AtomicRMWInst::FSub; + } +} + +static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) { + switch (Ordering) { + default: + case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic; + case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered; + case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic; + case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire; + case LLVMAtomicOrderingRelease: return AtomicOrdering::Release; + case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease; + case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent; + } +} + +inline LLVMAttributeRef wrap(Attribute Attr) { + return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer()); +} + +inline Attribute unwrap(LLVMAttributeRef Attr) { + return Attribute::fromRawPointer(Attr); +} + +LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, + LLVMValueRef PTR, LLVMValueRef Val, + LLVMAtomicOrdering ordering, LLVMBool singleThread) +{ + AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op); + return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR), + unwrap(Val), toLLVMOrdering(ordering), + singleThread ? SyncScope::SingleThread : SyncScope::System)); +} + static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); diff --git a/src/zig_llvm.h b/src/zig_llvm.h @@ -422,6 +422,26 @@ enum ZigLLVM_ObjectFormatType { ZigLLVM_XCOFF, }; +enum ZigLLVM_AtomicRMWBinOp { + ZigLLVMAtomicRMWBinOpXchg, + ZigLLVMAtomicRMWBinOpAdd, + ZigLLVMAtomicRMWBinOpSub, + ZigLLVMAtomicRMWBinOpAnd, + ZigLLVMAtomicRMWBinOpNand, + ZigLLVMAtomicRMWBinOpOr, + ZigLLVMAtomicRMWBinOpXor, + ZigLLVMAtomicRMWBinOpMax, + ZigLLVMAtomicRMWBinOpMin, + ZigLLVMAtomicRMWBinOpUMax, + ZigLLVMAtomicRMWBinOpUMin, + ZigLLVMAtomicRMWBinOpFAdd, + ZigLLVMAtomicRMWBinOpFSub, +}; + +LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, + LLVMValueRef PTR, LLVMValueRef Val, + LLVMAtomicOrdering ordering, LLVMBool singleThread); + #define ZigLLVM_DIFlags_Zero 0U #define ZigLLVM_DIFlags_Private 1U #define ZigLLVM_DIFlags_Protected 2U diff --git a/test/compile_errors.zig b/test/compile_errors.zig @@ -31,6 +31,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel", }); + cases.add( + "cmpxchg with float", + \\export fn entry() void { + \\ var x: f32 = 0; + \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); + \\} + , &[_][]const u8{ + "tmp.zig:3:22: error: expected integer, enum or pointer type, found 'f32'", + }); + + cases.add( + "atomicrmw with float op not .Xchg, .Add or .Sub", + \\export fn entry() void { + \\ var x: f32 = 0; + \\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); + \\} + , &[_][]const u8{ + "tmp.zig:3:29: error: @atomicRmw with float only works with .Xchg, .Add and .Sub", + }); + cases.add("intToPtr with misaligned address", \\pub fn main() void { \\ var y = @intToPtr([*]align(4) u8, 5); diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig @@ -144,3 +144,20 @@ fn testAtomicStore() void { @atomicStore(u32, &x, 12345678, .SeqCst); expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); } + +test "atomicrmw with floats" { + if (builtin.arch == .aarch64 or builtin.arch == .arm) + return; + testAtomicRmwFloat(); +} + +fn testAtomicRmwFloat() void { + var x: f32 = 0; + expect(x == 0); + _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); + expect(x == 1); + _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); + expect(x == 6); + _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); + expect(x == 4); +}