diff --git a/src/AstGen.zig b/src/AstGen.zig index 98937c7923..a8b7586f6e 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7322,7 +7322,7 @@ fn builtinCall( }, } }); const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); - const ordering = try expr(gz, scope, .{ .ty = .atomic_ordering_type }, params[2]); + const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[2]); const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{ .lhs = ptr, .rhs = ordering, @@ -7343,7 +7343,7 @@ fn builtinCall( const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); const operation = try expr(gz, scope, .{ .ty = .atomic_rmw_op_type }, params[2]); const operand = try expr(gz, scope, .{ .ty = int_type }, params[3]); - const ordering = try expr(gz, scope, .{ .ty = .atomic_ordering_type }, params[4]); + const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[4]); const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ .ptr = ptr, .operation = operation, @@ -7365,7 +7365,7 @@ fn builtinCall( } }); const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); const operand = try expr(gz, scope, .{ .ty = int_type }, params[2]); - const ordering = try expr(gz, scope, .{ .ty = .atomic_ordering_type }, params[3]); + const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[3]); const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ .ptr = ptr, .operand = operand, @@ -7553,11 +7553,11 @@ fn cmpxchg( } }); const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ // zig fmt: off - .ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]), - .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), - .new_value = try expr(gz, scope, .{ .ty = int_type }, params[3]), - .success_order = try expr(gz, scope, .{ .ty = .atomic_ordering_type }, params[4]), - .fail_order = try expr(gz, scope, .{ .ty = .atomic_ordering_type }, params[5]), + .ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]), + .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), + .new_value = try expr(gz, scope, .{ .ty = int_type }, params[3]), + .success_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[4]), + .fail_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[5]), // zig fmt: on }); return rvalue(gz, rl, result, node); diff --git a/src/Sema.zig b/src/Sema.zig index ec163dd0a6..ebfe0361d6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9702,7 +9702,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"), .extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"), .export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"), - .atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrdering"), + .atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"), .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"), .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"), .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"), @@ -10096,7 +10096,7 @@ fn typeHasOnePossibleValue( .var_args_param, .manyptr_u8, .manyptr_const_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -10281,7 +10281,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { .@"null" => return .null_type, .@"undefined" => return .undefined_type, .enum_literal => return .enum_literal_type, - .atomic_ordering => return .atomic_ordering_type, + .atomic_order => return .atomic_order_type, .atomic_rmw_op => return .atomic_rmw_op_type, .calling_convention => return .calling_convention_type, .float_mode => return .float_mode_type, diff --git a/src/Zir.zig b/src/Zir.zig index a3c0d5d3a8..dbc1442364 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1714,7 +1714,7 @@ pub const Inst = struct { null_type, undefined_type, enum_literal_type, - atomic_ordering_type, + atomic_order_type, atomic_rmw_op_type, calling_convention_type, float_mode_type, @@ -1961,9 +1961,9 @@ pub const Inst = struct { .ty = Type.initTag(.type), .val = Value.initTag(.manyptr_const_u8_type), }, - .atomic_ordering_type = .{ + .atomic_order_type = .{ .ty = Type.initTag(.type), - .val = Value.initTag(.atomic_ordering_type), + .val = Value.initTag(.atomic_order_type), }, .atomic_rmw_op_type = .{ .ty = Type.initTag(.type), diff --git a/src/type.zig b/src/type.zig index 565ca0496b..4d6ff3be0f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -124,7 +124,7 @@ pub const Type = extern union { .enum_full, .enum_nonexhaustive, .enum_simple, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -743,7 +743,7 @@ pub const Type = extern union { .empty_struct_literal, .manyptr_u8, .manyptr_const_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -955,7 +955,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int => return writer.writeAll("*const comptime_int"), .manyptr_u8 => return writer.writeAll("[*]u8"), .manyptr_const_u8 => return writer.writeAll("[*]const u8"), - .atomic_ordering => return writer.writeAll("std.builtin.AtomicOrdering"), + .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"), .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"), .calling_convention => return writer.writeAll("std.builtin.CallingConvention"), .float_mode => return writer.writeAll("std.builtin.FloatMode"), @@ -1183,7 +1183,7 @@ pub const Type = extern union { .@"anyframe", .@"null", .@"undefined", - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -1298,7 +1298,7 @@ pub const Type = extern union { .enum_literal => return Value.initTag(.enum_literal_type), .manyptr_u8 => return Value.initTag(.manyptr_u8_type), .manyptr_const_u8 => return Value.initTag(.manyptr_const_u8_type), - .atomic_ordering => return Value.initTag(.atomic_ordering_type), + .atomic_order => return Value.initTag(.atomic_order_type), .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type), .calling_convention => return Value.initTag(.calling_convention_type), .float_mode => return Value.initTag(.float_mode_type), @@ -1359,7 +1359,7 @@ pub const Type = extern union { .error_set_inferred, .manyptr_u8, .manyptr_const_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -1505,7 +1505,7 @@ pub const Type = extern union { .bool, .array_u8_sentinel_0, .array_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -1727,7 +1727,7 @@ pub const Type = extern union { .u8, .i8, .bool, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -2011,7 +2011,7 @@ pub const Type = extern union { @panic("TODO bitSize error union"); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -2725,7 +2725,7 @@ pub const Type = extern union { .var_args_param, .manyptr_u8, .manyptr_const_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -2903,7 +2903,7 @@ pub const Type = extern union { const enum_simple = ty.castTag(.enum_simple).?.data; return enum_simple.fields.count(); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -2927,7 +2927,7 @@ pub const Type = extern union { const enum_simple = ty.castTag(.enum_simple).?.data; return enum_simple.fields.keys()[field_index]; }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -2950,7 +2950,7 @@ pub const Type = extern union { const enum_simple = ty.castTag(.enum_simple).?.data; return enum_simple.fields.getIndex(field_name); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -3003,7 +3003,7 @@ pub const Type = extern union { const tag_ty = Type.initPayload(&buffer.base); return S.fieldWithRange(tag_ty, enum_tag, fields_len); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -3058,7 +3058,7 @@ pub const Type = extern union { const union_obj = ty.cast(Payload.Union).?.data; return union_obj.srcLoc(); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -3095,7 +3095,7 @@ pub const Type = extern union { return union_obj.owner_decl; }, .@"opaque" => @panic("TODO"), - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -3145,7 +3145,7 @@ pub const Type = extern union { const tag_ty = Type.initPayload(&buffer.base); return S.intInRange(tag_ty, int, fields_len); }, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, @@ -3205,7 +3205,7 @@ pub const Type = extern union { @"null", @"undefined", enum_literal, - atomic_ordering, + atomic_order, atomic_rmw_op, calling_convention, float_mode, @@ -3328,7 +3328,7 @@ pub const Type = extern union { .empty_struct_literal, .manyptr_u8, .manyptr_const_u8, - .atomic_ordering, + .atomic_order, .atomic_rmw_op, .calling_convention, .float_mode, diff --git a/src/value.zig b/src/value.zig index 8d399bc32b..88d0d04086 100644 --- a/src/value.zig +++ b/src/value.zig @@ -60,7 +60,7 @@ pub const Value = extern union { null_type, undefined_type, enum_literal_type, - atomic_ordering_type, + atomic_order_type, atomic_rmw_op_type, calling_convention_type, float_mode_type, @@ -223,7 +223,7 @@ pub const Value = extern union { .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, - .atomic_ordering_type, + .atomic_order_type, .atomic_rmw_op_type, .calling_convention_type, .float_mode_type, @@ -409,7 +409,7 @@ pub const Value = extern union { .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, - .atomic_ordering_type, + .atomic_order_type, .atomic_rmw_op_type, .calling_convention_type, .float_mode_type, @@ -622,7 +622,7 @@ pub const Value = extern union { .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), .manyptr_u8_type => return out_stream.writeAll("[*]u8"), .manyptr_const_u8_type => return out_stream.writeAll("[*]const u8"), - .atomic_ordering_type => return out_stream.writeAll("std.builtin.AtomicOrdering"), + .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"), .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"), .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"), .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"), @@ -789,7 +789,7 @@ pub const Value = extern union { .enum_literal_type => Type.initTag(.enum_literal), .manyptr_u8_type => Type.initTag(.manyptr_u8), .manyptr_const_u8_type => Type.initTag(.manyptr_const_u8), - .atomic_ordering_type => Type.initTag(.atomic_ordering), + .atomic_order_type => Type.initTag(.atomic_order), .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op), .calling_convention_type => Type.initTag(.calling_convention), .float_mode_type => Type.initTag(.float_mode), diff --git a/test/behavior.zig b/test/behavior.zig index b39c50df32..366753c3bf 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -11,6 +11,7 @@ test { _ = @import("behavior/cast.zig"); _ = @import("behavior/array.zig"); _ = @import("behavior/usingnamespace.zig"); + _ = @import("behavior/atomics.zig"); if (!builtin.zig_is_stage2) { // Tests that only pass for stage1. @@ -21,7 +22,7 @@ test { _ = @import("behavior/asm.zig"); _ = @import("behavior/async_fn.zig"); } - _ = @import("behavior/atomics.zig"); + _ = @import("behavior/atomics_stage1.zig"); _ = @import("behavior/await_struct.zig"); _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 18836931aa..f6c94f7155 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -2,215 +2,3 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); - -test "cmpxchg" { - try testCmpxchg(); - comptime try testCmpxchg(); -} - -fn testCmpxchg() !void { - var x: i32 = 1234; - if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } - try expect(x == 5678); - - try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); - try expect(x == 42); -} - -test "fence" { - var x: i32 = 1234; - @fence(.SeqCst); - x = 5678; -} - -test "atomicrmw and atomicload" { - var data: u8 = 200; - try testAtomicRmw(&data); - try expect(data == 42); - try testAtomicLoad(&data); -} - -fn testAtomicRmw(ptr: *u8) !void { - const prev_value = @atomicRmw(u8, ptr, .Xchg, 42, .SeqCst); - try expect(prev_value == 200); - comptime { - var x: i32 = 1234; - const y: i32 = 12345; - try expect(@atomicLoad(i32, &x, .SeqCst) == 1234); - try expect(@atomicLoad(i32, &y, .SeqCst) == 12345); - } -} - -fn testAtomicLoad(ptr: *u8) !void { - const x = @atomicLoad(u8, ptr, .SeqCst); - try expect(x == 42); -} - -test "cmpxchg with ptr" { - var data1: i32 = 1234; - var data2: i32 = 5678; - var data3: i32 = 9101; - var x: *i32 = &data1; - if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == &data1); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == &data1); - } - try expect(x == &data3); - - try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null); - try expect(x == &data2); -} - -test "128-bit cmpxchg" { - try test_u128_cmpxchg(); - comptime try test_u128_cmpxchg(); -} - -fn test_u128_cmpxchg() !void { - if (std.Target.current.cpu.arch != .x86_64) return error.SkipZigTest; - if (comptime !std.Target.x86.featureSetHas(std.Target.current.cpu.features, .cx16)) return error.SkipZigTest; - - var x: u128 = 1234; - if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } - try expect(x == 5678); - - try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); - try expect(x == 42); -} - -test "cmpxchg with ignored result" { - var x: i32 = 1234; - - _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); - - try expectEqual(@as(i32, 5678), x); -} - -var a_global_variable = @as(u32, 1234); - -test "cmpxchg on a global variable" { - _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); - try expectEqual(@as(u32, 42), a_global_variable); -} - -test "atomic load and rmw with enum" { - const Value = enum(u8) { - a, - b, - c, - }; - var x = Value.a; - - try expect(@atomicLoad(Value, &x, .SeqCst) != .b); - - _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); - try expect(@atomicLoad(Value, &x, .SeqCst) == .c); - try expect(@atomicLoad(Value, &x, .SeqCst) != .a); - try expect(@atomicLoad(Value, &x, .SeqCst) != .b); -} - -test "atomic store" { - var x: u32 = 0; - @atomicStore(u32, &x, 1, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 1); - @atomicStore(u32, &x, 12345678, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); -} - -test "atomic store comptime" { - comptime try testAtomicStore(); - try testAtomicStore(); -} - -fn testAtomicStore() !void { - var x: u32 = 0; - @atomicStore(u32, &x, 1, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 1); - @atomicStore(u32, &x, 12345678, .SeqCst); - try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); -} - -test "atomicrmw with floats" { - try testAtomicRmwFloat(); - comptime try testAtomicRmwFloat(); -} - -fn testAtomicRmwFloat() !void { - var x: f32 = 0; - try expect(x == 0); - _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); - try expect(x == 1); - _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); - try expect(x == 6); - _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); - try expect(x == 4); -} - -test "atomicrmw with ints" { - try testAtomicRmwInt(); - comptime try testAtomicRmwInt(); -} - -fn testAtomicRmwInt() !void { - var x: u8 = 1; - var res = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst); - try expect(x == 3 and res == 1); - _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst); - try expect(x == 6); - _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst); - try expect(x == 5); - _ = @atomicRmw(u8, &x, .And, 4, .SeqCst); - try expect(x == 4); - _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst); - try expect(x == 0xfb); - _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst); - try expect(x == 0xff); - _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst); - try expect(x == 0xfd); - - _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst); - try expect(x == 0xfd); - _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst); - try expect(x == 1); -} - -test "atomics with different types" { - try testAtomicsWithType(bool, true, false); - inline for (.{ u1, i4, u5, i15, u24 }) |T| { - try testAtomicsWithType(T, 0, 1); - } - try testAtomicsWithType(u0, 0, 0); - try testAtomicsWithType(i0, 0, 0); -} - -fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { - var x: T = b; - @atomicStore(T, &x, a, .SeqCst); - try expect(x == a); - try expect(@atomicLoad(T, &x, .SeqCst) == a); - try expect(@atomicRmw(T, &x, .Xchg, b, .SeqCst) == a); - try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst) == null); - if (@sizeOf(T) != 0) - try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a); -} diff --git a/test/behavior/atomics_stage1.zig b/test/behavior/atomics_stage1.zig new file mode 100644 index 0000000000..18836931aa --- /dev/null +++ b/test/behavior/atomics_stage1.zig @@ -0,0 +1,216 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const builtin = @import("builtin"); + +test "cmpxchg" { + try testCmpxchg(); + comptime try testCmpxchg(); +} + +fn testCmpxchg() !void { + var x: i32 = 1234; + if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } + try expect(x == 5678); + + try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); + try expect(x == 42); +} + +test "fence" { + var x: i32 = 1234; + @fence(.SeqCst); + x = 5678; +} + +test "atomicrmw and atomicload" { + var data: u8 = 200; + try testAtomicRmw(&data); + try expect(data == 42); + try testAtomicLoad(&data); +} + +fn testAtomicRmw(ptr: *u8) !void { + const prev_value = @atomicRmw(u8, ptr, .Xchg, 42, .SeqCst); + try expect(prev_value == 200); + comptime { + var x: i32 = 1234; + const y: i32 = 12345; + try expect(@atomicLoad(i32, &x, .SeqCst) == 1234); + try expect(@atomicLoad(i32, &y, .SeqCst) == 12345); + } +} + +fn testAtomicLoad(ptr: *u8) !void { + const x = @atomicLoad(u8, ptr, .SeqCst); + try expect(x == 42); +} + +test "cmpxchg with ptr" { + var data1: i32 = 1234; + var data2: i32 = 5678; + var data3: i32 = 9101; + var x: *i32 = &data1; + if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == &data1); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == &data1); + } + try expect(x == &data3); + + try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null); + try expect(x == &data2); +} + +test "128-bit cmpxchg" { + try test_u128_cmpxchg(); + comptime try test_u128_cmpxchg(); +} + +fn test_u128_cmpxchg() !void { + if (std.Target.current.cpu.arch != .x86_64) return error.SkipZigTest; + if (comptime !std.Target.x86.featureSetHas(std.Target.current.cpu.features, .cx16)) return error.SkipZigTest; + + var x: u128 = 1234; + if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } + try expect(x == 5678); + + try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); + try expect(x == 42); +} + +test "cmpxchg with ignored result" { + var x: i32 = 1234; + + _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); + + try expectEqual(@as(i32, 5678), x); +} + +var a_global_variable = @as(u32, 1234); + +test "cmpxchg on a global variable" { + _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); + try expectEqual(@as(u32, 42), a_global_variable); +} + +test "atomic load and rmw with enum" { + const Value = enum(u8) { + a, + b, + c, + }; + var x = Value.a; + + try expect(@atomicLoad(Value, &x, .SeqCst) != .b); + + _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); + try expect(@atomicLoad(Value, &x, .SeqCst) == .c); + try expect(@atomicLoad(Value, &x, .SeqCst) != .a); + try expect(@atomicLoad(Value, &x, .SeqCst) != .b); +} + +test "atomic store" { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 1); + @atomicStore(u32, &x, 12345678, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); +} + +test "atomic store comptime" { + comptime try testAtomicStore(); + try testAtomicStore(); +} + +fn testAtomicStore() !void { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 1); + @atomicStore(u32, &x, 12345678, .SeqCst); + try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); +} + +test "atomicrmw with floats" { + try testAtomicRmwFloat(); + comptime try testAtomicRmwFloat(); +} + +fn testAtomicRmwFloat() !void { + var x: f32 = 0; + try expect(x == 0); + _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); + try expect(x == 1); + _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); + try expect(x == 6); + _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); + try expect(x == 4); +} + +test "atomicrmw with ints" { + try testAtomicRmwInt(); + comptime try testAtomicRmwInt(); +} + +fn testAtomicRmwInt() !void { + var x: u8 = 1; + var res = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst); + try expect(x == 3 and res == 1); + _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst); + try expect(x == 6); + _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst); + try expect(x == 5); + _ = @atomicRmw(u8, &x, .And, 4, .SeqCst); + try expect(x == 4); + _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst); + try expect(x == 0xfb); + _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst); + try expect(x == 0xff); + _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst); + try expect(x == 0xfd); + + _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst); + try expect(x == 0xfd); + _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst); + try expect(x == 1); +} + +test "atomics with different types" { + try testAtomicsWithType(bool, true, false); + inline for (.{ u1, i4, u5, i15, u24 }) |T| { + try testAtomicsWithType(T, 0, 1); + } + try testAtomicsWithType(u0, 0, 0); + try testAtomicsWithType(i0, 0, 0); +} + +fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { + var x: T = b; + @atomicStore(T, &x, a, .SeqCst); + try expect(x == a); + try expect(@atomicLoad(T, &x, .SeqCst) == a); + try expect(@atomicRmw(T, &x, .Xchg, b, .SeqCst) == a); + try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst) == null); + if (@sizeOf(T) != 0) + try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a); +}