Commit 8bb1e04449

Vexu <git@vexu.eu>
2019-12-16 23:28:17
support some atomic operations with floats
1 parent 25e7121
Changed files (5)
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.
       </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>
@@ -6730,7 +6730,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.
@@ -6749,7 +6749,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>
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);
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;
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);
+}
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);