authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-12 17:57:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-12 17:57:35-07:00
logc19dcafa17117475f8334d5186bd87fb56c9d315
treef7af587f9a1355c5229699d78876f7d1a84942a4
parentc0b2813e0468586faee5bf3ee7583afad53d771a
parent2ab0c7391a871a3063f825e08e02ea2a8e9269e9

Merge remote-tracking branch 'origin/master' into llvm11


40 files changed, 964 insertions(+), 178 deletions(-)

CODE_OF_CONDUCT.md created+75
......@@ -0,0 +1,75 @@
1# Code of Conduct
2
3Hello, and welcome! 👋
4
5The Zig community is decentralized. Anyone is free to start and maintain their
6own space for people to gather, and edit
7[the Community wiki page](https://github.com/ziglang/zig/wiki/Community) to add
8a link. There is no concept of "official" or "unofficial", however, each
9gathering place has its own moderators and rules.
10
11This is Andrew Kelley speaking. At least for now, I'm the moderator of the
12ziglang organization GitHub repositories and the #zig IRC channel on Freenode.
13**This document contains the rules that govern these two spaces only**.
14
15The rules here are strict. This space is for focused, on topic, technical work
16on the Zig project only. It is everyone's responsibility to maintain a positive
17environment, especially when disagreements occur.
18
19## Our Standards
20
21Examples of behavior that contribute to creating a positive environment include:
22
23 * Using welcoming and inclusive language.
24 * Being respectful of differing viewpoints and experiences.
25 * Gracefully accepting constructive criticism.
26 * Helping another person accomplish their own goals.
27 * Showing empathy towards others.
28 * Showing appreciation for others' work.
29 * Validating someone else's experience, skills, insight, and use cases.
30
31Examples of unacceptable behavior by participants include:
32
33 * Unwelcome sexual attention or advances, or use of sexualized language or
34 imagery that causes discomfort.
35 * Trolling, insulting/derogatory comments, and personal attacks. Anything
36 antagonistic towards someone else.
37 * Off-topic discussion of any kind - especially offensive or sensitive issues.
38 * Publishing others' private information, such as a physical or electronic
39 address, without explicit permission.
40 * Discussing this Code of Conduct or publicly accusing someone of violating it.
41 * Making someone else feel like an outsider or implying a lack of technical
42 abilities.
43 * Destructive behavior. Anything that harms Zig or another open-source project.
44
45## Enforcement
46
47If you need to report an issue you can contact me or Loris Cro, who are both
48paid by the Zig Software Foundation, and so moderation of this space is part of
49our job. We will swiftly remove anyone who is antagonizing others or being
50generally destructive.
51
52This includes Private Harassment. If person A is directly harassed or
53antagonized by person B, person B will be blocked from participating in this
54space even if the harassment didn't take place on one of the mediums directly
55under rule of this Code of Conduct.
56
57As noted, discussing this Code of Conduct should not take place on GitHub or IRC
58because these spaces are for directly working on code, not for meta-discussion.
59If you have any issues with it, you can contact me directly, or you can join one
60of the community spaces that has different rules.
61
62 * Andrew Kelley <andrew@ziglang.org>
63 * Loris Cro <loris@ziglang.org>
64
65## Conclusion
66
67Thanks for reading the rules. Together, we can make this space welcoming and
68inclusive for everyone, regardless of age, body size, disability, ethnicity,
69sex characteristics, gender identity and expression, level of experience,
70education, socio-economic status, nationality, personal appearance, race,
71religion, or sexual identity and orientation.
72
73Sincerely,
74
75Andrew ✌️
README.md+2
......@@ -7,8 +7,10 @@ A general-purpose programming language and toolchain for maintaining
77
88 * [Introduction](https://ziglang.org/#Introduction)
99 * [Download & Documentation](https://ziglang.org/download)
10 * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/)
1011 * [Community](https://github.com/ziglang/zig/wiki/Community)
1112 * [Contributing](https://github.com/ziglang/zig/blob/master/CONTRIBUTING.md)
13 * [Code of Conduct](https://github.com/ziglang/zig/blob/master/CODE_OF_CONDUCT.md)
1214 * [Frequently Asked Questions](https://github.com/ziglang/zig/wiki/FAQ)
1315 * [Community Projects](https://github.com/ziglang/zig/wiki/Community-Projects)
1416
doc/langref.html.in+4-1
......@@ -9905,7 +9905,10 @@ const std = @import("std");
99059905const PreopenList = std.fs.wasi.PreopenList;
99069906
99079907pub fn main() !void {
9908 var preopens = PreopenList.init(std.heap.page_allocator);
9908 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
9909 const gpa = &general_purpose_allocator.allocator;
9910
9911 var preopens = PreopenList.init(gpa);
99099912 defer preopens.deinit();
99109913
99119914 try preopens.populate();
lib/std/c.zig+4-4
......@@ -22,7 +22,7 @@ pub usingnamespace @import("os/bits.zig");
2222pub usingnamespace switch (std.Target.current.os.tag) {
2323 .linux => @import("c/linux.zig"),
2424 .windows => @import("c/windows.zig"),
25 .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"),
25 .macos, .ios, .tvos, .watchos => @import("c/darwin.zig"),
2626 .freebsd, .kfreebsd => @import("c/freebsd.zig"),
2727 .netbsd => @import("c/netbsd.zig"),
2828 .dragonfly => @import("c/dragonfly.zig"),
......@@ -122,7 +122,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs
122122pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
123123
124124pub usingnamespace switch (builtin.os.tag) {
125 .macosx, .ios, .watchos, .tvos => struct {
125 .macos, .ios, .watchos, .tvos => struct {
126126 pub const realpath = @"realpath$DARWIN_EXTSN";
127127 pub const fstatat = @"fstatat$INODE64";
128128 },
......@@ -189,7 +189,7 @@ pub usingnamespace switch (builtin.os.tag) {
189189 pub const sigprocmask = __sigprocmask14;
190190 pub const stat = __stat50;
191191 },
192 .macosx, .ios, .watchos, .tvos => struct {
192 .macos, .ios, .watchos, .tvos => struct {
193193 // XXX: close -> close$NOCANCEL
194194 // XXX: getdirentries -> _getdirentries64
195195 pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;
......@@ -252,7 +252,7 @@ pub usingnamespace switch (builtin.os.tag) {
252252 .linux, .freebsd, .kfreebsd, .netbsd, .openbsd => struct {
253253 pub extern "c" fn malloc_usable_size(?*const c_void) usize;
254254 },
255 .macosx, .ios, .watchos, .tvos => struct {
255 .macos, .ios, .watchos, .tvos => struct {
256256 pub extern "c" fn malloc_size(?*const c_void) usize;
257257 },
258258 else => struct {},
lib/std/c/darwin.zig+8
......@@ -18,6 +18,14 @@ pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
1818pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;
1919pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8;
2020
21pub const COPYFILE_ACL = 1 << 0;
22pub const COPYFILE_STAT = 1 << 1;
23pub const COPYFILE_XATTR = 1 << 2;
24pub const COPYFILE_DATA = 1 << 3;
25
26pub const copyfile_state_t = *opaque {};
27pub extern "c" fn fcopyfile(from: fd_t, to: fd_t, state: ?copyfile_state_t, flags: u32) c_int;
28
2129pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
2230
2331pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;
lib/std/debug.zig+2-2
......@@ -654,7 +654,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
654654 .freebsd,
655655 .netbsd,
656656 .dragonfly,
657 .macosx,
657 .macos,
658658 .windows,
659659 => return DebugInfo.init(allocator),
660660 else => @compileError("openSelfDebugInfo unsupported for this platform"),
......@@ -1320,7 +1320,7 @@ const SymbolInfo = struct {
13201320};
13211321
13221322pub const ModuleDebugInfo = switch (builtin.os.tag) {
1323 .macosx, .ios, .watchos, .tvos => struct {
1323 .macos, .ios, .watchos, .tvos => struct {
13241324 base_address: usize,
13251325 mapped_memory: []const u8,
13261326 symbols: []const MachoSymbol,
lib/std/dynamic_library.zig+2-2
......@@ -19,7 +19,7 @@ const max = std.math.max;
1919pub const DynLib = switch (builtin.os.tag) {
2020 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
2121 .windows => WindowsDynLib,
22 .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib,
22 .macos, .tvos, .watchos, .ios, .freebsd => DlDynlib,
2323 else => void,
2424};
2525
......@@ -404,7 +404,7 @@ test "dynamic_library" {
404404 const libname = switch (builtin.os.tag) {
405405 .linux, .freebsd => "invalid_so.so",
406406 .windows => "invalid_dll.dll",
407 .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",
407 .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib",
408408 else => return error.SkipZigTest,
409409 };
410410
lib/std/event/loop.zig+12-12
......@@ -66,7 +66,7 @@ pub const Loop = struct {
6666 };
6767
6868 pub const EventFd = switch (builtin.os.tag) {
69 .macosx, .freebsd, .netbsd, .dragonfly => KEventFd,
69 .macos, .freebsd, .netbsd, .dragonfly => KEventFd,
7070 .linux => struct {
7171 base: ResumeNode,
7272 epoll_op: u32,
......@@ -85,7 +85,7 @@ pub const Loop = struct {
8585 };
8686
8787 pub const Basic = switch (builtin.os.tag) {
88 .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic,
88 .macos, .freebsd, .netbsd, .dragonfly => KEventBasic,
8989 .linux => struct {
9090 base: ResumeNode,
9191 },
......@@ -259,7 +259,7 @@ pub const Loop = struct {
259259 self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun);
260260 }
261261 },
262 .macosx, .freebsd, .netbsd, .dragonfly => {
262 .macos, .freebsd, .netbsd, .dragonfly => {
263263 self.os_data.kqfd = try os.kqueue();
264264 errdefer os.close(self.os_data.kqfd);
265265
......@@ -384,7 +384,7 @@ pub const Loop = struct {
384384 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
385385 os.close(self.os_data.epollfd);
386386 },
387 .macosx, .freebsd, .netbsd, .dragonfly => {
387 .macos, .freebsd, .netbsd, .dragonfly => {
388388 os.close(self.os_data.kqfd);
389389 },
390390 .windows => {
......@@ -478,7 +478,7 @@ pub const Loop = struct {
478478 .linux => {
479479 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN);
480480 },
481 .macosx, .freebsd, .netbsd, .dragonfly => {
481 .macos, .freebsd, .netbsd, .dragonfly => {
482482 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);
483483 },
484484 else => @compileError("Unsupported OS"),
......@@ -490,7 +490,7 @@ pub const Loop = struct {
490490 .linux => {
491491 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT);
492492 },
493 .macosx, .freebsd, .netbsd, .dragonfly => {
493 .macos, .freebsd, .netbsd, .dragonfly => {
494494 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);
495495 },
496496 else => @compileError("Unsupported OS"),
......@@ -502,7 +502,7 @@ pub const Loop = struct {
502502 .linux => {
503503 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN);
504504 },
505 .macosx, .freebsd, .netbsd, .dragonfly => {
505 .macos, .freebsd, .netbsd, .dragonfly => {
506506 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);
507507 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);
508508 },
......@@ -571,7 +571,7 @@ pub const Loop = struct {
571571 const eventfd_node = &resume_stack_node.data;
572572 eventfd_node.base.handle = next_tick_node.data;
573573 switch (builtin.os.tag) {
574 .macosx, .freebsd, .netbsd, .dragonfly => {
574 .macos, .freebsd, .netbsd, .dragonfly => {
575575 const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent);
576576 const empty_kevs = &[0]os.Kevent{};
577577 _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
......@@ -633,7 +633,7 @@ pub const Loop = struct {
633633 if (!builtin.single_threaded) {
634634 switch (builtin.os.tag) {
635635 .linux,
636 .macosx,
636 .macos,
637637 .freebsd,
638638 .netbsd,
639639 .dragonfly,
......@@ -725,7 +725,7 @@ pub const Loop = struct {
725725 }
726726 return;
727727 },
728 .macosx, .freebsd, .netbsd, .dragonfly => {
728 .macos, .freebsd, .netbsd, .dragonfly => {
729729 const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent);
730730 const empty_kevs = &[0]os.Kevent{};
731731 // cannot fail because we already added it and this just enables it
......@@ -1218,7 +1218,7 @@ pub const Loop = struct {
12181218 }
12191219 }
12201220 },
1221 .macosx, .freebsd, .netbsd, .dragonfly => {
1221 .macos, .freebsd, .netbsd, .dragonfly => {
12221222 var eventlist: [1]os.Kevent = undefined;
12231223 const empty_kevs = &[0]os.Kevent{};
12241224 const count = os.kevent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable;
......@@ -1344,7 +1344,7 @@ pub const Loop = struct {
13441344
13451345 const OsData = switch (builtin.os.tag) {
13461346 .linux => LinuxOsData,
1347 .macosx, .freebsd, .netbsd, .dragonfly => KEventData,
1347 .macos, .freebsd, .netbsd, .dragonfly => KEventData,
13481348 .windows => struct {
13491349 io_port: windows.HANDLE,
13501350 extra_thread_count: usize,
lib/std/fs.zig+66-6
......@@ -39,7 +39,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
3939/// fit into a UTF-8 encoded array of this length.
4040/// The byte count includes room for a null sentinel byte.
4141pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
42 .linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX,
42 .linux, .macos, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX,
4343 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
4444 // If it would require 4 UTF-8 bytes, then there would be a surrogate
4545 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
......@@ -303,7 +303,7 @@ pub const Dir = struct {
303303 const IteratorError = error{AccessDenied} || os.UnexpectedError;
304304
305305 pub const Iterator = switch (builtin.os.tag) {
306 .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct {
306 .macos, .ios, .freebsd, .netbsd, .dragonfly => struct {
307307 dir: Dir,
308308 seek: i64,
309309 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
......@@ -318,7 +318,7 @@ pub const Dir = struct {
318318 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
319319 pub fn next(self: *Self) Error!?Entry {
320320 switch (builtin.os.tag) {
321 .macosx, .ios => return self.nextDarwin(),
321 .macos, .ios => return self.nextDarwin(),
322322 .freebsd, .netbsd, .dragonfly => return self.nextBsd(),
323323 else => @compileError("unimplemented"),
324324 }
......@@ -615,7 +615,7 @@ pub const Dir = struct {
615615
616616 pub fn iterate(self: Dir) Iterator {
617617 switch (builtin.os.tag) {
618 .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{
618 .macos, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{
619619 .dir = self,
620620 .seek = 0,
621621 .index = 0,
......@@ -1302,7 +1302,7 @@ pub const Dir = struct {
13021302 error.AccessDenied => |e| switch (builtin.os.tag) {
13031303 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
13041304 // we need to handle that case specifically and translate the error
1305 .macosx, .ios, .freebsd, .netbsd, .dragonfly => {
1305 .macos, .ios, .freebsd, .netbsd, .dragonfly => {
13061306 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
13071307 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e;
13081308 const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR;
......@@ -1490,6 +1490,19 @@ pub const Dir = struct {
14901490 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
14911491 }
14921492
1493 /// Read all of file contents using a preallocated buffer.
1494 /// The returned slice has the same pointer as `buffer`. If the length matches `buffer.len`
1495 /// the situation is ambiguous. It could either mean that the entire file was read, and
1496 /// it exactly fits the buffer, or it could mean the buffer was not big enough for the
1497 /// entire file.
1498 pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {
1499 var file = try self.openFile(file_path, .{});
1500 defer file.close();
1501
1502 const end_index = try file.readAll(buffer);
1503 return buffer[0..end_index];
1504 }
1505
14931506 /// On success, caller owns returned buffer.
14941507 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
14951508 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {
......@@ -1823,7 +1836,7 @@ pub const Dir = struct {
18231836 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
18241837 defer atomic_file.deinit();
18251838
1826 try atomic_file.file.writeFileAll(in_file, .{ .in_len = size });
1839 try copy_file(in_file.handle, atomic_file.file.handle);
18271840 return atomic_file.finish();
18281841 }
18291842
......@@ -2271,6 +2284,53 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
22712284 return allocator.dupe(u8, try os.realpath(pathname, &buf));
22722285}
22732286
2287const CopyFileError = error{SystemResources} || os.CopyFileRangeError || os.SendFileError;
2288
2289// Transfer all the data between two file descriptors in the most efficient way.
2290// The copy starts at offset 0, the initial offsets are preserved.
2291// No metadata is transferred over.
2292fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t) CopyFileError!void {
2293 if (comptime std.Target.current.isDarwin()) {
2294 const rc = os.system.fcopyfile(fd_in, fd_out, null, os.system.COPYFILE_DATA);
2295 switch (os.errno(rc)) {
2296 0 => return,
2297 os.EINVAL => unreachable,
2298 os.ENOMEM => return error.SystemResources,
2299 // The source file is not a directory, symbolic link, or regular file.
2300 // Try with the fallback path before giving up.
2301 os.ENOTSUP => {},
2302 else => |err| return os.unexpectedErrno(err),
2303 }
2304 }
2305
2306 if (std.Target.current.os.tag == .linux) {
2307 // Try copy_file_range first as that works at the FS level and is the
2308 // most efficient method (if available).
2309 var offset: u64 = 0;
2310 cfr_loop: while (true) {
2311 // The kernel checks the u64 value `offset+count` for overflow, use
2312 // a 32 bit value so that the syscall won't return EINVAL except for
2313 // impossibly large files (> 2^64-1 - 2^32-1).
2314 const amt = try os.copy_file_range(fd_in, offset, fd_out, offset, math.maxInt(u32), 0);
2315 // Terminate when no data was copied
2316 if (amt == 0) break :cfr_loop;
2317 offset += amt;
2318 }
2319 return;
2320 }
2321
2322 // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the
2323 // fallback code will copy the contents chunk by chunk.
2324 const empty_iovec = [0]os.iovec_const{};
2325 var offset: u64 = 0;
2326 sendfile_loop: while (true) {
2327 const amt = try os.sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0);
2328 // Terminate when no data was copied
2329 if (amt == 0) break :sendfile_loop;
2330 offset += amt;
2331 }
2332}
2333
22742334test "" {
22752335 if (builtin.os.tag != .wasi) {
22762336 _ = makeDirAbsolute;
lib/std/fs/get_app_data_dir.zig+1-1
......@@ -42,7 +42,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
4242 else => return error.AppDataDirUnavailable,
4343 }
4444 },
45 .macosx => {
45 .macos => {
4646 const home_dir = os.getenv("HOME") orelse {
4747 // TODO look in /etc/passwd
4848 return error.AppDataDirUnavailable;
lib/std/fs/test.zig+1-1
......@@ -143,7 +143,7 @@ fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
143143
144144test "Dir.realpath smoke test" {
145145 switch (builtin.os.tag) {
146 .linux, .windows, .macosx, .ios, .watchos, .tvos => {},
146 .linux, .windows, .macos, .ios, .watchos, .tvos => {},
147147 else => return error.SkipZigTest,
148148 }
149149
lib/std/fs/watch.zig+4-4
......@@ -49,7 +49,7 @@ pub fn Watch(comptime V: type) type {
4949
5050 const OsData = switch (builtin.os.tag) {
5151 // TODO https://github.com/ziglang/zig/issues/3778
52 .macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
52 .macos, .freebsd, .netbsd, .dragonfly => KqOsData,
5353 .linux => LinuxOsData,
5454 .windows => WindowsOsData,
5555
......@@ -160,7 +160,7 @@ pub fn Watch(comptime V: type) type {
160160 return self;
161161 },
162162
163 .macosx, .freebsd, .netbsd, .dragonfly => {
163 .macos, .freebsd, .netbsd, .dragonfly => {
164164 self.* = Self{
165165 .allocator = allocator,
166166 .channel = channel,
......@@ -178,7 +178,7 @@ pub fn Watch(comptime V: type) type {
178178 /// All addFile calls and removeFile calls must have completed.
179179 pub fn deinit(self: *Self) void {
180180 switch (builtin.os.tag) {
181 .macosx, .freebsd, .netbsd, .dragonfly => {
181 .macos, .freebsd, .netbsd, .dragonfly => {
182182 // TODO we need to cancel the frames before destroying the lock
183183 self.os_data.table_lock.deinit();
184184 var it = self.os_data.file_table.iterator();
......@@ -229,7 +229,7 @@ pub fn Watch(comptime V: type) type {
229229
230230 pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
231231 switch (builtin.os.tag) {
232 .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
232 .macos, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
233233 .linux => return addFileLinux(self, file_path, value),
234234 .windows => return addFileWindows(self, file_path, value),
235235 else => @compileError("Unsupported OS"),
lib/std/math/big/int.zig+103-27
......@@ -446,6 +446,26 @@ pub const Mutable = struct {
446446 rma.positive = (a.positive == b.positive);
447447 }
448448
449 /// rma = a * a
450 ///
451 /// `rma` may not alias with `a`.
452 ///
453 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
454 /// rma is given by `2 * a.limbs.len + 1`.
455 ///
456 /// If `allocator` is provided, it will be used for temporary storage to improve
457 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
458 pub fn sqrNoAlias(rma: *Mutable, a: Const, opt_allocator: ?*Allocator) void {
459 assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing
460
461 mem.set(Limb, rma.limbs, 0);
462
463 llsquare_basecase(rma.limbs, a.limbs);
464
465 rma.normalize(2 * a.limbs.len + 1);
466 rma.positive = true;
467 }
468
449469 /// q = a / b (rem r)
450470 ///
451471 /// a / b are floored (rounded towards 0).
......@@ -1827,7 +1847,28 @@ pub const Managed = struct {
18271847 rma.setMetadata(m.positive, m.len);
18281848 }
18291849
1830 pub fn pow(rma: *Managed, a: Managed, b: u32) !void {
1850 /// r = a * a
1851 pub fn sqr(rma: *Managed, a: Const) !void {
1852 const needed_limbs = 2 * a.limbs.len + 1;
1853
1854 if (rma.limbs.ptr == a.limbs.ptr) {
1855 var m = try Managed.initCapacity(rma.allocator, needed_limbs);
1856 errdefer m.deinit();
1857 var m_mut = m.toMutable();
1858 m_mut.sqrNoAlias(a, rma.allocator);
1859 m.setMetadata(m_mut.positive, m_mut.len);
1860
1861 rma.deinit();
1862 rma.swap(&m);
1863 } else {
1864 try rma.ensureCapacity(needed_limbs);
1865 var rma_mut = rma.toMutable();
1866 rma_mut.sqrNoAlias(a, rma.allocator);
1867 rma.setMetadata(rma_mut.positive, rma_mut.len);
1868 }
1869 }
1870
1871 pub fn pow(rma: *Managed, a: Const, b: u32) !void {
18311872 const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b);
18321873
18331874 const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs);
......@@ -1837,7 +1878,7 @@ pub const Managed = struct {
18371878 var m = try Managed.initCapacity(rma.allocator, needed_limbs);
18381879 errdefer m.deinit();
18391880 var m_mut = m.toMutable();
1840 try m_mut.pow(a.toConst(), b, limbs_buffer);
1881 try m_mut.pow(a, b, limbs_buffer);
18411882 m.setMetadata(m_mut.positive, m_mut.len);
18421883
18431884 rma.deinit();
......@@ -1845,7 +1886,7 @@ pub const Managed = struct {
18451886 } else {
18461887 try rma.ensureCapacity(needed_limbs);
18471888 var rma_mut = rma.toMutable();
1848 try rma_mut.pow(a.toConst(), b, limbs_buffer);
1889 try rma_mut.pow(a, b, limbs_buffer);
18491890 rma.setMetadata(rma_mut.positive, rma_mut.len);
18501891 }
18511892 }
......@@ -1869,11 +1910,14 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L
18691910 assert(r.len >= x.len + y.len + 1);
18701911
18711912 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
1872 if (x.len > 48) {
1873 if (opt_allocator) |allocator| {
1874 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
1875 error.OutOfMemory => {}, // handled below
1876 };
1913 k_mul: {
1914 if (x.len > 48) {
1915 if (opt_allocator) |allocator| {
1916 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
1917 error.OutOfMemory => break :k_mul, // handled below
1918 };
1919 return;
1920 }
18771921 }
18781922 }
18791923
......@@ -2203,6 +2247,42 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
22032247 }
22042248}
22052249
2250/// r MUST NOT alias x.
2251fn llsquare_basecase(r: []Limb, x: []const Limb) void {
2252 @setRuntimeSafety(debug_safety);
2253
2254 const x_norm = x;
2255 assert(r.len >= 2 * x_norm.len + 1);
2256
2257 // Compute the square of a N-limb bigint with only (N^2 + N)/2
2258 // multiplications by exploting the symmetry of the coefficients around the
2259 // diagonal:
2260 //
2261 // a b c *
2262 // a b c =
2263 // -------------------
2264 // ca cb cc +
2265 // ba bb bc +
2266 // aa ab ac
2267 //
2268 // Note that:
2269 // - Each mixed-product term appears twice for each column,
2270 // - Squares are always in the 2k (0 <= k < N) column
2271
2272 for (x_norm) |v, i| {
2273 // Accumulate all the x[i]*x[j] (with x!=j) products
2274 llmulDigit(r[2 * i + 1 ..], x_norm[i + 1 ..], v);
2275 }
2276
2277 // Each product appears twice, multiply by 2
2278 llshl(r, r[0 .. 2 * x_norm.len], 1);
2279
2280 for (x_norm) |v, i| {
2281 // Compute and add the squares
2282 llmulDigit(r[2 * i ..], x[i .. i + 1], v);
2283 }
2284}
2285
22062286/// Knuth 4.6.3
22072287fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
22082288 var tmp1: []Limb = undefined;
......@@ -2212,9 +2292,9 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
22122292 // variable, use the output limbs and another temporary set to overcome this
22132293 // limitation.
22142294 // The initial assignment makes the result end in `r` so an extra memory
2215 // copy is saved, each 1 flips the index twice so it's a no-op so count the
2216 // 0.
2217 const b_leading_zeros = @intCast(u5, @clz(u32, b));
2295 // copy is saved, each 1 flips the index twice so it's only the zeros that
2296 // matter.
2297 const b_leading_zeros = @clz(u32, b);
22182298 const exp_zeros = @popCount(u32, ~b) - b_leading_zeros;
22192299 if (exp_zeros & 1 != 0) {
22202300 tmp1 = tmp_limbs;
......@@ -2224,32 +2304,28 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
22242304 tmp2 = tmp_limbs;
22252305 }
22262306
2227 const a_norm = a[0..llnormalize(a)];
2228
2229 mem.copy(Limb, tmp1, a_norm);
2230 mem.set(Limb, tmp1[a_norm.len..], 0);
2307 mem.copy(Limb, tmp1, a);
2308 mem.set(Limb, tmp1[a.len..], 0);
22312309
22322310 // Scan the exponent as a binary number, from left to right, dropping the
22332311 // most significant bit set.
2234 const exp_bits = @intCast(u5, 31 - b_leading_zeros);
2235 var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros;
2312 // Square the result if the current bit is zero, square and multiply by a if
2313 // it is one.
2314 var exp_bits = 32 - 1 - b_leading_zeros;
2315 var exp = b << @intCast(u5, 1 + b_leading_zeros);
22362316
2237 var i: u5 = 0;
2317 var i: usize = 0;
22382318 while (i < exp_bits) : (i += 1) {
22392319 // Square
2240 {
2241 mem.set(Limb, tmp2, 0);
2242 const op = tmp1[0..llnormalize(tmp1)];
2243 llmulacc(null, tmp2, op, op);
2244 mem.swap([]Limb, &tmp1, &tmp2);
2245 }
2320 mem.set(Limb, tmp2, 0);
2321 llsquare_basecase(tmp2, tmp1[0..llnormalize(tmp1)]);
2322 mem.swap([]Limb, &tmp1, &tmp2);
22462323 // Multiply by a
2247 if (exp & 1 != 0) {
2324 if (@shlWithOverflow(u32, exp, 1, &exp)) {
22482325 mem.set(Limb, tmp2, 0);
2249 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm);
2326 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a);
22502327 mem.swap([]Limb, &tmp1, &tmp2);
22512328 }
2252 exp >>= 1;
22532329 }
22542330}
22552331
lib/std/math/big/int_test.zig+34-10
......@@ -720,6 +720,27 @@ test "big.int mul 0*0" {
720720 testing.expect((try c.to(u32)) == 0);
721721}
722722
723test "big.int mul large" {
724 var a = try Managed.initCapacity(testing.allocator, 50);
725 defer a.deinit();
726 var b = try Managed.initCapacity(testing.allocator, 100);
727 defer b.deinit();
728 var c = try Managed.initCapacity(testing.allocator, 100);
729 defer c.deinit();
730
731 // Generate a number that's large enough to cross the thresholds for the use
732 // of subquadratic algorithms
733 for (a.limbs) |*p| {
734 p.* = std.math.maxInt(Limb);
735 }
736 a.setMetadata(true, 50);
737
738 try b.mul(a.toConst(), a.toConst());
739 try c.sqr(a.toConst());
740
741 testing.expect(b.eq(c));
742}
743
723744test "big.int div single-single no rem" {
724745 var a = try Managed.initSet(testing.allocator, 50);
725746 defer a.deinit();
......@@ -1483,11 +1504,14 @@ test "big.int const to managed" {
14831504
14841505test "big.int pow" {
14851506 {
1486 var a = try Managed.initSet(testing.allocator, 10);
1507 var a = try Managed.initSet(testing.allocator, -3);
14871508 defer a.deinit();
14881509
1489 try a.pow(a, 8);
1490 testing.expectEqual(@as(u32, 100000000), try a.to(u32));
1510 try a.pow(a.toConst(), 3);
1511 testing.expectEqual(@as(i32, -27), try a.to(i32));
1512
1513 try a.pow(a.toConst(), 4);
1514 testing.expectEqual(@as(i32, 531441), try a.to(i32));
14911515 }
14921516 {
14931517 var a = try Managed.initSet(testing.allocator, 10);
......@@ -1497,9 +1521,9 @@ test "big.int pow" {
14971521 defer y.deinit();
14981522
14991523 // y and a are not aliased
1500 try y.pow(a, 123);
1524 try y.pow(a.toConst(), 123);
15011525 // y and a are aliased
1502 try a.pow(a, 123);
1526 try a.pow(a.toConst(), 123);
15031527
15041528 testing.expect(a.eq(y));
15051529
......@@ -1517,18 +1541,18 @@ test "big.int pow" {
15171541 var a = try Managed.initSet(testing.allocator, 0);
15181542 defer a.deinit();
15191543
1520 try a.pow(a, 100);
1544 try a.pow(a.toConst(), 100);
15211545 testing.expectEqual(@as(i32, 0), try a.to(i32));
15221546
15231547 try a.set(1);
1524 try a.pow(a, 0);
1548 try a.pow(a.toConst(), 0);
15251549 testing.expectEqual(@as(i32, 1), try a.to(i32));
1526 try a.pow(a, 100);
1550 try a.pow(a.toConst(), 100);
15271551 testing.expectEqual(@as(i32, 1), try a.to(i32));
15281552 try a.set(-1);
1529 try a.pow(a, 15);
1553 try a.pow(a.toConst(), 15);
15301554 testing.expectEqual(@as(i32, -1), try a.to(i32));
1531 try a.pow(a, 16);
1555 try a.pow(a.toConst(), 16);
15321556 testing.expectEqual(@as(i32, 1), try a.to(i32));
15331557 }
15341558}
lib/std/os.zig+29-20
......@@ -62,7 +62,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This())
6262else if (builtin.link_libc)
6363 std.c
6464else switch (builtin.os.tag) {
65 .macosx, .ios, .watchos, .tvos => darwin,
65 .macos, .ios, .watchos, .tvos => darwin,
6666 .freebsd => freebsd,
6767 .linux => linux,
6868 .netbsd => netbsd,
......@@ -354,7 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
354354 // Prevents EINVAL.
355355 const max_count = switch (std.Target.current.os.tag) {
356356 .linux => 0x7ffff000,
357 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
357 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
358358 else => math.maxInt(isize),
359359 };
360360 const adjusted_len = math.min(max_count, buf.len);
......@@ -582,7 +582,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
582582/// On these systems, the read races with concurrent writes to the same file descriptor.
583583pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
584584 const have_pread_but_not_preadv = switch (std.Target.current.os.tag) {
585 .windows, .macosx, .ios, .watchos, .tvos => true,
585 .windows, .macos, .ios, .watchos, .tvos => true,
586586 else => false,
587587 };
588588 if (have_pread_but_not_preadv) {
......@@ -709,7 +709,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
709709
710710 const max_count = switch (std.Target.current.os.tag) {
711711 .linux => 0x7ffff000,
712 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
712 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
713713 else => math.maxInt(isize),
714714 };
715715 const adjusted_len = math.min(max_count, bytes.len);
......@@ -863,7 +863,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
863863 // Prevent EINVAL.
864864 const max_count = switch (std.Target.current.os.tag) {
865865 .linux => 0x7ffff000,
866 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
866 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
867867 else => math.maxInt(isize),
868868 };
869869 const adjusted_len = math.min(max_count, bytes.len);
......@@ -915,7 +915,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
915915/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
916916pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
917917 const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) {
918 .windows, .macosx, .ios, .watchos, .tvos => true,
918 .windows, .macos, .ios, .watchos, .tvos => true,
919919 else => false,
920920 };
921921
......@@ -4091,7 +4091,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
40914091 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
40924092 return out_buffer[0..end_index];
40934093 },
4094 .macosx, .ios, .watchos, .tvos => {
4094 .macos, .ios, .watchos, .tvos => {
40954095 // On macOS, we can use F_GETPATH fcntl command to query the OS for
40964096 // the path to the file descriptor.
40974097 @memset(out_buffer, 0, MAX_PATH_BYTES);
......@@ -4688,7 +4688,7 @@ pub fn sendfile(
46884688 });
46894689 const max_count = switch (std.Target.current.os.tag) {
46904690 .linux => 0x7ffff000,
4691 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
4691 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
46924692 else => math.maxInt(size_t),
46934693 };
46944694
......@@ -4846,7 +4846,7 @@ pub fn sendfile(
48464846 }
48474847 }
48484848 },
4849 .macosx, .ios, .tvos, .watchos => sf: {
4849 .macos, .ios, .tvos, .watchos => sf: {
48504850 var hdtr_data: std.c.sf_hdtr = undefined;
48514851 var hdtr: ?*std.c.sf_hdtr = null;
48524852 if (headers.len != 0 or trailers.len != 0) {
......@@ -4945,6 +4945,9 @@ pub fn sendfile(
49454945pub const CopyFileRangeError = error{
49464946 FileTooBig,
49474947 InputOutput,
4948 /// `fd_in` is not open for reading; or `fd_out` is not open for writing;
4949 /// or the `O_APPEND` flag is set for `fd_out`.
4950 FilesOpenedWithWrongFlags,
49484951 IsDir,
49494952 OutOfMemory,
49504953 NoSpaceLeft,
......@@ -4953,6 +4956,11 @@ pub const CopyFileRangeError = error{
49534956 FileBusy,
49544957} || PReadError || PWriteError || UnexpectedError;
49554958
4959var has_copy_file_range_syscall = init: {
4960 const kernel_has_syscall = std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true;
4961 break :init std.atomic.Int(bool).init(kernel_has_syscall);
4962};
4963
49564964/// Transfer data between file descriptors at specified offsets.
49574965/// Returns the number of bytes written, which can less than requested.
49584966///
......@@ -4981,22 +4989,18 @@ pub const CopyFileRangeError = error{
49814989pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
49824990 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
49834991
4984 // TODO support for other systems than linux
4985 const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false;
4986
4987 if (use_c or try_syscall) {
4992 if (std.Target.current.os.tag == .linux and
4993 (use_c or has_copy_file_range_syscall.get()))
4994 {
49884995 const sys = if (use_c) std.c else linux;
49894996
49904997 var off_in_copy = @bitCast(i64, off_in);
49914998 var off_out_copy = @bitCast(i64, off_out);
49924999
49935000 const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
4994
4995 // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018
4996
49975001 switch (sys.getErrno(rc)) {
49985002 0 => return @intCast(usize, rc),
4999 EBADF => unreachable,
5003 EBADF => return error.FilesOpenedWithWrongFlags,
50005004 EFBIG => return error.FileTooBig,
50015005 EIO => return error.InputOutput,
50025006 EISDIR => return error.IsDir,
......@@ -5005,9 +5009,14 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
50055009 EOVERFLOW => return error.Unseekable,
50065010 EPERM => return error.PermissionDenied,
50075011 ETXTBSY => return error.FileBusy,
5008 EINVAL => {}, // these may not be regular files, try fallback
5009 EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback
5010 ENOSYS => {}, // syscall added in Linux 4.5, use fallback
5012 // these may not be regular files, try fallback
5013 EINVAL => {},
5014 // support for cross-filesystem copy added in Linux 5.3, use fallback
5015 EXDEV => {},
5016 // syscall added in Linux 4.5, use fallback
5017 ENOSYS => {
5018 has_copy_file_range_syscall.set(false);
5019 },
50115020 else => |err| return unexpectedErrno(err),
50125021 }
50135022 }
lib/std/os/bits.zig+1-1
......@@ -12,7 +12,7 @@ const std = @import("std");
1212const root = @import("root");
1313
1414pub usingnamespace switch (std.Target.current.os.tag) {
15 .macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"),
15 .macos, .ios, .tvos, .watchos => @import("bits/darwin.zig"),
1616 .dragonfly => @import("bits/dragonfly.zig"),
1717 .freebsd => @import("bits/freebsd.zig"),
1818 .linux => @import("bits/linux.zig"),
lib/std/os/test.zig+1-1
......@@ -360,7 +360,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
360360}
361361
362362test "dl_iterate_phdr" {
363 if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macosx)
363 if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macos)
364364 return error.SkipZigTest;
365365
366366 var counter: usize = 0;
lib/std/packed_int_array.zig+2-2
......@@ -618,7 +618,7 @@ test "PackedIntArray at end of available memory" {
618618 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
619619
620620 switch (builtin.os.tag) {
621 .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
621 .linux, .macos, .ios, .freebsd, .netbsd, .windows => {},
622622 else => return,
623623 }
624624 const PackedArray = PackedIntArray(u3, 8);
......@@ -639,7 +639,7 @@ test "PackedIntSlice at end of available memory" {
639639 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
640640
641641 switch (builtin.os.tag) {
642 .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
642 .linux, .macos, .ios, .freebsd, .netbsd, .windows => {},
643643 else => return,
644644 }
645645 const PackedSlice = PackedIntSlice(u11);
lib/std/process.zig+3-3
......@@ -585,7 +585,7 @@ pub const UserInfo = struct {
585585/// POSIX function which gets a uid from username.
586586pub fn getUserInfo(name: []const u8) !UserInfo {
587587 return switch (builtin.os.tag) {
588 .linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name),
588 .linux, .macos, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name),
589589 else => @compileError("Unsupported OS"),
590590 };
591591}
......@@ -688,7 +688,7 @@ pub fn getBaseAddress() usize {
688688 const phdr = os.system.getauxval(std.elf.AT_PHDR);
689689 return phdr - @sizeOf(std.elf.Ehdr);
690690 },
691 .macosx, .freebsd, .netbsd => {
691 .macos, .freebsd, .netbsd => {
692692 return @ptrToInt(&std.c._mh_execute_header);
693693 },
694694 .windows => return @ptrToInt(os.windows.kernel32.GetModuleHandleW(null)),
......@@ -733,7 +733,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
733733 }.callback);
734734 return paths.toOwnedSlice();
735735 },
736 .macosx, .ios, .watchos, .tvos => {
736 .macos, .ios, .watchos, .tvos => {
737737 var paths = List.init(allocator);
738738 errdefer {
739739 const slice = paths.toOwnedSlice();
lib/std/special/compiler_rt/clear_cache.zig+1-1
......@@ -44,7 +44,7 @@ pub fn clear_cache(start: usize, end: usize) callconv(.C) void {
4444 else => false,
4545 };
4646 const apple = switch (os) {
47 .ios, .macosx, .watchos, .tvos => true,
47 .ios, .macos, .watchos, .tvos => true,
4848 else => false,
4949 };
5050 if (x86) {
lib/std/target.zig+8-8
......@@ -31,7 +31,7 @@ pub const Target = struct {
3131 kfreebsd,
3232 linux,
3333 lv2,
34 macosx,
34 macos,
3535 netbsd,
3636 openbsd,
3737 solaris,
......@@ -61,7 +61,7 @@ pub const Target = struct {
6161
6262 pub fn isDarwin(tag: Tag) bool {
6363 return switch (tag) {
64 .ios, .macosx, .watchos, .tvos => true,
64 .ios, .macos, .watchos, .tvos => true,
6565 else => false,
6666 };
6767 }
......@@ -234,7 +234,7 @@ pub const Target = struct {
234234 .max = .{ .major = 12, .minor = 1 },
235235 },
236236 },
237 .macosx => return .{
237 .macos => return .{
238238 .semver = .{
239239 .min = .{ .major = 10, .minor = 13 },
240240 .max = .{ .major = 10, .minor = 15, .patch = 3 },
......@@ -312,7 +312,7 @@ pub const Target = struct {
312312 .windows => return TaggedVersionRange{ .windows = self.version_range.windows },
313313
314314 .freebsd,
315 .macosx,
315 .macos,
316316 .ios,
317317 .tvos,
318318 .watchos,
......@@ -341,7 +341,7 @@ pub const Target = struct {
341341 return switch (os.tag) {
342342 .freebsd,
343343 .netbsd,
344 .macosx,
344 .macos,
345345 .ios,
346346 .tvos,
347347 .watchos,
......@@ -450,7 +450,7 @@ pub const Target = struct {
450450 .other,
451451 => return .eabi,
452452 .openbsd,
453 .macosx,
453 .macos,
454454 .freebsd,
455455 .ios,
456456 .tvos,
......@@ -1277,7 +1277,7 @@ pub const Target = struct {
12771277 .ios,
12781278 .tvos,
12791279 .watchos,
1280 .macosx,
1280 .macos,
12811281 .uefi,
12821282 .windows,
12831283 .emscripten,
......@@ -1450,7 +1450,7 @@ pub const Target = struct {
14501450 .ios,
14511451 .tvos,
14521452 .watchos,
1453 .macosx,
1453 .macos,
14541454 .uefi,
14551455 .windows,
14561456 .emscripten,
lib/std/time.zig+1-1
......@@ -143,7 +143,7 @@ pub const Timer = struct {
143143 /// be less precise
144144 frequency: switch (builtin.os.tag) {
145145 .windows => u64,
146 .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,
146 .macos, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,
147147 else => void,
148148 },
149149 resolution: u64,
lib/std/zig/cross_target.zig+3-3
......@@ -137,7 +137,7 @@ pub const CrossTarget = struct {
137137 },
138138
139139 .freebsd,
140 .macosx,
140 .macos,
141141 .ios,
142142 .tvos,
143143 .watchos,
......@@ -578,7 +578,7 @@ pub const CrossTarget = struct {
578578 const os = switch (self.getOsTag()) {
579579 .windows => "windows",
580580 .linux => "linux",
581 .macosx => "macos",
581 .macos => "macos",
582582 else => return error.UnsupportedVcpkgOperatingSystem,
583583 };
584584
......@@ -718,7 +718,7 @@ pub const CrossTarget = struct {
718718 => return error.InvalidOperatingSystemVersion,
719719
720720 .freebsd,
721 .macosx,
721 .macos,
722722 .ios,
723723 .tvos,
724724 .watchos,
lib/std/zig/system.zig+1-1
......@@ -267,7 +267,7 @@ pub const NativeTargetInfo = struct {
267267 os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version);
268268 os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version);
269269 },
270 .macosx => {
270 .macos => {
271271 var scbuf: [32]u8 = undefined;
272272 var size: usize = undefined;
273273
src/Cache.zig+24
......@@ -576,6 +576,30 @@ pub const Manifest = struct {
576576 }
577577};
578578
579/// On operating systems that support symlinks, does a readlink. On other operating systems,
580/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
581/// it is treated as not supporting symlinks.
582pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
583 if (std.Target.current.os.tag == .windows) {
584 return dir.readFile(sub_path, buffer);
585 } else {
586 return dir.readLink(sub_path, buffer);
587 }
588}
589
590/// On operating systems that support symlinks, does a symlink. On other operating systems,
591/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
592/// it is treated as not supporting symlinks.
593/// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer.
594pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void {
595 assert(data.len <= 255);
596 if (std.Target.current.os.tag == .windows) {
597 return dir.writeFile(sub_path, data);
598 } else {
599 return dir.symLink(data, sub_path, .{});
600 }
601}
602
579603fn hashFile(file: fs.File, bin_digest: []u8) !void {
580604 var buf: [1024]u8 = undefined;
581605
src/Compilation.zig+14-7
......@@ -922,7 +922,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
922922 try comp.work_queue.writeItem(.libcxx);
923923 try comp.work_queue.writeItem(.libcxxabi);
924924 }
925 if (is_exe_or_dyn_lib and build_options.is_stage1) {
925
926 const needs_compiler_rt_and_c = is_exe_or_dyn_lib or
927 (comp.getTarget().isWasm() and comp.bin_file.options.output_mode != .Obj);
928 if (needs_compiler_rt_and_c and build_options.is_stage1) {
926929 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
927930 if (!comp.bin_file.options.link_libc) {
928931 try comp.work_queue.writeItem(.{ .zig_libc = {} });
......@@ -2602,8 +2605,12 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
26022605
26032606 // We use an extra hex-encoded byte here to store some flags.
26042607 var prev_digest_buf: [digest.len + 2]u8 = undefined;
2605 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
2606 log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
2608 const prev_digest: []u8 = Cache.readSmallFile(
2609 directory.handle,
2610 id_symlink_basename,
2611 &prev_digest_buf,
2612 ) catch |err| blk: {
2613 log.debug("stage1 {} new_digest={} error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
26072614 // Handle this as a cache miss.
26082615 break :blk prev_digest_buf[0..0];
26092616 };
......@@ -2774,7 +2781,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
27742781
27752782 const digest = man.final();
27762783
2777 // Update the dangling symlink with the digest. If it fails we can continue; it only
2784 // Update the small file with the digest. If it fails we can continue; it only
27782785 // means that the next invocation will have an unnecessary cache miss.
27792786 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
27802787 log.debug("stage1 {} final digest={} flags={x}", .{
......@@ -2789,10 +2796,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
27892796 log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{
27902797 digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
27912798 });
2792 directory.handle.symLink(&digest_plus_flags, id_symlink_basename, .{}) catch |err| {
2793 log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});
2799 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| {
2800 log.warn("failed to save stage1 hash digest file: {}", .{@errorName(err)});
27942801 };
2795 // Again failure here only means an unnecessary cache miss.
2802 // Failure here only means an unnecessary cache miss.
27962803 man.writeManifest() catch |err| {
27972804 log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
27982805 };
src/Module.zig+12-3
......@@ -93,7 +93,7 @@ pub const Export = struct {
9393 /// Byte offset into the file that contains the export directive.
9494 src: usize,
9595 /// Represents the position of the export, if any, in the output file.
96 link: link.File.Elf.Export,
96 link: link.File.Export,
9797 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
9898 owner_decl: *Decl,
9999 /// The Decl being exported. Note this is *not* the Decl performing the export.
......@@ -1712,7 +1712,10 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
17121712 }
17131713 }
17141714 if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
1715 elf.deleteExport(exp.link);
1715 elf.deleteExport(exp.link.elf);
1716 }
1717 if (self.comp.bin_file.cast(link.File.MachO)) |macho| {
1718 macho.deleteExport(exp.link.macho);
17161719 }
17171720 if (self.failed_exports.remove(exp)) |entry| {
17181721 entry.value.destroy(self.gpa);
......@@ -1875,7 +1878,13 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n
18751878 new_export.* = .{
18761879 .options = .{ .name = symbol_name },
18771880 .src = src,
1878 .link = .{},
1881 .link = switch (self.comp.bin_file.tag) {
1882 .coff => .{ .coff = {} },
1883 .elf => .{ .elf = link.File.Elf.Export{} },
1884 .macho => .{ .macho = link.File.MachO.Export{} },
1885 .c => .{ .c = {} },
1886 .wasm => .{ .wasm = {} },
1887 },
18791888 .owner_decl = owner_decl,
18801889 .exported_decl = exported_decl,
18811890 .status = .in_progress,
src/codegen/llvm.zig+1-1
......@@ -69,7 +69,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
6969 .kfreebsd => "kfreebsd",
7070 .linux => "linux",
7171 .lv2 => "lv2",
72 .macosx => "macosx",
72 .macos => "macosx",
7373 .netbsd => "netbsd",
7474 .openbsd => "openbsd",
7575 .solaris => "solaris",
src/link.zig+16-4
......@@ -133,6 +133,14 @@ pub const File = struct {
133133 wasm: ?Wasm.FnData,
134134 };
135135
136 pub const Export = union {
137 elf: Elf.Export,
138 coff: void,
139 macho: MachO.Export,
140 c: void,
141 wasm: void,
142 };
143
136144 /// For DWARF .debug_info.
137145 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);
138146
......@@ -458,8 +466,12 @@ pub const File = struct {
458466 const digest = ch.final();
459467
460468 var prev_digest_buf: [digest.len]u8 = undefined;
461 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: {
462 log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) });
469 const prev_digest: []u8 = Cache.readSmallFile(
470 directory.handle,
471 id_symlink_basename,
472 &prev_digest_buf,
473 ) catch |err| b: {
474 log.debug("archive new_digest={} readFile error: {}", .{ digest, @errorName(err) });
463475 break :b prev_digest_buf[0..0];
464476 };
465477 if (mem.eql(u8, prev_digest, &digest)) {
......@@ -504,8 +516,8 @@ pub const File = struct {
504516 const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
505517 if (bad) return error.UnableToWriteArchive;
506518
507 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
508 std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)});
519 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
520 std.log.warn("failed to save archive hash digest file: {}", .{@errorName(err)});
509521 };
510522
511523 ch.writeManifest() catch |err| {
src/link/Coff.zig+9-5
......@@ -854,8 +854,12 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
854854 _ = try man.hit();
855855 digest = man.final();
856856 var prev_digest_buf: [digest.len]u8 = undefined;
857 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
858 log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
857 const prev_digest: []u8 = Cache.readSmallFile(
858 directory.handle,
859 id_symlink_basename,
860 &prev_digest_buf,
861 ) catch |err| blk: {
862 log.debug("COFF LLD new_digest={} error: {}", .{ digest, @errorName(err) });
859863 // Handle this as a cache miss.
860864 break :blk prev_digest_buf[0..0];
861865 };
......@@ -1180,10 +1184,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
11801184 }
11811185
11821186 if (!self.base.options.disable_lld_caching) {
1183 // Update the dangling symlink with the digest. If it fails we can continue; it only
1187 // Update the file with the digest. If it fails we can continue; it only
11841188 // means that the next invocation will have an unnecessary cache miss.
1185 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
1186 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
1189 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1190 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
11871191 };
11881192 // Again failure here only means an unnecessary cache miss.
11891193 man.writeManifest() catch |err| {
src/link/Elf.zig+11-7
......@@ -1326,8 +1326,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13261326 digest = man.final();
13271327
13281328 var prev_digest_buf: [digest.len]u8 = undefined;
1329 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
1330 log.debug("ELF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
1329 const prev_digest: []u8 = Cache.readSmallFile(
1330 directory.handle,
1331 id_symlink_basename,
1332 &prev_digest_buf,
1333 ) catch |err| blk: {
1334 log.debug("ELF LLD new_digest={} error: {}", .{ digest, @errorName(err) });
13311335 // Handle this as a cache miss.
13321336 break :blk prev_digest_buf[0..0];
13331337 };
......@@ -1647,10 +1651,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16471651 }
16481652
16491653 if (!self.base.options.disable_lld_caching) {
1650 // Update the dangling symlink with the digest. If it fails we can continue; it only
1654 // Update the file with the digest. If it fails we can continue; it only
16511655 // means that the next invocation will have an unnecessary cache miss.
1652 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
1653 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
1656 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1657 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
16541658 };
16551659 // Again failure here only means an unnecessary cache miss.
16561660 man.writeManifest() catch |err| {
......@@ -2588,7 +2592,7 @@ pub fn updateDeclExports(
25882592 },
25892593 };
25902594 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2591 if (exp.link.sym_index) |i| {
2595 if (exp.link.elf.sym_index) |i| {
25922596 const sym = &self.global_symbols.items[i];
25932597 sym.* = .{
25942598 .st_name = try self.updateString(sym.st_name, exp.options.name),
......@@ -2613,7 +2617,7 @@ pub fn updateDeclExports(
26132617 .st_size = decl_sym.st_size,
26142618 };
26152619
2616 exp.link.sym_index = @intCast(u32, i);
2620 exp.link.elf.sym_index = @intCast(u32, i);
26172621 }
26182622 }
26192623}
src/link/MachO.zig+54-27
......@@ -20,6 +20,8 @@ const File = link.File;
2020const Cache = @import("../Cache.zig");
2121const target_util = @import("../target.zig");
2222
23const Trie = @import("MachO/Trie.zig");
24
2325pub const base_tag: File.Tag = File.Tag.macho;
2426
2527const LoadCommand = union(enum) {
......@@ -113,6 +115,9 @@ local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
113115global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
114116/// Table of all undefined symbols
115117undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
118
119global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
120
116121dyld_stub_binder_index: ?u16 = null,
117122
118123/// Table of symbol names aka the string table.
......@@ -176,6 +181,10 @@ pub const TextBlock = struct {
176181 };
177182};
178183
184pub const Export = struct {
185 sym_index: ?u32 = null,
186};
187
179188pub const SrcFn = struct {
180189 pub const empty = SrcFn{};
181190};
......@@ -256,10 +265,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
256265
257266 switch (self.base.options.output_mode) {
258267 .Exe => {
259 if (self.entry_addr) |addr| {
260 // Write export trie.
261 try self.writeExportTrie();
268 // Write export trie.
269 try self.writeExportTrie();
262270
271 if (self.entry_addr) |addr| {
263272 // Update LC_MAIN with entry offset
264273 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
265274 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
......@@ -410,8 +419,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
410419 digest = man.final();
411420
412421 var prev_digest_buf: [digest.len]u8 = undefined;
413 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
414 log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
422 const prev_digest: []u8 = Cache.readSmallFile(
423 directory.handle,
424 id_symlink_basename,
425 &prev_digest_buf,
426 ) catch |err| blk: {
427 log.debug("MachO LLD new_digest={} error: {}", .{ digest, @errorName(err) });
415428 // Handle this as a cache miss.
416429 break :blk prev_digest_buf[0..0];
417430 };
......@@ -512,7 +525,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
512525 try argv.append(darwinArchString(target.cpu.arch));
513526
514527 switch (target.os.tag) {
515 .macosx => {
528 .macos => {
516529 try argv.append("-macosx_version_min");
517530 },
518531 .ios, .tvos, .watchos => switch (target.cpu.arch) {
......@@ -665,10 +678,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
665678 }
666679
667680 if (!self.base.options.disable_lld_caching) {
668 // Update the dangling symlink with the digest. If it fails we can continue; it only
681 // Update the file with the digest. If it fails we can continue; it only
669682 // means that the next invocation will have an unnecessary cache miss.
670 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
671 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
683 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
684 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
672685 };
673686 // Again failure here only means an unnecessary cache miss.
674687 man.writeManifest() catch |err| {
......@@ -711,6 +724,7 @@ pub fn deinit(self: *MachO) void {
711724 self.string_table.deinit(self.base.allocator);
712725 self.undef_symbols.deinit(self.base.allocator);
713726 self.global_symbols.deinit(self.base.allocator);
727 self.global_symbol_free_list.deinit(self.base.allocator);
714728 self.local_symbols.deinit(self.base.allocator);
715729 self.sections.deinit(self.base.allocator);
716730 self.load_commands.deinit(self.base.allocator);
......@@ -835,7 +849,7 @@ pub fn updateDeclExports(
835849 },
836850 };
837851 const n_type = decl_sym.n_type | macho.N_EXT;
838 if (exp.link.sym_index) |i| {
852 if (exp.link.macho.sym_index) |i| {
839853 const sym = &self.global_symbols.items[i];
840854 sym.* = .{
841855 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
......@@ -846,8 +860,10 @@ pub fn updateDeclExports(
846860 };
847861 } else {
848862 const name_str_index = try self.makeString(exp.options.name);
849 _ = self.global_symbols.addOneAssumeCapacity();
850 const i = self.global_symbols.items.len - 1;
863 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
864 _ = self.global_symbols.addOneAssumeCapacity();
865 break :blk self.global_symbols.items.len - 1;
866 };
851867 self.global_symbols.items[i] = .{
852868 .n_strx = name_str_index,
853869 .n_type = n_type,
......@@ -856,11 +872,17 @@ pub fn updateDeclExports(
856872 .n_value = decl_sym.n_value,
857873 };
858874
859 exp.link.sym_index = @intCast(u32, i);
875 exp.link.macho.sym_index = @intCast(u32, i);
860876 }
861877 }
862878}
863879
880pub fn deleteExport(self: *MachO, exp: Export) void {
881 const sym_index = exp.sym_index orelse return;
882 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
883 self.global_symbols.items[sym_index].n_type = 0;
884}
885
864886pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
865887
866888pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
......@@ -1383,25 +1405,30 @@ fn writeAllUndefSymbols(self: *MachO) !void {
13831405}
13841406
13851407fn writeExportTrie(self: *MachO) !void {
1386 assert(self.entry_addr != null);
1408 if (self.global_symbols.items.len == 0) return; // No exports, nothing to do.
13871409
1388 // TODO implement mechanism for generating a prefix tree of the exported symbols
1389 // single branch export trie
1390 var buf = [_]u8{0} ** 24;
1391 buf[0] = 0; // root node
1392 buf[1] = 1; // 1 branch from root
1393 mem.copy(u8, buf[2..], "_start");
1394 buf[8] = 0;
1395 buf[9] = 9 + 1;
1410 var trie: Trie = .{};
1411 defer trie.deinit(self.base.allocator);
13961412
13971413 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1398 const addr = self.entry_addr.? - text_segment.vmaddr;
1399 const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);
1400 buf[10] = @intCast(u8, written) + 1;
1401 buf[11] = 0;
1414 for (self.global_symbols.items) |symbol| {
1415 // TODO figure out if we should put all global symbols into the export trie
1416 const name = self.getString(symbol.n_strx);
1417 assert(symbol.n_value >= text_segment.vmaddr);
1418 try trie.put(self.base.allocator, .{
1419 .name = name,
1420 .vmaddr_offset = symbol.n_value - text_segment.vmaddr,
1421 .export_flags = 0, // TODO workout creation of export flags
1422 });
1423 }
1424
1425 var buffer: std.ArrayListUnmanaged(u8) = .{};
1426 defer buffer.deinit(self.base.allocator);
1427
1428 try trie.writeULEB128Mem(self.base.allocator, &buffer);
14021429
14031430 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1404 try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off);
1431 try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off);
14051432}
14061433
14071434fn writeStringTable(self: *MachO) !void {
src/link/MachO/Trie.zig created+436
......@@ -0,0 +1,436 @@
1//! Represents export trie used in MachO executables and dynamic libraries.
2//! The purpose of an export trie is to encode as compactly as possible all
3//! export symbols for the loader `dyld`.
4//! The export trie encodes offset and other information using ULEB128
5//! encoding, and is part of the __LINKEDIT segment.
6//!
7//! Description from loader.h:
8//!
9//! The symbols exported by a dylib are encoded in a trie. This is a compact
10//! representation that factors out common prefixes. It also reduces LINKEDIT pages
11//! in RAM because it encodes all information (name, address, flags) in one small,
12//! contiguous range. The export area is a stream of nodes. The first node sequentially
13//! is the start node for the trie.
14//!
15//! Nodes for a symbol start with a uleb128 that is the length of the exported symbol
16//! information for the string so far. If there is no exported symbol, the node starts
17//! with a zero byte. If there is exported info, it follows the length.
18//!
19//! First is a uleb128 containing flags. Normally, it is followed by a uleb128 encoded
20//! offset which is location of the content named by the symbol from the mach_header
21//! for the image. If the flags is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags
22//! is a uleb128 encoded library ordinal, then a zero terminated UTF8 string. If the string
23//! is zero length, then the symbol is re-export from the specified dylib with the same name.
24//! If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following the flags is two
25//! uleb128s: the stub offset and the resolver offset. The stub is used by non-lazy pointers.
26//! The resolver is used by lazy pointers and must be called to get the actual address to use.
27//!
28//! After the optional exported symbol information is a byte of how many edges (0-255) that
29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
30//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
31const Trie = @This();
32
33const std = @import("std");
34const mem = std.mem;
35const leb = std.debug.leb;
36const log = std.log.scoped(.link);
37const testing = std.testing;
38const assert = std.debug.assert;
39const Allocator = mem.Allocator;
40
41pub const Symbol = struct {
42 name: []const u8,
43 vmaddr_offset: u64,
44 export_flags: u64,
45};
46
47const Edge = struct {
48 from: *Node,
49 to: *Node,
50 label: []const u8,
51
52 fn deinit(self: *Edge, alloc: *Allocator) void {
53 self.to.deinit(alloc);
54 alloc.destroy(self.to);
55 self.from = undefined;
56 self.to = undefined;
57 }
58};
59
60const Node = struct {
61 /// Export flags associated with this exported symbol (if any).
62 export_flags: ?u64 = null,
63 /// VM address offset wrt to the section this symbol is defined against (if any).
64 vmaddr_offset: ?u64 = null,
65 /// Offset of this node in the trie output byte stream.
66 trie_offset: ?usize = null,
67 /// List of all edges originating from this node.
68 edges: std.ArrayListUnmanaged(Edge) = .{},
69
70 fn deinit(self: *Node, alloc: *Allocator) void {
71 for (self.edges.items) |*edge| {
72 edge.deinit(alloc);
73 }
74 self.edges.deinit(alloc);
75 }
76
77 const PutResult = struct {
78 /// Node reached at this stage of `put` op.
79 node: *Node,
80 /// Count of newly inserted nodes at this stage of `put` op.
81 node_count: usize,
82 };
83
84 /// Inserts a new node starting from `self`.
85 fn put(self: *Node, alloc: *Allocator, label: []const u8, node_count: usize) !PutResult {
86 var curr_node_count = node_count;
87 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return PutResult{
90 .node = edge.to,
91 .node_count = curr_node_count,
92 };
93 if (match == 0) continue;
94 if (match == edge.label.len) return edge.to.put(alloc, label[match..], curr_node_count);
95
96 // Found a match, need to splice up nodes.
97 // From: A -> B
98 // To: A -> C -> B
99 const mid = try alloc.create(Node);
100 mid.* = .{};
101 const to_label = edge.label;
102 const to_node = edge.to;
103 edge.to = mid;
104 edge.label = label[0..match];
105 curr_node_count += 1;
106
107 try mid.edges.append(alloc, .{
108 .from = mid,
109 .to = to_node,
110 .label = to_label[match..],
111 });
112
113 if (match == label.len) {
114 return PutResult{ .node = to_node, .node_count = curr_node_count };
115 } else {
116 return mid.put(alloc, label[match..], curr_node_count);
117 }
118 }
119
120 // Add a new node.
121 const node = try alloc.create(Node);
122 node.* = .{};
123 curr_node_count += 1;
124
125 try self.edges.append(alloc, .{
126 .from = self,
127 .to = node,
128 .label = label,
129 });
130
131 return PutResult{ .node = node, .node_count = curr_node_count };
132 }
133
134 /// This method should only be called *after* updateOffset has been called!
135 /// In case this is not upheld, this method will panic.
136 fn writeULEB128Mem(self: Node, buffer: *std.ArrayListUnmanaged(u8)) !void {
137 assert(self.trie_offset != null); // You need to call updateOffset first.
138 if (self.vmaddr_offset) |offset| {
139 // Terminal node info: encode export flags and vmaddr offset of this symbol.
140 var info_buf_len: usize = 0;
141 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
142 info_buf_len += try leb.writeULEB128Mem(info_buf[0..], self.export_flags.?);
143 info_buf_len += try leb.writeULEB128Mem(info_buf[info_buf_len..], offset);
144
145 // Encode the size of the terminal node info.
146 var size_buf: [@sizeOf(u64)]u8 = undefined;
147 const size_buf_len = try leb.writeULEB128Mem(size_buf[0..], info_buf_len);
148
149 // Now, write them to the output buffer.
150 buffer.appendSliceAssumeCapacity(size_buf[0..size_buf_len]);
151 buffer.appendSliceAssumeCapacity(info_buf[0..info_buf_len]);
152 } else {
153 // Non-terminal node is delimited by 0 byte.
154 buffer.appendAssumeCapacity(0);
155 }
156 // Write number of edges (max legal number of edges is 256).
157 buffer.appendAssumeCapacity(@intCast(u8, self.edges.items.len));
158
159 for (self.edges.items) |edge| {
160 // Write edges labels.
161 buffer.appendSliceAssumeCapacity(edge.label);
162 buffer.appendAssumeCapacity(0);
163
164 var buf: [@sizeOf(u64)]u8 = undefined;
165 const buf_len = try leb.writeULEB128Mem(buf[0..], edge.to.trie_offset.?);
166 buffer.appendSliceAssumeCapacity(buf[0..buf_len]);
167 }
168 }
169
170 const UpdateResult = struct {
171 /// Current size of this node in bytes.
172 node_size: usize,
173 /// True if the trie offset of this node in the output byte stream
174 /// would need updating; false otherwise.
175 updated: bool,
176 };
177
178 /// Updates offset of this node in the output byte stream.
179 fn updateOffset(self: *Node, offset: usize) UpdateResult {
180 var node_size: usize = 0;
181 if (self.vmaddr_offset) |vmaddr| {
182 node_size += sizeULEB128Mem(self.export_flags.?);
183 node_size += sizeULEB128Mem(vmaddr);
184 node_size += sizeULEB128Mem(node_size);
185 } else {
186 node_size += 1; // 0x0 for non-terminal nodes
187 }
188 node_size += 1; // 1 byte for edge count
189
190 for (self.edges.items) |edge| {
191 const next_node_offset = edge.to.trie_offset orelse 0;
192 node_size += edge.label.len + 1 + sizeULEB128Mem(next_node_offset);
193 }
194
195 const trie_offset = self.trie_offset orelse 0;
196 const updated = offset != trie_offset;
197 self.trie_offset = offset;
198
199 return .{ .node_size = node_size, .updated = updated };
200 }
201
202 /// Calculates number of bytes in ULEB128 encoding of value.
203 fn sizeULEB128Mem(value: u64) usize {
204 var res: usize = 0;
205 var v = value;
206 while (true) {
207 v = v >> 7;
208 res += 1;
209 if (v == 0) break;
210 }
211 return res;
212 }
213};
214
215/// Count of nodes in the trie.
216/// The count is updated at every `put` call.
217/// The trie always consists of at least a root node, hence
218/// the count always starts at 1.
219node_count: usize = 1,
220/// The root node of the trie.
221root: Node = .{},
222
223/// Insert a symbol into the trie, updating the prefixes in the process.
224/// This operation may change the layout of the trie by splicing edges in
225/// certain circumstances.
226pub fn put(self: *Trie, alloc: *Allocator, symbol: Symbol) !void {
227 const res = try self.root.put(alloc, symbol.name, 0);
228 self.node_count += res.node_count;
229 res.node.vmaddr_offset = symbol.vmaddr_offset;
230 res.node.export_flags = symbol.export_flags;
231}
232
233/// Write the trie to a buffer ULEB128 encoded.
234pub fn writeULEB128Mem(self: *Trie, alloc: *Allocator, buffer: *std.ArrayListUnmanaged(u8)) !void {
235 var ordered_nodes: std.ArrayListUnmanaged(*Node) = .{};
236 defer ordered_nodes.deinit(alloc);
237
238 try ordered_nodes.ensureCapacity(alloc, self.node_count);
239 walkInOrder(&self.root, &ordered_nodes);
240
241 var offset: usize = 0;
242 var more: bool = true;
243 while (more) {
244 offset = 0;
245 more = false;
246 for (ordered_nodes.items) |node| {
247 const res = node.updateOffset(offset);
248 offset += res.node_size;
249 if (res.updated) more = true;
250 }
251 }
252
253 try buffer.ensureCapacity(alloc, buffer.items.len + offset);
254 for (ordered_nodes.items) |node| {
255 try node.writeULEB128Mem(buffer);
256 }
257}
258
259/// Walks the trie in DFS order gathering all nodes into a linear stream of nodes.
260fn walkInOrder(node: *Node, list: *std.ArrayListUnmanaged(*Node)) void {
261 list.appendAssumeCapacity(node);
262 for (node.edges.items) |*edge| {
263 walkInOrder(edge.to, list);
264 }
265}
266
267pub fn deinit(self: *Trie, alloc: *Allocator) void {
268 self.root.deinit(alloc);
269}
270
271test "Trie node count" {
272 var gpa = testing.allocator;
273 var trie: Trie = .{};
274 defer trie.deinit(gpa);
275
276 testing.expectEqual(trie.node_count, 1);
277
278 try trie.put(gpa, .{
279 .name = "_main",
280 .vmaddr_offset = 0,
281 .export_flags = 0,
282 });
283 testing.expectEqual(trie.node_count, 2);
284
285 // Inserting the same node shouldn't update the trie.
286 try trie.put(gpa, .{
287 .name = "_main",
288 .vmaddr_offset = 0,
289 .export_flags = 0,
290 });
291 testing.expectEqual(trie.node_count, 2);
292
293 try trie.put(gpa, .{
294 .name = "__mh_execute_header",
295 .vmaddr_offset = 0x1000,
296 .export_flags = 0,
297 });
298 testing.expectEqual(trie.node_count, 4);
299
300 // Inserting the same node shouldn't update the trie.
301 try trie.put(gpa, .{
302 .name = "__mh_execute_header",
303 .vmaddr_offset = 0x1000,
304 .export_flags = 0,
305 });
306 testing.expectEqual(trie.node_count, 4);
307 try trie.put(gpa, .{
308 .name = "_main",
309 .vmaddr_offset = 0,
310 .export_flags = 0,
311 });
312 testing.expectEqual(trie.node_count, 4);
313}
314
315test "Trie basic" {
316 var gpa = testing.allocator;
317 var trie: Trie = .{};
318 defer trie.deinit(gpa);
319
320 // root
321 testing.expect(trie.root.edges.items.len == 0);
322
323 // root --- _st ---> node
324 try trie.put(gpa, .{
325 .name = "_st",
326 .vmaddr_offset = 0,
327 .export_flags = 0,
328 });
329 testing.expect(trie.root.edges.items.len == 1);
330 testing.expect(mem.eql(u8, trie.root.edges.items[0].label, "_st"));
331
332 {
333 // root --- _st ---> node --- art ---> node
334 try trie.put(gpa, .{
335 .name = "_start",
336 .vmaddr_offset = 0,
337 .export_flags = 0,
338 });
339 testing.expect(trie.root.edges.items.len == 1);
340
341 const nextEdge = &trie.root.edges.items[0];
342 testing.expect(mem.eql(u8, nextEdge.label, "_st"));
343 testing.expect(nextEdge.to.edges.items.len == 1);
344 testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art"));
345 }
346 {
347 // root --- _ ---> node --- st ---> node --- art ---> node
348 // |
349 // | --- main ---> node
350 try trie.put(gpa, .{
351 .name = "_main",
352 .vmaddr_offset = 0,
353 .export_flags = 0,
354 });
355 testing.expect(trie.root.edges.items.len == 1);
356
357 const nextEdge = &trie.root.edges.items[0];
358 testing.expect(mem.eql(u8, nextEdge.label, "_"));
359 testing.expect(nextEdge.to.edges.items.len == 2);
360 testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st"));
361 testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main"));
362
363 const nextNextEdge = &nextEdge.to.edges.items[0];
364 testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art"));
365 }
366}
367
368test "Trie.writeULEB128Mem" {
369 var gpa = testing.allocator;
370 var trie: Trie = .{};
371 defer trie.deinit(gpa);
372
373 try trie.put(gpa, .{
374 .name = "__mh_execute_header",
375 .vmaddr_offset = 0,
376 .export_flags = 0,
377 });
378 try trie.put(gpa, .{
379 .name = "_main",
380 .vmaddr_offset = 0x1000,
381 .export_flags = 0,
382 });
383
384 var buffer: std.ArrayListUnmanaged(u8) = .{};
385 defer buffer.deinit(gpa);
386
387 try trie.writeULEB128Mem(gpa, &buffer);
388
389 const exp_buffer = [_]u8{
390 0x0,
391 0x1,
392 0x5f,
393 0x0,
394 0x5,
395 0x0,
396 0x2,
397 0x5f,
398 0x6d,
399 0x68,
400 0x5f,
401 0x65,
402 0x78,
403 0x65,
404 0x63,
405 0x75,
406 0x74,
407 0x65,
408 0x5f,
409 0x68,
410 0x65,
411 0x61,
412 0x64,
413 0x65,
414 0x72,
415 0x0,
416 0x21,
417 0x6d,
418 0x61,
419 0x69,
420 0x6e,
421 0x0,
422 0x25,
423 0x2,
424 0x0,
425 0x0,
426 0x0,
427 0x3,
428 0x0,
429 0x80,
430 0x20,
431 0x0,
432 };
433
434 testing.expect(buffer.items.len == exp_buffer.len);
435 testing.expect(mem.eql(u8, buffer.items, exp_buffer[0..]));
436}
src/link/Wasm.zig+9-5
......@@ -310,8 +310,12 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
310310 digest = man.final();
311311
312312 var prev_digest_buf: [digest.len]u8 = undefined;
313 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
314 log.debug("WASM LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
313 const prev_digest: []u8 = Cache.readSmallFile(
314 directory.handle,
315 id_symlink_basename,
316 &prev_digest_buf,
317 ) catch |err| blk: {
318 log.debug("WASM LLD new_digest={} error: {}", .{ digest, @errorName(err) });
315319 // Handle this as a cache miss.
316320 break :blk prev_digest_buf[0..0];
317321 };
......@@ -374,7 +378,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
374378 try argv.append(p);
375379 }
376380
377 if (self.base.options.output_mode == .Exe and !self.base.options.is_compiler_rt_or_libc) {
381 if (self.base.options.output_mode != .Obj and !self.base.options.is_compiler_rt_or_libc) {
378382 if (!self.base.options.link_libc) {
379383 try argv.append(comp.libc_static_lib.?.full_object_path);
380384 }
......@@ -424,9 +428,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
424428 }
425429
426430 if (!self.base.options.disable_lld_caching) {
427 // Update the dangling symlink with the digest. If it fails we can continue; it only
431 // Update the file with the digest. If it fails we can continue; it only
428432 // means that the next invocation will have an unnecessary cache miss.
429 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
433 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
430434 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
431435 };
432436 // Again failure here only means an unnecessary cache miss.
src/target.zig+3-3
......@@ -123,14 +123,14 @@ pub fn cannotDynamicLink(target: std.Target) bool {
123123/// since this is the stable syscall interface.
124124pub fn osRequiresLibC(target: std.Target) bool {
125125 return switch (target.os.tag) {
126 .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true,
126 .freebsd, .netbsd, .dragonfly, .macos, .ios, .watchos, .tvos => true,
127127 else => false,
128128 };
129129}
130130
131131pub fn libcNeedsLibUnwind(target: std.Target) bool {
132132 return switch (target.os.tag) {
133 .macosx,
133 .macos,
134134 .ios,
135135 .watchos,
136136 .tvos,
......@@ -197,7 +197,7 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
197197 .kfreebsd => .KFreeBSD,
198198 .linux => .Linux,
199199 .lv2 => .Lv2,
200 .macosx => .MacOSX,
200 .macos => .MacOSX,
201201 .netbsd => .NetBSD,
202202 .openbsd => .OpenBSD,
203203 .solaris => .Solaris,
src/type.zig+1-1
......@@ -3104,7 +3104,7 @@ pub const CType = enum {
31043104 },
31053105
31063106 .linux,
3107 .macosx,
3107 .macos,
31083108 .freebsd,
31093109 .netbsd,
31103110 .dragonfly,
src/zig_llvm.cpp+3-1
......@@ -820,7 +820,9 @@ const char *ZigLLVMGetVendorTypeName(ZigLLVM_VendorType vendor) {
820820}
821821
822822const char *ZigLLVMGetOSTypeName(ZigLLVM_OSType os) {
823 return (const char*)Triple::getOSTypeName((Triple::OSType)os).bytes_begin();
823 const char* name = (const char*)Triple::getOSTypeName((Triple::OSType)os).bytes_begin();
824 if (strcmp(name, "macosx") == 0) return "macos";
825 return name;
824826}
825827
826828const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type) {
test/stack_traces.zig+1-1
......@@ -308,7 +308,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
308308 },
309309 );
310310 },
311 .macosx => {
311 .macos => {
312312 cases.addCase(
313313 "return",
314314 source_return,
test/stage2/test.zig+1-1
......@@ -13,7 +13,7 @@ const linux_x64 = std.zig.CrossTarget{
1313
1414const macosx_x64 = std.zig.CrossTarget{
1515 .cpu_arch = .x86_64,
16 .os_tag = .macosx,
16 .os_tag = .macos,
1717};
1818
1919const linux_riscv64 = std.zig.CrossTarget{
test/tests.zig+1-1
......@@ -234,7 +234,7 @@ const test_targets = blk: {
234234 TestTarget{
235235 .target = .{
236236 .cpu_arch = .x86_64,
237 .os_tag = .macosx,
237 .os_tag = .macos,
238238 .abi = .gnu,
239239 },
240240 // https://github.com/ziglang/zig/issues/3295