diff options
43 files changed, 2427 insertions, 1246 deletions
@@ -257,13 +257,10 @@ pub fn build(b: *std.Build) !void { var code: u8 = undefined; const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{ "git", - "-C", - b.build_root.path orelse ".", - "describe", - "--match", - "*.*.*", - "--tags", - "--abbrev=9", + "-C", b.build_root.path orelse ".", // affects the --git-dir argument + "--git-dir", ".git", // affected by the -C argument + "describe", "--match", "*.*.*", // + "--tags", "--abbrev=9", }, &code, .Ignore) catch { break :v version_string; }; diff --git a/lib/compiler/aro/aro/target.zig b/lib/compiler/aro/aro/target.zig index 7495eb5d9a..bea982daa2 100644 --- a/lib/compiler/aro/aro/target.zig +++ b/lib/compiler/aro/aro/target.zig @@ -204,7 +204,7 @@ pub fn unnamedFieldAffectsAlignment(target: std.Target) bool { }, .armeb => { if (std.Target.arm.featureSetHas(target.cpu.features, .has_v7)) { - if (std.Target.Abi.default(target.cpu.arch, target.os) == .eabi) return true; + if (std.Target.Abi.default(target.cpu.arch, target.os.tag) == .eabi) return true; } }, .arm => return true, @@ -716,7 +716,7 @@ test "alignment functions - smoke test" { const x86 = std.Target.Cpu.Arch.x86_64; target.os = std.Target.Os.Tag.defaultVersionRange(.linux, x86, .none); target.cpu = std.Target.Cpu.baseline(x86, target.os); - target.abi = std.Target.Abi.default(x86, target.os); + target.abi = std.Target.Abi.default(x86, target.os.tag); try std.testing.expect(isTlsSupported(target)); try std.testing.expect(!ignoreNonZeroSizedBitfieldTypeAlignment(target)); @@ -729,7 +729,7 @@ test "alignment functions - smoke test" { const arm = std.Target.Cpu.Arch.arm; target.os = std.Target.Os.Tag.defaultVersionRange(.ios, arm, .none); target.cpu = std.Target.Cpu.baseline(arm, target.os); - target.abi = std.Target.Abi.default(arm, target.os); + target.abi = std.Target.Abi.default(arm, target.os.tag); try std.testing.expect(!isTlsSupported(target)); try std.testing.expect(ignoreNonZeroSizedBitfieldTypeAlignment(target)); diff --git a/lib/compiler_rt/int_from_float.zig b/lib/compiler_rt/int_from_float.zig index 7bbcd90893..0c2c73bb42 100644 --- a/lib/compiler_rt/int_from_float.zig +++ b/lib/compiler_rt/int_from_float.zig @@ -72,10 +72,12 @@ pub inline fn bigIntFromFloat(comptime signedness: std.builtin.Signedness, resul } }); const parts = math.frexp(a); - const exponent = @max(parts.exponent - significand_bits, 0); + const significand_bits_adjusted_to_handle_smin = @as(i32, significand_bits) + + @intFromBool(signedness == .signed and parts.exponent == 32 * result.len); + const exponent = @max(parts.exponent - significand_bits_adjusted_to_handle_smin, 0); const int: I = @intFromFloat(switch (exponent) { 0 => a, - else => math.ldexp(parts.significand, significand_bits), + else => math.ldexp(parts.significand, significand_bits_adjusted_to_handle_smin), }); switch (signedness) { .signed => { diff --git a/lib/compiler_rt/int_from_float_test.zig b/lib/compiler_rt/int_from_float_test.zig index e10ed1ec00..5305ecf2a0 100644 --- a/lib/compiler_rt/int_from_float_test.zig +++ b/lib/compiler_rt/int_from_float_test.zig @@ -24,6 +24,8 @@ const __fixdfdi = @import("fixdfdi.zig").__fixdfdi; const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; const __fixdfti = @import("fixdfti.zig").__fixdfti; const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; +const __fixdfei = @import("fixdfei.zig").__fixdfei; +const __fixunsdfei = @import("fixunsdfei.zig").__fixunsdfei; // Conversion from f128 const __fixtfsi = @import("fixtfsi.zig").__fixtfsi; @@ -681,6 +683,44 @@ test "fixunsdfti" { try test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); } +fn test_fixdfei(comptime T: type, expected: T, a: f64) !void { + const int = @typeInfo(T).int; + var expected_buf: [@divExact(int.bits, 32)]u32 = undefined; + std.mem.writeInt(T, std.mem.asBytes(&expected_buf), expected, endian); + var actual_buf: [@divExact(int.bits, 32)]u32 = undefined; + _ = switch (int.signedness) { + .signed => __fixdfei, + .unsigned => __fixunsdfei, + }(&actual_buf, int.bits, a); + try testing.expect(std.mem.eql(u32, &expected_buf, &actual_buf)); +} + +test "fixdfei" { + try test_fixdfei(i256, -1 << 255, -0x1p255); + try test_fixdfei(i256, -1 << 127, -0x1p127); + try test_fixdfei(i256, -1 << 100, -0x1p100); + try test_fixdfei(i256, -1 << 50, -0x1p50); + try test_fixdfei(i256, -1 << 1, -0x1p1); + try test_fixdfei(i256, -1 << 0, -0x1p0); + try test_fixdfei(i256, 0, 0); + try test_fixdfei(i256, 1 << 0, 0x1p0); + try test_fixdfei(i256, 1 << 1, 0x1p1); + try test_fixdfei(i256, 1 << 50, 0x1p50); + try test_fixdfei(i256, 1 << 100, 0x1p100); + try test_fixdfei(i256, 1 << 127, 0x1p127); + try test_fixdfei(i256, 1 << 254, 0x1p254); +} + +test "fixundfei" { + try test_fixdfei(u256, 0, 0); + try test_fixdfei(u256, 1 << 0, 0x1p0); + try test_fixdfei(u256, 1 << 1, 0x1p1); + try test_fixdfei(u256, 1 << 50, 0x1p50); + try test_fixdfei(u256, 1 << 100, 0x1p100); + try test_fixdfei(u256, 1 << 127, 0x1p127); + try test_fixdfei(u256, 1 << 255, 0x1p255); +} + fn test__fixtfsi(a: f128, expected: i32) !void { const x = __fixtfsi(a); try testing.expect(x == expected); diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 24edee37a8..932ec00517 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -1254,11 +1254,6 @@ fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 { } test "cache file and then recall it" { - if (builtin.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); @@ -1320,11 +1315,6 @@ test "cache file and then recall it" { } test "check that changing a file makes cache fail" { - if (builtin.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); @@ -1394,11 +1384,6 @@ test "check that changing a file makes cache fail" { } test "no file inputs" { - if (builtin.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); @@ -1442,11 +1427,6 @@ test "no file inputs" { } test "Manifest with files added after initial hash work" { - if (builtin.os.tag == .wasi) { - // https://github.com/ziglang/zig/issues/5437 - return error.SkipZigTest; - } - var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 8e78e4dc29..79a78a41df 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -803,8 +803,8 @@ pub const Abi = enum { // - raygeneration // - vertex - pub fn default(arch: Cpu.Arch, os: Os) Abi { - return switch (os.tag) { + pub fn default(arch: Cpu.Arch, os_tag: Os.Tag) Abi { + return switch (os_tag) { .freestanding, .other => switch (arch) { // Soft float is usually a sane default for freestanding. .arm, diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index 2d5c734108..cf53a8175b 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -102,7 +102,7 @@ pub fn fromTarget(target: Target) Query { .os_version_min = undefined, .os_version_max = undefined, .abi = target.abi, - .glibc_version = target.os.versionRange().gnuLibCVersion(), + .glibc_version = if (target.abi.isGnu()) target.os.versionRange().gnuLibCVersion() else null, .android_api_level = if (target.abi.isAndroid()) target.os.version_range.linux.android else null, }; result.updateOsVersionRange(target.os); diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 27b2b11c4a..fe3bf0fcea 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -122,6 +122,8 @@ pub const max_name_len = switch (native_os) { .openbsd => 23, .dragonfly => 1023, .solaris, .illumos => 31, + // https://github.com/SerenityOS/serenity/blob/6b4c300353da49d3508b5442cf61da70bd04d757/Kernel/Tasks/Thread.h#L102 + .serenity => 63, else => 0, }; @@ -201,6 +203,15 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { else => |e| return posix.unexpectedErrno(e), } }, + .serenity => if (use_pthreads) { + const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr); + switch (@as(posix.E, @enumFromInt(err))) { + .SUCCESS => return, + .NAMETOOLONG => unreachable, + .SRCH => unreachable, + else => |e| return posix.unexpectedErrno(e), + } + }, .netbsd, .solaris, .illumos => if (use_pthreads) { const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null); switch (@as(posix.E, @enumFromInt(err))) { @@ -302,6 +313,16 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co else => |e| return posix.unexpectedErrno(e), } }, + .serenity => if (use_pthreads) { + const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1); + switch (@as(posix.E, @enumFromInt(err))) { + .SUCCESS => return, + .NAMETOOLONG => unreachable, + .SRCH => unreachable, + .FAULT => unreachable, + else => |e| return posix.unexpectedErrno(e), + } + }, .netbsd, .solaris, .illumos => if (use_pthreads) { const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1); switch (@as(posix.E, @enumFromInt(err))) { @@ -342,6 +363,7 @@ pub const Id = switch (native_os) { .openbsd, .haiku, .wasi, + .serenity, => u32, .macos, .ios, .watchos, .tvos, .visionos => u64, .windows => windows.DWORD, @@ -692,6 +714,9 @@ const PosixThreadImpl = struct { .haiku => { return @as(u32, @bitCast(c.find_thread(null))); }, + .serenity => { + return @as(u32, @bitCast(c.pthread_self())); + }, else => { return @intFromPtr(c.pthread_self()); }, @@ -713,11 +738,11 @@ const PosixThreadImpl = struct { }; return @as(usize, @intCast(count)); }, - .solaris, .illumos => { + .solaris, .illumos, .serenity => { // The "proper" way to get the cpu count would be to query // /dev/kstat via ioctls, and traverse a linked list for each - // cpu. - const rc = c.sysconf(std.c._SC.NPROCESSORS_ONLN); + // cpu. (solaris, illumos) + const rc = c.sysconf(@intFromEnum(std.c._SC.NPROCESSORS_ONLN)); return switch (posix.errno(rc)) { .SUCCESS => @as(usize, @intCast(rc)), else => |err| posix.unexpectedErrno(err), diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 7849e98b42..bedba88a73 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -50,19 +50,19 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn init(allocator: Allocator) Self { + pub fn init(gpa: Allocator) Self { return Self{ .items = &[_]T{}, .capacity = 0, - .allocator = allocator, + .allocator = gpa, }; } /// Initialize with capacity to hold `num` elements. /// The resulting capacity will equal `num` exactly. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!Self { - var self = Self.init(allocator); + pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self { + var self = Self.init(gpa); try self.ensureTotalCapacityPrecise(num); return self; } @@ -75,24 +75,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// ArrayList takes ownership of the passed in slice. The slice must have been - /// allocated with `allocator`. + /// allocated with `gpa`. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn fromOwnedSlice(allocator: Allocator, slice: Slice) Self { + pub fn fromOwnedSlice(gpa: Allocator, slice: Slice) Self { return Self{ .items = slice, .capacity = slice.len, - .allocator = allocator, + .allocator = gpa, }; } /// ArrayList takes ownership of the passed in slice. The slice must have been - /// allocated with `allocator`. + /// allocated with `gpa`. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn fromOwnedSliceSentinel(allocator: Allocator, comptime sentinel: T, slice: [:sentinel]T) Self { + pub fn fromOwnedSliceSentinel(gpa: Allocator, comptime sentinel: T, slice: [:sentinel]T) Self { return Self{ .items = slice, .capacity = slice.len + 1, - .allocator = allocator, + .allocator = gpa, }; } @@ -646,9 +646,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Initialize with capacity to hold `num` elements. /// The resulting capacity will equal `num` exactly. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!Self { + pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self { var self = Self{}; - try self.ensureTotalCapacityPrecise(allocator, num); + try self.ensureTotalCapacityPrecise(gpa, num); return self; } @@ -664,19 +664,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Release all allocated memory. - pub fn deinit(self: *Self, allocator: Allocator) void { - allocator.free(self.allocatedSlice()); + pub fn deinit(self: *Self, gpa: Allocator) void { + gpa.free(self.allocatedSlice()); self.* = undefined; } /// Convert this list into an analogous memory-managed one. /// The returned list has ownership of the underlying memory. - pub fn toManaged(self: *Self, allocator: Allocator) ArrayListAligned(T, alignment) { - return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator }; + pub fn toManaged(self: *Self, gpa: Allocator) ArrayListAligned(T, alignment) { + return .{ .items = self.items, .capacity = self.capacity, .allocator = gpa }; } - /// ArrayListUnmanaged takes ownership of the passed in slice. The slice must have been - /// allocated with `allocator`. + /// ArrayListUnmanaged takes ownership of the passed in slice. /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn fromOwnedSlice(slice: Slice) Self { return Self{ @@ -685,8 +684,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ }; } - /// ArrayListUnmanaged takes ownership of the passed in slice. The slice must have been - /// allocated with `allocator`. + /// ArrayListUnmanaged takes ownership of the passed in slice. /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn fromOwnedSliceSentinel(comptime sentinel: T, slice: [:sentinel]T) Self { return Self{ @@ -697,31 +695,31 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// The caller owns the returned memory. Empties this ArrayList. /// Its capacity is cleared, making deinit() safe but unnecessary to call. - pub fn toOwnedSlice(self: *Self, allocator: Allocator) Allocator.Error!Slice { + pub fn toOwnedSlice(self: *Self, gpa: Allocator) Allocator.Error!Slice { const old_memory = self.allocatedSlice(); - if (allocator.remap(old_memory, self.items.len)) |new_items| { + if (gpa.remap(old_memory, self.items.len)) |new_items| { self.* = .empty; return new_items; } - const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len); + const new_memory = try gpa.alignedAlloc(T, alignment, self.items.len); @memcpy(new_memory, self.items); - self.clearAndFree(allocator); + self.clearAndFree(gpa); return new_memory; } /// The caller owns the returned memory. ArrayList becomes empty. - pub fn toOwnedSliceSentinel(self: *Self, allocator: Allocator, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) { + pub fn toOwnedSliceSentinel(self: *Self, gpa: Allocator, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) { // This addition can never overflow because `self.items` can never occupy the whole address space - try self.ensureTotalCapacityPrecise(allocator, self.items.len + 1); + try self.ensureTotalCapacityPrecise(gpa, self.items.len + 1); self.appendAssumeCapacity(sentinel); - const result = try self.toOwnedSlice(allocator); + const result = try self.toOwnedSlice(gpa); return result[0 .. result.len - 1 :sentinel]; } /// Creates a copy of this ArrayList. - pub fn clone(self: Self, allocator: Allocator) Allocator.Error!Self { - var cloned = try Self.initCapacity(allocator, self.capacity); + pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self { + var cloned = try Self.initCapacity(gpa, self.capacity); cloned.appendSliceAssumeCapacity(self.items); return cloned; } @@ -731,8 +729,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// This operation is O(N). /// Invalidates element pointers if additional memory is needed. /// Asserts that the index is in bounds or equal to the length. - pub fn insert(self: *Self, allocator: Allocator, i: usize, item: T) Allocator.Error!void { - const dst = try self.addManyAt(allocator, i, 1); + pub fn insert(self: *Self, gpa: Allocator, i: usize, item: T) Allocator.Error!void { + const dst = try self.addManyAt(gpa, i, 1); dst[0] = item; } @@ -759,11 +757,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Asserts that the index is in bounds or equal to the length. pub fn addManyAt( self: *Self, - allocator: Allocator, + gpa: Allocator, index: usize, count: usize, ) Allocator.Error![]T { - var managed = self.toManaged(allocator); + var managed = self.toManaged(gpa); defer self.* = managed.moveToUnmanaged(); return managed.addManyAt(index, count); } @@ -795,12 +793,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Asserts that the index is in bounds or equal to the length. pub fn insertSlice( self: *Self, - allocator: Allocator, + gpa: Allocator, index: usize, items: []const T, ) Allocator.Error!void { const dst = try self.addManyAt( - allocator, + gpa, index, items.len, ); @@ -812,7 +810,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Asserts that the range is in bounds. pub fn replaceRange( self: *Self, - allocator: Allocator, + gpa: Allocator, start: usize, len: usize, new_items: []const T, @@ -823,7 +821,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ const first = new_items[0..range.len]; const rest = new_items[range.len..]; @memcpy(range[0..first.len], first); - try self.insertSlice(allocator, after_range, rest); + try self.insertSlice(gpa, after_range, rest); } else { self.replaceRangeAssumeCapacity(start, len, new_items); } @@ -859,8 +857,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Extend the list by 1 element. Allocates more memory as necessary. /// Invalidates element pointers if additional memory is needed. - pub fn append(self: *Self, allocator: Allocator, item: T) Allocator.Error!void { - const new_item_ptr = try self.addOne(allocator); + pub fn append(self: *Self, gpa: Allocator, item: T) Allocator.Error!void { + const new_item_ptr = try self.addOne(gpa); new_item_ptr.* = item; } @@ -899,8 +897,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Append the slice of items to the list. Allocates more /// memory as necessary. /// Invalidates element pointers if additional memory is needed. - pub fn appendSlice(self: *Self, allocator: Allocator, items: []const T) Allocator.Error!void { - try self.ensureUnusedCapacity(allocator, items.len); + pub fn appendSlice(self: *Self, gpa: Allocator, items: []const T) Allocator.Error!void { + try self.ensureUnusedCapacity(gpa, items.len); self.appendSliceAssumeCapacity(items); } @@ -918,8 +916,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// memory as necessary. Only call this function if a call to `appendSlice` instead would /// be a compile error. /// Invalidates element pointers if additional memory is needed. - pub fn appendUnalignedSlice(self: *Self, allocator: Allocator, items: []align(1) const T) Allocator.Error!void { - try self.ensureUnusedCapacity(allocator, items.len); + pub fn appendUnalignedSlice(self: *Self, gpa: Allocator, items: []align(1) const T) Allocator.Error!void { + try self.ensureUnusedCapacity(gpa, items.len); self.appendUnalignedSliceAssumeCapacity(items); } @@ -947,8 +945,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ std.io.Writer(WriterContext, Allocator.Error, appendWrite); /// Initializes a Writer which will append to the list. - pub fn writer(self: *Self, allocator: Allocator) Writer { - return .{ .context = .{ .self = self, .allocator = allocator } }; + pub fn writer(self: *Self, gpa: Allocator) Writer { + return .{ .context = .{ .self = self, .allocator = gpa } }; } /// Same as `append` except it returns the number of bytes written, @@ -983,9 +981,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Invalidates element pointers if additional memory is needed. /// The function is inline so that a comptime-known `value` parameter will /// have a more optimal memset codegen in case it has a repeated byte pattern. - pub inline fn appendNTimes(self: *Self, allocator: Allocator, value: T, n: usize) Allocator.Error!void { + pub inline fn appendNTimes(self: *Self, gpa: Allocator, value: T, n: usize) Allocator.Error!void { const old_len = self.items.len; - try self.resize(allocator, try addOrOom(old_len, n)); + try self.resize(gpa, try addOrOom(old_len, n)); @memset(self.items[old_len..self.items.len], value); } @@ -1004,15 +1002,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Adjust the list length to `new_len`. /// Additional elements contain the value `undefined`. /// Invalidates element pointers if additional memory is needed. - pub fn resize(self: *Self, allocator: Allocator, new_len: usize) Allocator.Error!void { - try self.ensureTotalCapacity(allocator, new_len); + pub fn resize(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void { + try self.ensureTotalCapacity(gpa, new_len); self.items.len = new_len; } /// Reduce allocated capacity to `new_len`. /// May invalidate element pointers. /// Asserts that the new length is less than or equal to the previous length. - pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void { + pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void { assert(new_len <= self.items.len); if (@sizeOf(T) == 0) { @@ -1021,13 +1019,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } const old_memory = self.allocatedSlice(); - if (allocator.remap(old_memory, new_len)) |new_items| { + if (gpa.remap(old_memory, new_len)) |new_items| { self.capacity = new_items.len; self.items = new_items; return; } - const new_memory = allocator.alignedAlloc(T, alignment, new_len) catch |e| switch (e) { + const new_memory = gpa.alignedAlloc(T, alignment, new_len) catch |e| switch (e) { error.OutOfMemory => { // No problem, capacity is still correct then. self.items.len = new_len; @@ -1036,7 +1034,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ }; @memcpy(new_memory, self.items[0..new_len]); - allocator.free(old_memory); + gpa.free(old_memory); self.items = new_memory; self.capacity = new_memory.len; } @@ -1056,8 +1054,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Invalidates all element pointers. - pub fn clearAndFree(self: *Self, allocator: Allocator) void { - allocator.free(self.allocatedSlice()); + pub fn clearAndFree(self: *Self, gpa: Allocator) void { + gpa.free(self.allocatedSlice()); self.items.len = 0; self.capacity = 0; } @@ -1073,7 +1071,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// If the current capacity is less than `new_capacity`, this function will /// modify the array so that it can hold exactly `new_capacity` items. /// Invalidates element pointers if additional memory is needed. - pub fn ensureTotalCapacityPrecise(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { + pub fn ensureTotalCapacityPrecise(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void { if (@sizeOf(T) == 0) { self.capacity = math.maxInt(usize); return; @@ -1087,13 +1085,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ // the allocator implementation would pointlessly copy our // extra capacity. const old_memory = self.allocatedSlice(); - if (allocator.remap(old_memory, new_capacity)) |new_memory| { + if (gpa.remap(old_memory, new_capacity)) |new_memory| { self.items.ptr = new_memory.ptr; self.capacity = new_memory.len; } else { - const new_memory = try allocator.alignedAlloc(T, alignment, new_capacity); + const new_memory = try gpa.alignedAlloc(T, alignment, new_capacity); @memcpy(new_memory[0..self.items.len], self.items); - allocator.free(old_memory); + gpa.free(old_memory); self.items.ptr = new_memory.ptr; self.capacity = new_memory.len; } @@ -1103,10 +1101,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Invalidates element pointers if additional memory is needed. pub fn ensureUnusedCapacity( self: *Self, - allocator: Allocator, + gpa: Allocator, additional_count: usize, ) Allocator.Error!void { - return self.ensureTotalCapacity(allocator, try addOrOom(self.items.len, additional_count)); + return self.ensureTotalCapacity(gpa, try addOrOom(self.items.len, additional_count)); } /// Increases the array's length to match the full capacity that is already allocated. @@ -1118,10 +1116,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Increase length by 1, returning pointer to the new item. /// The returned element pointer becomes invalid when the list is resized. - pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!*T { + pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!*T { // This can never overflow because `self.items` can never occupy the whole address space const newlen = self.items.len + 1; - try self.ensureTotalCapacity(allocator, newlen); + try self.ensureTotalCapacity(gpa, newlen); return self.addOneAssumeCapacity(); } @@ -1139,9 +1137,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. /// The returned pointer becomes invalid when the list is resized. - pub fn addManyAsArray(self: *Self, allocator: Allocator, comptime n: usize) Allocator.Error!*[n]T { + pub fn addManyAsArray(self: *Self, gpa: Allocator, comptime n: usize) Allocator.Error!*[n]T { const prev_len = self.items.len; - try self.resize(allocator, try addOrOom(self.items.len, n)); + try self.resize(gpa, try addOrOom(self.items.len, n)); return self.items[prev_len..][0..n]; } @@ -1161,9 +1159,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// The return value is a slice pointing to the newly allocated elements. /// The returned pointer becomes invalid when the list is resized. /// Resizes list if `self.capacity` is not large enough. - pub fn addManyAsSlice(self: *Self, allocator: Allocator, n: usize) Allocator.Error![]T { + pub fn addManyAsSlice(self: *Self, gpa: Allocator, n: usize) Allocator.Error![]T { const prev_len = self.items.len; - try self.resize(allocator, try addOrOom(self.items.len, n)); + try self.resize(gpa, try addOrOom(self.items.len, n)); return self.items[prev_len..][0..n]; } diff --git a/lib/std/c.zig b/lib/std/c.zig index 9b7d36cb32..53cd75f789 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -19,6 +19,7 @@ const netbsd = @import("c/netbsd.zig"); const dragonfly = @import("c/dragonfly.zig"); const haiku = @import("c/haiku.zig"); const openbsd = @import("c/openbsd.zig"); +const serenity = @import("c/serenity.zig"); // These constants are shared among all operating systems even when not linking // libc. @@ -75,12 +76,14 @@ pub const ino_t = switch (native_os) { .wasi => wasi.inode_t, .windows => windows.LARGE_INTEGER, .haiku => i64, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L38 else => u64, }; pub const off_t = switch (native_os) { .linux => linux.off_t, .emscripten => emscripten.off_t, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L39 else => i64, }; @@ -105,7 +108,8 @@ pub const timespec = switch (native_os) { @as(wasi.timestamp_t, @intCast(ts.nsec)); } }, - .windows => extern struct { + // https://github.com/SerenityOS/serenity/blob/0a78056453578c18e0a04a0b45ebfb1c96d59005/Kernel/API/POSIX/time.h#L17-L20 + .windows, .serenity => extern struct { sec: time_t, nsec: c_long, }, @@ -129,7 +133,8 @@ pub const dev_t = switch (native_os) { .emscripten => emscripten.dev_t, .wasi => wasi.device_t, .openbsd, .haiku, .solaris, .illumos, .macos, .ios, .tvos, .watchos, .visionos => i32, - .netbsd, .freebsd => u64, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L43 + .netbsd, .freebsd, .serenity => u64, else => void, }; @@ -137,7 +142,8 @@ pub const mode_t = switch (native_os) { .linux => linux.mode_t, .emscripten => emscripten.mode_t, .openbsd, .haiku, .netbsd, .solaris, .illumos, .wasi, .windows => u32, - .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .dragonfly => u16, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L44 + .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .dragonfly, .serenity => u16, else => u0, }; @@ -145,7 +151,8 @@ pub const nlink_t = switch (native_os) { .linux => linux.nlink_t, .emscripten => emscripten.nlink_t, .wasi => c_ulonglong, - .freebsd => u64, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L45 + .freebsd, .serenity => u64, .openbsd, .netbsd, .solaris, .illumos => u32, .haiku => i32, else => void, @@ -154,12 +161,14 @@ pub const nlink_t = switch (native_os) { pub const uid_t = switch (native_os) { .linux => linux.uid_t, .emscripten => emscripten.uid_t, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L28 else => u32, }; pub const gid_t = switch (native_os) { .linux => linux.gid_t, .emscripten => emscripten.gid_t, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L29 else => u32, }; @@ -167,11 +176,14 @@ pub const blksize_t = switch (native_os) { .linux => linux.blksize_t, .emscripten => emscripten.blksize_t, .wasi => c_long, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L42 + .serenity => u64, else => i32, }; pub const passwd = switch (native_os) { - .linux => extern struct { + // https://github.com/SerenityOS/serenity/blob/7442cfb5072b74a62c0e061e6e9ff44fda08780d/Userland/Libraries/LibC/pwd.h#L15-L23 + .linux, .serenity => extern struct { name: ?[*:0]const u8, // username passwd: ?[*:0]const u8, // user password uid: uid_t, // user ID @@ -199,6 +211,8 @@ pub const blkcnt_t = switch (native_os) { .linux => linux.blkcnt_t, .emscripten => emscripten.blkcnt_t, .wasi => c_longlong, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L41 + .serenity => u64, else => i64, }; @@ -206,6 +220,7 @@ pub const fd_t = switch (native_os) { .linux => linux.fd_t, .wasi => wasi.fd_t, .windows => windows.HANDLE, + .serenity => c_int, else => i32, }; @@ -309,6 +324,14 @@ pub const clockid_t = switch (native_os) { MONOTONIC = 3, THREAD_CPUTIME_ID = 4, }, + // https://github.com/SerenityOS/serenity/blob/0a78056453578c18e0a04a0b45ebfb1c96d59005/Kernel/API/POSIX/time.h#L24-L36 + .serenity => enum(c_int) { + REALTIME = 0, + MONOTONIC = 1, + MONOTONIC_RAW = 2, + REALTIME_COARSE = 3, + MONOTONIC_COARSE = 4, + }, else => void, }; pub const CPU_COUNT = switch (native_os) { @@ -681,6 +704,93 @@ pub const E = switch (native_os) { .dragonfly => dragonfly.E, .haiku => haiku.E, .openbsd => openbsd.E, + // https://github.com/SerenityOS/serenity/blob/dd59fe35c7e5bbaf6b6b3acb3f9edc56619d4b66/Kernel/API/POSIX/errno.h + .serenity => enum(c_int) { + SUCCESS = 0, + PERM = 1, + NOENT = 2, + SRCH = 3, + INTR = 4, + IO = 5, + NXIO = 6, + @"2BIG" = 7, + NOEXEC = 8, + BADF = 9, + CHILD = 10, + AGAIN = 11, + NOMEM = 12, + ACCES = 13, + FAULT = 14, + NOTBLK = 15, + BUSY = 16, + EXIST = 17, + XDEV = 18, + NODEV = 19, + NOTDIR = 20, + ISDIR = 21, + INVAL = 22, + NFILE = 23, + MFILE = 24, + NOTTY = 25, + TXTBSY = 26, + FBIG = 27, + NOSPC = 28, + SPIPE = 29, + ROFS = 30, + MLINK = 31, + PIPE = 32, + RANGE = 33, + NAMETOOLONG = 34, + LOOP = 35, + OVERFLOW = 36, + OPNOTSUPP = 37, + NOSYS = 38, + NOTIMPL = 39, + AFNOSUPPORT = 40, + NOTSOCK = 41, + ADDRINUSE = 42, + NOTEMPTY = 43, + DOM = 44, + CONNREFUSED = 45, + HOSTDOWN = 46, + ADDRNOTAVAIL = 47, + ISCONN = 48, + CONNABORTED = 49, + ALREADY = 50, + CONNRESET = 51, + DESTADDRREQ = 52, + HOSTUNREACH = 53, + ILSEQ = 54, + MSGSIZE = 55, + NETDOWN = 56, + NETUNREACH = 57, + NETRESET = 58, + NOBUFS = 59, + NOLCK = 60, + NOMSG = 61, + NOPROTOOPT = 62, + NOTCONN = 63, + SHUTDOWN = 64, + TOOMANYREFS = 65, + SOCKTNOSUPPORT = 66, + PROTONOSUPPORT = 67, + DEADLK = 68, + TIMEDOUT = 69, + PROTOTYPE = 70, + INPROGRESS = 71, + NOTHREAD = 72, + PROTO = 73, + NOTSUP = 74, + PFNOSUPPORT = 75, + DIRINTOSELF = 76, + DQUOT = 77, + NOTRECOVERABLE = 78, + CANCELED = 79, + PROMISEVIOLATION = 80, + STALE = 81, + SRCNOTFOUND = 82, + _, + }, else => void, }; pub const Elf_Symndx = switch (native_os) { @@ -1054,6 +1164,24 @@ pub const F = switch (native_os) { pub const UNLCK = 2; pub const WRLCK = 3; }, + .serenity => struct { + // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L15-L24 + pub const DUPFD = 0; + pub const GETFD = 1; + pub const SETFD = 2; + pub const GETFL = 3; + pub const SETFL = 4; + pub const ISTTY = 5; + pub const GETLK = 6; + pub const SETLK = 7; + pub const SETLKW = 8; + pub const DUPFD_CLOEXEC = 9; + + // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L45-L47 + pub const RDLCK = 0; + pub const WRLCK = 1; + pub const UNLCK = 2; + }, else => void, }; pub const FD_CLOEXEC = switch (native_os) { @@ -1129,18 +1257,29 @@ pub const Flock = switch (native_os) { len: off_t, pid: pid_t, }, + // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L54-L60 + .serenity => extern struct { + type: c_short, + whence: c_short, + start: off_t, + len: off_t, + pid: pid_t, + }, else => void, }; pub const HOST_NAME_MAX = switch (native_os) { .linux => linux.HOST_NAME_MAX, .macos, .ios, .tvos, .watchos, .visionos => 72, .openbsd, .haiku, .dragonfly, .netbsd, .solaris, .illumos, .freebsd => 255, + // https://github.com/SerenityOS/serenity/blob/c87557e9c1865fa1a6440de34ff6ce6fc858a2b7/Kernel/API/POSIX/sys/limits.h#L22 + .serenity => 64, else => {}, }; pub const IOV_MAX = switch (native_os) { .linux => linux.IOV_MAX, .emscripten => emscripten.IOV_MAX, - .openbsd, .haiku, .solaris, .illumos, .wasi => 1024, + // https://github.com/SerenityOS/serenity/blob/098af0f846a87b651731780ff48420205fd33754/Kernel/API/POSIX/sys/uio.h#L16 + .openbsd, .haiku, .solaris, .illumos, .wasi, .serenity => 1024, .macos, .ios, .tvos, .watchos, .visionos => 16, .dragonfly, .netbsd, .freebsd => KERN.IOV_MAX, else => {}, @@ -1425,6 +1564,16 @@ pub const MADV = switch (native_os) { pub const INVAL = 10; pub const SETMAP = 11; }, + // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L35-L41 + .serenity => struct { + pub const NORMAL = 0x0; + pub const SET_VOLATILE = 0x1; + pub const SET_NONVOLATILE = 0x2; + pub const DONTNEED = 0x3; + pub const WILLNEED = 0x4; + pub const SEQUENTIAL = 0x5; + pub const RANDOM = 0x6; + }, else => void, }; pub const MSF = switch (native_os) { @@ -1444,6 +1593,12 @@ pub const MSF = switch (native_os) { pub const INVALIDATE = 2; pub const SYNC = 4; }, + // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L50-L52 + .serenity => struct { + pub const SYNC = 1; + pub const ASYNC = 2; + pub const INVALIDATE = 4; + }, else => void, }; pub const MMAP2_UNIT = switch (native_os) { @@ -1456,7 +1611,8 @@ pub const NAME_MAX = switch (native_os) { // Haiku's headers make this 256, to contain room for the terminating null // character, but POSIX definition says that NAME_MAX does not include the // terminating null. - .haiku, .openbsd, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => 255, + // https://github.com/SerenityOS/serenity/blob/c87557e9c1865fa1a6440de34ff6ce6fc858a2b7/Kernel/API/POSIX/sys/limits.h#L20 + .haiku, .openbsd, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => 255, else => {}, }; pub const PATH_MAX = switch (native_os) { @@ -1464,7 +1620,7 @@ pub const PATH_MAX = switch (native_os) { .emscripten => emscripten.PATH_MAX, .wasi => 4096, .windows => 260, - .openbsd, .haiku, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => 1024, + .openbsd, .haiku, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => 1024, else => {}, }; @@ -1597,6 +1753,19 @@ pub const POLL = switch (native_os) { pub const RDBAND = 0x0080; pub const WRBAND = 0x0100; }, + // https://github.com/SerenityOS/serenity/blob/265764ff2fec038855193296588a887fc322d76a/Kernel/API/POSIX/poll.h#L15-L24 + .serenity => struct { + pub const IN = 0x0001; + pub const PRI = 0x0002; + pub const OUT = 0x0004; + pub const ERR = 0x0008; + pub const HUP = 0x0010; + pub const NVAL = 0x0020; + pub const RDNORM = IN; + pub const WRNORM = OUT; + pub const WRBAND = 0x1000; + pub const RDHUP = 0x2000; + }, else => void, }; @@ -1604,7 +1773,8 @@ pub const POLL = switch (native_os) { pub const PROT = switch (native_os) { .linux => linux.PROT, .emscripten => emscripten.PROT, - .openbsd, .haiku, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .windows => struct { + // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L28-L31 + .openbsd, .haiku, .dragonfly, .netbsd, .solaris, .illumos, .freebsd, .windows, .serenity => struct { /// page can not be accessed pub const NONE = 0x0; /// page can be read @@ -1719,7 +1889,8 @@ pub const REG = switch (native_os) { pub const RLIM = switch (native_os) { .linux => linux.RLIM, .emscripten => emscripten.RLIM, - .openbsd, .haiku, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => struct { + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/sys/resource.h#L52 + .openbsd, .haiku, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => struct { /// No limit pub const INFINITY: rlim_t = (1 << 63) - 1; @@ -2174,6 +2345,66 @@ pub const S = switch (native_os) { return m & IFMT == IFSOCK; } }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/sys/stat.h#L16-L51 + .serenity => struct { + pub const IFMT = 0o170000; + pub const IFDIR = 0o040000; + pub const IFCHR = 0o020000; + pub const IFBLK = 0o060000; + pub const IFREG = 0o100000; + pub const IFIFO = 0o010000; + pub const IFLNK = 0o120000; + pub const IFSOCK = 0o140000; + + pub const ISUID = 0o4000; + pub const ISGID = 0o2000; + pub const ISVTX = 0o1000; + pub const IRUSR = 0o400; + pub const IWUSR = 0o200; + pub const IXUSR = 0o100; + pub const IREAD = IRUSR; + pub const IWRITE = IWUSR; + pub const IEXEC = IXUSR; + pub const IRGRP = 0o040; + pub const IWGRP = 0o020; + pub const IXGRP = 0o010; + pub const IROTH = 0o004; + pub const IWOTH = 0o002; + pub const IXOTH = 0o001; + + pub const IRWXU = IRUSR | IWUSR | IXUSR; + + pub const IRWXG = IRWXU >> 3; + pub const IRWXO = IRWXG >> 3; + + pub fn ISDIR(m: u32) bool { + return m & IFMT == IFDIR; + } + + pub fn ISCHR(m: u32) bool { + return m & IFMT == IFCHR; + } + + pub fn ISBLK(m: u32) bool { + return m & IFMT == IFBLK; + } + + pub fn ISREG(m: u32) bool { + return m & IFMT == IFREG; + } + + pub fn ISFIFO(m: u32) bool { + return m & IFMT == IFIFO; + } + + pub fn ISLNK(m: u32) bool { + return m & IFMT == IFLNK; + } + + pub fn ISSOCK(m: u32) bool { + return m & IFMT == IFSOCK; + } + }, else => void, }; pub const SA = switch (native_os) { @@ -2254,6 +2485,18 @@ pub const SA = switch (native_os) { pub const NOCLDWAIT = 0x0020; pub const SIGINFO = 0x0040; }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L65-L71 + .serenity => struct { + pub const NOCLDSTOP = 1; + pub const NOCLDWAIT = 2; + pub const SIGINFO = 4; + pub const ONSTACK = 0x08000000; + pub const RESTART = 0x10000000; + pub const NODEFER = 0x40000000; + pub const RESETHAND = 0x80000000; + pub const NOMASK = NODEFER; + pub const ONESHOT = RESETHAND; + }, else => void, }; pub const sigval_t = switch (native_os) { @@ -2269,7 +2512,10 @@ pub const SC = switch (native_os) { else => void, }; -pub const _SC = switch (native_os) { +pub const _SC = if (builtin.abi.isAndroid()) enum(c_int) { + PAGESIZE = 39, + NPROCESSORS_ONLN = 97, +} else switch (native_os) { .driverkit, .ios, .macos, .tvos, .visionos, .watchos => enum(c_int) { PAGESIZE = 29, }, @@ -2298,6 +2544,24 @@ pub const _SC = switch (native_os) { PAGESIZE = 11, NPROCESSORS_ONLN = 15, }, + // https://github.com/SerenityOS/serenity/blob/1dfc9e2df39dd23f1de92530677c845aae4345f2/Kernel/API/POSIX/unistd.h#L36-L52 + .serenity => enum(c_int) { + MONOTONIC_CLOCK = 0, + NPROCESSORS_CONF = 1, + NPROCESSORS_ONLN = 2, + OPEN_MAX = 3, + HOST_NAME_MAX = 4, + TTY_NAME_MAX = 5, + PAGESIZE = 6, + GETPW_R_SIZE_MAX = 7, + GETGR_R_SIZE_MAX = 8, + CLK_TCK = 9, + SYMLOOP_MAX = 10, + MAPPED_FILES = 11, + ARG_MAX = 12, + IOV_MAX = 13, + PHYS_PAGES = 14, + }, else => void, }; @@ -2309,7 +2573,8 @@ pub const SEEK = switch (native_os) { pub const CUR: wasi.whence_t = .CUR; pub const END: wasi.whence_t = .END; }, - .openbsd, .haiku, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .windows => struct { + // https://github.com/SerenityOS/serenity/blob/808ce594db1f2190e5212a250e900bde2ffe710b/Kernel/API/POSIX/stdio.h#L15-L17 + .openbsd, .haiku, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .windows, .serenity => struct { pub const SET = 0; pub const CUR = 1; pub const END = 2; @@ -2326,6 +2591,7 @@ pub const SEEK = switch (native_os) { pub const SHUT = switch (native_os) { .linux => linux.SHUT, .emscripten => emscripten.SHUT, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L40-L42 else => struct { pub const RD = 0; pub const WR = 1; @@ -2785,6 +3051,42 @@ pub const SIG = switch (native_os) { pub const UNBLOCK = 2; pub const SETMASK = 3; }, + // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h + .serenity => struct { + pub const INVAL = 0; + pub const HUP = 1; + pub const INT = 2; + pub const QUIT = 3; + pub const ILL = 4; + pub const TRAP = 5; + pub const ABRT = 6; + pub const BUS = 7; + pub const FPE = 8; + pub const KILL = 9; + pub const USR1 = 10; + pub const SEGV = 11; + pub const USR2 = 12; + pub const PIPE = 13; + pub const ALRM = 14; + pub const TERM = 15; + pub const STKFLT = 16; + pub const CHLD = 17; + pub const CONT = 18; + pub const STOP = 19; + pub const TSTP = 20; + pub const TTIN = 21; + pub const TTOU = 22; + pub const URG = 23; + pub const XCPU = 24; + pub const XFSZ = 25; + pub const VTALRM = 26; + pub const PROF = 27; + pub const WINCH = 28; + pub const IO = 29; + pub const INFO = 30; + pub const SYS = 31; + pub const CANCEL = 32; + }, else => void, }; @@ -2792,6 +3094,8 @@ pub const SIOCGIFINDEX = switch (native_os) { .linux => linux.SIOCGIFINDEX, .emscripten => emscripten.SIOCGIFINDEX, .solaris, .illumos => solaris.SIOCGLIFINDEX, + // https://github.com/SerenityOS/serenity/blob/cb10f70394fb7e9cfc77f827adb2e46d199bc3a5/Kernel/API/Ioctl.h#L118 + .serenity => 34, else => void, }; @@ -2936,6 +3240,18 @@ pub const Sigaction = switch (native_os) { /// signal options flags: c_uint, }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L39-L46 + .serenity => extern struct { + pub const handler_fn = *align(1) const fn (c_int) callconv(.c) void; + pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*anyopaque) callconv(.c) void; + + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + flags: c_int, + }, else => void, }; pub const T = switch (native_os) { @@ -3311,6 +3627,22 @@ pub const T = switch (native_os) { pub const IOCGDRAINWAIT = 0x40047456; pub const IOCISPTMASTER = 0x20007455; }, + // https://github.com/SerenityOS/serenity/blob/cb10f70394fb7e9cfc77f827adb2e46d199bc3a5/Kernel/API/Ioctl.h#L84-L96 + .serenity => struct { + pub const IOCGPGRP = 0; + pub const IOCSPGRP = 1; + pub const CGETS = 2; + pub const CSETS = 3; + pub const CSETSW = 4; + pub const CSETSF = 5; + pub const CFLSH = 6; + pub const IOCGWINSZ = 7; + pub const IOCSCTTY = 8; + pub const IOCSTI = 9; + pub const IOCNOTTY = 10; + pub const IOCSWINSZ = 11; + pub const IOCGPTN = 12; + }, else => void, }; pub const IOCPARM_MASK = switch (native_os) { @@ -3546,6 +3878,43 @@ pub const W = switch (native_os) { return (((s) & 0o177) != 0o177) and (((s) & 0o177) != 0); } }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/sys/wait.h + .serenity => struct { + pub const NOHANG = 1; + pub const UNTRACED = 2; + pub const STOPPED = UNTRACED; + pub const EXITED = 4; + pub const CONTINUED = 8; + pub const NOWAIT = 0x1000000; + + pub fn EXITSTATUS(s: u32) u8 { + return @intCast((s & 0xff00) >> 8); + } + + pub fn STOPSIG(s: u32) u32 { + return EXITSTATUS(s); + } + + pub fn TERMSIG(s: u32) u32 { + return s & 0x7f; + } + + pub fn IFEXITED(s: u32) bool { + return TERMSIG(s) == 0; + } + + pub fn IFSTOPPED(s: u32) bool { + return (s & 0xff) == 0x7f; + } + + pub fn IFSIGNALED(s: u32) bool { + return (((s & 0x7f) + 1) >> 1) > 0; + } + + pub fn IFCONTINUED(s: u32) bool { + return s == 0xffff; + } + }, else => void, }; pub const clock_t = switch (native_os) { @@ -3556,6 +3925,8 @@ pub const clock_t = switch (native_os) { .openbsd, .solaris, .illumos => i64, .netbsd => u32, .haiku => i32, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L50 + .serenity => u64, else => void, }; pub const cpu_set_t = switch (native_os) { @@ -3568,7 +3939,7 @@ pub const dl_phdr_info = switch (native_os) { .emscripten => emscripten.dl_phdr_info, .freebsd => extern struct { /// Module relocation base. - addr: if (builtin.target.ptrBitWidth() == 32) std.elf.Elf32_Addr else std.elf.Elf64_Addr, + addr: std.elf.Addr, /// Module name. name: ?[*:0]const u8, /// Pointer to module's phdr. @@ -3592,11 +3963,12 @@ pub const dl_phdr_info = switch (native_os) { /// Incremented when an object is unmapped from the process. subs: u64, }, - .openbsd, .haiku, .dragonfly, .netbsd => extern struct { + // https://github.com/SerenityOS/serenity/blob/45d81dceed81df0c8ef75b440b20cc0938195faa/Userland/Libraries/LibC/link.h#L15-L20 + .openbsd, .haiku, .dragonfly, .netbsd, .serenity => extern struct { addr: usize, name: ?[*:0]const u8, phdr: [*]std.elf.Phdr, - phnum: u16, + phnum: std.elf.Half, }, else => void, }; @@ -3608,6 +3980,26 @@ pub const ifreq = switch (native_os) { .linux => linux.ifreq, .emscripten => emscripten.ifreq, .solaris, .illumos => lifreq, + // https://github.com/SerenityOS/serenity/blob/9882848e0bf783dfc8e8a6d887a848d70d9c58f4/Kernel/API/POSIX/net/if.h#L49-L82 + .serenity => extern struct { + // Not actually in a union, but the stdlib expects one for ifreq + ifrn: extern union { + name: [IFNAMESIZE]u8, + }, + ifru: extern union { + addr: sockaddr, + dstaddr: sockaddr, + broadaddr: sockaddr, + netmask: sockaddr, + hwaddr: sockaddr, + flags: c_short, + metric: c_int, + vnetid: i64, + media: u64, + data: ?*anyopaque, + index: c_uint, + }, + }, else => void, }; pub const itimerspec = switch (native_os) { @@ -3650,6 +4042,16 @@ pub const msghdr = switch (native_os) { /// flags on received message flags: i32, }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L74-L82 + .serenity => extern struct { + name: ?*anyopaque, + namelen: socklen_t, + iov: [*]iovec, + iovlen: c_int, + control: ?*anyopaque, + controllen: socklen_t, + flags: c_int, + }, else => void, }; pub const msghdr_const = switch (native_os) { @@ -3684,6 +4086,15 @@ pub const msghdr_const = switch (native_os) { /// flags on received message flags: i32, }, + .serenity => extern struct { + name: ?*const anyopaque, + namelen: socklen_t, + iov: [*]const iovec, + iovlen: c_int, + control: ?*const anyopaque, + controllen: socklen_t, + flags: c_int, + }, else => void, }; pub const nfds_t = switch (native_os) { @@ -3692,6 +4103,8 @@ pub const nfds_t = switch (native_os) { .haiku, .solaris, .illumos, .wasi => usize, .windows => c_ulong, .openbsd, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => u32, + // https://github.com/SerenityOS/serenity/blob/265764ff2fec038855193296588a887fc322d76a/Kernel/API/POSIX/poll.h#L32 + .serenity => c_uint, else => void, }; pub const perf_event_attr = switch (native_os) { @@ -3702,12 +4115,20 @@ pub const pid_t = switch (native_os) { .linux => linux.pid_t, .emscripten => emscripten.pid_t, .windows => windows.HANDLE, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L31-L32 + .serenity => c_int, else => i32, }; pub const pollfd = switch (native_os) { .linux => linux.pollfd, .emscripten => emscripten.pollfd, .windows => ws2_32.pollfd, + // https://github.com/SerenityOS/serenity/blob/265764ff2fec038855193296588a887fc322d76a/Kernel/API/POSIX/poll.h#L26-L30 + .serenity => extern struct { + fd: fd_t, + events: c_short, + revents: c_short, + }, else => extern struct { fd: fd_t, events: i16, @@ -3719,11 +4140,14 @@ pub const rlim_t = switch (native_os) { .emscripten => emscripten.rlim_t, .openbsd, .netbsd, .solaris, .illumos, .macos, .ios, .tvos, .watchos, .visionos => u64, .haiku, .dragonfly, .freebsd => i64, + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/sys/resource.h#L54 + .serenity => usize, else => void, }; pub const rlimit = switch (native_os) { .linux, .emscripten => linux.rlimit, .windows => void, + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/sys/resource.h#L56-L59 else => extern struct { /// Soft limit cur: rlim_t, @@ -3825,6 +4249,17 @@ pub const rlimit_resource = switch (native_os) { NOVMON = 7, _, }, + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/sys/resource.h#L42-L48 + .serenity => enum(c_int) { + CORE = 1, + CPU = 2, + DATA = 3, + FSIZE = 4, + NOFILE = 5, + STACK = 6, + AS = 7, + _, + }, else => void, }; pub const rusage = switch (native_os) { @@ -3873,6 +4308,28 @@ pub const rusage = switch (native_os) { pub const CHILDREN = -1; pub const THREAD = 1; }, + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/sys/resource.h#L18-L38 + .serenity => extern struct { + utime: timeval, + stime: timeval, + maxrss: c_long, + ixrss: c_long, + idrss: c_long, + isrss: c_long, + minflt: c_long, + majflt: c_long, + nswap: c_long, + inblock: c_long, + oublock: c_long, + msgsnd: c_long, + msgrcv: c_long, + nsignals: c_long, + nvcsw: c_long, + nivcsw: c_long, + + pub const SELF = 1; + pub const CHILDREN = 2; + }, else => void, }; @@ -4050,12 +4507,25 @@ pub const siginfo_t = switch (native_os) { assert(@sizeOf(@This()) == 136); } }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L27-L37 + .serenity => extern struct { + signo: c_int, + code: c_int, + errno: c_int, + pid: pid_t, + uid: uid_t, + addr: ?*anyopaque, + status: c_int, + band: c_int, + value: sigval, + }, else => void, }; pub const sigset_t = switch (native_os) { .linux => linux.sigset_t, .emscripten => emscripten.sigset_t, - .openbsd, .macos, .ios, .tvos, .watchos, .visionos => u32, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L19 + .openbsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => u32, .dragonfly, .netbsd, .solaris, .illumos, .freebsd => extern struct { __bits: [SIG.WORDS]u32, }, @@ -4075,7 +4545,8 @@ pub const filled_sigset = switch (native_os) { }; pub const sigval = switch (native_os) { .linux => linux.sigval, - .openbsd, .dragonfly, .freebsd => extern union { + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L22-L25 + .openbsd, .dragonfly, .freebsd, .serenity => extern union { int: c_int, ptr: ?*anyopaque, }, @@ -4144,7 +4615,8 @@ pub const addrinfo = if (builtin.abi.isAndroid()) extern struct { addr: ?*sockaddr, next: ?*addrinfo, }, - .openbsd => extern struct { + // https://github.com/SerenityOS/serenity/blob/d510d2aeb2facbd8f6c383d70fd1b033e1fee5dd/Userland/Libraries/LibC/netdb.h#L66-L75 + .openbsd, .serenity => extern struct { flags: AI, family: c_int, socktype: c_int, @@ -4460,11 +4932,41 @@ pub const sockaddr = switch (native_os) { path: [104]u8, }; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L110-L114 + .serenity => extern struct { + family: sa_family_t, + data: [26]u8, + + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/netinet/in.h + const in_addr = u32; + const in6_addr = [16]u8; + pub const in = extern struct { + family: sa_family_t = AF.INET, + port: in_port_t, + addr: in_addr, + zero: [8]u8 = @splat(0), + }; + pub const in6 = extern struct { + family: sa_family_t = AF.INET6, + port: in_port_t, + flowinfo: u32, + addr: in6_addr, + scope_id: u32, + }; + + // https://github.com/SerenityOS/serenity/blob/b92e6b02e53b2927732f31b1442cad420b62d1ef/Kernel/API/POSIX/sys/un.h + const UNIX_PATH_MAX = 108; + pub const un = extern struct { + family: sa_family_t = AF.LOCAL, + path: [UNIX_PATH_MAX]u8, + }; + }, else => void, }; pub const socklen_t = switch (native_os) { .linux, .emscripten => linux.socklen_t, .windows => ws2_32.socklen_t, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L57 else => u32, }; pub const in_port_t = u16; @@ -4472,7 +4974,8 @@ pub const sa_family_t = switch (native_os) { .linux, .emscripten => linux.sa_family_t, .windows => ws2_32.ADDRESS_FAMILY, .openbsd, .haiku, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => u8, - .solaris, .illumos => u16, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L66 + .solaris, .illumos, .serenity => u16, else => void, }; pub const AF = if (builtin.abi.isAndroid()) struct { @@ -4751,6 +5254,15 @@ pub const AF = if (builtin.abi.isAndroid()) struct { pub const ISDN = 26; pub const MAX = 36; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L17-L22 + .serenity => struct { + pub const UNSPEC = 0; + pub const LOCAL = 1; + pub const UNIX = LOCAL; + pub const INET = 2; + pub const INET6 = 3; + pub const MAX = 4; + }, else => void, }; pub const PF = if (builtin.abi.isAndroid()) struct { @@ -5020,11 +5532,21 @@ pub const PF = if (builtin.abi.isAndroid()) struct { pub const ISDN = AF.ISDN; pub const MAX = AF.MAX; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L24-L29 + .serenity => struct { + pub const LOCAL = AF.LOCAL; + pub const UNIX = AF.LOCAL; + pub const INET = AF.INET; + pub const INET6 = AF.INET6; + pub const UNSPEC = AF.UNSPEC; + pub const MAX = AF.MAX; + }, else => void, }; pub const DT = switch (native_os) { .linux => linux.DT, - .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => struct { + // https://github.com/SerenityOS/serenity/blob/1262a7d1424d0d2e89d80644409721cbf056ab17/Kernel/API/POSIX/dirent.h#L16-L35 + .netbsd, .freebsd, .openbsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => struct { pub const UNKNOWN = 0; pub const FIFO = 1; pub const CHR = 2; @@ -5047,17 +5569,6 @@ pub const DT = switch (native_os) { pub const WHT = 14; pub const DBF = 15; }, - .openbsd => struct { - pub const UNKNOWN = 0; - pub const FIFO = 1; - pub const CHR = 2; - pub const DIR = 4; - pub const BLK = 6; - pub const REG = 8; - pub const LNK = 10; - pub const SOCK = 12; - pub const WHT = 14; // XXX - }, else => void, }; pub const MSG = switch (native_os) { @@ -5078,6 +5589,18 @@ pub const MSG = switch (native_os) { pub const EOF = 0x0400; pub const NOSIGNAL = 0x0800; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L56-L64 + .serenity => struct { + pub const TRUNC = 0x1; + pub const CTRUNC = 0x2; + pub const PEEK = 0x4; + pub const OOB = 0x8; + pub const DONTROUTE = 0x10; + pub const WAITALL = 0x20; + pub const DONTWAIT = 0x40; + pub const NOSIGNAL = 0x80; + pub const EOR = 0x100; + }, else => void, }; pub const SOCK = switch (native_os) { @@ -5168,6 +5691,17 @@ pub const SOCK = switch (native_os) { pub const CLOEXEC = 0x8000; pub const NONBLOCK = 0x4000; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L31-L38 + .serenity => struct { + pub const STREAM = 1; + pub const DGRAM = 2; + pub const RAW = 3; + pub const RDM = 4; + pub const SEQPACKET = 5; + + pub const NONBLOCK = 0o4000; + pub const CLOEXEC = 0o2000000; + }, else => void, }; pub const TCP = switch (native_os) { @@ -5175,6 +5709,11 @@ pub const TCP = switch (native_os) { .linux => linux.TCP, .emscripten => emscripten.TCP, .windows => ws2_32.TCP, + // https://github.com/SerenityOS/serenity/blob/61ac554a3403838f79ca746bd1c65ded6f97d124/Kernel/API/POSIX/netinet/tcp.h#L13-L14 + .serenity => struct { + pub const NODELAY = 10; + pub const MAXSEG = 11; + }, else => void, }; pub const IPPROTO = switch (native_os) { @@ -5759,6 +6298,20 @@ pub const IPPROTO = switch (native_os) { /// raw IP packet pub const RAW = 255; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L44-L54 + .serenity => struct { + pub const IP = 0; + pub const ICMP = 1; + pub const IGMP = 2; + pub const IPIP = 4; + pub const TCP = 6; + pub const UDP = 17; + pub const IPV6 = 41; + pub const ESP = 50; + pub const AH = 51; + pub const ICMPV6 = 58; + pub const RAW = 255; + }, else => void, }; pub const SOL = switch (native_os) { @@ -5774,6 +6327,10 @@ pub const SOL = switch (native_os) { pub const PACKET = 0xfffd; pub const FILTER = 0xfffc; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L127 + .serenity => struct { + pub const SOCKET = 1; + }, else => void, }; pub const SO = switch (native_os) { @@ -5991,12 +6548,35 @@ pub const SO = switch (native_os) { pub const DOMAIN = 0x1024; pub const PROTOCOL = 0x1025; }, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L130-L150 + .serenity => struct { + pub const RCVTIMEO = 0; + pub const SNDTIMEO = 1; + pub const TYPE = 2; + pub const ERROR = 3; + pub const PEERCRED = 4; + pub const RCVBUF = 5; + pub const SNDBUF = 6; + pub const DEBUG = 7; + pub const REUSEADDR = 8; + pub const BINDTODEVICE = 9; + pub const KEEPALIVE = 10; + pub const TIMESTAMP = 11; + pub const BROADCAST = 12; + pub const LINGER = 13; + pub const ACCEPTCONN = 14; + pub const DONTROUTE = 15; + pub const OOBINLINE = 16; + pub const SNDLOWAT = 17; + pub const RCVLOWAT = 18; + }, else => void, }; pub const SOMAXCONN = switch (native_os) { .linux => linux.SOMAXCONN, .windows => ws2_32.SOMAXCONN, - .solaris, .illumos => 128, + // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L128 + .solaris, .illumos, .serenity => 128, .openbsd => 28, else => void, }; @@ -6004,7 +6584,8 @@ pub const IFNAMESIZE = switch (native_os) { .linux => linux.IFNAMESIZE, .emscripten => emscripten.IFNAMESIZE, .windows => 30, - .openbsd, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos => 16, + // https://github.com/SerenityOS/serenity/blob/9882848e0bf783dfc8e8a6d887a848d70d9c58f4/Kernel/API/POSIX/net/if.h#L50 + .openbsd, .dragonfly, .netbsd, .freebsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => 16, .solaris, .illumos => 32, else => void, }; @@ -6020,6 +6601,12 @@ pub const stack_t = switch (native_os) { /// SS_DISABLE and/or SS_ONSTACK. flags: i32, }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L48-L52 + .serenity => extern struct { + sp: *anyopaque, + flags: c_int, + size: usize, + }, else => extern struct { sp: [*]u8, size: isize, @@ -6030,10 +6617,12 @@ pub const time_t = switch (native_os) { .linux => linux.time_t, .emscripten => emscripten.time_t, .haiku, .dragonfly => isize, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L47 else => i64, }; pub const suseconds_t = switch (native_os) { - .solaris, .illumos => i64, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L49 + .solaris, .illumos, .serenity => i64, .freebsd, .dragonfly => c_long, .netbsd => c_int, .haiku => i32, @@ -6051,7 +6640,8 @@ pub const timeval = switch (native_os) { sec: c_long, usec: i32, }, - .dragonfly, .netbsd, .freebsd, .solaris, .illumos => extern struct { + // https://github.com/SerenityOS/serenity/blob/6b6eca0631c893c5f8cfb8274cdfe18e2d0637c0/Kernel/API/POSIX/sys/time.h#L15-L18 + .dragonfly, .netbsd, .freebsd, .solaris, .illumos, .serenity => extern struct { /// seconds sec: time_t, /// microseconds @@ -6070,6 +6660,11 @@ pub const timezone = switch (native_os) { minuteswest: i32, dsttime: i32, }, + // https://github.com/SerenityOS/serenity/blob/ba776390b5878ec0be1a9e595a3471a6cfe0a0cf/Userland/Libraries/LibC/sys/time.h#L19-L22 + .serenity => extern struct { + minuteswest: c_int, + dsttime: c_int, + }, else => void, }; @@ -6127,7 +6722,8 @@ pub const ucontext_t = switch (native_os) { arg: ?*void, _spare: [4]c_int, }, - .haiku => extern struct { + // https://github.com/SerenityOS/serenity/blob/87eac0e424cff4a1f941fb704b9362a08654c24d/Kernel/API/POSIX/ucontext.h#L19-L24 + .haiku, .serenity => extern struct { link: ?*ucontext_t, sigmask: sigset_t, stack: stack_t, @@ -6222,6 +6818,47 @@ pub const mcontext_t = switch (native_os) { }, .dragonfly => dragonfly.mcontext_t, .haiku => haiku.mcontext_t, + .serenity => switch (native_arch) { + // https://github.com/SerenityOS/serenity/blob/200e91cd7f1ec5453799a2720d4dc114a59cc289/Kernel/Arch/aarch64/mcontext.h#L15-L19 + .aarch64 => extern struct { + x: [31]u64, + sp: u64, + pc: u64, + }, + // https://github.com/SerenityOS/serenity/blob/66f8d0f031ef25c409dbb4fecaa454800fecae0f/Kernel/Arch/riscv64/mcontext.h#L15-L18 + .riscv64 => extern struct { + x: [31]u64, + pc: u64, + }, + // https://github.com/SerenityOS/serenity/blob/7b9ea3efdec9f86a1042893e8107d0b23aad8727/Kernel/Arch/x86_64/mcontext.h#L15-L40 + .x86_64 => extern struct { + rax: u64, + rcx: u64, + rdx: u64, + rbx: u64, + rsp: u64, + rbp: u64, + rsi: u64, + rdi: u64, + rip: u64, + r8: u64, + r9: u64, + r10: u64, + r11: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + rflags: u64, + cs: u32, + ss: u32, + ds: u32, + es: u32, + fs: u32, + gs: u32, + }, + else => struct {}, + }, else => void, }; @@ -6247,6 +6884,16 @@ pub const utsname = switch (native_os) { version: [256:0]u8, machine: [256:0]u8, }, + // https://github.com/SerenityOS/serenity/blob/d794ed1de7a46482272683f8dc4c858806390f29/Kernel/API/POSIX/sys/utsname.h#L17-L23 + .serenity => extern struct { + sysname: [UTSNAME_ENTRY_LEN:0]u8, + nodename: [UTSNAME_ENTRY_LEN:0]u8, + release: [UTSNAME_ENTRY_LEN:0]u8, + version: [UTSNAME_ENTRY_LEN:0]u8, + machine: [UTSNAME_ENTRY_LEN:0]u8, + + const UTSNAME_ENTRY_LEN = 65; + }, else => void, }; pub const PR = switch (native_os) { @@ -6265,6 +6912,8 @@ pub const _errno = switch (native_os) { .solaris, .illumos => private.___errno, .openbsd, .netbsd => private.__errno, .haiku => haiku._errnop, + // https://github.com/SerenityOS/serenity/blob/a353ceecf13b6f156a078e32f1ddf1d21366934c/Userland/Libraries/LibC/errno.h#L33 + .serenity => private.__errno_location, else => {}, }; @@ -6340,6 +6989,16 @@ pub const RTLD = switch (native_os) { FIRST: bool = false, _: u23 = 0, }, + // https://github.com/SerenityOS/serenity/blob/36a26d7fa80bc9c72b19442912d8967f448368ff/Userland/Libraries/LibC/dlfcn.h#L13-L17 + .serenity => packed struct(c_int) { + DEFAULT: bool = false, + _1: u1, + LAZY: bool = false, + NOW: bool = false, + GLOBAL: bool = false, + LOCAL: bool = false, + _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 6) = 0, + }, else => void, }; @@ -6413,11 +7072,20 @@ pub const dirent = switch (native_os) { _: u32 align(1) = 0, name: [MAXNAMLEN:0]u8, }, + // https://github.com/SerenityOS/serenity/blob/abc150085f532f123b598949218893cb272ccc4c/Userland/Libraries/LibC/dirent.h#L14-L20 + .serenity => extern struct { + ino: ino_t, + off: off_t, + reclen: c_ushort, + type: u8, + name: [256:0]u8, + }, else => void, }; pub const MAXNAMLEN = switch (native_os) { .netbsd, .solaris, .illumos => 511, - .haiku => NAME_MAX, + // https://github.com/SerenityOS/serenity/blob/1262a7d1424d0d2e89d80644409721cbf056ab17/Kernel/API/POSIX/dirent.h#L37 + .haiku, .serenity => NAME_MAX, .openbsd => 255, else => {}, }; @@ -6499,6 +7167,17 @@ pub const AI = if (builtin.abi.isAndroid()) packed struct(u32) { _: u19 = 0, }, .windows => ws2_32.AI, + // https://github.com/SerenityOS/serenity/blob/d510d2aeb2facbd8f6c383d70fd1b033e1fee5dd/Userland/Libraries/LibC/netdb.h#L90-L96 + .serenity => packed struct(c_int) { + PASSIVE: bool = false, + CANONNAME: bool = false, + NUMERICHOST: bool = false, + NUMERICSERV: bool = false, + V4MAPPED: bool = false, + ALL: bool = false, + ADDRCONFIG: bool = false, + _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 7) = 0, + }, else => void, }; @@ -6523,6 +7202,15 @@ pub const NI = switch (native_os) { NUMERICSCOPE: bool = false, _: u25 = 0, }, + // https://github.com/SerenityOS/serenity/blob/d510d2aeb2facbd8f6c383d70fd1b033e1fee5dd/Userland/Libraries/LibC/netdb.h#L101-L105 + .serenity => packed struct(c_int) { + NUMERICHOST: bool = false, + NUMERICSERV: bool = false, + NAMEREQD: bool = false, + NOFQDN: bool = false, + DGRAM: bool = false, + _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 5) = 0, + }, else => void, }; @@ -6675,6 +7363,22 @@ pub const EAI = if (builtin.abi.isAndroid()) enum(c_int) { OVERFLOW = -14, _, }, + // https://github.com/SerenityOS/serenity/blob/d510d2aeb2facbd8f6c383d70fd1b033e1fee5dd/Userland/Libraries/LibC/netdb.h#L77-L88 + .serenity => enum(c_int) { + ADDRFAMILY = 1, + AGAIN = 2, + BADFLAGS = 3, + FAIL = 4, + FAMILY = 5, + MEMORY = 6, + NODATA = 7, + NONAME = 8, + SERVICE = 9, + SOCKTYPE = 10, + SYSTEM = 11, + OVERFLOW = 12, + _, + }, else => void, }; @@ -7097,6 +7801,34 @@ pub const Stat = switch (native_os) { return self.birthtim; } }, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/sys/stat.h#L53-L67 + .serenity => extern struct { + dev: dev_t, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + size: off_t, + blksize: blksize_t, + blocks: blkcnt_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + + pub fn atime(self: @This()) timespec { + return self.atim; + } + + pub fn mtime(self: @This()) timespec { + return self.mtim; + } + + pub fn ctime(self: @This()) timespec { + return self.ctim; + } + }, else => void, }; @@ -7159,6 +7891,13 @@ pub const pthread_mutex_t = switch (native_os) { .emscripten => extern struct { data: [24]u8 align(4) = [_]u8{0} ** 24, }, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L68-L73 + .serenity => extern struct { + lock: u32 = 0, + owner: pthread_t = 0, + level: c_int = 0, + type: c_int = 0, + }, else => void, }; @@ -7201,6 +7940,12 @@ pub const pthread_cond_t = switch (native_os) { .fuchsia, .emscripten => extern struct { data: [48]u8 align(@alignOf(usize)) = [_]u8{0} ** 48, }, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L80-L84 + .serenity => extern struct { + mutex: ?*pthread_mutex_t = null, + value: u32 = 0, + clockid: clockid_t = .REALTIME_COARSE, + }, else => void, }; @@ -7258,6 +8003,10 @@ pub const pthread_rwlock_t = switch (native_os) { .emscripten => extern struct { size: [32]u8 align(4) = [_]u8{0} ** 32, }, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L86 + .serenity => extern struct { + inner: u64 = 0, + }, else => void, }; @@ -7270,7 +8019,8 @@ pub const pthread_attr_t = switch (native_os) { __sig: c_long, __opaque: [56]u8, }, - .freebsd => extern struct { + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L75 + .freebsd, .openbsd, .serenity => extern struct { inner: ?*anyopaque = null, }, .solaris, .illumos => extern struct { @@ -7288,16 +8038,14 @@ pub const pthread_attr_t = switch (native_os) { guard_size: i32, stack_address: ?*anyopaque, }, - .openbsd => extern struct { - inner: ?*anyopaque = null, - }, else => void, }; pub const pthread_key_t = switch (native_os) { .linux, .emscripten => c_uint, .macos, .ios, .tvos, .watchos, .visionos => c_ulong, - .openbsd, .solaris, .illumos => c_int, + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L65 + .openbsd, .solaris, .illumos, .serenity => c_int, else => void, }; @@ -7353,6 +8101,12 @@ pub const sem_t = switch (native_os) { }, padding: [2]i32, }, + // https://github.com/SerenityOS/serenity/blob/aae106e37b48f2158e68902293df1e4bf7b80c0f/Userland/Libraries/LibC/semaphore.h#L23-L27 + .serenity => extern struct { + magic: u32, + value: u32, + flags: u8, + }, else => void, }; @@ -7556,6 +8310,13 @@ pub const AT = switch (native_os) { /// file. pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3; }, + // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L49-L52 + .serenity => struct { + pub const FDCWD = -100; + pub const SYMLINK_NOFOLLOW = 0x100; + pub const REMOVEDIR = 0x200; + pub const EACCESS = 0x400; + }, else => void, }; @@ -7771,6 +8532,23 @@ pub const O = switch (native_os) { TMPFILE: bool = false, _: u9 = 0, }, + // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L28-L43 + .serenity => packed struct(c_int) { + ACCMODE: std.posix.ACCMODE = .RDONLY, + EXEC: bool = false, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + CLOEXEC: bool = false, + DIRECT: bool = false, + SYNC: bool = false, + _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 14) = 0, + }, else => void, }; @@ -7910,6 +8688,21 @@ pub const MAP = switch (native_os) { @"32BIT": bool = false, _: u12 = 0, }, + // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L16-L26 + .serenity => packed struct(c_int) { + FILE: bool = false, + SHARED: bool = false, + PRIVATE: bool = false, + _3: u2 = 0, + FIXED: bool = false, + ANONYMOUS: bool = false, + STACK: bool = false, + NORESERVE: bool = false, + RANDOMIZED: bool = false, + PURGEABLE: bool = false, + FIXED_NOREPLACE: bool = false, + _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 12) = 0, + }, else => void, }; @@ -8020,6 +8813,27 @@ pub const V = switch (native_os) { LNEXT, EOL2, }, + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L32-L49 + .serenity => enum { + INTR, + QUIT, + ERASE, + KILL, + EOF, + TIME, + MIN, + SWTC, + START, + STOP, + SUSP, + EOL, + REPRINT, + DISCARD, + WERASE, + LNEXT, + EOL2, + INFO, + }, else => void, }; @@ -8028,7 +8842,8 @@ pub const NCCS = switch (native_os) { .macos, .ios, .tvos, .watchos, .visionos, .freebsd, .netbsd, .openbsd, .dragonfly => 20, .haiku => 11, .solaris, .illumos => 19, - .emscripten, .wasi => 32, + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L15 + .emscripten, .wasi, .serenity => 32, else => void, }; @@ -8043,7 +8858,8 @@ pub const termios = switch (native_os) { ispeed: speed_t align(8), ospeed: speed_t, }, - .freebsd, .netbsd, .dragonfly, .openbsd => extern struct { + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L21-L29 + .freebsd, .netbsd, .dragonfly, .openbsd, .serenity => extern struct { iflag: tc_iflag_t, oflag: tc_oflag_t, cflag: tc_cflag_t, @@ -8171,7 +8987,8 @@ pub const tc_iflag_t = switch (native_os) { DOSMODE: bool = false, _: u16 = 0, }, - .emscripten, .wasi => packed struct(u32) { + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L52-L66 + .emscripten, .wasi, .serenity => packed struct(u32) { IGNBRK: bool = false, BRKINT: bool = false, IGNPAR: bool = false, @@ -8263,7 +9080,8 @@ pub const tc_oflag_t = switch (native_os) { WRAP: bool = false, _: u14 = 0, }, - .haiku, .wasi, .emscripten => packed struct(u32) { + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L69-L97 + .haiku, .wasi, .emscripten, .serenity => packed struct(u32) { OPOST: bool = false, OLCUC: bool = false, ONLCR: bool = false, @@ -8422,6 +9240,19 @@ pub const tc_cflag_t = switch (native_os) { CLOCAL: bool = false, _: u20 = 0, }, + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L131-L141 + .serenity => packed struct(u32) { + _0: u4 = 0, + CSIZE: CSIZE = .CS5, + CSTOPB: bool = false, + CREAD: bool = false, + PARENB: bool = false, + PARODD: bool = false, + HUPCL: bool = false, + CLOCAL: bool = false, + CBAUDEX: bool = false, + _: u19 = 0, + }, else => void, }; @@ -8548,6 +9379,27 @@ pub const tc_lflag_t = switch (native_os) { IEXTEN: bool = false, _: u16 = 0, }, + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L168-L189 + .serenity => packed struct(u32) { + ISIG: bool = false, + ICANON: bool = false, + XCASE: bool = false, + ECHO: bool = false, + ECHOE: bool = false, + ECHOK: bool = false, + ECHONL: bool = false, + NOFLSH: bool = false, + TOSTOP: bool = false, + ECHOCTL: bool = false, + ECHOPRT: bool = false, + ECHOKE: bool = false, + FLUSHO: bool = false, + PENDIN: bool = false, + _14: u6 = 0, + IEXTEN: bool = false, + EXTPROC: bool = false, + _: u15 = 0, + }, else => void, }; @@ -8696,7 +9548,8 @@ pub const speed_t = switch (native_os) { B3500000 = 30, B4000000 = 31, }, - .emscripten, .wasi => enum(u32) { + // https://github.com/SerenityOS/serenity/blob/d277cdfd4c7ed21d5248a83217ae03b9f890c3c8/Kernel/API/POSIX/termios.h#L111-L159 + .emscripten, .wasi, .serenity => enum(u32) { B0 = 0o0000000, B50 = 0o0000001, B75 = 0o0000002, @@ -8735,7 +9588,11 @@ pub const speed_t = switch (native_os) { pub const whence_t = if (native_os == .wasi) std.os.wasi.whence_t else c_int; -pub const sig_atomic_t = c_int; +pub const sig_atomic_t = switch (native_os) { + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L20 + .serenity => u32, + else => c_int, +}; /// maximum signal number + 1 pub const NSIG = switch (native_os) { @@ -8744,7 +9601,8 @@ pub const NSIG = switch (native_os) { .haiku => 65, .netbsd, .freebsd => 32, .solaris, .illumos => 75, - .openbsd => 33, + // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h#L42 + .openbsd, .serenity => 33, else => {}, }; @@ -8758,6 +9616,8 @@ pub const MINSIGSTKSZ = switch (native_os) { .solaris, .illumos => 2048, .haiku, .netbsd => 8192, .openbsd => 1 << openbsd.MAX_PAGE_SHIFT, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L58 + .serenity => 4096, else => {}, }; pub const SIGSTKSZ = switch (native_os) { @@ -8766,6 +9626,8 @@ pub const SIGSTKSZ = switch (native_os) { .solaris, .illumos => 8192, .haiku => 16384, .openbsd => MINSIGSTKSZ + (1 << openbsd.MAX_PAGE_SHIFT) * 4, + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L59 + .serenity => 32768, else => {}, }; pub const SS = switch (native_os) { @@ -8774,7 +9636,8 @@ pub const SS = switch (native_os) { pub const ONSTACK = 1; pub const DISABLE = 4; }, - .haiku, .solaris, .illumos => struct { + // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L54-L55 + .haiku, .solaris, .illumos, .serenity => struct { pub const ONSTACK = 0x1; pub const DISABLE = 0x2; }, @@ -9267,6 +10130,12 @@ pub const NOTE = switch (native_os) { else => void, }; +pub const FUTEX = switch (native_os) { + .openbsd => openbsd.FUTEX, + .serenity => serenity.FUTEX, + else => void, +}; + // Unix-like systems pub const DIR = opaque {}; pub extern "c" fn opendir(pathname: [*:0]const u8) ?*DIR; @@ -9327,7 +10196,7 @@ pub extern "c" fn sendfile64(out_fd: fd_t, in_fd: fd_t, offset: ?*i64, count: us pub extern "c" fn setrlimit64(resource: rlimit_resource, rlim: *const rlimit) c_int; pub const arc4random_buf = switch (native_os) { - .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .macos, .ios, .tvos, .watchos, .visionos => private.arc4random_buf, + .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => private.arc4random_buf, else => {}, }; pub const getentropy = switch (native_os) { @@ -9395,7 +10264,7 @@ pub const sigaltstack = switch (native_os) { pub extern "c" fn memfd_create(name: [*:0]const u8, flags: c_uint) c_int; pub const pipe2 = switch (native_os) { - .dragonfly, .emscripten, .netbsd, .freebsd, .solaris, .illumos, .openbsd, .linux => private.pipe2, + .dragonfly, .emscripten, .netbsd, .freebsd, .solaris, .illumos, .openbsd, .linux, .serenity => private.pipe2, else => {}, }; pub const copy_file_range = switch (native_os) { @@ -9501,15 +10370,15 @@ pub const _msize = switch (native_os) { else => {}, }; pub const malloc_size = switch (native_os) { - .macos, .ios, .tvos, .watchos, .visionos => private.malloc_size, + .macos, .ios, .tvos, .watchos, .visionos, .serenity => private.malloc_size, else => {}, }; pub const malloc_usable_size = switch (native_os) { - .freebsd, .linux => private.malloc_usable_size, + .freebsd, .linux, .serenity => private.malloc_usable_size, else => {}, }; pub const posix_memalign = switch (native_os) { - .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos => private.posix_memalign, + .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos, .serenity => private.posix_memalign, else => {}, }; pub const sysconf = switch (native_os) { @@ -9532,6 +10401,12 @@ pub const flock = switch (native_os) { else => private.flock, }; +pub const futex = switch (native_os) { + .openbsd => openbsd.futex, + .serenity => serenity.futex, + else => {}, +}; + pub extern "c" var environ: [*:null]?[*:0]u8; pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; @@ -9582,6 +10457,7 @@ pub const fork = switch (native_os) { .watchos, .visionos, .haiku, + .serenity, => private.fork, else => {}, }; @@ -9825,7 +10701,11 @@ pub extern "c" fn pthread_rwlock_tryrdlock(rwl: *pthread_rwlock_t) callconv(.c) pub extern "c" fn pthread_rwlock_trywrlock(rwl: *pthread_rwlock_t) callconv(.c) E; pub extern "c" fn pthread_rwlock_unlock(rwl: *pthread_rwlock_t) callconv(.c) E; -pub const pthread_t = *opaque {}; +pub const pthread_t = switch (native_os) { + // https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L64 + .serenity => c_int, + else => *opaque {}, +}; pub const FILE = opaque {}; pub extern "c" fn dlopen(path: ?[*:0]const u8, mode: RTLD) ?*anyopaque; @@ -9958,7 +10838,6 @@ pub const thread_id = haiku.thread_id; pub const AUTH = openbsd.AUTH; pub const BI = openbsd.BI; -pub const FUTEX = openbsd.FUTEX; pub const HW = openbsd.HW; pub const PTHREAD_STACK_MIN = openbsd.PTHREAD_STACK_MIN; pub const TCFLUSH = openbsd.TCFLUSH; @@ -9999,7 +10878,6 @@ pub const bcrypt_checkpass = openbsd.bcrypt_checkpass; pub const bcrypt_gensalt = openbsd.bcrypt_gensalt; pub const bcrypt_newhash = openbsd.bcrypt_newhash; pub const endpwent = openbsd.endpwent; -pub const futex = openbsd.futex; pub const getpwent = openbsd.getpwent; pub const getpwnam_r = openbsd.getpwnam_r; pub const getpwnam_shadow = openbsd.getpwnam_shadow; @@ -10226,6 +11104,24 @@ pub const lwp_gettid = dragonfly.lwp_gettid; pub const umtx_sleep = dragonfly.umtx_sleep; pub const umtx_wakeup = dragonfly.umtx_wakeup; +pub const PERF_EVENT = serenity.PERF_EVENT; +pub const disown = serenity.disown; +pub const profiling_enable = serenity.profiling_enable; +pub const profiling_disable = serenity.profiling_disable; +pub const profiling_free_buffer = serenity.profiling_free_buffer; +pub const futex_wait = serenity.futex_wait; +pub const futex_wake = serenity.futex_wake; +pub const purge = serenity.purge; +pub const perf_event = serenity.perf_event; +pub const perf_register_string = serenity.perf_register_string; +pub const get_stack_bounds = serenity.get_stack_bounds; +pub const anon_create = serenity.anon_create; +pub const serenity_readlink = serenity.serenity_readlink; +pub const serenity_open = serenity.serenity_open; +pub const getkeymap = serenity.getkeymap; +pub const setkeymap = serenity.setkeymap; +pub const internet_checksum = serenity.internet_checksum; + /// External definitions shared by two or more operating systems. const private = struct { extern "c" fn close(fd: fd_t) c_int; diff --git a/lib/std/c/serenity.zig b/lib/std/c/serenity.zig new file mode 100644 index 0000000000..e64bef98a7 --- /dev/null +++ b/lib/std/c/serenity.zig @@ -0,0 +1,75 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const O = std.c.O; +const clockid_t = std.c.clockid_t; +const pid_t = std.c.pid_t; +const timespec = std.c.timespec; + +comptime { + assert(builtin.os.tag == .serenity); // Prevent access of std.c symbols on wrong OS. +} + +// https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/futex.h#L46-L53 +pub const FUTEX = struct { + pub const WAIT = 1; + pub const WAKE = 2; + pub const REQUEUE = 3; + pub const CMP_REQUEUE = 4; + pub const WAKE_OP = 5; + pub const WAIT_BITSET = 9; + pub const WAKE_BITSET = 10; + + pub const CLOCK_REALTIME = 1 << 8; + pub const PRIVATE_FLAG = 1 << 9; +}; + +// https://github.com/SerenityOS/serenity/blob/54e79aa1d90bbcb69014255a59afb085802719d3/Kernel/API/POSIX/serenity.h#L18-L36 +pub const PERF_EVENT = packed struct(c_int) { + SAMPLE: bool = false, + MALLOC: bool = false, + FREE: bool = false, + MMAP: bool = false, + MUNMAP: bool = false, + PROCESS_CREATE: bool = false, + PROCESS_EXEC: bool = false, + PROCESS_EXIT: bool = false, + THREAD_CREATE: bool = false, + THREAD_EXIT: bool = false, + CONTEXT_SWITCH: bool = false, + KMALLOC: bool = false, + KFREE: bool = false, + PAGE_FAULT: bool = false, + SYSCALL: bool = false, + SIGNPOST: bool = false, + FILESYSTEM: bool = false, +}; + +// https://github.com/SerenityOS/serenity/blob/abc150085f532f123b598949218893cb272ccc4c/Userland/Libraries/LibC/serenity.h + +pub extern "c" fn disown(pid: pid_t) c_int; + +pub extern "c" fn profiling_enable(pid: pid_t, event_mask: PERF_EVENT) c_int; +pub extern "c" fn profiling_disable(pid: pid_t) c_int; +pub extern "c" fn profiling_free_buffer(pid: pid_t) c_int; + +pub extern "c" fn futex(userspace_address: *u32, futex_op: c_int, value: u32, timeout: *const timespec, userspace_address2: *u32, value3: u32) c_int; +pub extern "c" fn futex_wait(userspace_address: *u32, value: u32, abstime: *const timespec, clockid: clockid_t, process_shared: c_int) c_int; +pub extern "c" fn futex_wake(userspace_address: *u32, count: u32, process_shared: c_int) c_int; + +pub extern "c" fn purge(mode: c_int) c_int; + +pub extern "c" fn perf_event(type: PERF_EVENT, arg1: usize, arg2: usize) c_int; +pub extern "c" fn perf_register_string(string: [*]const u8, string_length: usize) c_int; + +pub extern "c" fn get_stack_bounds(user_stack_base: *usize, user_stack_size: *usize) c_int; + +pub extern "c" fn anon_create(size: usize, options: O) c_int; + +pub extern "c" fn serenity_readlink(path: [*]const u8, path_length: usize, buffer: [*]u8, buffer_size: usize) c_int; +pub extern "c" fn serenity_open(path: [*]const u8, path_length: usize, options: c_int, ...) c_int; + +pub extern "c" fn getkeymap(name_buffer: [*]u8, name_buffer_size: usize, map: [*]u32, shift_map: [*]u32, alt_map: [*]u32, altgr_map: [*]u32, shift_altgr_map: [*]u32) c_int; +pub extern "c" fn setkeymap(name: [*]const u8, map: [*]const u32, shift_map: [*]const u32, alt_map: [*]const u32, altgr_map: [*]const u32, shift_altgr_map: [*]const u32) c_int; + +pub extern "c" fn internet_checksum(ptr: *const anyopaque, count: usize) u16; diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index 9ca6aa5e48..c89141ec7d 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -607,11 +607,10 @@ const Date = struct { } { - const is_leap = std.time.epoch.isLeapYear(date.year); var month: u4 = 1; while (month < date.month) : (month += 1) { const days: u64 = std.time.epoch.getDaysInMonth( - @as(std.time.epoch.YearLeapKind, @enumFromInt(@intFromBool(is_leap))), + date.year, @as(std.time.epoch.Month, @enumFromInt(month)), ); sec += days * std.time.epoch.secs_per_day; diff --git a/lib/std/crypto/Certificate/Bundle.zig b/lib/std/crypto/Certificate/Bundle.zig index 627cd4172b..a74eb44a91 100644 --- a/lib/std/crypto/Certificate/Bundle.zig +++ b/lib/std/crypto/Certificate/Bundle.zig @@ -50,7 +50,7 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void { cb.* = undefined; } -pub const RescanError = RescanLinuxError || RescanMacError || RescanBSDError || RescanWindowsError; +pub const RescanError = RescanLinuxError || RescanMacError || RescanWithPathError || RescanWindowsError; /// Clears the set of certificates and then scans the host operating system /// file system standard locations for certificates. @@ -60,10 +60,12 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) RescanError!void { switch (builtin.os.tag) { .linux => return rescanLinux(cb, gpa), .macos => return rescanMac(cb, gpa), - .freebsd, .openbsd => return rescanBSD(cb, gpa, "/etc/ssl/cert.pem"), - .netbsd => return rescanBSD(cb, gpa, "/etc/openssl/certs/ca-certificates.crt"), - .dragonfly => return rescanBSD(cb, gpa, "/usr/local/etc/ssl/cert.pem"), - .solaris, .illumos => return rescanBSD(cb, gpa, "/etc/ssl/cacert.pem"), + .freebsd, .openbsd => return rescanWithPath(cb, gpa, "/etc/ssl/cert.pem"), + .netbsd => return rescanWithPath(cb, gpa, "/etc/openssl/certs/ca-certificates.crt"), + .dragonfly => return rescanWithPath(cb, gpa, "/usr/local/etc/ssl/cert.pem"), + .solaris, .illumos => return rescanWithPath(cb, gpa, "/etc/ssl/cacert.pem"), + // https://github.com/SerenityOS/serenity/blob/222acc9d389bc6b490d4c39539761b043a4bfcb0/Ports/ca-certificates/package.sh#L19 + .serenity => return rescanWithPath(cb, gpa, "/etc/ssl/certs/ca-certificates.crt"), .windows => return rescanWindows(cb, gpa), else => {}, } @@ -116,9 +118,9 @@ fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void { cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len); } -const RescanBSDError = AddCertsFromFilePathError; +const RescanWithPathError = AddCertsFromFilePathError; -fn rescanBSD(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanBSDError!void { +fn rescanWithPath(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanWithPathError!void { cb.bytes.clearRetainingCapacity(); cb.map.clearRetainingCapacity(); try addCertsFromFilePathAbsolute(cb, gpa, cert_file_path); diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 99936e9abd..fc2da3de72 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -52,7 +52,7 @@ pub const MAX_PATH_BYTES = @compileError("deprecated; renamed to max_path_bytes" /// * On other platforms, `[]u8` file paths are opaque sequences of bytes with /// no particular encoding. pub const max_path_bytes = switch (native_os) { - .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi => posix.PATH_MAX, + .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi, .serenity => posix.PATH_MAX, // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes. // If it would require 4 WTF-8 bytes, then there would be a surrogate // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. @@ -73,7 +73,7 @@ pub const max_path_bytes = switch (native_os) { /// On WASI, file name components are encoded as valid UTF-8. /// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. pub const max_name_bytes = switch (native_os) { - .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX, + .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos, .serenity => posix.NAME_MAX, // Haiku's NAME_MAX includes the null terminator, so subtract one. .haiku => posix.NAME_MAX - 1, // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. @@ -466,7 +466,7 @@ pub fn symLinkAbsoluteZ( pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError; pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { - if (native_os == .linux) { + if (native_os == .linux or native_os == .serenity) { return openFileAbsoluteZ("/proc/self/exe", flags); } if (native_os == .windows) { @@ -572,7 +572,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { return result; } switch (native_os) { - .linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) { + .linux, .serenity => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) { error.InvalidUtf8 => unreachable, // WASI-only error.InvalidWtf8 => unreachable, // Windows-only error.UnsupportedReparsePointType => unreachable, // Windows-only diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index cce99ea8cc..a256758a07 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi }; return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname }); }, - .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => { + .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos, .serenity => { if (posix.getenv("XDG_DATA_HOME")) |xdg| { if (xdg.len > 0) { return fs.path.join(allocator, &[_][]const u8{ xdg, appname }); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 98fef1b2ce..546435f4ab 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -320,14 +320,8 @@ test "accessAbsolute" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(testing.allocator, "."); + defer testing.allocator.free(base_path); try fs.accessAbsolute(base_path, .{}); } @@ -338,25 +332,52 @@ test "openDirAbsolute" { var tmp = tmpDir(.{}); defer tmp.cleanup(); + const tmp_ino = (try tmp.dir.stat()).inode; + try tmp.dir.makeDir("subdir"); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const sub_path = try tmp.dir.realpathAlloc(testing.allocator, "subdir"); + defer testing.allocator.free(sub_path); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..], "subdir" }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + // Can open sub_path + var tmp_sub = try fs.openDirAbsolute(sub_path, .{}); + defer tmp_sub.close(); + + const sub_ino = (try tmp_sub.stat()).inode; { - var dir = try fs.openDirAbsolute(base_path, .{}); + // Can open sub_path + ".." + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".." }); + defer testing.allocator.free(dir_path); + + var dir = try fs.openDirAbsolute(dir_path, .{}); defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(tmp_ino, ino); } - for ([_][]const u8{ ".", ".." }) |sub_path| { - const dir_path = try fs.path.join(allocator, &.{ base_path, sub_path }); + { + // Can open sub_path + "." + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, "." }); + defer testing.allocator.free(dir_path); + var dir = try fs.openDirAbsolute(dir_path, .{}); defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(sub_ino, ino); + } + + { + // Can open subdir + "..", with some extra "." + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".", "..", "." }); + defer testing.allocator.free(dir_path); + + var dir = try fs.openDirAbsolute(dir_path, .{}); + defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(tmp_ino, ino); } } @@ -409,10 +430,7 @@ test "readLinkAbsolute" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(allocator, "."); { const target_path = try fs.path.join(allocator, &.{ base_path, "file.txt" }); @@ -748,7 +766,6 @@ test "directory operations on files" { test "file operations on directories" { // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 if (native_os == .freebsd) return error.SkipZigTest; - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20747 try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { @@ -759,18 +776,30 @@ test "file operations on directories" { try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{})); try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name)); switch (native_os) { - // no error when reading a directory. - .dragonfly, .netbsd => {}, - // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle. - // TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved. - .wasi => {}, + .dragonfly, .netbsd => { + // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732 + const buf = try ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)); + testing.allocator.free(buf); + }, + .wasi => { + // WASI return EBADF, which gets mapped to NotOpenForReading. + // See https://github.com/bytecodealliance/wasmtime/issues/1935 + try testing.expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + }, else => { try testing.expectError(error.IsDir, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); }, } - // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms. - // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 - try testing.expectError(error.IsDir, ctx.dir.openFile(test_dir_name, .{ .mode = .read_write })); + + if (native_os == .wasi and builtin.link_libc) { + // wasmtime unexpectedly succeeds here, see https://github.com/ziglang/zig/issues/20747 + const handle = try ctx.dir.openFile(test_dir_name, .{ .mode = .read_write }); + handle.close(); + } else { + // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms. + // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 + try testing.expectError(error.IsDir, ctx.dir.openFile(test_dir_name, .{ .mode = .read_write })); + } if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) { try testing.expectError(error.IsDir, fs.createFileAbsolute(test_dir_name, .{})); @@ -993,10 +1022,7 @@ test "renameAbsolute" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp_dir.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp_dir.dir.realpathAlloc(allocator, "."); try testing.expectError(error.FileNotFound, fs.renameAbsolute( try fs.path.join(allocator, &.{ base_path, "missing_file_name" }), @@ -1386,7 +1412,6 @@ test "sendfile" { defer tmp.cleanup(); try tmp.dir.makePath("os_test_tmp"); - defer tmp.dir.deleteTree("os_test_tmp") catch {}; var dir = try tmp.dir.openDir("os_test_tmp", .{}); defer dir.close(); @@ -1451,7 +1476,6 @@ test "copyRangeAll" { defer tmp.cleanup(); try tmp.dir.makePath("os_test_tmp"); - defer tmp.dir.deleteTree("os_test_tmp") catch {}; var dir = try tmp.dir.openDir("os_test_tmp", .{}); defer dir.close(); @@ -1800,10 +1824,7 @@ test "'.' and '..' in absolute functions" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(allocator, "."); const subdir_path = try fs.path.join(allocator, &.{ base_path, "./subdir" }); try fs.makeDirAbsolute(subdir_path); diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 6505fcd4fa..523b25c9c8 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -108,10 +108,7 @@ test "File seek ops" { const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); try file.writeAll(&([_]u8{0x55} ** 8192)); @@ -135,10 +132,7 @@ test "setEndPos" { const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); // Verify that the file size changes and the file offset is not moved try std.testing.expect((try file.getEndPos()) == 0); @@ -161,10 +155,8 @@ test "updateTimes" { const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true }); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); + const stat_old = try file.stat(); // Set atime and mtime to 5s before try file.updateTimes( diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 1ad9533116..8ca7ed669b 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -308,7 +308,8 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { /// In such case, it is more efficient for the caller to perform those /// operations. /// -/// `allocation` may be an empty slice, in which case a new allocation is made. +/// `allocation` may be an empty slice, in which case `null` is returned, +/// unless `new_len` is also 0, in which case `allocation` is returned. /// /// `new_len` may be zero, in which case the allocation is freed. pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 7d68322c90..0df825187b 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -248,8 +248,8 @@ pub fn MultiArrayList(comptime T: type) type { /// Extend the list by 1 element, returning the newly reserved /// index with uninitialized data. /// Allocates more memory as necesasry. - pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!usize { - try self.ensureUnusedCapacity(allocator, 1); + pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!usize { + try self.ensureUnusedCapacity(gpa, 1); return self.addOneAssumeCapacity(); } diff --git a/lib/std/os.zig b/lib/std/os.zig index 80f45dd59d..45568caf3c 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -82,6 +82,7 @@ pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool { .solaris, .illumos, .freebsd, + .serenity, => true, .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt, @@ -127,7 +128,7 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix. const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes; return out_buffer[0..len]; }, - .linux => { + .linux, .serenity => { var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable; diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index f7e51c63aa..9674e704dc 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -141,11 +141,10 @@ pub const Time = extern struct { pub const unspecified_timezone: i16 = 0x7ff; fn daysInYear(year: u16, max_month: u4) u9 { - const leap_year: std.time.epoch.YearLeapKind = if (std.time.epoch.isLeapYear(year)) .leap else .not_leap; var days: u9 = 0; var month: u4 = 0; while (month < max_month) : (month += 1) { - days += std.time.epoch.getDaysInMonth(leap_year, @enumFromInt(month + 1)); + days += std.time.epoch.getDaysInMonth(year, @enumFromInt(month + 1)); } return days; } diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig index 5c86520194..2e17ef64c9 100644 --- a/lib/std/os/uefi/status.zig +++ b/lib/std/os/uefi/status.zig @@ -231,11 +231,57 @@ pub const Status = enum(usize) { else => {}, } } + + pub fn fromError(e: Error) Status { + return switch (e) { + Error.Aborted => .aborted, + Error.AccessDenied => .access_denied, + Error.AlreadyStarted => .already_started, + Error.BadBufferSize => .bad_buffer_size, + Error.BufferTooSmall => .buffer_too_small, + Error.CompromisedData => .compromised_data, + Error.ConnectionFin => .connection_fin, + Error.ConnectionRefused => .connection_refused, + Error.ConnectionReset => .connection_reset, + Error.CrcError => .crc_error, + Error.DeviceError => .device_error, + Error.EndOfFile => .end_of_file, + Error.EndOfMedia => .end_of_media, + Error.HostUnreachable => .host_unreachable, + Error.HttpError => .http_error, + Error.IcmpError => .icmp_error, + Error.IncompatibleVersion => .incompatible_version, + Error.InvalidLanguage => .invalid_language, + Error.InvalidParameter => .invalid_parameter, + Error.IpAddressConflict => .ip_address_conflict, + Error.LoadError => .load_error, + Error.MediaChanged => .media_changed, + Error.NetworkUnreachable => .network_unreachable, + Error.NoMapping => .no_mapping, + Error.NoMedia => .no_media, + Error.NoResponse => .no_response, + Error.NotFound => .not_found, + Error.NotReady => .not_ready, + Error.NotStarted => .not_started, + Error.OutOfResources => .out_of_resources, + Error.PortUnreachable => .port_unreachable, + Error.ProtocolError => .protocol_error, + Error.ProtocolUnreachable => .protocol_unreachable, + Error.SecurityViolation => .security_violation, + Error.TftpError => .tftp_error, + Error.Timeout => .timeout, + Error.Unsupported => .unsupported, + Error.VolumeCorrupted => .volume_corrupted, + Error.VolumeFull => .volume_full, + Error.WriteProtected => .write_protected, + }; + } }; test "status" { var st: Status = .device_error; try testing.expectError(error.DeviceError, st.err()); + try testing.expectEqual(st, Status.fromError(st.err())); st = .success; try st.err(); diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 29756cae69..9678bfb261 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -8,7 +8,6 @@ const io = std.io; const fs = std.fs; const mem = std.mem; const elf = std.elf; -const File = std.fs.File; const Thread = std.Thread; const linux = std.os.linux; @@ -19,8 +18,6 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; const AtomicOrder = std.builtin.AtomicOrder; const native_os = builtin.target.os.tag; const tmpDir = std.testing.tmpDir; -const Dir = std.fs.Dir; -const ArenaAllocator = std.heap.ArenaAllocator; // https://github.com/ziglang/zig/issues/20288 test "WTF-8 to WTF-16 conversion buffer overflows" { @@ -115,50 +112,62 @@ test "open smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + } - // Try this again with the same flags. This op should fail with error.PathAlreadyExists. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode)); + { + // Try this again with the same flags. This op should fail with error.PathAlreadyExists. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode)); + } - // Try opening without `EXCL` flag. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode); - posix.close(fd); + { + // Try opening without `EXCL` flag. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode); + posix.close(fd); + } - // Try opening as a directory which should fail. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode)); + { + // Try opening as a directory which should fail. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode)); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + } - // Open dir using `open` - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); - posix.close(fd); + { + // Open dir using `open` + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); + posix.close(fd); + } - // Try opening as file which should fail. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + { + // Try opening as file which should fail. + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + } } test "openat smoke test" { @@ -705,8 +714,6 @@ test "mmap" { try testing.expectEqual(i, try stream.readInt(u32, .little)); } } - - try tmp.dir.deleteFile(test_out_file); } test "getenv" { @@ -732,10 +739,7 @@ test "fcntl" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); // Note: The test assumes createFile opens the file with CLOEXEC { @@ -771,10 +775,7 @@ test "sync" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); posix.sync(); try posix.syncfs(file.handle); @@ -791,10 +792,7 @@ test "fsync" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); try posix.fsync(file.handle); try posix.fdatasync(file.handle); @@ -1041,54 +1039,65 @@ test "rename smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); - - // Rename the file - var new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - try posix.rename(file_path, new_file_path); - - // Try opening renamed file - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode); - posix.close(fd); + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + + // Rename the file + const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(new_file_path); + try posix.rename(file_path, new_file_path); + } - // Try opening original file - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + { + // Try opening renamed file + const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode); + posix.close(fd); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Try opening original file - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + } - // Rename the directory - new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" }); - try posix.rename(file_path, new_file_path); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + + // Rename the directory + const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" }); + defer a.free(new_file_path); + try posix.rename(file_path, new_file_path); + } - // Try opening renamed directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); - posix.close(fd); + { + // Try opening renamed directory + const file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); + posix.close(fd); + } - // Try opening original directory - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); + { + // Try opening original directory - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); + } } test "access smoke test" { @@ -1098,44 +1107,50 @@ test "access smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + } - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); + { + // Try to access() the file + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + if (native_os == .windows) { + try posix.access(file_path, posix.F_OK); + } else { + try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK); + } + } - // Try to access() the file - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - if (native_os == .windows) { - try posix.access(file_path, posix.F_OK); - } else { - try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK); + { + // Try to access() a non-existent file - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK)); } - // Try to access() a non-existent file - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK)); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Try to access() the directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); - // Try to access() the directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.access(file_path, posix.F_OK); + try posix.access(file_path, posix.F_OK); + } } test "timerfd" { @@ -1167,103 +1182,59 @@ test "isatty" { } test "read with empty buffer" { - if (native_os == .wasi) return error.SkipZigTest; - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{ .read = true }); + var file = try tmp.dir.createFile("read_empty", .{ .read = true }); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.read(file.handle, bytes); + const rc = try posix.read(file.handle, bytes); + try expectEqual(rc, 0); } test "pread with empty buffer" { - if (native_os == .wasi) return error.SkipZigTest; - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{ .read = true }); + var file = try tmp.dir.createFile("pread_empty", .{ .read = true }); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.pread(file.handle, bytes, 0); + const rc = try posix.pread(file.handle, bytes, 0); + try expectEqual(rc, 0); } test "write with empty buffer" { - if (native_os == .wasi) return error.SkipZigTest; - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{}); + var file = try tmp.dir.createFile("write_empty", .{}); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.write(file.handle, bytes); + const rc = try posix.write(file.handle, bytes); + try expectEqual(rc, 0); } test "pwrite with empty buffer" { - if (native_os == .wasi) return error.SkipZigTest; - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{}); + var file = try tmp.dir.createFile("pwrite_empty", .{}); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.pwrite(file.handle, bytes, 0); + const rc = try posix.pwrite(file.handle, bytes, 0); + try expectEqual(rc, 0); } fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { diff --git a/lib/std/process.zig b/lib/std/process.zig index dd08e88af2..b734276444 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1539,6 +1539,7 @@ pub fn getUserInfo(name: []const u8) !UserInfo { .haiku, .solaris, .illumos, + .serenity, => posixGetUserInfo(name), else => @compileError("Unsupported OS"), }; diff --git a/lib/std/time/epoch.zig b/lib/std/time/epoch.zig index b409d3e9eb..fa7499aec7 100644 --- a/lib/std/time/epoch.zig +++ b/lib/std/time/epoch.zig @@ -64,8 +64,6 @@ pub fn getDaysInYear(year: Year) u9 { return if (isLeapYear(year)) 366 else 365; } -pub const YearLeapKind = enum(u1) { not_leap, leap }; - pub const Month = enum(u4) { jan = 1, feb, @@ -87,13 +85,13 @@ pub const Month = enum(u4) { } }; -/// Get the number of days in the given month -pub fn getDaysInMonth(leap_year: YearLeapKind, month: Month) u5 { +/// Get the number of days in the given month and year +pub fn getDaysInMonth(year: Year, month: Month) u5 { return switch (month) { .jan => 31, - .feb => @as(u5, switch (leap_year) { - .leap => 29, - .not_leap => 28, + .feb => @as(u5, switch (isLeapYear(year)) { + true => 29, + false => 28, }), .mar => 31, .apr => 30, @@ -116,9 +114,8 @@ pub const YearAndDay = struct { pub fn calculateMonthDay(self: YearAndDay) MonthAndDay { var month: Month = .jan; var days_left = self.day; - const leap_kind: YearLeapKind = if (isLeapYear(self.year)) .leap else .not_leap; while (true) { - const days_in_month = getDaysInMonth(leap_kind, month); + const days_in_month = getDaysInMonth(self.year, month); if (days_left < days_in_month) break; days_left -= days_in_month; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 84f9cf7330..a4b16d6c46 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -539,7 +539,7 @@ pub fn abiAndDynamicLinkerFromFile( var result: Target = .{ .cpu = cpu, .os = os, - .abi = query.abi orelse Target.Abi.default(cpu.arch, os), + .abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag), .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), .dynamic_linker = query.dynamic_linker, }; @@ -1213,7 +1213,7 @@ fn detectAbiAndDynamicLinker( } fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target { - const abi = query.abi orelse Target.Abi.default(cpu.arch, os); + const abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag); return .{ .cpu = cpu, .os = os, diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index f4c6ae6885..ee46288903 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -318,6 +318,17 @@ pub fn isLibCLibName(target: std.Target, name: []const u8) bool { return true; } + if (target.os.tag == .serenity) { + if (eqlIgnoreCase(ignore_case, name, "dl")) + return true; + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "pthread")) + return true; + if (eqlIgnoreCase(ignore_case, name, "ssp")) + return true; + } + return false; } diff --git a/src/Compilation.zig b/src/Compilation.zig index 12221ba3dc..51f6293fc8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2261,7 +2261,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { zcu.compile_log_text.shrinkAndFree(gpa, 0); - zcu.skip_analysis_errors = false; + zcu.skip_analysis_this_update = false; // Make sure std.zig is inside the import_table. We unconditionally need // it for start.zig. @@ -2336,6 +2336,17 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { const pt: Zcu.PerThread = .activate(zcu, .main); defer pt.deactivate(); + if (!zcu.skip_analysis_this_update) { + if (comp.config.is_test) { + // The `test_functions` decl has been intentionally postponed until now, + // at which point we must populate it with the list of test functions that + // have been discovered and not filtered out. + try pt.populateTestFunctions(main_progress_node); + } + + try pt.processExports(); + } + if (build_options.enable_debug_extensions and comp.verbose_intern_pool) { std.debug.print("intern pool stats for '{s}':\n", .{ comp.root_name, @@ -2350,15 +2361,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { }); zcu.intern_pool.dumpGenericInstances(gpa); } - - if (comp.config.is_test) { - // The `test_functions` decl has been intentionally postponed until now, - // at which point we must populate it with the list of test functions that - // have been discovered and not filtered out. - try pt.populateTestFunctions(main_progress_node); - } - - try pt.processExports(); } if (anyErrors(comp)) { @@ -3310,7 +3312,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } } } - if (zcu.skip_analysis_errors) break :zcu_errors; + if (zcu.skip_analysis_this_update) break :zcu_errors; var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: { const SortOrder = struct { zcu: *Zcu, @@ -3446,7 +3448,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file); if (comp.zcu) |zcu| { - if (!zcu.skip_analysis_errors and bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { + if (!zcu.skip_analysis_this_update and bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { const values = zcu.compile_log_sources.values(); // First one will be the error; subsequent ones will be notes. const src_loc = values[0].src(); @@ -3957,7 +3959,7 @@ fn performAllTheWorkInner( // However, this means our analysis data is invalid, so we want to omit all analysis errors. assert(zcu.failed_files.count() > 0); // we will get an error - zcu.skip_analysis_errors = true; + zcu.skip_analysis_this_update = true; return; } @@ -5631,7 +5633,10 @@ pub fn addCCArgs( } if (target_util.llvmMachineAbi(target)) |mabi| { - try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); + // Clang's integrated Arm assembler doesn't support `-mabi` yet... + if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp))) { + try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); + } } // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future. @@ -6026,17 +6031,19 @@ pub fn addCCArgs( // function was called. try argv.append("-fno-sanitize=function"); - // It's recommended to use the minimal runtime in production environments - // due to the security implications of the full runtime. The minimal runtime - // doesn't provide much benefit over simply trapping. if (mod.optimize_mode == .ReleaseSafe) { + // It's recommended to use the minimal runtime in production + // environments due to the security implications of the full runtime. + // The minimal runtime doesn't provide much benefit over simply + // trapping, however, so we do that instead. try argv.append("-fsanitize-trap=undefined"); - } - - // This is necessary because, by default, Clang instructs LLVM to embed a COFF link - // dependency on `libclang_rt.ubsan_standalone.a` when the UBSan runtime is used. - if (target.os.tag == .windows) { - try argv.append("-fno-rtlib-defaultlib"); + } else { + // This is necessary because, by default, Clang instructs LLVM to embed + // a COFF link dependency on `libclang_rt.ubsan_standalone.a` when the + // UBSan runtime is used. + if (target.os.tag == .windows) { + try argv.append("-fno-rtlib-defaultlib"); + } } } } diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 61a01e3ab3..bb70518910 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -1850,7 +1850,11 @@ const FileHeader = struct { return magic_number == std.macho.MH_MAGIC or magic_number == std.macho.MH_MAGIC_64 or magic_number == std.macho.FAT_MAGIC or - magic_number == std.macho.FAT_MAGIC_64; + magic_number == std.macho.FAT_MAGIC_64 or + magic_number == std.macho.MH_CIGAM or + magic_number == std.macho.MH_CIGAM_64 or + magic_number == std.macho.FAT_CIGAM or + magic_number == std.macho.FAT_CIGAM_64; } pub fn isExecutable(self: *FileHeader) bool { @@ -1875,6 +1879,11 @@ test FileHeader { h.bytes_read = 0; h.update(&macho64_magic_bytes); try std.testing.expect(h.isExecutable()); + + const macho64_cigam_bytes = [_]u8{ 0xFE, 0xED, 0xFA, 0xCF }; + h.bytes_read = 0; + h.update(&macho64_cigam_bytes); + try std.testing.expect(h.isExecutable()); } // Result of the `unpackResource` operation. Enables collecting errors from diff --git a/src/Sema.zig b/src/Sema.zig index 1ef3834b61..8368ca3515 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17860,11 +17860,18 @@ fn zirThis( const zcu = pt.zcu; const namespace = pt.zcu.namespacePtr(block.namespace); - const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); - - switch (pt.zcu.intern_pool.indexToKey(new_ty)) { - .struct_type, .union_type => try sema.declareDependency(.{ .interned = new_ty }), + switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) { + .opaque_type => { + // Opaque types are never outdated since they don't undergo type resolution, so nothing to do! + return Air.internedToRef(namespace.owner_type); + }, + .struct_type, .union_type => { + const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); + try sema.declareDependency(.{ .interned = new_ty }); + return Air.internedToRef(new_ty); + }, .enum_type => { + const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); try sema.declareDependency(.{ .interned = new_ty }); // Since this is an enum, it has to be resolved immediately. // `ensureTypeUpToDate` has resolved the new type if necessary. @@ -17873,11 +17880,10 @@ fn zirThis( if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { return error.AnalysisFail; } + return Air.internedToRef(new_ty); }, - .opaque_type => {}, else => unreachable, } - return Air.internedToRef(new_ty); } fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -36742,7 +36748,7 @@ fn unionFields( if (enum_index != field_i) { const msg = msg: { const enum_field_src: LazySrcLoc = .{ - .base_node_inst = tag_info.zir_index.unwrap().?, + .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?, .offset = .{ .container_field_name = enum_index }, }; const msg = try sema.errMsg(name_src, "union field '{}' ordered differently than corresponding enum field", .{ @@ -38052,6 +38058,11 @@ fn compareScalar( const pt = sema.pt; const coerced_lhs = try pt.getCoerced(lhs, ty); const coerced_rhs = try pt.getCoerced(rhs, ty); + + // Equality comparisons of signed zero and NaN need to use floating point semantics + if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu)) + return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt); + switch (op) { .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), diff --git a/src/Value.zig b/src/Value.zig index be2c73c3e9..40e5331c4e 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -1132,6 +1132,8 @@ pub fn compareHeteroAdvanced( else => {}, } } + + if (lhs.isNan(zcu) or rhs.isNan(zcu)) return op == .neq; return (try orderAdvanced(lhs, rhs, strat, zcu, tid)).compare(op); } diff --git a/src/Zcu.zig b/src/Zcu.zig index 30bb6e5b3c..28b7ba1b65 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -181,7 +181,10 @@ analysis_roots: std.BoundedArray(*Package.Module, 4) = .{}, /// Allocated into `gpa`. resolved_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = null, -skip_analysis_errors: bool = false, +/// If `true`, then semantic analysis must not occur on this update due to AstGen errors. +/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped. +/// Reset to `false` at the start of each update in `Compilation.update`. +skip_analysis_this_update: bool = false, stage1_flags: packed struct { have_winmain: bool = false, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4af9b01257..2a9e25500c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2437,7 +2437,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { try cg.airArg(inst); - try cg.resetTemps(); + try cg.resetTemps(@enumFromInt(0)); cg.checkInvariantsAfterAirInst(); }, else => break, @@ -2477,7 +2477,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .shuffle => try cg.airShuffle(inst), .reduce => try cg.airReduce(inst), .reduce_optimized => try cg.airReduce(inst), - .aggregate_init => try cg.airAggregateInit(inst), // zig fmt: on .arg => if (cg.debug_output != .none) { @@ -80843,6 +80842,74 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { for (ops[1..]) |op| try op.die(cg); try res[0].finish(inst, &.{ty_op.operand}, ops[0..1], cg); }, + .aggregate_init => |air_tag| if (use_old) try cg.airAggregateInit(inst) else fallback: { + const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; + const agg_ty = ty_pl.ty.toType(); + if ((agg_ty.isVector(zcu) and agg_ty.childType(zcu).toIntern() == .bool_type) or + (agg_ty.zigTypeTag(zcu) == .@"struct" and agg_ty.containerLayout(zcu) == .@"packed")) break :fallback try cg.airAggregateInit(inst); + var res = try cg.tempAllocMem(agg_ty); + const reset_index = cg.next_temp_index; + var bt = cg.liveness.iterateBigTomb(inst); + switch (ip.indexToKey(agg_ty.toIntern())) { + inline .array_type, .vector_type => |sequence_type| { + const elems: []const Air.Inst.Ref = @ptrCast(cg.air.extra[ty_pl.payload..][0..@intCast(sequence_type.len)]); + const elem_size = Type.fromInterned(sequence_type.child).abiSize(zcu); + var elem_disp: u31 = 0; + for (elems) |elem_ref| { + var elem = try cg.tempFromOperand(elem_ref, bt.feed()); + try res.write(&elem, .{ .disp = elem_disp }, cg); + try elem.die(cg); + try cg.resetTemps(reset_index); + elem_disp += @intCast(elem_size); + } + if (@hasField(@TypeOf(sequence_type), "sentinel") and sequence_type.sentinel != .none) { + var sentinel = try cg.tempFromValue(.fromInterned(sequence_type.sentinel)); + try res.write(&sentinel, .{ .disp = elem_disp }, cg); + try sentinel.die(cg); + } + }, + .struct_type => { + const loaded_struct = ip.loadStructType(agg_ty.toIntern()); + const elems: []const Air.Inst.Ref = @ptrCast(cg.air.extra[ty_pl.payload..][0..loaded_struct.field_types.len]); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + for (elems, 0..) |elem_ref, field_index| { + const elem_dies = bt.feed(); + if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + var elem = try cg.tempFromOperand(elem_ref, elem_dies); + try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg); + try elem.die(cg); + try cg.resetTemps(reset_index); + } + }, + .@"packed" => return cg.fail("failed to select {s} {}", .{ + @tagName(air_tag), + agg_ty.fmt(pt), + }), + } + }, + .tuple_type => |tuple_type| { + const elems: []const Air.Inst.Ref = @ptrCast(cg.air.extra[ty_pl.payload..][0..tuple_type.types.len]); + var elem_disp: u31 = 0; + for (elems, 0..) |elem_ref, field_index| { + const elem_dies = bt.feed(); + if (tuple_type.values.get(ip)[field_index] != .none) continue; + const field_type = Type.fromInterned(tuple_type.types.get(ip)[field_index]); + elem_disp = @intCast(field_type.abiAlignment(zcu).forward(elem_disp)); + var elem = try cg.tempFromOperand(elem_ref, elem_dies); + try res.write(&elem, .{ .disp = elem_disp }, cg); + try elem.die(cg); + try cg.resetTemps(reset_index); + elem_disp += @intCast(field_type.abiSize(zcu)); + } + }, + else => return cg.fail("failed to select {s} {}", .{ + @tagName(air_tag), + agg_ty.fmt(pt), + }), + } + try res.finish(inst, &.{}, &.{}, cg); + }, .union_init => if (use_old) try cg.airUnionInit(inst) else { const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data; @@ -82199,14 +82266,14 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .c_va_start => try cg.airVaStart(inst), .work_item_id, .work_group_size, .work_group_id => unreachable, } - try cg.resetTemps(); + try cg.resetTemps(@enumFromInt(0)); cg.checkInvariantsAfterAirInst(); } verbose_tracking_log.debug("{}", .{cg.fmtTracking()}); } -fn genLazy(self: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { - const pt = self.pt; +fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { + const pt = cg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; switch (ip.indexToKey(lazy_sym.ty)) { @@ -82215,97 +82282,98 @@ fn genLazy(self: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)}); const param_regs = abi.getCAbiIntParamRegs(.auto); - const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); - defer for (param_locks) |lock| self.register_manager.unlockReg(lock); + const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); const ret_mcv: MCValue = .{ .register_pair = param_regs[0..2].* }; - const enum_mcv: MCValue = .{ .register = param_regs[0] }; + var enum_temp = try cg.tempInit(enum_ty, .{ .register = param_regs[0] }); - const data_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); - const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); - defer self.register_manager.unlockReg(data_lock); - try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = lazy_sym.ty }); + const data_reg = try cg.register_manager.allocReg(null, abi.RegisterClass.gp); + const data_lock = cg.register_manager.lockRegAssumeUnused(data_reg); + defer cg.register_manager.unlockReg(data_lock); + try cg.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = lazy_sym.ty }); var data_off: i32 = 0; + const reset_index = cg.next_temp_index; const tag_names = ip.loadEnumType(lazy_sym.ty).names; for (0..tag_names.len) |tag_index| { - var enum_temp = try self.tempInit(enum_ty, enum_mcv); - const tag_name_len = tag_names.get(ip)[tag_index].length(ip); - var tag_temp = try self.tempFromValue(try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index))); - const cc_temp = enum_temp.cmpInts(.neq, &tag_temp, self) catch |err| switch (err) { + var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index))); + const cc_temp = enum_temp.cmpInts(.neq, &tag_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try enum_temp.die(self); - try tag_temp.die(self); - const skip_reloc = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined); - try cc_temp.die(self); - try self.resetTemps(); + try tag_temp.die(cg); + const skip_reloc = try cg.asmJccReloc(cc_temp.tracking(cg).short.eflags, undefined); + try cc_temp.die(cg); + try cg.resetTemps(reset_index); - try self.genSetReg( + try cg.genSetReg( ret_mcv.register_pair[0], .usize, .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, .{}, ); - try self.genSetReg(ret_mcv.register_pair[1], .usize, .{ .immediate = tag_name_len }, .{}); - try self.asmOpOnly(.{ ._, .ret }); + try cg.genSetReg(ret_mcv.register_pair[1], .usize, .{ .immediate = tag_name_len }, .{}); + try cg.asmOpOnly(.{ ._, .ret }); - self.performReloc(skip_reloc); + cg.performReloc(skip_reloc); data_off += @intCast(tag_name_len + 1); } + try enum_temp.die(cg); - try self.genSetReg(ret_mcv.register_pair[0], .usize, .{ .immediate = 0 }, .{}); - try self.asmOpOnly(.{ ._, .ret }); + try cg.genSetReg(ret_mcv.register_pair[0], .usize, .{ .immediate = 0 }, .{}); + try cg.asmOpOnly(.{ ._, .ret }); }, .error_set_type => |error_set_type| { const err_ty: Type = .fromInterned(lazy_sym.ty); wip_mir_log.debug("{}.@errorCast:", .{err_ty.fmt(pt)}); const param_regs = abi.getCAbiIntParamRegs(.auto); - const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); - defer for (param_locks) |lock| self.register_manager.unlockReg(lock); + const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); const ret_mcv: MCValue = .{ .register = param_regs[0] }; const err_mcv: MCValue = .{ .register = param_regs[0] }; + var err_temp = try cg.tempInit(err_ty, err_mcv); const ExpectedContents = [32]Mir.Inst.Index; var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + std.heap.stackFallback(@sizeOf(ExpectedContents), cg.gpa); const allocator = stack.get(); const relocs = try allocator.alloc(Mir.Inst.Index, error_set_type.names.len); defer allocator.free(relocs); + const reset_index = cg.next_temp_index; for (0.., relocs) |tag_index, *reloc| { - var err_temp = try self.tempInit(err_ty, err_mcv); - - var tag_temp = try self.tempInit(.anyerror, .{ + var tag_temp = try cg.tempInit(.anyerror, .{ .immediate = ip.getErrorValueIfExists(error_set_type.names.get(ip)[tag_index]).?, }); - const cc_temp = err_temp.cmpInts(.eq, &tag_temp, self) catch |err| switch (err) { + const cc_temp = err_temp.cmpInts(.eq, &tag_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try err_temp.die(self); - try tag_temp.die(self); - reloc.* = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined); - try cc_temp.die(self); - try self.resetTemps(); + try tag_temp.die(cg); + reloc.* = try cg.asmJccReloc(cc_temp.tracking(cg).short.eflags, undefined); + try cc_temp.die(cg); + try cg.resetTemps(reset_index); } + try err_temp.die(cg); - try self.genCopy(.usize, ret_mcv, .{ .immediate = 0 }, .{}); - for (relocs) |reloc| self.performReloc(reloc); + try cg.genCopy(.usize, ret_mcv, .{ .immediate = 0 }, .{}); + for (relocs) |reloc| cg.performReloc(reloc); assert(ret_mcv.register == err_mcv.register); - try self.asmOpOnly(.{ ._, .ret }); + try cg.asmOpOnly(.{ ._, .ret }); }, - else => return self.fail( + else => return cg.fail( "TODO implement {s} for {}", .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) }, ), } + try cg.resetTemps(@enumFromInt(0)); + cg.checkInvariantsAfterAirInst(); } fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void { @@ -93621,17 +93689,17 @@ fn lowerBlock(self: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index } fn lowerSwitchBr( - self: *CodeGen, + cg: *CodeGen, inst: Air.Inst.Index, switch_br: Air.UnwrappedSwitch, condition: MCValue, condition_dies: bool, is_loop: bool, ) !void { - const zcu = self.pt.zcu; - const condition_ty = self.typeOf(switch_br.operand); - const condition_int_info = self.intInfo(condition_ty).?; - const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits); + const zcu = cg.pt.zcu; + const condition_ty = cg.typeOf(switch_br.operand); + const condition_int_info = cg.intInfo(condition_ty).?; + const condition_int_ty = try cg.pt.intType(condition_int_info.signedness, condition_int_info.bits); const ExpectedContents = extern struct { liveness_deaths: [1 << 8 | 1]Air.Inst.Index, @@ -93639,15 +93707,15 @@ fn lowerSwitchBr( relocs: [1 << 6]Mir.Inst.Index, }; var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + std.heap.stackFallback(@sizeOf(ExpectedContents), cg.gpa); const allocator = stack.get(); - const state = try self.saveState(); + const state = try cg.saveState(); - const liveness = try self.liveness.getSwitchBr(allocator, inst, switch_br.cases_len + 1); + const liveness = try cg.liveness.getSwitchBr(allocator, inst, switch_br.cases_len + 1); defer allocator.free(liveness.deaths); - if (!self.mod.pic and self.target.ofmt == .elf) table: { + if (!cg.mod.pic and cg.target.ofmt == .elf) table: { var prong_items: u32 = 0; var min: ?Value = null; var max: ?Value = null; @@ -93690,41 +93758,41 @@ fn lowerSwitchBr( if (prong_items < table_len >> 2) break :table; // no more than 75% waste const condition_index = if (condition_dies and condition.isModifiable()) condition else condition_index: { - const condition_index = try self.allocTempRegOrMem(condition_ty, true); - try self.genCopy(condition_ty, condition_index, condition, .{}); + const condition_index = try cg.allocTempRegOrMem(condition_ty, true); + try cg.genCopy(condition_ty, condition_index, condition, .{}); break :condition_index condition_index; }; - try self.spillEflagsIfOccupied(); - if (min.?.orderAgainstZero(zcu).compare(.neq)) try self.genBinOpMir( + try cg.spillEflagsIfOccupied(); + if (min.?.orderAgainstZero(zcu).compare(.neq)) try cg.genBinOpMir( .{ ._, .sub }, condition_ty, condition_index, .{ .air_ref = Air.internedToRef(min.?.toIntern()) }, ); const else_reloc = if (switch_br.else_body_len > 0) else_reloc: { - var cond_temp = try self.tempInit(condition_ty, condition_index); - var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table_len - 1)); - const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { + var cond_temp = try cg.tempInit(condition_ty, condition_index); + var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(condition_int_ty, table_len - 1)); + const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try cond_temp.die(self); - try table_max_temp.die(self); - const else_reloc = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined); - try cc_temp.die(self); + try cond_temp.die(cg); + try table_max_temp.die(cg); + const else_reloc = try cg.asmJccReloc(cc_temp.tracking(cg).short.eflags, undefined); + try cc_temp.die(cg); break :else_reloc else_reloc; } else undefined; - const table_start: u31 = @intCast(self.mir_table.items.len); + const table_start: u31 = @intCast(cg.mir_table.items.len); { const condition_index_reg = if (condition_index.isRegister()) condition_index.getReg().? else - try self.copyToTmpRegister(.usize, condition_index); - const condition_index_lock = self.register_manager.lockReg(condition_index_reg); - defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock); - try self.truncateRegister(condition_ty, condition_index_reg); - const ptr_size = @divExact(self.target.ptrBitWidth(), 8); - try self.asmMemory(.{ ._mp, .j }, .{ + try cg.copyToTmpRegister(.usize, condition_index); + const condition_index_lock = cg.register_manager.lockReg(condition_index_reg); + defer if (condition_index_lock) |lock| cg.register_manager.unlockReg(lock); + try cg.truncateRegister(condition_ty, condition_index_reg); + const ptr_size = @divExact(cg.target.ptrBitWidth(), 8); + try cg.asmMemory(.{ ._mp, .j }, .{ .base = .table, .mod = .{ .rm = .{ .size = .ptr, @@ -93735,32 +93803,32 @@ fn lowerSwitchBr( }); } const else_reloc_marker: u32 = 0; - assert(self.mir_instructions.len > else_reloc_marker); - try self.mir_table.appendNTimes(self.gpa, else_reloc_marker, table_len); - if (is_loop) try self.loop_switches.putNoClobber(self.gpa, inst, .{ + assert(cg.mir_instructions.len > else_reloc_marker); + try cg.mir_table.appendNTimes(cg.gpa, else_reloc_marker, table_len); + if (is_loop) try cg.loop_switches.putNoClobber(cg.gpa, inst, .{ .start = table_start, .len = table_len, .min = min.?, .else_relocs = if (switch_br.else_body_len > 0) .{ .forward = .empty } else .@"unreachable", }); defer if (is_loop) { - var loop_switch_data = self.loop_switches.fetchRemove(inst).?.value; + var loop_switch_data = cg.loop_switches.fetchRemove(inst).?.value; switch (loop_switch_data.else_relocs) { .@"unreachable", .backward => {}, - .forward => |*else_relocs| else_relocs.deinit(self.gpa), + .forward => |*else_relocs| else_relocs.deinit(cg.gpa), } }; var cases_it = switch_br.iterateCases(); while (cases_it.next()) |case| { { - const table = self.mir_table.items[table_start..][0..table_len]; + const table = cg.mir_table.items[table_start..][0..table_len]; for (case.items) |item| { const val = Value.fromInterned(item.toInterned().?); var val_space: Value.BigIntSpace = undefined; const val_bigint = val.toBigInt(&val_space, zcu); var index_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; index_bigint.sub(val_bigint, min_bigint); - table[index_bigint.toConst().to(u10) catch unreachable] = @intCast(self.mir_instructions.len); + table[index_bigint.toConst().to(u10) catch unreachable] = @intCast(cg.mir_instructions.len); } for (case.ranges) |range| { var low_space: Value.BigIntSpace = undefined; @@ -93772,14 +93840,14 @@ fn lowerSwitchBr( const start = index_bigint.toConst().to(u10) catch unreachable; index_bigint.sub(high_bigint, min_bigint); const end = @as(u11, index_bigint.toConst().to(u10) catch unreachable) + 1; - @memset(table[start..end], @intCast(self.mir_instructions.len)); + @memset(table[start..end], @intCast(cg.mir_instructions.len)); } } - for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); + for (liveness.deaths[case.idx]) |operand| try cg.processDeath(operand); - try self.genBodyBlock(case.body); - try self.restoreState(state, &.{}, .{ + try cg.genBodyBlock(case.body); + try cg.restoreState(state, &.{}, .{ .emit_instructions = false, .update_tracking = true, .resurrect = true, @@ -93790,21 +93858,21 @@ fn lowerSwitchBr( const else_body = cases_it.elseBody(); const else_deaths = liveness.deaths.len - 1; - for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); + for (liveness.deaths[else_deaths]) |operand| try cg.processDeath(operand); - self.performReloc(else_reloc); + cg.performReloc(else_reloc); if (is_loop) { - const loop_switch_data = self.loop_switches.getPtr(inst).?; - for (loop_switch_data.else_relocs.forward.items) |reloc| self.performReloc(reloc); - loop_switch_data.else_relocs.forward.deinit(self.gpa); - loop_switch_data.else_relocs = .{ .backward = @intCast(self.mir_instructions.len) }; + const loop_switch_data = cg.loop_switches.getPtr(inst).?; + for (loop_switch_data.else_relocs.forward.items) |reloc| cg.performReloc(reloc); + loop_switch_data.else_relocs.forward.deinit(cg.gpa); + loop_switch_data.else_relocs = .{ .backward = @intCast(cg.mir_instructions.len) }; } - for (self.mir_table.items[table_start..][0..table_len]) |*entry| if (entry.* == else_reloc_marker) { - entry.* = @intCast(self.mir_instructions.len); + for (cg.mir_table.items[table_start..][0..table_len]) |*entry| if (entry.* == else_reloc_marker) { + entry.* = @intCast(cg.mir_instructions.len); }; - try self.genBodyBlock(else_body); - try self.restoreState(state, &.{}, .{ + try cg.genBodyBlock(else_body); + try cg.restoreState(state, &.{}, .{ .emit_instructions = false, .update_tracking = true, .resurrect = true, @@ -93819,9 +93887,12 @@ fn lowerSwitchBr( const relocs = try allocator.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); defer allocator.free(relocs); - try self.spillEflagsIfOccupied(); + var cond_temp = try cg.tempInit(condition_ty, condition); + const reset_index = cg.next_temp_index; + + try cg.spillEflagsIfOccupied(); for (case.items, relocs[0..case.items.len]) |item, *reloc| { - const item_mcv = try self.resolveInst(item); + const item_mcv = try cg.resolveInst(item); const cc: Condition = switch (condition) { .eflags => |cc| switch (item_mcv.immediate) { 0 => cc.negate(), @@ -93829,27 +93900,24 @@ fn lowerSwitchBr( else => unreachable, }, else => cc: { - var cond_temp = try self.tempInit(condition_ty, condition); - var item_temp = try self.tempInit(condition_ty, item_mcv); - const cc_temp = cond_temp.cmpInts(.eq, &item_temp, self) catch |err| switch (err) { + var item_temp = try cg.tempInit(condition_ty, item_mcv); + const cc_temp = cond_temp.cmpInts(.eq, &item_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try cond_temp.die(self); - try item_temp.die(self); - const cc = cc_temp.tracking(self).short.eflags; - try cc_temp.die(self); - try self.resetTemps(); + try item_temp.die(cg); + const cc = cc_temp.tracking(cg).short.eflags; + try cc_temp.die(cg); + try cg.resetTemps(reset_index); break :cc cc; }, }; - reloc.* = try self.asmJccReloc(cc, undefined); + reloc.* = try cg.asmJccReloc(cc, undefined); } for (case.ranges, relocs[case.items.len..]) |range, *reloc| { - var cond_temp = try self.tempInit(condition_ty, condition); - const min_mcv = try self.resolveInst(range[0]); - const max_mcv = try self.resolveInst(range[1]); + const min_mcv = try cg.resolveInst(range[0]); + const max_mcv = try cg.resolveInst(range[1]); // `null` means always false. const lt_min = cc: switch (condition) { .eflags => |cc| switch (min_mcv.immediate) { @@ -93858,19 +93926,19 @@ fn lowerSwitchBr( else => unreachable, }, else => { - var min_temp = try self.tempInit(condition_ty, min_mcv); - const cc_temp = cond_temp.cmpInts(.lt, &min_temp, self) catch |err| switch (err) { + var min_temp = try cg.tempInit(condition_ty, min_mcv); + const cc_temp = cond_temp.cmpInts(.lt, &min_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try min_temp.die(self); - const cc = cc_temp.tracking(self).short.eflags; - try cc_temp.die(self); + try min_temp.die(cg); + const cc = cc_temp.tracking(cg).short.eflags; + try cc_temp.die(cg); break :cc cc; }, }; const lt_min_reloc = if (lt_min) |cc| r: { - break :r try self.asmJccReloc(cc, undefined); + break :r try cg.asmJccReloc(cc, undefined); } else null; // `null` means always true. const lte_max = switch (condition) { @@ -93880,38 +93948,41 @@ fn lowerSwitchBr( else => unreachable, }, else => cc: { - var max_temp = try self.tempInit(condition_ty, max_mcv); - const cc_temp = cond_temp.cmpInts(.lte, &max_temp, self) catch |err| switch (err) { + var max_temp = try cg.tempInit(condition_ty, max_mcv); + const cc_temp = cond_temp.cmpInts(.lte, &max_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try max_temp.die(self); - const cc = cc_temp.tracking(self).short.eflags; - try cc_temp.die(self); + try max_temp.die(cg); + const cc = cc_temp.tracking(cg).short.eflags; + try cc_temp.die(cg); break :cc cc; }, }; - try cond_temp.die(self); - try self.resetTemps(); + try cg.resetTemps(reset_index); // "Success" case is in `reloc`.... if (lte_max) |cc| { - reloc.* = try self.asmJccReloc(cc, undefined); + reloc.* = try cg.asmJccReloc(cc, undefined); } else { - reloc.* = try self.asmJmpReloc(undefined); + reloc.* = try cg.asmJmpReloc(undefined); } // ...and "fail" case falls through to next checks. - if (lt_min_reloc) |r| self.performReloc(r); + if (lt_min_reloc) |r| cg.performReloc(r); } + try cond_temp.die(cg); + try cg.resetTemps(@enumFromInt(0)); + cg.checkInvariantsAfterAirInst(); + // The jump to skip this case if the conditions all failed. - const skip_case_reloc = try self.asmJmpReloc(undefined); + const skip_case_reloc = try cg.asmJmpReloc(undefined); - for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); + for (liveness.deaths[case.idx]) |operand| try cg.processDeath(operand); // Relocate all success cases to the body we're about to generate. - for (relocs) |reloc| self.performReloc(reloc); - try self.genBodyBlock(case.body); - try self.restoreState(state, &.{}, .{ + for (relocs) |reloc| cg.performReloc(reloc); + try cg.genBodyBlock(case.body); + try cg.restoreState(state, &.{}, .{ .emit_instructions = false, .update_tracking = true, .resurrect = true, @@ -93919,16 +93990,16 @@ fn lowerSwitchBr( }); // Relocate the "skip" branch to fall through to the next case. - self.performReloc(skip_case_reloc); + cg.performReloc(skip_case_reloc); } if (switch_br.else_body_len > 0) { const else_body = cases_it.elseBody(); const else_deaths = liveness.deaths.len - 1; - for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); + for (liveness.deaths[else_deaths]) |operand| try cg.processDeath(operand); - try self.genBodyBlock(else_body); - try self.restoreState(state, &.{}, .{ + try cg.genBodyBlock(else_body); + try cg.restoreState(state, &.{}, .{ .emit_instructions = false, .update_tracking = true, .resurrect = true, @@ -95003,7 +95074,7 @@ fn moveStrategy(cg: *CodeGen, ty: Type, class: Register.Class, aligned: bool) !M .mmx => {}, .sse => switch (ty.zigTypeTag(zcu)) { else => { - const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, cg.target.*, .other), .none); + const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, cg.target, .other), .none); assert(std.mem.indexOfNone(abi.Class, classes, &.{ .integer, .sse, .sseup, .memory, .float, .float_combine, }) == null); @@ -97909,16 +97980,150 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { switch (pred_mcv) { .register => |pred_reg| switch (pred_reg.class()) { .general_purpose => {}, - .sse => if (need_xmm0 and pred_reg.id() != comptime Register.xmm0.id()) { - try self.register_manager.getKnownReg(.xmm0, null); - try self.genSetReg(.xmm0, pred_ty, pred_mcv, .{}); - break :mask .xmm0; - } else break :mask if (has_blend) - pred_reg + .sse => if (elem_ty.toIntern() == .bool_type) + if (need_xmm0 and pred_reg.id() != comptime Register.xmm0.id()) { + try self.register_manager.getKnownReg(.xmm0, null); + try self.genSetReg(.xmm0, pred_ty, pred_mcv, .{}); + break :mask .xmm0; + } else break :mask if (has_blend) + pred_reg + else + try self.copyToTmpRegister(pred_ty, pred_mcv) else - try self.copyToTmpRegister(pred_ty, pred_mcv), + return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}), else => unreachable, }, + .register_mask => |pred_reg_mask| { + if (pred_reg_mask.info.scalar.bitSize(self.target) != 8 * elem_abi_size) + return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); + + const mask_reg: Register = if (need_xmm0 and pred_reg_mask.reg.id() != comptime Register.xmm0.id()) mask_reg: { + try self.register_manager.getKnownReg(.xmm0, null); + try self.genSetReg(.xmm0, ty, .{ .register = pred_reg_mask.reg }, .{}); + break :mask_reg .xmm0; + } else pred_reg_mask.reg; + const mask_alias = registerAlias(mask_reg, abi_size); + const mask_lock = self.register_manager.lockRegAssumeUnused(mask_reg); + defer self.register_manager.unlockReg(mask_lock); + + const lhs_mcv = try self.resolveInst(extra.lhs); + const lhs_lock = switch (lhs_mcv) { + .register => |lhs_reg| self.register_manager.lockRegAssumeUnused(lhs_reg), + else => null, + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + + const rhs_mcv = try self.resolveInst(extra.rhs); + const rhs_lock = switch (rhs_mcv) { + .register => |rhs_reg| self.register_manager.lockReg(rhs_reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + const order = has_blend != pred_reg_mask.info.inverted; + const reuse_mcv, const other_mcv = if (order) + .{ rhs_mcv, lhs_mcv } + else + .{ lhs_mcv, rhs_mcv }; + const dst_mcv: MCValue = if (reuse_mcv.isRegister() and self.reuseOperand( + inst, + if (order) extra.rhs else extra.lhs, + @intFromBool(order), + reuse_mcv, + )) reuse_mcv else if (has_avx) + .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } + else + try self.copyToRegisterWithInstTracking(inst, ty, reuse_mcv); + const dst_reg = dst_mcv.getReg().?; + const dst_alias = registerAlias(dst_reg, abi_size); + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + const mir_tag = @as(?Mir.Inst.FixedTag, if ((pred_reg_mask.info.kind == .all and + elem_ty.toIntern() != .f32_type and elem_ty.toIntern() != .f64_type) or pred_reg_mask.info.scalar == .byte) + if (has_avx) + .{ .vp_b, .blendv } + else if (has_blend) + .{ .p_b, .blendv } + else if (pred_reg_mask.info.kind == .all) + .{ .p_, undefined } + else + null + else if ((pred_reg_mask.info.kind == .all and (elem_ty.toIntern() != .f64_type or !self.hasFeature(.sse2))) or + pred_reg_mask.info.scalar == .dword) + if (has_avx) + .{ .v_ps, .blendv } + else if (has_blend) + .{ ._ps, .blendv } + else if (pred_reg_mask.info.kind == .all) + .{ ._ps, undefined } + else + null + else if (pred_reg_mask.info.kind == .all or pred_reg_mask.info.scalar == .qword) + if (has_avx) + .{ .v_pd, .blendv } + else if (has_blend) + .{ ._pd, .blendv } + else if (pred_reg_mask.info.kind == .all) + .{ ._pd, undefined } + else + null + else + null) orelse return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); + if (has_avx) { + const rhs_alias = if (reuse_mcv.isRegister()) + registerAlias(reuse_mcv.getReg().?, abi_size) + else rhs: { + try self.genSetReg(dst_reg, ty, reuse_mcv, .{}); + break :rhs dst_alias; + }; + if (other_mcv.isBase()) try self.asmRegisterRegisterMemoryRegister( + mir_tag, + dst_alias, + rhs_alias, + try other_mcv.mem(self, .{ .size = self.memSize(ty) }), + mask_alias, + ) else try self.asmRegisterRegisterRegisterRegister( + mir_tag, + dst_alias, + rhs_alias, + registerAlias(if (other_mcv.isRegister()) + other_mcv.getReg().? + else + try self.copyToTmpRegister(ty, other_mcv), abi_size), + mask_alias, + ); + } else if (has_blend) if (other_mcv.isBase()) try self.asmRegisterMemoryRegister( + mir_tag, + dst_alias, + try other_mcv.mem(self, .{ .size = self.memSize(ty) }), + mask_alias, + ) else try self.asmRegisterRegisterRegister( + mir_tag, + dst_alias, + registerAlias(if (other_mcv.isRegister()) + other_mcv.getReg().? + else + try self.copyToTmpRegister(ty, other_mcv), abi_size), + mask_alias, + ) else { + try self.asmRegisterRegister(.{ mir_tag[0], .@"and" }, dst_alias, mask_alias); + if (other_mcv.isBase()) try self.asmRegisterMemory( + .{ mir_tag[0], .andn }, + mask_alias, + try other_mcv.mem(self, .{ .size = .fromSize(abi_size) }), + ) else try self.asmRegisterRegister( + .{ mir_tag[0], .andn }, + mask_alias, + if (other_mcv.isRegister()) + other_mcv.getReg().? + else + try self.copyToTmpRegister(ty, other_mcv), + ); + try self.asmRegisterRegister(.{ mir_tag[0], .@"or" }, dst_alias, mask_alias); + } + break :result dst_mcv; + }, else => {}, } const mask_reg: Register = if (need_xmm0) mask_reg: { @@ -98121,7 +98326,7 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { const dst_lock = self.register_manager.lockReg(dst_reg); defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.childType(zcu).zigTypeTag(zcu)) { + const mir_tag = @as(?Mir.Inst.FixedTag, switch (elem_ty.zigTypeTag(zcu)) { else => null, .int => switch (abi_size) { 0 => unreachable, @@ -98137,7 +98342,7 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { null, else => null, }, - .float => switch (ty.childType(zcu).floatBits(self.target.*)) { + .float => switch (elem_ty.floatBits(self.target.*)) { else => unreachable, 16, 80, 128 => null, 32 => switch (vec_len) { @@ -98191,30 +98396,20 @@ fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { try self.copyToTmpRegister(ty, lhs_mcv), abi_size), mask_alias, ) else { - const mir_fixes = @as(?Mir.Inst.Fixes, switch (elem_ty.zigTypeTag(zcu)) { - else => null, - .int => .p_, - .float => switch (elem_ty.floatBits(self.target.*)) { - 32 => ._ps, - 64 => ._pd, - 16, 80, 128 => null, - else => unreachable, - }, - }) orelse return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); - try self.asmRegisterRegister(.{ mir_fixes, .@"and" }, dst_alias, mask_alias); + try self.asmRegisterRegister(.{ mir_tag[0], .@"and" }, dst_alias, mask_alias); if (rhs_mcv.isBase()) try self.asmRegisterMemory( - .{ mir_fixes, .andn }, + .{ mir_tag[0], .andn }, mask_alias, try rhs_mcv.mem(self, .{ .size = .fromSize(abi_size) }), ) else try self.asmRegisterRegister( - .{ mir_fixes, .andn }, + .{ mir_tag[0], .andn }, mask_alias, if (rhs_mcv.isRegister()) rhs_mcv.getReg().? else try self.copyToTmpRegister(ty, rhs_mcv), ); - try self.asmRegisterRegister(.{ mir_fixes, .@"or" }, dst_alias, mask_alias); + try self.asmRegisterRegister(.{ mir_tag[0], .@"or" }, dst_alias, mask_alias); } break :result dst_mcv; }; @@ -99635,7 +99830,7 @@ fn airVaArg(self: *CodeGen, inst: Air.Inst.Index) !void { const overflow_arg_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 8 } }; const reg_save_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 16 } }; - const classes = std.mem.sliceTo(&abi.classifySystemV(promote_ty, zcu, self.target.*, .arg), .none); + const classes = std.mem.sliceTo(&abi.classifySystemV(promote_ty, zcu, self.target, .arg), .none); switch (classes[0]) { .integer => { assert(classes.len == 1); @@ -99980,7 +100175,7 @@ fn resolveCallingConventionValues( var ret_tracking_i: usize = 0; const classes = switch (cc) { - .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target.*, .ret), .none), + .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target, .ret), .none), .x86_64_win => &.{abi.classifyWindows(ret_ty, zcu)}, else => unreachable, }; @@ -100069,7 +100264,7 @@ fn resolveCallingConventionValues( var arg_mcv_i: usize = 0; const classes = switch (cc) { - .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .arg), .none), + .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target, .arg), .none), .x86_64_win => &.{abi.classifyWindows(ty, zcu)}, else => unreachable, }; @@ -100373,7 +100568,7 @@ fn splitType(self: *CodeGen, comptime parts_len: usize, ty: Type) ![parts_len]Ty error.DivisionByZero => unreachable, error.UnexpectedRemainder => {}, }; - const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .other), .none); + const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target, .other), .none); if (classes.len == parts_len) for (&parts, classes, 0..) |*part, class, part_i| { part.* = switch (class) { .integer => if (part_i < parts_len - 1) @@ -100753,11 +100948,11 @@ const Temp = struct { const new_temp_index = cg.next_temp_index; cg.temp_type[@intFromEnum(new_temp_index)] = .usize; cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); - switch (temp.tracking(cg).short) { - else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), + const mcv = temp.tracking(cg).short; + switch (mcv) { + else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), .register => |reg| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); try cg.asmRegisterMemory(.{ ._, .lea }, new_reg.to64(), .{ .base = .{ .reg = reg.to64() }, @@ -100765,33 +100960,22 @@ const Temp = struct { }); }, .register_offset => |reg_off| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); try cg.asmRegisterMemory(.{ ._, .lea }, new_reg.to64(), .{ .base = .{ .reg = reg_off.reg.to64() }, .mod = .{ .rm = .{ .disp = reg_off.off + off } }, }); }, + .load_symbol, .load_frame => { + const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register_offset = .{ .reg = new_reg, .off = off } }); + try cg.genSetReg(new_reg, .usize, mcv, .{}); + }, .lea_symbol => |sym_off| new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = .{ .sym_index = sym_off.sym_index, .off = sym_off.off + off, } }), - .load_frame => |frame_addr| { - const new_reg = - try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); - new_temp_index.tracking(cg).* = .init(.{ .register_offset = .{ - .reg = new_reg, - .off = off, - } }); - try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ - .base = .{ .frame = frame_addr.index }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = frame_addr.off, - } }, - }); - }, .lea_frame => |frame_addr| new_temp_index.tracking(cg).* = .init(.{ .lea_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off, @@ -101108,7 +101292,8 @@ const Temp = struct { => return temp.toRegClass(true, .general_purpose, cg), .lea_symbol => |sym_off| { const off = sym_off.off; - if (off == 0) return false; + // hack around linker relocation bugs + if (false and off == 0) return false; try temp.toOffset(-off, cg); while (try temp.toRegClass(true, .general_purpose, cg)) {} try temp.toOffset(off, cg); @@ -101339,6 +101524,7 @@ const Temp = struct { const val_mcv = val.tracking(cg).short; switch (val_mcv) { else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), + .none => {}, .undef => if (opts.safe) { var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address().offset(opts.disp)); var pat = try cg.tempInit(.u8, .{ .immediate = 0xaa }); @@ -101371,19 +101557,19 @@ const Temp = struct { .disp = opts.disp, }), ), - .register => |val_reg| try dst.writeRegs(opts.disp, val_ty, &.{registerAlias( + .register => |val_reg| try dst.writeReg(opts.disp, val_ty, registerAlias( val_reg, @intCast(val_ty.abiSize(cg.pt.zcu)), - )}, cg), + ), cg), inline .register_pair, .register_triple, .register_quadruple, => |val_regs| try dst.writeRegs(opts.disp, val_ty, &val_regs, cg), .register_offset => |val_reg_off| switch (val_reg_off.off) { - 0 => try dst.writeRegs(opts.disp, val_ty, &.{registerAlias( + 0 => try dst.writeReg(opts.disp, val_ty, registerAlias( val_reg_off.reg, @intCast(val_ty.abiSize(cg.pt.zcu)), - )}, cg), + ), cg), else => continue :val_to_gpr, }, .register_overflow => |val_reg_ov| { @@ -101401,7 +101587,7 @@ const Temp = struct { else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, val_ty.fmt(cg.pt) }), }); const first_size: u31 = @intCast(first_ty.abiSize(cg.pt.zcu)); - try dst.writeRegs(opts.disp, first_ty, &.{registerAlias(val_reg_ov.reg, first_size)}, cg); + try dst.writeReg(opts.disp, first_ty, registerAlias(val_reg_ov.reg, first_size), cg); try cg.asmSetccMemory( val_reg_ov.eflags, try dst.tracking(cg).short.mem(cg, .{ @@ -101492,42 +101678,76 @@ const Temp = struct { })); } + fn writeReg(dst: Temp, disp: i32, src_ty: Type, src_reg: Register, cg: *CodeGen) InnerError!void { + const src_abi_size: u31 = @intCast(src_ty.abiSize(cg.pt.zcu)); + const src_rc = src_reg.class(); + if (src_rc == .x87 or std.math.isPowerOfTwo(src_abi_size)) { + const strat = try cg.moveStrategy(src_ty, src_rc, false); + try strat.write(cg, try dst.tracking(cg).short.mem(cg, .{ + .size = .fromBitSize(@min(8 * src_abi_size, src_reg.bitSize())), + .disp = disp, + }), registerAlias(src_reg, src_abi_size)); + } else { + const frame_size = std.math.ceilPowerOfTwoAssert(u32, src_abi_size); + const frame_index = try cg.allocFrameIndex(.init(.{ + .size = frame_size, + .alignment = .fromNonzeroByteUnits(frame_size), + })); + const strat = try cg.moveStrategy(src_ty, src_rc, true); + try strat.write(cg, .{ + .base = .{ .frame = frame_index }, + .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, + }, registerAlias(src_reg, frame_size)); + var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address()); + try dst_ptr.toOffset(disp, cg); + var src_ptr = try cg.tempInit(.usize, .{ .lea_frame = .{ .index = frame_index } }); + var len = try cg.tempInit(.usize, .{ .immediate = src_abi_size }); + try dst_ptr.memcpy(&src_ptr, &len, cg); + try dst_ptr.die(cg); + try src_ptr.die(cg); + try len.die(cg); + } + } + fn writeRegs(dst: Temp, disp: i32, src_ty: Type, src_regs: []const Register, cg: *CodeGen) InnerError!void { + const zcu = cg.pt.zcu; + const classes = std.mem.sliceTo(&abi.classifySystemV(src_ty, zcu, cg.target, .other), .none); + var next_class_index: u4 = 0; var part_disp = disp; - var src_abi_size: u32 = @intCast(src_ty.abiSize(cg.pt.zcu)); + var remaining_abi_size = src_ty.abiSize(zcu); for (src_regs) |src_reg| { - const src_rc = src_reg.class(); - const part_bit_size = @min(8 * src_abi_size, src_reg.bitSize()); - const part_size = @divExact(part_bit_size, 8); - if (src_rc == .x87 or std.math.isPowerOfTwo(part_size)) { - const strat = try cg.moveStrategy(src_ty, src_rc, false); - try strat.write(cg, try dst.tracking(cg).short.mem(cg, .{ - .size = .fromBitSize(part_bit_size), - .disp = part_disp, - }), registerAlias(src_reg, part_size)); - } else { - const frame_size = std.math.ceilPowerOfTwoAssert(u32, part_size); - const frame_index = try cg.allocFrameIndex(.init(.{ - .size = frame_size, - .alignment = .fromNonzeroByteUnits(frame_size), - })); - const strat = try cg.moveStrategy(src_ty, src_rc, true); - try strat.write(cg, .{ - .base = .{ .frame = frame_index }, - .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, - }, registerAlias(src_reg, frame_size)); - var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address()); - try dst_ptr.toOffset(part_disp, cg); - var src_ptr = try cg.tempInit(.usize, .{ .lea_frame = .{ .index = frame_index } }); - var len = try cg.tempInit(.usize, .{ .immediate = src_abi_size }); - try dst_ptr.memcpy(&src_ptr, &len, cg); - try dst_ptr.die(cg); - try src_ptr.die(cg); - try len.die(cg); - } + const class_index = next_class_index; + const class = classes[class_index]; + next_class_index = @intCast(switch (class) { + .integer, .memory, .float, .float_combine => class_index + 1, + .sse => std.mem.indexOfNonePos(abi.Class, classes, class_index + 1, &.{.sseup}) orelse classes.len, + .x87 => std.mem.indexOfNonePos(abi.Class, classes, class_index + 1, &.{.x87up}) orelse classes.len, + .sseup, .x87up, .complex_x87, .none, .win_i128, .integer_per_element => unreachable, + }); + const part_size = switch (class) { + .integer, .sse, .memory => @min(8 * @as(u7, next_class_index - class_index), remaining_abi_size), + .x87 => 16, + .float => 4, + .float_combine => 8, + .sseup, .x87up, .complex_x87, .none, .win_i128, .integer_per_element => unreachable, + }; + try dst.writeReg(part_disp, switch (class) { + .integer => .u64, + .sse => switch (part_size) { + else => unreachable, + 8 => .f64, + 16 => .vector_2_f64, + 32 => .vector_4_f64, + }, + .x87 => .f80, + .float => .f32, + .float_combine => .vector_2_f32, + .sseup, .x87up, .complex_x87, .memory, .none, .win_i128, .integer_per_element => unreachable, + }, src_reg, cg); part_disp += part_size; - src_abi_size -= part_size; + remaining_abi_size -= part_size; } + assert(next_class_index == classes.len); } fn memcpy(dst: *Temp, src: *Temp, len: *Temp, cg: *CodeGen) InnerError!void { @@ -105786,9 +106006,9 @@ const Temp = struct { }; }; -fn resetTemps(cg: *CodeGen) InnerError!void { +fn resetTemps(cg: *CodeGen, from_index: Temp.Index) InnerError!void { var any_valid = false; - for (0..@intFromEnum(cg.next_temp_index)) |temp_index| { + for (@intFromEnum(from_index)..@intFromEnum(cg.next_temp_index)) |temp_index| { const temp: Temp.Index = @enumFromInt(temp_index); if (temp.isValid(cg)) { any_valid = true; @@ -105800,7 +106020,7 @@ fn resetTemps(cg: *CodeGen) InnerError!void { cg.temp_type[temp_index] = undefined; } if (any_valid) return cg.fail("failed to kill all temps", .{}); - cg.next_temp_index = @enumFromInt(0); + cg.next_temp_index = from_index; } fn reuseTemp( @@ -105889,70 +106109,75 @@ fn tempMemFromValue(cg: *CodeGen, value: Value) InnerError!Temp { return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.lowerUav(value)); } -fn tempFromOperand( - cg: *CodeGen, - inst: Air.Inst.Index, - op_index: Liveness.OperandInt, - op_ref: Air.Inst.Ref, - ignore_death: bool, -) InnerError!Temp { +fn tempFromOperand(cg: *CodeGen, op_ref: Air.Inst.Ref, op_dies: bool) InnerError!Temp { const zcu = cg.pt.zcu; const ip = &zcu.intern_pool; - if (ignore_death or !cg.liveness.operandDies(inst, op_index)) { - if (op_ref.toIndex()) |op_inst| return .{ .index = op_inst }; - const val = op_ref.toInterned().?; - const gop = try cg.const_tracking.getOrPut(cg.gpa, val); - if (!gop.found_existing) gop.value_ptr.* = .init(init: { - const const_mcv = try cg.genTypedValue(.fromInterned(val)); - switch (const_mcv) { - .lea_tlv => |tlv_sym| switch (cg.bin_file.tag) { - .elf, .macho => { - if (cg.mod.pic) { - try cg.spillRegisters(&.{ .rdi, .rax }); - } else { - try cg.spillRegisters(&.{.rax}); - } - const frame_index = try cg.allocFrameIndex(.init(.{ - .size = 8, - .alignment = .@"8", - })); - try cg.genSetMem( - .{ .frame = frame_index }, - 0, - .usize, - .{ .lea_symbol = .{ .sym_index = tlv_sym } }, - .{}, - ); - break :init .{ .load_frame = .{ .index = frame_index } }; - }, - else => break :init const_mcv, + if (op_dies) { + const temp_index = cg.next_temp_index; + const temp: Temp = .{ .index = temp_index.toIndex() }; + const op_inst = op_ref.toIndex().?; + const tracking = cg.getResolvedInstValue(op_inst); + temp_index.tracking(cg).* = tracking.*; + if (!cg.reuseTemp(temp.index, op_inst, tracking)) return .{ .index = op_ref.toIndex().? }; + cg.temp_type[@intFromEnum(temp_index)] = cg.typeOf(op_ref); + cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); + return temp; + } + + if (op_ref.toIndex()) |op_inst| return .{ .index = op_inst }; + const val = op_ref.toInterned().?; + const gop = try cg.const_tracking.getOrPut(cg.gpa, val); + if (!gop.found_existing) gop.value_ptr.* = .init(init: { + const const_mcv = try cg.genTypedValue(.fromInterned(val)); + switch (const_mcv) { + .lea_tlv => |tlv_sym| switch (cg.bin_file.tag) { + .elf, .macho => { + if (cg.mod.pic) { + try cg.spillRegisters(&.{ .rdi, .rax }); + } else { + try cg.spillRegisters(&.{.rax}); + } + const frame_index = try cg.allocFrameIndex(.init(.{ + .size = 8, + .alignment = .@"8", + })); + try cg.genSetMem( + .{ .frame = frame_index }, + 0, + .usize, + .{ .lea_symbol = .{ .sym_index = tlv_sym } }, + .{}, + ); + break :init .{ .load_frame = .{ .index = frame_index } }; }, else => break :init const_mcv, - } - }); - return cg.tempInit(.fromInterned(ip.typeOf(val)), gop.value_ptr.short); - } + }, + else => break :init const_mcv, + } + }); + return cg.tempInit(.fromInterned(ip.typeOf(val)), gop.value_ptr.short); +} - const temp_index = cg.next_temp_index; - const temp: Temp = .{ .index = temp_index.toIndex() }; - const op_inst = op_ref.toIndex().?; - const tracking = cg.getResolvedInstValue(op_inst); - temp_index.tracking(cg).* = tracking.*; - if (!cg.reuseTemp(temp.index, op_inst, tracking)) return .{ .index = op_ref.toIndex().? }; - cg.temp_type[@intFromEnum(temp_index)] = cg.typeOf(op_ref); - cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); - return temp; +fn tempsFromOperandsInner( + cg: *CodeGen, + inst: Air.Inst.Index, + op_temps: []Temp, + op_refs: []const Air.Inst.Ref, +) InnerError!void { + for (op_temps, 0.., op_refs) |*op_temp, op_index, op_ref| op_temp.* = try cg.tempFromOperand(op_ref, for (op_refs[0..op_index]) |prev_op_ref| { + if (op_ref == prev_op_ref) break false; + } else cg.liveness.operandDies(inst, @intCast(op_index))); } -inline fn tempsFromOperands(cg: *CodeGen, inst: Air.Inst.Index, op_refs: anytype) InnerError![op_refs.len]Temp { - var temps: [op_refs.len]Temp = undefined; - inline for (&temps, 0.., op_refs) |*temp, op_index, op_ref| { - temp.* = try cg.tempFromOperand(inst, op_index, op_ref, inline for (0..op_index) |prev_op_index| { - if (op_ref == op_refs[prev_op_index]) break true; - } else false); - } - return temps; +inline fn tempsFromOperands( + cg: *CodeGen, + inst: Air.Inst.Index, + op_refs: anytype, +) InnerError![op_refs.len]Temp { + var op_temps: [op_refs.len]Temp = undefined; + try cg.tempsFromOperandsInner(inst, &op_temps, &op_refs); + return op_temps; } const Operand = union(enum) { diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 2ac3402fd8..6d11a44c2a 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -100,7 +100,7 @@ pub const Context = enum { ret, arg, field, other }; /// There are a maximum of 8 possible return slots. Returned values are in /// the beginning of the array; unused slots are filled with .none. -pub fn classifySystemV(ty: Type, zcu: *Zcu, target: std.Target, ctx: Context) [8]Class { +pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Context) [8]Class { const memory_class = [_]Class{ .memory, .none, .none, .none, .none, .none, .none, .none, @@ -148,7 +148,7 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: std.Target, ctx: Context) [8 result[0] = .integer; return result; }, - .float => switch (ty.floatBits(target)) { + .float => switch (ty.floatBits(target.*)) { 16 => { if (ctx == .field) { result[0] = .memory; @@ -330,7 +330,7 @@ fn classifySystemVStruct( starting_byte_offset: u64, loaded_struct: InternPool.LoadedStructType, zcu: *Zcu, - target: std.Target, + target: *const std.Target, ) u64 { const ip = &zcu.intern_pool; var byte_offset = starting_byte_offset; @@ -379,7 +379,7 @@ fn classifySystemVUnion( starting_byte_offset: u64, loaded_union: InternPool.LoadedUnionType, zcu: *Zcu, - target: std.Target, + target: *const std.Target, ) u64 { const ip = &zcu.intern_pool; for (0..loaded_union.field_types.len) |field_index| { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e82d75311e..f8aeface59 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -100,7 +100,6 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 { .kalimba, .propeller, => unreachable, // Gated by hasLlvmSupport(). - }; try llvm_triple.appendSlice(llvm_arch); @@ -309,467 +308,154 @@ pub fn supportsTailCall(target: std.Target) bool { } } -const DataLayoutBuilder = struct { - target: std.Target, +pub fn dataLayout(target: std.Target) []const u8 { + // These data layouts should match Clang. + return switch (target.cpu.arch) { + .arc => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-i64:32-f64:32-a:0:32-n32", + .xcore => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32", + .hexagon => "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048", + .lanai => "E-m:e-p:32:32-i64:64-a:0:32-n32-S64", + .aarch64 => if (target.ofmt == .macho) + if (target.os.tag == .windows) + "e-m:o-i64:64-i128:128-n32:64-S128-Fn32" + else if (target.abi == .ilp32) + "e-m:o-p:32:32-i64:64-i128:128-n32:64-S128-Fn32" + else + "e-m:o-i64:64-i128:128-n32:64-S128-Fn32" + else if (target.os.tag == .windows) + "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32" + else + "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", + .aarch64_be => "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", + .arm => if (target.ofmt == .macho) + "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .armeb, .thumbeb => if (target.ofmt == .macho) + "E-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .thumb => if (target.ofmt == .macho) + "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else if (target.os.tag == .windows) + "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .avr => "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", + .bpfeb => "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .bpfel => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .msp430 => "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16", + .mips => "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + .mipsel => "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + .mips64 => switch (target.abi) { + .gnuabin32, .muslabin32 => "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128", + else => "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128", + }, + .mips64el => switch (target.abi) { + .gnuabin32, .muslabin32 => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128", + else => "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128", + }, + .m68k => "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16", + .powerpc => if (target.os.tag == .aix) + "E-m:a-p:32:32-Fi32-i64:64-n32" + else + "E-m:e-p:32:32-Fn32-i64:64-n32", + .powerpcle => "e-m:e-p:32:32-Fn32-i64:64-n32", + .powerpc64 => switch (target.os.tag) { + .aix => "E-m:a-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512", + .linux => if (target.abi.isMusl()) + "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512" + else + "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512", + .ps3 => "E-m:e-p:32:32-Fi64-i64:64-n32:64", + else => if (target.os.tag == .openbsd or + (target.os.tag == .freebsd and target.os.version_range.semver.isAtLeast(.{ .major = 13, .minor = 0, .patch = 0 }) orelse false)) + "E-m:e-Fn32-i64:64-n32:64" + else + "E-m:e-Fi64-i64:64-n32:64", + }, + .powerpc64le => if (target.os.tag == .linux) + "e-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512" + else + "e-m:e-Fn32-i64:64-n32:64", + .nvptx => "e-p:32:32-i64:64-i128:128-v16:16-v32:32-n16:32:64", + .nvptx64 => "e-i64:64-i128:128-v16:16-v32:32-n16:32:64", + .amdgcn => "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9", + .riscv32 => if (std.Target.riscv.featureSetHas(target.cpu.features, .e)) + "e-m:e-p:32:32-i64:64-n32-S32" + else + "e-m:e-p:32:32-i64:64-n32-S128", + .riscv64 => if (std.Target.riscv.featureSetHas(target.cpu.features, .e)) + "e-m:e-p:64:64-i64:64-i128:128-n32:64-S64" + else + "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .sparc => "E-m:e-p:32:32-i64:64-f128:64-n32-S64", + .sparc64 => "E-m:e-i64:64-n32:64-S128", + .s390x => if (target.os.tag == .zos) + "E-m:l-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64" + else + "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64", + .x86 => switch (target.os.tag) { + .elfiamcu => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:32-f64:32-f128:32-n8:16:32-a:0:32-S32", + .windows => switch (target.abi) { + .cygnus => "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32", + .gnu => if (target.ofmt == .coff) + "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" + else + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32", + else => blk: { + const msvc = switch (target.abi) { + .none, .msvc => true, + else => false, + }; - pub fn format( - self: DataLayoutBuilder, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - try writer.writeByte(switch (self.target.cpu.arch.endian()) { - .little => 'e', - .big => 'E', - }); - switch (self.target.cpu.arch) { - .amdgcn, - .nvptx, - .nvptx64, - => {}, - .avr => try writer.writeAll("-P1"), - else => try writer.print("-m:{c}", .{@as(u8, switch (self.target.cpu.arch) { - .mips, .mipsel => 'm', // Mips mangling: Private symbols get a $ prefix. - else => switch (self.target.ofmt) { - .elf => 'e', // ELF mangling: Private symbols get a `.L` prefix. - //.goff => 'l', // GOFF mangling: Private symbols get a `@` prefix. - .macho => 'o', // Mach-O mangling: Private symbols get `L` prefix. - // Other symbols get a `_` prefix. - .coff => switch (self.target.os.tag) { - .uefi, .windows => switch (self.target.cpu.arch) { - .x86 => 'x', // Windows x86 COFF mangling: Private symbols get the usual - // prefix. Regular C symbols get a `_` prefix. Functions with `__stdcall`, - //`__fastcall`, and `__vectorcall` have custom mangling that appends `@N` - // where N is the number of bytes used to pass parameters. C++ symbols - // starting with `?` are not mangled in any way. - else => 'w', // Windows COFF mangling: Similar to x, except that normal C - // symbols do not receive a `_` prefix. - }, - else => 'e', - }, - //.xcoff => 'a', // XCOFF mangling: Private symbols get a `L..` prefix. - else => 'e', - }, - })}), - } - const stack_abi = self.target.stackAlignment() * 8; - if (self.target.cpu.arch == .csky) try writer.print("-S{d}", .{stack_abi}); - var any_non_integral = false; - const ptr_bit_width = self.target.ptrBitWidth(); - var default_info = struct { size: u16, abi: u16, pref: u16, idx: u16 }{ - .size = 64, - .abi = 64, - .pref = 64, - .idx = 64, - }; - const addr_space_info = llvmAddrSpaceInfo(self.target); - for (addr_space_info, 0..) |info, i| { - assert((info.llvm == .default) == (i == 0)); - if (info.non_integral) { - assert(info.llvm != .default); - any_non_integral = true; - } - const size = info.size orelse ptr_bit_width; - const abi = info.abi orelse ptr_bit_width; - const pref = info.pref orelse abi; - const idx = info.idx orelse size; - const matches_default = - size == default_info.size and - abi == default_info.abi and - pref == default_info.pref and - idx == default_info.idx; - if (info.llvm == .default) default_info = .{ - .size = size, - .abi = abi, - .pref = pref, - .idx = idx, - }; - if (!info.force_in_data_layout and matches_default and - self.target.cpu.arch != .riscv64 and - self.target.cpu.arch != .loongarch64 and - !(self.target.cpu.arch == .aarch64 and - (self.target.os.tag == .uefi or self.target.os.tag == .windows)) and - self.target.cpu.arch != .bpfeb and self.target.cpu.arch != .bpfel) continue; - try writer.writeAll("-p"); - if (info.llvm != .default) try writer.print("{d}", .{@intFromEnum(info.llvm)}); - try writer.print(":{d}:{d}", .{ size, abi }); - if (pref != abi or idx != size or self.target.cpu.arch == .hexagon) { - try writer.print(":{d}", .{pref}); - if (idx != size) try writer.print(":{d}", .{idx}); - } - } - if (self.target.cpu.arch.isArm()) - try writer.writeAll("-Fi8") // for thumb interwork - else if (self.target.cpu.arch == .powerpc64 and - self.target.os.tag != .freebsd and - self.target.os.tag != .openbsd and - !self.target.abi.isMusl()) - try writer.writeAll("-Fi64") - else if (self.target.cpu.arch.isPowerPC() and self.target.os.tag == .aix) - try writer.writeAll(if (self.target.cpu.arch.isPowerPC64()) "-Fi64" else "-Fi32") - else if (self.target.cpu.arch.isPowerPC()) - try writer.writeAll("-Fn32"); - if (self.target.cpu.arch != .hexagon) { - if (self.target.cpu.arch == .arc or self.target.cpu.arch == .s390x) - try self.typeAlignment(.integer, 1, 8, 8, false, writer); - try self.typeAlignment(.integer, 8, 8, 8, false, writer); - try self.typeAlignment(.integer, 16, 16, 16, false, writer); - try self.typeAlignment(.integer, 32, 32, 32, false, writer); - if (self.target.cpu.arch == .arc) - try self.typeAlignment(.float, 32, 32, 32, false, writer); - try self.typeAlignment(.integer, 64, 32, 64, false, writer); - try self.typeAlignment(.integer, 128, 32, 64, false, writer); - if (backendSupportsF16(self.target)) - try self.typeAlignment(.float, 16, 16, 16, false, writer); - if (self.target.cpu.arch != .arc) - try self.typeAlignment(.float, 32, 32, 32, false, writer); - try self.typeAlignment(.float, 64, 64, 64, false, writer); - if (self.target.cpu.arch.isX86()) try self.typeAlignment(.float, 80, 0, 0, false, writer); - try self.typeAlignment(.float, 128, 128, 128, false, writer); - } - switch (self.target.cpu.arch) { - .amdgcn => { - try self.typeAlignment(.vector, 16, 16, 16, false, writer); - try self.typeAlignment(.vector, 24, 32, 32, false, writer); - try self.typeAlignment(.vector, 32, 32, 32, false, writer); - try self.typeAlignment(.vector, 48, 64, 64, false, writer); - try self.typeAlignment(.vector, 96, 128, 128, false, writer); - try self.typeAlignment(.vector, 192, 256, 256, false, writer); - try self.typeAlignment(.vector, 256, 256, 256, false, writer); - try self.typeAlignment(.vector, 512, 512, 512, false, writer); - try self.typeAlignment(.vector, 1024, 1024, 1024, false, writer); - try self.typeAlignment(.vector, 2048, 2048, 2048, false, writer); - }, - .ve => {}, - else => { - try self.typeAlignment(.vector, 16, 32, 32, false, writer); - try self.typeAlignment(.vector, 32, 32, 32, false, writer); - try self.typeAlignment(.vector, 64, 64, 64, false, writer); - try self.typeAlignment(.vector, 128, 128, 128, true, writer); - }, - } - const swap_agg_nat = switch (self.target.cpu.arch) { - .x86, .x86_64 => switch (self.target.os.tag) { - .uefi, .windows => true, - else => false, - }, - .avr, .m68k => true, - else => false, - }; - if (!swap_agg_nat) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); - if (self.target.cpu.arch == .csky) try writer.writeAll("-Fi32"); - for (@as([]const u24, switch (self.target.cpu.arch) { - .avr => &.{8}, - .msp430 => &.{ 8, 16 }, - .arc, - .arm, - .armeb, - .csky, - .loongarch32, - .mips, - .mipsel, - .powerpc, - .powerpcle, - .riscv32, - .sparc, - .thumb, - .thumbeb, - .xtensa, - => &.{32}, - .aarch64, - .aarch64_be, - .amdgcn, - .bpfeb, - .bpfel, - .loongarch64, - .mips64, - .mips64el, - .powerpc64, - .powerpc64le, - .riscv64, - .s390x, - .sparc64, - .ve, - .wasm32, - .wasm64, - => &.{ 32, 64 }, - .hexagon => &.{ 16, 32 }, - .m68k, - .x86, - => &.{ 8, 16, 32 }, - .nvptx, - .nvptx64, - => &.{ 16, 32, 64 }, - .x86_64 => &.{ 8, 16, 32, 64 }, - else => &.{}, - }), 0..) |natural, index| switch (index) { - 0 => try writer.print("-n{d}", .{natural}), - else => try writer.print(":{d}", .{natural}), - }; - if (swap_agg_nat) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); - if (self.target.cpu.arch == .hexagon) { - try self.typeAlignment(.integer, 64, 64, 64, true, writer); - try self.typeAlignment(.integer, 32, 32, 32, true, writer); - try self.typeAlignment(.integer, 16, 16, 16, true, writer); - try self.typeAlignment(.integer, 1, 8, 8, true, writer); - try self.typeAlignment(.float, 32, 32, 32, true, writer); - try self.typeAlignment(.float, 64, 64, 64, true, writer); - } - if (stack_abi != ptr_bit_width or self.target.cpu.arch == .msp430 or - self.target.os.tag == .uefi or self.target.os.tag == .windows or - self.target.cpu.arch == .riscv32) - try writer.print("-S{d}", .{stack_abi}); - if (self.target.cpu.arch.isAARCH64()) - try writer.writeAll("-Fn32"); - switch (self.target.cpu.arch) { - .hexagon, .ve => { - try self.typeAlignment(.vector, 32, 128, 128, true, writer); - try self.typeAlignment(.vector, 64, 128, 128, true, writer); - try self.typeAlignment(.vector, 128, 128, 128, true, writer); - }, - else => {}, - } - if (self.target.cpu.arch != .amdgcn) { - try self.typeAlignment(.vector, 256, 128, 128, true, writer); - try self.typeAlignment(.vector, 512, 128, 128, true, writer); - try self.typeAlignment(.vector, 1024, 128, 128, true, writer); - try self.typeAlignment(.vector, 2048, 128, 128, true, writer); - try self.typeAlignment(.vector, 4096, 128, 128, true, writer); - try self.typeAlignment(.vector, 8192, 128, 128, true, writer); - try self.typeAlignment(.vector, 16384, 128, 128, true, writer); - } - const alloca_addr_space = llvmAllocaAddressSpace(self.target); - if (alloca_addr_space != .default) try writer.print("-A{d}", .{@intFromEnum(alloca_addr_space)}); - const global_addr_space = llvmDefaultGlobalAddressSpace(self.target); - if (global_addr_space != .default) try writer.print("-G{d}", .{@intFromEnum(global_addr_space)}); - if (any_non_integral) { - try writer.writeAll("-ni"); - for (addr_space_info) |info| if (info.non_integral) - try writer.print(":{d}", .{@intFromEnum(info.llvm)}); - } - } - - fn typeAlignment( - self: DataLayoutBuilder, - kind: enum { integer, vector, float, aggregate }, - size: u24, - default_abi: u24, - default_pref: u24, - default_force_pref: bool, - writer: anytype, - ) @TypeOf(writer).Error!void { - var abi = default_abi; - var pref = default_pref; - var force_abi = false; - var force_pref = default_force_pref; - if (kind == .float and size == 80) { - abi = 128; - pref = 128; - } - for (@as([]const std.Target.CType, switch (kind) { - .integer => &.{ .char, .short, .int, .long, .longlong }, - .float => &.{ .float, .double, .longdouble }, - .vector, .aggregate => &.{}, - })) |cty| { - if (self.target.cTypeBitSize(cty) != size) continue; - abi = self.target.cTypeAlignment(cty) * 8; - pref = self.target.cTypePreferredAlignment(cty) * 8; - break; - } - switch (kind) { - .integer => { - if (self.target.ptrBitWidth() <= 16 and size >= 128) return; - abi = @min(abi, Type.maxIntAlignment(self.target) * 8); - switch (self.target.cpu.arch) { - .aarch64, - .aarch64_be, - => if (size == 128) { - abi = size; - pref = size; - } else switch (self.target.os.tag) { - .macos, .ios, .watchos, .tvos, .visionos => {}, - .uefi, .windows => { - pref = size; - force_abi = size >= 32; - }, - else => pref = @max(pref, 32), - }, - .arc => if (size <= 64) { - abi = @min((std.math.divCeil(u24, size, 8) catch unreachable) * 8, 32); - pref = 32; - force_abi = true; - force_pref = size <= 32; - }, - .bpfeb, - .bpfel, - .nvptx, - .nvptx64, - .riscv64, - => if (size == 128) { - abi = size; - pref = size; - }, - .csky => if (size == 32 or size == 64) { - abi = 32; - pref = 32; - force_abi = true; - force_pref = true; - }, - .hexagon => force_abi = true, - .m68k => if (size <= 32) { - abi = @min(size, 16); - pref = size; - force_abi = true; - force_pref = true; - } else if (size == 64) { - abi = 32; - pref = size; - }, - .mips, - .mipsel, - .mips64, - .mips64el, - => pref = @max(pref, 32), - .s390x => pref = @max(pref, 16), - .ve => if (size == 64) { - abi = size; - pref = size; - }, - .xtensa => if (size <= 64) { - pref = @max(size, 32); - abi = size; - force_abi = size == 64; - }, - .x86 => switch (size) { - 128 => { - abi = size; - pref = size; - }, - else => {}, - }, - .x86_64 => switch (size) { - 64, 128 => { - abi = size; - pref = size; - }, - else => {}, - }, - .loongarch64 => switch (size) { - 128 => { - abi = size; - pref = size; - force_abi = true; - }, - else => {}, - }, - else => {}, - } - }, - .vector => if (self.target.cpu.arch.isArm()) { - switch (size) { - 128 => abi = 64, - else => {}, - } - } else if ((self.target.cpu.arch.isPowerPC64() and self.target.os.tag == .linux and - (size == 256 or size == 512)) or - (self.target.cpu.arch.isNvptx() and (size == 16 or size == 32))) - { - force_abi = true; - abi = size; - pref = size; - } else if (self.target.cpu.arch == .amdgcn and size <= 2048) { - force_abi = true; - } else if (self.target.cpu.arch == .csky and (size == 64 or size == 128)) { - abi = 32; - pref = 32; - force_pref = true; - } else if (self.target.cpu.arch == .hexagon and - ((size >= 32 and size <= 64) or (size >= 512 and size <= 2048))) - { - abi = size; - pref = size; - force_pref = true; - } else if (self.target.cpu.arch == .s390x and size == 128) { - abi = 64; - pref = 64; - force_pref = false; - } else if (self.target.cpu.arch == .ve and (size >= 64 and size <= 16384)) { - abi = 64; - pref = 64; - force_abi = true; - force_pref = true; - }, - .float => switch (self.target.cpu.arch) { - .amdgcn => if (size == 128) { - abi = size; - pref = size; - }, - .arc => if (size == 32 or size == 64) { - abi = 32; - pref = 32; - force_abi = true; - force_pref = size == 32; - }, - .avr, .msp430, .sparc64 => if (size != 32 and size != 64) return, - .csky => if (size == 32 or size == 64) { - abi = 32; - pref = 32; - force_abi = true; - force_pref = true; - }, - .hexagon => if (size == 32 or size == 64) { - force_abi = true; - }, - .ve, .xtensa => if (size == 64) { - abi = size; - pref = size; - }, - .wasm32, .wasm64 => if (self.target.os.tag == .emscripten and size == 128) { - abi = 64; - pref = 64; - }, - else => {}, - }, - .aggregate => if (self.target.os.tag == .uefi or self.target.os.tag == .windows or - self.target.cpu.arch.isArm()) - { - pref = @min(pref, self.target.ptrBitWidth()); - } else switch (self.target.cpu.arch) { - .arc, .csky => { - abi = 0; - pref = 32; - }, - .hexagon => { - abi = 0; - pref = 0; - }, - .m68k => { - abi = 0; - pref = 16; - }, - .msp430 => { - abi = 8; - pref = 8; - }, - .s390x => { - abi = 8; - pref = 16; + break :blk if (target.ofmt == .coff) + if (msvc) + "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" + else + "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" + else if (msvc) + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" + else + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32"; }, - else => {}, }, - } - if (kind != .vector and self.target.cpu.arch == .avr) { - force_abi = true; - abi = 8; - pref = 8; - } - if (!force_abi and abi == default_abi and pref == default_pref) return; - try writer.print("-{c}", .{@tagName(kind)[0]}); - if (size != 0) try writer.print("{d}", .{size}); - try writer.print(":{d}", .{abi}); - if (pref != abi or force_pref) try writer.print(":{d}", .{pref}); - } -}; + else => if (target.ofmt == .macho) + "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128" + else + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + }, + .x86_64 => if (target.os.tag.isDarwin() or target.ofmt == .macho) + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + else switch (target.abi) { + .gnux32, .muslx32 => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + else => if (target.os.tag == .windows and target.ofmt == .coff) + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + else + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + }, + .spirv => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + .spirv32 => "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + .spirv64 => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + .wasm32 => if (target.os.tag == .emscripten) + "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20" + else + "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20", + .wasm64 => if (target.os.tag == .emscripten) + "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20" + else + "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20", + .ve => "e-m:e-i64:64-n32:64-S128-v64:64:64-v128:64:64-v256:64:64-v512:64:64-v1024:64:64-v2048:64:64-v4096:64:64-v8192:64:64-v16384:64:64", + .csky => "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32", + .loongarch32 => "e-m:e-p:32:32-i64:64-n32-S128", + .loongarch64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .xtensa => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32", + + .kalimba, + .propeller, + => unreachable, // Gated by hasLlvmSupport(). + }; +} pub const Object = struct { gpa: Allocator, @@ -856,7 +542,7 @@ pub const Object = struct { }); errdefer builder.deinit(); - builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }}); + builder.data_layout = try builder.string(dataLayout(target)); const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref = if (!builder.strip) debug_info: { @@ -12071,7 +11757,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe } fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: std.Target) bool { - const class = x86_64_abi.classifySystemV(ty, zcu, target, .ret); + const class = x86_64_abi.classifySystemV(ty, zcu, &target, .ret); if (class[0] == .memory) return true; if (class[0] == .x87 and class[2] != .none) return true; return false; @@ -12181,7 +11867,7 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E return o.lowerType(return_type); } const target = zcu.getTarget(); - const classes = x86_64_abi.classifySystemV(return_type, zcu, target, .ret); + const classes = x86_64_abi.classifySystemV(return_type, zcu, &target, .ret); if (classes[0] == .memory) return .void; var types_index: u32 = 0; var types_buffer: [8]Builder.Type = undefined; @@ -12459,7 +12145,7 @@ const ParamTypeIterator = struct { const zcu = it.object.pt.zcu; const ip = &zcu.intern_pool; const target = zcu.getTarget(); - const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg); + const classes = x86_64_abi.classifySystemV(ty, zcu, &target, .arg); if (classes[0] == .memory) { it.zig_index += 1; it.llvm_index += 1; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 1b3dcfe8b5..a657667f15 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -2008,9 +2008,9 @@ pub const WipNav = struct { .decl_const_runtime_bits, .decl_const_comptime_state, .decl_const_runtime_bits_comptime_state, - .decl_empty_func, + .decl_nullary_func, .decl_func, - .decl_empty_func_generic, + .decl_nullary_func_generic, .decl_func_generic, => false, .generic_decl_var, @@ -2626,8 +2626,8 @@ pub fn finishWipNavFunc( abbrev_code_buf, try dwarf.refAbbrevCode(switch (abbrev_code) { else => unreachable, - .decl_func => .decl_empty_func, - .decl_instance_func => .decl_instance_empty_func, + .decl_func => .decl_nullary_func, + .decl_instance_func => .decl_instance_nullary_func, }), ); } @@ -3012,29 +3012,34 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo if (nav_gop.found_existing) switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, nav_gop.value_ptr.*)) { .null => {}, else => unreachable, - .decl_empty_func, .decl_func, .decl_instance_empty_func, .decl_instance_func => return, - .decl_empty_func_generic, + .decl_nullary_func, .decl_func, .decl_instance_nullary_func, .decl_instance_func => return, + .decl_nullary_func_generic, .decl_func_generic, - .decl_instance_empty_func_generic, + .decl_instance_nullary_func_generic, .decl_instance_func_generic, => dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear(), } else nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); wip_nav.entry = nav_gop.value_ptr.*; const func_type = ip.indexToKey(func.ty).func_type; + const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| { + if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false; + } else true; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try wip_nav.declCommon(if (func_type.param_types.len > 0 or func_type.is_var_args) .{ - .decl = .decl_func_generic, + try wip_nav.declCommon(if (is_nullary) .{ + .decl = .decl_nullary_func_generic, .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_func_generic, + .decl_instance = .decl_instance_nullary_func_generic, } else .{ - .decl = .decl_empty_func_generic, + .decl = .decl_func_generic, .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_empty_func_generic, + .decl_instance = .decl_instance_func_generic, }, &nav, inst_info.file, &decl); try wip_nav.refType(.fromInterned(func_type.return_type)); - if (func_type.param_types.len > 0 or func_type.is_var_args) { + if (!is_nullary) { for (0..func_type.param_types.len) |param_index| { + if (std.math.cast(u5, param_index)) |small_param_index| + if (func_type.paramIsComptime(small_param_index)) continue; try wip_nav.abbrevCode(.func_type_param); try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); } @@ -3568,12 +3573,14 @@ fn updateLazyType( }; try diw.writeByte(@intFromEnum(cc)); try wip_nav.refType(.fromInterned(func_type.return_type)); - for (0..func_type.param_types.len) |param_index| { - try wip_nav.abbrevCode(.func_type_param); - try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); + if (!is_nullary) { + for (0..func_type.param_types.len) |param_index| { + try wip_nav.abbrevCode(.func_type_param); + try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); + } + if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); + try uleb128(diw, @intFromEnum(AbbrevCode.null)); } - if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); - if (!is_nullary) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .error_set_type => |error_set_type| { try wip_nav.abbrevCode(if (error_set_type.names.len == 0) .generated_empty_enum_type else .generated_enum_type); @@ -4787,9 +4794,9 @@ const AbbrevCode = enum { decl_const_runtime_bits, decl_const_comptime_state, decl_const_runtime_bits_comptime_state, - decl_empty_func, + decl_nullary_func, decl_func, - decl_empty_func_generic, + decl_nullary_func_generic, decl_func_generic, generic_decl_var, generic_decl_const, @@ -4806,9 +4813,9 @@ const AbbrevCode = enum { decl_instance_const_runtime_bits, decl_instance_const_comptime_state, decl_instance_const_runtime_bits_comptime_state, - decl_instance_empty_func, + decl_instance_nullary_func, decl_instance_func, - decl_instance_empty_func_generic, + decl_instance_nullary_func_generic, decl_instance_func_generic, // the rest are unrestricted other than empty variants must not be longer // than the non-empty variant, and so should appear first @@ -5019,7 +5026,7 @@ const AbbrevCode = enum { .{ .ZIG_comptime_value, .ref_addr }, }, }, - .decl_empty_func = .{ + .decl_nullary_func = .{ .tag = .subprogram, .attrs = decl_abbrev_common_attrs ++ .{ .{ .linkage_name, .strp }, @@ -5044,7 +5051,7 @@ const AbbrevCode = enum { .{ .noreturn, .flag }, }, }, - .decl_empty_func_generic = .{ + .decl_nullary_func_generic = .{ .tag = .subprogram, .attrs = decl_abbrev_common_attrs ++ .{ .{ .type, .ref_addr }, @@ -5167,7 +5174,7 @@ const AbbrevCode = enum { .{ .ZIG_comptime_value, .ref_addr }, }, }, - .decl_instance_empty_func = .{ + .decl_instance_nullary_func = .{ .tag = .subprogram, .attrs = decl_instance_abbrev_common_attrs ++ .{ .{ .linkage_name, .strp }, @@ -5192,7 +5199,7 @@ const AbbrevCode = enum { .{ .noreturn, .flag }, }, }, - .decl_instance_empty_func_generic = .{ + .decl_instance_nullary_func_generic = .{ .tag = .subprogram, .attrs = decl_instance_abbrev_common_attrs ++ .{ .{ .type, .ref_addr }, diff --git a/src/main.zig b/src/main.zig index bc8fc930f5..356808804a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -39,7 +39,7 @@ test { _ = Package; } -const thread_stack_size = 32 << 20; +const thread_stack_size = 50 << 20; pub const std_options: std.Options = .{ .wasiCwd = wasi_cwd, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index f2fa2f4b0a..4b11f61f19 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -132,13 +132,20 @@ test "cmp f16" { try comptime testCmp(f16); } -test "cmp f32/f64" { +test "cmp f32" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; try testCmp(f32); try comptime testCmp(f32); +} + +test "cmp f64" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; + if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 + try testCmp(f64); try comptime testCmp(f64); } @@ -224,6 +231,98 @@ fn testCmp(comptime T: type) !void { } } +test "vector cmp f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + + try testCmpVector(f16); + try comptime testCmpVector(f16); +} + +test "vector cmp f32" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + + try testCmpVector(f32); + try comptime testCmpVector(f32); +} + +test "vector cmp f64" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + + try testCmpVector(f64); + try comptime testCmpVector(f64); +} + +test "vector cmp f128" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + + try testCmpVector(f128); + try comptime testCmpVector(f128); +} + +test "vector cmp f80/c_longdouble" { + if (true) return error.SkipZigTest; + + try testCmpVector(f80); + try comptime testCmpVector(f80); + try testCmpVector(c_longdouble); + try comptime testCmpVector(c_longdouble); +} +fn testCmpVector(comptime T: type) !void { + var edges = [_]T{ + -math.inf(T), + -math.floatMax(T), + -math.floatMin(T), + -math.floatTrueMin(T), + -0.0, + math.nan(T), + 0.0, + math.floatTrueMin(T), + math.floatMin(T), + math.floatMax(T), + math.inf(T), + }; + _ = &edges; + for (edges, 0..) |rhs, rhs_i| { + const rhs_v: @Vector(4, T) = .{ rhs, rhs, rhs, rhs }; + for (edges, 0..) |lhs, lhs_i| { + const no_nan = lhs_i != 5 and rhs_i != 5; + const lhs_order = if (lhs_i < 5) lhs_i else lhs_i - 2; + const rhs_order = if (rhs_i < 5) rhs_i else rhs_i - 2; + const lhs_v: @Vector(4, T) = .{ lhs, lhs, lhs, lhs }; + try expect(@reduce(.And, (lhs_v == rhs_v)) == (no_nan and lhs_order == rhs_order)); + try expect(@reduce(.And, (lhs_v != rhs_v)) == !(no_nan and lhs_order == rhs_order)); + try expect(@reduce(.And, (lhs_v < rhs_v)) == (no_nan and lhs_order < rhs_order)); + try expect(@reduce(.And, (lhs_v > rhs_v)) == (no_nan and lhs_order > rhs_order)); + try expect(@reduce(.And, (lhs_v <= rhs_v)) == (no_nan and lhs_order <= rhs_order)); + try expect(@reduce(.And, (lhs_v >= rhs_v)) == (no_nan and lhs_order >= rhs_order)); + } + } +} + test "different sized float comparisons" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1703,3 +1802,33 @@ test "optimized float mode" { try expect(S.optimized(small) == small); try expect(S.strict(small) == tiny); } + +fn MakeType(comptime x: anytype) type { + return struct { + fn get() @TypeOf(x) { + return x; + } + }; +} + +const nan_a: f32 = @bitCast(@as(u32, 0xffc00000)); +const nan_b: f32 = @bitCast(@as(u32, 0xffe00000)); + +fn testMemoization() !void { + try expect(MakeType(nan_a) == MakeType(nan_a)); + try expect(MakeType(nan_b) == MakeType(nan_b)); + try expect(MakeType(nan_a) != MakeType(nan_b)); +} + +fn testVectorMemoization(comptime T: type) !void { + const nan_a_v: T = @splat(nan_a); + const nan_b_v: T = @splat(nan_b); + try expect(MakeType(nan_a_v) == MakeType(nan_a_v)); + try expect(MakeType(nan_b_v) == MakeType(nan_b_v)); + try expect(MakeType(nan_a_v) != MakeType(nan_b_v)); +} + +test "comptime calls are only memoized when float arguments are bit-for-bit equal" { + try comptime testMemoization(); + try comptime testVectorMemoization(@Vector(4, f32)); +} diff --git a/test/behavior/select.zig b/test/behavior/select.zig index f551ff9533..604227b17f 100644 --- a/test/behavior/select.zig +++ b/test/behavior/select.zig @@ -66,3 +66,23 @@ fn selectArrays() !void { const xyz = @select(f32, x, y, z); try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); } + +test "@select compare result" { + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; + + const S = struct { + fn min(comptime V: type, lhs: V, rhs: V) V { + return @select(@typeInfo(V).vector.child, lhs < rhs, lhs, rhs); + } + + fn doTheTest() !void { + try expect(@reduce(.And, min(@Vector(4, f32), .{ -1, 2, -3, 4 }, .{ 1, -2, 3, -4 }) == @Vector(4, f32){ -1, -2, -3, -4 })); + try expect(@reduce(.And, min(@Vector(2, f64), .{ -1, 2 }, .{ 1, -2 }) == @Vector(2, f64){ -1, -2 })); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); +} diff --git a/test/behavior/this.zig b/test/behavior/this.zig index 3f8fe13316..9f10348b46 100644 --- a/test/behavior/this.zig +++ b/test/behavior/this.zig @@ -1,4 +1,5 @@ -const expect = @import("std").testing.expect; +const std = @import("std"); +const expect = std.testing.expect; const builtin = @import("builtin"); const module = @This(); @@ -55,3 +56,10 @@ test "this used as optional function parameter" { global.enter = prev; global.enter(null); } + +test "@This() in opaque" { + const T = opaque { + const Self = @This(); + }; + comptime std.debug.assert(T.Self == T); +} diff --git a/test/cases/compile_errors/union_field_ordered_differently_than_enum.zig b/test/cases/compile_errors/union_field_ordered_differently_than_enum.zig new file mode 100644 index 0000000000..5c86fb4080 --- /dev/null +++ b/test/cases/compile_errors/union_field_ordered_differently_than_enum.zig @@ -0,0 +1,27 @@ +const Tag = enum { a, b }; + +const Union = union(Tag) { + b, + a, +}; + +const BaseUnion = union(enum) { + a, + b, +}; + +const GeneratedTagUnion = union(@typeInfo(BaseUnion).@"union".tag_type.?) { + b, + a, +}; + +export fn entry() usize { + return @sizeOf(Union) + @sizeOf(GeneratedTagUnion); +} + +// error +// +// :4:5: error: union field 'b' ordered differently than corresponding enum field +// :1:23: note: enum field here +// :14:5: error: union field 'b' ordered differently than corresponding enum field +// :10:5: note: enum field here diff --git a/test/link/elf.zig b/test/link/elf.zig index 1afe1777d3..92b857a565 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -114,7 +114,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { elf_step.dependOn(testLargeBss(b, .{ .target = gnu_target })); elf_step.dependOn(testLinkOrder(b, .{ .target = gnu_target })); elf_step.dependOn(testLdScript(b, .{ .target = gnu_target })); - elf_step.dependOn(testLdScriptPathError(b, .{ .target = gnu_target })); + // https://github.com/ziglang/zig/issues/23125 + // elf_step.dependOn(testLdScriptPathError(b, .{ .target = gnu_target })); elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true })); elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true })); // https://github.com/ziglang/zig/issues/17451 diff --git a/test/tests.zig b/test/tests.zig index 7ef5a33a03..28c6fbc67e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1430,14 +1430,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) continue; - // TODO get compiler-rt tests passing for wasm32-wasi - // currently causes "LLVM ERROR: Unable to expand fixed point multiplication." - if (target.cpu.arch == .wasm32 and target.os.tag == .wasi and - mem.eql(u8, options.name, "compiler-rt")) - { - continue; - } - // TODO get universal-libc tests passing for other self-hosted backends. if (target.cpu.arch != .x86_64 and test_target.use_llvm == false and mem.eql(u8, options.name, "universal-libc")) |
