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...@@ -7,8 +7,10 @@ A general-purpose programming language and toolchain for maintaining
77
8 * [Introduction](https://ziglang.org/#Introduction)8 * [Introduction](https://ziglang.org/#Introduction)
9 * [Download & Documentation](https://ziglang.org/download)9 * [Download & Documentation](https://ziglang.org/download)
10 * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/)
10 * [Community](https://github.com/ziglang/zig/wiki/Community)11 * [Community](https://github.com/ziglang/zig/wiki/Community)
11 * [Contributing](https://github.com/ziglang/zig/blob/master/CONTRIBUTING.md)12 * [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)
12 * [Frequently Asked Questions](https://github.com/ziglang/zig/wiki/FAQ)14 * [Frequently Asked Questions](https://github.com/ziglang/zig/wiki/FAQ)
13 * [Community Projects](https://github.com/ziglang/zig/wiki/Community-Projects)15 * [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");...@@ -9905,7 +9905,10 @@ const std = @import("std");
9905const PreopenList = std.fs.wasi.PreopenList;9905const PreopenList = std.fs.wasi.PreopenList;
99069906
9907pub fn main() !void {9907pub 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);
9909 defer preopens.deinit();9912 defer preopens.deinit();
99109913
9911 try preopens.populate();9914 try preopens.populate();
lib/std/c.zig+4-4
...@@ -22,7 +22,7 @@ pub usingnamespace @import("os/bits.zig");...@@ -22,7 +22,7 @@ pub usingnamespace @import("os/bits.zig");
22pub usingnamespace switch (std.Target.current.os.tag) {22pub usingnamespace switch (std.Target.current.os.tag) {
23 .linux => @import("c/linux.zig"),23 .linux => @import("c/linux.zig"),
24 .windows => @import("c/windows.zig"),24 .windows => @import("c/windows.zig"),
25 .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"),25 .macos, .ios, .tvos, .watchos => @import("c/darwin.zig"),
26 .freebsd, .kfreebsd => @import("c/freebsd.zig"),26 .freebsd, .kfreebsd => @import("c/freebsd.zig"),
27 .netbsd => @import("c/netbsd.zig"),27 .netbsd => @import("c/netbsd.zig"),
28 .dragonfly => @import("c/dragonfly.zig"),28 .dragonfly => @import("c/dragonfly.zig"),
...@@ -122,7 +122,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs...@@ -122,7 +122,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs
122pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;122pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
123123
124pub usingnamespace switch (builtin.os.tag) {124pub usingnamespace switch (builtin.os.tag) {
125 .macosx, .ios, .watchos, .tvos => struct {125 .macos, .ios, .watchos, .tvos => struct {
126 pub const realpath = @"realpath$DARWIN_EXTSN";126 pub const realpath = @"realpath$DARWIN_EXTSN";
127 pub const fstatat = @"fstatat$INODE64";127 pub const fstatat = @"fstatat$INODE64";
128 },128 },
...@@ -189,7 +189,7 @@ pub usingnamespace switch (builtin.os.tag) {...@@ -189,7 +189,7 @@ pub usingnamespace switch (builtin.os.tag) {
189 pub const sigprocmask = __sigprocmask14;189 pub const sigprocmask = __sigprocmask14;
190 pub const stat = __stat50;190 pub const stat = __stat50;
191 },191 },
192 .macosx, .ios, .watchos, .tvos => struct {192 .macos, .ios, .watchos, .tvos => struct {
193 // XXX: close -> close$NOCANCEL193 // XXX: close -> close$NOCANCEL
194 // XXX: getdirentries -> _getdirentries64194 // XXX: getdirentries -> _getdirentries64
195 pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;195 pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int;
...@@ -252,7 +252,7 @@ pub usingnamespace switch (builtin.os.tag) {...@@ -252,7 +252,7 @@ pub usingnamespace switch (builtin.os.tag) {
252 .linux, .freebsd, .kfreebsd, .netbsd, .openbsd => struct {252 .linux, .freebsd, .kfreebsd, .netbsd, .openbsd => struct {
253 pub extern "c" fn malloc_usable_size(?*const c_void) usize;253 pub extern "c" fn malloc_usable_size(?*const c_void) usize;
254 },254 },
255 .macosx, .ios, .watchos, .tvos => struct {255 .macos, .ios, .watchos, .tvos => struct {
256 pub extern "c" fn malloc_size(?*const c_void) usize;256 pub extern "c" fn malloc_size(?*const c_void) usize;
257 },257 },
258 else => struct {},258 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;...@@ -18,6 +18,14 @@ pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
18pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;18pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;
19pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8;19pub 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
21pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;29pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
2230
23pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;31pub 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 {...@@ -654,7 +654,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
654 .freebsd,654 .freebsd,
655 .netbsd,655 .netbsd,
656 .dragonfly,656 .dragonfly,
657 .macosx,657 .macos,
658 .windows,658 .windows,
659 => return DebugInfo.init(allocator),659 => return DebugInfo.init(allocator),
660 else => @compileError("openSelfDebugInfo unsupported for this platform"),660 else => @compileError("openSelfDebugInfo unsupported for this platform"),
...@@ -1320,7 +1320,7 @@ const SymbolInfo = struct {...@@ -1320,7 +1320,7 @@ const SymbolInfo = struct {
1320};1320};
13211321
1322pub const ModuleDebugInfo = switch (builtin.os.tag) {1322pub const ModuleDebugInfo = switch (builtin.os.tag) {
1323 .macosx, .ios, .watchos, .tvos => struct {1323 .macos, .ios, .watchos, .tvos => struct {
1324 base_address: usize,1324 base_address: usize,
1325 mapped_memory: []const u8,1325 mapped_memory: []const u8,
1326 symbols: []const MachoSymbol,1326 symbols: []const MachoSymbol,
lib/std/dynamic_library.zig+2-2
...@@ -19,7 +19,7 @@ const max = std.math.max;...@@ -19,7 +19,7 @@ const max = std.math.max;
19pub const DynLib = switch (builtin.os.tag) {19pub const DynLib = switch (builtin.os.tag) {
20 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,20 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
21 .windows => WindowsDynLib,21 .windows => WindowsDynLib,
22 .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib,22 .macos, .tvos, .watchos, .ios, .freebsd => DlDynlib,
23 else => void,23 else => void,
24};24};
2525
...@@ -404,7 +404,7 @@ test "dynamic_library" {...@@ -404,7 +404,7 @@ test "dynamic_library" {
404 const libname = switch (builtin.os.tag) {404 const libname = switch (builtin.os.tag) {
405 .linux, .freebsd => "invalid_so.so",405 .linux, .freebsd => "invalid_so.so",
406 .windows => "invalid_dll.dll",406 .windows => "invalid_dll.dll",
407 .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",407 .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib",
408 else => return error.SkipZigTest,408 else => return error.SkipZigTest,
409 };409 };
410410
lib/std/event/loop.zig+12-12
...@@ -66,7 +66,7 @@ pub const Loop = struct {...@@ -66,7 +66,7 @@ pub const Loop = struct {
66 };66 };
6767
68 pub const EventFd = switch (builtin.os.tag) {68 pub const EventFd = switch (builtin.os.tag) {
69 .macosx, .freebsd, .netbsd, .dragonfly => KEventFd,69 .macos, .freebsd, .netbsd, .dragonfly => KEventFd,
70 .linux => struct {70 .linux => struct {
71 base: ResumeNode,71 base: ResumeNode,
72 epoll_op: u32,72 epoll_op: u32,
...@@ -85,7 +85,7 @@ pub const Loop = struct {...@@ -85,7 +85,7 @@ pub const Loop = struct {
85 };85 };
8686
87 pub const Basic = switch (builtin.os.tag) {87 pub const Basic = switch (builtin.os.tag) {
88 .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic,88 .macos, .freebsd, .netbsd, .dragonfly => KEventBasic,
89 .linux => struct {89 .linux => struct {
90 base: ResumeNode,90 base: ResumeNode,
91 },91 },
...@@ -259,7 +259,7 @@ pub const Loop = struct {...@@ -259,7 +259,7 @@ pub const Loop = struct {
259 self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun);259 self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun);
260 }260 }
261 },261 },
262 .macosx, .freebsd, .netbsd, .dragonfly => {262 .macos, .freebsd, .netbsd, .dragonfly => {
263 self.os_data.kqfd = try os.kqueue();263 self.os_data.kqfd = try os.kqueue();
264 errdefer os.close(self.os_data.kqfd);264 errdefer os.close(self.os_data.kqfd);
265265
...@@ -384,7 +384,7 @@ pub const Loop = struct {...@@ -384,7 +384,7 @@ pub const Loop = struct {
384 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);384 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
385 os.close(self.os_data.epollfd);385 os.close(self.os_data.epollfd);
386 },386 },
387 .macosx, .freebsd, .netbsd, .dragonfly => {387 .macos, .freebsd, .netbsd, .dragonfly => {
388 os.close(self.os_data.kqfd);388 os.close(self.os_data.kqfd);
389 },389 },
390 .windows => {390 .windows => {
...@@ -478,7 +478,7 @@ pub const Loop = struct {...@@ -478,7 +478,7 @@ pub const Loop = struct {
478 .linux => {478 .linux => {
479 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN);479 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN);
480 },480 },
481 .macosx, .freebsd, .netbsd, .dragonfly => {481 .macos, .freebsd, .netbsd, .dragonfly => {
482 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);482 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);
483 },483 },
484 else => @compileError("Unsupported OS"),484 else => @compileError("Unsupported OS"),
...@@ -490,7 +490,7 @@ pub const Loop = struct {...@@ -490,7 +490,7 @@ pub const Loop = struct {
490 .linux => {490 .linux => {
491 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT);491 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT);
492 },492 },
493 .macosx, .freebsd, .netbsd, .dragonfly => {493 .macos, .freebsd, .netbsd, .dragonfly => {
494 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);494 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);
495 },495 },
496 else => @compileError("Unsupported OS"),496 else => @compileError("Unsupported OS"),
...@@ -502,7 +502,7 @@ pub const Loop = struct {...@@ -502,7 +502,7 @@ pub const Loop = struct {
502 .linux => {502 .linux => {
503 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN);503 self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN);
504 },504 },
505 .macosx, .freebsd, .netbsd, .dragonfly => {505 .macos, .freebsd, .netbsd, .dragonfly => {
506 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);506 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_READ, os.EV_ONESHOT);
507 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);507 self.bsdWaitKev(@intCast(usize, fd), os.EVFILT_WRITE, os.EV_ONESHOT);
508 },508 },
...@@ -571,7 +571,7 @@ pub const Loop = struct {...@@ -571,7 +571,7 @@ pub const Loop = struct {
571 const eventfd_node = &resume_stack_node.data;571 const eventfd_node = &resume_stack_node.data;
572 eventfd_node.base.handle = next_tick_node.data;572 eventfd_node.base.handle = next_tick_node.data;
573 switch (builtin.os.tag) {573 switch (builtin.os.tag) {
574 .macosx, .freebsd, .netbsd, .dragonfly => {574 .macos, .freebsd, .netbsd, .dragonfly => {
575 const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent);575 const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent);
576 const empty_kevs = &[0]os.Kevent{};576 const empty_kevs = &[0]os.Kevent{};
577 _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {577 _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
...@@ -633,7 +633,7 @@ pub const Loop = struct {...@@ -633,7 +633,7 @@ pub const Loop = struct {
633 if (!builtin.single_threaded) {633 if (!builtin.single_threaded) {
634 switch (builtin.os.tag) {634 switch (builtin.os.tag) {
635 .linux,635 .linux,
636 .macosx,636 .macos,
637 .freebsd,637 .freebsd,
638 .netbsd,638 .netbsd,
639 .dragonfly,639 .dragonfly,
...@@ -725,7 +725,7 @@ pub const Loop = struct {...@@ -725,7 +725,7 @@ pub const Loop = struct {
725 }725 }
726 return;726 return;
727 },727 },
728 .macosx, .freebsd, .netbsd, .dragonfly => {728 .macos, .freebsd, .netbsd, .dragonfly => {
729 const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent);729 const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent);
730 const empty_kevs = &[0]os.Kevent{};730 const empty_kevs = &[0]os.Kevent{};
731 // cannot fail because we already added it and this just enables it731 // cannot fail because we already added it and this just enables it
...@@ -1218,7 +1218,7 @@ pub const Loop = struct {...@@ -1218,7 +1218,7 @@ pub const Loop = struct {
1218 }1218 }
1219 }1219 }
1220 },1220 },
1221 .macosx, .freebsd, .netbsd, .dragonfly => {1221 .macos, .freebsd, .netbsd, .dragonfly => {
1222 var eventlist: [1]os.Kevent = undefined;1222 var eventlist: [1]os.Kevent = undefined;
1223 const empty_kevs = &[0]os.Kevent{};1223 const empty_kevs = &[0]os.Kevent{};
1224 const count = os.kevent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable;1224 const count = os.kevent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable;
...@@ -1344,7 +1344,7 @@ pub const Loop = struct {...@@ -1344,7 +1344,7 @@ pub const Loop = struct {
13441344
1345 const OsData = switch (builtin.os.tag) {1345 const OsData = switch (builtin.os.tag) {
1346 .linux => LinuxOsData,1346 .linux => LinuxOsData,
1347 .macosx, .freebsd, .netbsd, .dragonfly => KEventData,1347 .macos, .freebsd, .netbsd, .dragonfly => KEventData,
1348 .windows => struct {1348 .windows => struct {
1349 io_port: windows.HANDLE,1349 io_port: windows.HANDLE,
1350 extra_thread_count: usize,1350 extra_thread_count: usize,
lib/std/fs.zig+66-6
...@@ -39,7 +39,7 @@ pub const Watch = @import("fs/watch.zig").Watch;...@@ -39,7 +39,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
39/// fit into a UTF-8 encoded array of this length.39/// fit into a UTF-8 encoded array of this length.
40/// The byte count includes room for a null sentinel byte.40/// The byte count includes room for a null sentinel byte.
41pub const MAX_PATH_BYTES = switch (builtin.os.tag) {41pub 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,
43 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.43 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
44 // If it would require 4 UTF-8 bytes, then there would be a surrogate44 // If it would require 4 UTF-8 bytes, then there would be a surrogate
45 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.45 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
...@@ -303,7 +303,7 @@ pub const Dir = struct {...@@ -303,7 +303,7 @@ pub const Dir = struct {
303 const IteratorError = error{AccessDenied} || os.UnexpectedError;303 const IteratorError = error{AccessDenied} || os.UnexpectedError;
304304
305 pub const Iterator = switch (builtin.os.tag) {305 pub const Iterator = switch (builtin.os.tag) {
306 .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct {306 .macos, .ios, .freebsd, .netbsd, .dragonfly => struct {
307 dir: Dir,307 dir: Dir,
308 seek: i64,308 seek: i64,
309 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),309 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
...@@ -318,7 +318,7 @@ pub const Dir = struct {...@@ -318,7 +318,7 @@ pub const Dir = struct {
318 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.318 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
319 pub fn next(self: *Self) Error!?Entry {319 pub fn next(self: *Self) Error!?Entry {
320 switch (builtin.os.tag) {320 switch (builtin.os.tag) {
321 .macosx, .ios => return self.nextDarwin(),321 .macos, .ios => return self.nextDarwin(),
322 .freebsd, .netbsd, .dragonfly => return self.nextBsd(),322 .freebsd, .netbsd, .dragonfly => return self.nextBsd(),
323 else => @compileError("unimplemented"),323 else => @compileError("unimplemented"),
324 }324 }
...@@ -615,7 +615,7 @@ pub const Dir = struct {...@@ -615,7 +615,7 @@ pub const Dir = struct {
615615
616 pub fn iterate(self: Dir) Iterator {616 pub fn iterate(self: Dir) Iterator {
617 switch (builtin.os.tag) {617 switch (builtin.os.tag) {
618 .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{618 .macos, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{
619 .dir = self,619 .dir = self,
620 .seek = 0,620 .seek = 0,
621 .index = 0,621 .index = 0,
...@@ -1302,7 +1302,7 @@ pub const Dir = struct {...@@ -1302,7 +1302,7 @@ pub const Dir = struct {
1302 error.AccessDenied => |e| switch (builtin.os.tag) {1302 error.AccessDenied => |e| switch (builtin.os.tag) {
1303 // non-Linux POSIX systems return EPERM when trying to delete a directory, so1303 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
1304 // we need to handle that case specifically and translate the error1304 // we need to handle that case specifically and translate the error
1305 .macosx, .ios, .freebsd, .netbsd, .dragonfly => {1305 .macos, .ios, .freebsd, .netbsd, .dragonfly => {
1306 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)1306 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
1307 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e;1307 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e;
1308 const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR;1308 const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR;
...@@ -1490,6 +1490,19 @@ pub const Dir = struct {...@@ -1490,6 +1490,19 @@ pub const Dir = struct {
1490 return os.windows.ReadLink(self.fd, sub_path_w, buffer);1490 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
1491 }1491 }
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
1493 /// On success, caller owns returned buffer.1506 /// On success, caller owns returned buffer.
1494 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.1507 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
1495 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {1508 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {
...@@ -1823,7 +1836,7 @@ pub const Dir = struct {...@@ -1823,7 +1836,7 @@ pub const Dir = struct {
1823 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });1836 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
1824 defer atomic_file.deinit();1837 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);
1827 return atomic_file.finish();1840 return atomic_file.finish();
1828 }1841 }
18291842
...@@ -2271,6 +2284,53 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {...@@ -2271,6 +2284,53 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
2271 return allocator.dupe(u8, try os.realpath(pathname, &buf));2284 return allocator.dupe(u8, try os.realpath(pathname, &buf));
2272}2285}
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
2274test "" {2334test "" {
2275 if (builtin.os.tag != .wasi) {2335 if (builtin.os.tag != .wasi) {
2276 _ = makeDirAbsolute;2336 _ = 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...@@ -42,7 +42,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
42 else => return error.AppDataDirUnavailable,42 else => return error.AppDataDirUnavailable,
43 }43 }
44 },44 },
45 .macosx => {45 .macos => {
46 const home_dir = os.getenv("HOME") orelse {46 const home_dir = os.getenv("HOME") orelse {
47 // TODO look in /etc/passwd47 // TODO look in /etc/passwd
48 return error.AppDataDirUnavailable;48 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 {...@@ -143,7 +143,7 @@ fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
143143
144test "Dir.realpath smoke test" {144test "Dir.realpath smoke test" {
145 switch (builtin.os.tag) {145 switch (builtin.os.tag) {
146 .linux, .windows, .macosx, .ios, .watchos, .tvos => {},146 .linux, .windows, .macos, .ios, .watchos, .tvos => {},
147 else => return error.SkipZigTest,147 else => return error.SkipZigTest,
148 }148 }
149149
lib/std/fs/watch.zig+4-4
...@@ -49,7 +49,7 @@ pub fn Watch(comptime V: type) type {...@@ -49,7 +49,7 @@ pub fn Watch(comptime V: type) type {
4949
50 const OsData = switch (builtin.os.tag) {50 const OsData = switch (builtin.os.tag) {
51 // TODO https://github.com/ziglang/zig/issues/377851 // TODO https://github.com/ziglang/zig/issues/3778
52 .macosx, .freebsd, .netbsd, .dragonfly => KqOsData,52 .macos, .freebsd, .netbsd, .dragonfly => KqOsData,
53 .linux => LinuxOsData,53 .linux => LinuxOsData,
54 .windows => WindowsOsData,54 .windows => WindowsOsData,
5555
...@@ -160,7 +160,7 @@ pub fn Watch(comptime V: type) type {...@@ -160,7 +160,7 @@ pub fn Watch(comptime V: type) type {
160 return self;160 return self;
161 },161 },
162162
163 .macosx, .freebsd, .netbsd, .dragonfly => {163 .macos, .freebsd, .netbsd, .dragonfly => {
164 self.* = Self{164 self.* = Self{
165 .allocator = allocator,165 .allocator = allocator,
166 .channel = channel,166 .channel = channel,
...@@ -178,7 +178,7 @@ pub fn Watch(comptime V: type) type {...@@ -178,7 +178,7 @@ pub fn Watch(comptime V: type) type {
178 /// All addFile calls and removeFile calls must have completed.178 /// All addFile calls and removeFile calls must have completed.
179 pub fn deinit(self: *Self) void {179 pub fn deinit(self: *Self) void {
180 switch (builtin.os.tag) {180 switch (builtin.os.tag) {
181 .macosx, .freebsd, .netbsd, .dragonfly => {181 .macos, .freebsd, .netbsd, .dragonfly => {
182 // TODO we need to cancel the frames before destroying the lock182 // TODO we need to cancel the frames before destroying the lock
183 self.os_data.table_lock.deinit();183 self.os_data.table_lock.deinit();
184 var it = self.os_data.file_table.iterator();184 var it = self.os_data.file_table.iterator();
...@@ -229,7 +229,7 @@ pub fn Watch(comptime V: type) type {...@@ -229,7 +229,7 @@ pub fn Watch(comptime V: type) type {
229229
230 pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {230 pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
231 switch (builtin.os.tag) {231 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),
233 .linux => return addFileLinux(self, file_path, value),233 .linux => return addFileLinux(self, file_path, value),
234 .windows => return addFileWindows(self, file_path, value),234 .windows => return addFileWindows(self, file_path, value),
235 else => @compileError("Unsupported OS"),235 else => @compileError("Unsupported OS"),
lib/std/math/big/int.zig+103-27
...@@ -446,6 +446,26 @@ pub const Mutable = struct {...@@ -446,6 +446,26 @@ pub const Mutable = struct {
446 rma.positive = (a.positive == b.positive);446 rma.positive = (a.positive == b.positive);
447 }447 }
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
449 /// q = a / b (rem r)469 /// q = a / b (rem r)
450 ///470 ///
451 /// a / b are floored (rounded towards 0).471 /// a / b are floored (rounded towards 0).
...@@ -1827,7 +1847,28 @@ pub const Managed = struct {...@@ -1827,7 +1847,28 @@ pub const Managed = struct {
1827 rma.setMetadata(m.positive, m.len);1847 rma.setMetadata(m.positive, m.len);
1828 }1848 }
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 {
1831 const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b);1872 const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b);
18321873
1833 const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs);1874 const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs);
...@@ -1837,7 +1878,7 @@ pub const Managed = struct {...@@ -1837,7 +1878,7 @@ pub const Managed = struct {
1837 var m = try Managed.initCapacity(rma.allocator, needed_limbs);1878 var m = try Managed.initCapacity(rma.allocator, needed_limbs);
1838 errdefer m.deinit();1879 errdefer m.deinit();
1839 var m_mut = m.toMutable();1880 var m_mut = m.toMutable();
1840 try m_mut.pow(a.toConst(), b, limbs_buffer);1881 try m_mut.pow(a, b, limbs_buffer);
1841 m.setMetadata(m_mut.positive, m_mut.len);1882 m.setMetadata(m_mut.positive, m_mut.len);
18421883
1843 rma.deinit();1884 rma.deinit();
...@@ -1845,7 +1886,7 @@ pub const Managed = struct {...@@ -1845,7 +1886,7 @@ pub const Managed = struct {
1845 } else {1886 } else {
1846 try rma.ensureCapacity(needed_limbs);1887 try rma.ensureCapacity(needed_limbs);
1847 var rma_mut = rma.toMutable();1888 var rma_mut = rma.toMutable();
1848 try rma_mut.pow(a.toConst(), b, limbs_buffer);1889 try rma_mut.pow(a, b, limbs_buffer);
1849 rma.setMetadata(rma_mut.positive, rma_mut.len);1890 rma.setMetadata(rma_mut.positive, rma_mut.len);
1850 }1891 }
1851 }1892 }
...@@ -1869,11 +1910,14 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L...@@ -1869,11 +1910,14 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L
1869 assert(r.len >= x.len + y.len + 1);1910 assert(r.len >= x.len + y.len + 1);
18701911
1871 // 48 is a pretty abitrary size chosen based on performance of a factorial program.1912 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
1872 if (x.len > 48) {1913 k_mul: {
1873 if (opt_allocator) |allocator| {1914 if (x.len > 48) {
1874 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {1915 if (opt_allocator) |allocator| {
1875 error.OutOfMemory => {}, // handled below1916 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
1876 };1917 error.OutOfMemory => break :k_mul, // handled below
1918 };
1919 return;
1920 }
1877 }1921 }
1878 }1922 }
18791923
...@@ -2203,6 +2247,42 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {...@@ -2203,6 +2247,42 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
2203 }2247 }
2204}2248}
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
2206/// Knuth 4.6.32286/// Knuth 4.6.3
2207fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {2287fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
2208 var tmp1: []Limb = undefined;2288 var tmp1: []Limb = undefined;
...@@ -2212,9 +2292,9 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {...@@ -2212,9 +2292,9 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
2212 // variable, use the output limbs and another temporary set to overcome this2292 // variable, use the output limbs and another temporary set to overcome this
2213 // limitation.2293 // limitation.
2214 // The initial assignment makes the result end in `r` so an extra memory2294 // 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 the2295 // copy is saved, each 1 flips the index twice so it's only the zeros that
2216 // 0.2296 // matter.
2217 const b_leading_zeros = @intCast(u5, @clz(u32, b));2297 const b_leading_zeros = @clz(u32, b);
2218 const exp_zeros = @popCount(u32, ~b) - b_leading_zeros;2298 const exp_zeros = @popCount(u32, ~b) - b_leading_zeros;
2219 if (exp_zeros & 1 != 0) {2299 if (exp_zeros & 1 != 0) {
2220 tmp1 = tmp_limbs;2300 tmp1 = tmp_limbs;
...@@ -2224,32 +2304,28 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {...@@ -2224,32 +2304,28 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
2224 tmp2 = tmp_limbs;2304 tmp2 = tmp_limbs;
2225 }2305 }
22262306
2227 const a_norm = a[0..llnormalize(a)];2307 mem.copy(Limb, tmp1, a);
22282308 mem.set(Limb, tmp1[a.len..], 0);
2229 mem.copy(Limb, tmp1, a_norm);
2230 mem.set(Limb, tmp1[a_norm.len..], 0);
22312309
2232 // Scan the exponent as a binary number, from left to right, dropping the2310 // Scan the exponent as a binary number, from left to right, dropping the
2233 // most significant bit set.2311 // most significant bit set.
2234 const exp_bits = @intCast(u5, 31 - b_leading_zeros);2312 // Square the result if the current bit is zero, square and multiply by a if
2235 var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros;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;
2238 while (i < exp_bits) : (i += 1) {2318 while (i < exp_bits) : (i += 1) {
2239 // Square2319 // Square
2240 {2320 mem.set(Limb, tmp2, 0);
2241 mem.set(Limb, tmp2, 0);2321 llsquare_basecase(tmp2, tmp1[0..llnormalize(tmp1)]);
2242 const op = tmp1[0..llnormalize(tmp1)];2322 mem.swap([]Limb, &tmp1, &tmp2);
2243 llmulacc(null, tmp2, op, op);
2244 mem.swap([]Limb, &tmp1, &tmp2);
2245 }
2246 // Multiply by a2323 // Multiply by a
2247 if (exp & 1 != 0) {2324 if (@shlWithOverflow(u32, exp, 1, &exp)) {
2248 mem.set(Limb, tmp2, 0);2325 mem.set(Limb, tmp2, 0);
2249 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm);2326 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a);
2250 mem.swap([]Limb, &tmp1, &tmp2);2327 mem.swap([]Limb, &tmp1, &tmp2);
2251 }2328 }
2252 exp >>= 1;
2253 }2329 }
2254}2330}
22552331
lib/std/math/big/int_test.zig+34-10
...@@ -720,6 +720,27 @@ test "big.int mul 0*0" {...@@ -720,6 +720,27 @@ test "big.int mul 0*0" {
720 testing.expect((try c.to(u32)) == 0);720 testing.expect((try c.to(u32)) == 0);
721}721}
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
723test "big.int div single-single no rem" {744test "big.int div single-single no rem" {
724 var a = try Managed.initSet(testing.allocator, 50);745 var a = try Managed.initSet(testing.allocator, 50);
725 defer a.deinit();746 defer a.deinit();
...@@ -1483,11 +1504,14 @@ test "big.int const to managed" {...@@ -1483,11 +1504,14 @@ test "big.int const to managed" {
14831504
1484test "big.int pow" {1505test "big.int pow" {
1485 {1506 {
1486 var a = try Managed.initSet(testing.allocator, 10);1507 var a = try Managed.initSet(testing.allocator, -3);
1487 defer a.deinit();1508 defer a.deinit();
14881509
1489 try a.pow(a, 8);1510 try a.pow(a.toConst(), 3);
1490 testing.expectEqual(@as(u32, 100000000), try a.to(u32));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));
1491 }1515 }
1492 {1516 {
1493 var a = try Managed.initSet(testing.allocator, 10);1517 var a = try Managed.initSet(testing.allocator, 10);
...@@ -1497,9 +1521,9 @@ test "big.int pow" {...@@ -1497,9 +1521,9 @@ test "big.int pow" {
1497 defer y.deinit();1521 defer y.deinit();
14981522
1499 // y and a are not aliased1523 // y and a are not aliased
1500 try y.pow(a, 123);1524 try y.pow(a.toConst(), 123);
1501 // y and a are aliased1525 // y and a are aliased
1502 try a.pow(a, 123);1526 try a.pow(a.toConst(), 123);
15031527
1504 testing.expect(a.eq(y));1528 testing.expect(a.eq(y));
15051529
...@@ -1517,18 +1541,18 @@ test "big.int pow" {...@@ -1517,18 +1541,18 @@ test "big.int pow" {
1517 var a = try Managed.initSet(testing.allocator, 0);1541 var a = try Managed.initSet(testing.allocator, 0);
1518 defer a.deinit();1542 defer a.deinit();
15191543
1520 try a.pow(a, 100);1544 try a.pow(a.toConst(), 100);
1521 testing.expectEqual(@as(i32, 0), try a.to(i32));1545 testing.expectEqual(@as(i32, 0), try a.to(i32));
15221546
1523 try a.set(1);1547 try a.set(1);
1524 try a.pow(a, 0);1548 try a.pow(a.toConst(), 0);
1525 testing.expectEqual(@as(i32, 1), try a.to(i32));1549 testing.expectEqual(@as(i32, 1), try a.to(i32));
1526 try a.pow(a, 100);1550 try a.pow(a.toConst(), 100);
1527 testing.expectEqual(@as(i32, 1), try a.to(i32));1551 testing.expectEqual(@as(i32, 1), try a.to(i32));
1528 try a.set(-1);1552 try a.set(-1);
1529 try a.pow(a, 15);1553 try a.pow(a.toConst(), 15);
1530 testing.expectEqual(@as(i32, -1), try a.to(i32));1554 testing.expectEqual(@as(i32, -1), try a.to(i32));
1531 try a.pow(a, 16);1555 try a.pow(a.toConst(), 16);
1532 testing.expectEqual(@as(i32, 1), try a.to(i32));1556 testing.expectEqual(@as(i32, 1), try a.to(i32));
1533 }1557 }
1534}1558}
lib/std/os.zig+29-20
...@@ -62,7 +62,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This())...@@ -62,7 +62,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This())
62else if (builtin.link_libc)62else if (builtin.link_libc)
63 std.c63 std.c
64else switch (builtin.os.tag) {64else switch (builtin.os.tag) {
65 .macosx, .ios, .watchos, .tvos => darwin,65 .macos, .ios, .watchos, .tvos => darwin,
66 .freebsd => freebsd,66 .freebsd => freebsd,
67 .linux => linux,67 .linux => linux,
68 .netbsd => netbsd,68 .netbsd => netbsd,
...@@ -354,7 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -354,7 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
354 // Prevents EINVAL.354 // Prevents EINVAL.
355 const max_count = switch (std.Target.current.os.tag) {355 const max_count = switch (std.Target.current.os.tag) {
356 .linux => 0x7ffff000,356 .linux => 0x7ffff000,
357 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),357 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
358 else => math.maxInt(isize),358 else => math.maxInt(isize),
359 };359 };
360 const adjusted_len = math.min(max_count, buf.len);360 const adjusted_len = math.min(max_count, buf.len);
...@@ -582,7 +582,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -582,7 +582,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
582/// On these systems, the read races with concurrent writes to the same file descriptor.582/// On these systems, the read races with concurrent writes to the same file descriptor.
583pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {583pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
584 const have_pread_but_not_preadv = switch (std.Target.current.os.tag) {584 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,
586 else => false,586 else => false,
587 };587 };
588 if (have_pread_but_not_preadv) {588 if (have_pread_but_not_preadv) {
...@@ -709,7 +709,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -709,7 +709,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
709709
710 const max_count = switch (std.Target.current.os.tag) {710 const max_count = switch (std.Target.current.os.tag) {
711 .linux => 0x7ffff000,711 .linux => 0x7ffff000,
712 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),712 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
713 else => math.maxInt(isize),713 else => math.maxInt(isize),
714 };714 };
715 const adjusted_len = math.min(max_count, bytes.len);715 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 {...@@ -863,7 +863,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
863 // Prevent EINVAL.863 // Prevent EINVAL.
864 const max_count = switch (std.Target.current.os.tag) {864 const max_count = switch (std.Target.current.os.tag) {
865 .linux => 0x7ffff000,865 .linux => 0x7ffff000,
866 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),866 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
867 else => math.maxInt(isize),867 else => math.maxInt(isize),
868 };868 };
869 const adjusted_len = math.min(max_count, bytes.len);869 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 {...@@ -915,7 +915,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
915/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.915/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
916pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {916pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
917 const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) {917 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,
919 else => false,919 else => false,
920 };920 };
921921
...@@ -4091,7 +4091,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {...@@ -4091,7 +4091,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4091 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;4091 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
4092 return out_buffer[0..end_index];4092 return out_buffer[0..end_index];
4093 },4093 },
4094 .macosx, .ios, .watchos, .tvos => {4094 .macos, .ios, .watchos, .tvos => {
4095 // On macOS, we can use F_GETPATH fcntl command to query the OS for4095 // On macOS, we can use F_GETPATH fcntl command to query the OS for
4096 // the path to the file descriptor.4096 // the path to the file descriptor.
4097 @memset(out_buffer, 0, MAX_PATH_BYTES);4097 @memset(out_buffer, 0, MAX_PATH_BYTES);
...@@ -4688,7 +4688,7 @@ pub fn sendfile(...@@ -4688,7 +4688,7 @@ pub fn sendfile(
4688 });4688 });
4689 const max_count = switch (std.Target.current.os.tag) {4689 const max_count = switch (std.Target.current.os.tag) {
4690 .linux => 0x7ffff000,4690 .linux => 0x7ffff000,
4691 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),4691 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
4692 else => math.maxInt(size_t),4692 else => math.maxInt(size_t),
4693 };4693 };
46944694
...@@ -4846,7 +4846,7 @@ pub fn sendfile(...@@ -4846,7 +4846,7 @@ pub fn sendfile(
4846 }4846 }
4847 }4847 }
4848 },4848 },
4849 .macosx, .ios, .tvos, .watchos => sf: {4849 .macos, .ios, .tvos, .watchos => sf: {
4850 var hdtr_data: std.c.sf_hdtr = undefined;4850 var hdtr_data: std.c.sf_hdtr = undefined;
4851 var hdtr: ?*std.c.sf_hdtr = null;4851 var hdtr: ?*std.c.sf_hdtr = null;
4852 if (headers.len != 0 or trailers.len != 0) {4852 if (headers.len != 0 or trailers.len != 0) {
...@@ -4945,6 +4945,9 @@ pub fn sendfile(...@@ -4945,6 +4945,9 @@ pub fn sendfile(
4945pub const CopyFileRangeError = error{4945pub const CopyFileRangeError = error{
4946 FileTooBig,4946 FileTooBig,
4947 InputOutput,4947 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,
4948 IsDir,4951 IsDir,
4949 OutOfMemory,4952 OutOfMemory,
4950 NoSpaceLeft,4953 NoSpaceLeft,
...@@ -4953,6 +4956,11 @@ pub const CopyFileRangeError = error{...@@ -4953,6 +4956,11 @@ pub const CopyFileRangeError = error{
4953 FileBusy,4956 FileBusy,
4954} || PReadError || PWriteError || UnexpectedError;4957} || 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
4956/// Transfer data between file descriptors at specified offsets.4964/// Transfer data between file descriptors at specified offsets.
4957/// Returns the number of bytes written, which can less than requested.4965/// Returns the number of bytes written, which can less than requested.
4958///4966///
...@@ -4981,22 +4989,18 @@ pub const CopyFileRangeError = error{...@@ -4981,22 +4989,18 @@ pub const CopyFileRangeError = error{
4981pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {4989pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
4982 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;4990 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
49834991
4984 // TODO support for other systems than linux4992 if (std.Target.current.os.tag == .linux and
4985 const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false;4993 (use_c or has_copy_file_range_syscall.get()))
49864994 {
4987 if (use_c or try_syscall) {
4988 const sys = if (use_c) std.c else linux;4995 const sys = if (use_c) std.c else linux;
49894996
4990 var off_in_copy = @bitCast(i64, off_in);4997 var off_in_copy = @bitCast(i64, off_in);
4991 var off_out_copy = @bitCast(i64, off_out);4998 var off_out_copy = @bitCast(i64, off_out);
49924999
4993 const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);5000 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
4997 switch (sys.getErrno(rc)) {5001 switch (sys.getErrno(rc)) {
4998 0 => return @intCast(usize, rc),5002 0 => return @intCast(usize, rc),
4999 EBADF => unreachable,5003 EBADF => return error.FilesOpenedWithWrongFlags,
5000 EFBIG => return error.FileTooBig,5004 EFBIG => return error.FileTooBig,
5001 EIO => return error.InputOutput,5005 EIO => return error.InputOutput,
5002 EISDIR => return error.IsDir,5006 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...@@ -5005,9 +5009,14 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
5005 EOVERFLOW => return error.Unseekable,5009 EOVERFLOW => return error.Unseekable,
5006 EPERM => return error.PermissionDenied,5010 EPERM => return error.PermissionDenied,
5007 ETXTBSY => return error.FileBusy,5011 ETXTBSY => return error.FileBusy,
5008 EINVAL => {}, // these may not be regular files, try fallback5012 // these may not be regular files, try fallback
5009 EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback5013 EINVAL => {},
5010 ENOSYS => {}, // syscall added in Linux 4.5, use fallback5014 // 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 },
5011 else => |err| return unexpectedErrno(err),5020 else => |err| return unexpectedErrno(err),
5012 }5021 }
5013 }5022 }
lib/std/os/bits.zig+1-1
...@@ -12,7 +12,7 @@ const std = @import("std");...@@ -12,7 +12,7 @@ const std = @import("std");
12const root = @import("root");12const root = @import("root");
1313
14pub usingnamespace switch (std.Target.current.os.tag) {14pub 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"),
16 .dragonfly => @import("bits/dragonfly.zig"),16 .dragonfly => @import("bits/dragonfly.zig"),
17 .freebsd => @import("bits/freebsd.zig"),17 .freebsd => @import("bits/freebsd.zig"),
18 .linux => @import("bits/linux.zig"),18 .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 {...@@ -360,7 +360,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
360}360}
361361
362test "dl_iterate_phdr" {362test "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)
364 return error.SkipZigTest;364 return error.SkipZigTest;
365365
366 var counter: usize = 0;366 var counter: usize = 0;
lib/std/packed_int_array.zig+2-2
...@@ -618,7 +618,7 @@ test "PackedIntArray at end of available memory" {...@@ -618,7 +618,7 @@ test "PackedIntArray at end of available memory" {
618 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;618 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
619619
620 switch (builtin.os.tag) {620 switch (builtin.os.tag) {
621 .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},621 .linux, .macos, .ios, .freebsd, .netbsd, .windows => {},
622 else => return,622 else => return,
623 }623 }
624 const PackedArray = PackedIntArray(u3, 8);624 const PackedArray = PackedIntArray(u3, 8);
...@@ -639,7 +639,7 @@ test "PackedIntSlice at end of available memory" {...@@ -639,7 +639,7 @@ test "PackedIntSlice at end of available memory" {
639 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;639 if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
640640
641 switch (builtin.os.tag) {641 switch (builtin.os.tag) {
642 .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},642 .linux, .macos, .ios, .freebsd, .netbsd, .windows => {},
643 else => return,643 else => return,
644 }644 }
645 const PackedSlice = PackedIntSlice(u11);645 const PackedSlice = PackedIntSlice(u11);
lib/std/process.zig+3-3
...@@ -585,7 +585,7 @@ pub const UserInfo = struct {...@@ -585,7 +585,7 @@ pub const UserInfo = struct {
585/// POSIX function which gets a uid from username.585/// POSIX function which gets a uid from username.
586pub fn getUserInfo(name: []const u8) !UserInfo {586pub fn getUserInfo(name: []const u8) !UserInfo {
587 return switch (builtin.os.tag) {587 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),
589 else => @compileError("Unsupported OS"),589 else => @compileError("Unsupported OS"),
590 };590 };
591}591}
...@@ -688,7 +688,7 @@ pub fn getBaseAddress() usize {...@@ -688,7 +688,7 @@ pub fn getBaseAddress() usize {
688 const phdr = os.system.getauxval(std.elf.AT_PHDR);688 const phdr = os.system.getauxval(std.elf.AT_PHDR);
689 return phdr - @sizeOf(std.elf.Ehdr);689 return phdr - @sizeOf(std.elf.Ehdr);
690 },690 },
691 .macosx, .freebsd, .netbsd => {691 .macos, .freebsd, .netbsd => {
692 return @ptrToInt(&std.c._mh_execute_header);692 return @ptrToInt(&std.c._mh_execute_header);
693 },693 },
694 .windows => return @ptrToInt(os.windows.kernel32.GetModuleHandleW(null)),694 .windows => return @ptrToInt(os.windows.kernel32.GetModuleHandleW(null)),
...@@ -733,7 +733,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]...@@ -733,7 +733,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
733 }.callback);733 }.callback);
734 return paths.toOwnedSlice();734 return paths.toOwnedSlice();
735 },735 },
736 .macosx, .ios, .watchos, .tvos => {736 .macos, .ios, .watchos, .tvos => {
737 var paths = List.init(allocator);737 var paths = List.init(allocator);
738 errdefer {738 errdefer {
739 const slice = paths.toOwnedSlice();739 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 {...@@ -44,7 +44,7 @@ pub fn clear_cache(start: usize, end: usize) callconv(.C) void {
44 else => false,44 else => false,
45 };45 };
46 const apple = switch (os) {46 const apple = switch (os) {
47 .ios, .macosx, .watchos, .tvos => true,47 .ios, .macos, .watchos, .tvos => true,
48 else => false,48 else => false,
49 };49 };
50 if (x86) {50 if (x86) {
lib/std/target.zig+8-8
...@@ -31,7 +31,7 @@ pub const Target = struct {...@@ -31,7 +31,7 @@ pub const Target = struct {
31 kfreebsd,31 kfreebsd,
32 linux,32 linux,
33 lv2,33 lv2,
34 macosx,34 macos,
35 netbsd,35 netbsd,
36 openbsd,36 openbsd,
37 solaris,37 solaris,
...@@ -61,7 +61,7 @@ pub const Target = struct {...@@ -61,7 +61,7 @@ pub const Target = struct {
6161
62 pub fn isDarwin(tag: Tag) bool {62 pub fn isDarwin(tag: Tag) bool {
63 return switch (tag) {63 return switch (tag) {
64 .ios, .macosx, .watchos, .tvos => true,64 .ios, .macos, .watchos, .tvos => true,
65 else => false,65 else => false,
66 };66 };
67 }67 }
...@@ -234,7 +234,7 @@ pub const Target = struct {...@@ -234,7 +234,7 @@ pub const Target = struct {
234 .max = .{ .major = 12, .minor = 1 },234 .max = .{ .major = 12, .minor = 1 },
235 },235 },
236 },236 },
237 .macosx => return .{237 .macos => return .{
238 .semver = .{238 .semver = .{
239 .min = .{ .major = 10, .minor = 13 },239 .min = .{ .major = 10, .minor = 13 },
240 .max = .{ .major = 10, .minor = 15, .patch = 3 },240 .max = .{ .major = 10, .minor = 15, .patch = 3 },
...@@ -312,7 +312,7 @@ pub const Target = struct {...@@ -312,7 +312,7 @@ pub const Target = struct {
312 .windows => return TaggedVersionRange{ .windows = self.version_range.windows },312 .windows => return TaggedVersionRange{ .windows = self.version_range.windows },
313313
314 .freebsd,314 .freebsd,
315 .macosx,315 .macos,
316 .ios,316 .ios,
317 .tvos,317 .tvos,
318 .watchos,318 .watchos,
...@@ -341,7 +341,7 @@ pub const Target = struct {...@@ -341,7 +341,7 @@ pub const Target = struct {
341 return switch (os.tag) {341 return switch (os.tag) {
342 .freebsd,342 .freebsd,
343 .netbsd,343 .netbsd,
344 .macosx,344 .macos,
345 .ios,345 .ios,
346 .tvos,346 .tvos,
347 .watchos,347 .watchos,
...@@ -450,7 +450,7 @@ pub const Target = struct {...@@ -450,7 +450,7 @@ pub const Target = struct {
450 .other,450 .other,
451 => return .eabi,451 => return .eabi,
452 .openbsd,452 .openbsd,
453 .macosx,453 .macos,
454 .freebsd,454 .freebsd,
455 .ios,455 .ios,
456 .tvos,456 .tvos,
...@@ -1277,7 +1277,7 @@ pub const Target = struct {...@@ -1277,7 +1277,7 @@ pub const Target = struct {
1277 .ios,1277 .ios,
1278 .tvos,1278 .tvos,
1279 .watchos,1279 .watchos,
1280 .macosx,1280 .macos,
1281 .uefi,1281 .uefi,
1282 .windows,1282 .windows,
1283 .emscripten,1283 .emscripten,
...@@ -1450,7 +1450,7 @@ pub const Target = struct {...@@ -1450,7 +1450,7 @@ pub const Target = struct {
1450 .ios,1450 .ios,
1451 .tvos,1451 .tvos,
1452 .watchos,1452 .watchos,
1453 .macosx,1453 .macos,
1454 .uefi,1454 .uefi,
1455 .windows,1455 .windows,
1456 .emscripten,1456 .emscripten,
lib/std/time.zig+1-1
...@@ -143,7 +143,7 @@ pub const Timer = struct {...@@ -143,7 +143,7 @@ pub const Timer = struct {
143 /// be less precise143 /// be less precise
144 frequency: switch (builtin.os.tag) {144 frequency: switch (builtin.os.tag) {
145 .windows => u64,145 .windows => u64,
146 .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,146 .macos, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,
147 else => void,147 else => void,
148 },148 },
149 resolution: u64,149 resolution: u64,
lib/std/zig/cross_target.zig+3-3
...@@ -137,7 +137,7 @@ pub const CrossTarget = struct {...@@ -137,7 +137,7 @@ pub const CrossTarget = struct {
137 },137 },
138138
139 .freebsd,139 .freebsd,
140 .macosx,140 .macos,
141 .ios,141 .ios,
142 .tvos,142 .tvos,
143 .watchos,143 .watchos,
...@@ -578,7 +578,7 @@ pub const CrossTarget = struct {...@@ -578,7 +578,7 @@ pub const CrossTarget = struct {
578 const os = switch (self.getOsTag()) {578 const os = switch (self.getOsTag()) {
579 .windows => "windows",579 .windows => "windows",
580 .linux => "linux",580 .linux => "linux",
581 .macosx => "macos",581 .macos => "macos",
582 else => return error.UnsupportedVcpkgOperatingSystem,582 else => return error.UnsupportedVcpkgOperatingSystem,
583 };583 };
584584
...@@ -718,7 +718,7 @@ pub const CrossTarget = struct {...@@ -718,7 +718,7 @@ pub const CrossTarget = struct {
718 => return error.InvalidOperatingSystemVersion,718 => return error.InvalidOperatingSystemVersion,
719719
720 .freebsd,720 .freebsd,
721 .macosx,721 .macos,
722 .ios,722 .ios,
723 .tvos,723 .tvos,
724 .watchos,724 .watchos,
lib/std/zig/system.zig+1-1
...@@ -267,7 +267,7 @@ pub const NativeTargetInfo = struct {...@@ -267,7 +267,7 @@ pub const NativeTargetInfo = struct {
267 os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version);267 os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version);
268 os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version);268 os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version);
269 },269 },
270 .macosx => {270 .macos => {
271 var scbuf: [32]u8 = undefined;271 var scbuf: [32]u8 = undefined;
272 var size: usize = undefined;272 var size: usize = undefined;
273273
src/Cache.zig+24
...@@ -576,6 +576,30 @@ pub const Manifest = struct {...@@ -576,6 +576,30 @@ pub const Manifest = struct {
576 }576 }
577};577};
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
579fn hashFile(file: fs.File, bin_digest: []u8) !void {603fn hashFile(file: fs.File, bin_digest: []u8) !void {
580 var buf: [1024]u8 = undefined;604 var buf: [1024]u8 = undefined;
581605
src/Compilation.zig+14-7
...@@ -922,7 +922,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -922,7 +922,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
922 try comp.work_queue.writeItem(.libcxx);922 try comp.work_queue.writeItem(.libcxx);
923 try comp.work_queue.writeItem(.libcxxabi);923 try comp.work_queue.writeItem(.libcxxabi);
924 }924 }
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) {
926 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });929 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
927 if (!comp.bin_file.options.link_libc) {930 if (!comp.bin_file.options.link_libc) {
928 try comp.work_queue.writeItem(.{ .zig_libc = {} });931 try comp.work_queue.writeItem(.{ .zig_libc = {} });
...@@ -2602,8 +2605,12 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -2602,8 +2605,12 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
26022605
2603 // We use an extra hex-encoded byte here to store some flags.2606 // We use an extra hex-encoded byte here to store some flags.
2604 var prev_digest_buf: [digest.len + 2]u8 = undefined;2607 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: {2608 const prev_digest: []u8 = Cache.readSmallFile(
2606 log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });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) });
2607 // Handle this as a cache miss.2614 // Handle this as a cache miss.
2608 break :blk prev_digest_buf[0..0];2615 break :blk prev_digest_buf[0..0];
2609 };2616 };
...@@ -2774,7 +2781,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -2774,7 +2781,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
27742781
2775 const digest = man.final();2782 const digest = man.final();
27762783
2777 // Update the dangling symlink with the digest. If it fails we can continue; it only2784 // Update the small file with the digest. If it fails we can continue; it only
2778 // means that the next invocation will have an unnecessary cache miss.2785 // means that the next invocation will have an unnecessary cache miss.
2779 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);2786 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
2780 log.debug("stage1 {} final digest={} flags={x}", .{2787 log.debug("stage1 {} final digest={} flags={x}", .{
...@@ -2789,10 +2796,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -2789,10 +2796,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
2789 log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{2796 log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{
2790 digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,2797 digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
2791 });2798 });
2792 directory.handle.symLink(&digest_plus_flags, id_symlink_basename, .{}) catch |err| {2799 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| {
2793 log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});2800 log.warn("failed to save stage1 hash digest file: {}", .{@errorName(err)});
2794 };2801 };
2795 // Again failure here only means an unnecessary cache miss.2802 // Failure here only means an unnecessary cache miss.
2796 man.writeManifest() catch |err| {2803 man.writeManifest() catch |err| {
2797 log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});2804 log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
2798 };2805 };
src/Module.zig+12-3
...@@ -93,7 +93,7 @@ pub const Export = struct {...@@ -93,7 +93,7 @@ pub const Export = struct {
93 /// Byte offset into the file that contains the export directive.93 /// Byte offset into the file that contains the export directive.
94 src: usize,94 src: usize,
95 /// Represents the position of the export, if any, in the output file.95 /// Represents the position of the export, if any, in the output file.
96 link: link.File.Elf.Export,96 link: link.File.Export,
97 /// The Decl that performs the export. Note that this is *not* the Decl being exported.97 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
98 owner_decl: *Decl,98 owner_decl: *Decl,
99 /// The Decl being exported. Note this is *not* the Decl performing the export.99 /// The Decl being exported. Note this is *not* the Decl performing the export.
...@@ -1712,7 +1712,10 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -1712,7 +1712,10 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
1712 }1712 }
1713 }1713 }
1714 if (self.comp.bin_file.cast(link.File.Elf)) |elf| {1714 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);
1716 }1719 }
1717 if (self.failed_exports.remove(exp)) |entry| {1720 if (self.failed_exports.remove(exp)) |entry| {
1718 entry.value.destroy(self.gpa);1721 entry.value.destroy(self.gpa);
...@@ -1875,7 +1878,13 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n...@@ -1875,7 +1878,13 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n
1875 new_export.* = .{1878 new_export.* = .{
1876 .options = .{ .name = symbol_name },1879 .options = .{ .name = symbol_name },
1877 .src = src,1880 .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 },
1879 .owner_decl = owner_decl,1888 .owner_decl = owner_decl,
1880 .exported_decl = exported_decl,1889 .exported_decl = exported_decl,
1881 .status = .in_progress,1890 .status = .in_progress,
src/codegen/llvm.zig+1-1
...@@ -69,7 +69,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {...@@ -69,7 +69,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
69 .kfreebsd => "kfreebsd",69 .kfreebsd => "kfreebsd",
70 .linux => "linux",70 .linux => "linux",
71 .lv2 => "lv2",71 .lv2 => "lv2",
72 .macosx => "macosx",72 .macos => "macosx",
73 .netbsd => "netbsd",73 .netbsd => "netbsd",
74 .openbsd => "openbsd",74 .openbsd => "openbsd",
75 .solaris => "solaris",75 .solaris => "solaris",
src/link.zig+16-4
...@@ -133,6 +133,14 @@ pub const File = struct {...@@ -133,6 +133,14 @@ pub const File = struct {
133 wasm: ?Wasm.FnData,133 wasm: ?Wasm.FnData,
134 };134 };
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
136 /// For DWARF .debug_info.144 /// For DWARF .debug_info.
137 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);145 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);
138146
...@@ -458,8 +466,12 @@ pub const File = struct {...@@ -458,8 +466,12 @@ pub const File = struct {
458 const digest = ch.final();466 const digest = ch.final();
459467
460 var prev_digest_buf: [digest.len]u8 = undefined;468 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: {469 const prev_digest: []u8 = Cache.readSmallFile(
462 log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) });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) });
463 break :b prev_digest_buf[0..0];475 break :b prev_digest_buf[0..0];
464 };476 };
465 if (mem.eql(u8, prev_digest, &digest)) {477 if (mem.eql(u8, prev_digest, &digest)) {
...@@ -504,8 +516,8 @@ pub const File = struct {...@@ -504,8 +516,8 @@ pub const File = struct {
504 const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);516 const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
505 if (bad) return error.UnableToWriteArchive;517 if (bad) return error.UnableToWriteArchive;
506518
507 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {519 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
508 std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)});520 std.log.warn("failed to save archive hash digest file: {}", .{@errorName(err)});
509 };521 };
510522
511 ch.writeManifest() catch |err| {523 ch.writeManifest() catch |err| {
src/link/Coff.zig+9-5
...@@ -854,8 +854,12 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -854,8 +854,12 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
854 _ = try man.hit();854 _ = try man.hit();
855 digest = man.final();855 digest = man.final();
856 var prev_digest_buf: [digest.len]u8 = undefined;856 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: {857 const prev_digest: []u8 = Cache.readSmallFile(
858 log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });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) });
859 // Handle this as a cache miss.863 // Handle this as a cache miss.
860 break :blk prev_digest_buf[0..0];864 break :blk prev_digest_buf[0..0];
861 };865 };
...@@ -1180,10 +1184,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1180,10 +1184,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1180 }1184 }
11811185
1182 if (!self.base.options.disable_lld_caching) {1186 if (!self.base.options.disable_lld_caching) {
1183 // Update the dangling symlink with the digest. If it fails we can continue; it only1187 // Update the file with the digest. If it fails we can continue; it only
1184 // means that the next invocation will have an unnecessary cache miss.1188 // means that the next invocation will have an unnecessary cache miss.
1185 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {1189 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1186 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});1190 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
1187 };1191 };
1188 // Again failure here only means an unnecessary cache miss.1192 // Again failure here only means an unnecessary cache miss.
1189 man.writeManifest() catch |err| {1193 man.writeManifest() catch |err| {
src/link/Elf.zig+11-7
...@@ -1326,8 +1326,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1326,8 +1326,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1326 digest = man.final();1326 digest = man.final();
13271327
1328 var prev_digest_buf: [digest.len]u8 = undefined;1328 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: {1329 const prev_digest: []u8 = Cache.readSmallFile(
1330 log.debug("ELF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });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) });
1331 // Handle this as a cache miss.1335 // Handle this as a cache miss.
1332 break :blk prev_digest_buf[0..0];1336 break :blk prev_digest_buf[0..0];
1333 };1337 };
...@@ -1647,10 +1651,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1647,10 +1651,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1647 }1651 }
16481652
1649 if (!self.base.options.disable_lld_caching) {1653 if (!self.base.options.disable_lld_caching) {
1650 // Update the dangling symlink with the digest. If it fails we can continue; it only1654 // Update the file with the digest. If it fails we can continue; it only
1651 // means that the next invocation will have an unnecessary cache miss.1655 // means that the next invocation will have an unnecessary cache miss.
1652 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {1656 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1653 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});1657 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
1654 };1658 };
1655 // Again failure here only means an unnecessary cache miss.1659 // Again failure here only means an unnecessary cache miss.
1656 man.writeManifest() catch |err| {1660 man.writeManifest() catch |err| {
...@@ -2588,7 +2592,7 @@ pub fn updateDeclExports(...@@ -2588,7 +2592,7 @@ pub fn updateDeclExports(
2588 },2592 },
2589 };2593 };
2590 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);2594 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| {
2592 const sym = &self.global_symbols.items[i];2596 const sym = &self.global_symbols.items[i];
2593 sym.* = .{2597 sym.* = .{
2594 .st_name = try self.updateString(sym.st_name, exp.options.name),2598 .st_name = try self.updateString(sym.st_name, exp.options.name),
...@@ -2613,7 +2617,7 @@ pub fn updateDeclExports(...@@ -2613,7 +2617,7 @@ pub fn updateDeclExports(
2613 .st_size = decl_sym.st_size,2617 .st_size = decl_sym.st_size,
2614 };2618 };
26152619
2616 exp.link.sym_index = @intCast(u32, i);2620 exp.link.elf.sym_index = @intCast(u32, i);
2617 }2621 }
2618 }2622 }
2619}2623}
src/link/MachO.zig+54-27
...@@ -20,6 +20,8 @@ const File = link.File;...@@ -20,6 +20,8 @@ const File = link.File;
20const Cache = @import("../Cache.zig");20const Cache = @import("../Cache.zig");
21const target_util = @import("../target.zig");21const target_util = @import("../target.zig");
2222
23const Trie = @import("MachO/Trie.zig");
24
23pub const base_tag: File.Tag = File.Tag.macho;25pub const base_tag: File.Tag = File.Tag.macho;
2426
25const LoadCommand = union(enum) {27const LoadCommand = union(enum) {
...@@ -113,6 +115,9 @@ local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},...@@ -113,6 +115,9 @@ local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
113global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},115global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
114/// Table of all undefined symbols116/// Table of all undefined symbols
115undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},117undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
118
119global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
120
116dyld_stub_binder_index: ?u16 = null,121dyld_stub_binder_index: ?u16 = null,
117122
118/// Table of symbol names aka the string table.123/// Table of symbol names aka the string table.
...@@ -176,6 +181,10 @@ pub const TextBlock = struct {...@@ -176,6 +181,10 @@ pub const TextBlock = struct {
176 };181 };
177};182};
178183
184pub const Export = struct {
185 sym_index: ?u32 = null,
186};
187
179pub const SrcFn = struct {188pub const SrcFn = struct {
180 pub const empty = SrcFn{};189 pub const empty = SrcFn{};
181};190};
...@@ -256,10 +265,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -256,10 +265,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
256265
257 switch (self.base.options.output_mode) {266 switch (self.base.options.output_mode) {
258 .Exe => {267 .Exe => {
259 if (self.entry_addr) |addr| {268 // Write export trie.
260 // Write export trie.269 try self.writeExportTrie();
261 try self.writeExportTrie();
262270
271 if (self.entry_addr) |addr| {
263 // Update LC_MAIN with entry offset272 // Update LC_MAIN with entry offset
264 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;273 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
265 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;274 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
...@@ -410,8 +419,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -410,8 +419,12 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
410 digest = man.final();419 digest = man.final();
411420
412 var prev_digest_buf: [digest.len]u8 = undefined;421 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: {422 const prev_digest: []u8 = Cache.readSmallFile(
414 log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });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) });
415 // Handle this as a cache miss.428 // Handle this as a cache miss.
416 break :blk prev_digest_buf[0..0];429 break :blk prev_digest_buf[0..0];
417 };430 };
...@@ -512,7 +525,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -512,7 +525,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
512 try argv.append(darwinArchString(target.cpu.arch));525 try argv.append(darwinArchString(target.cpu.arch));
513526
514 switch (target.os.tag) {527 switch (target.os.tag) {
515 .macosx => {528 .macos => {
516 try argv.append("-macosx_version_min");529 try argv.append("-macosx_version_min");
517 },530 },
518 .ios, .tvos, .watchos => switch (target.cpu.arch) {531 .ios, .tvos, .watchos => switch (target.cpu.arch) {
...@@ -665,10 +678,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -665,10 +678,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
665 }678 }
666679
667 if (!self.base.options.disable_lld_caching) {680 if (!self.base.options.disable_lld_caching) {
668 // Update the dangling symlink with the digest. If it fails we can continue; it only681 // Update the file with the digest. If it fails we can continue; it only
669 // means that the next invocation will have an unnecessary cache miss.682 // means that the next invocation will have an unnecessary cache miss.
670 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {683 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
671 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});684 std.log.warn("failed to save linking hash digest file: {}", .{@errorName(err)});
672 };685 };
673 // Again failure here only means an unnecessary cache miss.686 // Again failure here only means an unnecessary cache miss.
674 man.writeManifest() catch |err| {687 man.writeManifest() catch |err| {
...@@ -711,6 +724,7 @@ pub fn deinit(self: *MachO) void {...@@ -711,6 +724,7 @@ pub fn deinit(self: *MachO) void {
711 self.string_table.deinit(self.base.allocator);724 self.string_table.deinit(self.base.allocator);
712 self.undef_symbols.deinit(self.base.allocator);725 self.undef_symbols.deinit(self.base.allocator);
713 self.global_symbols.deinit(self.base.allocator);726 self.global_symbols.deinit(self.base.allocator);
727 self.global_symbol_free_list.deinit(self.base.allocator);
714 self.local_symbols.deinit(self.base.allocator);728 self.local_symbols.deinit(self.base.allocator);
715 self.sections.deinit(self.base.allocator);729 self.sections.deinit(self.base.allocator);
716 self.load_commands.deinit(self.base.allocator);730 self.load_commands.deinit(self.base.allocator);
...@@ -835,7 +849,7 @@ pub fn updateDeclExports(...@@ -835,7 +849,7 @@ pub fn updateDeclExports(
835 },849 },
836 };850 };
837 const n_type = decl_sym.n_type | macho.N_EXT;851 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| {
839 const sym = &self.global_symbols.items[i];853 const sym = &self.global_symbols.items[i];
840 sym.* = .{854 sym.* = .{
841 .n_strx = try self.updateString(sym.n_strx, exp.options.name),855 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
...@@ -846,8 +860,10 @@ pub fn updateDeclExports(...@@ -846,8 +860,10 @@ pub fn updateDeclExports(
846 };860 };
847 } else {861 } else {
848 const name_str_index = try self.makeString(exp.options.name);862 const name_str_index = try self.makeString(exp.options.name);
849 _ = self.global_symbols.addOneAssumeCapacity();863 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
850 const i = self.global_symbols.items.len - 1;864 _ = self.global_symbols.addOneAssumeCapacity();
865 break :blk self.global_symbols.items.len - 1;
866 };
851 self.global_symbols.items[i] = .{867 self.global_symbols.items[i] = .{
852 .n_strx = name_str_index,868 .n_strx = name_str_index,
853 .n_type = n_type,869 .n_type = n_type,
...@@ -856,11 +872,17 @@ pub fn updateDeclExports(...@@ -856,11 +872,17 @@ pub fn updateDeclExports(
856 .n_value = decl_sym.n_value,872 .n_value = decl_sym.n_value,
857 };873 };
858874
859 exp.link.sym_index = @intCast(u32, i);875 exp.link.macho.sym_index = @intCast(u32, i);
860 }876 }
861 }877 }
862}878}
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
864pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}886pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
865887
866pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {888pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
...@@ -1383,25 +1405,30 @@ fn writeAllUndefSymbols(self: *MachO) !void {...@@ -1383,25 +1405,30 @@ fn writeAllUndefSymbols(self: *MachO) !void {
1383}1405}
13841406
1385fn writeExportTrie(self: *MachO) !void {1407fn 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 symbols1410 var trie: Trie = .{};
1389 // single branch export trie1411 defer trie.deinit(self.base.allocator);
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;
13961412
1397 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;1413 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1398 const addr = self.entry_addr.? - text_segment.vmaddr;1414 for (self.global_symbols.items) |symbol| {
1399 const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);1415 // TODO figure out if we should put all global symbols into the export trie
1400 buf[10] = @intCast(u8, written) + 1;1416 const name = self.getString(symbol.n_strx);
1401 buf[11] = 0;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
1403 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;1430 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);
1405}1432}
14061433
1407fn writeStringTable(self: *MachO) !void {1434fn 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 {...@@ -310,8 +310,12 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
310 digest = man.final();310 digest = man.final();
311311
312 var prev_digest_buf: [digest.len]u8 = undefined;312 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: {313 const prev_digest: []u8 = Cache.readSmallFile(
314 log.debug("WASM LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });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) });
315 // Handle this as a cache miss.319 // Handle this as a cache miss.
316 break :blk prev_digest_buf[0..0];320 break :blk prev_digest_buf[0..0];
317 };321 };
...@@ -374,7 +378,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -374,7 +378,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
374 try argv.append(p);378 try argv.append(p);
375 }379 }
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) {
378 if (!self.base.options.link_libc) {382 if (!self.base.options.link_libc) {
379 try argv.append(comp.libc_static_lib.?.full_object_path);383 try argv.append(comp.libc_static_lib.?.full_object_path);
380 }384 }
...@@ -424,9 +428,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -424,9 +428,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
424 }428 }
425429
426 if (!self.base.options.disable_lld_caching) {430 if (!self.base.options.disable_lld_caching) {
427 // Update the dangling symlink with the digest. If it fails we can continue; it only431 // Update the file with the digest. If it fails we can continue; it only
428 // means that the next invocation will have an unnecessary cache miss.432 // 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| {
430 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});434 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
431 };435 };
432 // Again failure here only means an unnecessary cache miss.436 // 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 {...@@ -123,14 +123,14 @@ pub fn cannotDynamicLink(target: std.Target) bool {
123/// since this is the stable syscall interface.123/// since this is the stable syscall interface.
124pub fn osRequiresLibC(target: std.Target) bool {124pub fn osRequiresLibC(target: std.Target) bool {
125 return switch (target.os.tag) {125 return switch (target.os.tag) {
126 .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true,126 .freebsd, .netbsd, .dragonfly, .macos, .ios, .watchos, .tvos => true,
127 else => false,127 else => false,
128 };128 };
129}129}
130130
131pub fn libcNeedsLibUnwind(target: std.Target) bool {131pub fn libcNeedsLibUnwind(target: std.Target) bool {
132 return switch (target.os.tag) {132 return switch (target.os.tag) {
133 .macosx,133 .macos,
134 .ios,134 .ios,
135 .watchos,135 .watchos,
136 .tvos,136 .tvos,
...@@ -197,7 +197,7 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {...@@ -197,7 +197,7 @@ pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
197 .kfreebsd => .KFreeBSD,197 .kfreebsd => .KFreeBSD,
198 .linux => .Linux,198 .linux => .Linux,
199 .lv2 => .Lv2,199 .lv2 => .Lv2,
200 .macosx => .MacOSX,200 .macos => .MacOSX,
201 .netbsd => .NetBSD,201 .netbsd => .NetBSD,
202 .openbsd => .OpenBSD,202 .openbsd => .OpenBSD,
203 .solaris => .Solaris,203 .solaris => .Solaris,
src/type.zig+1-1
...@@ -3104,7 +3104,7 @@ pub const CType = enum {...@@ -3104,7 +3104,7 @@ pub const CType = enum {
3104 },3104 },
31053105
3106 .linux,3106 .linux,
3107 .macosx,3107 .macos,
3108 .freebsd,3108 .freebsd,
3109 .netbsd,3109 .netbsd,
3110 .dragonfly,3110 .dragonfly,
src/zig_llvm.cpp+3-1
...@@ -820,7 +820,9 @@ const char *ZigLLVMGetVendorTypeName(ZigLLVM_VendorType vendor) {...@@ -820,7 +820,9 @@ const char *ZigLLVMGetVendorTypeName(ZigLLVM_VendorType vendor) {
820}820}
821821
822const char *ZigLLVMGetOSTypeName(ZigLLVM_OSType os) {822const 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;
824}826}
825827
826const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type) {828const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type) {
test/stack_traces.zig+1-1
...@@ -308,7 +308,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -308,7 +308,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
308 },308 },
309 );309 );
310 },310 },
311 .macosx => {311 .macos => {
312 cases.addCase(312 cases.addCase(
313 "return",313 "return",
314 source_return,314 source_return,
test/stage2/test.zig+1-1
...@@ -13,7 +13,7 @@ const linux_x64 = std.zig.CrossTarget{...@@ -13,7 +13,7 @@ const linux_x64 = std.zig.CrossTarget{
1313
14const macosx_x64 = std.zig.CrossTarget{14const macosx_x64 = std.zig.CrossTarget{
15 .cpu_arch = .x86_64,15 .cpu_arch = .x86_64,
16 .os_tag = .macosx,16 .os_tag = .macos,
17};17};
1818
19const linux_riscv64 = std.zig.CrossTarget{19const linux_riscv64 = std.zig.CrossTarget{
test/tests.zig+1-1
...@@ -234,7 +234,7 @@ const test_targets = blk: {...@@ -234,7 +234,7 @@ const test_targets = blk: {
234 TestTarget{234 TestTarget{
235 .target = .{235 .target = .{
236 .cpu_arch = .x86_64,236 .cpu_arch = .x86_64,
237 .os_tag = .macosx,237 .os_tag = .macos,
238 .abi = .gnu,238 .abi = .gnu,
239 },239 },
240 // https://github.com/ziglang/zig/issues/3295240 // https://github.com/ziglang/zig/issues/3295