From 3ad81c40c01649551b4ad3d2c450d8b5f7934362 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 11 Jul 2024 21:36:34 -0400 Subject: [PATCH] Zcu: allow atomic operations on packed structs Same validation rules as the backing integer would have. --- src/Sema.zig | 4 +- src/Zcu.zig | 54 +++++++++---------- test/behavior/atomics.zig | 20 +++++++ .../atomics_with_invalid_type.zig | 18 +++++++ .../compile_errors/cmpxchg_with_float.zig | 10 ---- 5 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 test/cases/compile_errors/atomics_with_invalid_type.zig delete mode 100644 test/cases/compile_errors/cmpxchg_with_float.zig diff --git a/src/Sema.zig b/src/Sema.zig index 1062ece2be..896d18d21f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23950,7 +23950,7 @@ fn checkAtomicPtrOperand( error.BadType => return sema.fail( block, elem_ty_src, - "expected bool, integer, float, enum, or pointer type; found '{}'", + "expected bool, integer, float, enum, packed struct, or pointer type; found '{}'", .{elem_ty.fmt(pt)}, ), }; @@ -24279,7 +24279,7 @@ fn zirCmpxchg( return sema.fail( block, elem_ty_src, - "expected bool, integer, enum, or pointer type; found '{}'", + "expected bool, integer, enum, packed struct, or pointer type; found '{}'", .{elem_ty.fmt(pt)}, ); } diff --git a/src/Zcu.zig b/src/Zcu.zig index a9d80b4fdf..5179225fc1 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3305,37 +3305,31 @@ pub fn atomicPtrAlignment( .spirv => @panic("TODO what should this value be?"), }; - const int_ty = switch (ty.zigTypeTag(mod)) { - .Int => ty, - .Enum => ty.intTagType(mod), - .Float => { - const bit_count = ty.floatBits(target); - if (bit_count > max_atomic_bits) { - diags.* = .{ - .bits = bit_count, - .max_bits = max_atomic_bits, - }; - return error.FloatTooBig; - } - return .none; - }, - .Bool => return .none, - else => { - if (ty.isPtrAtRuntime(mod)) return .none; - return error.BadType; - }, - }; - - const bit_count = int_ty.intInfo(mod).bits; - if (bit_count > max_atomic_bits) { - diags.* = .{ - .bits = bit_count, - .max_bits = max_atomic_bits, - }; - return error.IntTooBig; + if (ty.toIntern() == .bool_type) return .none; + if (ty.isRuntimeFloat()) { + const bit_count = ty.floatBits(target); + if (bit_count > max_atomic_bits) { + diags.* = .{ + .bits = bit_count, + .max_bits = max_atomic_bits, + }; + return error.FloatTooBig; + } + return .none; } - - return .none; + if (ty.isAbiInt(mod)) { + const bit_count = ty.intInfo(mod).bits; + if (bit_count > max_atomic_bits) { + diags.* = .{ + .bits = bit_count, + .max_bits = max_atomic_bits, + }; + return error.IntTooBig; + } + return .none; + } + if (ty.isPtrAtRuntime(mod)) return .none; + return error.BadType; } pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File { diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 830c8a951d..87ccc72174 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -413,6 +413,14 @@ test "atomics with different types" { try testAtomicsWithType(u0, 0, 0); try testAtomicsWithType(i0, 0, 0); + + try testAtomicsWithType(enum(u32) { x = 1234, y = 5678 }, .x, .y); + + try testAtomicsWithPackedStruct( + packed struct { x: u7, y: u24, z: bool }, + .{ .x = 1, .y = 2, .z = true }, + .{ .x = 3, .y = 4, .z = false }, + ); } fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { @@ -426,6 +434,18 @@ fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { try expect(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst).? == a); } +fn testAtomicsWithPackedStruct(comptime T: type, a: T, b: T) !void { + const BackingInt = @typeInfo(T).Struct.backing_integer.?; + var x: T = b; + @atomicStore(T, &x, a, .seq_cst); + try expect(@as(BackingInt, @bitCast(x)) == @as(BackingInt, @bitCast(a))); + try expect(@as(BackingInt, @bitCast(@atomicLoad(T, &x, .seq_cst))) == @as(BackingInt, @bitCast(a))); + try expect(@as(BackingInt, @bitCast(@atomicRmw(T, &x, .Xchg, b, .seq_cst))) == @as(BackingInt, @bitCast(a))); + try expect(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst) == null); + if (@sizeOf(T) != 0) + try expect(@as(BackingInt, @bitCast(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst).?)) == @as(BackingInt, @bitCast(a))); +} + test "return @atomicStore, using it as a void value" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/cases/compile_errors/atomics_with_invalid_type.zig b/test/cases/compile_errors/atomics_with_invalid_type.zig new file mode 100644 index 0000000000..321cda3655 --- /dev/null +++ b/test/cases/compile_errors/atomics_with_invalid_type.zig @@ -0,0 +1,18 @@ +export fn float() void { + var x: f32 = 0; + _ = @cmpxchgWeak(f32, &x, 1, 2, .seq_cst, .seq_cst); +} + +const NormalStruct = struct { x: u32 }; +export fn normalStruct() void { + var x: NormalStruct = 0; + _ = @cmpxchgWeak(NormalStruct, &x, .{ .x = 1 }, .{ .x = 2 }, .seq_cst, .seq_cst); +} + +// error +// backend=stage2 +// target=native +// +// :3:22: error: expected bool, integer, enum, packed struct, or pointer type; found 'f32' +// :8:27: error: expected type 'tmp.NormalStruct', found 'comptime_int' +// :6:22: note: struct declared here diff --git a/test/cases/compile_errors/cmpxchg_with_float.zig b/test/cases/compile_errors/cmpxchg_with_float.zig deleted file mode 100644 index 1c2b7b6393..0000000000 --- a/test/cases/compile_errors/cmpxchg_with_float.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - var x: f32 = 0; - _ = @cmpxchgWeak(f32, &x, 1, 2, .seq_cst, .seq_cst); -} - -// error -// backend=stage2 -// target=native -// -// :3:22: error: expected bool, integer, enum, or pointer type; found 'f32'