From 8bb1e0444951b7b83254277c52534c7ab58fd135 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Tue, 17 Dec 2019 00:28:17 +0200
Subject: [PATCH 1/3] support some atomic operations with floats
---
doc/langref.html.in | 6 +++---
src/codegen.cpp | 12 +++++++-----
src/ir.cpp | 20 +++++++++++++++++++-
test/compile_errors.zig | 20 ++++++++++++++++++++
test/stage1/behavior/atomics.zig | 15 +++++++++++++++
5 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 5894810555..5536d2492f 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6695,7 +6695,7 @@ async fn func(y: *i32) void {
This builtin function atomically dereferences a pointer and returns the value.
- {#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:
@@ -6730,7 +6730,7 @@ async fn func(y: *i32) void {
Supported operations:
- - {#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.
+ - {#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified. Supports enums, integers and floats.
- {#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition.
Also supports {#link|Floats#}.
- {#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction.
@@ -6749,7 +6749,7 @@ async fn func(y: *i32) void {
This builtin function atomically stores a value.
- {#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:
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 1455b4b743..cd4b4320fa 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5129,11 +5129,13 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
zig_unreachable();
}
-static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) {
+static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(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_add:
+ return is_float ? LLVMAtomicRMWBinOpFAdd: LLVMAtomicRMWBinOpAdd;
+ case AtomicRmwOp_sub:
+ return is_float ? LLVMAtomicRMWBinOpFSub: LLVMAtomicRMWBinOpSub;
case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd;
case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand;
case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr;
@@ -5725,14 +5727,14 @@ static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInst
static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
IrInstructionAtomicRmw *instruction)
{
- 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);
+ LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(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);
diff --git a/src/ir.cpp b/src/ir.cpp
index a862ff1068..46ea3b9f37 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -23956,6 +23956,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;
@@ -27433,9 +27439,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;
}
@@ -27470,6 +27484,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/test/compile_errors.zig b/test/compile_errors.zig
index 450f91b9be..38d6355d50 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -13,6 +13,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);
+ \\}
+ ,
+ "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);
+ \\}
+ ,
+ "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
index d76be6ce6f..cb83b991e6 100644
--- a/test/stage1/behavior/atomics.zig
+++ b/test/stage1/behavior/atomics.zig
@@ -144,3 +144,18 @@ fn testAtomicStore() void {
@atomicStore(u32, &x, 12345678, .SeqCst);
expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
}
+
+test "atomicrmw with floats" {
+ 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);
+}
From ab7fc33c8342583a8323c19a2596b7e4cd0c5e90 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Tue, 17 Dec 2019 00:24:23 +0200
Subject: [PATCH 2/3] add zig llvm wrapper for atomicrmw
---
src/codegen.cpp | 27 +++++++++++-----------
src/zig_llvm.cpp | 50 +++++++++++++++++++++++++++++++++++++++++
src/zig_llvm.h | 20 +++++++++++++++++
test/compile_errors.zig | 8 +++----
4 files changed, 88 insertions(+), 17 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index cd4b4320fa..3da61c5a74 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5129,21 +5129,21 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
zig_unreachable();
}
-static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) {
+static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) {
switch (op) {
- case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg;
+ case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg;
case AtomicRmwOp_add:
- return is_float ? LLVMAtomicRMWBinOpFAdd: LLVMAtomicRMWBinOpAdd;
+ return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd;
case AtomicRmwOp_sub:
- return is_float ? LLVMAtomicRMWBinOpFSub: LLVMAtomicRMWBinOpSub;
- case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd;
- case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand;
- case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr;
- case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor;
+ 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();
}
@@ -5727,6 +5727,7 @@ static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInst
static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
IrInstructionAtomicRmw *instruction)
{
+ bool is_signed;
ZigType *operand_type = instruction->operand->value->type;
bool is_float = operand_type->id == ZigTypeIdFloat;
if (operand_type->id == ZigTypeIdInt) {
@@ -5734,20 +5735,20 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
} else {
is_signed = false;
}
- LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed, is_float);
+ 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/zig_llvm.cpp b/src/zig_llvm.cpp
index 852475c3c4..7ecb717047 100644
--- 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(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
index 0fad21a19b..4d8a3fa927 100644
--- 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
index 38d6355d50..c084763434 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -19,9 +19,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) 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",
@@ -29,9 +29,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) 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 {
From 2252951066f772ac6b77ab183ad3bc074ae8f09f Mon Sep 17 00:00:00 2001
From: Vexu
Date: Tue, 17 Dec 2019 01:57:06 +0200
Subject: [PATCH 3/3] disable test on arm
---
test/stage1/behavior/atomics.zig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig
index cb83b991e6..7155e80094 100644
--- a/test/stage1/behavior/atomics.zig
+++ b/test/stage1/behavior/atomics.zig
@@ -146,6 +146,8 @@ fn testAtomicStore() void {
}
test "atomicrmw with floats" {
+ if (builtin.arch == .aarch64 or builtin.arch == .arm)
+ return;
testAtomicRmwFloat();
}