| author | |
| committer | |
| log | c19dcafa17117475f8334d5186bd87fb56c9d315 |
| tree | f7af587f9a1355c5229699d78876f7d1a84942a4 |
| parent | c0b2813e0468586faee5bf3ee7583afad53d771a |
| parent | 2ab0c7391a871a3063f825e08e02ea2a8e9269e9 |
40 files changed, 964 insertions(+), 178 deletions(-)
CODE_OF_CONDUCT.md created+75| ... | @@ -0,0 +1,75 @@ | ||
| 1 | # Code of Conduct | ||
| 2 | |||
| 3 | Hello, and welcome! 👋 | ||
| 4 | |||
| 5 | The Zig community is decentralized. Anyone is free to start and maintain their | ||
| 6 | own space for people to gather, and edit | ||
| 7 | [the Community wiki page](https://github.com/ziglang/zig/wiki/Community) to add | ||
| 8 | a link. There is no concept of "official" or "unofficial", however, each | ||
| 9 | gathering place has its own moderators and rules. | ||
| 10 | |||
| 11 | This is Andrew Kelley speaking. At least for now, I'm the moderator of the | ||
| 12 | ziglang organization GitHub repositories and the #zig IRC channel on Freenode. | ||
| 13 | **This document contains the rules that govern these two spaces only**. | ||
| 14 | |||
| 15 | The rules here are strict. This space is for focused, on topic, technical work | ||
| 16 | on the Zig project only. It is everyone's responsibility to maintain a positive | ||
| 17 | environment, especially when disagreements occur. | ||
| 18 | |||
| 19 | ## Our Standards | ||
| 20 | |||
| 21 | Examples 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 | |||
| 31 | Examples 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 | |||
| 47 | If you need to report an issue you can contact me or Loris Cro, who are both | ||
| 48 | paid by the Zig Software Foundation, and so moderation of this space is part of | ||
| 49 | our job. We will swiftly remove anyone who is antagonizing others or being | ||
| 50 | generally destructive. | ||
| 51 | |||
| 52 | This includes Private Harassment. If person A is directly harassed or | ||
| 53 | antagonized by person B, person B will be blocked from participating in this | ||
| 54 | space even if the harassment didn't take place on one of the mediums directly | ||
| 55 | under rule of this Code of Conduct. | ||
| 56 | |||
| 57 | As noted, discussing this Code of Conduct should not take place on GitHub or IRC | ||
| 58 | because these spaces are for directly working on code, not for meta-discussion. | ||
| 59 | If you have any issues with it, you can contact me directly, or you can join one | ||
| 60 | of the community spaces that has different rules. | ||
| 61 | |||
| 62 | * Andrew Kelley <andrew@ziglang.org> | ||
| 63 | * Loris Cro <loris@ziglang.org> | ||
| 64 | |||
| 65 | ## Conclusion | ||
| 66 | |||
| 67 | Thanks for reading the rules. Together, we can make this space welcoming and | ||
| 68 | inclusive for everyone, regardless of age, body size, disability, ethnicity, | ||
| 69 | sex characteristics, gender identity and expression, level of experience, | ||
| 70 | education, socio-economic status, nationality, personal appearance, race, | ||
| 71 | religion, or sexual identity and orientation. | ||
| 72 | |||
| 73 | Sincerely, | ||
| 74 | |||
| 75 | Andrew ✌️ | ||
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 |
| 7 | 7 | ||
| 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) |
| 14 | 16 |
doc/langref.html.in+4-1| ... | @@ -9905,7 +9905,10 @@ const std = @import("std"); | ... | @@ -9905,7 +9905,10 @@ const std = @import("std"); |
| 9905 | const PreopenList = std.fs.wasi.PreopenList; | 9905 | const PreopenList = std.fs.wasi.PreopenList; |
| 9906 | 9906 | ||
| 9907 | pub fn main() !void { | 9907 | pub 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(); |
| 9910 | 9913 | ||
| 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"); |
| 22 | pub usingnamespace switch (std.Target.current.os.tag) { | 22 | pub 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 |
| 122 | pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; | 122 | pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; |
| 123 | 123 | ||
| 124 | pub usingnamespace switch (builtin.os.tag) { | 124 | pub 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$NOCANCEL | 193 | // XXX: close -> close$NOCANCEL |
| 194 | // XXX: getdirentries -> _getdirentries64 | 194 | // 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; |
| 18 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; | 18 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; |
| 19 | pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8; | 19 | pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8; |
| 20 | 20 | ||
| 21 | pub const COPYFILE_ACL = 1 << 0; | ||
| 22 | pub const COPYFILE_STAT = 1 << 1; | ||
| 23 | pub const COPYFILE_XATTR = 1 << 2; | ||
| 24 | pub const COPYFILE_DATA = 1 << 3; | ||
| 25 | |||
| 26 | pub const copyfile_state_t = *opaque {}; | ||
| 27 | pub extern "c" fn fcopyfile(from: fd_t, to: fd_t, state: ?copyfile_state_t, flags: u32) c_int; | ||
| 28 | |||
| 21 | pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; | 29 | pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; |
| 22 | 30 | ||
| 23 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; | 31 | pub 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 | }; |
| 1321 | 1321 | ||
| 1322 | pub const ModuleDebugInfo = switch (builtin.os.tag) { | 1322 | pub 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; |
| 19 | pub const DynLib = switch (builtin.os.tag) { | 19 | pub 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 | }; |
| 25 | 25 | ||
| ... | @@ -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 | }; |
| 410 | 410 |
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 | }; |
| 67 | 67 | ||
| 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 | }; |
| 86 | 86 | ||
| 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); |
| 265 | 265 | ||
| ... | @@ -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 it | 731 | // 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 { |
| 1344 | 1344 | ||
| 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. |
| 41 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { | 41 | pub 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 surrogate | 44 | // 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; |
| 304 | 304 | ||
| 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 { |
| 615 | 615 | ||
| 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, so | 1303 | // non-Linux POSIX systems return EPERM when trying to delete a directory, so |
| 1304 | // we need to handle that case specifically and translate the error | 1304 | // 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 | } |
| 1492 | 1492 | ||
| 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(); |
| 1825 | 1838 | ||
| 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 | } |
| 1829 | 1842 | ||
| ... | @@ -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 | } |
| 2273 | 2286 | ||
| 2287 | const 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. | ||
| 2292 | fn 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 | |||
| 2274 | test "" { | 2334 | test "" { |
| 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/passwd | 47 | // 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 { |
| 143 | 143 | ||
| 144 | test "Dir.realpath smoke test" { | 144 | test "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 | } |
| 149 | 149 |
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 { |
| 49 | 49 | ||
| 50 | const OsData = switch (builtin.os.tag) { | 50 | const OsData = switch (builtin.os.tag) { |
| 51 | // TODO https://github.com/ziglang/zig/issues/3778 | 51 | // 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, |
| 55 | 55 | ||
| ... | @@ -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 | }, |
| 162 | 162 | ||
| 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 lock | 182 | // 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 { |
| 229 | 229 | ||
| 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 | } |
| 448 | 448 | ||
| 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 | } |
| 1829 | 1849 | ||
| 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); |
| 1832 | 1873 | ||
| 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); |
| 1842 | 1883 | ||
| 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); |
| 1870 | 1911 | ||
| 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 below | 1916 | 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 | } |
| 1879 | 1923 | ||
| ... | @@ -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 | } |
| 2205 | 2249 | ||
| 2250 | /// r MUST NOT alias x. | ||
| 2251 | fn 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.3 | 2286 | /// Knuth 4.6.3 |
| 2207 | fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void { | 2287 | fn 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 this | 2292 | // 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 memory | 2294 | // The initial assignment makes the result end in `r` so an extra memory |
| 2215 | // copy is saved, each 1 flips the index twice so it's a no-op so count the | 2295 | // 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 | } |
| 2226 | 2306 | ||
| 2227 | const a_norm = a[0..llnormalize(a)]; | 2307 | mem.copy(Limb, tmp1, a); |
| 2228 | 2308 | mem.set(Limb, tmp1[a.len..], 0); | |
| 2229 | mem.copy(Limb, tmp1, a_norm); | ||
| 2230 | mem.set(Limb, tmp1[a_norm.len..], 0); | ||
| 2231 | 2309 | ||
| 2232 | // Scan the exponent as a binary number, from left to right, dropping the | 2310 | // 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); | ||
| 2236 | 2316 | ||
| 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 | // Square | 2319 | // 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 a | 2323 | // 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 | } |
| 2255 | 2331 |
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 | } |
| 722 | 722 | ||
| 723 | test "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 | |||
| 723 | test "big.int div single-single no rem" { | 744 | test "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" { |
| 1483 | 1504 | ||
| 1484 | test "big.int pow" { | 1505 | test "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(); |
| 1488 | 1509 | ||
| 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(); |
| 1498 | 1522 | ||
| 1499 | // y and a are not aliased | 1523 | // y and a are not aliased |
| 1500 | try y.pow(a, 123); | 1524 | try y.pow(a.toConst(), 123); |
| 1501 | // y and a are aliased | 1525 | // y and a are aliased |
| 1502 | try a.pow(a, 123); | 1526 | try a.pow(a.toConst(), 123); |
| 1503 | 1527 | ||
| 1504 | testing.expect(a.eq(y)); | 1528 | testing.expect(a.eq(y)); |
| 1505 | 1529 | ||
| ... | @@ -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(); |
| 1519 | 1543 | ||
| 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)); |
| 1522 | 1546 | ||
| 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()) |
| 62 | else if (builtin.link_libc) | 62 | else if (builtin.link_libc) |
| 63 | std.c | 63 | std.c |
| 64 | else switch (builtin.os.tag) { | 64 | else 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. |
| 583 | pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { | 583 | pub 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 { |
| 709 | 709 | ||
| 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. |
| 916 | pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize { | 916 | pub 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 | }; |
| 921 | 921 | ||
| ... | @@ -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 for | 4095 | // 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 | }; |
| 4694 | 4694 | ||
| ... | @@ -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( |
| 4945 | pub const CopyFileRangeError = error{ | 4945 | pub 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; |
| 4955 | 4958 | ||
| 4959 | var 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{ |
| 4981 | pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize { | 4989 | pub 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; |
| 4983 | 4991 | ||
| 4984 | // TODO support for other systems than linux | 4992 | 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())) |
| 4986 | 4994 | { | |
| 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; |
| 4989 | 4996 | ||
| 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); |
| 4992 | 4999 | ||
| 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 fallback | 5012 | // these may not be regular files, try fallback |
| 5009 | EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback | 5013 | EINVAL => {}, |
| 5010 | ENOSYS => {}, // syscall added in Linux 4.5, use fallback | 5014 | // support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5015 | EXDEV => {}, | ||
| 5016 | // syscall added in Linux 4.5, use fallback | ||
| 5017 | ENOSYS => { | ||
| 5018 | has_copy_file_range_syscall.set(false); | ||
| 5019 | }, | ||
| 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"); |
| 12 | const root = @import("root"); | 12 | const root = @import("root"); |
| 13 | 13 | ||
| 14 | pub usingnamespace switch (std.Target.current.os.tag) { | 14 | pub 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 | } |
| 361 | 361 | ||
| 362 | test "dl_iterate_phdr" { | 362 | test "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; |
| 365 | 365 | ||
| 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; |
| 619 | 619 | ||
| 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; |
| 640 | 640 | ||
| 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. |
| 586 | pub fn getUserInfo(name: []const u8) !UserInfo { | 586 | pub 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 { |
| 61 | 61 | ||
| 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 }, |
| 313 | 313 | ||
| 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 precise | 143 | /// 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 | }, |
| 138 | 138 | ||
| 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 | }; |
| 584 | 584 | ||
| ... | @@ -718,7 +718,7 @@ pub const CrossTarget = struct { | ... | @@ -718,7 +718,7 @@ pub const CrossTarget = struct { |
| 718 | => return error.InvalidOperatingSystemVersion, | 718 | => return error.InvalidOperatingSystemVersion, |
| 719 | 719 | ||
| 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; |
| 273 | 273 |
src/Cache.zig+24| ... | @@ -576,6 +576,30 @@ pub const Manifest = struct { | ... | @@ -576,6 +576,30 @@ pub const Manifest = struct { |
| 576 | } | 576 | } |
| 577 | }; | 577 | }; |
| 578 | 578 | ||
| 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. | ||
| 582 | pub 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. | ||
| 594 | pub 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 | |||
| 579 | fn hashFile(file: fs.File, bin_digest: []u8) !void { | 603 | fn hashFile(file: fs.File, bin_digest: []u8) !void { |
| 580 | var buf: [1024]u8 = undefined; | 604 | var buf: [1024]u8 = undefined; |
| 581 | 605 |
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 |
| 2602 | 2605 | ||
| 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 |
| 2774 | 2781 | ||
| 2775 | const digest = man.final(); | 2782 | const digest = man.final(); |
| 2776 | 2783 | ||
| 2777 | // Update the dangling symlink with the digest. If it fails we can continue; it only | 2784 | // Update the small file with the digest. If it fails we can continue; it only |
| 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 | }; |
| 135 | 135 | ||
| 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); |
| 138 | 146 | ||
| ... | @@ -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(); |
| 459 | 467 | ||
| 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; |
| 506 | 518 | ||
| 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 | }; |
| 510 | 522 | ||
| 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 | } |
| 1181 | 1185 | ||
| 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 only | 1187 | // 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(); |
| 1327 | 1327 | ||
| 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 | } |
| 1648 | 1652 | ||
| 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 only | 1654 | // 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 | }; |
| 2615 | 2619 | ||
| 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; |
| 20 | const Cache = @import("../Cache.zig"); | 20 | const Cache = @import("../Cache.zig"); |
| 21 | const target_util = @import("../target.zig"); | 21 | const target_util = @import("../target.zig"); |
| 22 | 22 | ||
| 23 | const Trie = @import("MachO/Trie.zig"); | ||
| 24 | |||
| 23 | pub const base_tag: File.Tag = File.Tag.macho; | 25 | pub const base_tag: File.Tag = File.Tag.macho; |
| 24 | 26 | ||
| 25 | const LoadCommand = union(enum) { | 27 | const 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) = .{}, |
| 113 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 115 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 114 | /// Table of all undefined symbols | 116 | /// Table of all undefined symbols |
| 115 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 117 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 118 | |||
| 119 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 120 | |||
| 116 | dyld_stub_binder_index: ?u16 = null, | 121 | dyld_stub_binder_index: ?u16 = null, |
| 117 | 122 | ||
| 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 | }; |
| 178 | 183 | ||
| 184 | pub const Export = struct { | ||
| 185 | sym_index: ?u32 = null, | ||
| 186 | }; | ||
| 187 | |||
| 179 | pub const SrcFn = struct { | 188 | pub 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 { |
| 256 | 265 | ||
| 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(); | ||
| 262 | 270 | ||
| 271 | if (self.entry_addr) |addr| { | ||
| 263 | // Update LC_MAIN with entry offset | 272 | // 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(); |
| 411 | 420 | ||
| 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)); |
| 513 | 526 | ||
| 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 | } |
| 666 | 679 | ||
| 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 only | 681 | // 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 | }; |
| 858 | 874 | ||
| 859 | exp.link.sym_index = @intCast(u32, i); | 875 | exp.link.macho.sym_index = @intCast(u32, i); |
| 860 | } | 876 | } |
| 861 | } | 877 | } |
| 862 | } | 878 | } |
| 863 | 879 | ||
| 880 | pub 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 | |||
| 864 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} | 886 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} |
| 865 | 887 | ||
| 866 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | 888 | pub 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 | } |
| 1384 | 1406 | ||
| 1385 | fn writeExportTrie(self: *MachO) !void { | 1407 | fn writeExportTrie(self: *MachO) !void { |
| 1386 | assert(self.entry_addr != null); | 1408 | if (self.global_symbols.items.len == 0) return; // No exports, nothing to do. |
| 1387 | 1409 | ||
| 1388 | // TODO implement mechanism for generating a prefix tree of the exported symbols | 1410 | var trie: Trie = .{}; |
| 1389 | // single branch export trie | 1411 | 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; | ||
| 1396 | 1412 | ||
| 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); | ||
| 1402 | 1429 | ||
| 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 | } |
| 1406 | 1433 | ||
| 1407 | fn writeStringTable(self: *MachO) !void { | 1434 | fn 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. | ||
| 31 | const Trie = @This(); | ||
| 32 | |||
| 33 | const std = @import("std"); | ||
| 34 | const mem = std.mem; | ||
| 35 | const leb = std.debug.leb; | ||
| 36 | const log = std.log.scoped(.link); | ||
| 37 | const testing = std.testing; | ||
| 38 | const assert = std.debug.assert; | ||
| 39 | const Allocator = mem.Allocator; | ||
| 40 | |||
| 41 | pub const Symbol = struct { | ||
| 42 | name: []const u8, | ||
| 43 | vmaddr_offset: u64, | ||
| 44 | export_flags: u64, | ||
| 45 | }; | ||
| 46 | |||
| 47 | const 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 | |||
| 60 | const 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. | ||
| 219 | node_count: usize = 1, | ||
| 220 | /// The root node of the trie. | ||
| 221 | root: 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. | ||
| 226 | pub 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. | ||
| 234 | pub 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. | ||
| 260 | fn 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 | |||
| 267 | pub fn deinit(self: *Trie, alloc: *Allocator) void { | ||
| 268 | self.root.deinit(alloc); | ||
| 269 | } | ||
| 270 | |||
| 271 | test "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 | |||
| 315 | test "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 | |||
| 368 | test "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(); |
| 311 | 311 | ||
| 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 | } |
| 376 | 380 | ||
| 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 | } |
| 425 | 429 | ||
| 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 only | 431 | // 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. |
| 124 | pub fn osRequiresLibC(target: std.Target) bool { | 124 | pub 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 | } |
| 130 | 130 | ||
| 131 | pub fn libcNeedsLibUnwind(target: std.Target) bool { | 131 | pub 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 | }, |
| 3105 | 3105 | ||
| 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 | } |
| 821 | 821 | ||
| 822 | const char *ZigLLVMGetOSTypeName(ZigLLVM_OSType os) { | 822 | const 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 | } |
| 825 | 827 | ||
| 826 | const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type) { | 828 | const 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{ |
| 13 | 13 | ||
| 14 | const macosx_x64 = std.zig.CrossTarget{ | 14 | const 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 | }; |
| 18 | 18 | ||
| 19 | const linux_riscv64 = std.zig.CrossTarget{ | 19 | const 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/3295 | 240 | // https://github.com/ziglang/zig/issues/3295 |